Unify Print Preview extraction across all desktop platforms (#30632)
This commit is contained in:
@@ -227,77 +227,6 @@ class AIChatUIBrowserTest : public InProcessBrowserTest {
|
||||
content::ContentMockCertVerifier mock_cert_verifier_;
|
||||
};
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(AIChatUIBrowserTest, PrintPreview) {
|
||||
NavigateURL(https_server_.GetURL("docs.google.com", "/long_canvas.html"),
|
||||
false);
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
FetchPageContent(
|
||||
FROM_HERE, "This is the way.\n\nI have spoken.\nWherever I Go, He Goes.");
|
||||
// Panel is still active so we don't need to set it up again
|
||||
|
||||
// Page recognition host with a canvas element
|
||||
NavigateURL(https_server_.GetURL("docs.google.com", "/canvas.html"), false);
|
||||
FetchPageContent(FROM_HERE, "this is the way");
|
||||
|
||||
// Ignores all dom content, only does print preview extraction
|
||||
NavigateURL(https_server_.GetURL("docs.google.com",
|
||||
"/long_canvas_with_dom_content.html"));
|
||||
FetchPageContent(FROM_HERE,
|
||||
"This is the way.\n\nI have spoken.\nWherever I Go, He "
|
||||
"Goes.\nOr maybe not.");
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Unsupported locale should return no content for Windows only
|
||||
// Other platforms do not use locale for extraction
|
||||
const brave_l10n::test::ScopedDefaultLocale locale("xx_XX");
|
||||
NavigateURL(https_server_.GetURL("docs.google.com", "/canvas.html"), false);
|
||||
FetchPageContent(FROM_HERE, "");
|
||||
#endif // #if BUILDFLAG(IS_WIN)
|
||||
#else
|
||||
FetchPageContent(FROM_HERE, "");
|
||||
#endif
|
||||
// Each request is cleared after extraction.
|
||||
EXPECT_FALSE(HasPendingGetContentRequest());
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION) && BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
IN_PROC_BROWSER_TEST_F(AIChatUIBrowserTest, PrintPreviewPagesLimit) {
|
||||
NavigateURL(
|
||||
https_server_.GetURL("docs.google.com", "/extra_long_canvas.html"),
|
||||
false);
|
||||
std::string expected_string(ai_chat::kMaxPreviewPages - 1, '\n');
|
||||
base::StrAppend(&expected_string, {"This is the way."});
|
||||
FetchPageContent(FROM_HERE, expected_string);
|
||||
}
|
||||
|
||||
// Test print preview extraction while print dialog open
|
||||
IN_PROC_BROWSER_TEST_F(AIChatUIBrowserTest, PrintDiaglogExtraction) {
|
||||
NavigateURL(https_server_.GetURL("docs.google.com", "/long_canvas.html"),
|
||||
false);
|
||||
|
||||
printing::TestPrintPreviewObserver print_preview_observer(
|
||||
/*wait_for_loaded=*/true);
|
||||
content::ExecuteScriptAsync(GetActiveWebContents()->GetPrimaryMainFrame(),
|
||||
"window.print();");
|
||||
print_preview_observer.WaitUntilPreviewIsReady();
|
||||
FetchPageContent(
|
||||
FROM_HERE, "This is the way.\n\nI have spoken.\nWherever I Go, He Goes.");
|
||||
}
|
||||
|
||||
// Test print dialog can still be open after print preview extraction
|
||||
IN_PROC_BROWSER_TEST_F(AIChatUIBrowserTest, ExtractionPrintDialog) {
|
||||
NavigateURL(https_server_.GetURL("docs.google.com", "/long_canvas.html"),
|
||||
false);
|
||||
FetchPageContent(
|
||||
FROM_HERE, "This is the way.\n\nI have spoken.\nWherever I Go, He Goes.");
|
||||
|
||||
printing::TestPrintPreviewObserver print_preview_observer(
|
||||
/*wait_for_loaded=*/true);
|
||||
content::ExecuteScriptAsync(GetActiveWebContents()->GetPrimaryMainFrame(),
|
||||
"window.print();");
|
||||
print_preview_observer.WaitUntilPreviewIsReady();
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_TEXT_RECOGNITION) && BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(AIChatUIBrowserTest, PrintPreviewDisabled) {
|
||||
prefs()->SetBoolean(prefs::kPrintPreviewDisabled, true);
|
||||
|
||||
@@ -26,38 +26,19 @@ PrintPreviewExtractor::PrintPreviewExtractor(content::WebContents* web_contents,
|
||||
|
||||
PrintPreviewExtractor::~PrintPreviewExtractor() = default;
|
||||
|
||||
void PrintPreviewExtractor::Extract(ExtractCallback callback) {
|
||||
// Overwrite any existing extraction in progress, cancelling the operation.
|
||||
// If AIChatTabHelper for this WebContents is asking for a new extraction
|
||||
// then it has navigated, or the previous extraction failed to report itself
|
||||
// somehow.
|
||||
extractor_ = create_extractor_callback_.Run(
|
||||
web_contents_, IsPdf(web_contents_),
|
||||
Extractor::CallbackVariant(base::BindOnce(
|
||||
&PrintPreviewExtractor::OnComplete<ExtractCallback, std::string>,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
|
||||
extractor_->CreatePrintPreview();
|
||||
}
|
||||
|
||||
void PrintPreviewExtractor::CapturePdf(CapturePdfCallback callback) {
|
||||
if (!IsPdf(web_contents_)) {
|
||||
std::move(callback).Run(base::unexpected("Not pdf content"));
|
||||
return;
|
||||
}
|
||||
void PrintPreviewExtractor::CaptureImages(CaptureImagesCallback callback) {
|
||||
// Overwrite any existing extraction in progress, cancelling the operation.
|
||||
extractor_ = create_extractor_callback_.Run(
|
||||
web_contents_, IsPdf(web_contents_),
|
||||
Extractor::CallbackVariant(base::BindOnce(
|
||||
&PrintPreviewExtractor::OnComplete<CapturePdfCallback,
|
||||
std::vector<std::vector<uint8_t>>>,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
|
||||
Extractor::ImageCallback(
|
||||
base::BindOnce(&PrintPreviewExtractor::OnComplete,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
|
||||
extractor_->CreatePrintPreview();
|
||||
}
|
||||
|
||||
template <typename CallbackType, typename ResultType>
|
||||
void PrintPreviewExtractor::OnComplete(
|
||||
CallbackType callback,
|
||||
base::expected<ResultType, std::string> result) {
|
||||
Extractor::ImageCallback callback,
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string> result) {
|
||||
extractor_.reset();
|
||||
std::move(callback).Run(std::move(result));
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "brave/components/ai_chat/content/browser/ai_chat_tab_helper.h"
|
||||
@@ -30,11 +29,8 @@ class PrintPreviewExtractor
|
||||
// Performs the print preview extraction. Used only for a single operation.
|
||||
class Extractor {
|
||||
public:
|
||||
using TextCallback =
|
||||
AIChatTabHelper::PrintPreviewExtractionDelegate::ExtractCallback;
|
||||
using ImageCallback =
|
||||
AIChatTabHelper::PrintPreviewExtractionDelegate::CapturePdfCallback;
|
||||
using CallbackVariant = std::variant<TextCallback, ImageCallback>;
|
||||
AIChatTabHelper::PrintPreviewExtractionDelegate::CaptureImagesCallback;
|
||||
|
||||
virtual ~Extractor() = default;
|
||||
virtual void CreatePrintPreview() = 0;
|
||||
@@ -45,22 +41,20 @@ class PrintPreviewExtractor
|
||||
base::RepeatingCallback<std::unique_ptr<Extractor>(
|
||||
content::WebContents* web_contents,
|
||||
bool is_pdf,
|
||||
Extractor::CallbackVariant&&)>;
|
||||
Extractor::ImageCallback&&)>;
|
||||
PrintPreviewExtractor(content::WebContents* web_contents,
|
||||
CreateExtractorCallback callback);
|
||||
~PrintPreviewExtractor() override;
|
||||
PrintPreviewExtractor(const PrintPreviewExtractor&) = delete;
|
||||
PrintPreviewExtractor& operator=(const PrintPreviewExtractor&) = delete;
|
||||
|
||||
void Extract(ExtractCallback callback) override;
|
||||
|
||||
void CapturePdf(CapturePdfCallback callback) override;
|
||||
void CaptureImages(CaptureImagesCallback callback) override;
|
||||
|
||||
private:
|
||||
friend class PrintPreviewExtractorTest;
|
||||
template <typename CallbackType, typename ResultType>
|
||||
void OnComplete(CallbackType callback,
|
||||
base::expected<ResultType, std::string> result);
|
||||
void OnComplete(
|
||||
Extractor::ImageCallback callback,
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string> result);
|
||||
|
||||
CreateExtractorCallback create_extractor_callback_;
|
||||
std::unique_ptr<Extractor> extractor_;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check.h"
|
||||
@@ -18,14 +17,11 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/types/expected.h"
|
||||
#include "brave/browser/ai_chat/print_preview_extractor.h"
|
||||
#include "brave/components/ai_chat/content/browser/ai_chat_tab_helper.h"
|
||||
#include "brave/components/ai_chat/content/browser/pdf_utils.h"
|
||||
#include "brave/components/ai_chat/core/browser/constants.h"
|
||||
#include "brave/components/ai_chat/core/browser/utils.h"
|
||||
#include "brave/components/text_recognition/common/buildflags/buildflags.h"
|
||||
#include "chrome/browser/pdf/pdf_pref_names.h"
|
||||
#include "chrome/browser/printing/print_compositor_util.h"
|
||||
#include "chrome/browser/printing/print_preview_data_service.h"
|
||||
@@ -62,8 +58,6 @@ using printing::mojom::PrintPreviewUI;
|
||||
|
||||
namespace ai_chat {
|
||||
|
||||
using CallbackVariant = PrintPreviewExtractor::Extractor::CallbackVariant;
|
||||
using TextCallback = PrintPreviewExtractor::Extractor::TextCallback;
|
||||
using ImageCallback = PrintPreviewExtractor::Extractor::ImageCallback;
|
||||
|
||||
namespace {
|
||||
@@ -90,13 +84,13 @@ content::RenderFrameHost* GetRenderFrameHostToUse(
|
||||
|
||||
} // namespace
|
||||
|
||||
PreviewPageTextExtractor::PreviewPageTextExtractor() = default;
|
||||
PreviewPageImageExtractor::PreviewPageImageExtractor() = default;
|
||||
|
||||
PreviewPageTextExtractor::~PreviewPageTextExtractor() = default;
|
||||
PreviewPageImageExtractor::~PreviewPageImageExtractor() = default;
|
||||
|
||||
void PreviewPageTextExtractor::StartExtract(
|
||||
void PreviewPageImageExtractor::StartExtract(
|
||||
base::ReadOnlySharedMemoryRegion pdf_region,
|
||||
CallbackVariant callback,
|
||||
ImageCallback callback,
|
||||
std::optional<bool> pdf_use_skia_renderer_enabled) {
|
||||
pdf_region_ = std::move(pdf_region);
|
||||
callback_ = std::move(callback);
|
||||
@@ -105,110 +99,63 @@ void PreviewPageTextExtractor::StartExtract(
|
||||
pdf_to_bitmap_converter_.BindNewPipeAndPassReceiver());
|
||||
}
|
||||
pdf_to_bitmap_converter_.set_disconnect_handler(
|
||||
base::BindOnce(&PreviewPageTextExtractor::BitmapConverterDisconnected,
|
||||
base::BindOnce(&PreviewPageImageExtractor::BitmapConverterDisconnected,
|
||||
base::Unretained(this)));
|
||||
if (pdf_use_skia_renderer_enabled.has_value()) {
|
||||
pdf_to_bitmap_converter_->SetUseSkiaRendererPolicy(
|
||||
pdf_use_skia_renderer_enabled.value());
|
||||
}
|
||||
current_page_index_ = 0;
|
||||
preview_text_.clear();
|
||||
// No text preview to clear for image extraction
|
||||
pdf_to_bitmap_converter_->GetPdfPageCount(
|
||||
pdf_region_.Duplicate(),
|
||||
base::BindOnce(&PreviewPageTextExtractor::OnGetPageCount,
|
||||
base::BindOnce(&PreviewPageImageExtractor::OnGetPageCount,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::BindForTesting(
|
||||
void PreviewPageImageExtractor::BindForTesting(
|
||||
mojo::PendingRemote<printing::mojom::PdfToBitmapConverter> converter) {
|
||||
CHECK_IS_TEST();
|
||||
pdf_to_bitmap_converter_.Bind(std::move(converter));
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::ScheduleNextPageOrComplete() {
|
||||
void PreviewPageImageExtractor::ScheduleNextPageOrComplete() {
|
||||
DCHECK_GT(total_page_count_, 0u);
|
||||
if (current_page_index_ < total_page_count_) {
|
||||
if (current_page_index_ &&
|
||||
std::holds_alternative<TextCallback>(callback_)) {
|
||||
base::StrAppend(&preview_text_, {"\n"});
|
||||
}
|
||||
pdf_to_bitmap_converter_->GetBitmap(
|
||||
pdf_region_.Duplicate(), current_page_index_,
|
||||
base::BindOnce(&PreviewPageTextExtractor::OnGetBitmap,
|
||||
base::BindOnce(&PreviewPageImageExtractor::OnGetBitmap,
|
||||
base::Unretained(this)));
|
||||
} else {
|
||||
if (auto* text_cb = std::get_if<TextCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(base::ok(preview_text_));
|
||||
} else if (auto* image_cb = std::get_if<ImageCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(base::ok(std::move(pdf_pages_image_data_)));
|
||||
}
|
||||
std::move(callback_).Run(base::ok(std::move(pdf_pages_image_data_)));
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::OnGetPageCount(
|
||||
void PreviewPageImageExtractor::OnGetPageCount(
|
||||
std::optional<uint32_t> page_count) {
|
||||
if (!page_count.has_value() || !page_count.value()) {
|
||||
if (auto* text_cb = std::get_if<TextCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(base::unexpected("Failed to get page count"));
|
||||
} else if (auto* image_cb = std::get_if<ImageCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(base::unexpected("Failed to get page count"));
|
||||
}
|
||||
std::move(callback_).Run(base::unexpected("Failed to get page count"));
|
||||
return;
|
||||
}
|
||||
total_page_count_ = page_count.value();
|
||||
ScheduleNextPageOrComplete();
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::OnGetBitmap(const SkBitmap& bitmap) {
|
||||
void PreviewPageImageExtractor::OnGetBitmap(const SkBitmap& bitmap) {
|
||||
if (bitmap.drawsNothing()) {
|
||||
if (auto* text_cb = std::get_if<TextCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(base::unexpected("Invalid bitmap"));
|
||||
} else if (auto* image_cb = std::get_if<ImageCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(base::unexpected("Invalid bitmap"));
|
||||
}
|
||||
std::move(callback_).Run(base::unexpected("Invalid bitmap"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<ImageCallback>(callback_)) {
|
||||
ProcessNextBitmapPage(bitmap);
|
||||
} else if (std::holds_alternative<TextCallback>(callback_)) {
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
GetOCRText(bitmap,
|
||||
base::BindOnce(&PreviewPageTextExtractor::ProcessNextTextPage,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
#else
|
||||
auto* text_cb = std::get_if<TextCallback>(&callback_);
|
||||
std::move(*text_cb).Run(base::ok(""));
|
||||
#endif
|
||||
}
|
||||
ProcessNextBitmapPage(bitmap);
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::ProcessNextTextPage(std::string page_content) {
|
||||
auto* text_cb = std::get_if<TextCallback>(&callback_);
|
||||
DCHECK(text_cb);
|
||||
VLOG(4) << "Page index(" << current_page_index_
|
||||
<< ") content: " << page_content;
|
||||
base::StrAppend(&preview_text_, {page_content});
|
||||
|
||||
// Stop processing if we have reached the maximum number of pages
|
||||
if (current_page_index_ + 1 >= kMaxPreviewPages) {
|
||||
std::move(*text_cb).Run(base::ok(preview_text_));
|
||||
return;
|
||||
}
|
||||
|
||||
++current_page_index_;
|
||||
ScheduleNextPageOrComplete();
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::ProcessNextBitmapPage(const SkBitmap& bitmap) {
|
||||
auto* image_cb = std::get_if<ImageCallback>(&callback_);
|
||||
DCHECK(image_cb);
|
||||
|
||||
void PreviewPageImageExtractor::ProcessNextBitmapPage(const SkBitmap& bitmap) {
|
||||
// Encode bitmap to PNG for capture
|
||||
auto png_data =
|
||||
gfx::PNGCodec::EncodeBGRASkBitmap(ScaleDownBitmap(bitmap), false);
|
||||
if (!png_data) {
|
||||
std::move(*image_cb).Run(base::unexpected("Failed to encode the bitmap"));
|
||||
std::move(callback_).Run(base::unexpected("Failed to encode the bitmap"));
|
||||
return;
|
||||
} else {
|
||||
pdf_pages_image_data_.push_back(*png_data);
|
||||
@@ -218,19 +165,15 @@ void PreviewPageTextExtractor::ProcessNextBitmapPage(const SkBitmap& bitmap) {
|
||||
ScheduleNextPageOrComplete();
|
||||
}
|
||||
|
||||
void PreviewPageTextExtractor::BitmapConverterDisconnected() {
|
||||
if (auto* text_cb = std::get_if<TextCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(base::unexpected("Bitmap converter disconnected"));
|
||||
} else if (auto* image_cb = std::get_if<ImageCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(base::unexpected("Bitmap converter disconnected"));
|
||||
}
|
||||
void PreviewPageImageExtractor::BitmapConverterDisconnected() {
|
||||
std::move(callback_).Run(base::unexpected("Bitmap converter disconnected"));
|
||||
}
|
||||
|
||||
PrintPreviewExtractorInternal::PrintPreviewExtractorInternal(
|
||||
content::WebContents* web_contents,
|
||||
Profile* profile,
|
||||
bool is_pdf,
|
||||
CallbackVariant callback,
|
||||
ImageCallback callback,
|
||||
GetPrintPreviewUIIdMapCallback id_map_callback,
|
||||
GetPrintPreviewUIRequestIdMapCallback request_id_map_callback)
|
||||
: is_pdf_(is_pdf),
|
||||
@@ -320,19 +263,15 @@ PrintPreviewExtractorInternal::GetPrintPreviewUIIdForTesting() {
|
||||
return print_preview_ui_id_;
|
||||
}
|
||||
|
||||
void PrintPreviewExtractorInternal::SetPreviewPageTextExtractorForTesting(
|
||||
std::unique_ptr<PreviewPageTextExtractor> extractor) {
|
||||
void PrintPreviewExtractorInternal::SetPreviewPageImageExtractorForTesting(
|
||||
std::unique_ptr<PreviewPageImageExtractor> extractor) {
|
||||
CHECK_IS_TEST();
|
||||
preview_page_text_extractor_ = std::move(extractor);
|
||||
preview_page_image_extractor_ = std::move(extractor);
|
||||
}
|
||||
|
||||
void PrintPreviewExtractorInternal::SendError(const std::string& error) {
|
||||
PreviewCleanup();
|
||||
if (auto* text_cb = std::get_if<ExtractCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(base::unexpected(error));
|
||||
} else if (auto* image_cb = std::get_if<CapturePdfCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(base::unexpected(error));
|
||||
}
|
||||
std::move(callback_).Run(base::unexpected(error));
|
||||
}
|
||||
|
||||
void PrintPreviewExtractorInternal::SetOptionsFromDocument(
|
||||
@@ -584,44 +523,22 @@ void PrintPreviewExtractorInternal::OnPreviewReady() {
|
||||
prefs->GetBoolean(::prefs::kPdfUseSkiaRendererEnabled);
|
||||
}
|
||||
|
||||
// Create the appropriate callback based on the variant type
|
||||
CallbackVariant callback;
|
||||
if (std::holds_alternative<ExtractCallback>(callback_)) {
|
||||
callback = base::BindOnce(&PrintPreviewExtractorInternal::OnGetOCRResult,
|
||||
weak_ptr_factory_.GetWeakPtr());
|
||||
} else if (std::holds_alternative<CapturePdfCallback>(callback_)) {
|
||||
callback =
|
||||
base::BindOnce(&PrintPreviewExtractorInternal::OnCaptureBitmapResult,
|
||||
weak_ptr_factory_.GetWeakPtr());
|
||||
}
|
||||
|
||||
if (!preview_page_text_extractor_) {
|
||||
preview_page_text_extractor_ = std::make_unique<PreviewPageTextExtractor>();
|
||||
}
|
||||
preview_page_text_extractor_->StartExtract(std::move(pdf_region.region),
|
||||
std::move(callback),
|
||||
pdf_use_skia_renderer_enabled);
|
||||
}
|
||||
|
||||
void PrintPreviewExtractorInternal::OnGetOCRResult(
|
||||
base::expected<std::string, std::string> result) {
|
||||
if (result.has_value()) {
|
||||
PreviewCleanup();
|
||||
if (auto* text_cb = std::get_if<ExtractCallback>(&callback_)) {
|
||||
std::move(*text_cb).Run(std::move(result));
|
||||
}
|
||||
} else {
|
||||
SendError(result.error());
|
||||
if (!preview_page_image_extractor_) {
|
||||
preview_page_image_extractor_ =
|
||||
std::make_unique<PreviewPageImageExtractor>();
|
||||
}
|
||||
preview_page_image_extractor_->StartExtract(
|
||||
std::move(pdf_region.region),
|
||||
base::BindOnce(&PrintPreviewExtractorInternal::OnCaptureBitmapResult,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
pdf_use_skia_renderer_enabled);
|
||||
}
|
||||
|
||||
void PrintPreviewExtractorInternal::OnCaptureBitmapResult(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string> result) {
|
||||
if (result.has_value()) {
|
||||
PreviewCleanup();
|
||||
if (auto* image_cb = std::get_if<CapturePdfCallback>(&callback_)) {
|
||||
std::move(*image_cb).Run(std::move(result));
|
||||
}
|
||||
std::move(callback_).Run(std::move(result));
|
||||
} else {
|
||||
SendError(result.error());
|
||||
}
|
||||
|
||||
@@ -38,17 +38,16 @@ static_assert(BUILDFLAG(ENABLE_PRINT_PREVIEW));
|
||||
|
||||
namespace ai_chat {
|
||||
|
||||
using ExtractCallback = PrintPreviewExtractor::ExtractCallback;
|
||||
using CapturePdfCallback = PrintPreviewExtractor::CapturePdfCallback;
|
||||
using CaptureImagesCallback = PrintPreviewExtractor::CaptureImagesCallback;
|
||||
|
||||
class PreviewPageTextExtractor {
|
||||
class PreviewPageImageExtractor {
|
||||
public:
|
||||
PreviewPageTextExtractor();
|
||||
virtual ~PreviewPageTextExtractor();
|
||||
PreviewPageImageExtractor();
|
||||
virtual ~PreviewPageImageExtractor();
|
||||
|
||||
virtual void StartExtract(
|
||||
base::ReadOnlySharedMemoryRegion pdf_region,
|
||||
PrintPreviewExtractor::Extractor::CallbackVariant callback,
|
||||
PrintPreviewExtractor::Extractor::ImageCallback callback,
|
||||
std::optional<bool> pdf_use_skia_renderer_enabled);
|
||||
|
||||
void BindForTesting(
|
||||
@@ -58,19 +57,17 @@ class PreviewPageTextExtractor {
|
||||
void ScheduleNextPageOrComplete();
|
||||
void OnGetPageCount(std::optional<uint32_t> page_count);
|
||||
void OnGetBitmap(const SkBitmap& bitmap);
|
||||
void ProcessNextTextPage(std::string page_content);
|
||||
void ProcessNextBitmapPage(const SkBitmap& bitmap);
|
||||
void BitmapConverterDisconnected();
|
||||
|
||||
std::string preview_text_;
|
||||
size_t current_page_index_ = 0;
|
||||
size_t total_page_count_ = 0;
|
||||
base::ReadOnlySharedMemoryRegion pdf_region_;
|
||||
PrintPreviewExtractor::Extractor::CallbackVariant callback_;
|
||||
PrintPreviewExtractor::Extractor::ImageCallback callback_;
|
||||
// raw bytes data of captured pdf pages
|
||||
std::vector<std::vector<uint8_t>> pdf_pages_image_data_;
|
||||
mojo::Remote<printing::mojom::PdfToBitmapConverter> pdf_to_bitmap_converter_;
|
||||
base::WeakPtrFactory<PreviewPageTextExtractor> weak_ptr_factory_{this};
|
||||
base::WeakPtrFactory<PreviewPageImageExtractor> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
class PrintPreviewExtractorInternal : public PrintPreviewExtractor::Extractor,
|
||||
@@ -84,7 +81,7 @@ class PrintPreviewExtractorInternal : public PrintPreviewExtractor::Extractor,
|
||||
content::WebContents* web_contents,
|
||||
Profile* profile,
|
||||
bool is_pdf,
|
||||
PrintPreviewExtractor::Extractor::CallbackVariant callback,
|
||||
PrintPreviewExtractor::Extractor::ImageCallback callback,
|
||||
GetPrintPreviewUIIdMapCallback id_map_callback,
|
||||
GetPrintPreviewUIRequestIdMapCallback request_id_map_callback);
|
||||
|
||||
@@ -98,8 +95,8 @@ class PrintPreviewExtractorInternal : public PrintPreviewExtractor::Extractor,
|
||||
|
||||
std::optional<int32_t> GetPrintPreviewUIIdForTesting() override;
|
||||
|
||||
void SetPreviewPageTextExtractorForTesting(
|
||||
std::unique_ptr<PreviewPageTextExtractor> extractor);
|
||||
void SetPreviewPageImageExtractorForTesting(
|
||||
std::unique_ptr<PreviewPageImageExtractor> extractor);
|
||||
|
||||
void SendError(const std::string& error);
|
||||
|
||||
@@ -167,14 +164,12 @@ class PrintPreviewExtractorInternal : public PrintPreviewExtractor::Extractor,
|
||||
|
||||
void OnPreviewReady();
|
||||
|
||||
void OnGetOCRResult(base::expected<std::string, std::string> result);
|
||||
|
||||
void OnCaptureBitmapResult(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string> result);
|
||||
|
||||
private:
|
||||
bool is_pdf_ = false;
|
||||
PrintPreviewExtractor::Extractor::CallbackVariant callback_;
|
||||
PrintPreviewExtractor::Extractor::ImageCallback callback_;
|
||||
GetPrintPreviewUIIdMapCallback id_map_callback_;
|
||||
GetPrintPreviewUIRequestIdMapCallback request_id_map_callback_;
|
||||
// unique id to avoid conflicts with other print preview UIs
|
||||
@@ -182,7 +177,7 @@ class PrintPreviewExtractorInternal : public PrintPreviewExtractor::Extractor,
|
||||
mojo::AssociatedReceiver<PrintPreviewUI> print_preview_ui_receiver_{this};
|
||||
|
||||
int preview_request_id_ = -1;
|
||||
std::unique_ptr<PreviewPageTextExtractor> preview_page_text_extractor_;
|
||||
std::unique_ptr<PreviewPageImageExtractor> preview_page_image_extractor_;
|
||||
mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> print_render_frame_;
|
||||
|
||||
raw_ptr<content::WebContents> web_contents_ = nullptr;
|
||||
|
||||
@@ -148,45 +148,33 @@ class MockPrintPreviewPrintRenderFrame
|
||||
mojo::AssociatedReceiver<printing::mojom::PrintRenderFrame> receiver_{this};
|
||||
};
|
||||
|
||||
class MockPreviewPageTextExtractor : public PreviewPageTextExtractor {
|
||||
class MockPreviewPageImageExtractor : public PreviewPageImageExtractor {
|
||||
public:
|
||||
MockPreviewPageTextExtractor(base::ReadOnlySharedMemoryRegion expected_region,
|
||||
bool expected_error)
|
||||
MockPreviewPageImageExtractor(
|
||||
base::ReadOnlySharedMemoryRegion expected_region,
|
||||
bool expected_error)
|
||||
: expected_region_(std::move(expected_region)),
|
||||
expected_error_(expected_error) {}
|
||||
~MockPreviewPageTextExtractor() override = default;
|
||||
~MockPreviewPageImageExtractor() override = default;
|
||||
|
||||
MockPreviewPageTextExtractor(const MockPreviewPageTextExtractor&) = delete;
|
||||
MockPreviewPageTextExtractor& operator=(const MockPreviewPageTextExtractor&) =
|
||||
delete;
|
||||
MockPreviewPageImageExtractor(const MockPreviewPageImageExtractor&) = delete;
|
||||
MockPreviewPageImageExtractor& operator=(
|
||||
const MockPreviewPageImageExtractor&) = delete;
|
||||
|
||||
void StartExtract(
|
||||
base::ReadOnlySharedMemoryRegion pdf_region,
|
||||
PrintPreviewExtractor::Extractor::CallbackVariant callback,
|
||||
PrintPreviewExtractor::Extractor::ImageCallback callback,
|
||||
std::optional<bool> pdf_use_skia_renderer_enabled) override {
|
||||
// verify the correct memory region is passed
|
||||
EXPECT_EQ(pdf_region.Map().GetMemoryAsSpan<const uint8_t>(),
|
||||
expected_region_.Map().GetMemoryAsSpan<const uint8_t>());
|
||||
|
||||
if (auto* text_cb =
|
||||
std::get_if<PrintPreviewExtractor::Extractor::TextCallback>(
|
||||
&callback)) {
|
||||
if (!expected_error_) {
|
||||
std::move(*text_cb).Run(base::ok("extracted text"));
|
||||
} else {
|
||||
std::move(*text_cb).Run(
|
||||
base::unexpected("PreviewPageTextExtractor error"));
|
||||
}
|
||||
} else if (auto* image_cb =
|
||||
std::get_if<PrintPreviewExtractor::Extractor::ImageCallback>(
|
||||
&callback)) {
|
||||
if (!expected_error_) {
|
||||
std::vector<std::vector<uint8_t>> result = {{0xde, 0xad}, {0xbe, 0xef}};
|
||||
std::move(*image_cb).Run(base::ok(std::move(result)));
|
||||
} else {
|
||||
std::move(*image_cb).Run(
|
||||
base::unexpected("PreviewPageTextExtractor error"));
|
||||
}
|
||||
if (!expected_error_) {
|
||||
std::vector<std::vector<uint8_t>> result = {{0xde, 0xad}, {0xbe, 0xef}};
|
||||
std::move(callback).Run(base::ok(std::move(result)));
|
||||
} else {
|
||||
std::move(callback).Run(
|
||||
base::unexpected("PreviewPageImageExtractor error"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +232,6 @@ class MockPdfToBitmapConverter : public printing::mojom::PdfToBitmapConverter {
|
||||
|
||||
using ImageResult =
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>;
|
||||
using TextResult = base::expected<std::string, std::string>;
|
||||
|
||||
class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
public:
|
||||
@@ -265,13 +252,13 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
web_contents(),
|
||||
base::BindRepeating([](content::WebContents* web_contents, bool is_pdf,
|
||||
ai_chat::PrintPreviewExtractor::Extractor::
|
||||
CallbackVariant&& variant)
|
||||
ImageCallback&& callback)
|
||||
-> std::unique_ptr<
|
||||
ai_chat::PrintPreviewExtractor::Extractor> {
|
||||
return std::make_unique<ai_chat::PrintPreviewExtractorInternal>(
|
||||
web_contents,
|
||||
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
|
||||
is_pdf, std::move(variant),
|
||||
is_pdf, std::move(callback),
|
||||
base::BindRepeating(
|
||||
[]() -> base::IDMap<printing::mojom::PrintPreviewUI*>& {
|
||||
return printing::PrintPreviewUI::GetPrintPreviewUIIdMap();
|
||||
@@ -321,7 +308,6 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
// Intentionally setup a PrintRenderFrame error to stop early so we can
|
||||
// verify the print settings we pass is correct.
|
||||
void RunPrintSettingsTest(const std::string& mime_type,
|
||||
bool use_capture_pdf,
|
||||
bool expect_preview_modifiable,
|
||||
const base::Location& location = FROM_HERE) {
|
||||
SCOPED_TRACE(location.ToString());
|
||||
@@ -374,21 +360,13 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewFailed,
|
||||
std::move(on_complete));
|
||||
|
||||
if (use_capture_pdf) {
|
||||
base::test::TestFuture<
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>>
|
||||
future;
|
||||
pp_extractor_->CapturePdf(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), "PrintPreviewFailed");
|
||||
} else {
|
||||
base::test::TestFuture<base::expected<std::string, std::string>> future;
|
||||
pp_extractor_->Extract(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), "PrintPreviewFailed");
|
||||
}
|
||||
base::test::TestFuture<
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>>
|
||||
future;
|
||||
pp_extractor_->CaptureImages(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), "PrintPreviewFailed");
|
||||
|
||||
EXPECT_TRUE(
|
||||
printing::PrintPreviewUI::GetPrintPreviewUIRequestIdMap().empty());
|
||||
@@ -414,7 +392,6 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
// Test for all possible PrintRenderFrame errors
|
||||
void RunErrorTest(
|
||||
const std::string& mime_type,
|
||||
bool use_capture_pdf,
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError expected_error,
|
||||
const std::string& expected_error_message,
|
||||
const base::Location& location = FROM_HERE) {
|
||||
@@ -422,21 +399,13 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
|
||||
auto print_render_frame = SetupPrintPreviewTest(mime_type, expected_error);
|
||||
|
||||
if (use_capture_pdf) {
|
||||
base::test::TestFuture<
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>>
|
||||
future;
|
||||
pp_extractor_->CapturePdf(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), expected_error_message);
|
||||
} else {
|
||||
base::test::TestFuture<base::expected<std::string, std::string>> future;
|
||||
pp_extractor_->Extract(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), expected_error_message);
|
||||
}
|
||||
base::test::TestFuture<
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>>
|
||||
future;
|
||||
pp_extractor_->CaptureImages(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), expected_error_message);
|
||||
|
||||
EXPECT_TRUE(
|
||||
printing::PrintPreviewUI::GetPrintPreviewUIRequestIdMap().empty());
|
||||
@@ -445,8 +414,8 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
|
||||
void SimulateFullFlow(bool extractor_error) {
|
||||
auto full_pdf_region = CreatePageRegion(64).region;
|
||||
internal_extractor()->SetPreviewPageTextExtractorForTesting(
|
||||
std::make_unique<MockPreviewPageTextExtractor>(
|
||||
internal_extractor()->SetPreviewPageImageExtractorForTesting(
|
||||
std::make_unique<MockPreviewPageImageExtractor>(
|
||||
full_pdf_region.Duplicate(), extractor_error));
|
||||
|
||||
// Simulate page composition
|
||||
@@ -461,12 +430,11 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
full_pdf_region.Duplicate());
|
||||
}
|
||||
|
||||
// Test all possible flows with mocked PreviewPageTextExtractor that retruns
|
||||
// Test all possible flows with mocked PreviewPageImageExtractor that returns
|
||||
// fixed successful results.
|
||||
template <typename T>
|
||||
void RunTestCase(base::test::TestFuture<T>& future,
|
||||
const std::string& mime_type,
|
||||
bool use_capture_pdf,
|
||||
const std::string& expected_error_msg,
|
||||
bool extractor_error,
|
||||
bool simulate_partial_composition) {
|
||||
@@ -496,13 +464,11 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
if (expect_error) {
|
||||
ASSERT_FALSE(result.has_value())
|
||||
<< "Expected error for mime_type=" << mime_type
|
||||
<< ", use_capture_pdf=" << use_capture_pdf
|
||||
<< ", partial_composition=" << simulate_partial_composition;
|
||||
EXPECT_EQ(result.error(), expected_error_msg);
|
||||
} else {
|
||||
ASSERT_TRUE(result.has_value())
|
||||
<< "Expected success for mime_type=" << mime_type
|
||||
<< ", use_capture_pdf=" << use_capture_pdf;
|
||||
<< "Expected success for mime_type=" << mime_type;
|
||||
if constexpr (std::is_same_v<T, ImageResult>) {
|
||||
const std::vector<std::vector<uint8_t>> expected = {{0xde, 0xad},
|
||||
{0xbe, 0xef}};
|
||||
@@ -522,67 +488,65 @@ class PrintPreviewExtractorTest : public ChromeRenderViewHostTestHarness {
|
||||
std::unique_ptr<PrintPreviewExtractor> pp_extractor_;
|
||||
};
|
||||
|
||||
TEST_F(PrintPreviewExtractorTest, CapturePdfWithNotPdf) {
|
||||
TEST_F(PrintPreviewExtractorTest, CaptureImagesWithNonPdf) {
|
||||
content::WebContentsTester::For(web_contents())
|
||||
->SetMainFrameMimeType("text/html");
|
||||
|
||||
auto print_render_frame = SetupPrintPreviewTest(
|
||||
"text/html",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewFailed);
|
||||
|
||||
base::test::TestFuture<
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>>
|
||||
future;
|
||||
pp_extractor_->CapturePdf(future.GetCallback());
|
||||
pp_extractor_->CaptureImages(future.GetCallback());
|
||||
auto result = future.Take();
|
||||
ASSERT_FALSE(result.has_value());
|
||||
EXPECT_EQ(result.error(), "Not pdf content");
|
||||
EXPECT_EQ(result.error(), "PrintPreviewFailed");
|
||||
}
|
||||
|
||||
TEST_F(PrintPreviewExtractorTest, PrintSettings) {
|
||||
// Test with non-PDF content using Extract()
|
||||
RunPrintSettingsTest("text/html", false, true);
|
||||
// Test with non-PDF content
|
||||
RunPrintSettingsTest("text/html", true);
|
||||
|
||||
// Test with PDF content using Extract()
|
||||
RunPrintSettingsTest("application/pdf", false, false);
|
||||
|
||||
// Test with PDF content using CapturePdf()
|
||||
RunPrintSettingsTest("application/pdf", true, false);
|
||||
// Test with PDF content using CaptureImages()
|
||||
RunPrintSettingsTest("application/pdf", false);
|
||||
}
|
||||
|
||||
TEST_F(PrintPreviewExtractorTest, Errors) {
|
||||
// Test Extract() with various errors
|
||||
// Test CaptureImages() with various errors
|
||||
RunErrorTest(
|
||||
"text/html", false,
|
||||
"text/html",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewFailed,
|
||||
"PrintPreviewFailed");
|
||||
RunErrorTest(
|
||||
"text/html", false,
|
||||
"text/html",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewCanceled,
|
||||
"PrintPreviewCancelled");
|
||||
RunErrorTest(
|
||||
"text/html", false,
|
||||
"text/html",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrinterSettingsInvalid,
|
||||
"PrinterSettingsInvalid");
|
||||
|
||||
// Test CapturePdf() with various errors
|
||||
RunErrorTest(
|
||||
"application/pdf", true,
|
||||
"application/pdf",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewFailed,
|
||||
"PrintPreviewFailed");
|
||||
RunErrorTest(
|
||||
"application/pdf", true,
|
||||
"application/pdf",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrintPreviewCanceled,
|
||||
"PrintPreviewCancelled");
|
||||
RunErrorTest(
|
||||
"application/pdf", true,
|
||||
"application/pdf",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kPrinterSettingsInvalid,
|
||||
"PrinterSettingsInvalid");
|
||||
|
||||
// Test disabled print preview
|
||||
profile()->GetPrefs()->SetBoolean(::prefs::kPrintPreviewDisabled, true);
|
||||
RunErrorTest("application/pdf", true,
|
||||
RunErrorTest("application/pdf",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kNone,
|
||||
"Print preview is disabled");
|
||||
RunErrorTest("text/html", false,
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kNone,
|
||||
"Print preview is disabled");
|
||||
RunErrorTest("application/pdf", false,
|
||||
RunErrorTest("text/html",
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kNone,
|
||||
"Print preview is disabled");
|
||||
}
|
||||
@@ -590,7 +554,6 @@ TEST_F(PrintPreviewExtractorTest, Errors) {
|
||||
TEST_F(PrintPreviewExtractorTest, PrintPreviewData) {
|
||||
struct TestParams {
|
||||
std::string mime_type;
|
||||
bool use_capture_pdf;
|
||||
std::string expected_error_msg; // Empty string means success
|
||||
bool extractor_error;
|
||||
bool simulate_partial_composition;
|
||||
@@ -598,21 +561,21 @@ TEST_F(PrintPreviewExtractorTest, PrintPreviewData) {
|
||||
|
||||
const TestParams kTestCases[] = {
|
||||
// Missing preview data cases - error on OnCompositeToPdfDone
|
||||
{"text/html", false, "Failed to get preview data", false, false},
|
||||
{"application/pdf", true, "Failed to get preview data", false, false},
|
||||
{"text/html", "Failed to get preview data", false, false},
|
||||
{"application/pdf", "Failed to get preview data", false, false},
|
||||
|
||||
// Missing preview data cases - missing OnCompositePdfPageDone and error
|
||||
// on OnCompositeToPdfDone
|
||||
{"text/html", false, "Failed to get preview data", false, true},
|
||||
{"application/pdf", true, "Failed to get preview data", false, true},
|
||||
{"text/html", "Failed to get preview data", false, true},
|
||||
{"application/pdf", "Failed to get preview data", false, true},
|
||||
|
||||
// Successful extraction cases
|
||||
{"text/html", false, "", false, false},
|
||||
{"application/pdf", true, "", false, false},
|
||||
{"text/html", "", false, false},
|
||||
{"application/pdf", "", false, false},
|
||||
|
||||
// Extraction error cases
|
||||
{"text/html", false, "PreviewPageTextExtractor error", true, false},
|
||||
{"application/pdf", true, "PreviewPageTextExtractor error", true, false},
|
||||
{"text/html", "PreviewPageImageExtractor error", true, false},
|
||||
{"application/pdf", "PreviewPageImageExtractor error", true, false},
|
||||
};
|
||||
|
||||
for (const auto& test_case : kTestCases) {
|
||||
@@ -620,34 +583,26 @@ TEST_F(PrintPreviewExtractorTest, PrintPreviewData) {
|
||||
test_case.mime_type,
|
||||
MockPrintPreviewPrintRenderFrame::ExpectedError::kNone);
|
||||
|
||||
if (test_case.use_capture_pdf) {
|
||||
base::test::TestFuture<ImageResult> future;
|
||||
pp_extractor_->CapturePdf(future.GetCallback());
|
||||
RunTestCase(future, test_case.mime_type, test_case.use_capture_pdf,
|
||||
test_case.expected_error_msg, test_case.extractor_error,
|
||||
test_case.simulate_partial_composition);
|
||||
} else {
|
||||
base::test::TestFuture<TextResult> future;
|
||||
pp_extractor_->Extract(future.GetCallback());
|
||||
RunTestCase(future, test_case.mime_type, test_case.use_capture_pdf,
|
||||
test_case.expected_error_msg, test_case.extractor_error,
|
||||
test_case.simulate_partial_composition);
|
||||
}
|
||||
base::test::TestFuture<ImageResult> future;
|
||||
pp_extractor_->CaptureImages(future.GetCallback());
|
||||
RunTestCase(future, test_case.mime_type, test_case.expected_error_msg,
|
||||
test_case.extractor_error,
|
||||
test_case.simulate_partial_composition);
|
||||
}
|
||||
}
|
||||
|
||||
class PreviewPageTextExtractorTest : public testing::Test {
|
||||
class PreviewPageImageExtractorTest : public testing::Test {
|
||||
public:
|
||||
PreviewPageTextExtractorTest() = default;
|
||||
~PreviewPageTextExtractorTest() override = default;
|
||||
PreviewPageImageExtractorTest() = default;
|
||||
~PreviewPageImageExtractorTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
extractor_ = std::make_unique<PreviewPageTextExtractor>();
|
||||
extractor_ = std::make_unique<PreviewPageImageExtractor>();
|
||||
converter_ = std::make_unique<MockPdfToBitmapConverter>();
|
||||
extractor()->BindForTesting(converter()->Bind());
|
||||
}
|
||||
|
||||
PreviewPageTextExtractor* extractor() { return extractor_.get(); }
|
||||
PreviewPageImageExtractor* extractor() { return extractor_.get(); }
|
||||
|
||||
MockPdfToBitmapConverter* converter() { return converter_.get(); }
|
||||
|
||||
@@ -664,7 +619,7 @@ class PreviewPageTextExtractorTest : public testing::Test {
|
||||
EXPECT_EQ(result.error(), expected_error);
|
||||
}
|
||||
{
|
||||
base::test::TestFuture<TextResult> future;
|
||||
base::test::TestFuture<ImageResult> future;
|
||||
extractor()->StartExtract(CreatePageRegion(50).region,
|
||||
future.GetCallback(), std::nullopt);
|
||||
auto result = future.Take();
|
||||
@@ -690,47 +645,18 @@ class PreviewPageTextExtractorTest : public testing::Test {
|
||||
}));
|
||||
}
|
||||
|
||||
// Testing text extraction with page count and inspect page separators are
|
||||
// placed correctly or none for single page.
|
||||
void RunExtractTextTest(uint32_t page_count,
|
||||
const base::Location& location = FROM_HERE) {
|
||||
SCOPED_TRACE(location.ToString());
|
||||
converter()->SetExpectedPageCount(page_count);
|
||||
base::test::TestFuture<TextResult> future;
|
||||
extractor()->StartExtract(CreatePageRegion(50).region, future.GetCallback(),
|
||||
std::nullopt);
|
||||
auto result = future.Take();
|
||||
ASSERT_TRUE(result.has_value());
|
||||
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
// OCR will fail intentionally so we are testing if each page is processed
|
||||
// as expected.
|
||||
std::string expected;
|
||||
if (page_count == 1) {
|
||||
expected = "";
|
||||
} else if (page_count > kMaxPreviewPages) {
|
||||
expected = std::string(kMaxPreviewPages - 1, '\n');
|
||||
} else {
|
||||
expected = std::string(page_count - 1, '\n');
|
||||
}
|
||||
EXPECT_EQ(result.value(), expected);
|
||||
#else
|
||||
EXPECT_EQ(result.value(), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
std::unique_ptr<PreviewPageTextExtractor> extractor_;
|
||||
std::unique_ptr<PreviewPageImageExtractor> extractor_;
|
||||
std::unique_ptr<MockPdfToBitmapConverter> converter_;
|
||||
};
|
||||
|
||||
TEST_F(PreviewPageTextExtractorTest, GetPdfPageCountError) {
|
||||
TEST_F(PreviewPageImageExtractorTest, GetPdfPageCountError) {
|
||||
converter()->SetExpectedPageCount(std::nullopt);
|
||||
RunErrorTest("Failed to get page count");
|
||||
}
|
||||
|
||||
TEST_F(PreviewPageTextExtractorTest, GetBitmapError) {
|
||||
TEST_F(PreviewPageImageExtractorTest, GetBitmapError) {
|
||||
converter()->SetExpectedPageCount(1);
|
||||
converter()->SetExpectedEmptyBitmap(true);
|
||||
RunErrorTest("Invalid bitmap");
|
||||
@@ -739,7 +665,7 @@ TEST_F(PreviewPageTextExtractorTest, GetBitmapError) {
|
||||
RunErrorTest("Invalid bitmap");
|
||||
}
|
||||
|
||||
TEST_F(PreviewPageTextExtractorTest, CaptureImages) {
|
||||
TEST_F(PreviewPageImageExtractorTest, CaptureImages) {
|
||||
converter()->SetExpectedEmptyBitmap(false);
|
||||
|
||||
// Test with single page
|
||||
@@ -748,30 +674,11 @@ TEST_F(PreviewPageTextExtractorTest, CaptureImages) {
|
||||
// Test with multiple pages
|
||||
RunCaptureImageTest(3);
|
||||
|
||||
// Test with max pages
|
||||
RunCaptureImageTest(kMaxPreviewPages);
|
||||
// Test with many pages
|
||||
RunCaptureImageTest(20);
|
||||
|
||||
// Test exceeding max pages
|
||||
RunCaptureImageTest(kMaxPreviewPages + 1);
|
||||
}
|
||||
|
||||
TEST_F(PreviewPageTextExtractorTest, ExtractText) {
|
||||
converter()->SetExpectedEmptyBitmap(false);
|
||||
|
||||
// Test single page (empty string expected)
|
||||
RunExtractTextTest(1);
|
||||
|
||||
// Test two pages (one newline)
|
||||
RunExtractTextTest(2);
|
||||
|
||||
// Test max pages
|
||||
RunExtractTextTest(kMaxPreviewPages);
|
||||
|
||||
// Test less than max pages
|
||||
RunExtractTextTest(kMaxPreviewPages - 1);
|
||||
|
||||
// Test exceeding max pages (should be capped)
|
||||
RunExtractTextTest(kMaxPreviewPages + 1);
|
||||
// Test with even more pages
|
||||
RunCaptureImageTest(25);
|
||||
}
|
||||
|
||||
} // namespace ai_chat
|
||||
|
||||
@@ -135,8 +135,8 @@ void AttachTabHelpers(content::WebContents* web_contents) {
|
||||
web_contents,
|
||||
base::BindRepeating(
|
||||
[](content::WebContents* web_contents, bool is_pdf,
|
||||
ai_chat::PrintPreviewExtractor::Extractor::CallbackVariant&&
|
||||
variant)
|
||||
ai_chat::PrintPreviewExtractor::Extractor::ImageCallback&&
|
||||
callback)
|
||||
-> std::unique_ptr<
|
||||
ai_chat::PrintPreviewExtractor::Extractor> {
|
||||
return std::make_unique<
|
||||
@@ -144,7 +144,7 @@ void AttachTabHelpers(content::WebContents* web_contents) {
|
||||
web_contents,
|
||||
Profile::FromBrowserContext(
|
||||
web_contents->GetBrowserContext()),
|
||||
is_pdf, std::move(variant),
|
||||
is_pdf, std::move(callback),
|
||||
base::BindRepeating(
|
||||
[]() -> base::IDMap<
|
||||
printing::mojom::PrintPreviewUI*>& {
|
||||
|
||||
@@ -44,8 +44,7 @@ class MockPrintPreviewExtractor
|
||||
MockPrintPreviewExtractor() = default;
|
||||
~MockPrintPreviewExtractor() override = default;
|
||||
|
||||
MOCK_METHOD(void, Extract, (ExtractCallback), (override));
|
||||
MOCK_METHOD(void, CapturePdf, (CapturePdfCallback), (override));
|
||||
MOCK_METHOD(void, CaptureImages, (CaptureImagesCallback), (override));
|
||||
};
|
||||
|
||||
class MockPageContentFetcher
|
||||
@@ -152,6 +151,11 @@ class AIChatTabHelperUnitTest : public content::RenderViewHostTestHarness,
|
||||
helper_->GetPageContent(std::move(callback), invalidation_token);
|
||||
}
|
||||
|
||||
void GetScreenshots(
|
||||
mojom::ConversationHandler::GetScreenshotsCallback callback) {
|
||||
helper_->GetScreenshots(std::move(callback));
|
||||
}
|
||||
|
||||
void TitleWasSet(content::NavigationEntry* entry) {
|
||||
helper_->TitleWasSet(entry);
|
||||
}
|
||||
@@ -243,10 +247,8 @@ TEST_P(AIChatTabHelperUnitTest, GetPageContent_HasContent) {
|
||||
NavigateTo(GURL("https://www.brave.com"));
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent)
|
||||
.WillOnce(base::test::RunOnceCallback<1>(kSuppliedText, false, ""));
|
||||
if (print_preview_extractor_) {
|
||||
// Fallback won't initiate if we already have content
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract).Times(0);
|
||||
}
|
||||
// Note: No need to mock CaptureImages since print preview hosts
|
||||
// are not triggered when regular content is available
|
||||
base::MockCallback<AIChatTabHelper::FetchPageContentCallback> callback;
|
||||
EXPECT_CALL(callback, Run(kExpectedText, false, ""));
|
||||
GetPageContent(callback.Get(), "");
|
||||
@@ -257,10 +259,8 @@ TEST_P(AIChatTabHelperUnitTest, GetPageContent_VideoContent) {
|
||||
NavigateTo(GURL("https://www.brave.com"));
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent)
|
||||
.WillOnce(base::test::RunOnceCallback<1>("", true, ""));
|
||||
if (print_preview_extractor_) {
|
||||
// Fallback won't initiate for video content.
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract).Times(0);
|
||||
}
|
||||
// Note: No need to mock CaptureImages since print preview hosts
|
||||
// are not triggered for video content
|
||||
base::MockCallback<AIChatTabHelper::FetchPageContentCallback> callback;
|
||||
EXPECT_CALL(callback, Run("", true, ""));
|
||||
GetPageContent(callback.Get(), "");
|
||||
@@ -268,20 +268,20 @@ TEST_P(AIChatTabHelperUnitTest, GetPageContent_VideoContent) {
|
||||
|
||||
TEST_P(AIChatTabHelperUnitTest, GetPageContent_PrintPreviewTriggeringURL) {
|
||||
base::MockCallback<AIChatTabHelper::FetchPageContentCallback> callback;
|
||||
constexpr char kExpectedText[] = "This is the way.";
|
||||
// A url that does by itself trigger print preview extraction.
|
||||
// A url that triggers print preview extraction - should return empty content
|
||||
// to allow autoscreenshots mechanism to handle server-side OCR
|
||||
for (const auto& host : kPrintPreviewRetrievalHosts) {
|
||||
NavigateTo(GURL(base::StrCat({"https://", host})));
|
||||
if (is_print_preview_supported_) {
|
||||
// PrintPreview always initiated on URL
|
||||
// PrintPreview returns empty content to trigger autoscreenshots
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent).Times(0);
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(base::ok(kExpectedText)));
|
||||
// No Extract call - we now return empty to trigger autoscreenshots
|
||||
} else {
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent)
|
||||
.WillOnce(base::test::RunOnceCallback<1>(kExpectedText, false, ""));
|
||||
.WillOnce(base::test::RunOnceCallback<1>("", false, ""));
|
||||
}
|
||||
EXPECT_CALL(callback, Run(kExpectedText, false, ""));
|
||||
// Expect empty content which will trigger autoscreenshots in real usage
|
||||
EXPECT_CALL(callback, Run("", false, ""));
|
||||
GetPageContent(callback.Get(), "");
|
||||
}
|
||||
}
|
||||
@@ -293,8 +293,7 @@ TEST_P(AIChatTabHelperUnitTest,
|
||||
// Don't fallback to regular fetch on failed print preview extraction.
|
||||
if (print_preview_extractor_) {
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent).Times(0);
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(base::unexpected("")));
|
||||
// Print preview now returns empty content directly
|
||||
} else {
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent)
|
||||
.WillOnce(base::test::RunOnceCallback<1>("", false, ""));
|
||||
@@ -314,7 +313,7 @@ TEST_P(AIChatTabHelperUnitTest,
|
||||
if (is_print_preview_supported_) {
|
||||
// Nothing should be called until page load
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent).Times(0);
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract).Times(0);
|
||||
// No CaptureImages calls expected until page loads
|
||||
GetPageContent(callback.Get(), "");
|
||||
testing::Mock::VerifyAndClearExpectations(&page_content_fetcher_);
|
||||
testing::Mock::VerifyAndClearExpectations(&print_preview_extractor_);
|
||||
@@ -324,8 +323,7 @@ TEST_P(AIChatTabHelperUnitTest,
|
||||
// empty content, callback should run.
|
||||
EXPECT_CALL(callback, Run("", false, ""));
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent).Times(0);
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(base::unexpected("")));
|
||||
// Print preview now returns empty content directly
|
||||
SimulateLoadFinished();
|
||||
|
||||
testing::Mock::VerifyAndClearExpectations(&page_content_fetcher_);
|
||||
@@ -371,9 +369,7 @@ TEST_P(AIChatTabHelperUnitTest,
|
||||
// Navigatng should result in our pending callback being run with no content
|
||||
// and no content extraction initiated.
|
||||
EXPECT_CALL(*page_content_fetcher_, FetchPageContent).Times(0);
|
||||
if (is_print_preview_supported_) {
|
||||
EXPECT_CALL(*print_preview_extractor_, Extract).Times(0);
|
||||
}
|
||||
// No CaptureImages calls expected during navigation
|
||||
EXPECT_CALL(callback, Run("", false, ""));
|
||||
NavigateTo(initial_url.Resolve("/2"), /*keep_loading=*/true,
|
||||
is_same_document);
|
||||
@@ -455,4 +451,147 @@ TEST_P(AIChatTabHelperUnitTest, GetPageContent_NoFallbackWhenNotPDF) {
|
||||
testing::Mock::VerifyAndClearExpectations(&page_content_fetcher_);
|
||||
}
|
||||
|
||||
// Tests for GetScreenshots method decision logic
|
||||
TEST_P(AIChatTabHelperUnitTest, GetScreenshots_PrintPreviewHost) {
|
||||
// Test that print preview hosts use CaptureImages when delegate is available
|
||||
NavigateTo(GURL("https://docs.google.com/document"));
|
||||
|
||||
base::test::TestFuture<std::optional<std::vector<mojom::UploadedFilePtr>>>
|
||||
future;
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
// Should use print preview extraction
|
||||
EXPECT_CALL(*print_preview_extractor_, CaptureImages)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>(
|
||||
std::vector<std::vector<uint8_t>>{{0x89, 0x50, 0x4E, 0x47}})));
|
||||
}
|
||||
|
||||
GetScreenshots(future.GetCallback());
|
||||
|
||||
auto result = future.Take();
|
||||
if (is_print_preview_supported_) {
|
||||
// Should receive screenshots when print preview is supported
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
} else {
|
||||
// When print preview is not supported, should return empty result
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AIChatTabHelperUnitTest, GetScreenshots_RegularHost) {
|
||||
// Test that regular hosts use FullScreenshotter (we can't easily mock this)
|
||||
NavigateTo(GURL("https://www.example.com"));
|
||||
|
||||
base::test::TestFuture<std::optional<std::vector<mojom::UploadedFilePtr>>>
|
||||
future;
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
// Print preview extractor should NOT be called for regular hosts
|
||||
EXPECT_CALL(*print_preview_extractor_, CaptureImages).Times(0);
|
||||
}
|
||||
|
||||
// Note: We can't easily mock FullScreenshotter since it's created internally,
|
||||
// but we can verify that CaptureImages is not called on the print preview
|
||||
// extractor
|
||||
GetScreenshots(future.GetCallback());
|
||||
|
||||
// The result should be provided by FullScreenshotter
|
||||
auto result = future.Take();
|
||||
// We can't predict the exact result since FullScreenshotter behavior
|
||||
// depends on the actual rendering, but we can verify the call completed
|
||||
}
|
||||
|
||||
TEST_P(AIChatTabHelperUnitTest, GetScreenshots_MultipleHosts) {
|
||||
// Test all print preview hosts
|
||||
for (const auto& host : kPrintPreviewRetrievalHosts) {
|
||||
SCOPED_TRACE(testing::Message() << "Testing host: " << host);
|
||||
NavigateTo(GURL(base::StrCat({"https://", host, "/document"})));
|
||||
|
||||
base::test::TestFuture<std::optional<std::vector<mojom::UploadedFilePtr>>>
|
||||
future;
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
// Should use print preview extraction for all print preview hosts
|
||||
EXPECT_CALL(*print_preview_extractor_, CaptureImages)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>(
|
||||
std::vector<std::vector<uint8_t>>{
|
||||
{0x89, 0x50, 0x4E, 0x47}})));
|
||||
}
|
||||
|
||||
GetScreenshots(future.GetCallback());
|
||||
|
||||
auto result = future.Take();
|
||||
if (is_print_preview_supported_) {
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
} else {
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
testing::Mock::VerifyAndClearExpectations(&print_preview_extractor_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AIChatTabHelperUnitTest, GetScreenshots_PrintPreviewError) {
|
||||
// Test error handling when print preview extraction fails
|
||||
NavigateTo(GURL("https://docs.google.com/document"));
|
||||
|
||||
base::test::TestFuture<std::optional<std::vector<mojom::UploadedFilePtr>>>
|
||||
future;
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
// Simulate print preview extraction error
|
||||
EXPECT_CALL(*print_preview_extractor_, CaptureImages)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(
|
||||
base::unexpected<std::string>("Print preview extraction failed")));
|
||||
}
|
||||
|
||||
GetScreenshots(future.GetCallback());
|
||||
|
||||
auto result = future.Take();
|
||||
// Should return empty result on error or when not supported
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
TEST_P(AIChatTabHelperUnitTest, GetScreenshots_PDFContent) {
|
||||
// Test that PDF content uses print preview extraction
|
||||
NavigateTo(GURL("https://example.com/document.pdf"));
|
||||
|
||||
// Set the main frame MIME type to PDF
|
||||
content::WebContentsTester::For(web_contents())
|
||||
->SetMainFrameMimeType(pdf::kPDFMimeType);
|
||||
|
||||
base::test::TestFuture<std::optional<std::vector<mojom::UploadedFilePtr>>>
|
||||
future;
|
||||
|
||||
if (is_print_preview_supported_) {
|
||||
// Should use print preview extraction for PDFs even on non-print-preview
|
||||
// hosts
|
||||
EXPECT_CALL(*print_preview_extractor_, CaptureImages)
|
||||
.WillOnce(base::test::RunOnceCallback<0>(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>(
|
||||
std::vector<std::vector<uint8_t>>{{0x25, 0x50, 0x44, 0x46}})));
|
||||
}
|
||||
|
||||
GetScreenshots(future.GetCallback());
|
||||
|
||||
auto result = future.Take();
|
||||
if (is_print_preview_supported_) {
|
||||
// When print preview is supported, should receive screenshots from print
|
||||
// preview extraction
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
}
|
||||
// When print preview is not supported, PDFs fall back to FullScreenshotter
|
||||
// We can't predict the exact FullScreenshotter result, but the call should
|
||||
// complete
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
|
||||
} // namespace ai_chat
|
||||
|
||||
@@ -142,14 +142,25 @@ void AIChatTabHelper::GetPageContent(FetchPageContentCallback callback,
|
||||
std::move(callback).Run("", false, "");
|
||||
return;
|
||||
}
|
||||
if (kPrintPreviewRetrievalHosts.contains(
|
||||
if (print_preview_extraction_delegate_ &&
|
||||
kPrintPreviewRetrievalHosts.contains(
|
||||
web_contents()->GetLastCommittedURL().host_piece())) {
|
||||
// Get content using a printing / OCR mechanism, instead of
|
||||
// directly from the source, if available.
|
||||
// Get content using print preview image capture for server-side OCR
|
||||
DVLOG(1) << __func__ << " print preview url";
|
||||
if (MaybePrintPreviewExtract(callback)) {
|
||||
// For print preview hosts, we always return empty content to trigger
|
||||
// the autoscreenshots mechanism which will use CaptureImages for
|
||||
// server-side OCR. However, if the page isn't loaded yet, wait for load
|
||||
// completion.
|
||||
if (!is_page_loaded_) {
|
||||
DVLOG(1) << "print preview page was not loaded yet, will return empty "
|
||||
"after load";
|
||||
SetPendingGetContentCallback(std::move(callback));
|
||||
return;
|
||||
}
|
||||
DVLOG(1) << "print preview host detected, returning empty to trigger "
|
||||
"autoscreenshots";
|
||||
std::move(callback).Run("", false, "");
|
||||
return;
|
||||
}
|
||||
page_content_fetcher_delegate_->FetchPageContent(
|
||||
invalidation_token,
|
||||
@@ -182,37 +193,6 @@ void AIChatTabHelper::SetPendingGetContentCallback(
|
||||
pending_get_page_content_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
bool AIChatTabHelper::MaybePrintPreviewExtract(
|
||||
FetchPageContentCallback& callback) {
|
||||
if (print_preview_extraction_delegate_ == nullptr) {
|
||||
DVLOG(1) << "print preview extraction not supported";
|
||||
return false;
|
||||
}
|
||||
if (!is_page_loaded_) {
|
||||
DVLOG(1) << "will extract print preview content when page is loaded";
|
||||
SetPendingGetContentCallback(std::move(callback));
|
||||
} else {
|
||||
// When page is already loaded, fallback to print preview extraction
|
||||
DVLOG(1) << "extracting print preview content now";
|
||||
print_preview_extraction_delegate_->Extract(
|
||||
base::BindOnce(&AIChatTabHelper::OnExtractPrintPreviewContentComplete,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIChatTabHelper::OnExtractPrintPreviewContentComplete(
|
||||
FetchPageContentCallback callback,
|
||||
base::expected<std::string, std::string> result) {
|
||||
// Invalidation token not applicable for print preview OCR
|
||||
if (result.has_value()) {
|
||||
std::move(callback).Run(std::move(result.value()), false, "");
|
||||
} else {
|
||||
VLOG(1) << result.error();
|
||||
std::move(callback).Run("", false, "");
|
||||
}
|
||||
}
|
||||
|
||||
void AIChatTabHelper::OnNewPage(int64_t navigation_id) {
|
||||
DVLOG(3) << __func__ << " id: " << navigation_id;
|
||||
AssociatedContentDriver::OnNewPage(navigation_id);
|
||||
@@ -341,8 +321,13 @@ bool AIChatTabHelper::HasOpenAIChatPermission() const {
|
||||
|
||||
void AIChatTabHelper::GetScreenshots(
|
||||
mojom::ConversationHandler::GetScreenshotsCallback callback) {
|
||||
if (IsPdf(web_contents())) {
|
||||
print_preview_extraction_delegate_->CapturePdf(
|
||||
if (print_preview_extraction_delegate_ &&
|
||||
(IsPdf(web_contents()) ||
|
||||
kPrintPreviewRetrievalHosts.contains(
|
||||
web_contents()->GetLastCommittedURL().host_piece()))) {
|
||||
// Use print preview extraction for PDFs and print preview hosts
|
||||
// when delegate is available
|
||||
print_preview_extraction_delegate_->CaptureImages(
|
||||
base::BindOnce(&AIChatTabHelper::OnScreenshotsCaptured,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
|
||||
} else {
|
||||
|
||||
@@ -53,18 +53,13 @@ class AIChatTabHelper : public content::WebContentsObserver,
|
||||
// Delegate to extract print preview content
|
||||
class PrintPreviewExtractionDelegate {
|
||||
public:
|
||||
// Result is extracted text or error
|
||||
using ExtractCallback =
|
||||
base::OnceCallback<void(base::expected<std::string, std::string>)>;
|
||||
// Result is image data of pdf pages or error
|
||||
using CapturePdfCallback = base::OnceCallback<void(
|
||||
// Result is image data of pages or error
|
||||
using CaptureImagesCallback = base::OnceCallback<void(
|
||||
base::expected<std::vector<std::vector<uint8_t>>, std::string>)>;
|
||||
|
||||
virtual ~PrintPreviewExtractionDelegate() = default;
|
||||
// Get the current text from the WebContents using Print Preview and OCR
|
||||
virtual void Extract(ExtractCallback callback) = 0;
|
||||
// Capture images of pdf without doing OCR
|
||||
virtual void CapturePdf(CapturePdfCallback callback) = 0;
|
||||
// Capture images of content without doing OCR
|
||||
virtual void CaptureImages(CaptureImagesCallback callback) = 0;
|
||||
};
|
||||
|
||||
class PageContentFetcherDelegate {
|
||||
@@ -151,10 +146,6 @@ class AIChatTabHelper : public content::WebContentsObserver,
|
||||
bool is_video,
|
||||
std::string invalidation_token);
|
||||
|
||||
void OnExtractPrintPreviewContentComplete(
|
||||
FetchPageContentCallback callback,
|
||||
base::expected<std::string, std::string>);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
void OnPDFDocumentLoadComplete(FetchPageContentCallback callback);
|
||||
|
||||
@@ -168,7 +159,6 @@ class AIChatTabHelper : public content::WebContentsObserver,
|
||||
const std::vector<std::pair<size_t, std::string>>& page_texts);
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
|
||||
bool MaybePrintPreviewExtract(FetchPageContentCallback& callback);
|
||||
|
||||
void SetPendingGetContentCallback(FetchPageContentCallback callback);
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ inline constexpr auto kPrintPreviewRetrievalHosts =
|
||||
"watermark.silverchair.com",
|
||||
});
|
||||
|
||||
inline constexpr uint8_t kMaxPreviewPages = 20;
|
||||
inline constexpr char kLeoModelSupportUrl[] =
|
||||
"https://support.brave.app/hc/en-us/articles/26727364100493-"
|
||||
"What-are-the-differences-between-Leo-s-AI-Models";
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Leo test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
// Here we create 21 pages to test kMaxPreviewPages which is 20.
|
||||
// These pages will be created only after receiving the beforeprint event.
|
||||
window.addEventListener('beforeprint', () => {
|
||||
for (let i = 0; i < 21; i++) {
|
||||
const div = document.createElement('div');
|
||||
div.style.width = '559px';
|
||||
div.style.height = '794px';
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
canvas.width = 559
|
||||
canvas.height = 794
|
||||
context.font = '30px Arial';
|
||||
if (i == 19) {
|
||||
// This should be the last page because of the truncation.
|
||||
context.fillText('This is the way.', 50, 100);
|
||||
} else if (i == 20) {
|
||||
// This will be truncated.
|
||||
context.fillText('There is no way.', 50, 100);
|
||||
}
|
||||
|
||||
div.appendChild(canvas);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Leo test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 559px; height: 794px;">
|
||||
<canvas id="page1" width="559" height="794">
|
||||
</canvas>
|
||||
</div>
|
||||
<div style="width: 559px; height: 794px;">
|
||||
<canvas id="page2" width="559" height="794">
|
||||
</canvas>
|
||||
</div>
|
||||
<div style="width: 559px; height: 794px;">
|
||||
<canvas id="page3" width="559" height="794">
|
||||
</canvas>
|
||||
</div>
|
||||
<div style="width: 559px; height: 794px;">
|
||||
<canvas id="page4" width="559" height="794">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var canvas = document.getElementById('page1');
|
||||
var context = canvas.getContext('2d');
|
||||
context.font = '30px Arial';
|
||||
context.fillText('This is the way.', 50, 100);
|
||||
|
||||
// leave page 2 empty
|
||||
|
||||
canvas = document.getElementById('page3');
|
||||
context = canvas.getContext('2d');
|
||||
context.font = '30px Arial';
|
||||
context.fillText('I have spoken.', 50, 100);
|
||||
|
||||
// smaller text with higher DPI
|
||||
canvas = document.getElementById('page4');
|
||||
const dpr = 2;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = rect.height * dpr;
|
||||
canvas.style.width = `${rect.width}px`;
|
||||
canvas.style.height = `${rect.height}px`;
|
||||
context = canvas.getContext('2d');
|
||||
context.scale(dpr, dpr);
|
||||
context.font = '15px Arial';
|
||||
context.fillText('Wherever I Go, He Goes.', 50, 100);
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<p>Or maybe not.</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user