| // 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 <optional> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #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/common/chrome_switches.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/enterprise/buildflags/buildflags.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 "content/public/test/no_renderer_crashes_assertion.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 "printing/printing_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size_f.h" |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| #include "chrome/browser/printing/print_backend_service_manager.h" |
| #include "chrome/browser/printing/print_backend_service_test_impl.h" |
| #include "chrome/browser/printing/print_job_worker_oop.h" |
| #include "chrome/browser/printing/print_preview_dialog_controller.h" |
| #include "chrome/browser/printing/printer_query_oop.h" |
| #include "chrome/browser/task_manager/task_manager_browsertest_util.h" |
| #include "chrome/browser/task_manager/task_manager_interface.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/services/printing/public/mojom/print_backend_service.mojom.h" |
| #endif |
| |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| #include "chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h" |
| #include "chrome/browser/enterprise/connectors/common.h" |
| #include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h" // nogncheck |
| #include "chrome/browser/enterprise/connectors/test/fake_content_analysis_delegate.h" // nogncheck |
| #include "chrome/browser/policy/dm_token_utils.h" |
| #include "components/enterprise/common/proto/connectors.pb.h" |
| |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| #include "chrome/browser/enterprise/connectors/test/fake_content_analysis_sdk_manager.h" // nogncheck |
| #endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| #endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // TODO(crbug.com/40567307) ChromeOS uses different testing setup that isn't |
| // hooked up to make use of `TestPrintingContext` yet. |
| #error "ChromeOS not supported here yet" |
| #endif |
| |
| using task_manager::TaskManagerInterface; |
| using task_manager::browsertest_util::MatchAnyTab; |
| using task_manager::browsertest_util::MatchUtility; |
| using task_manager::browsertest_util::WaitForTaskManagerRows; |
| |
| namespace printing { |
| |
| namespace { |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) && !BUILDFLAG(IS_CHROMEOS) |
| constexpr gfx::SizeF kLetterPhysicalSize = gfx::SizeF(612, 792); |
| constexpr gfx::RectF kLetterPrintableArea = gfx::RectF(5, 5, 602, 782); |
| constexpr gfx::SizeF kLegalPhysicalSize = gfx::SizeF(612, 1008); |
| constexpr gfx::RectF kLegalPrintableArea = gfx::RectF(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::SizeF kLetterExpectedContentSize = gfx::SizeF(556, 736); |
| constexpr gfx::SizeF kLegalExpectedContentSize = gfx::SizeF(556, 952); |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) && !BUILDFLAG(IS_CHROMEOS) |
| |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| constexpr char kFakeDmToken[] = "fake-dm-token"; |
| |
| // The policy values below correspond to the schema described in |
| // https://chromeenterprise.google/policies/#OnPrintEnterpriseConnector |
| constexpr char kCloudAnalysisBlockingPolicy[] = R"({ |
| "service_provider": "google", |
| "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ], |
| "block_until_verdict": 1, |
| "block_large_files": true |
| })"; |
| |
| constexpr char kCloudAnalysisNonBlockingPolicy[] = R"({ |
| "service_provider": "google", |
| "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ], |
| "block_until_verdict": 0, |
| "block_large_files": true |
| })"; |
| |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| constexpr char kLocalAnalysisPolicy[] = R"({ |
| "service_provider": "local_user_agent", |
| "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ], |
| "block_until_verdict": 1, |
| "block_large_files": true |
| })"; |
| #endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| |
| using OnDidCompositeForContentAnalysis = |
| base::RepeatingCallback<void(bool allowed)>; |
| #endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| void CancelPrintPreview(content::WebContents* preview_dialog) { |
| // This script locates and clicks the Cancel button for a Print Preview |
| // dialog. |
| const char kScript[] = R"( |
| const button = document.getElementsByTagName('print-preview-app')[0] |
| .$['sidebar'] |
| .shadowRoot.querySelector('print-preview-button-strip') |
| .shadowRoot.querySelector('.cancel-button'); |
| button.click();)"; |
| |
| // It is possible for sufficient processing for the cancel 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 |
| // a successful cancel, do not assert on this return result, just ignore the |
| // error instead. Rely upon tests using other methods to catch errors, such |
| // as monitoring for the Print Preview to be done if that is needed. |
| std::ignore = content::ExecJs(preview_dialog, kScript); |
| } |
| |
| // 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, |
| }; |
| |
| const char* GetPrintBackendString(PrintBackendFeatureVariation variation) { |
| switch (variation) { |
| case PrintBackendFeatureVariation::kInBrowserProcess: |
| return "InBrowser"; |
| case PrintBackendFeatureVariation::kOopSandboxedService: |
| return "OopSandboxed"; |
| case PrintBackendFeatureVariation::kOopUnsandboxedService: |
| return "OopUnsandboxed"; |
| } |
| } |
| |
| enum class PlatformPrintApiVariation { |
| #if BUILDFLAG(IS_WIN) |
| // Windows print drivers can have a language type which alters how the print |
| // data is processed. While much of the GDI printing pipeline is not |
| // concerned with the language type differences, certain portions of the |
| // printing pipeline are impacted by it. Most tests only need test against |
| // one GDI language type. |
| kGdiEmf, |
| kGdiPostScriptLevel2, |
| kGdiPostScriptLevel3, |
| kGdiTextOnly, |
| kXps, |
| #else |
| kCups, |
| #endif |
| }; |
| |
| const char* GetPlatformPrintApiString(PlatformPrintApiVariation variation) { |
| switch (variation) { |
| #if BUILDFLAG(IS_WIN) |
| case PlatformPrintApiVariation::kGdiEmf: |
| return "GdiEmf"; |
| case PlatformPrintApiVariation::kGdiPostScriptLevel2: |
| return "GdiPostScriptLevel2"; |
| case PlatformPrintApiVariation::kGdiPostScriptLevel3: |
| return "GdiPostScriptLevel3"; |
| case PlatformPrintApiVariation::kGdiTextOnly: |
| return "GdiTextOnly"; |
| case PlatformPrintApiVariation::kXps: |
| return "Xps"; |
| #else |
| case PlatformPrintApiVariation::kCups: |
| return "Cups"; |
| #endif |
| } |
| } |
| |
| // Caution must be taken with platform API variations, as `kXps` should not |
| // be generated with `kInBrowserProcess`. Use of `testing::Combine()` between |
| // `PrintBackendFeatureVariation` and `PlatformPrintApiVariation` could |
| // inadvertently cause this illegal combination. This can be avoided by using |
| // a local helper method to generate the allowed combinations. |
| // |
| // `SystemAccessProcessPrintBrowserTestBase` will check this constraint at |
| // runtime. |
| struct PrintBackendAndPlatformPrintApiVariation { |
| PrintBackendFeatureVariation print_backend; |
| PlatformPrintApiVariation platform_api; |
| }; |
| |
| // Tests using these variations are concerned with all the different language |
| // types on Windows. |
| constexpr PrintBackendAndPlatformPrintApiVariation |
| kSandboxedServicePlatformPrintLanguageApiVariations[] = { |
| #if BUILDFLAG(IS_WIN) |
| // TODO(crbug.com/40100562): Include XPS variation. |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PlatformPrintApiVariation::kGdiEmf}, |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PlatformPrintApiVariation::kGdiPostScriptLevel2}, |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PlatformPrintApiVariation::kGdiPostScriptLevel3}, |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PlatformPrintApiVariation::kGdiTextOnly}, |
| #else |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PlatformPrintApiVariation::kCups}, |
| #endif |
| }; |
| |
| std::string GetPrintBackendAndPlatformPrintApiString( |
| const PrintBackendAndPlatformPrintApiVariation& variation) { |
| return base::JoinString({GetPrintBackendString(variation.print_backend), |
| GetPlatformPrintApiString(variation.platform_api)}, |
| /*separator=*/"_"); |
| } |
| |
| std::string GetPrintBackendAndPlatformPrintApiTestSuffix( |
| const testing::TestParamInfo<PrintBackendAndPlatformPrintApiVariation>& |
| info) { |
| return GetPrintBackendAndPlatformPrintApiString(info.param); |
| } |
| |
| std::vector<PrintBackendAndPlatformPrintApiVariation> |
| GeneratePrintBackendAndPlatformPrintApiVariations( |
| std::vector<PrintBackendFeatureVariation> print_backend_variations) { |
| std::vector<PrintBackendAndPlatformPrintApiVariation> variations; |
| |
| for (PrintBackendFeatureVariation print_backend_variation : |
| print_backend_variations) { |
| #if BUILDFLAG(IS_WIN) |
| // Only need one GDI variation, not interested in different language types. |
| // TODO(crbug.com/40100562): Include XPS variation, only when the |
| // `print_backend_variation` is not `kInBrowserProcess`. |
| variations.emplace_back(print_backend_variation, |
| PlatformPrintApiVariation::kGdiEmf); |
| #else |
| variations.emplace_back(print_backend_variation, |
| PlatformPrintApiVariation::kCups); |
| #endif |
| } |
| |
| return variations; |
| } |
| |
| std::string GetServiceLaunchTimingTestSuffix( |
| const testing::TestParamInfo<bool>& info) { |
| return info.param ? "EarlyStart" : "OnDemand"; |
| } |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) |
| |
| } // 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 OnFinishDocumentDoneCallback = base::RepeatingCallback<void(int job_id)>; |
| 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(int job_id, mojom::ResultCode result)>; |
| using OnDidCancelCallback = base::RepeatingClosure; |
| using OnDidShowErrorDialog = base::RepeatingClosure; |
| |
| class TestPrintJobWorker : public PrintJobWorker { |
| public: |
| // Callbacks to run for overrides. |
| struct PrintCallbacks { |
| ErrorCheckCallback error_check_callback; |
| OnUseDefaultSettingsCallback did_use_default_settings_callback; |
| OnGetSettingsWithUICallback did_get_settings_with_ui_callback; |
| OnDidUpdatePrintSettingsCallback did_update_print_settings_callback; |
| OnFinishDocumentDoneCallback did_finish_document_done_callback; |
| }; |
| |
| TestPrintJobWorker( |
| std::unique_ptr<PrintingContext::Delegate> printing_context_delegate, |
| std::unique_ptr<PrintingContext> printing_context, |
| PrintJob* print_job, |
| PrintCallbacks* callbacks) |
| : PrintJobWorker(std::move(printing_context_delegate), |
| std::move(printing_context), |
| print_job), |
| callbacks_(callbacks) {} |
| TestPrintJobWorker(const TestPrintJobWorker&) = delete; |
| TestPrintJobWorker& operator=(const TestPrintJobWorker&) = delete; |
| |
| // `PrintJobWorker` overrides: |
| void Cancel() override { |
| callbacks_->error_check_callback.Run(mojom::ResultCode::kCanceled); |
| PrintJobWorker::Cancel(); |
| } |
| |
| private: |
| // `PrintJobWorker` overrides: |
| void FinishDocumentDone(int job_id) override { |
| callbacks_->did_finish_document_done_callback.Run(job_id); |
| PrintJobWorker::FinishDocumentDone(job_id); |
| } |
| void OnCancel() override { |
| callbacks_->error_check_callback.Run(mojom::ResultCode::kCanceled); |
| PrintJobWorker::OnCancel(); |
| } |
| void OnFailure() override { |
| callbacks_->error_check_callback.Run(mojom::ResultCode::kFailed); |
| PrintJobWorker::OnFailure(); |
| } |
| |
| const raw_ptr<PrintCallbacks> callbacks_; |
| }; |
| |
| class TestPrinterQuery : public PrinterQuery { |
| public: |
| TestPrinterQuery(content::GlobalRenderFrameHostId rfh_id, |
| TestPrintJobWorker::PrintCallbacks* callbacks) |
| : PrinterQuery(rfh_id), callbacks_(callbacks) {} |
| |
| void UpdatePrintSettings(base::Value::Dict new_settings, |
| SettingsCallback callback) override { |
| PrinterQuery::UpdatePrintSettings( |
| std::move(new_settings), |
| base::BindOnce(&TestPrinterQuery::OnDidUpdatePrintSettings, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| 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(); |
| } |
| |
| void OnDidUpdatePrintSettings(SettingsCallback callback, |
| std::unique_ptr<PrintSettings> settings, |
| mojom::ResultCode result) { |
| DVLOG(1) << "Observed: update print settings"; |
| std::move(callback).Run(std::move(settings), result); |
| callbacks_->did_update_print_settings_callback.Run(result); |
| } |
| |
| std::unique_ptr<PrintJobWorker> CreatePrintJobWorker( |
| PrintJob* print_job) override { |
| return std::make_unique<TestPrintJobWorker>( |
| std::move(printing_context_delegate_), std::move(printing_context_), |
| print_job, callbacks_); |
| } |
| |
| const raw_ptr<TestPrintJobWorker::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; |
| #if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) |
| OnDidUseDefaultSettingsCallback did_use_default_settings_callback; |
| OnDidAskUserForSettingsCallback did_ask_user_for_settings_callback; |
| #else |
| // Need to use the base class version of callbacks when the system dialog |
| // must be displayed from the browser process. |
| OnUseDefaultSettingsCallback did_use_default_settings_callback; |
| OnGetSettingsWithUICallback did_get_settings_with_ui_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, |
| std::optional<PrintBackendServiceManager::ClientId> client_id, |
| std::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, int job_id) override { |
| DVLOG(1) << "Observed: start printing of document"; |
| callbacks_->error_check_callback.Run(result); |
| PrintJobWorkerOop::OnDidStartPrinting(result, job_id); |
| 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(job_id, result); |
| } |
| |
| void OnDidCancel(scoped_refptr<PrintJob> job, |
| mojom::ResultCode result) 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, result); |
| callbacks_->did_cancel_callback.Run(); |
| } |
| |
| const 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) {} |
| |
| #if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) |
| 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); |
| } |
| |
| 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); |
| } |
| #else |
| void UseDefaultSettings(SettingsCallback callback) override { |
| DVLOG(1) << "Observed: invoke use default settings"; |
| PrinterQueryOop::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"; |
| PrinterQueryOop::GetSettingsWithUI(document_page_count, has_selection, |
| is_scripted, std::move(callback)); |
| callbacks_->did_get_settings_with_ui_callback.Run(); |
| } |
| #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> CreatePrintJobWorkerOop( |
| 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_; |
| const raw_ptr<TestPrintJobWorkerOop::PrintCallbacks> callbacks_; |
| }; |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) |
| |
| class SystemAccessProcessPrintBrowserTestBase |
| : public PrintBrowserTest, |
| public PrintJob::Observer, |
| public PrintViewManagerBase::TestObserver { |
| public: |
| SystemAccessProcessPrintBrowserTestBase() = default; |
| ~SystemAccessProcessPrintBrowserTestBase() override = default; |
| |
| virtual bool UseService() = 0; |
| |
| // Only of interest when `UseService()` returns true. |
| virtual bool SandboxService() = 0; |
| |
| // Only applicable when `UseService()` returns true. |
| virtual bool EarlyStartService() { return false; } |
| |
| #if BUILDFLAG(IS_WIN) |
| // Only applicable when `UseService()` returns true. |
| virtual bool UseXps() = 0; |
| #endif |
| |
| void SetUpFeatures() { |
| std::vector<base::test::FeatureRefAndParams> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| if (UseService()) { |
| enabled_features.push_back( |
| {features::kEnableOopPrintDrivers, |
| {{features::kEnableOopPrintDriversEarlyStart.name, |
| EarlyStartService() ? "true" : "false"}, |
| {features::kEnableOopPrintDriversJobPrint.name, "true"}, |
| {features::kEnableOopPrintDriversSandbox.name, |
| SandboxService() ? "true" : "false"}}}); |
| #if BUILDFLAG(IS_WIN) |
| if (UseXps()) { |
| enabled_features.push_back({features::kUseXpsForPrinting, {}}); |
| } else { |
| disabled_features.push_back(features::kUseXpsForPrinting); |
| } |
| // TODO(crbug.com/40111626): Support `kUseXpsForPrintingFromPdf`. |
| disabled_features.push_back(features::kUseXpsForPrintingFromPdf); |
| // TODO(crbug.com/40212677): Support `kReadPrinterCapabilitiesWithXps`. |
| disabled_features.push_back(features::kReadPrinterCapabilitiesWithXps); |
| #endif // BUILDFLAG(IS_WIN) |
| } else { |
| disabled_features.push_back(features::kEnableOopPrintDrivers); |
| #if BUILDFLAG(IS_WIN) |
| CHECK(!UseXps()); |
| disabled_features.push_back(features::kUseXpsForPrinting); |
| disabled_features.push_back(features::kUseXpsForPrintingFromPdf); |
| disabled_features.push_back(features::kReadPrinterCapabilitiesWithXps); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) |
| feature_list_.InitWithFeaturesAndParameters(enabled_features, |
| disabled_features); |
| } |
| |
| void SetUp() override { |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| SetUpFeatures(); |
| |
| if (UseService()) { |
| // 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::OopErrorCheck, |
| base::Unretained(this)); |
| #if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) |
| test_print_job_worker_oop_callbacks_.did_use_default_settings_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnDidUseDefaultSettings, |
| base::Unretained(this)); |
| test_print_job_worker_oop_callbacks_.did_ask_user_for_settings_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnDidAskUserForSettings, |
| base::Unretained(this)); |
| #else |
| test_print_job_worker_oop_callbacks_.did_use_default_settings_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnUseDefaultSettings, |
| base::Unretained(this)); |
| test_print_job_worker_oop_callbacks_.did_get_settings_with_ui_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnGetSettingsWithUI, |
| base::Unretained(this)); |
| #endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) |
| 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_.error_check_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::InProcessErrorCheck, |
| base::Unretained(this)); |
| 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_print_job_worker_callbacks_ |
| .did_update_print_settings_callback = base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnDidUpdatePrintSettings, |
| base::Unretained(this)); |
| test_print_job_worker_callbacks_.did_finish_document_done_callback = |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnFinishDocumentDone, |
| 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() && !EarlyStartService()) { |
| 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); |
| } |
| #endif |
| ASSERT_EQ(print_job_construction_count(), print_job_destruction_count()); |
| } |
| |
| void TearDownOnMainThread() override { |
| PrintBrowserTest::TearDownOnMainThread(); |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| PrintBackendServiceManager::ResetForTesting(); |
| #endif |
| } |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| // `PrintBackendServiceTestImpl` does a debug check on shutdown that there |
| // are no residual persistent printing contexts left in the service. For |
| // tests which are known to break this (either by design, for test simplicity |
| // or because a related change is only partly implemented), use this method |
| // to notify the service to not DCHECK on such a condition. |
| void SkipPersistentContextsCheckOnShutdown() { |
| print_backend_service_->SkipPersistentContextsCheckOnShutdown(); |
| } |
| |
| // PrintViewManagerBase::TestObserver: |
| void OnPrintPreviewDone() override { |
| if (check_for_print_preview_done_) { |
| CheckForQuit(); |
| } |
| } |
| |
| void OnRegisterSystemPrintClient(bool succeeded) override { |
| system_print_registration_succeeded_ = succeeded; |
| } |
| #endif |
| |
| void OnDidPrintDocument() override { |
| ++did_print_document_count_; |
| CheckForQuit(); |
| } |
| |
| void OnRenderFrameDeleted() override { |
| if (check_for_render_frame_deleted_) { |
| CheckForQuit(); |
| } |
| } |
| |
| // PrintJob::Observer: |
| void OnDestruction() override { |
| ++print_job_destruction_count_; |
| CheckForQuit(); |
| } |
| |
| void OnCreatedPrintJob(PrintJob* print_job) { |
| ++print_job_construction_count_; |
| print_job->AddObserver(*this); |
| } |
| |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| void OnCompositedForContentAnalysis(bool allowed) { |
| ++composited_for_content_analysis_count_; |
| CheckForQuit(); |
| } |
| #endif |
| |
| TestPrintViewManager* SetUpAndReturnPrintViewManager( |
| content::WebContents* web_contents) { |
| // Safe to use `base::Unretained(this)` since this testing class |
| // necessarily must outlive all interactions from the tests which will |
| // run through `PrintViewManagerBase`, which is what causes new jobs to |
| // be created and use this callback. |
| auto manager = std::make_unique<TestPrintViewManager>( |
| web_contents, |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnCreatedPrintJob, |
| base::Unretained(this))); |
| manager->AddTestObserver(*this); |
| #if BUILDFLAG(IS_WIN) |
| if (simulate_pdf_conversion_error_on_page_index_.has_value()) { |
| manager->set_simulate_pdf_conversion_error_on_page_index( |
| *simulate_pdf_conversion_error_on_page_index_); |
| } |
| #endif |
| 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); |
| } |
| |
| content::WebContents* PrintAfterPreviewIsReadyAndLoaded() { |
| return PrintAfterPreviewIsReadyAndLoaded(PrintParams()); |
| } |
| |
| content::WebContents* PrintAfterPreviewIsReadyAndLoaded( |
| const PrintParams& params) { |
| return PrintAfterPreviewIsReadyAndMaybeLoaded(params, |
| /*wait_for_loaded=*/true); |
| } |
| |
| content::WebContents* PrintAfterPreviewIsReadyAndMaybeLoaded( |
| const PrintParams& params, |
| bool wait_for_loaded) { |
| // First invoke the Print Preview dialog with requested method. |
| content::WebContents* preview_dialog = |
| wait_for_loaded ? PrintAndWaitUntilPreviewIsReadyAndLoaded(params) |
| : PrintAndWaitUntilPreviewIsReady(params); |
| if (!preview_dialog) { |
| ADD_FAILURE() << "Unable to get Print Preview dialog"; |
| return nullptr; |
| } |
| |
| // 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();)"; |
| auto result = content::ExecJs(preview_dialog, kScript); |
| // TODO(crbug.com/40926610): Update once it is known if the assertion |
| // should not happen if the failure is just because the renderer |
| // terminated. |
| // If the renderer terminates, it will return a failing result. It has |
| // been observed in other tests that sometimes the renderer terminates |
| // and the test was successful; all the needed callbacks happened before |
| // ExecJs() returned. |
| // Add a warning for the logs to help with debugging, and then only do |
| // the assert check after having done the wait. |
| // If the renderer terminated but the printing was all successful, then |
| // `WaitUntilCallbackReceived()` should return successfully, and any crash |
| // logs should show the assert. Otherwise the crashes for this bug should |
| // change to become the test timeouts. |
| if (!result) { |
| ADD_FAILURE() << "ExecJs() failed; if reason is because the renderer " |
| "terminated, it is possibly okay? " |
| << result.message(); |
| } |
| WaitUntilCallbackReceived(); |
| return preview_dialog; |
| } |
| |
| void AdjustMediaAfterPreviewIsReadyAndLoaded() { |
| // First invoke the Print Preview dialog with `StartPrint()`. |
| content::WebContents* preview_dialog = |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| ASSERT_TRUE(preview_dialog); |
| |
| // Initial Print Preview is completely ready. |
| // Create an observer and 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. |
| TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true); |
| 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()`. |
| content::WebContents* preview_dialog = |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| ASSERT_TRUE(preview_dialog); |
| |
| // 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) |
| |
| #if BUILDFLAG(IS_MAC) |
| void OpenPdfInPreviewOnceReadyAndLoaded() { |
| // First invoke the Print Preview dialog with `StartPrint()`. |
| content::WebContents* preview_dialog = |
| PrintAndWaitUntilPreviewIsReadyAndLoaded(); |
| ASSERT_TRUE(preview_dialog); |
| |
| // Print Preview is completely ready, can now initiate printing. |
| // This script locates and clicks "Open PDF in Preview", which is still |
| // enabled even if it is hidden. |
| const char kOpenPdfWithPreviewScript[] = R"( |
| const openPdfInPreview = |
| document.getElementsByTagName('print-preview-app')[0] |
| .$['sidebar'] |
| .shadowRoot.querySelector('print-preview-link-container') |
| .$['openPdfInPreviewLink']; |
| openPdfInPreview.click();)"; |
| ASSERT_TRUE(content::ExecJs(preview_dialog, kOpenPdfWithPreviewScript)); |
| WaitUntilCallbackReceived(); |
| } |
| #endif // BUILDFLAG(IS_MAC) |
| |
| void PrimeAsRepeatingErrorGenerator() { reset_errors_after_check_ = false; } |
| |
| #if BUILDFLAG(IS_WIN) |
| void PrimeForPdfConversionErrorOnPageIndex(uint32_t page_index) { |
| simulate_pdf_conversion_error_on_page_index_ = page_index; |
| |
| // Queuing converted pages to be spooled occurs on the UI thread, while the |
| // actual spooling occurs on the worker thread. The worker thread polls for |
| // pages, making it difficult to know if earlier pages that are successfully |
| // converted will get spooled before some other error causes the job to be |
| // canceled. Do not use rendered page counts as part of any test |
| // expectations in this case. Other events should be used to know when it |
| // is safe to terminate the test. |
| DisableCheckForOnRenderedPrintedPage(); |
| } |
| #endif |
| |
| #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 PrimeForFailInUpdatePrinterSettings() { |
| test_printing_context_factory()->SetFailedErrorOnUpdatePrinterSettings(); |
| } |
| |
| 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); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| void DisableCheckForOnRenderedPrintedPage() { |
| check_for_rendered_printed_page_ = false; |
| } |
| #endif |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| void SetCheckForPrintPreviewDone(bool check) { |
| check_for_print_preview_done_ = check; |
| } |
| #endif |
| |
| void SetCheckForRenderFrameDeleted(bool check) { |
| check_for_render_frame_deleted_ = check; |
| } |
| |
| const std::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_; |
| } |
| |
| std::optional<int> document_done_job_id() const { |
| return document_done_job_id_; |
| } |
| |
| int cancel_count() const { return cancel_count_; } |
| std::optional<mojom::ResultCode> in_process_last_error_result_code() const { |
| return in_process_last_error_result_code_; |
| } |
| |
| 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_; } |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| int composited_for_content_analysis_count() const { |
| return composited_for_content_analysis_count_; |
| } |
| #endif |
| |
| 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 OopErrorCheck(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 InProcessErrorCheck(mojom::ResultCode result) { |
| // This is expected to only be called with unsuccessful results. |
| DCHECK_NE(result, mojom::ResultCode::kSuccess); |
| in_process_last_error_result_code_ = result; |
| } |
| |
| 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_++; |
| } |
| if (check_for_rendered_printed_page_) { |
| CheckForQuit(); |
| } |
| } |
| #endif |
| |
| void OnDidRenderPrintedDocument(mojom::ResultCode result) { |
| render_printed_document_result_ = result; |
| CheckForQuit(); |
| } |
| |
| void OnDidDocumentDone(int job_id, mojom::ResultCode result) { |
| document_done_job_id_ = job_id; |
| document_done_result_ = result; |
| CheckForQuit(); |
| } |
| |
| void OnFinishDocumentDone(int job_id) { document_done_job_id_ = job_id; } |
| |
| 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(IS_WIN) |
| bool check_for_rendered_printed_page_ = true; |
| #endif |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| bool check_for_print_preview_done_ = false; |
| bool check_for_render_frame_deleted_ = false; |
| TestPrintJobWorker::PrintCallbacks test_print_job_worker_callbacks_; |
| TestPrintJobWorkerOop::PrintCallbacks test_print_job_worker_oop_callbacks_; |
| CreatePrinterQueryCallback test_create_printer_query_callback_; |
| std::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; |
| #if BUILDFLAG(IS_WIN) |
| std::optional<uint32_t> simulate_pdf_conversion_error_on_page_index_; |
| #endif // BUILDFLAG(IS_WIN) |
| mojo::Remote<mojom::PrintBackendService> test_remote_; |
| std::unique_ptr<PrintBackendServiceTestImpl> print_backend_service_; |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) |
| std::optional<mojom::ResultCode> in_process_last_error_result_code_; |
| 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; |
| std::optional<int> document_done_job_id_; |
| int cancel_count_ = 0; |
| int print_job_construction_count_ = 0; |
| int print_job_destruction_count_ = 0; |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| int composited_for_content_analysis_count_ = 0; |
| #endif |
| }; |
| |
| #if BUILDFLAG(ENABLE_OOP_PRINTING) |
| |
| class SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest |
| : public SystemAccessProcessPrintBrowserTestBase, |
| public testing::WithParamInterface<bool> { |
| public: |
| SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest() = default; |
| ~SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest() override = |
| default; |
| |
| bool UseService() override { return true; } |
| bool SandboxService() override { return false; } |
| bool EarlyStartService() override { return GetParam(); } |
| #if BUILDFLAG(IS_WIN) |
| bool UseXps() override { return false; } |
| #endif |
| |
| bool DoesPrintBackendServiceTaskExist() { |
| TaskManagerInterface* task_mgr = TaskManagerInterface::GetTaskManager(); |
| std::u16string title = MatchUtility(u"Print Backend Service"); |
| for (auto task_id : task_mgr->GetTaskIdsList()) { |
| if (title == task_mgr->GetTitle(task_id)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest, |
| /*EarlyStartService=*/testing::Bool(), |
| GetServiceLaunchTimingTestSuffix); |
| |
| class SystemAccessProcessPrintBrowserTest |
| : public SystemAccessProcessPrintBrowserTestBase, |
| public testing::WithParamInterface< |
| PrintBackendAndPlatformPrintApiVariation> { |
| public: |
| SystemAccessProcessPrintBrowserTest() = default; |
| ~SystemAccessProcessPrintBrowserTest() override = default; |
| |
| bool UseService() override { |
| return GetParam().print_backend != |
| PrintBackendFeatureVariation::kInBrowserProcess; |
| } |
| bool SandboxService() override { |
| return GetParam().print_backend == |
| PrintBackendFeatureVariation::kOopSandboxedService; |
| } |
| #if BUILDFLAG(IS_WIN) |
| bool UseXps() override { |
| return GetParam().platform_api == PlatformPrintApiVariation::kXps; |
| } |
| #endif |
| |
| #if BUILDFLAG(IS_WIN) |
| mojom::PrinterLanguageType UseLanguageType() { |
| switch (GetParam().platform_api) { |
| case PlatformPrintApiVariation::kGdiEmf: |
| return mojom::PrinterLanguageType::kNone; |
| case PlatformPrintApiVariation::kGdiPostScriptLevel2: |
| return mojom::PrinterLanguageType::kPostscriptLevel2; |
| case PlatformPrintApiVariation::kGdiPostScriptLevel3: |
| return mojom::PrinterLanguageType::kPostscriptLevel3; |
| case PlatformPrintApiVariation::kGdiTextOnly: |
| return mojom::PrinterLanguageType::kTextOnly; |
| case PlatformPrintApiVariation::kXps: |
| return mojom::PrinterLanguageType::kXps; |
| } |
| } |
| #endif |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| SystemAccessProcessPrintBrowserTest, |
| testing::ValuesIn(GeneratePrintBackendAndPlatformPrintApiVariations( |
| {PrintBackendFeatureVariation::kInBrowserProcess, |
| PrintBackendFeatureVariation::kOopSandboxedService, |
| PrintBackendFeatureVariation::kOopUnsandboxedService})), |
| GetPrintBackendAndPlatformPrintApiTestSuffix); |
| |
| using SystemAccessProcessServicePrintBrowserTest = |
| SystemAccessProcessPrintBrowserTest; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| SystemAccessProcessServicePrintBrowserTest, |
| testing::ValuesIn(GeneratePrintBackendAndPlatformPrintApiVariations( |
| {PrintBackendFeatureVariation::kOopSandboxedService, |
| PrintBackendFeatureVariation::kOopUnsandboxedService})), |
| GetPrintBackendAndPlatformPrintApiTestSuffix); |
| |
| using SystemAccessProcessSandboxedServicePrintBrowserTest = |
| SystemAccessProcessServicePrintBrowserTest; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| SystemAccessProcessSandboxedServicePrintBrowserTest, |
| testing::ValuesIn(GeneratePrintBackendAndPlatformPrintApiVariations( |
| {PrintBackendFeatureVariation::kOopSandboxedService})), |
| GetPrintBackendAndPlatformPrintApiTestSuffix); |
| |
| using SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest = |
| SystemAccessProcessServicePrintBrowserTest; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest, |
| testing::ValuesIn(kSandboxedServicePlatformPrintLanguageApiVariations), |
| GetPrintBackendAndPlatformPrintApiTestSuffix); |
| |
| IN_PROC_BROWSER_TEST_P( |
| SystemAccessProcessUnsandboxedEarlyStartServicePrintBrowserTest, |
| ServiceLaunched) { |
| chrome::ShowTaskManager(browser()); |
| |
| // Wait for browser to open with a tab. |
| ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab())); |
| |
| // Now that startup is complete, look through the list of processes in the |
| // Task Manager to see if a Print Backend service has bee started (even |
| // though there has not been any request for printing). |
| CHECK_EQ(DoesPrintBackendServiceTaskExist(), EarlyStartService()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| UpdatePrintSettings) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| TestPrintViewManager print_view_manager(web_contents); |
| PrintViewManager::SetReceiverImplForTesting(&print_view_manager); |
| |
| 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); |
| |
| #if BUILDFLAG(IS_MAC) |
| EXPECT_EQ(kLegalPhysicalSize, snooped_params->params->page_size); |
| EXPECT_EQ(kLegalPrintableArea, snooped_params->params->printable_area); |
| EXPECT_EQ(kLegalExpectedContentSize, snooped_params->params->content_size); |
| #else |
| EXPECT_EQ(kLetterPhysicalSize, snooped_params->params->page_size); |
| EXPECT_EQ(kLetterPrintableArea, snooped_params->params->printable_area); |
| EXPECT_EQ(kLetterExpectedContentSize, snooped_params->params->content_size); |
| #endif |
| } |
| |
| 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); |
| |
| #if BUILDFLAG(IS_MAC) |
| EXPECT_EQ(kLetterPhysicalSize, snooped_params->params->page_size); |
| EXPECT_EQ(kLetterPrintableArea, snooped_params->params->printable_area); |
| EXPECT_EQ(kLetterExpectedContentSize, snooped_params->params->content_size); |
| #else |
| EXPECT_EQ(kLegalPhysicalSize, snooped_params->params->page_size); |
| EXPECT_EQ(kLegalPrintableArea, snooped_params->params->printable_area); |
| EXPECT_EQ(kLegalExpectedContentSize, snooped_params->params->content_size); |
| #endif |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| UpdatePrintSettingsFails) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForFailInUpdatePrinterSettings(); |
| |
| 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, which fails. No print job is created. |
| SetNumExpectedMessages(/*num=*/1); |
| |
| // Since the preview is loaded before initiating the Print, the error |
| // is displayed in the preview and the dialog remains open. |
| content::WebContents* preview_dialog = PrintAfterPreviewIsReadyAndLoaded(); |
| ASSERT_TRUE(preview_dialog); |
| |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kFailed); |
| |
| // Need to close the dialog to ensure proper cleanup is done before |
| // sanity checks at test termination. This posts to UI thread to close, |
| // so need to ensure that has a chance to run before terminating. |
| CancelPrintPreview(preview_dialog); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| UpdatePrintSettingsFailsErrorDialog) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForFailInUpdatePrinterSettings(); |
| |
| 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, which fails. No print job is created. |
| // TODO(crbug.com/40286396): Update expectations once an error dialog is |
| // shown to the user after this failure. |
| SetNumExpectedMessages(/*num=*/1); |
| PrintAfterPreviewIsReadyAndMaybeLoaded(PrintParams(), |
| /*wait_for_loaded=*/false); |
| |
| // Print Preview dialog will have been closed, ensure that the UI thread |
| // runs all posted tasks to reflect that before checking it is gone. |
| base::RunLoop().RunUntilIdle(); |
| content::WebContents* preview_dialog = |
| PrintPreviewDialogController::GetInstance()->GetPrintPreviewForContents( |
| web_contents); |
| ASSERT_FALSE(preview_dialog); |
| |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kFailed); |
| |
| // Initiating printing before the document is ready hides the Print Preview |
| // dialog. No error is shown to the user when this happens. |
| // TODO(crbug.com/40286396): Update once an error message is shown. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| } |
| |
| IN_PROC_BROWSER_TEST_P( |
| SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest, |
| StartPrinting) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| #if BUILDFLAG(IS_WIN) |
| SetPrinterLanguageTypeForSubsequentContexts(UseLanguageType()); |
| #endif |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| |
| 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/40100562) 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_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| |
| #if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS) |
| std::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_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| StartPrintingMultipage) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.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/40100562) 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/40100562) 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_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| 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); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| StartPrintingPdfConversionFails) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForPdfConversionErrorOnPageIndex(/*page_index=*/1); |
| |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.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 (UseService()) { |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. A print job is started. |
| // 3. PDF conversion fails, which results in the print job being |
| // canceled. |
| // 4. Wait for the print job to be destroyed, to ensure printing finished |
| // cleanly before completing the test. |
| // No error dialog is shown. |
| SetNumExpectedMessages(/*num=*/4); |
| } else { |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. Print job is started, but is canceled and destroyed due to failure |
| // during PDF conversion failure. |
| // No error dialog is shown. |
| SetNumExpectedMessages(/*num=*/2); |
| } |
| PrintAfterPreviewIsReadyAndLoaded(); |
| |
| // No tracking of start printing or cancel callbacks for in-browser tests, |
| // only for OOP. |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); |
| } else { |
| EXPECT_THAT(in_process_last_error_result_code(), |
| testing::Optional(mojom::ResultCode::kCanceled)); |
| } |
| // TODO(crbug.com/40288222): Update expectation once an error is shown for |
| // this failure. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| 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 (UseService()) { |
| // 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); |
| } else { |
| // The print job is started, but that fails and gets canceled. |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. An error dialog is shown. |
| // 3. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/3); |
| } |
| |
| PrintAfterPreviewIsReadyAndLoaded(); |
| |
| EXPECT_EQ(error_dialog_shown_count(), 1u); |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed); |
| EXPECT_EQ(cancel_count(), 1); |
| } else { |
| EXPECT_THAT(in_process_last_error_result_code(), |
| testing::Optional(mojom::ResultCode::kCanceled)); |
| } |
| 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 (UseService()) { |
| // 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); |
| } else { |
| // 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. Update print settings. |
| // 2. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/2); |
| } |
| |
| PrintAfterPreviewIsReadyAndLoaded(); |
| |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kCanceled); |
| EXPECT_EQ(cancel_count(), 1); |
| } else { |
| EXPECT_THAT(in_process_last_error_result_code(), |
| testing::Optional(mojom::ResultCode::kCanceled)); |
| } |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| StartPrintingAccessDenied) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| 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/40100562) 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_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(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_P(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_P(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/3_pages.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/40100562) Include Windows once XPS print pipeline is added. |
| #if !BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_P(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_P(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/40100562) 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"); |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| |
| 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 (UseService()) { |
| #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. Use default settings. |
| // 2. Ask the user for settings. |
| // 3. A print job is started. |
| // 4. Rendering for 1 page of document of content. |
| // 5. Completes with document done. |
| // 6. Wait until all processing for DidPrintDocument is known to have |
| // completed, to ensure printing finished cleanly before completing the |
| // test. |
| // 7. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/7); |
| #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. There are no other callbacks that trigger for print stages with |
| // in-browser printing for the Windows case. The only other 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=*/2); |
| #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) |
| } |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); |
| #if BUILDFLAG(IS_WIN) |
| // TODO(crbug.com/40100562) 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(*test::MakeUserModifiedPrintSettings("printer1"), |
| *document_print_settings()); |
| } else { |
| #if !BUILDFLAG(IS_WIN) |
| EXPECT_TRUE(did_get_settings_with_ui()); |
| EXPECT_EQ(did_print_document_count(), 1); |
| #endif |
| EXPECT_TRUE(!in_process_last_error_result_code().has_value()); |
| EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"), |
| *document_print_settings()); |
| } |
| EXPECT_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| // This test is Windows-only because of Print Preview behavior in |
| // `onPrintWithSystemDialog_()`. For Windows this call ends up going through |
| // `PrintViewManagerBase::PrintForPrintPreview()`, and thus invokes |
| // `UpdatePrintSettings()` before displaying the system dialog. Other |
| // platforms end up going through `PrintViewManager::PrintForSystemDialogNow()` |
| // and thus do not update print settings before the system dialog is displayed. |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| SystemPrintFromPrintPreviewUpdatePrintSettingsFails) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForFailInUpdatePrinterSettings(); |
| |
| 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); |
| |
| // Once the transition to system print is initiated, the expected events |
| // are: |
| // 1. Update the print settings, which fails. No further printing calls |
| // are made. No print job is created because of such an early failure. |
| SetNumExpectedMessages(/*num=*/1); |
| |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kFailed); |
| // TODO(crbug.com/40286396): Update once an error dialog is shown for this |
| // failure to print. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| } |
| |
| // This test is Windows-only because of Print Preview behavior in |
| // `onPrintWithSystemDialog_()`. For Windows this call ends up going through |
| // `PrintViewManagerBase::PrintForPrintPreview()`, and thus invokes |
| // `UpdatePrintSettings()` before displaying the system dialog. Other |
| // platforms end up going through `PrintViewManager::PrintForSystemDialogNow()` |
| // and thus do not update print settings before the system dialog is displayed. |
| IN_PROC_BROWSER_TEST_P( |
| SystemAccessProcessSandboxedServicePrintBrowserTest, |
| PrintPreviewAfterSystemPrintFromPrintPreviewUpdatePrintSettingsFails) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForFailInUpdatePrinterSettings(); |
| |
| 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); |
| |
| // First invoke system print from Print Preview. Must wait until the |
| // PrintPreviewUI is completely done before proceeding to the second part |
| // of this test to ensure that the client is unregistered from the |
| // `PrintBackendServiceManager`. |
| SetCheckForPrintPreviewDone(/*check=*/true); |
| |
| // Once the transition to system print is initiated, the expected events |
| // are: |
| // 1. Update the print settings, which fails. No further printing calls |
| // are made. No print job is created because of such an early failure. |
| // 2. Print Preview UI is done. |
| SetNumExpectedMessages(/*num=*/2); |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kFailed); |
| // TODO(crbug.com/40286396): Update once an error dialog is shown for this |
| // failure to print. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| |
| // Reset before initiating another Print Preview. |
| PrepareRunloop(); |
| ResetNumReceivedMessages(); |
| |
| // No longer expect the `PrintPreviewUI` to issue a done callback as part of |
| // the test expectations, since the Print Preview will stay open displaying |
| // an error message. There will still be a preview done callback during |
| // test shutdown though, so disable doing an expectation check for that. |
| SetCheckForPrintPreviewDone(/*check=*/false); |
| |
| // The expected events for this are: |
| // 1. Update the print settings, which fails. No further printing calls |
| // are made. No print job is created because of such an early failure. |
| SetNumExpectedMessages(/*num=*/1); |
| PrintAfterPreviewIsReadyAndLoaded(); |
| |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kFailed); |
| // TODO(crbug.com/40286396): Update once an error dialog is shown for this |
| // failure to print. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| } |
| |
| // 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/40561724): Expand this to also cover in-browser, once an |
| // appropriate signal is available to use for tracking expected events. |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| 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); |
| |
| // First invoke system print from Print Preview. Must wait until the |
| // PrintPreviewUI is completely done before proceeding to the second part |
| // of this test to ensure that the client is unregistered from the |
| // `PrintBackendServiceManager`. |
| SetCheckForPrintPreviewDone(/*check=*/true); |
| |
| // 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. |
| // 2. Print Preview UI is done. |
| SetNumExpectedMessages(/*num=*/2); |
| |
| 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. |
| PrepareRunloop(); |
| 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); |
| } |
| |
| // TODO(crbug.com/40942272): Enable test for Linux and macOS once renderer |
| // RunLoop behavior can be made to work with test expectations. |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| SystemPrintAfterSystemPrintFromPrintPreview) { |
| 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); |
| |
| // First invoke system print from Print Preview. Wait until the |
| // PrintPreviewUI is done before proceeding to the second part of the |
| // test. |
| SetCheckForPrintPreviewDone(/*check=*/true); |
| |
| if (UseService()) { |
| // Once the transition to system print is initiated, the expected events |
| // are: |
| // 1. Update the print settings. This internally invokes the system |
| // print dialog which cancels. |
| // 2. Print Preview is done. |
| // No print job is created because of such an early cancel. |
| SetNumExpectedMessages(/*num=*/2); |
| } else { |
| // Once the transition to system print is initiated, the expected events |
| // are: |
| // 1. Update the print settings. |
| // 2. Print Preview is done. |
| // No print job is created because of such an early cancel. |
| SetNumExpectedMessages(/*num=*/2); |
| } |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| |
| if (UseService()) { |
| // Windows invokes system print dialog from UpdatePrintSettings(). |
| EXPECT_EQ(update_print_settings_result(), mojom::ResultCode::kCanceled); |
| } else { |
| // User settings are invoked from within UpdatePrintSettings(). |
| EXPECT_FALSE(did_use_default_settings()); |
| EXPECT_FALSE(did_get_settings_with_ui()); |
| |
| // `PrintBackendService` should never be used when printing in-browser. |
| EXPECT_FALSE(print_backend_service_use_detected()); |
| } |
| |
| // Reset before initiating system print. |
| PrepareRunloop(); |
| ResetNumReceivedMessages(); |
| |
| // The expected events for this are: |
| // 1. Get the default settings. |
| // 2. Ask the user for settings, which cancels out. No further printing |
| // calls are made. |
| SetNumExpectedMessages(/*num=*/2); |
| |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| if (UseService()) { |
| EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess); |
| EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kCanceled); |
| } else { |
| EXPECT_TRUE(did_use_default_settings()); |
| EXPECT_TRUE(did_get_settings_with_ui()); |
| } |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 0); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| PrintPreviewPrintAfterSystemPrintRendererCrash) { |
| 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); |
| |
| content::RenderFrameHost* frame = web_contents->GetPrimaryMainFrame(); |
| content::RenderProcessHost* frame_rph = frame->GetProcess(); |
| |
| KillPrintRenderFrame frame_content(frame_rph, |
| GetPrintRenderFrame(frame).get()); |
| frame_content.OverrideBinderForTesting(frame); |
| |
| // With the renderer being prepared to fake a crash, the test needs to watch |
| // for it being deleted. |
| SetCheckForRenderFrameDeleted(/*check=*/true); |
| content::ScopedAllowRendererCrashes allow_renderer_crash; |
| |
| // First invoke system print directly. |
| |
| // The expected events for this are: |
| // 1. Printing is attempted, but quickly get notified that the render frame |
| // has been deleted because the renderer "crashed". |
| SetNumExpectedMessages(/*num=*/1); |
| |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| // After renderer crash, reload the page again in the same tab. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Now try to initiate print from a Print Preview. |
| PrepareRunloop(); |
| ResetNumReceivedMessages(); |
| |
| // No longer interested in when the renderer is deleted. |
| SetCheckForRenderFrameDeleted(/*check=*/false); |
| |
| // 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/40100562) 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_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| StartBasicPrint) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| |
| 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. |
| // 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); |
| |
| 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); |
| #else |
| EXPECT_TRUE(did_use_default_settings()); |
| EXPECT_TRUE(did_get_settings_with_ui()); |
| #endif |
| EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"), |
| *document_print_settings()); |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); |
| #if BUILDFLAG(IS_WIN) |
| // TODO(crbug.com/40100562) 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_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(did_print_document_count(), 1); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| 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(); |
| |
| if (UseService()) { |
| #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::kCanceled); |
| #else |
| EXPECT_TRUE(did_use_default_settings()); |
| EXPECT_TRUE(did_get_settings_with_ui()); |
| #endif |
| } else { |
| EXPECT_TRUE(did_use_default_settings()); |
| EXPECT_TRUE(did_get_settings_with_ui()); |
| |
| // `PrintBackendService` should never be used when printing in-browser. |
| EXPECT_FALSE(print_backend_service_use_detected()); |
| } |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(did_print_document_count(), 0); |
| EXPECT_EQ(print_job_destruction_count(), 0); |
| } |
| |
| 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 (UseService()) { |
| // 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 { |
| // 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 and gets canceled. 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); |
| } |
| |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed); |
| EXPECT_EQ(cancel_count(), 1); |
| } else { |
| EXPECT_THAT(in_process_last_error_result_code(), |
| testing::Optional(mojom::ResultCode::kCanceled)); |
| } |
| EXPECT_EQ(error_dialog_shown_count(), 1u); |
| EXPECT_EQ(did_print_document_count(), 1); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, |
| StartBasicPrintPdfConversionFails) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| PrimeForPdfConversionErrorOnPageIndex(/*page_index=*/1); |
| |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| GURL url(embedded_test_server()->GetURL("/printing/3_pages.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 (UseService()) { |
| // The expected events for this are: |
| // 1. Gets default settings. |
| // 2. Asks user for settings. |
| // 3. A print job is started. |
| // 4. Notified of DidPrintDocument(), that composition of the print |
| // document has completed. |
| // 5. The PDF conversion fails, resulting in canceling the print job. |
| // 6. The print job is destroyed. |
| // No error dialog is shown. |
| SetNumExpectedMessages(/*num=*/6); |
| } else { |
| // 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 is canceled due to failure during PDF |
| // conversion. |
| // 4. 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. |
| // No error dialog is shown. |
| SetNumExpectedMessages(/*num=*/4); |
| } |
| |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); |
| } else { |
| EXPECT_THAT(in_process_last_error_result_code(), |
| testing::Optional(mojom::ResultCode::kCanceled)); |
| } |
| // TODO(crbug.com/40288222): Update expectation once an error is shown for |
| // this failure. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| #if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS) |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| StartBasicPrintConcurrentAllowed) { |
| 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 = |
| SetUpAndReturnPrintViewManager(web_contents); |
| |
| // Pretend that a window has started a system print. |
| std::optional<PrintBackendServiceManager::ClientId> client_id = |
| PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient(); |
| ASSERT_TRUE(client_id.has_value()); |
| |
| // The expected events for this are: |
| // 1. Gets default settings. |
| // 2. Asks user for settings. |
| // 3. Start the print job. |
| // 4. Rendering for 1 page of document of content. |
| // 5. Completes with document done. |
| // 6. Wait until all processing for DidPrintDocument is known to have |
| // completed, to ensure printing finished cleanly before completing the |
| // test. |
| // 7. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/7); |
| |
| // Now initiate a system print that would exist concurrently with that. |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| EXPECT_THAT(print_view_manager->print_now_result(), testing::Optional(true)); |
| |
| // Cleanup before test shutdown. |
| PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id); |
| } |
| |
| #if BUILDFLAG(ENABLE_PRINT_PREVIEW) |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| SystemPrintFromPrintPreviewConcurrentAllowed) { |
| 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/40561724) Improve on this test by using a persistent fake |
| // system print dialog. |
| std::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. |
| // The expected events for this are: |
| // 1. Gets default settings. |
| // 2. Asks user for settings. |
| // 3. Start the print job. |
| // 4. Rendering for 1 page of document of content. |
| // 5. Completes with document done. |
| // 6. Wait until all processing for DidPrintDocument is known to have |
| // completed, to ensure printing finished cleanly before completing the |
| // test. |
| // 7. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/7); |
| |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| |
| // Concurrent system print is allowed. |
| EXPECT_THAT(system_print_registration_succeeded(), testing::Optional(true)); |
| |
| // Cleanup before test shutdown. |
| PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id); |
| } |
| #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW) |
| |
| #else // BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS) |
| |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| StartBasicPrintConcurrentNotAllowed) { |
| 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. |
| std::optional<PrintBackendServiceManager::ClientId> client_id = |
| PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient(); |
| ASSERT_TRUE(client_id.has_value()); |
| |
| // Now initiate a system print that would exist concurrently with that. |
| StartBasicPrint(web_contents); |
| |
| // Concurrent system print is not allowed. |
| EXPECT_THAT(print_view_manager->print_now_result(), testing::Optional(false)); |
| // The denied concurrent print is silent without an error. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| |
| // Cleanup before test shutdown. |
| PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id); |
| } |
| |
| #if BUILDFLAG(ENABLE_PRINT_PREVIEW) |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest, |
| SystemPrintFromPrintPreviewConcurrentNotAllowed) { |
| 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/40561724) Improve on this test by using a persistent fake |
| // system print dialog. |
| std::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. |
| // Inability to support this should be detected immediately without needing |
| // to wait for callback. |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/false); |
| |
| // Concurrent system print is not allowed. |
| EXPECT_THAT(system_print_registration_succeeded(), testing::Optional(false)); |
| // The denied concurrent print is silent without an error. |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| |
| // Cleanup before test shutdown. |
| PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id); |
| } |
| #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW) |
| |
| #endif // BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS) |
| |
| 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); |
| |
| // 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); |
| |
| StartBasicPrint(web_contents); |
| |
| WaitUntilCallbackReceived(); |
| |
| #if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) |
| EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kFailed); |
| #else |
| EXPECT_TRUE(did_use_default_settings()); |
| #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) |
| |
| #if BUILDFLAG(IS_MAC) |
| IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, OpenPdfInPreview) { |
| AddPrinter("printer1"); |
| SetPrinterNameForSubsequentContexts("printer1"); |
| constexpr int kJobId = 1; |
| SetNewDocumentJobId(kJobId); |
| |
| 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 (UseService()) { |
| // The expected events for this are: |
| // 1. Update printer 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 { |
| // The expected events for this are: |
| // 1. Update printer settings. |
| // 2. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/2); |
| } |
| OpenPdfInPreviewOnceReadyAndLoaded(); |
| |
| if (UseService()) { |
| EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess); |
| EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess); |
| EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess); |
| } else { |
| EXPECT_FALSE(in_process_last_error_result_code().has_value()); |
| } |
| EXPECT_THAT(document_done_job_id(), testing::Optional(kJobId)); |
| EXPECT_TRUE(destination_is_preview()); |
| EXPECT_EQ(error_dialog_shown_count(), 0u); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| } |
| #endif // BUILDFLAG(IS_MAC) |
| |
| #endif // BUILDFLAG(ENABLE_OOP_PRINTING) |
| |
| #if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager { |
| public: |
| class Observer : public PrintViewManagerBase::TestObserver { |
| public: |
| void OnPrintNow(const content::RenderFrameHost* rfh) override { |
| print_now_called_ = true; |
| } |
| |
| void OnScriptedPrint() override { scripted_print_called_ = true; } |
| |
| void OnPrintPreviewDone() override { |
| if (on_print_preview_done_) { |
| std::move(on_print_preview_done_).Run(); |
| } |
| } |
| |
| bool print_now_called() const { return print_now_called_; } |
| |
| bool scripted_print_called() const { return scripted_print_called_; } |
| |
| void set_on_print_preview_done_closure(base::OnceClosure closure) { |
| on_print_preview_done_ = std::move(closure); |
| } |
| |
| private: |
| bool print_now_called_ = false; |
| bool scripted_print_called_ = false; |
| base::OnceClosure on_print_preview_done_; |
| }; |
| |
| TestPrintViewManagerForContentAnalysis( |
| content::WebContents* web_contents, |
| const char* policy_value, |
| enterprise_connectors::ContentAnalysisRequest::Reason expected_reason, |
| OnDidCreatePrintJobCallback create_print_job_callback, |
| OnDidCompositeForContentAnalysis composite_for_content_analysis_callback) |
| : TestPrintViewManager(web_contents, |
| std::move(create_print_job_callback)), |
| expected_reason_(expected_reason), |
| policy_value_(policy_value), |
| did_composite_for_content_analysis_callback_( |
| std::move(composite_for_content_analysis_callback)) { |
| AddTestObserver(observer_); |
| PrintViewManager::SetReceiverImplForTesting(this); |
| } |
| |
| ~TestPrintViewManagerForContentAnalysis() override { |
| PrintViewManager::SetReceiverImplForTesting(nullptr); |
| } |
| |
| void WaitOnPreview() { preview_run_loop_.Run(); } |
| |
| bool print_now_called() const { return observer_.print_now_called(); } |
| |
| bool scripted_print_called() const { |
| return observer_.scripted_print_called(); |
| } |
| |
| const std::optional<bool>& preview_allowed() const { |
| return preview_allowed_; |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| void set_allowed_by_dlp(bool allowed) { allowed_by_dlp_ = allowed; } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| void set_on_print_preview_done_closure(base::OnceClosure closure) { |
| observer_.set_on_print_preview_done_closure(std::move(closure)); |
| } |
| |
| protected: |
| void ContentAnalysisBeforePrintingDocument( |
| enterprise_connectors::ContentAnalysisDelegate::Data scanning_data, |
| scoped_refptr<base::RefCountedMemory> print_data, |
| const gfx::Size& page_size, |
| const gfx::Rect& content_area, |
| const gfx::Point& offsets) override { |
| // The settings passed to this function should match the content of the |
| // print Connector policy. |
| EXPECT_EQ(scanning_data.settings.tags.size(), 1u); |
| EXPECT_TRUE(base::Contains(scanning_data.settings.tags, "dlp")); |
| if (scanning_data.settings.cloud_or_local_settings.is_cloud_analysis()) { |
| EXPECT_EQ(scanning_data.settings.cloud_or_local_settings.dm_token(), |
| kFakeDmToken); |
| } else { |
| EXPECT_EQ(scanning_data.settings.cloud_or_local_settings.local_path(), |
| "path_user"); |
| EXPECT_TRUE( |
| scanning_data.settings.cloud_or_local_settings.user_specific()); |
| } |
| EXPECT_TRUE( |
| ExpectedBlockUntilVerdict(scanning_data.settings.block_until_verdict)); |
| EXPECT_TRUE(scanning_data.settings.block_large_files); |
| EXPECT_EQ(scanning_data.url, |
| web_contents()->GetOutermostWebContents()->GetLastCommittedURL()); |
| EXPECT_EQ(scanning_data.reason, expected_reason_); |
| |
| // The data of the document should be a valid PDF as this code should be |
| // called as the print job is about to start printing. |
| EXPECT_TRUE(LooksLikePdf(*print_data)); |
| |
| TestPrintViewManager::ContentAnalysisBeforePrintingDocument( |
| std::move(scanning_data), print_data, page_size, content_area, offsets); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| void OnDlpPrintingRestrictionsChecked( |
| content::GlobalRenderFrameHostId rfh_id, |
| base::OnceCallback<void(bool should_proceed)> callback, |
| bool should_proceed) override { |
| PrintViewManager::OnDlpPrintingRestrictionsChecked( |
| rfh_id, std::move(callback), allowed_by_dlp_); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| void CompleteScriptedPrint(content::RenderFrameHost* rfh, |
| mojom::ScriptedPrintParamsPtr params, |
| ScriptedPrintCallback callback) override { |
| TestPrintViewManager::CompleteScriptedPrint(rfh, std::move(params), |
| std::move(callback)); |
| |
| for (auto& observer : GetTestObservers()) { |
| observer.OnScriptedPrint(); |
| } |
| } |
| |
| private: |
| void PrintPreviewRejectedForTesting() override { |
| preview_allowed_ = false; |
| preview_run_loop_.Quit(); |
| } |
| |
| void PrintPreviewAllowedForTesting() override { |
| preview_allowed_ = true; |
| preview_run_loop_.Quit(); |
| } |
| |
| bool ExpectedBlockUntilVerdict( |
| enterprise_connectors::BlockUntilVerdict block_until_verdict) { |
| if (policy_value_ == kCloudAnalysisNonBlockingPolicy) { |
| return block_until_verdict == |
| enterprise_connectors::BlockUntilVerdict::kNoBlock; |
| } |
| |
| return block_until_verdict == |
| enterprise_connectors::BlockUntilVerdict::kBlock; |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| bool allowed_by_dlp_ = true; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Indicates whether the preview was allowed after checking against content |
| // analysis and DLP (if on CrOS). This is `std::nullopt` until then. |
| std::optional<bool> preview_allowed_; |
| |
| // Used to validate the corresponding `ContentAnalysisDelegate::Data` passed |
| // in various content analysis-related functions. |
| enterprise_connectors::ContentAnalysisRequest::Reason expected_reason_; |
| |
| // Used to validate the corresponding `ContentAnalysisDelegate::Data` passed |
| // in various content analysis-related functions. Corresponds to the value |
| // return by `PolicyValue()` for the current test. |
| const char* policy_value_ = nullptr; |
| |
| base::RunLoop preview_run_loop_; |
| OnDidCompositeForContentAnalysis did_composite_for_content_analysis_callback_; |
| Observer observer_; |
| }; |
| |
| using ContentAnalysisConfigurationVariation = testing::tuple< |
| const char* /*policy_value*/, |
| bool /*content_analysis_allows_print*/, |
| PrintBackendAndPlatformPrintApiVariation /*backend_and_print_api*/>; |
| |
| class ContentAnalysisPrintBrowserTestBase |
| : public SystemAccessProcessPrintBrowserTestBase { |
| public: |
| ContentAnalysisPrintBrowserTestBase() { |
| policy::SetDMTokenForTesting( |
| policy::DMToken::CreateValidToken(kFakeDmToken)); |
| enterprise_connectors::ContentAnalysisDelegate::SetFactoryForTesting( |
| base::BindRepeating( |
| &enterprise_connectors::test::FakeContentAnalysisDelegate::Create, |
| base::DoNothing(), |
| base::BindRepeating( |
| &ContentAnalysisPrintBrowserTestBase::ScanningResponse, |
| base::Unretained(this)), |
| kFakeDmToken)); |
| |
| // These overrides make the overall tests faster as the content analysis |
| // dialog won't stay in each state for mandatory minimum times. |
| enterprise_connectors::ContentAnalysisDialog:: |
| SetMinimumPendingDialogTimeForTesting(base::Milliseconds(0)); |
| enterprise_connectors::ContentAnalysisDialog::SetShowDialogDelayForTesting( |
| base::Milliseconds(0)); |
| enterprise_connectors::ContentAnalysisDialog:: |
| SetSuccessDialogTimeoutForTesting(base::Milliseconds(0)); |
| } |
| |
| enterprise_connectors::ContentAnalysisResponse ScanningResponse( |
| const std::string& contents, |
| const base::FilePath& path) { |
| ++scanning_responses_; |
| enterprise_connectors::ContentAnalysisResponse response; |
| |
| auto* result = response.add_results(); |
| result->set_tag("dlp"); |
| result->set_status( |
| enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); |
| |
| if (!ContentAnalysisAllowsPrint()) { |
| auto* rule = result->add_triggered_rules(); |
| rule->set_rule_name("blocking_rule_name"); |
| rule->set_action(enterprise_connectors::TriggeredRule::BLOCK); |
| } |
| |
| return response; |
| } |
| |
| void SetUp() override { |
| test_printing_context_factory()->SetPrinterNameForSubsequentContexts( |
| "printer_name"); |
| SystemAccessProcessPrintBrowserTestBase::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| SystemAccessProcessPrintBrowserTestBase::SetUpOnMainThread(); |
| enterprise_connectors::test::SetAnalysisConnector( |
| browser()->profile()->GetPrefs(), |
| enterprise_connectors::AnalysisConnector::PRINT, PolicyValue()); |
| } |
| |
| void TearDownOnMainThread() override { |
| enterprise_connectors::test::ClearAnalysisConnector( |
| browser()->profile()->GetPrefs(), |
| enterprise_connectors::AnalysisConnector::PRINT); |
| SystemAccessProcessPrintBrowserTestBase::TearDownOnMainThread(); |
| } |
| |
| TestPrintViewManagerForContentAnalysis* |
| SetUpAndReturnPrintViewManagerForContentAnalysis( |
| content::WebContents* web_contents, |
| enterprise_connectors::ContentAnalysisRequest::Reason expected_reason) { |
| // Safe to use `base::Unretained(this)` since this testing class |
| // necessarily must outlive all interactions from the tests which will |
| // run through `PrintViewManagerBase`, which is what causes new jobs to |
| // be created and use this callback. |
| auto manager = std::make_unique<TestPrintViewManagerForContentAnalysis>( |
| web_contents, PolicyValue(), expected_reason, |
| base::BindRepeating( |
| &SystemAccessProcessPrintBrowserTestBase::OnCreatedPrintJob, |
| base::Unretained(this)), |
| base::BindRepeating(&SystemAccessProcessPrintBrowserTestBase:: |
| OnCompositedForContentAnalysis, |
| base::Unretained(this))); |
| manager->AddTestObserver(*this); |
| TestPrintViewManagerForContentAnalysis* manager_ptr = manager.get(); |
| web_contents->SetUserData(PrintViewManager::UserDataKey(), |
| std::move(manager)); |
| return manager_ptr; |
| } |
| |
| int scanning_responses_count() { return scanning_responses_; } |
| |
| bool SandboxService() override { return true; } |
| |
| int GetExpectedNewDocumentCalledCount() { |
| return PrintAllowedOrNonBlockingPolicy() ? (UseService() ? 2 : 1) : 0; |
| } |
| |
| // The value OnPrintEnterpriseConnector should be set to. |
| virtual const char* PolicyValue() const = 0; |
| |
| // Whether content analysis should let printing proceed. |
| virtual bool ContentAnalysisAllowsPrint() const = 0; |
| |
| // Helper to check if printing is allowed altogether or not. It's possible for |
| // the policy to be set to be non-blocking and still obtain a "block" verdict |
| // from a content analysis server, and in such a case the printing will be |
| // allowed to proceed. |
| bool PrintAllowedOrNonBlockingPolicy() { |
| return ContentAnalysisAllowsPrint() || |
| PolicyValue() == kCloudAnalysisNonBlockingPolicy; |
| } |
| |
| static std::string GetTestSuffix( |
| const testing::TestParamInfo<ContentAnalysisConfigurationVariation>& |
| info) { |
| return base::JoinString( |
| {GetPolicyTypeString(std::get<0>(info.param)), |
| GetAllowsPrintString(std::get<1>(info.param)), |
| GetPrintBackendAndPlatformPrintApiString(std::get<2>(info.param))}, |
| /*separator=*/"_"); |
| } |
| |
| protected: |
| static const char* GetPolicyTypeString(const char* policy_value) { |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| if (policy_value == kLocalAnalysisPolicy) { |
| return "LocalPolicy"; |
| } |
| #endif |
| if (policy_value == kCloudAnalysisBlockingPolicy) { |
| return "BlockingCloudPolicy"; |
| } |
| CHECK_EQ(policy_value, kCloudAnalysisNonBlockingPolicy); |
| return "NonBlockingCloudPolicy"; |
| } |
| |
| static const char* GetAllowsPrintString(bool allows_print) { |
| return allows_print ? "AllowsPrint" : "DisallowsPrint"; |
| } |
| |
| private: |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| enterprise_connectors::FakeContentAnalysisSdkManager sdk_manager_; |
| #endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| |
| // Counts the number of times `ScanningResponse` is called, why is equivalent |
| // to the number of times a printed page's bytes would reach a scanner. |
| int scanning_responses_ = 0; |
| }; |
| |
| class ContentAnalysisAfterPrintPreviewBrowserTest |
| : public ContentAnalysisPrintBrowserTestBase, |
| public testing::WithParamInterface< |
| ContentAnalysisConfigurationVariation> { |
| public: |
| const char* PolicyValue() const override { return std::get<0>(GetParam()); } |
| bool ContentAnalysisAllowsPrint() const override { |
| return std::get<1>(GetParam()); |
| } |
| bool UseService() override { |
| return backend_and_print_api().print_backend != |
| PrintBackendFeatureVariation::kInBrowserProcess; |
| } |
| #if BUILDFLAG(IS_WIN) |
| bool UseXps() override { |
| return backend_and_print_api().platform_api == |
| PlatformPrintApiVariation::kXps; |
| } |
| #endif |
| |
| // PrintJob::Observer: |
| void OnCanceling() override { CheckForQuit(); } |
| |
| private: |
| PrintBackendAndPlatformPrintApiVariation backend_and_print_api() const { |
| return std::get<2>(GetParam()); |
| } |
| }; |
| |
| class ContentAnalysisScriptedPreviewlessPrintBrowserTestBase |
| : public ContentAnalysisPrintBrowserTestBase, |
| public testing::WithParamInterface< |
| ContentAnalysisConfigurationVariation> { |
| public: |
| const char* PolicyValue() const override { return std::get<0>(GetParam()); } |
| bool ContentAnalysisAllowsPrint() const override { |
| return std::get<1>(GetParam()); |
| } |
| bool UseService() override { |
| return backend_and_print_api().print_backend != |
| PrintBackendFeatureVariation::kInBrowserProcess; |
| } |
| #if BUILDFLAG(IS_WIN) |
| bool UseXps() override { |
| return backend_and_print_api().platform_api == |
| PlatformPrintApiVariation::kXps; |
| } |
| #endif |
| |
| void SetUpCommandLine(base::CommandLine* cmd_line) override { |
| cmd_line->AppendSwitch(switches::kDisablePrintPreview); |
| ContentAnalysisPrintBrowserTestBase::SetUpCommandLine(cmd_line); |
| } |
| |
| private: |
| PrintBackendAndPlatformPrintApiVariation backend_and_print_api() const { |
| return std::get<2>(GetParam()); |
| } |
| }; |
| |
| class ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest |
| : public ContentAnalysisScriptedPreviewlessPrintBrowserTestBase { |
| public: |
| void RunScriptedPrintTest(const std::string& script) { |
| AddPrinter("printer_name"); |
| |
| if (UseService() && !PrintAllowedOrNonBlockingPolicy()) { |
| // This results in a stranded context left in the Print Backend service. |
| // It will persist harmlessly until the service terminates after a short |
| // period of no printing activity. |
| SkipPersistentContextsCheckOnShutdown(); |
| } |
| |
| 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); |
| auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis( |
| web_contents, |
| enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT); |
| |
| if (PrintAllowedOrNonBlockingPolicy()) { |
| if (UseService()) { |
| // The expected events are: |
| // 1. Get the default settings. |
| // 2. Ask the user for settings. |
| // 3. A print job is started. |
| // 4. The one page of the document is rendered. |
| // 5. Receive document done notification. |
| // 6. Wait until all processing for DidPrintDocument is known to have |
| // completed, to ensure printing finished cleanly before |
| // completing the test. |
| // 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. Use default settings. |
| // 2. Ask the user for settings. |
| // 3. The print compositor will complete generating the document. |
| // 4. The print job is destroyed. |
| SetNumExpectedMessages(/*num=*/4); |
| } |
| } else { |
| // The expected events for this 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 actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/4); |
| } |
| |
| content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), script); |
| |
| WaitUntilCallbackReceived(); |
| |
| ASSERT_TRUE(print_view_manager->scripted_print_called()); |
| EXPECT_EQ(composited_for_content_analysis_count(), 0); |
| EXPECT_EQ(scanning_responses_count(), 1); |
| |
| // Validate that `NewDocument()` is only called for actual printing, not as |
| // part of content analysis, since that can needlessly prompt the user. |
| // When printing OOP, an extra call for a new document will occur since it |
| // gets called in both the browser process and in the Print Backend service. |
| EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount()); |
| } |
| }; |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| |
| IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest, |
| PrintWithPreviewBeforeLoaded) { |
| AddPrinter("printer_name"); |
| |
| 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); |
| auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis( |
| web_contents, |
| enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT); |
| |
| if (PrintAllowedOrNonBlockingPolicy() && UseService()) { |
| // 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); |
| } else { |
| print_view_manager->set_on_print_preview_done_closure(base::BindOnce( |
| &ContentAnalysisAfterPrintPreviewBrowserTest::CheckForQuit, |
| base::Unretained(this))); |
| if (PrintAllowedOrNonBlockingPolicy()) { |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. Print preview completes. |
| // 3. Wait for the one print job to be destroyed, to ensure printing |
| // finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/3); |
| } else { |
| // The expected events for this are: |
| // 1. Print preview completes. No print job is created. |
| SetNumExpectedMessages(/*num=*/1); |
| } |
| } |
| |
| PrintAfterPreviewIsReadyAndMaybeLoaded(PrintParams(), |
| /*wait_for_loaded=*/false); |
| |
| EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true)); |
| EXPECT_EQ(scanning_responses_count(), 1); |
| |
| // Validate that `NewDocument()` is only called for actual printing, not as |
| // part of content analysis, since that can needlessly prompt the user. |
| // When printing OOP, an extra call for a new document will occur since it |
| // gets called in both the browser process and in the Print Backend service. |
| EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest, |
| SystemPrintFromPrintPreview) { |
| AddPrinter("printer_name"); |
| |
| if (UseService() && !PrintAllowedOrNonBlockingPolicy()) { |
| // This results in a stranded context left in the Print Backend service. |
| // It will persist harmlessly until the service terminates after a short |
| // period of no printing activity. |
| SkipPersistentContextsCheckOnShutdown(); |
| } |
| |
| 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); |
| auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis( |
| web_contents, |
| enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT); |
| |
| if (PrintAllowedOrNonBlockingPolicy()) { |
| if (UseService()) { |
| #if BUILDFLAG(IS_WIN) |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. A print job is started, for actual printing. |
| // 3. Rendering for 1 page of document of content. |
| // 4. Completes with document done. |
| // 5. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/5); |
| #else |
| // The expected events for this are: |
| // 1. Get the default settings. |
| // 2. Ask the user for settings. |
| // 3. A print job is started for actual printing. |
| // 4. The print compositor will complete generating the document. |
| // 5. Rendering for 1 page of document of content. |
| // 6. Completes with document done. |
| // 7. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/7); |
| #endif // BUILDFLAG(IS_WIN) |
| } else { |
| #if BUILDFLAG(IS_WIN) |
| // The expected event for this is: |
| // 1. Update print settings. |
| // 2. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/2); |
| #else |
| // The expected events for this are: |
| // 1. Get the default settings. |
| // 2. Ask the user for settings. |
| // 3. The print compositor will complete generating the document. |
| // 4. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/4); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| } else { |
| #if BUILDFLAG(IS_WIN) |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. The print job is cancelled. |
| // 3. The print job is destroyed. |
| SetNumExpectedMessages(/*num=*/3); |
| #else |
| // The expected events for this are: |
| // 1. Use default settings. |
| // 2. Ask the user for settings. |
| // 3. The print compositor will complete generating the document. |
| // 4. The print job is cancelled. |
| // 5. The print job is destroyed. |
| SetNumExpectedMessages(/*num=*/5); |
| #endif // BUILDFLAG(IS_WIN) |
| SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true); |
| } |
| |
| EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true)); |
| EXPECT_EQ(print_job_destruction_count(), 1); |
| EXPECT_EQ(scanning_responses_count(), 1); |
| |
| // Validate that `NewDocument()` is only called for actual printing, not as |
| // part of content analysis, since that can needlessly prompt the user. |
| // When printing OOP, an extra call for a new document will occur since it |
| // gets called in both the browser process and in the Print Backend service. |
| EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount()); |
| } |
| |
| #if BUILDFLAG(IS_MAC) |
| IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest, |
| OpenPdfInPreviewFromPrintPreview) { |
| AddPrinter("printer_name"); |
| |
| if (UseService() && !PrintAllowedOrNonBlockingPolicy()) { |
| // This results in a stranded context left in the Print Backend service. |
| // It will persist harmlessly until the service terminates after a short |
| // period of no printing activity. |
| SkipPersistentContextsCheckOnShutdown(); |
| } |
| |
| 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); |
| auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis( |
| web_contents, |
| enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT); |
| |
| if (PrintAllowedOrNonBlockingPolicy()) { |
| if (UseService()) { |
| // The expected events for this are: |
| // 1. Ask the user for settings. |
| // 2. A print job is started for actual printing. |
| // 3. The print compositor will complete generating the document. |
| // 4. Completes with document done. |
| // 5. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/5); |
| } else { |
| // The expected events for this are: |
| // 1. Update print settings. |
| // 2. Wait for the actual printing job to be destroyed, to ensure |
| // printing finished cleanly before completing the test. |
| SetNumExpectedMessages(/*num=*/2); |
| } |
| } else { |
| print_view_manager->set_on_print_preview_done_closure(base::BindOnce( |
| &ContentAnalysisAfterPrintPreviewBrowserTest::CheckForQuit, |
| base::Unretained(this))); |
| // The expected events for this are: |
| // 1. Print Preview is done. |
| SetNumExpectedMessages(/*num=*/1); |
| } |
| OpenPdfInPreviewOnceReadyAndLoaded(); |
| |
| EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true)); |
| |
| EXPECT_EQ(print_job_destruction_count(), |
| PrintAllowedOrNonBlockingPolicy() ? 1 : 0); |
| EXPECT_EQ(scanning_responses_count(), 1); |
| |
| // Validate that `NewDocument()` is only called for actual printing, not as |
| // part of content analysis, since that can needlessly prompt the user. |
| // When printing OOP, an extra call for a new document will occur since it |
| // gets called in both the browser process and in the Print Backend service. |
| EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount()); |
| } |
| #endif // BUILDFLAG(IS_MAC) |
| |
| IN_PROC_BROWSER_TEST_P( |
| ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest, |
| PrintNow) { |
| AddPrinter("printer_name"); |
| |
| if (UseService() && !PrintAllowedOrNonBlockingPolicy()) { |
| // This results in a stranded context left in the Print Backend service. |
| // It will persist harmlessly until the service terminates after a short |
| // period of no printing activity. |
| SkipPersistentContextsCheckOnShutdown(); |
| } |
| |
| 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); |
| auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis( |
| web_contents, |
| enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT); |
| |
| if (PrintAllowedOrNonBlockingPolicy()) { |
| if (UseService()) { |
| // The expected events are: |
| // 1. Get the default settings. |
| // 2. Ask the user for settings. |
| // 3. A print job is started. |
| // 4. The one page of the document is rendered. |
| // 5. Receive document done notification. |
| // 6. Wait until all processing for DidPrintDocument is known to have |
| // completed, to ensure printing finished cleanly before completing |
| // the test. |
| // 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. Use default settings. |
| // 2. Ask the user for settings. |
| // 3. The print compositor will complete generating the document. |
| // 4. The print job is destroyed. |
| SetNumExpectedMessages(/*num=*/4); |
| } |
| } else { |
| // The expected events for this are: |
| // 1. Use default settings. |
| // 2. Ask the user for settings. |
| // 3. The print compositor will complete generating the document. |
| // 4. The print job is destroyed. |
| SetNumExpectedMessages(/*num=*/4); |
| } |
| |
| StartPrint(browser()->tab_strip_model()->GetActiveWebContents(), |
| /*print_preview_disabled=*/true, |
| /*has_selection=*/false); |
| |
| WaitUntilCallbackReceived(); |
| |
| ASSERT_TRUE(print_view_manager->scripted_print_called()); |
| EXPECT_EQ(scanning_responses_count(), 1); |
| |
| // Validate that `NewDocument()` is only called for actual printing, not as |
| // part of content analysis, since that can needlessly prompt the user. |
| // When printing OOP, an extra call for a new document will occur since it |
| // gets called in both the browser process and in the Print Backend service. |
| EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P( |
| ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest, |
| DocumentExecPrint) { |
| RunScriptedPrintTest("document.execCommand('print');"); |
| } |
| |
| IN_PROC_BROWSER_TEST_P( |
| ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest, |
| WindowPrint) { |
| RunScriptedPrintTest("window.print()"); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ContentAnalysisAfterPrintPreviewBrowserTest, |
| testing::Combine( |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy, |
| kCloudAnalysisNonBlockingPolicy, |
| kLocalAnalysisPolicy), |
| #else |
| /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy, |
| kCloudAnalysisNonBlockingPolicy), |
| #endif |
| /*content_analysis_allows_print=*/testing::Bool(), |
| /*backend_and_print_api=*/ |
| testing::ValuesIn(GeneratePrintBackendAndPlatformPrintApiVariations( |
| {PrintBackendFeatureVariation::kInBrowserProcess, |
| PrintBackendFeatureVariation::kOopSandboxedService}))), |
| ContentAnalysisAfterPrintPreviewBrowserTest::GetTestSuffix); |
| |
| #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest, |
| testing::Combine( |
| #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy, |
| kCloudAnalysisNonBlockingPolicy, |
| kLocalAnalysisPolicy), |
| #else |
| /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy, |
| kCloudAnalysisNonBlockingPolicy), |
| #endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS) |
| /*content_analysis_allows_print=*/testing::Bool(), |
| /*backend_and_print_api=*/ |
| testing::ValuesIn(GeneratePrintBackendAndPlatformPrintApiVariations( |
| {PrintBackendFeatureVariation::kInBrowserProcess, |
| PrintBackendFeatureVariation::kOopSandboxedService}))), |
| ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest:: |
| GetTestSuffix); |
| |
| #endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) |
| |
| #endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS) |
| |
| } // namespace printing |