Repository: electron/electron
Pull request: fix: prefill native print dialog options on macOS with OOP printing

#### Description of Change

Chromium enabled out-of-process (OOP) printing by default on macOS in CL:6032774. This broke `webContents.print()` option prefilling (e.g. copies, collate, duplex) in two ways:

1. `ScriptedPrint()` silently aborted because `RegisterSystemPrintClient()` was only called from `GetDefaultPrintSettings()`, but Electron's flow calls `UpdatePrintSettings()` instead when options are provided.

2. `PrinterQueryOop::UpdatePrintSettings()` sends settings to the remote `PrintBackend` service, but on macOS the native dialog runs in-browser using the local `PrintingContextMac::print_info_`, which was never updated with the user's requested settings.

Fix by registering the system print client in `UpdatePrintSettings()` and applying cached settings to the local printing context before showing the in-browser system print dialog.

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included
- [x] I have built and tested this PR
- [x] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where custom options in `webContents.print()` did not prefill the print dialog on macOS.

Changed files:
filename: patches/chromium/printing.patch
patch: @@ -68,7 +68,7 @@ index f91857eb0b6ad385721b8224100de26dfdd7dd8d..45e8766fcb8d46d8edc3bf8d21d3f826
                 : PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3;
    }
 diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
-index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f0c49c1e8 100644
+index aa79c324af2cec50019bca3bccff5d420fb30ffd..0b85598f87673537eccdd0b310e8462e96990d04 100644
 --- a/chrome/browser/printing/print_view_manager_base.cc
 +++ b/chrome/browser/printing/print_view_manager_base.cc
 @@ -80,6 +80,20 @@ namespace printing {
@@ -260,12 +260,18 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    if (prefs && prefs->HasPrefPath(prefs::kPrintRasterizePdfDpi)) {
      int value = prefs->GetInteger(prefs::kPrintRasterizePdfDpi);
      if (value > 0)
-@@ -740,8 +765,22 @@ void PrintViewManagerBase::UpdatePrintSettings(
+@@ -740,8 +765,28 @@ void PrintViewManagerBase::UpdatePrintSettings(
      }
    }
  
 -#if BUILDFLAG(IS_WIN)
 -  // TODO(crbug.com/40260379):  Remove this if the printable areas can be made
++#if BUILDFLAG(ENABLE_OOP_PRINTING)
++  if (ShouldPrintJobOop() && !query_with_ui_client_id().has_value()) {
++    RegisterSystemPrintClient();
++  }
++#endif
++
 +  std::unique_ptr<PrinterQuery> query =
 +      queue_->CreatePrinterQuery(GetCurrentTargetFrame()->GetGlobalId());
 +  auto* query_ptr = query.get();
@@ -285,7 +291,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    // fully available from `PrintBackend::GetPrinterSemanticCapsAndDefaults()`
    // for in-browser queries.
    if (printer_type == mojom::PrinterType::kLocal) {
-@@ -762,8 +801,6 @@ void PrintViewManagerBase::UpdatePrintSettings(
+@@ -762,8 +807,6 @@ void PrintViewManagerBase::UpdatePrintSettings(
    }
  #endif
  
@@ -294,7 +300,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
  }
  
  void PrintViewManagerBase::SetAccessibilityTree(
-@@ -779,7 +816,7 @@ void PrintViewManagerBase::SetAccessibilityTree(
+@@ -779,7 +822,7 @@ void PrintViewManagerBase::SetAccessibilityTree(
  void PrintViewManagerBase::IsPrintingEnabled(
      IsPrintingEnabledCallback callback) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -303,7 +309,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
  }
  
  void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
-@@ -805,7 +842,7 @@ void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
+@@ -805,7 +848,7 @@ void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
      return;
    }
  #endif
@@ -312,7 +318,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    std::optional<enterprise_connectors::ContentAnalysisDelegate::Data>
        scanning_data = enterprise_data_protection::GetPrintAnalysisData(
            web_contents(), enterprise_data_protection::PrintScanningContext::
-@@ -835,11 +872,9 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
+@@ -835,11 +878,9 @@ void PrintViewManagerBase::PrintingFailed(int32_t cookie,
    // destroyed.  In such cases the error notification to the user will
    // have already been displayed, and a second message should not be
    // shown.
@@ -326,7 +332,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    ReleasePrinterQuery();
  }
  
-@@ -851,15 +886,33 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
+@@ -851,15 +892,33 @@ void PrintViewManagerBase::RemoveTestObserver(TestObserver& observer) {
    test_observers_.RemoveObserver(&observer);
  }
  
@@ -360,7 +366,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
  }
  
  void PrintViewManagerBase::RenderFrameDeleted(
-@@ -901,13 +954,14 @@ void PrintViewManagerBase::SystemDialogCancelled() {
+@@ -901,13 +960,14 @@ void PrintViewManagerBase::SystemDialogCancelled() {
    // System dialog was cancelled. Clean up the print job and notify the
    // BackgroundPrintingManager.
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -376,7 +382,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
  }
  
  void PrintViewManagerBase::OnDocDone(int job_id, PrintedDocument* document) {
-@@ -921,18 +975,26 @@ void PrintViewManagerBase::OnJobDone() {
+@@ -921,18 +981,26 @@ void PrintViewManagerBase::OnJobDone() {
    // Printing is done, we don't need it anymore.
    // print_job_->is_job_pending() may still be true, depending on the order
    // of object registration.
@@ -405,7 +411,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    TerminatePrintJob(true);
  }
  
-@@ -942,7 +1004,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
+@@ -942,7 +1010,7 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() {
  
    // Is the document already complete?
    if (print_job_->document() && print_job_->document()->IsComplete()) {
@@ -414,7 +420,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
      return true;
    }
  
-@@ -995,7 +1057,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
+@@ -995,7 +1063,10 @@ bool PrintViewManagerBase::SetupNewPrintJob(
  
    // Disconnect the current `print_job_`.
    auto weak_this = weak_ptr_factory_.GetWeakPtr();
@@ -426,7 +432,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    if (!weak_this)
      return false;
  
-@@ -1015,7 +1080,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
+@@ -1015,7 +1086,7 @@ bool PrintViewManagerBase::SetupNewPrintJob(
  #endif
    print_job_->AddObserver(*this);
  
@@ -435,7 +441,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    return true;
  }
  
-@@ -1073,7 +1138,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
+@@ -1073,7 +1144,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
      // Ensure that any residual registration of printing client is released.
      // This might be necessary in some abnormal cases, such as the associated
      // render process having terminated.
@@ -444,7 +450,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
      if (!analyzing_content_) {
        UnregisterSystemPrintClient();
      }
-@@ -1083,6 +1148,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
+@@ -1083,6 +1154,11 @@ void PrintViewManagerBase::ReleasePrintJob() {
    }
  #endif
  
@@ -456,7 +462,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    if (!print_job_)
      return;
  
-@@ -1090,7 +1160,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
+@@ -1090,7 +1166,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
      // printing_rfh_ should only ever point to a RenderFrameHost with a live
      // RenderFrame.
      DCHECK(rfh->IsRenderFrameLive());
@@ -465,7 +471,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    }
  
    print_job_->RemoveObserver(*this);
-@@ -1132,7 +1202,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
+@@ -1132,7 +1208,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() {
  }
  
  bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
@@ -474,7 +480,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
      return true;
  
    if (!cookie) {
-@@ -1155,7 +1225,7 @@ bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
+@@ -1155,7 +1231,7 @@ bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
      return false;
    }
  
@@ -483,7 +489,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    // Don't start printing if enterprise checks are being performed to check if
    // printing is allowed, or if content analysis is going to take place right
    // before starting `print_job_`.
-@@ -1286,6 +1356,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
+@@ -1286,6 +1362,8 @@ void PrintViewManagerBase::CompleteScriptedPrint(
    auto callback_wrapper = base::BindOnce(
        &PrintViewManagerBase::ScriptedPrintReply, weak_ptr_factory_.GetWeakPtr(),
        std::move(callback), render_process_host->GetDeprecatedID());
@@ -492,7 +498,7 @@ index aa79c324af2cec50019bca3bccff5d420fb30ffd..455095a2cd63eabe4f267747070b443f
    std::unique_ptr<PrinterQuery> printer_query =
        queue()->PopPrinterQuery(params->cookie);
    if (!printer_query)
-@@ -1296,10 +1368,10 @@ void PrintViewManagerBase::CompleteScriptedPrint(
+@@ -1296,10 +1374,10 @@ void PrintViewManagerBase::CompleteScriptedPrint(
        params->expected_pages_count, params->has_selection, params->margin_type,
        params->is_scripted, !render_process_host->IsPdf(),
        base::BindOnce(&OnDidScriptedPrint, queue_, std::move(printer_query),
@@ -614,7 +620,7 @@ index 2a477e820d9f0126a05f86cd44f02c2189275bad..a2e9442ff9f5acf8e301f457b1806251
  
  #if BUILDFLAG(IS_CHROMEOS)
 diff --git a/chrome/browser/printing/printer_query_oop.cc b/chrome/browser/printing/printer_query_oop.cc
-index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..8facb5981cc421cad6bce71dfa8985b0a3270405 100644
+index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..e197026e8a7f132c1bf90a0f5f1eabb4f5f064ee 100644
 --- a/chrome/browser/printing/printer_query_oop.cc
 +++ b/chrome/browser/printing/printer_query_oop.cc
 @@ -126,7 +126,7 @@ void PrinterQueryOop::OnDidAskUserForSettings(
@@ -626,6 +632,28 @@ index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..8facb5981cc421cad6bce71dfa8985b0
      // Want the same PrintBackend service as the query so that we use the same
      // device context.
      print_document_client_id_ =
+@@ -189,6 +189,21 @@ void PrinterQueryOop::GetSettingsWithUI(uint32_t document_page_count,
+   //       browser process.
+   //   - Other platforms don't have a system print UI or do not use OOP
+   //     printing, so this does not matter.
++
++  // Apply cached settings to the local printing context so that the in-browser
++  // system print dialog is prefilled with user-specified options (e.g. copies,
++  // collate, duplex). OOP UpdatePrintSettings only applies settings to the
++  // remote service context, not the local one used by the native dialog.
++  if (settings().dpi()) {
++    printing_context()->SetPrintSettings(settings());
++    printing_context()->UpdatePrinterSettings(PrintingContext::PrinterSettings{
++#if BUILDFLAG(IS_MAC)
++        .external_preview = false,
++#endif
++        .show_system_dialog = false,
++    });
++  }
++
+   PrinterQuery::GetSettingsWithUI(
+       document_page_count, has_selection, is_scripted,
+       base::BindOnce(&PrinterQueryOop::OnDidAskUserForSettings,
 diff --git a/components/printing/browser/print_manager.cc b/components/printing/browser/print_manager.cc
 index 21c81377d32ae8d4185598a7eba88ed1d2063ef0..0767f4e9369e926b1cea99178c1a1975941f1765 100644
 --- a/components/printing/browser/print_manager.cc