blob: 36abb6edd3e9963d13225793952132968ced131b [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <tuple>
#include <utility>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/printing/print_browsertest.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/print_test_utils.h"
#include "chrome/browser/printing/print_view_manager_base.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/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "printing/buildflags/buildflags.h"
#include "printing/mojom/print.mojom.h"
#include "printing/printing_context.h"
#include "printing/printing_features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.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
#if BUILDFLAG(IS_CHROMEOS)
// TODO(crbug.com/822505) ChromeOS uses different testing setup that isn't
// hooked up to make use of `TestPrintingContext` yet.
#error "ChromeOS not supported here yet"
#endif
namespace printing {
namespace {
#if !BUILDFLAG(IS_CHROMEOS)
constexpr gfx::Size kLetterPhysicalSize = gfx::Size(612, 792);
constexpr gfx::Rect kLetterPrintableArea = gfx::Rect(5, 5, 602, 782);
constexpr gfx::Size kLegalPhysicalSize = gfx::Size(612, 1008);
constexpr gfx::Rect kLegalPrintableArea = gfx::Rect(5, 5, 602, 998);
// The default margins are set to 1.0cm in //printing/print_settings.cc, which
// is about 28 printer units. The resulting content size is 556 x 736 for
// Letter, and similarly is 556 x 952 for Legal.
constexpr gfx::Size kLetterExpectedContentSize = gfx::Size(556, 736);
constexpr gfx::Size kLegalExpectedContentSize = gfx::Size(556, 952);
#endif // !BUILDFLAG(IS_CHROMEOS)
} // namespace
#if BUILDFLAG(ENABLE_OOP_PRINTING)
using OnUseDefaultSettingsCallback = base::RepeatingClosure;
using OnGetSettingsWithUICallback = base::RepeatingClosure;
using ErrorCheckCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
using OnDidUseDefaultSettingsCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
using OnDidAskUserForSettingsCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
#endif
using OnDidUpdatePrintSettingsCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
using OnDidStartPrintingCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
#if BUILDFLAG(IS_WIN)
using OnDidRenderPrintedPageCallback =
base::RepeatingCallback<void(uint32_t page_number,
mojom::ResultCode result)>;
#endif
using OnDidRenderPrintedDocumentCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
using OnDidDocumentDoneCallback =
base::RepeatingCallback<void(mojom::ResultCode result)>;
using OnDidCancelCallback = base::RepeatingClosure;
using OnDidShowErrorDialog = base::RepeatingClosure;
class TestPrinterQuery : public PrinterQuery {
public:
// Callbacks to run for overrides.
struct PrintCallbacks {
OnUseDefaultSettingsCallback did_use_default_settings_callback;
OnGetSettingsWithUICallback did_get_settings_with_ui_callback;
};
TestPrinterQuery(content::GlobalRenderFrameHostId rfh_id,
PrintCallbacks* callbacks)
: PrinterQuery(rfh_id), callbacks_(callbacks) {}
void UseDefaultSettings(SettingsCallback callback) override {
DVLOG(1) << "Observed: invoke use default settings";
PrinterQuery::UseDefaultSettings(std::move(callback));
callbacks_->did_use_default_settings_callback.Run();
}
void GetSettingsWithUI(uint32_t document_page_count,
bool has_selection,
bool is_scripted,
SettingsCallback callback) override {
DVLOG(1) << "Observed: invoke get settings with UI";
PrinterQuery::GetSettingsWithUI(document_page_count, has_selection,
is_scripted, std::move(callback));
callbacks_->did_get_settings_with_ui_callback.Run();
}
raw_ptr<PrintCallbacks> callbacks_;
};
class TestPrintJobWorkerOop : public PrintJobWorkerOop {
public:
// Callbacks to run for overrides are broken into the following steps:
// 1. Error case processing. Call `error_check_callback` to reset any
// triggers that were primed to cause errors in the testing context.
// 2. Run the base class callback for normal handling. If there was an
// access-denied error then this can lead to a retry. The retry has a
// chance to succeed since error triggers were removed.
// 3. Exercise the associated test callback (e.g.,
// `did_start_printing_callback` when in `OnDidStartPrinting()`) to note
// the callback was observed and completed. This ensures all base class
// processing was done before possibly quitting the test run loop.
struct PrintCallbacks {
ErrorCheckCallback error_check_callback;
OnDidUseDefaultSettingsCallback did_use_default_settings_callback;
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
OnDidAskUserForSettingsCallback did_ask_user_for_settings_callback;
#endif
OnDidUpdatePrintSettingsCallback did_update_print_settings_callback;
OnDidStartPrintingCallback did_start_printing_callback;
#if BUILDFLAG(IS_WIN)
OnDidRenderPrintedPageCallback did_render_printed_page_callback;
#endif
OnDidRenderPrintedDocumentCallback did_render_printed_document_callback;
OnDidDocumentDoneCallback did_document_done_callback;
OnDidCancelCallback did_cancel_callback;
};
TestPrintJobWorkerOop(
std::unique_ptr<PrintingContext::Delegate> printing_context_delegate,
std::unique_ptr<PrintingContext> printing_context,
absl::optional<PrintBackendServiceManager::ClientId> client_id,
absl::optional<PrintBackendServiceManager::ContextId> context_id,
PrintJob* print_job,
bool print_from_system_dialog,
bool simulate_spooling_memory_errors,
TestPrintJobWorkerOop::PrintCallbacks* callbacks)
: PrintJobWorkerOop(std::move(printing_context_delegate),
std::move(printing_context),
client_id,
context_id,
print_job,
print_from_system_dialog,
simulate_spooling_memory_errors),
callbacks_(callbacks) {}
TestPrintJobWorkerOop(const TestPrintJobWorkerOop&) = delete;
TestPrintJobWorkerOop& operator=(const TestPrintJobWorkerOop&) = delete;
~TestPrintJobWorkerOop() override = default;
private:
void OnDidStartPrinting(mojom::ResultCode result) override {
DVLOG(1) << "Observed: start printing of document";
callbacks_->error_check_callback.Run(result);
PrintJobWorkerOop::OnDidStartPrinting(result);
callbacks_->did_start_printing_callback.Run(result);
}
#if BUILDFLAG(IS_WIN)
void OnDidRenderPrintedPage(uint32_t page_number,
mojom::ResultCode result) override {
DVLOG(1) << "Observed render for printed page " << page_number;
callbacks_->error_check_callback.Run(result);
PrintJobWorkerOop::OnDidRenderPrintedPage(page_number, result);
callbacks_->did_render_printed_page_callback.Run(page_number, result);
}
#endif // BUILDFLAG(IS_WIN)
void OnDidRenderPrintedDocument(mojom::ResultCode result) override {
DVLOG(1) << "Observed render for printed document";
callbacks_->error_check_callback.Run(result);
PrintJobWorkerOop::OnDidRenderPrintedDocument(result);
callbacks_->did_render_printed_document_callback.Run(result);
}
void OnDidDocumentDone(int job_id, mojom::ResultCode result) override {
DVLOG(1) << "Observed: document done";
callbacks_->error_check_callback.Run(result);
PrintJobWorkerOop::OnDidDocumentDone(job_id, result);
callbacks_->did_document_done_callback.Run(result);
}
void OnDidCancel(scoped_refptr<PrintJob> job) override {
DVLOG(1) << "Observed: cancel";
// Must not use `std::move(job)`, as that could potentially cause the `job`
// (and consequentially `this`) to be destroyed before
// `did_cancel_callback` is run.
PrintJobWorkerOop::OnDidCancel(job);
callbacks_->did_cancel_callback.Run();
}
raw_ptr<PrintCallbacks> callbacks_;
};
class TestPrinterQueryOop : public PrinterQueryOop {
public:
TestPrinterQueryOop(content::GlobalRenderFrameHostId rfh_id,
bool simulate_spooling_memory_errors,
TestPrintJobWorkerOop::PrintCallbacks* callbacks)
: PrinterQueryOop(rfh_id),
simulate_spooling_memory_errors_(simulate_spooling_memory_errors),
callbacks_(callbacks) {}
void OnDidUseDefaultSettings(
SettingsCallback callback,
mojom::PrintSettingsResultPtr print_settings) override {
DVLOG(1) << "Observed: use default settings";
mojom::ResultCode result = print_settings->is_result_code()
? print_settings->get_result_code()
: mojom::ResultCode::kSuccess;
callbacks_->error_check_callback.Run(result);
PrinterQueryOop::OnDidUseDefaultSettings(std::move(callback),
std::move(print_settings));
callbacks_->did_use_default_settings_callback.Run(result);
}
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void OnDidAskUserForSettings(
SettingsCallback callback,
mojom::PrintSettingsResultPtr print_settings) override {
DVLOG(1) << "Observed: ask user for settings";
mojom::ResultCode result = print_settings->is_result_code()
? print_settings->get_result_code()
: mojom::ResultCode::kSuccess;
callbacks_->error_check_callback.Run(result);
PrinterQueryOop::OnDidAskUserForSettings(std::move(callback),
std::move(print_settings));
callbacks_->did_ask_user_for_settings_callback.Run(result);
}
#endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void OnDidUpdatePrintSettings(
const std::string& device_name,
SettingsCallback callback,
mojom::PrintSettingsResultPtr print_settings) override {
DVLOG(1) << "Observed: update print settings";
mojom::ResultCode result = print_settings->is_result_code()
? print_settings->get_result_code()
: mojom::ResultCode::kSuccess;
callbacks_->error_check_callback.Run(result);
PrinterQueryOop::OnDidUpdatePrintSettings(device_name, std::move(callback),
std::move(print_settings));
callbacks_->did_update_print_settings_callback.Run(result);
}
std::unique_ptr<PrintJobWorkerOop> CreatePrintJobWorker(
PrintJob* print_job) override {
return std::make_unique<TestPrintJobWorkerOop>(
std::move(printing_context_delegate_), std::move(printing_context_),
print_document_client_id(), context_id(), print_job,
print_from_system_dialog(), simulate_spooling_memory_errors_,
callbacks_);
}
bool simulate_spooling_memory_errors_;
raw_ptr<TestPrintJobWorkerOop::PrintCallbacks> callbacks_;
};
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
class SystemAccessProcessPrintBrowserTestBase
: public PrintBrowserTest,
public PrintJob::Observer,
public PrintViewManagerBase::Observer {
public:
SystemAccessProcessPrintBrowserTestBase() = default;
~SystemAccessProcessPrintBrowserTestBase() override = default;
virtual bool UseService() = 0;
// Only of interest when `UseService()` returns true.
virtual bool SandboxService() = 0;
void SetUp() override {
#if BUILDFLAG(ENABLE_OOP_PRINTING)
if (UseService()) {
feature_list_.InitAndEnableFeatureWithParameters(
features::kEnableOopPrintDrivers,
{{features::kEnableOopPrintDriversJobPrint.name, "true"},
{features::kEnableOopPrintDriversSandbox.name,
SandboxService() ? "true" : "false"}});
// Safe to use `base::Unretained(this)` since this testing class
// necessarily must outlive all interactions from the tests which will
// run through `TestPrintJobWorkerOop`, the user of these callbacks.
test_print_job_worker_oop_callbacks_.error_check_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::ErrorCheck,
base::Unretained(this));
test_print_job_worker_oop_callbacks_.did_use_default_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidUseDefaultSettings,
base::Unretained(this));
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
test_print_job_worker_oop_callbacks_.did_ask_user_for_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidAskUserForSettings,
base::Unretained(this));
#endif
test_print_job_worker_oop_callbacks_
.did_update_print_settings_callback = base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidUpdatePrintSettings,
base::Unretained(this));
test_print_job_worker_oop_callbacks_.did_start_printing_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidStartPrinting,
base::Unretained(this));
#if BUILDFLAG(IS_WIN)
test_print_job_worker_oop_callbacks_.did_render_printed_page_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidRenderPrintedPage,
base::Unretained(this));
#endif
test_print_job_worker_oop_callbacks_
.did_render_printed_document_callback = base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidRenderPrintedDocument,
base::Unretained(this));
test_print_job_worker_oop_callbacks_.did_document_done_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidDocumentDone,
base::Unretained(this));
test_print_job_worker_oop_callbacks_.did_cancel_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidCancel,
base::Unretained(this));
} else {
test_print_job_worker_callbacks_.did_use_default_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnUseDefaultSettings,
base::Unretained(this));
test_print_job_worker_callbacks_.did_get_settings_with_ui_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnGetSettingsWithUI,
base::Unretained(this));
}
test_create_printer_query_callback_ = base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::CreatePrinterQuery,
base::Unretained(this), UseService());
PrinterQuery::SetCreatePrinterQueryCallbackForTest(
&test_create_printer_query_callback_);
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
PrintBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
#if BUILDFLAG(ENABLE_OOP_PRINTING)
if (UseService()) {
print_backend_service_ = PrintBackendServiceTestImpl::LaunchForTesting(
test_remote_, test_print_backend(), /*sandboxed=*/true);
}
#endif
PrintBrowserTest::SetUpOnMainThread();
}
void TearDown() override {
PrintBrowserTest::TearDown();
#if BUILDFLAG(ENABLE_OOP_PRINTING)
PrinterQuery::SetCreatePrinterQueryCallbackForTest(/*callback=*/nullptr);
if (UseService()) {
// Check that there is never a straggler client registration.
EXPECT_EQ(
PrintBackendServiceManager::GetInstance().GetClientsRegisteredCount(),
0u);
}
PrintBackendServiceManager::ResetForTesting();
#endif
ASSERT_EQ(print_job_construction_count(), print_job_destruction_count());
}
// PrintViewManagerBase::Observer:
void OnRegisterSystemPrintClient(bool succeeded) override {
system_print_registration_succeeded_ = succeeded;
}
void OnDidPrintDocument() override {
++did_print_document_count_;
CheckForQuit();
}
// PrintJob::Observer:
void OnDestruction() override {
++print_job_destruction_count_;
CheckForQuit();
}
void OnCreatedPrintJob(PrintJob* print_job) {
++print_job_construction_count_;
print_job->AddObserver(*this);
}
TestPrintViewManager* SetUpAndReturnPrintViewManager(
content::WebContents* web_contents) {
auto manager = std::make_unique<TestPrintViewManager>(
web_contents,
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnCreatedPrintJob,
base::Unretained(this)));
manager->AddObserver(*this);
TestPrintViewManager* manager_ptr = manager.get();
web_contents->SetUserData(PrintViewManager::UserDataKey(),
std::move(manager));
return manager_ptr;
}
void SetUpPrintViewManager(content::WebContents* web_contents) {
std::ignore = SetUpAndReturnPrintViewManager(web_contents);
}
void PrintAfterPreviewIsReadyAndLoaded() {
// First invoke the Print Preview dialog with `StartPrint()`.
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);
set_rendered_page_count(print_preview_observer.rendered_page_count());
// Print Preview is completely ready, can now initiate printing.
// This script locates and clicks the Print button.
const char kScript[] = R"(
const button = document.getElementsByTagName('print-preview-app')[0]
.$['sidebar']
.shadowRoot.querySelector('print-preview-button-strip')
.shadowRoot.querySelector('.action-button');
button.click();)";
ASSERT_TRUE(content::ExecuteScript(preview_dialog, kScript));
WaitUntilCallbackReceived();
}
void AdjustMediaAfterPreviewIsReadyAndLoaded() {
// First invoke the Print Preview dialog with `StartPrint()`.
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);
set_rendered_page_count(print_preview_observer.rendered_page_count());
// Initial Print Preview is completely ready.
// Reset the observer, and then modify the paper size. This will initiate
// another preview render.
// The default paper size is first in the list at index zero, so choose
// the second item from the list to cause a change.
print_preview_observer.ResetForAnotherPreview();
const char kSetPaperSizeScript[] = R"(
var element =
document.getElementsByTagName('print-preview-app')[0]
.$['sidebar']
.shadowRoot.querySelector('print-preview-media-size-settings');
element.setSetting('mediaSize', element.capability.option[1]);)";
ASSERT_TRUE(content::ExecJs(preview_dialog, kSetPaperSizeScript));
print_preview_observer.WaitUntilPreviewIsReady();
}
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void SystemPrintFromPreviewOnceReadyAndLoaded(bool wait_for_callback) {
// First invoke the Print Preview dialog with `StartPrint()`.
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);
set_rendered_page_count(print_preview_observer.rendered_page_count());
// Print Preview is completely ready, can now initiate printing.
// This script locates and clicks the "Print using system dialog",
// which is still enabled even if it is hidden.
const char kPrintWithSystemDialogScript[] = R"(
const printSystemDialog
= document.getElementsByTagName('print-preview-app')[0]
.$['sidebar']
.shadowRoot.querySelector('print-preview-link-container')
.$['systemDialogLink'];
printSystemDialog.click();)";
// It is possible for sufficient processing for the system print to
// complete such that the renderer naturally terminates before ExecJs()
// returns here. This causes ExecJs() to return false, with a JavaScript
// error of "Renderer terminated". Since the termination can actually be
// a result of successful print processing, do not assert on this return
// result, just ignore the error instead. Rely upon tests catching any
// failure through the use of other expectation checks.
std::ignore = content::ExecJs(preview_dialog, kPrintWithSystemDialogScript);
if (wait_for_callback) {
WaitUntilCallbackReceived();
}
}
#endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void PrimeAsRepeatingErrorGenerator() { reset_errors_after_check_ = false; }
#if BUILDFLAG(ENABLE_OOP_PRINTING)
void PrimeForSpoolingSharedMemoryErrors() {
simulate_spooling_memory_errors_ = true;
}
void PrimeForFailInUseDefaultSettings() {
test_printing_context_factory()->SetFailErrorOnUseDefaultSettings();
}
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void PrimeForCancelInAskUserForSettings() {
test_printing_context_factory()->SetCancelErrorOnAskUserForSettings();
}
#endif
void PrimeForCancelInNewDocument() {
test_printing_context_factory()->SetCancelErrorOnNewDocument(
/*cause_errors=*/true);
}
void PrimeForErrorsInNewDocument() {
test_printing_context_factory()->SetFailedErrorOnNewDocument(
/*cause_errors=*/true);
}
void PrimeForAccessDeniedErrorsInNewDocument() {
test_printing_context_factory()->SetAccessDeniedErrorOnNewDocument(
/*cause_errors=*/true);
}
#if BUILDFLAG(IS_WIN)
void PrimeForAccessDeniedErrorsInRenderPrintedPage() {
test_printing_context_factory()->SetAccessDeniedErrorOnRenderPage(
/*cause_errors=*/true);
}
void PrimeForDelayedRenderingUntilPage(uint32_t page_number) {
print_backend_service_->set_rendering_delayed_until_page(page_number);
}
void PrimeForRenderingErrorOnPage(uint32_t page_number) {
test_printing_context_factory()->SetFailedErrorForRenderPage(page_number);
}
#endif
void PrimeForAccessDeniedErrorsInRenderPrintedDocument() {
test_printing_context_factory()->SetAccessDeniedErrorOnRenderDocument(
/*cause_errors=*/true);
}
void PrimeForAccessDeniedErrorsInDocumentDone() {
test_printing_context_factory()->SetAccessDeniedErrorOnDocumentDone(
/*cause_errors=*/true);
}
const absl::optional<bool> system_print_registration_succeeded() const {
return system_print_registration_succeeded_;
}
bool did_use_default_settings() const { return did_use_default_settings_; }
bool did_get_settings_with_ui() const { return did_get_settings_with_ui_; }
bool print_backend_service_use_detected() const {
return print_backend_service_use_detected_;
}
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
mojom::ResultCode use_default_settings_result() const {
return use_default_settings_result_;
}
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
mojom::ResultCode ask_user_for_settings_result() const {
return ask_user_for_settings_result_;
}
#endif
mojom::ResultCode update_print_settings_result() const {
return update_print_settings_result_;
}
mojom::ResultCode start_printing_result() const {
return start_printing_result_;
}
#if BUILDFLAG(IS_WIN)
mojom::ResultCode render_printed_page_result() const {
return render_printed_page_result_;
}
int render_printed_page_count() const { return render_printed_pages_count_; }
#endif // BUILDFLAG(IS_WIN)
mojom::ResultCode render_printed_document_result() {
return render_printed_document_result_;
}
mojom::ResultCode document_done_result() const {
return document_done_result_;
}
int cancel_count() const { return cancel_count_; }
int print_job_construction_count() const {
return print_job_construction_count_;
}
int print_job_destruction_count() const {
return print_job_destruction_count_;
}
int did_print_document_count() const { return did_print_document_count_; }
private:
#if BUILDFLAG(ENABLE_OOP_PRINTING)
std::unique_ptr<PrinterQuery> CreatePrinterQuery(
bool use_service,
content::GlobalRenderFrameHostId rfh_id) {
if (use_service) {
return std::make_unique<TestPrinterQueryOop>(
rfh_id, simulate_spooling_memory_errors_,
&test_print_job_worker_oop_callbacks_);
}
return std::make_unique<TestPrinterQuery>(
rfh_id, &test_print_job_worker_callbacks_);
}
void OnUseDefaultSettings() {
did_use_default_settings_ = true;
PrintBackendServiceDetectionCheck();
CheckForQuit();
}
void OnGetSettingsWithUI() {
did_get_settings_with_ui_ = true;
PrintBackendServiceDetectionCheck();
CheckForQuit();
}
void PrintBackendServiceDetectionCheck() {
// Want to know if `PrintBackendService` clients are ever detected, since
// registrations could have gone away by the time checks are made at the
// end of tests.
if (PrintBackendServiceManager::GetInstance().GetClientsRegisteredCount() >
0) {
print_backend_service_use_detected_ = true;
}
}
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
void ErrorCheck(mojom::ResultCode result) {
// Interested to reset any trigger for causing access-denied errors, so
// that retry logic has a chance to be exercised and succeed.
if (result == mojom::ResultCode::kAccessDenied) {
ResetForNoAccessDeniedErrors();
}
}
void OnDidUseDefaultSettings(mojom::ResultCode result) {
use_default_settings_result_ = result;
CheckForQuit();
}
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void OnDidAskUserForSettings(mojom::ResultCode result) {
ask_user_for_settings_result_ = result;
CheckForQuit();
}
#endif
void OnDidUpdatePrintSettings(mojom::ResultCode result) {
update_print_settings_result_ = result;
CheckForQuit();
}
void OnDidStartPrinting(mojom::ResultCode result) {
start_printing_result_ = result;
CheckForQuit();
}
#if BUILDFLAG(IS_WIN)
void OnDidRenderPrintedPage(uint32_t page_number, mojom::ResultCode result) {
render_printed_page_result_ = result;
if (result == mojom::ResultCode::kSuccess) {
render_printed_pages_count_++;
}
CheckForQuit();
}
#endif
void OnDidRenderPrintedDocument(mojom::ResultCode result) {
render_printed_document_result_ = result;
CheckForQuit();
}
void OnDidDocumentDone(mojom::ResultCode result) {
document_done_result_ = result;
CheckForQuit();
}
void OnDidCancel() {
++cancel_count_;
CheckForQuit();
}
void OnDidDestroyPrintJob() {
++print_job_destruction_count_;
CheckForQuit();
}
void ResetForNoAccessDeniedErrors() {
// Don't do the reset if test scenario is repeatedly return errors.
if (!reset_errors_after_check_) {
return;
}
test_printing_context_factory()->SetAccessDeniedErrorOnNewDocument(
/*cause_errors=*/false);
#if BUILDFLAG(IS_WIN)
test_printing_context_factory()->SetAccessDeniedErrorOnRenderPage(
/*cause_errors=*/false);
#endif
test_printing_context_factory()->SetAccessDeniedErrorOnRenderDocument(
/*cause_errors=*/false);
test_printing_context_factory()->SetAccessDeniedErrorOnDocumentDone(
/*cause_errors=*/false);
}
base::test::ScopedFeatureList feature_list_;
#if BUILDFLAG(ENABLE_OOP_PRINTING)
TestPrinterQuery::PrintCallbacks test_print_job_worker_callbacks_;
TestPrintJobWorkerOop::PrintCallbacks test_print_job_worker_oop_callbacks_;
CreatePrinterQueryCallback test_create_printer_query_callback_;
absl::optional<bool> system_print_registration_succeeded_;
bool did_use_default_settings_ = false;
bool did_get_settings_with_ui_ = false;
bool print_backend_service_use_detected_ = false;
bool simulate_spooling_memory_errors_ = false;
mojo::Remote<mojom::PrintBackendService> test_remote_;
std::unique_ptr<PrintBackendServiceTestImpl> print_backend_service_;
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
bool reset_errors_after_check_ = true;
int did_print_document_count_ = 0;
mojom::ResultCode use_default_settings_result_ = mojom::ResultCode::kFailed;
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
mojom::ResultCode ask_user_for_settings_result_ = mojom::ResultCode::kFailed;
#endif
mojom::ResultCode update_print_settings_result_ = mojom::ResultCode::kFailed;
mojom::ResultCode start_printing_result_ = mojom::ResultCode::kFailed;
#if BUILDFLAG(IS_WIN)
mojom::ResultCode render_printed_page_result_ = mojom::ResultCode::kFailed;
int render_printed_pages_count_ = 0;
#endif
mojom::ResultCode render_printed_document_result_ =
mojom::ResultCode::kFailed;
mojom::ResultCode document_done_result_ = mojom::ResultCode::kFailed;
int cancel_count_ = 0;
int print_job_construction_count_ = 0;
int print_job_destruction_count_ = 0;
};
#if BUILDFLAG(ENABLE_OOP_PRINTING)
// Values for parameterized testing.
enum class PrintBackendFeatureVariation {
// `PrintBackend` calls occur from browser process.
kInBrowserProcess,
// Use OOP `PrintBackend`. Attempt to have `PrintBackendService` be
// sandboxed.
kOopSandboxedService,
// Use OOP `PrintBackend`. Always use `PrintBackendService` unsandboxed.
kOopUnsandboxedService,
};
class SystemAccessProcessSandboxedServicePrintBrowserTest
: public SystemAccessProcessPrintBrowserTestBase {
public:
SystemAccessProcessSandboxedServicePrintBrowserTest() = default;
~SystemAccessProcessSandboxedServicePrintBrowserTest() override = default;
bool UseService() override { return true; }
bool SandboxService() override { return true; }
};
class SystemAccessProcessServicePrintBrowserTest
: public SystemAccessProcessPrintBrowserTestBase,
public testing::WithParamInterface<PrintBackendFeatureVariation> {
public:
SystemAccessProcessServicePrintBrowserTest() = default;
~SystemAccessProcessServicePrintBrowserTest() override = default;
bool UseService() override { return true; }
bool SandboxService() override {
return GetParam() == PrintBackendFeatureVariation::kOopSandboxedService;
}
};
INSTANTIATE_TEST_SUITE_P(
All,
SystemAccessProcessServicePrintBrowserTest,
testing::Values(PrintBackendFeatureVariation::kOopSandboxedService,
PrintBackendFeatureVariation::kOopUnsandboxedService));
#endif
class SystemAccessProcessInBrowserPrintBrowserTest
: public SystemAccessProcessPrintBrowserTestBase {
public:
SystemAccessProcessInBrowserPrintBrowserTest() = default;
~SystemAccessProcessInBrowserPrintBrowserTest() override = default;
bool UseService() override { return false; }
bool SandboxService() override { return false; }
};
class SystemAccessProcessPrintBrowserTest
: public SystemAccessProcessPrintBrowserTestBase,
public testing::WithParamInterface<PrintBackendFeatureVariation> {
public:
SystemAccessProcessPrintBrowserTest() = default;
~SystemAccessProcessPrintBrowserTest() override = default;
bool UseService() override {
return GetParam() != PrintBackendFeatureVariation::kInBrowserProcess;
}
bool SandboxService() override {
return GetParam() == PrintBackendFeatureVariation::kOopSandboxedService;
}
};
INSTANTIATE_TEST_SUITE_P(
All,
SystemAccessProcessPrintBrowserTest,
testing::Values(PrintBackendFeatureVariation::kInBrowserProcess,
PrintBackendFeatureVariation::kOopSandboxedService,
PrintBackendFeatureVariation::kOopUnsandboxedService));
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
UpdatePrintSettings) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.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();
EXPECT_EQ(3u, rendered_page_count());
const mojom::PrintPagesParamsPtr& snooped_params =
print_view_manager.snooped_params();
ASSERT_TRUE(snooped_params);
EXPECT_EQ(test::kPrinterCapabilitiesDpi, snooped_params->params->dpi);
EXPECT_EQ(kLetterPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLetterPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLetterExpectedContentSize, snooped_params->params->content_size);
}
#if BUILDFLAG(ENABLE_OOP_PRINTING)
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
UpdatePrintSettingsPrintableArea) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.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);
AdjustMediaAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(1u, rendered_page_count());
const mojom::PrintPagesParamsPtr& snooped_params =
print_view_manager.snooped_params();
ASSERT_TRUE(snooped_params);
EXPECT_EQ(test::kPrinterCapabilitiesDpi, snooped_params->params->dpi);
EXPECT_EQ(kLegalPhysicalSize, snooped_params->params->page_size);
EXPECT_EQ(kLegalPrintableArea, snooped_params->params->printable_area);
EXPECT_EQ(kLegalExpectedContentSize, snooped_params->params->content_size);
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrinting) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Rendering for 1 page of document of content.
// 4. Completes with document done.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 1);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS)
absl::optional<PrintSettings> settings = document_print_settings();
ASSERT_TRUE(settings);
// Collect just the keys to compare the info options vs. advanced settings.
std::vector<std::string> advanced_setting_keys;
std::vector<std::string> print_info_options_keys;
const PrintSettings::AdvancedSettings& advanced_settings =
settings->advanced_settings();
for (const auto& advanced_setting : advanced_settings) {
advanced_setting_keys.push_back(advanced_setting.first);
}
for (const auto& option : test::kPrintInfoOptions) {
print_info_options_keys.push_back(option.first);
}
EXPECT_THAT(advanced_setting_keys,
testing::UnorderedElementsAreArray(print_info_options_keys));
#endif // BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS)
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingMultipage) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
#if BUILDFLAG(IS_WIN)
// Windows GDI results in a callback for each rendered page.
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started.
// 3. First page is rendered.
// 4. Second page is rendered.
// 5. Third page is rendered.
// 6. Completes with document done.
// 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
SetNumExpectedMessages(/*num=*/7);
#else
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Document is rendered.
// 4. Completes with document done.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
#endif
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 3);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
}
IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
StartPrintingSpoolingSharedMemoryError) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForSpoolingSharedMemoryErrors();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// No attempt to retry is made if a job has a shared memory error when trying
// to spool a page/document fails on a shared memory error. The test
// sequence for this is:
// 1. Update print settings.
// 2. A print job is started.
// 3. Spooling to send the render data will fail. An error dialog is shown.
// 4. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
// TODO(crbug.com/1384459): Flaky on MSan builds.
#if defined(MEMORY_SANITIZER)
#define MAYBE_StartPrintingFails DISABLED_StartPrintingFails
#else
#define MAYBE_StartPrintingFails StartPrintingFails
#endif
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
MAYBE_StartPrintingFails) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForErrorsInNewDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
// There are no callbacks for print stages with in-browser printing. So
// the print job is started, but that fails, and there is no capturing of
// that result.
// The expected events for this are:
// 1. An error dialog is shown.
// 2. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/2);
} else {
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started, but that fails.
// 3. An error dialog is shown.
// 4. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
}
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed);
EXPECT_EQ(error_dialog_shown_count(), 1u);
// No tracking of cancel for in-browser tests, only for OOP.
if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) {
EXPECT_EQ(cancel_count(), 1);
}
EXPECT_EQ(print_job_destruction_count(), 1);
}
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
StartPrintingCanceled) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInNewDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
// A print job is started, but results in a cancel. There are no callbacks
// to notice the start job. The expected events for this are:
// 1. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/1);
} else {
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started, but results in a cancel.
// 3. The print job is canceled.
// 4. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/4);
}
PrintAfterPreviewIsReadyAndLoaded();
// No tracking of start printing or cancel callbacks for in-browser tests,
// only for OOP.
if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) {
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kCanceled);
EXPECT_EQ(cancel_count(), 1);
}
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingAccessDenied) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForAccessDeniedErrorsInNewDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// The expected events for this are:
// 1. Update print settings.
// 2. A print job is started, but has an access-denied error.
// 3. A retry to start the print job with adjusted access will succeed.
// 4. Rendering for 1 page of document of content.
// 5. Completes with document done.
// 6. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/6);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 1);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingRepeatedAccessDenied) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeAsRepeatingErrorGenerator();
PrimeForAccessDeniedErrorsInNewDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// Test of a misbehaving printer driver which only returns access-denied
// errors. The expected events for this are:
// 1. Update print settings.
// 2. A print job is started, but has an access-denied error.
// 3. A retry to start the print job with adjusted access will still fail.
// 4. An error dialog is shown.
// 5. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 6. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/6);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kAccessDenied);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
#if BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingRenderPageAccessDenied) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForAccessDeniedErrorsInRenderPrintedPage();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// No attempt to retry is made if an access-denied error occurs when trying
// to render a page. The expected events for this are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Rendering for 1 page of document of content fails with access denied.
// 4. An error dialog is shown.
// 5. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 6. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/6);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kAccessDenied);
EXPECT_EQ(render_printed_page_count(), 0);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingMultipageMidJobError) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
// Delay rendering until all pages have been sent, to avoid any race
// conditions related to error handling. This is to ensure that page 3 is in
// the service queued for processing, before we let page 2 be processed and
// have it trigger an error that could affect page 3 processing.
PrimeForDelayedRenderingUntilPage(/*page_number=*/3);
PrimeForRenderingErrorOnPage(/*page_number=*/2);
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// The expected events for this are:
// 1. Update print settings.
// 2. Start the print job.
// 3. First page render callback shows success.
// 4. Second page render callback shows failure. Will start failure
// processing to cancel the print job.
// 5. A printing error dialog is displayed.
// 6. Third page render callback will show it was canceled (due to prior
// failure). This is disregarded by the browser, since the job has
// already been canceled.
// 7. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 8. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/8);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
// First failure page is `kFailed`, but is followed by another page with
// status `kCanceled`.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kCanceled);
EXPECT_EQ(render_printed_page_count(), 1);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
#endif // BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows once XPS print pipeline is added.
#if !BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingRenderDocumentAccessDenied) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForAccessDeniedErrorsInRenderPrintedDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// No attempt to retry is made if an access-denied error occurs when trying
// to render a document. The expected events for this are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Rendering for 1 page of document of content fails with access denied.
// 4. An error dialog is shown.
// 5. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 6. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/6);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kAccessDenied);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
#endif // !BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartPrintingDocumentDoneAccessDenied) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForAccessDeniedErrorsInDocumentDone();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// No attempt to retry is made if an access-denied error occurs when trying
// do wrap-up a rendered document. The expected events are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Rendering for 1 page of document of content.
// 4. Document done results in an access-denied error.
// 5. An error dialog is shown.
// 6. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
PrintAfterPreviewIsReadyAndLoaded();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 1);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kAccessDenied);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(cancel_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
SystemPrintFromPrintPreview) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
#if BUILDFLAG(IS_WIN)
// There are no callbacks that trigger for print stages with in-browser
// printing for the Windows case. The only expected event for this is to
// wait for the one print job to be destroyed, to ensure printing finished
// cleanly before completing the test.
SetNumExpectedMessages(/*num=*/1);
#else
// Once the transition to system print is initiated, the expected events
// are:
// 1. Use default settings.
// 2. Ask the user for settings.
// 3. Wait until all processing for DidPrintDocument is known to have
// completed, to ensure printing finished cleanly before completing the
// test.
// 4. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/4);
#endif // BUILDFLAG(IS_WIN)
} else {
#if BUILDFLAG(IS_WIN)
// Once the transition to system print is initiated, the expected events
// are:
// 1. Update print settings.
// 2. A print job is started.
// 3. Rendering for 1 page of document of content.
// 4. Completes with document done.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
#else
// Once the transition to system print is initiated, the expected events
// are:
// 1. A print job is started.
// 2. Rendering for 1 page of document of content.
// 3. Completes with document done.
// 4. Wait until all processing for DidPrintDocument is known to have
// completed, to ensure printing finished cleanly before completing the
// test.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
#endif // BUILDFLAG(IS_WIN)
}
SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
#if !BUILDFLAG(IS_WIN)
EXPECT_TRUE(did_get_settings_with_ui());
EXPECT_EQ(did_print_document_count(), 1);
#endif
EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
} else {
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 1);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
#else
// TODO(crbug.com/1414968): Update the expectation once system print
// settings are properly reflected at start of job print.
EXPECT_NE(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
#endif
}
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
}
#if BUILDFLAG(IS_WIN)
// This test is Windows-only, since it is the only platform which can invoke
// the system print dialog from within `PrintingContext::UpdatePrintSettings()`.
// From that system dialog we can cause a cancel to occur.
// TODO(crbug.com/809738): Expand this to also cover in-browser, once an
// appropriate signal is available to use for tracking expected events.
// TODO(crbug.com/1435566): Enable this test once it works without the need
// for --single-process-tests flag.
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
DISABLED_SystemPrintFromPrintPreviewCancelRetry) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInAskUserForSettings();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// The expected events for this are:
// 1. Update the print settings, which indicates to cancel the print
// request. No further printing calls are made.
// No print job is created because of such an early cancel.
SetNumExpectedMessages(/*num=*/1);
SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kCanceled);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 0);
// Now try to initiate the system print from a Print Preview again.
// Same number of expected events.
ResetNumReceivedMessages();
SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kCanceled);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 0);
}
#endif // BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartBasicPrint) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings.
// 3. A print job is started.
// 4. The print compositor will complete generating the document.
// 5. The document is rendered.
// 6. Receive document done notification.
// 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
#else
// The expected events for this are:
// 1. Get default settings, followed by asking user for settings. This is
// invoked from the browser process, so there is no override to observe
// this. Then a print job is started.
// 2. The print compositor will complete generating the document.
// 3. The document is rendered.
// 4. Receive document done notification.
// 5. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
#endif
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
// macOS and Linux currently have to invoke a system dialog from within the
// browser process. There is not a callback to capture the result in these
// cases.
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
#else
// TODO(crbug.com/1414968): Update the expectation once system print
// settings are properly reflected at start of job print.
EXPECT_NE(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
#endif
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
// RenderPrintedDocument() once XPS print pipeline is added.
EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(render_printed_page_count(), 1);
#else
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(did_print_document_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
// TODO(crbug.com/1375007): Very flaky on Mac and slightly on Linux.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_StartBasicPrintCancel DISABLED_StartBasicPrintCancel
#else
#define MAYBE_StartBasicPrintCancel StartBasicPrintCancel
#endif
IN_PROC_BROWSER_TEST_F(SystemAccessProcessInBrowserPrintBrowserTest,
MAYBE_StartBasicPrintCancel) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInAskUserForSettings();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings, which indicates to cancel the print
// request. No further printing calls are made.
// No print job is created because of such an early cancel.
SetNumExpectedMessages(/*num=*/2);
#else
// TODO(crbug.com/1375007) Need a good signal to use for test expectations.
#endif
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
EXPECT_TRUE(did_use_default_settings());
EXPECT_TRUE(did_get_settings_with_ui());
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(did_print_document_count(), 0);
EXPECT_EQ(print_job_destruction_count(), 0);
// `PrintBackendService` should never be used when printing in-browser.
EXPECT_FALSE(print_backend_service_use_detected());
}
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
StartBasicPrintFails) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForErrorsInNewDocument();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
// There are only partial overrides to track most steps in the printing
// pipeline, so the expected events for this are:
// 1. Gets default settings.
// 2. Asks user for settings.
// 3. A print job is started, but that fails. There is no override to
// this notice directly. This does cause an error dialog to be shown.
// 4. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
// 5. The renderer will have initiated printing of document, which could
// invoke the print compositor. Wait until all processing for
// DidPrintDocument is known to have completed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
} else {
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Gets default settings.
// 2. Asks user for settings.
// 3. A print job is started, which fails.
// 4. An error dialog is shown.
// 5. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 6. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
// 7. The renderer will have initiated printing of document, which could
// invoke the print compositor. Wait until all processing for
// DidPrintDocument is known to have completed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
#else
// The expected events for this are:
// 1. Get default settings, followed by asking user for settings. This is
// invoked from the browser process, so there is no override to observe
// this. Then a print job is started, which fails.
// 2. An error dialog is shown.
// 3. The print job is canceled. The callback from the service could occur
// after the print job has been destroyed.
// 4. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
// 5. The print compositor will have started to generate the document.
// Wait until that is known to have completed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
#endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
}
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed);
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(
cancel_count(),
GetParam() == PrintBackendFeatureVariation::kInBrowserProcess ? 0 : 1);
EXPECT_EQ(did_print_document_count(), 1);
EXPECT_EQ(print_job_destruction_count(), 1);
}
// macOS and Linux currently have to invoke a system dialog from within the
// browser process. There is not a callback to capture the result in these
// cases.
// TODO(crbug.com/1374188) Re-enable for Linux once `AskForUserSettings()` is
// able to be pushed OOP for Linux.
#undef MAYBE_StartBasicPrintCancel
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_StartBasicPrintCancel DISABLED_StartBasicPrintCancel
#else
#define MAYBE_StartBasicPrintCancel StartBasicPrintCancel
#endif
IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
MAYBE_StartBasicPrintCancel) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInAskUserForSettings();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings, which indicates to cancel the print
// request. No further printing calls are made.
// No print job is created because of such an early cancel.
SetNumExpectedMessages(/*num=*/2);
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kCanceled);
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(did_print_document_count(), 0);
EXPECT_EQ(print_job_construction_count(), 0);
}
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
StartBasicPrintConcurrent) {
// Linux allows concurrent printing, so regular setup for printing is needed.
// It is uninteresting to do a full print in this case, it is better to exit
// the print sequence early, but at a known time after when PrintNow() would
// fail if concurrent printing isn't allowed. That can be achieved by just
// canceling out from asking for settings.
#if BUILDFLAG(IS_LINUX)
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInAskUserForSettings();
#endif
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.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 =
SetUpAndReturnPrintViewManager(web_contents);
// Pretend that a window has started a system print.
absl::optional<PrintBackendServiceManager::ClientId> client_id =
PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
ASSERT_TRUE(client_id.has_value());
#if BUILDFLAG(IS_LINUX)
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings, which indicates to cancel the print
// request. No further printing calls are made.
// No print job is created because of such an early cancel.
SetNumExpectedMessages(/*num=*/2);
#endif
// Now initiate a system print that would exist concurrently with that.
StartBasicPrint(web_contents);
#if BUILDFLAG(IS_LINUX)
WaitUntilCallbackReceived();
#endif
const absl::optional<bool>& result = print_view_manager->print_now_result();
ASSERT_TRUE(result.has_value());
// With the exception of Linux, concurrent system print is not allowed.
#if BUILDFLAG(IS_LINUX)
EXPECT_TRUE(*result);
#else
// The denied concurrent print is silent without an error.
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_FALSE(*result);
#endif
// Cleanup before test shutdown.
PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
}
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
SystemPrintFromPrintPreviewConcurrent) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
// Pretend that another tab has started a system print.
// TODO(crbug.com/809738) Improve on this test by using a persistent fake
// system print dialog.
absl::optional<PrintBackendServiceManager::ClientId> client_id =
PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
ASSERT_TRUE(client_id.has_value());
// Now do a print preview which will try to switch to doing system print.
#if BUILDFLAG(IS_LINUX)
// The expected events for this are:
// 1. Start printing.
// 2. The document is rendered.
// 3. Receive document done notification.
// 4. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/4);
constexpr bool kWaitForCallback = true;
#else
// Inability to support this should be detected immediately without needing
// to wait for callback.
constexpr bool kWaitForCallback = false;
#endif
SystemPrintFromPreviewOnceReadyAndLoaded(kWaitForCallback);
// With the exception of Linux, concurrent system print is not allowed.
ASSERT_TRUE(system_print_registration_succeeded().has_value());
#if BUILDFLAG(IS_LINUX)
EXPECT_TRUE(*system_print_registration_succeeded());
#else
// The denied concurrent print is silent without an error.
EXPECT_FALSE(*system_print_registration_succeeded());
EXPECT_EQ(error_dialog_shown_count(), 0u);
#endif
// Cleanup before test shutdown.
PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
StartBasicPrintUseDefaultFails) {
PrimeForFailInUseDefaultSettings();
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings, which fails.
// 2. The print error dialog is shown.
// No print job is created from such an early failure.
SetNumExpectedMessages(/*num=*/2);
#else
// When get default settings is invoked from the browser process, there is no
// override to observe this failure. This means the expected events are:
// 1. The print error dialog is shown.
// No print job is created from such an early failure.
SetNumExpectedMessages(/*num=*/1);
#endif
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kFailed);
#endif
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(did_print_document_count(), 0);
EXPECT_EQ(print_job_construction_count(), 0);
}
#endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
} // namespace printing