#include "chrome/browser/printing/print_view_manager.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_WIN)
#include "base/auto_reset.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/printing/print_job_worker.h"
#include "chrome/browser/printing/print_test_utils.h"
#include "chrome/browser/printing/print_view_manager_base.h"
#include "chrome/browser/printing/printer_query.h"
#include "components/printing/common/print.mojom.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/test/test_renderer_host.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "printing/mojom/print.mojom.h"
#include "printing/print_settings.h"
#include "printing/print_settings_conversion.h"
#include "printing/printed_document.h"
#include "printing/units.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#endif
namespace printing {
PrintViewManagerTest;
namespace {
#if BUILDFLAG(IS_WIN)
class TestPrintQueriesQueueWin : public PrintQueriesQueue {
public:
TestPrintQueriesQueueWin() = default;
TestPrintQueriesQueueWin(const TestPrintQueriesQueueWin&) = delete;
TestPrintQueriesQueueWin& operator=(const TestPrintQueriesQueueWin&) = delete;
std::unique_ptr<PrinterQuery> CreatePrinterQuery(
content::GlobalRenderFrameHostId rfh_id) override;
void SetupPrinterOffsets(int offset_x, int offset_y);
void SetupPrinterLanguageType(mojom::PrinterLanguageType type);
private:
~TestPrintQueriesQueueWin() override = default;
mojom::PrinterLanguageType printer_language_type_;
int printable_offset_x_;
int printable_offset_y_;
};
class TestPrinterQueryWin : public PrinterQuery {
public:
explicit TestPrinterQueryWin(content::GlobalRenderFrameHostId rfh_id);
TestPrinterQueryWin(const TestPrinterQueryWin&) = delete;
TestPrinterQueryWin& operator=(const TestPrinterQueryWin&) = delete;
~TestPrinterQueryWin() override;
void SetSettings(base::Value::Dict new_settings,
base::OnceClosure callback) override;
void SetPrinterLanguageType(mojom::PrinterLanguageType type);
void SetPrintableAreaOffsets(int offset_x, int offset_y);
private:
std::optional<gfx::Point> offsets_;
std::optional<mojom::PrinterLanguageType> printer_language_type_;
};
std::unique_ptr<PrinterQuery> TestPrintQueriesQueueWin::CreatePrinterQuery(
content::GlobalRenderFrameHostId rfh_id) {
auto test_query = std::make_unique<TestPrinterQueryWin>(rfh_id);
test_query->SetPrinterLanguageType(printer_language_type_);
test_query->SetPrintableAreaOffsets(printable_offset_x_, printable_offset_y_);
return test_query;
}
void TestPrintQueriesQueueWin::SetupPrinterOffsets(int offset_x, int offset_y) {
printable_offset_x_ = offset_x;
printable_offset_y_ = offset_y;
}
void TestPrintQueriesQueueWin::SetupPrinterLanguageType(
mojom::PrinterLanguageType type) {
printer_language_type_ = type;
}
TestPrinterQueryWin::TestPrinterQueryWin(
content::GlobalRenderFrameHostId rfh_id)
: PrinterQuery(rfh_id) {}
TestPrinterQueryWin::~TestPrinterQueryWin() = default;
void TestPrinterQueryWin::SetSettings(base::Value::Dict new_settings,
base::OnceClosure callback) {
DCHECK(offsets_);
DCHECK(printer_language_type_);
std::unique_ptr<PrintSettings> settings =
PrintSettingsFromJobSettings(new_settings);
mojom::ResultCode result = mojom::ResultCode::kSuccess;
if (!settings) {
settings = std::make_unique<PrintSettings>();
result = mojom::ResultCode::kFailed;
}
float device_microns_per_device_unit =
static_cast<float>(kMicronsPerInch) / settings->device_units_per_inch();
gfx::Size paper_size =
gfx::Size(settings->requested_media().size_microns.width() /
device_microns_per_device_unit,
settings->requested_media().size_microns.height() /
device_microns_per_device_unit);
gfx::Rect paper_rect(0, 0, paper_size.width(), paper_size.height());
paper_rect.Inset(gfx::Insets::VH(offsets_->y(), offsets_->x()));
settings->SetPrinterPrintableArea(paper_size, paper_rect, true);
settings->set_printer_language_type(*printer_language_type_);
GetSettingsDone(std::move(callback), std::nullopt,
std::move(settings), result);
}
void TestPrinterQueryWin::SetPrinterLanguageType(
mojom::PrinterLanguageType type) {
printer_language_type_ = type;
}
void TestPrinterQueryWin::SetPrintableAreaOffsets(int offset_x, int offset_y) {
offsets_ = gfx::Point(offset_x, offset_y);
}
class TestPrintJobWin : public PrintJob {
public:
TestPrintJobWin() = default;
const gfx::Size& page_size() const { return page_size_; }
const gfx::Rect& content_area() const { return content_area_; }
const gfx::Point& physical_offsets() const { return physical_offsets_; }
mojom::PrinterLanguageType type() const { return type_; }
void Initialize(std::unique_ptr<PrinterQuery> query,
const std::u16string& name,
uint32_t page_count) override {
std::unique_ptr<PrintJobWorker> worker =
query->TransferContextToNewWorker(nullptr);
scoped_refptr<PrintedDocument> new_doc =
base::MakeRefCounted<PrintedDocument>(query->ExtractSettings(), name,
query->cookie());
new_doc->set_page_count(page_count);
UpdatePrintedDocument(new_doc.get());
}
void StartPrinting() override { set_job_pending_for_testing(true); }
void Stop() override { set_job_pending_for_testing(false); }
void Cancel() override { set_job_pending_for_testing(false); }
void OnFailed() override {}
void OnDocDone(int job_id, PrintedDocument* document) override {}
bool FlushJob(base::TimeDelta timeout) override { return true; }
void StartPdfToEmfConversion(scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
const gfx::Rect& content_area,
const GURL& url) override {
page_size_ = page_size;
content_area_ = content_area;
type_ = mojom::PrinterLanguageType::kNone;
}
void StartPdfToPostScriptConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
bool ps_level2,
const GURL& url) override {
content_area_ = content_area;
physical_offsets_ = physical_offsets;
type_ = ps_level2 ? mojom::PrinterLanguageType::kPostscriptLevel2
: mojom::PrinterLanguageType::kPostscriptLevel3;
}
void StartPdfToTextConversion(scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
const GURL& url) override {
page_size_ = page_size;
type_ = mojom::PrinterLanguageType::kTextOnly;
}
private:
~TestPrintJobWin() override { set_job_pending_for_testing(false); }
gfx::Size page_size_;
gfx::Rect content_area_;
gfx::Point physical_offsets_;
mojom::PrinterLanguageType type_;
};
#endif
class TestPrintViewManagerForSystemDialogPrint : public PrintViewManager { … };
}
#if BUILDFLAG(IS_WIN)
class TestPrintViewManagerWin : public PrintViewManagerBase {
public:
explicit TestPrintViewManagerWin(content::WebContents* web_contents)
: PrintViewManagerBase(web_contents) {}
TestPrintViewManagerWin(const TestPrintViewManagerWin&) = delete;
TestPrintViewManagerWin& operator=(const TestPrintViewManagerWin&) = delete;
~TestPrintViewManagerWin() override {
print_job_ = nullptr;
}
bool PrintPreviewNow(content::RenderFrameHost* rfh, bool has_selection) {
if (IsCrashed())
return false;
mojo::AssociatedRemote<mojom::PrintRenderFrame> print_render_frame;
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&print_render_frame);
print_render_frame->InitiatePrintPreview(
has_selection);
return true;
}
const gfx::Size& page_size() { return test_job()->page_size(); }
const gfx::Rect& content_area() { return test_job()->content_area(); }
const gfx::Point& physical_offsets() {
return test_job()->physical_offsets();
}
mojom::PrinterLanguageType type() { return test_job()->type(); }
void FakePrintCallback(const base::Value& error) {
DCHECK(run_loop_);
run_loop_->Quit();
}
void WaitForCallback() {
base::RunLoop run_loop;
base::AutoReset<raw_ptr<base::RunLoop>> auto_reset(&run_loop_, &run_loop);
run_loop.Run();
}
protected:
bool SetupNewPrintJob(std::unique_ptr<PrinterQuery> query) override {
print_job_ = base::MakeRefCounted<TestPrintJobWin>();
print_job_->Initialize(std::move(query), RenderSourceName(),
number_pages());
return true;
}
void SetupScriptedPrintPreview(
SetupScriptedPrintPreviewCallback callback) override {
NOTREACHED_IN_MIGRATION();
}
void ShowScriptedPrintPreview(bool is_modifiable) override {
NOTREACHED_IN_MIGRATION();
}
void RequestPrintPreview(
mojom::RequestPrintPreviewParamsPtr params) override {
NOTREACHED_IN_MIGRATION();
}
void CheckForCancel(int32_t preview_ui_id,
int32_t request_id,
CheckForCancelCallback callback) override {
NOTREACHED_IN_MIGRATION();
}
private:
TestPrintJobWin* test_job() {
return static_cast<TestPrintJobWin*>(print_job_.get());
}
raw_ptr<base::RunLoop> run_loop_ = nullptr;
};
#endif
TEST_F(PrintViewManagerTest, PrintSubFrameAndDestroy) { … }
TEST_F(PrintViewManagerTest, PrintForSystemDialog) { … }
#if BUILDFLAG(IS_WIN)
TEST_F(PrintViewManagerTest, PostScriptHasCorrectOffsets) {
scoped_refptr<TestPrintQueriesQueueWin> queue =
base::MakeRefCounted<TestPrintQueriesQueueWin>();
queue->SetupPrinterLanguageType(
mojom::PrinterLanguageType::kPostscriptLevel2);
int offset_in_pixels = static_cast<int>(test::kPrinterDpi * 0.1f);
queue->SetupPrinterOffsets(offset_in_pixels, offset_in_pixels);
g_browser_process->print_job_manager()->SetQueueForTest(queue);
chrome::NewTab(browser());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
auto print_view_manager =
std::make_unique<TestPrintViewManagerWin>(web_contents);
PrintViewManager::SetReceiverImplForTesting(print_view_manager.get());
print_view_manager->PrintPreviewNow(web_contents->GetPrimaryMainFrame(),
false);
base::Value::Dict print_ticket =
test::GetPrintTicket(mojom::PrinterType::kLocal);
const char kTestData[] = "abc";
auto print_data = base::MakeRefCounted<base::RefCountedStaticMemory>(
kTestData, sizeof(kTestData));
PrinterHandler::PrintCallback callback =
base::BindOnce(&TestPrintViewManagerWin::FakePrintCallback,
base::Unretained(print_view_manager.get()));
print_view_manager->PrintForPrintPreview(std::move(print_ticket), print_data,
web_contents->GetPrimaryMainFrame(),
std::move(callback));
print_view_manager->WaitForCallback();
EXPECT_EQ(gfx::Point(60, 60), print_view_manager->physical_offsets());
EXPECT_EQ(gfx::Rect(0, 0, 5100, 6600), print_view_manager->content_area());
EXPECT_EQ(mojom::PrinterLanguageType::kPostscriptLevel2,
print_view_manager->type());
PrintViewManager::SetReceiverImplForTesting(nullptr);
}
#endif
}