[go: nahoru, domu]

M123: Add tests for OOPPD GDI metafile variations

Windows print drivers for which the browser does a PDF conversion to a
form other than regular EMF have been found to cause blank pages when
the out-of-process print drivers feature is enabled.  This is due to
the Print Backend service treating all GDI jobs as one single type of
EMF object, which is problematic since PostScript is handled with a
different EMF playback method at printing time.

Gain coverage for this scenario by extending the browser tests based on
SystemAccessProcessSandboxedServicePrintBrowserTest to include the
Windows printer language type in the test parameterization.

Update the TestPrintingContext logic to support specifying the language
type that the faked printer driver is supposed to expect.  This allows
for adding a check that provided page metafile data matches the
expectation.

To communicate this expected type to the Print Backend service, the
necessary additional mojom MetafileDataType must be included as part of
adding these tests.  To show the current problematic behavior, the
PrintBackendServiceImpl still creates the same EMF metafile object for
this new type.

Together these changes show that the metafile objects created for
PostScript and text-only print drivers are incorrectly handled by the
Print Backend service, so that the playback of such objects will not
provide imaging as desired.

(cherry picked from commit a8bc2d1f87a7b3e5a911923a7233714c084d0caa)

Bug: 41492268
Change-Id: Icca0a89c1a9cde102f4ec04874470536468c5cc6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5333336
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Alan Screen <awscreen@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1268739}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5361239
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/6312@{#536}
Cr-Branched-From: 6711dcdae48edaf98cbc6964f90fac85b7d9986e-refs/heads/main@{#1262506}
diff --git a/chrome/browser/printing/browser_printing_context_factory_for_test.cc b/chrome/browser/printing/browser_printing_context_factory_for_test.cc
index e612eb2..38a1384 100644
--- a/chrome/browser/printing/browser_printing_context_factory_for_test.cc
+++ b/chrome/browser/printing/browser_printing_context_factory_for_test.cc
@@ -15,22 +15,6 @@
 
 namespace printing {
 
-namespace {
-
-std::unique_ptr<TestPrintingContext> MakeDefaultTestPrintingContext(
-    PrintingContext::Delegate* delegate,
-    PrintingContext::ProcessBehavior process_behavior,
-    const std::string& printer_name) {
-  auto context =
-      std::make_unique<TestPrintingContext>(delegate, process_behavior);
-
-  context->SetDeviceSettings(printer_name,
-                             test::MakeDefaultPrintSettings(printer_name));
-  return context;
-}
-
-}  // namespace
-
 BrowserPrintingContextFactoryForTest::BrowserPrintingContextFactoryForTest() =
     default;
 
@@ -96,6 +80,14 @@
   printer_name_ = printer_name;
 }
 
+#if BUILDFLAG(IS_WIN)
+void BrowserPrintingContextFactoryForTest::
+    SetPrinterLanguageTypeForSubsequentContexts(
+        mojom::PrinterLanguageType printer_language_type) {
+  printer_language_type_ = printer_language_type;
+}
+#endif
+
 void BrowserPrintingContextFactoryForTest::
     SetFailedErrorOnUpdatePrinterSettings() {
   failed_error_for_update_printer_settings_ = true;
@@ -158,4 +150,25 @@
   on_new_document_callback_ = std::move(callback);
 }
 
+std::unique_ptr<TestPrintingContext>
+BrowserPrintingContextFactoryForTest::MakeDefaultTestPrintingContext(
+    PrintingContext::Delegate* delegate,
+    PrintingContext::ProcessBehavior process_behavior,
+    const std::string& printer_name) {
+  auto context =
+      std::make_unique<TestPrintingContext>(delegate, process_behavior);
+
+  std::unique_ptr<PrintSettings> settings =
+      test::MakeDefaultPrintSettings(printer_name);
+
+#if BUILDFLAG(IS_WIN)
+  if (printer_language_type_.has_value()) {
+    settings->set_printer_language_type(printer_language_type_.value());
+  }
+#endif
+
+  context->SetDeviceSettings(printer_name, std::move(settings));
+  return context;
+}
+
 }  // namespace printing
diff --git a/chrome/browser/printing/browser_printing_context_factory_for_test.h b/chrome/browser/printing/browser_printing_context_factory_for_test.h
index be4b901..61ea80b 100644
--- a/chrome/browser/printing/browser_printing_context_factory_for_test.h
+++ b/chrome/browser/printing/browser_printing_context_factory_for_test.h
@@ -28,6 +28,11 @@
       PrintingContext::ProcessBehavior process_behavior) override;
 
   void SetPrinterNameForSubsequentContexts(const std::string& printer_name);
