| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/printing/print_browsertest.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/base64.h" |
| #include "base/check_op.h" |
| #include "base/containers/contains.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/path_service.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/extensions/extension_browsertest.h" |
| #include "chrome/browser/printing/browser_printing_context_factory_for_test.h" |
| #include "chrome/browser/printing/print_error_dialog.h" |
| #include "chrome/browser/printing/print_job.h" |
| #include "chrome/browser/printing/print_job_manager.h" |
| #include "chrome/browser/printing/print_preview_sticky_settings.h" |
| #include "chrome/browser/printing/print_test_utils.h" |
| #include "chrome/browser/printing/print_view_manager.h" |
| #include "chrome/browser/printing/print_view_manager_common.h" |
| #include "chrome/browser/printing/printer_query.h" |
| #include "chrome/browser/printing/test_print_preview_observer.h" |
| #include "chrome/browser/printing/test_print_view_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/printing/browser/print_composite_client.h" |
| #include "components/printing/browser/print_manager_utils.h" |
| #include "components/printing/common/print.mojom-test-utils.h" |
| #include "components/printing/common/print.mojom.h" |
| #include "components/web_modal/web_contents_modal_dialog_manager.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/global_routing_id.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/back_forward_cache_util.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/fenced_frame_test_util.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "extensions/common/extension.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "printing/backend/test_print_backend.h" |
| #include "printing/buildflags/buildflags.h" |
| #include "printing/mojom/print.mojom.h" |
| #include "printing/page_setup.h" |
| #include "printing/print_settings.h" |
| #include "printing/printing_context.h" |
| #include "printing/printing_context_factory_for_test.h" |
| #include "printing/printing_features.h" |
| #include "printing/test_printing_context.h" |
| #include "printing/units.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h" |
| #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/size_f.h" |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| #include "chrome/browser/printing/print_backend_service_manager.h" |
| #include "chrome/browser/printing/print_backend_service_test_impl.h" |
| #include "chrome/browser/printing/print_job_worker_oop.h" |
| #include "chrome/browser/printing/printer_query_oop.h" |
| #include "chrome/services/printing/public/mojom/print_backend_service.mojom.h" |
| #endif |
| |
| namespace printing { |
| |
| using testing::_; |
| |
| using OnDidCreatePrintJobCallback = |
| base::RepeatingCallback<void(PrintJob* print_job)>; |
| |
| namespace { |
| |
| constexpr int kTestPrinterCapabilitiesMaxCopies = 99; |
| const int kDefaultDocumentCookie = PrintSettings::NewCookie(); |
| |
| // Sticky settings containing an extension printer as the most recently used |
| // destination. This must be in sync with |
| // //chrome/test/data/printing/test_extension/background.js. |
| constexpr char kStickySettingsWithExtensionPrinter[] = R"({ |
| "version": 2, |
| "recentDestinations": [ |
| { |
| "id": "%s:printer", |
| "origin": "extension", |
| "capabilities": null, |
| "displayName": "test extension printer", |
| "extensionId": "%s", |
| "extensionName": "Test Printer Provider Extension" |
| } |
| ] |
| })"; |
| |
| // Sticky settings containing an extension printer with a missing printable area |
| // as the most recently used destination. This must be in sync with |
| // //chrome/test/data/printing/test_extension/background.js. |
| constexpr char kStickySettingsWithExtensionPrinterMissingPrintableArea[] = R"({ |
| "version": 2, |
| "recentDestinations": [ |
| { |
| "id": "%s:printer_missing_printable_area", |
| "origin": "extension", |
| "capabilities": null, |
| "displayName": "extension printer missing printable area", |
| "extensionId": "%s", |
| "extensionName": "Test Printer Provider Extension" |
| } |
| ] |
| })"; |
| |
| class KillPrintRenderFrame |
| : public mojom::PrintRenderFrameInterceptorForTesting { |
| public: |
| explicit KillPrintRenderFrame(content::RenderProcessHost* rph) : rph_(rph) {} |
| ~KillPrintRenderFrame() override = default; |
| |
| void OverrideBinderForTesting(content::RenderFrameHost* render_frame_host) { |
| render_frame_host->GetRemoteAssociatedInterfaces() |
| ->OverrideBinderForTesting( |
| mojom::PrintRenderFrame::Name_, |
| base::BindRepeating(&KillPrintRenderFrame::Bind, |
| base::Unretained(this))); |
| } |
| |
| void KillRenderProcess(int document_cookie, |
| mojom::DidPrintContentParamsPtr param, |
| PrintFrameContentCallback callback) const { |
| std::move(callback).Run(document_cookie, std::move(param)); |
| rph_->Shutdown(0); |
| } |
| |
| void Bind(mojo::ScopedInterfaceEndpointHandle handle) { |
| receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::PrintRenderFrame>( |
| std::move(handle))); |
| } |
| |
| // mojom::PrintRenderFrameInterceptorForTesting |
| mojom::PrintRenderFrame* GetForwardingInterface() override { |
| NOTREACHED(); |
| return nullptr; |
| } |
| void PrintFrameContent(mojom::PrintFrameContentParamsPtr params, |
| PrintFrameContentCallback callback) override { |
| // Sends the printed result back. |
| const size_t kSize = 10; |
| mojom::DidPrintContentParamsPtr printed_frame_params = |
| mojom::DidPrintContentParams::New(); |
| base::MappedReadOnlyRegion region_mapping = |
| base::ReadOnlySharedMemoryRegion::Create(kSize); |
| printed_frame_params->metafile_data_region = |
| std::move(region_mapping.region); |
| KillRenderProcess(params->document_cookie, std::move(printed_frame_params), |
| std::move(callback)); |
| } |
| |
| private: |
| const raw_ptr<content::RenderProcessHost> rph_; |
| mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this}; |
| }; |
| |
| class PrintPreviewDoneObserver |
| : public mojom::PrintRenderFrameInterceptorForTesting { |
| public: |
| PrintPreviewDoneObserver(content::RenderFrameHost* render_frame_host, |
| mojom::PrintRenderFrame* print_render_frame) |
| : render_frame_host_(render_frame_host), |
| print_render_frame_(print_render_frame) { |
| OverrideBinderForTesting(); |
| } |
| ~PrintPreviewDoneObserver() override { |
| render_frame_host_->GetRemoteAssociatedInterfaces() |
| ->OverrideBinderForTesting(mojom::PrintRenderFrame::Name_, |
| base::NullCallback()); |
| } |
| |
| void WaitForPrintPreviewDialogClosed() { run_loop_.Run(); } |
| |
| // mojom::PrintRenderFrameInterceptorForTesting: |
| mojom::PrintRenderFrame* GetForwardingInterface() override { |
| return print_render_frame_; |
| } |
| void OnPrintPreviewDialogClosed() override { |
| GetForwardingInterface()->OnPrintPreviewDialogClosed(); |
| run_loop_.Quit(); |
| } |
| |
| private: |
| void OverrideBinderForTesting() { |
| // Safe to use base::Unretained() below because: |
| // 1) Normally, Bind() will unregister the override after it gets called. |
| // 2) If Bind() does not get called, the dtor will unregister the override. |
| render_frame_host_->GetRemoteAssociatedInterfaces() |
| ->OverrideBinderForTesting( |
| mojom::PrintRenderFrame::Name_, |
| base::BindRepeating(&PrintPreviewDoneObserver::Bind, |
| base::Unretained(this))); |
| } |
| |
| void Bind(mojo::ScopedInterfaceEndpointHandle handle) { |
| receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::PrintRenderFrame>( |
| std::move(handle))); |
| |
| // After the initial binding, reset the binder override. Otherwise, |
| // PrintPreviewDoneObserver will also try to bind the remote passed by |
| // SetPrintPreviewUI(), which will fail since `receiver_` is already bound. |
| render_frame_host_->GetRemoteAssociatedInterfaces() |
| ->OverrideBinderForTesting(mojom::PrintRenderFrame::Name_, |
| base::NullCallback()); |
| } |
| |
| raw_ptr<content::RenderFrameHost> const render_frame_host_; |
| raw_ptr<mojom::PrintRenderFrame> const print_render_frame_; |
| mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this}; |
| base::RunLoop run_loop_; |
| }; |
| |
| } // namespace |
| |
| class TestPrintRenderFrame |
| : public mojom::PrintRenderFrameInterceptorForTesting { |
| public: |
| TestPrintRenderFrame(content::RenderFrameHost* frame_host, |
| content::WebContents* web_contents, |
| int document_cookie, |
| base::RepeatingClosure msg_callback) |
| : frame_host_(frame_host), |
| web_contents_(web_contents), |
| document_cookie_(document_cookie), |
| task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), |
| msg_callback_(msg_callback) {} |
| ~TestPrintRenderFrame() override = default; |
| |
| void OnDidPrintFrameContent(int document_cookie, |
| mojom::DidPrintContentParamsPtr param, |
| PrintFrameContentCallback callback) const { |
| EXPECT_EQ(document_cookie, document_cookie_); |
| ASSERT_TRUE(param->metafile_data_region.IsValid()); |
| EXPECT_GT(param->metafile_data_region.GetSize(), 0U); |
| std::move(callback).Run(document_cookie, std::move(param)); |
| task_runner_->PostTask(FROM_HERE, msg_callback_); |
| } |
| |
| void Bind(mojo::ScopedInterfaceEndpointHandle handle) { |
| receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::PrintRenderFrame>( |
| std::move(handle))); |
| } |
| |
| static mojom::DidPrintContentParamsPtr GetDefaultDidPrintContentParams() { |
| auto printed_frame_params = mojom::DidPrintContentParams::New(); |
| // Creates a small amount of region to avoid passing empty data to mojo. |
| constexpr size_t kSize = 10; |
| base::MappedReadOnlyRegion region_mapping = |
| base::ReadOnlySharedMemoryRegion::Create(kSize); |
| printed_frame_params->metafile_data_region = |
| std::move(region_mapping.region); |
| return printed_frame_params; |
| } |
| |
| // mojom::PrintRenderFrameInterceptorForTesting |
| mojom::PrintRenderFrame* GetForwardingInterface() override { |
| NOTREACHED(); |
| return nullptr; |
| } |
| void PrintFrameContent(mojom::PrintFrameContentParamsPtr params, |
| PrintFrameContentCallback callback) override { |
| // Sends the printed result back. |
| OnDidPrintFrameContent(params->document_cookie, |
| GetDefaultDidPrintContentParams(), |
| std::move(callback)); |
| |
| auto* client = PrintCompositeClient::FromWebContents(web_contents_); |
| if (!client) { |
| return; |
| } |
| |
| // Prints its children. |
| content::RenderFrameHost* child = ChildFrameAt(frame_host_.get(), 0); |
| for (size_t i = 1; child; i++) { |
| if (child->GetSiteInstance() != frame_host_->GetSiteInstance()) { |
| client->PrintCrossProcessSubframe(gfx::Rect(), params->document_cookie, |
| child); |
| } |
| child = ChildFrameAt(frame_host_.get(), i); |
| } |
| } |
| |
| private: |
| raw_ptr<content::RenderFrameHost> frame_host_; |
| raw_ptr<content::WebContents> web_contents_; |
| const int document_cookie_; |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| base::RepeatingClosure msg_callback_; |
| mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this}; |
| }; |
| |
| // Lives on the UI thread. |
| class TestPrintViewManagerForDLP : public TestPrintViewManager { |
| public: |
| // Used to simulate Data Leak Prevention polices and possible user actions. |
| enum class RestrictionLevel { |
| // No DLP restrictions set - printing is allowed. |
| kNotSet, |
| // The user is warned and selects "continue" - printing is allowed. |
| kWarnAllow, |
| // The user is warned and selects "cancel" - printing is not allowed. |
| kWarnCancel, |
| // Printing is blocked, no print preview is shown. |
| kBlock, |
| }; |
| |
| static TestPrintViewManagerForDLP* CreateForWebContents( |
| content::WebContents* web_contents, |
| RestrictionLevel restriction_level) { |
| auto manager = std::make_unique<TestPrintViewManagerForDLP>( |
| web_contents, restriction_level); |
| auto* manager_ptr = manager.get(); |
| web_contents->SetUserData(PrintViewManager::UserDataKey(), |
| std::move(manager)); |
| return manager_ptr; |
| } |
| |
| // Used by the TestPrintViewManagerForDLP to check that the correct action is |
| // taken based on the restriction level. |
| enum class PrintAllowance { |
| // No checks done yet to determine whether printing is allowed or not. |
| kUnknown, |
| // There are no restrictions/user allowed printing. |
| kAllowed, |
| // There are BLOCK restrictions or user canceled the printing. |
| kDisallowed, |
| }; |
| |
| TestPrintViewManagerForDLP(content::WebContents* web_contents, |
| RestrictionLevel restriction_level) |
| : TestPrintViewManager(web_contents), |
| restriction_level_(restriction_level) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| PrintViewManager::SetReceiverImplForTesting(this); |
| } |
| TestPrintViewManagerForDLP(const TestPrintViewManagerForDLP&) = delete; |
| TestPrintViewManagerForDLP& operator=(const TestPrintViewManagerForDLP&) = |
| delete; |
| ~TestPrintViewManagerForDLP() override { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| PrintViewManager::SetReceiverImplForTesting(nullptr); |
| } |
| |
| PrintAllowance GetPrintAllowance() const { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return allowance_; |
| } |
| |
| private: |
| void RejectPrintPreviewRequestIfRestricted( |
| content::GlobalRenderFrameHostId rfh_id, |
| base::OnceCallback<void(bool)> callback) override { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| switch (restriction_level_) { |
| case RestrictionLevel::kNotSet: |
| case RestrictionLevel::kWarnAllow: |
| std::move(callback).Run(true); |
| break; |
| case RestrictionLevel::kBlock: |
| case RestrictionLevel::kWarnCancel: |
| std::move(callback).Run(false); |
| break; |
| } |
| } |
| |
| void PrintPreviewRejectedForTesting() override { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| run_loop_->Quit(); |
| allowance_ = PrintAllowance::kDisallowed; |
| } |
| |
| void PrintPreviewAllowedForTesting() override { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| run_loop_->Quit(); |
| allowance_ = PrintAllowance::kAllowed; |
| } |
| |
| RestrictionLevel restriction_level_ = RestrictionLevel::kNotSet; |
| PrintAllowance allowance_ = PrintAllowance::kUnknown; |
| }; |
| |
| PrintBrowserTest::WorkerHelper::WorkerHelper( |
| base::WeakPtr<PrintBrowserTest> owner) |
| : owner_(owner) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| } |
| |
| PrintBrowserTest::WorkerHelper::~WorkerHelper() = default; |
| |
| void PrintBrowserTest::WorkerHelper::OnNewDocument( |
| #if BUILDFLAG(IS_MAC) |
| bool destination_is_preview, |
| #endif |
| const PrintSettings& settings) { |
| if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { |
| content::GetUIThreadTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&WorkerHelper::OnNewDocument, this, |
| #if BUILDFLAG(IS_MAC) |
| destination_is_preview, |
| #endif |
| settings)); |
| return; |
| } |
| if (owner_) { |
| owner_->OnNewDocument( |
| #if BUILDFLAG(IS_MAC) |
| destination_is_preview, |
| #endif |
| settings); |
| } |
| } |
| |
| PrintBrowserTest::PrintBrowserTest() = default; |
| PrintBrowserTest::~PrintBrowserTest() = default; |
| |
| void PrintBrowserTest::SetUp() { |
| test_print_backend_ = base::MakeRefCounted<TestPrintBackend>(); |
| PrintBackend::SetPrintBackendForTesting(test_print_backend_.get()); |
| |
| num_expected_messages_ = 1; // By default, only wait on one message. |
| num_received_messages_ = 0; |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void PrintBrowserTest::SetUpOnMainThread() { |
| // Create `worker_helper_` here, once the UI thread exists. |
| worker_helper_ = |
| base::MakeRefCounted<WorkerHelper>(weak_factory_.GetWeakPtr()); |
| test_printing_context_factory_.SetOnNewDocumentCallback( |
| base::BindRepeating(&WorkerHelper::OnNewDocument, worker_helper_)); |
| PrintingContext::SetPrintingContextFactoryForTest( |
| &test_printing_context_factory_); |
| |
| SetShowPrintErrorDialogForTest(base::BindRepeating( |
| &PrintBrowserTest::ShowPrintErrorDialog, weak_factory_.GetWeakPtr())); |
| |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| content::SetupCrossSiteRedirector(embedded_test_server()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| PrepareRunloop(); |
| } |
| |
| void PrintBrowserTest::TearDownOnMainThread() { |
| // Remove map of objects pointing to //content objects before they go away. |
| frame_content_.clear(); |
| |
| SetShowPrintErrorDialogForTest(base::NullCallback()); |
| |
| PrintingContext::SetPrintingContextFactoryForTest(/*factory=*/nullptr); |
| test_printing_context_factory_.SetOnNewDocumentCallback(base::NullCallback()); |
| |
| InProcessBrowserTest::TearDownOnMainThread(); |
| } |
| |
| void PrintBrowserTest::TearDown() { |
| InProcessBrowserTest::TearDown(); |
| PrintBackend::SetPrintBackendForTesting(/*print_backend=*/nullptr); |
| } |
| |
| void PrintBrowserTest::AddPrinter(const std::string& printer_name) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| PrinterBasicInfo printer_info( |
| printer_name, |
| /*display_name=*/"test printer", |
| /*printer_description=*/"A printer for testing.", |
| /*printer_status=*/0, |
| /*is_default=*/true, test::kPrintInfoOptions); |
| |
| auto default_caps = std::make_unique<PrinterSemanticCapsAndDefaults>(); |
| default_caps->copies_max = kTestPrinterCapabilitiesMaxCopies; |
| default_caps->dpis = test::kPrinterCapabilitiesDefaultDpis; |
| default_caps->default_dpi = test::kPrinterCapabilitiesDpi; |
| default_caps->papers.push_back(test::kPaperLetter); |
| default_caps->papers.push_back(test::kPaperLegal); |
| test_print_backend_->AddValidPrinter( |
| printer_name, std::move(default_caps), |
| std::make_unique<PrinterBasicInfo>(printer_info)); |
| } |
| |
| void PrintBrowserTest::SetPrinterNameForSubsequentContexts( |
| const std::string& printer_name) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| test_printing_context_factory_.SetPrinterNameForSubsequentContexts( |
| printer_name); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| void PrintBrowserTest::SetPrinterLanguageTypeForSubsequentContexts( |
| mojom::PrinterLanguageType printer_language_type) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| test_printing_context_factory_.SetPrinterLanguageTypeForSubsequentContexts( |
| printer_language_type); |
| } |
| #endif |
| |
| void PrintBrowserTest::SetNewDocumentJobId(int job_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| test_printing_context_factory_.SetJobIdOnNewDocument(job_id); |
| } |
| |
| void PrintBrowserTest::PrintAndWaitUntilPreviewIsReady() { |
| PrintAndWaitUntilPreviewIsReady(PrintParams()); |
| } |
| |
| content::WebContents* PrintBrowserTest::PrintAndWaitUntilPreviewIsReady( |
| const PrintParams& params) { |
| return PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded( |
| params, |
| /*wait_for_loaded=*/false); |
| } |
| |
| content::WebContents* |
| PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded() { |
| return PrintAndWaitUntilPreviewIsReadyAndLoaded(PrintParams()); |
| } |
| |
| content::WebContents* |
| PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded( |
| const PrintParams& params) { |
| return PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded( |
| params, |
| /*wait_for_loaded=*/true); |
| } |
| |
| content::WebContents* |
| PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded( |
| const PrintParams& params, |
| bool wait_for_loaded) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| TestPrintPreviewObserver print_preview_observer(wait_for_loaded, |
| params.pages_per_sheet); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| switch (params.invoke_method) { |
| case InvokePrintMethod::kStartPrint: |
| StartPrint(web_contents, |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| /*print_renderer=*/mojo::NullAssociatedRemote(), |
| #endif |
| /*print_preview_disabled=*/false, params.print_only_selection); |
| break; |
| case InvokePrintMethod::kWindowDotPrint: |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| break; |
| } |
| |
| content::WebContents* preview_dialog = |
| print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog(); |
| |
| set_rendered_page_count(print_preview_observer.rendered_page_count()); |
| |
| return preview_dialog; |
| } |
| |
| // The following are helper functions for having a wait loop in the test and |
| // exit when all expected messages are received. |
| void PrintBrowserTest::SetNumExpectedMessages(unsigned int num) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| num_expected_messages_ = num; |
| } |
| |
| void PrintBrowserTest::ResetNumReceivedMessages() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| num_received_messages_ = 0; |
| } |
| |
| void PrintBrowserTest::WaitUntilCallbackReceived() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| ASSERT_TRUE(run_loop_); |
| run_loop_->Run(); |
| } |
| |
| void PrintBrowserTest::CheckForQuit() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (++num_received_messages_ != num_expected_messages_) { |
| // Beware of tests which have more events checking than expected! |
| // Such tests might be exiting too early, and thus be flaky. |
| ASSERT_LT(num_received_messages_, num_expected_messages_); |
| return; |
| } |
| if (quit_callback_) { |
| std::move(quit_callback_).Run(); |
| } |
| } |
| |
| void PrintBrowserTest::CreateTestPrintRenderFrame( |
| content::RenderFrameHost* frame_host, |
| content::WebContents* web_contents) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| frame_content_.emplace( |
| frame_host, std::make_unique<TestPrintRenderFrame>( |
| frame_host, web_contents, kDefaultDocumentCookie, |
| base::BindRepeating(&PrintBrowserTest::CheckForQuit, |
| weak_factory_.GetWeakPtr()))); |
| OverrideBinderForTesting(frame_host); |
| } |
| |
| // static |
| mojom::PrintFrameContentParamsPtr |
| PrintBrowserTest::GetDefaultPrintFrameParams() { |
| return mojom::PrintFrameContentParams::New(gfx::Rect(800, 600), |
| kDefaultDocumentCookie); |
| } |
| |
| const mojo::AssociatedRemote<mojom::PrintRenderFrame>& |
| PrintBrowserTest::GetPrintRenderFrame(content::RenderFrameHost* rfh) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!remote_) { |
| rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote_); |
| } |
| return remote_; |
| } |
| |
| TestPrintRenderFrame* PrintBrowserTest::GetFrameContent( |
| content::RenderFrameHost* host) const { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| auto iter = frame_content_.find(host); |
| return iter != frame_content_.end() ? iter->second.get() : nullptr; |
| } |
| |
| void PrintBrowserTest::OverrideBinderForTesting( |
| content::RenderFrameHost* render_frame_host) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| render_frame_host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting( |
| mojom::PrintRenderFrame::Name_, |
| base::BindRepeating( |
| &TestPrintRenderFrame::Bind, |
| base::Unretained(GetFrameContent(render_frame_host)))); |
| } |
| |
| void PrintBrowserTest::PrepareRunloop() { |
| // `run_loop_` and `quit_callback_` are initialized together to avoid having |
| // a race between the last expected `CheckForQuit()` call and |
| // `WaitUntilCallbackReceived()` being called. |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| quit_callback_ = run_loop_->QuitClosure(); |
| } |
| |
| void PrintBrowserTest::OnNewDocument( |
| #if BUILDFLAG(IS_MAC) |
| bool destination_is_preview, |
| #endif |
| const PrintSettings& settings) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| DVLOG(1) << " Observed: new document"; |
| new_document_called_count_++; |
| document_print_settings_ = settings; |
| #if BUILDFLAG(IS_MAC) |
| destination_is_preview_ = destination_is_preview; |
| #endif |
| } |
| |
| void PrintBrowserTest::ShowPrintErrorDialog() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| ++error_dialog_shown_count_; |
| CheckForQuit(); |
| } |
| |
| class SitePerProcessPrintBrowserTest : public PrintBrowserTest { |
| public: |
| SitePerProcessPrintBrowserTest() = default; |
| ~SitePerProcessPrintBrowserTest() override = default; |
| |
| // content::BrowserTestBase |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| content::IsolateAllSitesForTesting(command_line); |
| } |
| }; |
| |
| class IsolateOriginsPrintBrowserTest : public PrintBrowserTest { |
| public: |
| static constexpr char kIsolatedSite[] = "b.com"; |
| |
| IsolateOriginsPrintBrowserTest() = default; |
| ~IsolateOriginsPrintBrowserTest() override = default; |
| |
| // content::BrowserTestBase |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| std::string origin_list = |
| embedded_test_server()->GetURL(kIsolatedSite, "/").spec(); |
| command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| } |
| }; |
| |
| class BackForwardCachePrintBrowserTest : public PrintBrowserTest { |
| public: |
| BackForwardCachePrintBrowserTest() = default; |
| BackForwardCachePrintBrowserTest(const BackForwardCachePrintBrowserTest&) = |
| delete; |
| BackForwardCachePrintBrowserTest& operator=( |
| const BackForwardCachePrintBrowserTest&) = delete; |
| ~BackForwardCachePrintBrowserTest() override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| content::GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| /*ignore_outstanding_network_request=*/false), |
| content::GetDefaultDisabledBackForwardCacheFeaturesForTesting()); |
| |
| PrintBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| content::WebContents* web_contents() const { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::RenderFrameHost* current_frame_host() { |
| return web_contents()->GetPrimaryMainFrame(); |
| } |
| |
| void ExpectBlocklistedFeature( |
| blink::scheduler::WebSchedulerTrackedFeature feature, |
| base::Location location) { |
| base::HistogramBase::Sample sample = base::HistogramBase::Sample(feature); |
| AddSampleToBuckets(&expected_blocklisted_features_, sample); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples( |
| "BackForwardCache.HistoryNavigationOutcome." |
| "BlocklistedFeature"), |
| testing::UnorderedElementsAreArray(expected_blocklisted_features_)) |
| << location.ToString(); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples( |
| "BackForwardCache.AllSites.HistoryNavigationOutcome." |
| "BlocklistedFeature"), |
| testing::UnorderedElementsAreArray(expected_blocklisted_features_)) |
| << location.ToString(); |
| } |
| |
| private: |
| void AddSampleToBuckets(std::vector<base::Bucket>* buckets, |
| base::HistogramBase::Sample sample) { |
| auto it = base::ranges::find(*buckets, sample, &base::Bucket::min); |
| if (it == buckets->end()) { |
| buckets->push_back(base::Bucket(sample, 1)); |
| } else { |
| it->count++; |
| } |
| } |
| |
| base::HistogramTester histogram_tester_; |
| std::vector<base::Bucket> expected_blocklisted_features_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| constexpr char IsolateOriginsPrintBrowserTest::kIsolatedSite[]; |
| |
| class PrintExtensionBrowserTest : public extensions::ExtensionBrowserTest { |
| public: |
| PrintExtensionBrowserTest() = default; |
| ~PrintExtensionBrowserTest() override = default; |
| |
| void PrintAndWaitUntilPreviewIsReady() { |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false); |
| |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| print_preview_observer.WaitUntilPreviewIsReady(); |
| } |
| |
| void LoadExtensionAndNavigateToOptionPage() { |
| const extensions::Extension* extension = nullptr; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath test_data_dir; |
| base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); |
| extension = LoadExtension( |
| test_data_dir.AppendASCII("printing").AppendASCII("test_extension")); |
| extension_id_ = extension->id(); |
| ASSERT_TRUE(extension); |
| } |
| |
| GURL url(chrome::kChromeUIExtensionsURL); |
| std::string query = base::StringPrintf("options=%s", extension_id_.c_str()); |
| GURL::Replacements replacements; |
| replacements.SetQueryStr(query); |
| url = url.ReplaceComponents(replacements); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| } |
| |
| const extensions::ExtensionId& extension_id() const { return extension_id_; } |
| |
| private: |
| extensions::ExtensionId extension_id_; |
| }; |
| |
| class SitePerProcessPrintExtensionBrowserTest |
| : public PrintExtensionBrowserTest { |
| public: |
| // content::BrowserTestBase |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| content::IsolateAllSitesForTesting(command_line); |
| } |
| }; |
| |
| // Printing only a selection containing iframes is partially supported. |
| // Iframes aren't currently displayed. This test passes whenever the print |
| // preview is rendered (i.e. no timeout in the test). |
| // This test shouldn't crash. See https://crbug.com/732780. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, SelectionContainsIframe) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/selection_iframe.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| const PrintParams kParams{.print_only_selection = true}; |
| PrintAndWaitUntilPreviewIsReady(kParams); |
| } |
| |
| // https://crbug.com/1125972 |
| // https://crbug.com/1131598 |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, NoScrolling) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/with-scrollable.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression1[] = "iframe.contentWindow.scrollY"; |
| const char kExpression2[] = "scrollable.scrollTop"; |
| const char kExpression3[] = "shapeshifter.scrollTop"; |
| |
| double old_scroll1 = content::EvalJs(contents, kExpression1).ExtractDouble(); |
| double old_scroll2 = content::EvalJs(contents, kExpression2).ExtractDouble(); |
| double old_scroll3 = content::EvalJs(contents, kExpression3).ExtractDouble(); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| double new_scroll1 = content::EvalJs(contents, kExpression1).ExtractDouble(); |
| |
| // TODO(crbug.com/1131598): Perform the corresponding EvalJs() calls here and |
| // assign to new_scroll2 and new_scroll3, once the printing code has been |
| // fixed to handle these cases. Right now, the scroll offset jumps. |
| double new_scroll2 = old_scroll2; |
| double new_scroll3 = old_scroll3; |
| |
| EXPECT_EQ(old_scroll1, new_scroll1); |
| EXPECT_EQ(old_scroll2, new_scroll2); |
| EXPECT_EQ(old_scroll3, new_scroll3); |
| } |
| |
| // https://crbug.com/1131598 |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DISABLED_NoScrollingFrameset) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/frameset.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = |
| "document.getElementById('frame').contentWindow.scrollY"; |
| |
| double old_scroll = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| double new_scroll = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| EXPECT_EQ(old_scroll, new_scroll); |
| } |
| |
| // https://crbug.com/1125972 |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, NoScrollingVerticalRl) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/vertical-rl.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| // Test that entering print preview didn't mess up the scroll position. |
| EXPECT_EQ( |
| 0, content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), |
| "window.scrollX")); |
| } |
| |
| // https://crbug.com/1285208 |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, LegacyLayoutEngineFallback) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/legacy-layout-engine-known-bug.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = "target.offsetHeight"; |
| |
| // The non-printed document should be laid out with LayoutNG. We're testing |
| // this by looking for a known margin-collapsing / clearance bug in the legacy |
| // engine, not present in LayoutNG. The height should be 0 if the bug isn't |
| // present. |
| |
| double old_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| if (old_height != 0) { |
| // LayoutNG seems to be disabled. There's nothing useful to test here then. |
| return; |
| } |
| |
| // Entering print preview may trigger legacy engine fallback, but this should |
| // only be temporary. |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| // The non-printed document should still be laid out with LayoutNG. |
| double new_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| EXPECT_EQ(new_height, 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, LazyLoadedImagesFetched) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/lazy-loaded-image-offscreen.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = "target.offsetHeight"; |
| |
| double old_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| // The non-printed document should have loaded the image, which will have |
| // a different height. |
| double new_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| EXPECT_NE(old_height, new_height); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, LazyLoadedIframeFetched) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/lazy-loaded-iframe-offscreen.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = |
| "target.contentWindow.document.documentElement.clientHeight"; |
| |
| double old_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| double new_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| EXPECT_NE(old_height, new_height); |
| } |
| |
| // TODO(crbug.com/1305193) Reenable after flakes have been resolved. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| DISABLED_LazyLoadedIframeFetchedCrossOrigin) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/lazy-loaded-iframe-offscreen-cross-origin.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = "document.documentElement.clientHeight"; |
| |
| double old_height = |
| content::EvalJs(content::ChildFrameAt(contents, 0), kExpression) |
| .ExtractDouble(); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| double new_height = |
| content::EvalJs(content::ChildFrameAt(contents, 0), kExpression) |
| .ExtractDouble(); |
| |
| EXPECT_NE(old_height, new_height); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, LazyLoadedImagesFetchedScriptedPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/lazy-loaded-image-offscreen.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| const char kExpression[] = "target.offsetHeight"; |
| |
| double old_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| TestPrintViewManager* print_view_manager = |
| TestPrintViewManager::CreateForWebContents(web_contents); |
| |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| |
| // The non-printed document should have loaded the image, which will have |
| // a different height. |
| double new_height = content::EvalJs(contents, kExpression).ExtractDouble(); |
| EXPECT_NE(old_height, new_height); |
| } |
| |
| // Before invoking print preview, page scale is changed to a different value. |
| // Test that when print preview is ready, in other words when printing is |
| // finished, the page scale factor is still the same, and that it hasn't been |
| // messed up by printing. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, ResetPageScaleAfterPrintPreview) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| constexpr double kScaleFactor = 1.5; |
| contents->SetPageScale(kScaleFactor); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| double contents_page_scale_after_print = |
| content::EvalJs(contents, "window.visualViewport.scale").ExtractDouble(); |
| |
| EXPECT_EQ(kScaleFactor, contents_page_scale_after_print); |
| } |
| |
| // Printing frame content for the main frame of a generic webpage. |
| // This test passes when the printed result is sent back and checked in |
| // TestPrintRenderFrame::OnDidPrintFrameContent(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintFrameContent) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* rfh = original_contents->GetPrimaryMainFrame(); |
| CreateTestPrintRenderFrame(rfh, original_contents); |
| GetPrintRenderFrame(rfh)->PrintFrameContent(GetDefaultPrintFrameParams(), |
| base::DoNothing()); |
| |
| // The printed result will be received and checked in |
| // TestPrintRenderFrame. |
| WaitUntilCallbackReceived(); |
| } |
| |
| // Printing frame content for a cross-site iframe. |
| // This test passes when the iframe responds to the print message. |
| // The response is checked in TestPrintRenderFrame::OnDidPrintFrameContent(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeContent) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url( |
| embedded_test_server()->GetURL("/printing/content_with_iframe.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* test_frame = ChildFrameAt(original_contents, 0); |
| ASSERT_TRUE(test_frame); |
| |
| CreateTestPrintRenderFrame(test_frame, original_contents); |
| GetPrintRenderFrame(test_frame) |
| ->PrintFrameContent(GetDefaultPrintFrameParams(), base::DoNothing()); |
| |
| // The printed result will be received and checked in |
| // TestPrintRenderFrame. |
| WaitUntilCallbackReceived(); |
| } |
| |
| // Printing frame content with a cross-site iframe which also has a cross-site |
| // iframe. The site reference chain is a.com --> b.com --> c.com. |
| // This test passes when both cross-site frames are printed and their |
| // responses which are checked in |
| // TestPrintRenderFrame::OnDidPrintFrameContent(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeChain) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/content_with_iframe_chain.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| // Create composite client so subframe print message can be forwarded. |
| PrintCompositeClient::CreateForWebContents(original_contents); |
| |
| content::RenderFrameHost* main_frame = |
| original_contents->GetPrimaryMainFrame(); |
| content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0); |
| ASSERT_TRUE(child_frame); |
| ASSERT_NE(child_frame, main_frame); |
| bool oopif_enabled = child_frame->GetProcess() != main_frame->GetProcess(); |
| |
| content::RenderFrameHost* grandchild_frame = |
| content::ChildFrameAt(child_frame, 0); |
| ASSERT_TRUE(grandchild_frame); |
| ASSERT_NE(grandchild_frame, child_frame); |
| if (oopif_enabled) { |
| ASSERT_NE(grandchild_frame->GetProcess(), child_frame->GetProcess()); |
| ASSERT_NE(grandchild_frame->GetProcess(), main_frame->GetProcess()); |
| } |
| |
| CreateTestPrintRenderFrame(main_frame, original_contents); |
| if (oopif_enabled) { |
| CreateTestPrintRenderFrame(child_frame, original_contents); |
| CreateTestPrintRenderFrame(grandchild_frame, original_contents); |
| } |
| |
| GetPrintRenderFrame(main_frame) |
| ->PrintFrameContent(GetDefaultPrintFrameParams(), base::DoNothing()); |
| |
| // The printed result will be received and checked in |
| // TestPrintRenderFrame. |
| SetNumExpectedMessages(oopif_enabled ? 3 : 1); |
| WaitUntilCallbackReceived(); |
| } |
| |
| // Printing frame content with a cross-site iframe who also has a cross site |
| // iframe, but this iframe resides in the same site as the main frame. |
| // The site reference loop is a.com --> b.com --> a.com. |
| // This test passes when both cross-site frames are printed and send back |
| // responses which are checked in |
| // TestPrintRenderFrame::OnDidPrintFrameContent(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeABA) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "a.com", "/printing/content_with_iframe_loop.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| // Create composite client so subframe print message can be forwarded. |
| PrintCompositeClient::CreateForWebContents(original_contents); |
| |
| content::RenderFrameHost* main_frame = |
| original_contents->GetPrimaryMainFrame(); |
| content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0); |
| ASSERT_TRUE(child_frame); |
| ASSERT_NE(child_frame, main_frame); |
| bool oopif_enabled = main_frame->GetProcess() != child_frame->GetProcess(); |
| |
| content::RenderFrameHost* grandchild_frame = |
| content::ChildFrameAt(child_frame, 0); |
| ASSERT_TRUE(grandchild_frame); |
| ASSERT_NE(grandchild_frame, child_frame); |
| // `grandchild_frame` is in the same site as `frame`, so whether OOPIF is |
| // enabled, they will be in the same process. |
| ASSERT_EQ(grandchild_frame->GetProcess(), main_frame->GetProcess()); |
| |
| CreateTestPrintRenderFrame(main_frame, original_contents); |
| if (oopif_enabled) { |
| CreateTestPrintRenderFrame(child_frame, original_contents); |
| CreateTestPrintRenderFrame(grandchild_frame, original_contents); |
| } |
| |
| GetPrintRenderFrame(main_frame) |
| ->PrintFrameContent(GetDefaultPrintFrameParams(), base::DoNothing()); |
| |
| // The printed result will be received and checked in |
| // TestPrintRenderFrame. |
| SetNumExpectedMessages(oopif_enabled ? 3 : 1); |
| WaitUntilCallbackReceived(); |
| } |
| |
| // Printing frame content with a cross-site iframe before creating |
| // PrintCompositor by the main frame. |
| // This test passes if PrintCompositeClient queues subframes when |
| // it doesn't have PrintCompositor and clears them after PrintCompositor is |
| // created. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| PrintSubframeContentBeforeCompositeClientCreation) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url( |
| embedded_test_server()->GetURL("/printing/content_with_iframe.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // When OOPIF is not enabled, CompositorClient is not used. |
| if (!IsOopifEnabled()) |
| return; |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* main_frame = |
| original_contents->GetPrimaryMainFrame(); |
| ASSERT_TRUE(main_frame); |
| content::RenderFrameHost* test_frame = ChildFrameAt(main_frame, 0); |
| ASSERT_TRUE(test_frame); |
| ASSERT_NE(main_frame->GetProcess(), test_frame->GetProcess()); |
| |
| CreateTestPrintRenderFrame(main_frame, original_contents); |
| CreateTestPrintRenderFrame(test_frame, original_contents); |
| SetNumExpectedMessages(2); |
| |
| // Print on the main frame. |
| GetPrintRenderFrame(main_frame) |
| ->PrintFrameContent(GetDefaultPrintFrameParams(), base::DoNothing()); |
| |
| // The printed result will be received and checked in TestPrintRenderFrame. |
| WaitUntilCallbackReceived(); |
| |
| // As PrintFrameContent() with the main frame doesn't call |
| // PrintCompositeClient::DoCompositeDocumentToPdf() on this test, when |
| // PrintCompositeClient::OnDidPrintFrameContent() is called with the sub |
| // frame, it doesn't have mojom::PrintCompositor. |
| auto* client = PrintCompositeClient::FromWebContents(original_contents); |
| ASSERT_FALSE(client->compositor_); |
| |
| // When there is no mojom::PrintCompositor, PrintCompositeClient queues |
| // subframes and handles them when mojom::PrintCompositor is created. |
| // `requested_subframes_` should have the requested subframes. |
| ASSERT_EQ(1u, client->requested_subframes_.size()); |
| PrintCompositeClient::RequestedSubFrame* subframe_in_queue = |
| client->requested_subframes_.begin()->get(); |
| ASSERT_EQ(kDefaultDocumentCookie, subframe_in_queue->document_cookie_); |
| ASSERT_EQ(test_frame->GetGlobalId(), subframe_in_queue->rfh_id_); |
| |
| // Creates mojom::PrintCompositor. |
| client->CompositeDocument( |
| kDefaultDocumentCookie, main_frame, |
| *TestPrintRenderFrame::GetDefaultDidPrintContentParams(), |
| ui::AXTreeUpdate(), PrintCompositeClient::GetDocumentType(), |
| base::DoNothing()); |
| ASSERT_TRUE(client->GetCompositeRequest(kDefaultDocumentCookie)); |
| // `requested_subframes_` should be empty. |
| ASSERT_TRUE(client->requested_subframes_.empty()); |
| } |
| |
| // Printing preview a simple webpage when site per process is enabled. |
| // Test that the basic oopif printing should succeed. The test should not crash |
| // or timed out. There could be other reasons that cause the test fail, but the |
| // most obvious ones would be font access outage or web sandbox support being |
| // absent because we explicitly check these when pdf compositor service starts. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, BasicPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| } |
| |
| // Printing a web page with a dead subframe for site per process should succeed. |
| // This test passes whenever the print preview is rendered. This should not be |
| // a timed out test which indicates the print preview hung. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, |
| SubframeUnavailableBeforePrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url( |
| embedded_test_server()->GetURL("/printing/content_with_iframe.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* test_frame = ChildFrameAt(original_contents, 0); |
| ASSERT_TRUE(test_frame); |
| ASSERT_TRUE(test_frame->IsRenderFrameLive()); |
| // Wait for the renderer to be down. |
| content::RenderProcessHostWatcher render_process_watcher( |
| test_frame->GetProcess(), |
| content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| // Shutdown the subframe. |
| ASSERT_TRUE(test_frame->GetProcess()->Shutdown(0)); |
| render_process_watcher.Wait(); |
| ASSERT_FALSE(test_frame->IsRenderFrameLive()); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| } |
| |
| // If a subframe dies during printing, the page printing should still succeed. |
| // This test passes whenever the print preview is rendered. This should not be |
| // a timed out test which indicates the print preview hung. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, |
| SubframeUnavailableDuringPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url( |
| embedded_test_server()->GetURL("/printing/content_with_iframe.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* subframe = ChildFrameAt(original_contents, 0); |
| ASSERT_TRUE(subframe); |
| auto* subframe_rph = subframe->GetProcess(); |
| |
| KillPrintRenderFrame frame_content(subframe_rph); |
| frame_content.OverrideBinderForTesting(subframe); |
| |
| // Waits for the renderer to be down. |
| content::RenderProcessHostWatcher process_watcher( |
| subframe_rph, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| |
| // Adds the observer to get the status for the preview. |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false); |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| // Makes sure that `subframe_rph` is terminated. |
| process_watcher.Wait(); |
| // Confirms that the preview pages are rendered. |
| print_preview_observer.WaitUntilPreviewIsReady(); |
| } |
| |
| // Printing preview a web page with an iframe from an isolated origin. |
| // This test passes whenever the print preview is rendered. This should not be |
| // a timed out test which indicates the print preview hung or crash. |
| IN_PROC_BROWSER_TEST_F(IsolateOriginsPrintBrowserTest, PrintIsolatedSubframe) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL( |
| "/printing/content_with_same_site_iframe.html")); |
| GURL isolated_url( |
| embedded_test_server()->GetURL(kIsolatedSite, "/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* original_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_TRUE(NavigateIframeToURL(original_contents, "iframe", isolated_url)); |
| |
| auto* main_frame = original_contents->GetPrimaryMainFrame(); |
| auto* subframe = ChildFrameAt(main_frame, 0); |
| ASSERT_NE(main_frame->GetProcess(), subframe->GetProcess()); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| } |
| |
| // Printing preview a webpage. |
| // Test that we use oopif printing by default when full site isolation is |
| // enabled. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, RegularPrinting) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| EXPECT_EQ(content::AreAllSitesIsolatedForTesting(), IsOopifEnabled()); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Test that if user allows printing after being shown a warning due to DLP |
| // restrictions, the print preview is rendered. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPWarnAllowed) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, |
| TestPrintViewManagerForDLP::RestrictionLevel::kWarnAllow); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kAllowed); |
| } |
| |
| // Test that if user cancels printing after being shown a warning due to DLP |
| // restrictions, the print preview is not rendered. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPWarnCanceled) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, |
| TestPrintViewManagerForDLP::RestrictionLevel::kWarnCancel); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kDisallowed); |
| } |
| |
| // Test that if printing is blocked due to DLP restrictions, the print preview |
| // is not rendered. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPBlocked) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, TestPrintViewManagerForDLP::RestrictionLevel::kBlock); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kDisallowed); |
| } |
| |
| // Test that if user allows printing after being shown a warning due to DLP |
| // restrictions, the print preview is rendered when initiated by window.print(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPWarnAllowedWithWindowDotPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, |
| TestPrintViewManagerForDLP::RestrictionLevel::kWarnAllow); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kAllowed); |
| } |
| |
| // Test that if user cancels printing after being shown a warning due to DLP |
| // restrictions, the print preview is not rendered when initiated by |
| // window.print(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPWarnCanceledWithWindowDotPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, |
| TestPrintViewManagerForDLP::RestrictionLevel::kWarnCancel); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kDisallowed); |
| } |
| |
| // Test that if printing is blocked due to DLP restrictions, the print preview |
| // is not rendered when initiated by window.print(). |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DLPBlockedWithWindowDotPrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| // Set up the print view manager and DLP restrictions. |
| TestPrintViewManagerForDLP* print_view_manager = |
| TestPrintViewManagerForDLP::CreateForWebContents( |
| web_contents, TestPrintViewManagerForDLP::RestrictionLevel::kBlock); |
| |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kUnknown); |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_view_manager->WaitUntilPreviewIsShownOrCancelled(); |
| ASSERT_EQ(print_view_manager->GetPrintAllowance(), |
| TestPrintViewManagerForDLP::PrintAllowance::kDisallowed); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Printing preview a webpage with isolate-origins enabled. |
| // Test that we will use oopif printing for this case. |
| IN_PROC_BROWSER_TEST_F(IsolateOriginsPrintBrowserTest, OopifPrinting) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/test1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| EXPECT_TRUE(IsOopifEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(BackForwardCachePrintBrowserTest, DisableCaching) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| |
| // 1) Navigate to A and trigger printing. |
| GURL url(embedded_test_server()->GetURL( |
| "a.com", "/back_forward_cache/no-favicon.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| content::RenderFrameHost* rfh_a = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| // 2) Navigate to B. |
| // The first page is not cached because printing preview was open. |
| GURL url_2(embedded_test_server()->GetURL( |
| "b.com", "/back_forward_cache/no-favicon.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_2)); |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| |
| // 3) Navigate back and checks the blocklisted feature is recorded in UMA. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| ExpectBlocklistedFeature( |
| blink::scheduler::WebSchedulerTrackedFeature::kPrinting, FROM_HERE); |
| } |
| |
| // Printing an extension option page. |
| // The test should not crash or timeout. |
| IN_PROC_BROWSER_TEST_F(PrintExtensionBrowserTest, PrintOptionPage) { |
| LoadExtensionAndNavigateToOptionPage(); |
| PrintAndWaitUntilPreviewIsReady(); |
| } |
| |
| // Test fetching an extension printer. |
| IN_PROC_BROWSER_TEST_F(PrintExtensionBrowserTest, |
| UpdatePrintSettingsExtensionPrinter) { |
| // Size and printable area are in device units, which is different for macOS. |
| // See PrintSettings::device_units_per_inch(). |
| #if BUILDFLAG(IS_MAC) |
| static constexpr gfx::SizeF kLetterPdfPhysicalSize{612, 792}; |
| static constexpr gfx::RectF kExpectedPrintableArea{72, 72, 432, 684}; |
| static constexpr gfx::SizeF kExpectedContentSize{432, 656}; |
| #else |
| static constexpr gfx::SizeF kLetterPdfPhysicalSize{2550, 3300}; |
| static constexpr gfx::RectF kExpectedPrintableArea{300, 300, 1800, 2850}; |
| static constexpr gfx::SizeF kExpectedContentSize{1800, 2732}; |
| #endif |
| LoadExtensionAndNavigateToOptionPage(); |
| |
| // The extension id may vary from device to device, so directly use the |
| // extension id instead of a hardcoded value. |
| const char* test_extension_id = extension_id().c_str(); |
| std::string extension_printer_settings = |
| base::StringPrintf(kStickySettingsWithExtensionPrinter, test_extension_id, |
| test_extension_id); |
| |
| // Setting a recent destination as an extension printer triggers extension |
| // printer handling instead of defaulting to "Save as PDF" or a local printer. |
| auto* sticky_settings = PrintPreviewStickySettings::GetInstance(); |
| sticky_settings->StoreAppState(extension_printer_settings); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| TestPrintViewManager print_view_manager(web_contents); |
| PrintViewManager::SetReceiverImplForTesting(&print_view_manager); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| const mojom::PrintPagesParamsPtr& snooped_params = |
| print_view_manager.snooped_params(); |
| ASSERT_TRUE(snooped_params); |
| EXPECT_EQ(gfx::Size(kDefaultPdfDpi, kDefaultPdfDpi), |
| snooped_params->params->dpi); |
| EXPECT_EQ(kLetterPdfPhysicalSize, snooped_params->params->page_size); |
| // This must be in sync with |
| // //chrome/test/data/printing/test_extension/background.js. |
| EXPECT_EQ(kExpectedPrintableArea, snooped_params->params->printable_area); |
| EXPECT_EQ(kExpectedContentSize, snooped_params->params->content_size); |
| } |
| |
| // Test fetching an extension printer that has missing printable area. The |
| // printable area should be set to a default value. |
| IN_PROC_BROWSER_TEST_F( |
| PrintExtensionBrowserTest, |
| UpdatePrintSettingsExtensionPrinterMissingPrintableArea) { |
| // Size is in device units, which is different for macOS. See |
| // PrintSettings::device_units_per_inch(). |
| #if BUILDFLAG(IS_MAC) |
| static constexpr gfx::SizeF kIsoA4PdfPhysicalSize{595, 841}; |
| #else |
| static constexpr gfx::SizeF kIsoA4PdfPhysicalSize{2480, 3507}; |
| #endif |
| |
| LoadExtensionAndNavigateToOptionPage(); |
| |
| // The extension id may vary from device to device, so directly use the |
| // extension id instead of a hardcoded value. |
| const char* test_extension_id = extension_id().c_str(); |
| std::string extension_printer_settings = base::StringPrintf( |
| kStickySettingsWithExtensionPrinterMissingPrintableArea, |
| test_extension_id, test_extension_id); |
| |
| // Setting a recent destination as an extension printer triggers extension |
| // printer handling instead of defaulting to "Save as PDF" or a local printer. |
| auto* sticky_settings = PrintPreviewStickySettings::GetInstance(); |
| sticky_settings->StoreAppState(extension_printer_settings); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| TestPrintViewManager print_view_manager(web_contents); |
| PrintViewManager::SetReceiverImplForTesting(&print_view_manager); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| const mojom::PrintPagesParamsPtr& snooped_params = |
| print_view_manager.snooped_params(); |
| ASSERT_TRUE(snooped_params); |
| EXPECT_EQ(gfx::Size(kDefaultPdfDpi, kDefaultPdfDpi), |
| snooped_params->params->dpi); |
| EXPECT_EQ(kIsoA4PdfPhysicalSize, snooped_params->params->page_size); |
| // The default printable area is platform-dependent, so just check that the |
| // printable area and content size are non-empty. |
| EXPECT_FALSE(snooped_params->params->printable_area.IsEmpty()); |
| EXPECT_FALSE(snooped_params->params->content_size.IsEmpty()); |
| } |
| |
| // Printing an extension option page with site per process is enabled. |
| // The test should not crash or timeout. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintExtensionBrowserTest, |
| PrintOptionPage) { |
| LoadExtensionAndNavigateToOptionPage(); |
| PrintAndWaitUntilPreviewIsReady(); |
| } |
| |
| // Printing frame content for the main frame of a generic webpage with N-up |
| // printing. This is a regression test for https://crbug.com/937247 |
| // TODO(crbug.com/1371776): Fix flakiness and re-enable. |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, DISABLED_PrintNup) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/7_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| TestPrintViewManager print_view_manager(web_contents); |
| PrintViewManager::SetReceiverImplForTesting(&print_view_manager); |
| |
| // Override print parameters to do N-up, specify 4 pages per sheet. |
| const PrintParams kParams{.pages_per_sheet = 4}; |
| PrintAndWaitUntilPreviewIsReady(kParams); |
| |
| PrintViewManager::SetReceiverImplForTesting(nullptr); |
| |
| // With 4 pages per sheet requested by `GetPrintParams()`, a 7 page input |
| // will result in 2 pages in the print preview. |
| EXPECT_EQ(rendered_page_count(), 2u); |
| } |
| |
| // Site per process version of PrintBrowserTest.PrintNup. |
| // TODO(crbug.com/1371776): Fix flakiness and re-enable. |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, DISABLED_PrintNup) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/7_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| TestPrintViewManager print_view_manager(web_contents); |
| PrintViewManager::SetReceiverImplForTesting(&print_view_manager); |
| |
| // Override print parameters to do N-up, specify 4 pages per sheet. |
| const PrintParams kParams{.pages_per_sheet = 4}; |
| PrintAndWaitUntilPreviewIsReady(kParams); |
| |
| PrintViewManager::SetReceiverImplForTesting(nullptr); |
| |
| // With 4 pages per sheet requested by `GetPrintParams()`, a 7 page input |
| // will result in 2 pages in the print preview. |
| EXPECT_EQ(rendered_page_count(), 2u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, MultipagePrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| |
| EXPECT_EQ(rendered_page_count(), 3u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, MultipagePrint) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| |
| EXPECT_EQ(rendered_page_count(), 3u); |
| } |
| |
| // Disabled due to flakiness: crbug.com/1311998 |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| DISABLED_PDFPluginNotKeyboardFocusable) { |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true); |
| test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents()); |
| content::WebContents* preview_dialog = |
| print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog(); |
| ASSERT_TRUE(preview_dialog); |
| |
| // The script will ensure we return the id of <zoom-out-button> when |
| // focused. Focus the element after PDF plugin in tab order. |
| const char kScript[] = R"( |
| new Promise(resolve => { |
| const button = document.getElementsByTagName('print-preview-app')[0] |
| .$['previewArea'] |
| .shadowRoot.querySelector('iframe') |
| .contentDocument.querySelector('pdf-viewer-pp') |
| .shadowRoot.querySelector('#zoomToolbar') |
| .$['zoom-out-button']; |
| button.addEventListener('focus', (e) => { |
| window.domAutomationController.send(e.target.id); |
| }); |
| |
| const select_tag = document.getElementsByTagName('print-preview-app')[0] |
| .$['sidebar'] |
| .$['destinationSettings'] |
| .$['destinationSelect']; |
| select_tag.addEventListener('focus', () => { |
| resolve(true); |
| }); |
| select_tag.focus(); |
| }); |
| )"; |
| ASSERT_EQ(true, content::EvalJs(preview_dialog, kScript)); |
| |
| // Simulate a <shift-tab> press and wait for a focus message. |
| content::DOMMessageQueue msg_queue(preview_dialog); |
| SimulateKeyPress(preview_dialog, ui::DomKey::TAB, ui::DomCode::TAB, |
| ui::VKEY_TAB, false, true, false, false); |
| std::string reply; |
| ASSERT_TRUE(msg_queue.WaitForMessage(&reply)); |
| // Pressing <shift-tab> should focus the last toolbar element |
| // (zoom-out-button) instead of PDF plugin. |
| EXPECT_EQ("\"zoom-out-button\"", reply); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, WindowDotPrint) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false); |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_preview_observer.WaitUntilPreviewIsReady(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PdfWindowDotPrint) { |
| // Do not add any other printers; will default to "Save as PDF". |
| |
| // Load test page and check the initial state. |
| const GURL kUrl(embedded_test_server()->GetURL("/printing/hello_world.pdf")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true); |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), |
| "window.print();"); |
| print_preview_observer.WaitUntilPreviewIsReady(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| WindowDotPrintTriggersBeforeAfterEvents) { |
| // Load test page and check the initial state. |
| const GURL kUrl( |
| embedded_test_server()->GetURL("/printing/on_before_after_events.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame(); |
| EXPECT_EQ(false, content::EvalJs(rfh, "firedBeforePrint")); |
| EXPECT_EQ(false, content::EvalJs(rfh, "firedAfterPrint")); |
| |
| // Set up the PrintPreviewDoneObserver early, as it needs to intercept Mojo |
| // IPCs before they start. |
| PrintPreviewDoneObserver done_observer(rfh, GetPrintRenderFrame(rfh).get()); |
| |
| // Load Print Preview and make sure the beforeprint event fired. |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false); |
| content::ExecuteScriptAsync(rfh, "window.print();"); |
| print_preview_observer.WaitUntilPreviewIsReady(); |
| EXPECT_EQ(true, content::EvalJs(rfh, "firedBeforePrint")); |
| EXPECT_EQ(false, content::EvalJs(rfh, "firedAfterPrint")); |
| |
| // Close the Print Preview dialog and make sure the afterprint event fired. |
| auto* web_contents_modal_dialog_manager = |
| web_modal::WebContentsModalDialogManager::FromWebContents(web_contents); |
| ASSERT_TRUE(web_contents_modal_dialog_manager); |
| web_contents_modal_dialog_manager->CloseAllDialogs(); |
| done_observer.WaitForPrintPreviewDialogClosed(); |
| EXPECT_EQ(true, content::EvalJs(rfh, "firedBeforePrint")); |
| EXPECT_EQ(true, content::EvalJs(rfh, "firedAfterPrint")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| WindowDotPrintWhilePrintPreviewIsInProgress) { |
| const char kHtmlData[] = "data:text/html,hello"; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kHtmlData))); |
| |
| PrintAndWaitUntilPreviewIsReady(); |
| |
| // This should not crash the renderer. In older builds, this can trigger 2 |
| // beforeprint events in a row without an afterprint event in between. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(content::ExecJs(web_contents, "window.print()")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, |
| WindowDotPrintWhilePrintPreviewForNodeIsInProgress) { |
| // This test is a bit quirky and brittle: |
| // - `ContextMenuWaiter` does not seem to work in general, but does work for |
| // the data scheme. |
| // - Normally only PDF nodes can be printed, but loading PDFs in an iframe |
| // reliably is very hard, so use an image instead. |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| const char kHtmlData[] = |
| "data:text/html," |
| "<iframe src='data:image/png;base64,%s' name='imageframe'>"; |
| base::FilePath image_path = |
| base::PathService::CheckedGet(chrome::DIR_TEST_DATA) |
| .AppendASCII("printing") |
| .AppendASCII("test1.png"); |
| std::optional<std::vector<uint8_t>> image_data = |
| base::ReadFileToBytes(image_path); |
| ASSERT_TRUE(image_data.has_value()); |
| GURL data_url(base::StringPrintf( |
| kHtmlData, base::Base64Encode(image_data.value()).c_str())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), data_url)); |
| } |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame(); |
| ASSERT_TRUE(main_rfh); |
| |
| content::RenderFrameHost* iframe_rfh = nullptr; |
| main_rfh->ForEachRenderFrameHost( |
| [&iframe_rfh](content::RenderFrameHost* rfh) { |
| if (rfh->GetFrameName() != "imageframe") { |
| return; |
| } |
| |
| DCHECK(!iframe_rfh); // There can be only 1. |
| iframe_rfh = rfh; |
| }); |
| ASSERT_TRUE(iframe_rfh); |
| EXPECT_NE(main_rfh, iframe_rfh); |
| |
| { |
| // `ContextMenuWaiter` can issue `IDC_PRINT` even though it is not in the |
| // menu. |
| ContextMenuWaiter menu_observer(IDC_PRINT); |
| |
| // Send mouse right-click to activate context menu. Otherwise the renderer |
| // does not have a WebNode to print. |
| content::SimulateMouseClickAt(web_contents, /*modifiers=*/0, |
| blink::WebMouseEvent::Button::kRight, |
| gfx::Point(100, 100)); |
| menu_observer.WaitForMenuOpenAndClose(); |
| EXPECT_EQ(menu_observer.params().media_type, |
| blink::mojom::ContextMenuDataMediaType::kImage); |
| } |
| |
| // This should not crash the renderer. In older builds, this can trigger 2 |
| // beforeprint events in a row without an afterprint event in between. |
| ASSERT_TRUE(content::ExecJs(iframe_rfh, "window.print()")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, NoResizeEvent) { |
| const GURL kUrl( |
| embedded_test_server()->GetURL("/printing/resize_event_counter.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame(); |
| |
| // In case there's some resizing taking place before printing, keep track of |
| // it. |
| int before = content::EvalJs(rfh, "resizeCount").ExtractInt(); |
| |
| // Printing itself should not trigger window resize events. |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| |
| int after = content::EvalJs(rfh, "resizeCount").ExtractInt(); |
| EXPECT_EQ(before, after); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintBrowserTest, SpecifiedPageSizeCrash) { |
| const GURL kUrl( |
| embedded_test_server()->GetURL("/printing/specified_page_size.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| |
| // Pass if no crash. A specified page size may trigger the print preview UI to |
| // restart and regenerate print layout. |
| // See PrintPreviewAppElement.StateChanged_. |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| } |
| |
| class PrintPrerenderBrowserTest |
| : public PrintBrowserTest, |
| public testing::WithParamInterface<std::string> { |
| public: |
| PrintPrerenderBrowserTest() |
| : prerender_helper_( |
| base::BindRepeating(&PrintPrerenderBrowserTest::web_contents, |
| base::Unretained(this))) {} |
| |
| void SetUpCommandLine(base::CommandLine* cmd_line) override { |
| cmd_line->AppendSwitch(switches::kDisablePrintPreview); |
| PrintBrowserTest::SetUpCommandLine(cmd_line); |
| } |
| |
| void SetUp() override { |
| prerender_helper_.RegisterServerRequestMonitor(embedded_test_server()); |
| PrintBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| protected: |
| std::string GetTargetHint() { return GetParam(); } |
| |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| PrintPrerenderBrowserTest, |
| testing::Values("_self", "_blank"), |
| [](const testing::TestParamInfo<std::string>& info) { |
| return info.param; |
| }); |
| |
| // Test that print() is silently ignored. |
| // https://wicg.github.io/nav-speculation/prerendering.html#patch-modals |
| IN_PROC_BROWSER_TEST_P(PrintPrerenderBrowserTest, QuietBlockWithWindowPrint) { |
| // Navigate to an initial page. |
| const GURL kUrl(embedded_test_server()->GetURL("/empty.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| |
| // Start a prerender. |
| GURL prerender_url = |
| embedded_test_server()->GetURL("/printing/prerendering.html"); |
| |
| int prerender_id = prerender_helper_.AddPrerender( |
| prerender_url, /*eagerness=*/std::nullopt, GetTargetHint()); |
| auto* prerender_web_contents = |
| content::WebContents::FromFrameTreeNodeId(prerender_id); |
| content::RenderFrameHost* prerender_host = |
| content::test::PrerenderTestHelper::GetPrerenderedMainFrameHost( |
| *prerender_web_contents, prerender_id); |
| |
| content::WebContentsConsoleObserver console_observer(prerender_web_contents); |
| EXPECT_EQ(0u, console_observer.messages().size()); |
| |
| // Try to print by JS during prerendering. |
| EXPECT_EQ(true, content::ExecJs(prerender_host, "window.print();", |
| content::EXECUTE_SCRIPT_NO_USER_GESTURE)); |
| EXPECT_EQ(false, content::EvalJs(prerender_host, "firedBeforePrint")); |
| EXPECT_EQ(false, content::EvalJs(prerender_host, "firedAfterPrint")); |
| EXPECT_EQ(1u, console_observer.messages().size()); |
| } |
| |
| // Test that execCommand('print') is silently ignored. |
| // execCommand() is not specced, but |
| // https://wicg.github.io/nav-speculation/prerendering.html#patch-modals |
| // indicates the intent to silently ignore print APIs. |
| IN_PROC_BROWSER_TEST_P(PrintPrerenderBrowserTest, |
| QuietBlockWithDocumentExecCommand) { |
| // Navigate to an initial page. |
| const GURL kUrl(embedded_test_server()->GetURL("/empty.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl)); |
| |
| // Start a prerender. |
| GURL prerender_url = |
| embedded_test_server()->GetURL("/printing/prerendering.html"); |
| |
| int prerender_id = prerender_helper_.AddPrerender( |
| prerender_url, /*eagerness=*/std::nullopt, GetTargetHint()); |
| auto* prerender_web_contents = |
| content::WebContents::FromFrameTreeNodeId(prerender_id); |
| content::RenderFrameHost* prerender_host = |
| content::test::PrerenderTestHelper::GetPrerenderedMainFrameHost( |
| *prerender_web_contents, prerender_id); |
| |
| content::WebContentsConsoleObserver console_observer(prerender_web_contents); |
| EXPECT_EQ(0u, console_observer.messages().size()); |
| |
| // Try to print by JS during prerendering. |
| EXPECT_EQ(false, |
| content::EvalJs(prerender_host, "document.execCommand('print');")); |
| EXPECT_EQ(false, content::EvalJs(prerender_host, "firedBeforePrint")); |
| EXPECT_EQ(false, content::EvalJs(prerender_host, "firedAfterPrint")); |
| EXPECT_EQ(1u, console_observer.messages().size()); |
| } |
| |
| class PrintFencedFrameBrowserTest : public PrintBrowserTest { |
| public: |
| PrintFencedFrameBrowserTest() { |
| fenced_frame_helper_ = |
| std::make_unique<content::test::FencedFrameTestHelper>(); |
| } |
| ~PrintFencedFrameBrowserTest() override = default; |
| |
| void SetUpOnMainThread() override { |
| PrintBrowserTest::SetUpOnMainThread(); |
| https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(https_server_.Start()); |
| } |
| |
| PrintFencedFrameBrowserTest(const PrintFencedFrameBrowserTest&) = delete; |
| PrintFencedFrameBrowserTest& operator=(const PrintFencedFrameBrowserTest&) = |
| delete; |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::test::FencedFrameTestHelper* fenced_frame_test_helper() { |
| return fenced_frame_helper_.get(); |
| } |
| |
| protected: |
| content::RenderFrameHost* CreateFencedFrame( |
| content::RenderFrameHost* fenced_frame_parent, |
| const GURL& url) { |
| if (fenced_frame_helper_) |
| return fenced_frame_helper_->CreateFencedFrame(fenced_frame_parent, url); |
| |
| // FencedFrameTestHelper only supports the MPArch version of fenced frames. |
| // So need to maually create a fenced frame for the ShadowDOM version. |
| content::TestNavigationManager navigation(web_contents(), url); |
| constexpr char kAddFencedFrameScript[] = R"({ |
| const fenced_frame = document.createElement('fencedframe'); |
| fenced_frame.src = $1; |
| document.body.appendChild(fenced_frame); |
| })"; |
| EXPECT_TRUE(ExecJs(fenced_frame_parent, |
| content::JsReplace(kAddFencedFrameScript, url))); |
| EXPECT_TRUE(navigation.WaitForNavigationFinished()); |
| |
| content::RenderFrameHost* new_frame = ChildFrameAt(fenced_frame_parent, 0); |
| |
| return new_frame; |
| } |
| |
| void RunPrintTest(const std::string& print_command) { |
| // Navigate to an initial page. |
| const GURL url(https_server_.GetURL("/empty.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Load a fenced frame. |
| GURL fenced_frame_url = https_server_.GetURL("/fenced_frames/title1.html"); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* fenced_frame_host = CreateFencedFrame( |
| web_contents->GetPrimaryMainFrame(), fenced_frame_url); |
| ASSERT_TRUE(fenced_frame_host); |
| content::WebContentsConsoleObserver console_observer(web_contents); |
| EXPECT_EQ(0u, console_observer.messages().size()); |
| |
| constexpr char kAddListenersScript[] = R"( |
| (async () => { |
| let firedBeforePrint = false; |
| let firedAfterPrint = false; |
| window.addEventListener('beforeprint', () => { |
| firedBeforePrint = true; |
| }); |
| window.addEventListener('afterprint', () => { |
| firedAfterPrint = true; |
| }); |
| %s |
| return 'beforeprint: ' + firedBeforePrint + |
| ', afterprint: ' + firedAfterPrint; |
| })(); |
| )"; |
| const std::string test_script = |
| base::StringPrintf(kAddListenersScript, print_command.c_str()); |
| |
| EXPECT_EQ("beforeprint: false, afterprint: false", |
| content::EvalJs(fenced_frame_host, test_script)); |
| ASSERT_TRUE(console_observer.Wait()); |
| ASSERT_EQ(1u, console_observer.messages().size()); |
| EXPECT_EQ( |
| "Ignored call to 'print()'. The document is in a fenced frame tree.", |
| console_observer.GetMessageAt(0)); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| std::unique_ptr<content::test::FencedFrameTestHelper> fenced_frame_helper_; |
| net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PrintFencedFrameBrowserTest, ScriptedPrint) { |
| RunPrintTest("window.print();"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrintFencedFrameBrowserTest, DocumentExecCommand) { |
| RunPrintTest("document.execCommand('print');"); |
| } |
| |
| } // namespace printing |