[go: nahoru, domu]

blob: c5bf0918b32e38f7b2b9bb08626a0536893b686c [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/printing/print_job_manager.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/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.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 "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.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/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "extensions/common/extension.h"
#include "mojo/public/cpp/bindings/associated_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/mojom/print.mojom.h"
#include "printing/print_settings.h"
#include "printing/printing_context.h"
#include "printing/printing_context_factory_for_test.h"
#include "printing/test_printing_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/types/optional.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"
namespace printing {
namespace {
constexpr int kPrinterCapabilitiesMaxCopies = 99;
constexpr int kPrintSettingsCopies = 42;
constexpr int kDefaultDocumentCookie = 1234;
mojom::PrintParamsPtr GetPrintParams() {
auto params = mojom::PrintParams::New();
params->page_size = gfx::Size(612, 792);
params->content_size = gfx::Size(540, 720);
params->printable_area = gfx::Rect(612, 792);
params->dpi = gfx::Size(72, 72);
params->document_cookie = kDefaultDocumentCookie;
params->pages_per_sheet = 4;
params->printed_doc_type = IsOopifEnabled() ? mojom::SkiaDocumentType::kMSKP
: mojom::SkiaDocumentType::kPDF;
return params;
}
void UpdatePrintSettingsReplyOnIO(
std::unique_ptr<PrintSettings>& snooped_settings,
scoped_refptr<PrintQueriesQueue> queue,
std::unique_ptr<PrinterQuery> printer_query,
mojom::PrintManagerHost::UpdatePrintSettingsCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(printer_query);
auto params = mojom::PrintPagesParams::New();
params->params = mojom::PrintParams::New();
if (printer_query->last_status() == mojom::ResultCode::kSuccess) {
RenderParamsFromPrintSettings(printer_query->settings(),
params->params.get());
params->params->document_cookie = printer_query->cookie();
params->pages = PageRange::GetPages(printer_query->settings().ranges());
snooped_settings =
std::make_unique<PrintSettings>(printer_query->settings());
}
bool canceled = printer_query->last_status() == mojom::ResultCode::kCanceled;
params->params = GetPrintParams();
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
[](mojom::PrintManagerHost::UpdatePrintSettingsCallback callback,
mojom::PrintPagesParamsPtr params, bool canceled) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::move(callback).Run(std::move(params), canceled);
},
std::move(callback), std::move(params), canceled));
if (printer_query->cookie() && printer_query->settings().dpi()) {
queue->QueuePrinterQuery(std::move(printer_query));
} else {
printer_query->StopWorker();
}
}
void UpdatePrintSettingsOnIO(
std::unique_ptr<PrintSettings>& snooped_settings,
int32_t cookie,
mojom::PrintManagerHost::UpdatePrintSettingsCallback callback,
scoped_refptr<PrintQueriesQueue> queue,
base::Value job_settings) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
std::unique_ptr<PrinterQuery> printer_query = queue->PopPrinterQuery(cookie);
if (!printer_query) {
printer_query = queue->CreatePrinterQuery(
content::ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
}
auto* printer_query_ptr = printer_query.get();
printer_query_ptr->SetSettings(
std::move(job_settings),
base::BindOnce(&UpdatePrintSettingsReplyOnIO, std::ref(snooped_settings),
queue, std::move(printer_query), std::move(callback)));
}
class PrintPreviewObserver : PrintPreviewUI::TestDelegate {
public:
explicit PrintPreviewObserver(bool wait_for_loaded) {
if (wait_for_loaded)
queue_.emplace(); // DOMMessageQueue doesn't allow assignment
PrintPreviewUI::SetDelegateForTesting(this);
}
PrintPreviewObserver(const PrintPreviewObserver&) = delete;
PrintPreviewObserver& operator=(const PrintPreviewObserver&) = delete;
~PrintPreviewObserver() override {
PrintPreviewUI::SetDelegateForTesting(nullptr);
}
void WaitUntilPreviewIsReady() {
if (rendered_page_count_ >= total_page_count_)
return;
base::RunLoop run_loop;
base::AutoReset<base::RunLoop*> auto_reset(&run_loop_, &run_loop);
run_loop.Run();
if (queue_.has_value()) {
std::string message;
EXPECT_TRUE(queue_->WaitForMessage(&message));
EXPECT_EQ("\"success\"", message);
}
}
content::WebContents* GetPrintPreviewDialog() { return preview_dialog_; }
private:
// PrintPreviewUI::TestDelegate:
void DidGetPreviewPageCount(uint32_t page_count) override {
total_page_count_ = page_count;
}
// PrintPreviewUI::TestDelegate:
void DidRenderPreviewPage(content::WebContents* preview_dialog) override {
++rendered_page_count_;
CHECK(rendered_page_count_ <= total_page_count_);
if (rendered_page_count_ == total_page_count_ && run_loop_) {
run_loop_->Quit();
preview_dialog_ = preview_dialog;
if (queue_.has_value()) {
content::ExecuteScriptAsync(
preview_dialog,
"window.addEventListener('message', event => {"
" if (event.data.type === 'documentLoaded') {"
" domAutomationController.send(event.data.load_state);"
" }"
"});");
}
}
}
absl::optional<content::DOMMessageQueue> queue_;
uint32_t total_page_count_ = 1;
uint32_t rendered_page_count_ = 0;
content::WebContents* preview_dialog_ = nullptr;
base::RunLoop* run_loop_ = nullptr;
};
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::SequencedTaskRunnerHandle::Get()),
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_, 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_, i);
}
}
private:
content::RenderFrameHost* frame_host_;
content::WebContents* web_contents_;
const int document_cookie_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::RepeatingClosure msg_callback_;
mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this};
};
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:
content::RenderProcessHost* const rph_;
mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this};
};
} // namespace
class TestPrintViewManager : public PrintViewManager {
public:
explicit TestPrintViewManager(content::WebContents* web_contents)
: PrintViewManager(web_contents) {}
TestPrintViewManager(const TestPrintViewManager&) = delete;
TestPrintViewManager& operator=(const TestPrintViewManager&) = delete;
~TestPrintViewManager() override = default;
PrintSettings* snooped_settings() { return snooped_settings_.get(); }
private:
// printing::mojom::PrintManagerHost:
void UpdatePrintSettings(int32_t cookie,
base::Value job_settings,
UpdatePrintSettingsCallback callback) override {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&UpdatePrintSettingsOnIO, std::ref(snooped_settings_),
cookie, std::move(callback), queue_,
std::move(job_settings)));
}
std::unique_ptr<PrintSettings> snooped_settings_;
};
class PrintBrowserTest : public InProcessBrowserTest {
public:
PrintBrowserTest() = default;
~PrintBrowserTest() override = default;
void SetUp() override {
num_expected_messages_ = 1; // By default, only wait on one message.
num_received_messages_ = 0;
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) {
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false);
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_renderer=*/mojo::NullAssociatedRemote(),
/*print_preview_disabled=*/false, print_only_selection);
print_preview_observer.WaitUntilPreviewIsReady();
}
void PrintAndWaitUntilPreviewIsReadyAndLoaded(bool print_only_selection) {
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_renderer=*/mojo::NullAssociatedRemote(),
/*print_preview_disabled=*/false, print_only_selection);
print_preview_observer.WaitUntilPreviewIsReady();
}
// The following are helper functions for having a wait loop in the test and
// exit when all expected messages are received.
void SetNumExpectedMessages(unsigned int num) {
num_expected_messages_ = num;
}
void WaitUntilCallbackReceived() {
base::RunLoop run_loop;
quit_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
void CheckForQuit() {
if (++num_received_messages_ != num_expected_messages_)
return;
if (quit_callback_)
std::move(quit_callback_).Run();
}
void CreateTestPrintRenderFrame(content::RenderFrameHost* frame_host,
content::WebContents* web_contents) {
frame_content_.emplace(
frame_host, std::make_unique<TestPrintRenderFrame>(
frame_host, web_contents, kDefaultDocumentCookie,
base::BindRepeating(&PrintBrowserTest::CheckForQuit,
base::Unretained(this))));
OverrideBinderForTesting(frame_host);
}
static mojom::PrintFrameContentParamsPtr GetDefaultPrintFrameParams() {
return mojom::PrintFrameContentParams::New(gfx::Rect(800, 600),
kDefaultDocumentCookie);
}
const mojo::AssociatedRemote<mojom::PrintRenderFrame>& GetPrintRenderFrame(
content::RenderFrameHost* rfh) {
if (!remote_)
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote_);
return remote_;
}
private:
TestPrintRenderFrame* GetFrameContent(content::RenderFrameHost* host) const {
auto iter = frame_content_.find(host);
return iter != frame_content_.end() ? iter->second.get() : nullptr;
}
void OverrideBinderForTesting(content::RenderFrameHost* render_frame_host) {
render_frame_host->GetRemoteAssociatedInterfaces()
->OverrideBinderForTesting(
mojom::PrintRenderFrame::Name_,
base::BindRepeating(
&TestPrintRenderFrame::Bind,
base::Unretained(GetFrameContent(render_frame_host))));
}
unsigned int num_expected_messages_;
unsigned int num_received_messages_;
base::OnceClosure quit_callback_;
mojo::AssociatedRemote<mojom::PrintRenderFrame> remote_;
std::map<content::RenderFrameHost*, std::unique_ptr<TestPrintRenderFrame>>
frame_content_;
};
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(
{{features::kBackForwardCache,
// Set a very long TTL before expiration (longer than the test
// timeout) so tests that are expecting deletion don't pass when
// they shouldn't.
{{"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
// Allow BackForwardCache for all devices regardless of their memory.
{features::kBackForwardCacheMemoryControls});
PrintBrowserTest::SetUpCommandLine(command_line);
}
content::WebContents* web_contents() const {
return browser()->tab_strip_model()->GetActiveWebContents();
}
content::RenderFrameHost* current_frame_host() {
return web_contents()->GetMainFrame();
}
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 = std::find_if(
buckets->begin(), buckets->end(),
[sample](const base::Bucket& bucket) { return bucket.min == sample; });
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(bool print_only_selection) {
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false);
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_renderer=*/mojo::NullAssociatedRemote(),
/*print_preview_disabled=*/false, print_only_selection);
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"));
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));
}
};
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));
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/true);
}
// 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(/*print_only_selection=*/false);
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(/*print_only_selection=*/false);
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(/*print_only_selection=*/false);
// Test that entering print preview didn't mess up the scroll position.
EXPECT_EQ(
0, content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
"window.scrollX"));
}
// 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 gets reset to initial scale.
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();
contents->SetPageScale(1.5);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
double contents_page_scale_after_print =
content::EvalJs(contents, "window.visualViewport.scale").ExtractDouble();
constexpr double kContentsInitialScale = 1.0;
EXPECT_EQ(kContentsInitialScale, 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->GetMainFrame();
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();
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
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();
ASSERT_EQ(3u, original_contents->GetAllFrames().size());
// Create composite client so subframe print message can be forwarded.
PrintCompositeClient::CreateForWebContents(original_contents);
content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
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();
ASSERT_EQ(3u, original_contents->GetAllFrames().size());
// Create composite client so subframe print message can be forwarded.
PrintCompositeClient::CreateForWebContents(original_contents);
content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
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();
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
ASSERT_TRUE(main_frame);
content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
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->GetProcess()->GetID(),
subframe_in_queue->render_process_id_);
ASSERT_EQ(test_frame->GetRoutingID(), subframe_in_queue->render_frame_id_);
// Creates mojom::PrintCompositor.
client->DoCompositeDocumentToPdf(
kDefaultDocumentCookie, main_frame,
*TestPrintRenderFrame::GetDefaultDidPrintContentParams(),
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(/*print_only_selection=*/false);
}
// 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();
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
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(/*print_only_selection=*/false);
}
// 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();
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
content::RenderFrameHost* subframe = original_contents->GetAllFrames()[1];
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.
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false);
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_renderer=*/mojo::NullAssociatedRemote(),
/*print_preview_disabled=*/false, /*has_selection*/ false);
// 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,
DISABLED_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));
ASSERT_EQ(2u, original_contents->GetAllFrames().size());
auto* main_frame = original_contents->GetMainFrame();
auto* subframe = original_contents->GetAllFrames()[1];
ASSERT_NE(main_frame->GetProcess(), subframe->GetProcess());
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
}
// 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());
}
// 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(/*print_only_selection=*/false);
// 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(/*print_only_selection=*/false);
}
// 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(/*print_only_selection=*/false);
}
// Printing frame content for the main frame of a generic webpage with N-up
// priting. This is a regression test for https://crbug.com/937247
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintNup) {
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);
TestPrintViewManager print_view_manager(web_contents);
PrintViewManager::SetReceiverImplForTesting(&print_view_manager);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
PrintViewManager::SetReceiverImplForTesting(nullptr);
}
// Site per process version of PrintBrowserTest.PrintNup.
IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, PrintNup) {
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);
TestPrintViewManager print_view_manager(web_contents);
PrintViewManager::SetReceiverImplForTesting(&print_view_manager);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
PrintViewManager::SetReceiverImplForTesting(nullptr);
}
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, MultipagePrint) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
PrintAndWaitUntilPreviewIsReadyAndLoaded(/*print_only_selection=*/false);
}
IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, MultipagePrint) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
PrintAndWaitUntilPreviewIsReadyAndLoaded(/*print_only_selection=*/false);
}
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PDFPluginNotKeyboardFocusable) {
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
/*print_renderer=*/mojo::NullAssociatedRemote(),
/*print_preview_disabled=*/false, /*print_only_selection=*/false);
print_preview_observer.WaitUntilPreviewIsReady();
content::WebContents* preview_dialog =
print_preview_observer.GetPrintPreviewDialog();
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"(
const button = document.getElementsByTagName('print-preview-app')[0]
.$['previewArea']
.$$('iframe')
.contentDocument.querySelector('pdf-viewer-pp')
.shadowRoot.querySelector('#zoom-toolbar')
.$['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', () => {
window.domAutomationController.send(true);
});
select_tag.focus();)";
bool success = false;
ASSERT_TRUE(
content::ExecuteScriptAndExtractBool(preview_dialog, kScript, &success));
ASSERT_TRUE(success);
// Simulate a <shift-tab> press and wait for a focus message.
content::DOMMessageQueue msg_queue;
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();
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false);
content::ExecuteScriptAsync(web_contents->GetMainFrame(), "window.print();");
print_preview_observer.WaitUntilPreviewIsReady();
}
class PrintPrerenderBrowserTest : public PrintBrowserTest {
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_.SetUp(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:
content::test::PrerenderTestHelper prerender_helper_;
};
// Test that print() is silently ignored.
// https://jeremyroman.github.io/alternate-loading-modes/#patch-modals
IN_PROC_BROWSER_TEST_F(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");
content::WebContentsConsoleObserver console_observer(web_contents());
int prerender_id = prerender_helper_.AddPrerender(prerender_url);
content::RenderFrameHost* prerender_host =
prerender_helper_.GetPrerenderedMainFrameHost(prerender_id);
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://jeremyroman.github.io/alternate-loading-modes/#patch-modals indicates
// the intent to silently ignore print APIs.
IN_PROC_BROWSER_TEST_F(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");
content::WebContentsConsoleObserver console_observer(web_contents());
int prerender_id = prerender_helper_.AddPrerender(prerender_url);
content::RenderFrameHost* prerender_host =
prerender_helper_.GetPrerenderedMainFrameHost(prerender_id);
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 PrintBackendPrintBrowserTest : public PrintBrowserTest {
public:
PrintBackendPrintBrowserTest() = default;
~PrintBackendPrintBrowserTest() override = default;
void SetUp() override {
test_backend_ = base::MakeRefCounted<TestPrintBackend>();
PrintBackend::SetPrintBackendForTesting(test_backend_.get());
PrintingContext::SetPrintingContextFactoryForTest(
&test_printing_context_factory_);
PrintBrowserTest::SetUp();
}
void TearDown() override {
PrintBrowserTest::TearDown();
PrintingContext::SetPrintingContextFactoryForTest(/*factory=*/nullptr);
PrintBackend::SetPrintBackendForTesting(/*print_backend=*/nullptr);
}
void AddPrinter(const std::string& printer_name) {
const PrinterBasicInfo kPrinterInfo(
printer_name,
/*display_name=*/"test printer",
/*printer_description=*/"A printer for testing.",
/*printer_status=*/0,
/*is_default=*/true,
/*options=*/{});
auto default_caps = std::make_unique<PrinterSemanticCapsAndDefaults>();
default_caps->copies_max = kPrinterCapabilitiesMaxCopies;
test_backend_->AddValidPrinter(
printer_name, std::move(default_caps),
std::make_unique<PrinterBasicInfo>(kPrinterInfo));
}
void SetPrinterNameForSubsequentContexts(const std::string& printer_name) {
test_printing_context_factory_.SetPrinterNameForSubsequentContexts(
printer_name);
}
private:
class PrintBackendPrintingContextFactoryForTest
: public PrintingContextFactoryForTest {
public:
std::unique_ptr<PrintingContext> CreatePrintingContext(
PrintingContext::Delegate* delegate) override {
auto context = std::make_unique<TestPrintingContext>(delegate);
auto settings = std::make_unique<PrintSettings>();
settings->set_copies(kPrintSettingsCopies);
settings->set_device_name(
base::ASCIIToUTF16(base::StringPiece(printer_name_)));
context->SetDeviceSettings(printer_name_, std::move(settings));
return std::move(context);
}
void SetPrinterNameForSubsequentContexts(const std::string& printer_name) {
printer_name_ = printer_name;
}
private:
std::string printer_name_;
};
scoped_refptr<TestPrintBackend> test_backend_;
TestPrintingContextDelegate test_printing_context_delegate_;
PrintBackendPrintingContextFactoryForTest test_printing_context_factory_;
};
// TODO(crbug.com/822505) ChromeOS uses different testing setup that isn't
// hooked up to make use of `TestPrintingContext` yet.
#if !defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(PrintBackendPrintBrowserTest, UpdatePrintSettings) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
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);
TestPrintViewManager print_view_manager(web_contents);
PrintViewManager::SetReceiverImplForTesting(&print_view_manager);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
ASSERT_TRUE(print_view_manager.snooped_settings());
EXPECT_EQ(print_view_manager.snooped_settings()->copies(),
kPrintSettingsCopies);
}
#endif // !defined(OS_CHROMEOS)
} // namespace printing