+#if BUILDFLAG(IS_WIN)
+  void SetPrinterLanguageTypeForSubsequentContexts(
+      mojom::PrinterLanguageType printer_language_type);
+#endif
+
   void SetFailedErrorOnUpdatePrinterSettings();
   void SetCancelErrorOnNewDocument(bool cause_errors);
   void SetFailedErrorOnNewDocument(bool cause_errors);
@@ -47,7 +52,16 @@
       TestPrintingContext::OnNewDocumentCallback callback);
 
  private:
+  std::unique_ptr<TestPrintingContext> MakeDefaultTestPrintingContext(
+      PrintingContext::Delegate* delegate,
+      PrintingContext::ProcessBehavior process_behavior,
+      const std::string& printer_name);
+
   std::string printer_name_;
+#if BUILDFLAG(IS_WIN)
+  std::optional<mojom::PrinterLanguageType> printer_language_type_;
+#endif
+
   bool failed_error_for_update_printer_settings_ = false;
   bool cancels_in_new_document_ = false;
   bool failed_error_for_new_document_ = false;
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc
index 74743e3..a0d5532 100644
--- a/chrome/browser/printing/print_browsertest.cc
+++ b/chrome/browser/printing/print_browsertest.cc
@@ -531,6 +531,15 @@
       printer_name);
 }
 
