blob: eb56366719c8812367559da04e5d452a4b689c45 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/wallet/content/renderer/image_extractor.h"
#include <utility>
#include "base/memory/ptr_util.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_element_collection.h"
#include "third_party/blink/public/web/web_local_frame.h"
namespace {
// Key used to associate ImageExtractor with a RenderFrame.
const void* const kUserDataKey = &kUserDataKey;
// The maximum number of images to extract from a page.
// TODO(crbug.com/445386472): Use finch params to control.
constexpr size_t kMaxImages = 10;
// The minimum height and width of an image to be considered qualified for
// barcode detection.
// TODO(crbug.com/445386472): Use finch params to control.
constexpr int kMinImageSize = 10;
// The maximum aspect ratio of an image to be considered qualified for barcode
// detection.
// TODO(crbug.com/445386472): Use finch params to control.
constexpr int kMaxAspectRatio = 15;
} // namespace
namespace wallet {
ImageExtractor::ImageExtractor(content::RenderFrame* render_frame,
service_manager::BinderRegistry* registry)
: render_frame_(render_frame) {
// Unretained is safe here because `registry` is also scoped to the
// RenderFrame and the ImageExtractor's lifetime is tied to the RenderFrame.
registry->AddInterface(base::BindRepeating(&ImageExtractor::BindReceiver,
base::Unretained(this)));
}
ImageExtractor::~ImageExtractor() = default;
// static
void ImageExtractor::Create(content::RenderFrame* render_frame,
service_manager::BinderRegistry* registry) {
// Do nothing if an ImageExtractor is already attached to the RenderFrame.
if (render_frame->GetUserData(kUserDataKey)) {
return;
}
render_frame->SetUserData(kUserDataKey, base::WrapUnique(new ImageExtractor(
render_frame, registry)));
}
void ImageExtractor::ExtractImages(ExtractImagesCallback callback) {
blink::WebDocument doc = render_frame_->GetWebFrame()->GetDocument();
std::move(callback).Run(ExtractQualifiedImageElements(doc));
}
void ImageExtractor::BindReceiver(
mojo::PendingReceiver<wallet::mojom::ImageExtractor> receiver) {
receiver_.reset();
receiver_.Bind(std::move(receiver));
}
// Extract all <img> elements from the document
std::vector<SkBitmap> ImageExtractor::ExtractQualifiedImageElements(
const blink::WebDocument& document) const {
blink::WebElementCollection image_elements =
document.GetElementsByHTMLTagName("img");
std::vector<SkBitmap> skia_images;
skia_images.reserve(image_elements.length());
for (blink::WebElement element = image_elements.FirstItem();
!element.IsNull(); element = image_elements.NextItem()) {
if (skia_images.size() >= kMaxImages) {
break;
}
SkBitmap skia_image = element.ImageContents();
if (IsImageQualified(skia_image)) {
skia_images.push_back(std::move(skia_image));
}
}
return skia_images;
}
// Checks if an image is qualified for barcode detection. An image is
// considered qualified if it is not empty, meets minimum size requirements,
// and does not have an extreme aspect ratio.
bool ImageExtractor::IsImageQualified(const SkBitmap& bitmap) const {
// Empty images are not qualified.
if (bitmap.empty()) {
return false;
}
// Images that are too small are not qualified.
if (std::min(bitmap.height(), bitmap.width()) < kMinImageSize) {
return false;
}
// Images with extreme aspect ratios are not qualified.
const int larger_dim = std::max(bitmap.width(), bitmap.height());
const int smaller_dim = std::min(bitmap.width(), bitmap.height());
if (static_cast<double>(larger_dim) >
static_cast<double>(smaller_dim) * kMaxAspectRatio) {
return false;
}
return true;
}
} // namespace wallet