+#if BUILDFLAG(IS_WIN)
+void PrintBrowserTest::SetPrinterLanguageTypeForSubsequentContexts(
+    mojom::PrinterLanguageType printer_language_type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  test_printing_context_factory_.SetPrinterLanguageTypeForSubsequentContexts(
+      printer_language_type);
+}
+#endif
+
 void PrintBrowserTest::SetNewDocumentJobId(int job_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   test_printing_context_factory_.SetJobIdOnNewDocument(job_id);
diff --git a/chrome/browser/printing/print_browsertest.h b/chrome/browser/printing/print_browsertest.h
index 79693984..8e39aff 100644
--- a/chrome/browser/printing/print_browsertest.h
+++ b/chrome/browser/printing/print_browsertest.h
@@ -53,6 +53,10 @@
 
   void AddPrinter(const std::string& printer_name);
   void SetPrinterNameForSubsequentContexts(const std::string& printer_name);
+#if BUILDFLAG(IS_WIN)
+  void SetPrinterLanguageTypeForSubsequentContexts(
+      mojom::PrinterLanguageType printer_language_type);
+#endif
   void SetNewDocumentJobId(int job_id);
 
   void PrintAndWaitUntilPreviewIsReady();
diff --git a/chrome/browser/printing/system_access_process_print_browsertest.cc b/chrome/browser/printing/system_access_process_print_browsertest.cc
index 065ed71..7205939 100644
--- a/chrome/browser/printing/system_access_process_print_browsertest.cc
+++ b/chrome/browser/printing/system_access_process_print_browsertest.cc
@@ -165,7 +165,15 @@
 
 enum class PlatformPrintApiVariation {
 #if BUILDFLAG(IS_WIN)
-  kGdi,
+  // 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,
@@ -175,8 +183,14 @@
 const char* GetPlatformPrintApiString(PlatformPrintApiVariation variation) {
   switch (variation) {
 #if BUILDFLAG(IS_WIN)
-    case PlatformPrintApiVariation::kGdi:
-      return "Gdi";
+    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
@@ -191,11 +205,28 @@
   return GetPlatformPrintApiString(info.param);
 }
 
-const PlatformPrintApiVariation kSandboxedServicePlatformPrintApiVariations[] =
-    {
+// Tests using these variations are not concerned with the different language
+// types, where one Windows GDI type is sufficient.
+constexpr PlatformPrintApiVariation
+    kSandboxedServicePlatformPrintApiVariations[] = {
 #if BUILDFLAG(IS_WIN)
         // TODO(crbug.com/1008222):  Include XPS variation.
-        PlatformPrintApiVariation::kGdi,
+        PlatformPrintApiVariation::kGdiEmf,
+#else
+        PlatformPrintApiVariation::kCups,
+#endif
+};
+
+// Tests using these variations are concerned with all the different language
+// types on Windows.
+constexpr PlatformPrintApiVariation
+    kSandboxedServicePlatformPrintLanguageApiVariations[] = {
+#if BUILDFLAG(IS_WIN)
+        // TODO(crbug.com/1008222):  Include XPS variation.
+        PlatformPrintApiVariation::kGdiEmf,
+        PlatformPrintApiVariation::kGdiPostScriptLevel2,
+        PlatformPrintApiVariation::kGdiPostScriptLevel3,
+        PlatformPrintApiVariation::kGdiTextOnly,
 #else
         PlatformPrintApiVariation::kCups,
 #endif
@@ -232,10 +263,11 @@
   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/1008222):  Include XPS variation, only when the
     // `print_backend_variation` is not `kInBrowserProcess`.
     variations.emplace_back(print_backend_variation,
-                            PlatformPrintApiVariation::kGdi);
+                            PlatformPrintApiVariation::kGdiEmf);
 #else
     variations.emplace_back(print_backend_variation,
                             PlatformPrintApiVariation::kCups);
@@ -1277,6 +1309,37 @@
     testing::ValuesIn(kSandboxedServicePlatformPrintApiVariations),
     GetPlatformPrintApiTestSuffix);
 
+class SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest
+    : public SystemAccessProcessSandboxedServicePrintBrowserTest {
+ public:
+  SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest() = default;
+  ~SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest() override =
+      default;
+
+#if BUILDFLAG(IS_WIN)
+  mojom::PrinterLanguageType UseLanguageType() {
+    switch (GetParam()) {
+      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,
+    SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest,
+    testing::ValuesIn(kSandboxedServicePlatformPrintLanguageApiVariations),
+    GetPlatformPrintApiTestSuffix);
+
 class SystemAccessProcessServicePrintBrowserTest
     : public SystemAccessProcessPrintBrowserTestBase,
       public testing::WithParamInterface<
@@ -1479,10 +1542,14 @@
   EXPECT_EQ(error_dialog_shown_count(), 0u);
 }
 
-IN_PROC_BROWSER_TEST_P(SystemAccessProcessSandboxedServicePrintBrowserTest,
-                       StartPrinting) {
+IN_PROC_BROWSER_TEST_P(
+    SystemAccessProcessSandboxedServiceLanguagePrintBrowserTest,
+    StartPrinting) {
   AddPrinter("printer1");
   SetPrinterNameForSubsequentContexts("printer1");
+#if BUILDFLAG(IS_WIN)
+  SetPrinterLanguageTypeForSubsequentContexts(UseLanguageType());
+#endif
   constexpr int kJobId = 1;
   SetNewDocumentJobId(kJobId);
 
@@ -1495,6 +1562,31 @@
   ASSERT_TRUE(web_contents);
   SetUpPrintViewManager(web_contents);
 
+#if BUILDFLAG(IS_WIN)
+  if (UseLanguageType() == mojom::PrinterLanguageType::kNone) {
+    // 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 {
+    // TODO(crbug.com/41492268):  Change to match the block above (language
+    // equals `kNone`) once the correct variation of EMF type is properly
+    // passed through to the Print Backend service.
+    // 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, which fails.
+    // 4.  The print jobs is canceled.
+    // 5.  An error dialog is shown.
+    // 6.  Wait for the one print job to be destroyed, to ensure printing
+    //     finished cleanly before completing the test.
+    SetNumExpectedMessages(/*num=*/6);
+  }
+#else
   // The expected events for this are:
   // 1.  Update print settings.
   // 2.  A print job is started.
@@ -1503,20 +1595,33 @@
   // 5.  Wait for the one print job to be destroyed, to ensure printing
   //     finished cleanly before completing the test.
   SetNumExpectedMessages(/*num=*/5);
+#endif  // BUILDFLAG(IS_WIN)
   PrintAfterPreviewIsReadyAndLoaded();
 
   EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
 #if BUILDFLAG(IS_WIN)
   // TODO(crbug.com/1008222)  Include Windows coverage of
   // RenderPrintedDocument() once XPS print pipeline is added.
-  EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
-  EXPECT_EQ(render_printed_page_count(), 1);
+  if (UseLanguageType() == mojom::PrinterLanguageType::kNone) {
+    EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kSuccess);
+    EXPECT_EQ(render_printed_page_count(), 1);
+    EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
+    EXPECT_THAT(document_done_job_id(), testing::Optional(kJobId));
+    EXPECT_EQ(error_dialog_shown_count(), 0u);
+  } else {
+    // TODO(crbug.com/41492268):  Update for no failures once the correct
+    // variation of EMF type is properly passed through to the Print Backend
+    // service.
+    EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kFailed);
+    EXPECT_EQ(document_done_result(), mojom::ResultCode::kFailed);
+    EXPECT_EQ(error_dialog_shown_count(), 1u);
+  }
 #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);
+#endif
   EXPECT_EQ(print_job_destruction_count(), 1);
 
 #if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS)
diff --git a/chrome/services/printing/print_backend_service_impl.cc b/chrome/services/printing/print_backend_service_impl.cc
index 942d91e..167e06ad 100644
--- a/chrome/services/printing/print_backend_service_impl.cc
+++ b/chrome/services/printing/print_backend_service_impl.cc
@@ -118,6 +118,7 @@
       return std::make_unique<MetafileSkia>();
 #if BUILDFLAG(IS_WIN)
     case mojom::MetafileDataType::kEMF:
+    case mojom::MetafileDataType::kPostScriptEmf:
       return std::make_unique<Emf>();
 #endif
   }
diff --git a/printing/mojom/print.mojom b/printing/mojom/print.mojom
index 6b1d6c3..ef18f11 100644
--- a/printing/mojom/print.mojom
+++ b/printing/mojom/print.mojom
@@ -110,6 +110,8 @@
   kPDF,
   [EnableIf=is_win]
   kEMF,
+  [EnableIf=is_win]
+  kPostScriptEmf,
 };
 
 // What kind of margins to use.
diff --git a/printing/test_printing_context.cc b/printing/test_printing_context.cc
index 2b6202d..24ac6146 100644
--- a/printing/test_printing_context.cc
+++ b/printing/test_printing_context.cc
@@ -30,6 +30,32 @@
 
 namespace printing {
 
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+// Metafile data that is generated by
+// chrome/browser/printing/pdf_to_emf_converter.cc can be of different types,
+// which are expected to match particular printer languages.
+bool AreLanguageAndMetafileTypesCompatible(
+    mojom::PrinterLanguageType printer_language_type,
+    mojom::MetafileDataType metafile_data_type) {
+  switch (printer_language_type) {
+    case mojom::PrinterLanguageType::kTextOnly:
+    case mojom::PrinterLanguageType::kPostscriptLevel2:
+    case mojom::PrinterLanguageType::kPostscriptLevel3:
+      return metafile_data_type == mojom::MetafileDataType::kPostScriptEmf;
+    case mojom::PrinterLanguageType::kNone:
+      return metafile_data_type == mojom::MetafileDataType::kEMF;
+    case mojom::PrinterLanguageType::kXps:
+      // TODO(crbug.com/40100562):  Update to use an XPS MetafileDataType once
+      // it is available.
+      return false;
+  }
+}
+#endif  // BUILDFLAG(IS_WIN)
+
+}  // namespace
+
 TestPrintingContextDelegate::TestPrintingContextDelegate() = default;
 
 TestPrintingContextDelegate::~TestPrintingContextDelegate() = default;
@@ -146,7 +172,7 @@
 
   // The printer name is to be embedded in the printing context's existing
   // settings.
-  const std::string& device_name = base::UTF16ToUTF8(settings_->device_name());
+  const std::string device_name = base::UTF16ToUTF8(settings_->device_name());
   auto found = device_settings_.find(device_name);
   if (found == device_settings_.end()) {
     DLOG(ERROR) << "No such device found in test printing context: `"
@@ -249,6 +275,32 @@
     return mojom::ResultCode::kFailed;
   }
 
+#if BUILDFLAG(IS_WIN)
+  // Examine the driver type for the printer, to make sure the provided
+  // metafile is of the correct type.
+  const std::string device_name =
+      base::UTF16ToUTF8(applied_settings_.device_name());
+  // TODO(crbug.com/327554077):  Update to expect having a valid `device_name`
+  // once sandboxed OOPPD gets fixed for current API call flow.
+  if (!device_name.empty()) {
+    auto found = device_settings_.find(device_name);
+    if (found == device_settings_.end()) {
+      DLOG(ERROR) << "No such device found in test printing context: `"
+                  << device_name << "`";
+      return mojom::ResultCode::kFailed;
+    }
+    const PrintSettings* device_default_print_settings = found->second.get();
+    if (!AreLanguageAndMetafileTypesCompatible(
+            device_default_print_settings->printer_language_type(),
+            page.metafile()->GetDataType())) {
+      DVLOG(1) << "Incompatible metafile type for printer language type: "
+               << device_default_print_settings->printer_language_type() << ", "
+               << page.metafile()->GetDataType();
+      return mojom::ResultCode::kFailed;
+    }
+  }
+#endif  // BUILDFLAG(IS_WIN)
+
   // No-op.
   return mojom::ResultCode::kSuccess;
 }