Browse Source

refactor: printing implementation (#15143)

* refactor: basic printing

* move build files to chromium_src/BUILD.gn
* remove dependency on chrome prerender sources

* spec: move printing specs behind feature flag

* build: register pdf compositor service
Robo 6 years ago
parent
commit
82322968a3

+ 8 - 56
BUILD.gn

@@ -209,7 +209,6 @@ static_library("electron_lib") {
     "//base:base_static",
     "//base:i18n",
     "//chrome/app/resources:platform_locale_settings",
-    "//chrome/common",
     "//components/certificate_transparency",
     "//components/net_log",
     "//components/network_session_configurator/common",
@@ -433,62 +432,12 @@ static_library("electron_lib") {
   }
 
   if (enable_basic_printing) {
-    deps += [
-      "//chrome/common:mojo_bindings",
-      "//chrome/services/printing:lib",
-      "//components/printing/browser",
-      "//components/printing/common",
-      "//components/printing/renderer",
-      "//components/services/pdf_compositor/public/cpp:utils",
-      "//components/services/pdf_compositor/public/interfaces",
-      "//printing",
-    ]
-
     sources += [
-      "//chrome/browser/printing/print_job.cc",
-      "//chrome/browser/printing/print_job.h",
-      "//chrome/browser/printing/print_job_manager.cc",
-      "//chrome/browser/printing/print_job_manager.h",
-      "//chrome/browser/printing/print_job_worker.cc",
-      "//chrome/browser/printing/print_job_worker.h",
-      "//chrome/browser/printing/print_preview_message_handler.cc",
-      "//chrome/browser/printing/print_preview_message_handler.h",
-      "//chrome/browser/printing/print_view_manager_base.cc",
-      "//chrome/browser/printing/print_view_manager_base.h",
-      "//chrome/browser/printing/print_view_manager_basic.cc",
-      "//chrome/browser/printing/print_view_manager_basic.h",
-      "//chrome/browser/printing/print_view_manager_common.cc",
-      "//chrome/browser/printing/print_view_manager_common.h",
-      "//chrome/browser/printing/printer_manager_dialog.h",
-      "//chrome/browser/printing/printer_manager_dialog_linux.cc",
-      "//chrome/browser/printing/printer_manager_dialog_mac.mm",
-      "//chrome/browser/printing/printer_manager_dialog_win.cc",
-      "//chrome/browser/printing/printer_query.cc",
-      "//chrome/browser/printing/printer_query.h",
-      "//chrome/browser/printing/printing_message_filter.cc",
-      "//chrome/browser/printing/printing_message_filter.h",
-      "//chrome/renderer/prerender/prerender_dispatcher.cc",
-      "//chrome/renderer/prerender/prerender_dispatcher.h",
-      "//chrome/renderer/prerender/prerender_extra_data.cc",
-      "//chrome/renderer/prerender/prerender_extra_data.h",
-      "//chrome/renderer/prerender/prerender_helper.cc",
-      "//chrome/renderer/prerender/prerender_helper.h",
-      "//chrome/renderer/prerender/prerenderer_client.cc",
-      "//chrome/renderer/prerender/prerenderer_client.h",
-      "//chrome/renderer/printing/chrome_print_render_frame_helper_delegate.cc",
-      "//chrome/renderer/printing/chrome_print_render_frame_helper_delegate.h",
-      "//electron/atom/browser/atom_print_preview_message_handler.cc",
-      "//electron/atom/browser/atom_print_preview_message_handler.h",
+      "atom/browser/printing/print_preview_message_handler.cc",
+      "atom/browser/printing/print_preview_message_handler.h",
+      "atom/renderer/printing/print_render_frame_helper_delegate.cc",
+      "atom/renderer/printing/print_render_frame_helper_delegate.h",
     ]
-
-    if (is_win) {
-      sources += [
-        "//chrome/browser/printing/pdf_to_emf_converter.cc",
-        "//chrome/browser/printing/pdf_to_emf_converter.h",
-        "//chrome/utility/printing_handler.cc",
-        "//chrome/utility/printing_handler.h",
-      ]
-    }
   }
 
   if (enable_pepper_flash) {
@@ -939,7 +888,10 @@ service_manifest("electron_content_packaged_services_manifest_overlay") {
   packaged_services = [ "//services/proxy_resolver:proxy_resolver_manifest" ]
 
   if (enable_basic_printing) {
-    packaged_services += [ "//chrome/services/printing:manifest" ]
+    packaged_services += [
+      "//chrome/services/printing:manifest",
+      "//components/services/pdf_compositor:pdf_compositor_manifest",
+    ]
   }
 }
 

+ 46 - 60
atom/browser/api/atom_api_web_contents.cc

@@ -80,7 +80,6 @@
 #include "native_mate/dictionary.h"
 #include "native_mate/object_template_builder.h"
 #include "net/url_request/url_request_context.h"
-#include "printing/buildflags/buildflags.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/web/web_find_options.h"
 #include "ui/display/screen.h"
@@ -102,39 +101,15 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PRINTING)
-#include "atom/browser/atom_print_preview_message_handler.h"
 #include "chrome/browser/printing/print_view_manager_basic.h"
+#include "components/printing/common/print_messages.h"
 #endif
 
 #include "atom/common/node_includes.h"
 
-namespace {
-
-struct PrintSettings {
-  bool silent;
-  bool print_background;
-  base::string16 device_name;
-};
-
-}  // namespace
-
 namespace mate {
 
-template <>
-struct Converter<PrintSettings> {
-  static bool FromV8(v8::Isolate* isolate,
-                     v8::Local<v8::Value> val,
-                     PrintSettings* out) {
-    mate::Dictionary dict;
-    if (!ConvertFromV8(isolate, val, &dict))
-      return false;
-    dict.Get("silent", &(out->silent));
-    dict.Get("printBackground", &(out->print_background));
-    dict.Get("deviceName", &(out->device_name));
-    return true;
-  }
-};
-
+#if BUILDFLAG(ENABLE_PRINTING)
 template <>
 struct Converter<printing::PrinterBasicInfo> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
@@ -148,6 +123,7 @@ struct Converter<printing::PrinterBasicInfo> {
     return dict.GetHandle();
   }
 };
+#endif
 
 template <>
 struct Converter<WindowOpenDisposition> {
@@ -1482,50 +1458,58 @@ bool WebContents::IsCurrentlyAudible() {
   return web_contents()->IsCurrentlyAudible();
 }
 
-void WebContents::Print(mate::Arguments* args) {
 #if BUILDFLAG(ENABLE_PRINTING)
-  PrintSettings settings = {false, false, base::string16()};
-  if (args->Length() >= 1 && !args->GetNext(&settings)) {
-    args->ThrowError();
+void WebContents::Print(mate::Arguments* args) {
+  bool silent, print_background = false;
+  base::string16 device_name;
+  mate::Dictionary options = mate::Dictionary::CreateEmpty(args->isolate());
+  base::DictionaryValue settings;
+  if (args->Length() >= 1 && !args->GetNext(&options)) {
+    args->ThrowError("Invalid print settings specified");
     return;
   }
-  auto* print_view_manager_basic_ptr =
-      printing::PrintViewManagerBasic::FromWebContents(web_contents());
-  if (args->Length() == 2) {
-    base::Callback<void(bool)> callback;
-    if (!args->GetNext(&callback)) {
-      args->ThrowError();
-      return;
-    }
-    print_view_manager_basic_ptr->SetCallback(callback);
+  printing::CompletionCallback callback;
+  if (args->Length() == 2 && !args->GetNext(&callback)) {
+    args->ThrowError("Invalid optional callback provided");
+    return;
   }
-  print_view_manager_basic_ptr->PrintNow(
-      web_contents()->GetMainFrame(), settings.silent,
-      settings.print_background, settings.device_name);
-#else
-  LOG(ERROR) << "Printing is disabled";
-#endif
+  options.Get("silent", &silent);
+  options.Get("printBackground", &print_background);
+  if (options.Get("deviceName", &device_name) && !device_name.empty()) {
+    settings.SetString(printing::kSettingDeviceName, device_name);
+  }
+  auto* print_view_manager =
+      printing::PrintViewManagerBasic::FromWebContents(web_contents());
+  auto* focused_frame = web_contents()->GetFocusedFrame();
+  auto* rfh = focused_frame && focused_frame->HasSelection()
+                  ? focused_frame
+                  : web_contents()->GetMainFrame();
+  print_view_manager->PrintNow(
+      rfh,
+      std::make_unique<PrintMsg_PrintPages>(rfh->GetRoutingID(), silent,
+                                            print_background, settings),
+      std::move(callback));
 }
 
 std::vector<printing::PrinterBasicInfo> WebContents::GetPrinterList() {
   std::vector<printing::PrinterBasicInfo> printers;
-
-#if BUILDFLAG(ENABLE_PRINTING)
   auto print_backend = printing::PrintBackend::CreateInstance(nullptr);
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  print_backend->EnumeratePrinters(&printers);
-#endif
-
+  {
+    // TODO(deepak1556): Deprecate this api in favor of an
+    // async version and post a non blocing task call.
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    print_backend->EnumeratePrinters(&printers);
+  }
   return printers;
 }
 
-void WebContents::PrintToPDF(const base::DictionaryValue& setting,
-                             const PrintToPDFCallback& callback) {
-#if BUILDFLAG(ENABLE_PRINTING)
-  AtomPrintPreviewMessageHandler::FromWebContents(web_contents())
-      ->PrintToPDF(setting, callback);
-#endif
+void WebContents::PrintToPDF(
+    const base::DictionaryValue& settings,
+    const PrintPreviewMessageHandler::PrintToPDFCallback& callback) {
+  PrintPreviewMessageHandler::FromWebContents(web_contents())
+      ->PrintToPDF(settings, callback);
 }
+#endif
 
 void WebContents::AddWorkSpace(mate::Arguments* args,
                                const base::FilePath& path) {
@@ -2144,9 +2128,11 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("unregisterServiceWorker",
                  &WebContents::UnregisterServiceWorker)
       .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
-      .SetMethod("print", &WebContents::Print)
-      .SetMethod("getPrinters", &WebContents::GetPrinterList)
+#if BUILDFLAG(ENABLE_PRINTING)
+      .SetMethod("_print", &WebContents::Print)
+      .SetMethod("_getPrinters", &WebContents::GetPrinterList)
       .SetMethod("_printToPDF", &WebContents::PrintToPDF)
+#endif
       .SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
       .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
       .SetMethod("showDefinitionForSelection",

+ 13 - 10
atom/browser/api/atom_api_web_contents.h

@@ -22,9 +22,14 @@
 #include "content/public/common/favicon_url.h"
 #include "electron/buildflags/buildflags.h"
 #include "native_mate/handle.h"
-#include "printing/backend/print_backend.h"
+#include "printing/buildflags/buildflags.h"
 #include "ui/gfx/image/image.h"
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "atom/browser/printing/print_preview_message_handler.h"
+#include "printing/backend/print_backend.h"
+#endif
+
 namespace blink {
 struct WebDeviceEmulationParams;
 }
@@ -75,10 +80,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
     OFF_SCREEN,       // Used for offscreen rendering
   };
 
-  // For node.js callback function type: function(error, buffer)
-  using PrintToPDFCallback =
-      base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>;
-
   // Create a new WebContents and return the V8 wrapper of it.
   static mate::Handle<WebContents> Create(v8::Isolate* isolate,
                                           const mate::Dictionary& options);
@@ -162,15 +163,18 @@ class WebContents : public mate::TrackableObject<WebContents>,
   void SetAudioMuted(bool muted);
   bool IsAudioMuted();
   bool IsCurrentlyAudible();
-  void Print(mate::Arguments* args);
-  std::vector<printing::PrinterBasicInfo> GetPrinterList();
   void SetEmbedder(const WebContents* embedder);
   void SetDevToolsWebContents(const WebContents* devtools);
   v8::Local<v8::Value> GetNativeView() const;
 
+#if BUILDFLAG(ENABLE_PRINTING)
+  void Print(mate::Arguments* args);
+  std::vector<printing::PrinterBasicInfo> GetPrinterList();
   // Print current page as PDF.
-  void PrintToPDF(const base::DictionaryValue& setting,
-                  const PrintToPDFCallback& callback);
+  void PrintToPDF(
+      const base::DictionaryValue& settings,
+      const PrintPreviewMessageHandler::PrintToPDFCallback& callback);
+#endif
 
   // DevTools workspace api.
   void AddWorkSpace(mate::Arguments* args, const base::FilePath& path);
@@ -498,7 +502,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
 
   std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
   std::unique_ptr<WebViewGuestDelegate> guest_delegate_;
-
   std::unique_ptr<FrameSubscriber> frame_subscriber_;
 
   // The host webcontents that may contain this webcontents.

+ 8 - 2
atom/browser/atom_browser_client.cc

@@ -45,7 +45,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/printing/printing_message_filter.h"
 #include "components/net_log/chrome_net_log.h"
 #include "content/public/browser/browser_ppapi_host.h"
 #include "content/public/browser/client_certificate_delegate.h"
@@ -96,7 +95,9 @@
 #endif  // BUILDFLAG(ENABLE_TTS)
 
 #if BUILDFLAG(ENABLE_PRINTING)
+#include "chrome/browser/printing/printing_message_filter.h"
 #include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 
 using content::BrowserThread;
@@ -256,7 +257,8 @@ void AtomBrowserClient::RenderProcessWillLaunch(
     return;
 
 #if BUILDFLAG(ENABLE_PRINTING)
-  host->AddFilter(new printing::PrintingMessageFilter(process_id));
+  host->AddFilter(new printing::PrintingMessageFilter(
+      process_id, host->GetBrowserContext()));
 #endif
 
 #if BUILDFLAG(ENABLE_TTS)
@@ -592,6 +594,10 @@ void AtomBrowserClient::RegisterOutOfProcessServices(
                           IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME);
 
 #if BUILDFLAG(ENABLE_PRINTING)
+  (*services)[printing::mojom::kServiceName] =
+      base::BindRepeating(&l10n_util::GetStringUTF16,
+                          IDS_UTILITY_PROCESS_PDF_COMPOSITOR_SERVICE_NAME);
+
   (*services)[printing::mojom::kChromePrintingServiceName] =
       base::BindRepeating(&l10n_util::GetStringUTF16,
                           IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME);

+ 0 - 134
atom/browser/atom_print_preview_message_handler.cc

@@ -1,134 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "atom/browser/atom_print_preview_message_handler.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/memory/shared_memory.h"
-#include "base/memory/shared_memory_handle.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/printing/print_job_manager.h"
-#include "chrome/browser/printing/print_preview_dialog_controller.h"
-#include "chrome/browser/printing/print_view_manager.h"
-#include "chrome/browser/printing/printer_query.h"
-#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
-#include "components/printing/browser/print_composite_client.h"
-#include "components/printing/browser/print_manager_utils.h"
-#include "components/printing/common/print_messages.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "printing/page_size_margins.h"
-#include "printing/print_job_constants.h"
-#include "printing/print_settings.h"
-
-#include "atom/common/node_includes.h"
-
-using content::BrowserThread;
-using content::WebContents;
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::AtomPrintPreviewMessageHandler);
-
-namespace atom {
-
-namespace {
-
-char* CopyPDFDataOnIOThread(
-    const PrintHostMsg_DidPreviewDocument_Params& params) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  const PrintHostMsg_DidPrintContent_Params& content = params.content;
-  std::unique_ptr<base::SharedMemory> shared_buf(
-      new base::SharedMemory(content.metafile_data_handle, true));
-  if (!shared_buf->Map(content.data_size))
-    return nullptr;
-  char* pdf_data = new char[content.data_size];
-  memcpy(pdf_data, shared_buf->memory(), content.data_size);
-  return pdf_data;
-}
-
-void FreeNodeBufferData(char* data, void* hint) {
-  delete[] data;
-}
-
-}  // namespace
-
-AtomPrintPreviewMessageHandler::AtomPrintPreviewMessageHandler(
-    WebContents* web_contents)
-    : printing::PrintPreviewMessageHandler(web_contents),
-      weak_ptr_factory_(this) {
-  DCHECK(web_contents);
-}
-
-AtomPrintPreviewMessageHandler::~AtomPrintPreviewMessageHandler() {}
-
-void AtomPrintPreviewMessageHandler::OnMetafileReadyForPrinting(
-    content::RenderFrameHost* render_frame_host,
-    const PrintHostMsg_DidPreviewDocument_Params& params,
-    const PrintHostMsg_PreviewIds& ids) {
-  printing::PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
-      render_frame_host, params, ids);
-
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::IO, FROM_HERE, base::Bind(&CopyPDFDataOnIOThread, params),
-      base::Bind(&AtomPrintPreviewMessageHandler::RunPrintToPDFCallback,
-                 base::Unretained(this), ids.request_id,
-                 params.content.data_size));
-}
-
-void AtomPrintPreviewMessageHandler::OnPrintPreviewFailed(
-    int document_cookie,
-    const PrintHostMsg_PreviewIds& ids) {
-  printing::PrintPreviewMessageHandler::OnPrintPreviewFailed(document_cookie,
-                                                             ids);
-
-  RunPrintToPDFCallback(ids.request_id, 0, nullptr);
-}
-
-void AtomPrintPreviewMessageHandler::PrintToPDF(
-    const base::DictionaryValue& options,
-    const PrintToPDFCallback& callback) {
-  int request_id;
-  options.GetInteger(printing::kPreviewRequestID, &request_id);
-  print_to_pdf_callback_map_[request_id] = callback;
-
-  content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
-  rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), options));
-}
-
-void AtomPrintPreviewMessageHandler::RunPrintToPDFCallback(int request_id,
-                                                           uint32_t data_size,
-                                                           char* data) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  v8::Isolate* isolate = v8::Isolate::GetCurrent();
-  v8::Locker locker(isolate);
-  v8::HandleScope handle_scope(isolate);
-  if (data) {
-    v8::Local<v8::Value> buffer =
-        node::Buffer::New(isolate, data, static_cast<size_t>(data_size),
-                          &FreeNodeBufferData, nullptr)
-            .ToLocalChecked();
-    print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer);
-  } else {
-    v8::Local<v8::String> error_message =
-        v8::String::NewFromUtf8(isolate, "Failed to generate PDF");
-    print_to_pdf_callback_map_[request_id].Run(
-        v8::Exception::Error(error_message), v8::Null(isolate));
-  }
-  print_to_pdf_callback_map_.erase(request_id);
-}
-
-}  // namespace atom

+ 0 - 52
atom/browser/atom_print_preview_message_handler.h

@@ -1,52 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ATOM_BROWSER_ATOM_PRINT_PREVIEW_MESSAGE_HANDLER_H_
-#define ATOM_BROWSER_ATOM_PRINT_PREVIEW_MESSAGE_HANDLER_H_
-
-#include <map>
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/printing/print_preview_message_handler.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "v8/include/v8.h"
-
-namespace atom {
-
-// Manages the print preview handling for a WebContents.
-class AtomPrintPreviewMessageHandler
-    : public printing::PrintPreviewMessageHandler,
-      public content::WebContentsUserData<AtomPrintPreviewMessageHandler> {
- public:
-  ~AtomPrintPreviewMessageHandler() override;
-
-  using PrintToPDFCallback =
-      base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>;
-
-  void PrintToPDF(const base::DictionaryValue& options,
-                  const PrintToPDFCallback& callback);
-
- private:
-  explicit AtomPrintPreviewMessageHandler(content::WebContents* web_contents);
-  friend class content::WebContentsUserData<AtomPrintPreviewMessageHandler>;
-  typedef std::map<int, PrintToPDFCallback> PrintToPDFCallbackMap;
-
-  void OnMetafileReadyForPrinting(
-      content::RenderFrameHost* render_frame_host,
-      const PrintHostMsg_DidPreviewDocument_Params& params,
-      const PrintHostMsg_PreviewIds& ids) override;
-  void OnPrintPreviewFailed(int document_cookie,
-                            const PrintHostMsg_PreviewIds& ids) override;
-  void RunPrintToPDFCallback(int request_id, uint32_t data_size, char* data);
-
-  PrintToPDFCallbackMap print_to_pdf_callback_map_;
-
-  base::WeakPtrFactory<PrintPreviewMessageHandler> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AtomPrintPreviewMessageHandler);
-};
-
-}  // namespace atom
-
-#endif  // ATOM_BROWSER_ATOM_PRINT_PREVIEW_MESSAGE_HANDLER_H_

+ 9 - 4
atom/browser/browser_process_impl.cc

@@ -4,14 +4,13 @@
 
 #include "atom/browser/browser_process_impl.h"
 
-#include "chrome/browser/printing/print_job_manager.h"
-#include "printing/buildflags/buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
 
-BrowserProcessImpl::BrowserProcessImpl() : print_job_manager_(nullptr) {
 #if BUILDFLAG(ENABLE_PRINTING)
-  print_job_manager_.reset(new printing::PrintJobManager());
+#include "chrome/browser/printing/print_job_manager.h"
 #endif
+
+BrowserProcessImpl::BrowserProcessImpl() {
   g_browser_process = this;
 }
 
@@ -216,5 +215,11 @@ const std::string& BrowserProcessImpl::GetApplicationLocale() {
 }
 
 printing::PrintJobManager* BrowserProcessImpl::print_job_manager() {
+#if BUILDFLAG(ENABLE_PRINTING)
+  if (!print_job_manager_)
+    print_job_manager_.reset(new printing::PrintJobManager());
   return print_job_manager_.get();
+#else
+  return nullptr;
+#endif
 }

+ 3 - 0
atom/browser/browser_process_impl.h

@@ -15,6 +15,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/browser_process.h"
+#include "printing/buildflags/buildflags.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace printing {
@@ -98,7 +99,9 @@ class BrowserProcessImpl : public BrowserProcess {
   printing::PrintJobManager* print_job_manager() override;
 
  private:
+#if BUILDFLAG(ENABLE_PRINTING)
   std::unique_ptr<printing::PrintJobManager> print_job_manager_;
+#endif
   std::string locale_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessImpl);

+ 4 - 2
atom/browser/common_web_contents_delegate.cc

@@ -44,8 +44,9 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PRINTING)
-#include "atom/browser/atom_print_preview_message_handler.h"
+#include "atom/browser/printing/print_preview_message_handler.h"
 #include "chrome/browser/printing/print_view_manager_basic.h"
+#include "components/printing/browser/print_manager_utils.h"
 #endif
 
 using content::BrowserThread;
@@ -178,8 +179,9 @@ void CommonWebContentsDelegate::InitWithWebContents(
   web_contents->SetDelegate(this);
 
 #if BUILDFLAG(ENABLE_PRINTING)
+  PrintPreviewMessageHandler::CreateForWebContents(web_contents);
   printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
-  AtomPrintPreviewMessageHandler::CreateForWebContents(web_contents);
+  printing::CreateCompositeClientIfNeeded(web_contents);
 #endif
 
   // Determien whether the WebContents is offscreen.

+ 197 - 0
atom/browser/printing/print_preview_message_handler.cc

@@ -0,0 +1,197 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/browser/printing/print_preview_message_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "components/printing/browser/print_composite_client.h"
+#include "components/printing/browser/print_manager_utils.h"
+#include "components/printing/common/print_messages.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+#include "atom/common/node_includes.h"
+
+using content::BrowserThread;
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::PrintPreviewMessageHandler);
+
+namespace atom {
+
+namespace {
+
+void StopWorker(int document_cookie) {
+  if (document_cookie <= 0)
+    return;
+  scoped_refptr<printing::PrintQueriesQueue> queue =
+      g_browser_process->print_job_manager()->queue();
+  scoped_refptr<printing::PrinterQuery> printer_query =
+      queue->PopPrinterQuery(document_cookie);
+  if (printer_query.get()) {
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&printing::PrinterQuery::StopWorker, printer_query));
+  }
+}
+
+scoped_refptr<base::RefCountedMemory> GetDataFromHandle(
+    base::SharedMemoryHandle handle,
+    uint32_t data_size) {
+  auto shared_buf = std::make_unique<base::SharedMemory>(handle, true);
+  if (!shared_buf->Map(data_size)) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<base::RefCountedSharedMemory>(
+      std::move(shared_buf), data_size);
+}
+
+}  // namespace
+
+PrintPreviewMessageHandler::PrintPreviewMessageHandler(
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents), weak_ptr_factory_(this) {
+  DCHECK(web_contents);
+}
+
+PrintPreviewMessageHandler::~PrintPreviewMessageHandler() = default;
+
+bool PrintPreviewMessageHandler::OnMessageReceived(
+    const IPC::Message& message,
+    content::RenderFrameHost* render_frame_host) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(PrintPreviewMessageHandler, message,
+                                   render_frame_host)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
+                        OnMetafileReadyForPrinting)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  if (handled)
+    return true;
+
+  handled = true;
+  IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed, OnPrintPreviewFailed)
+    IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewCancelled,
+                        OnPrintPreviewCancelled)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
+    content::RenderFrameHost* render_frame_host,
+    const PrintHostMsg_DidPreviewDocument_Params& params,
+    const PrintHostMsg_PreviewIds& ids) {
+  // Always try to stop the worker.
+  StopWorker(params.document_cookie);
+
+  const PrintHostMsg_DidPrintContent_Params& content = params.content;
+  if (!content.metafile_data_handle.IsValid() ||
+      params.expected_pages_count <= 0) {
+    RunPrintToPDFCallback(ids.request_id, nullptr);
+    return;
+  }
+
+  if (printing::IsOopifEnabled()) {
+    auto* client =
+        printing::PrintCompositeClient::FromWebContents(web_contents());
+    DCHECK(client);
+    client->DoCompositeDocumentToPdf(
+        params.document_cookie, render_frame_host, content.metafile_data_handle,
+        content.data_size, content.subframe_content_info,
+        base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       params.expected_pages_count, ids));
+  } else {
+    RunPrintToPDFCallback(
+        ids.request_id,
+        GetDataFromHandle(content.metafile_data_handle, content.data_size));
+  }
+}
+
+void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(
+    int page_number,
+    const PrintHostMsg_PreviewIds& ids,
+    printing::mojom::PdfCompositor::Status status,
+    base::ReadOnlySharedMemoryRegion region) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (status != printing::mojom::PdfCompositor::Status::SUCCESS) {
+    DLOG(ERROR) << "Compositing pdf failed with error " << status;
+    RunPrintToPDFCallback(ids.request_id, nullptr);
+    return;
+  }
+
+  RunPrintToPDFCallback(
+      ids.request_id,
+      base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
+}
+
+void PrintPreviewMessageHandler::OnPrintPreviewFailed(
+    int document_cookie,
+    const PrintHostMsg_PreviewIds& ids) {
+  StopWorker(document_cookie);
+
+  RunPrintToPDFCallback(ids.request_id, nullptr);
+}
+
+void PrintPreviewMessageHandler::OnPrintPreviewCancelled(
+    int document_cookie,
+    const PrintHostMsg_PreviewIds& ids) {
+  StopWorker(document_cookie);
+
+  RunPrintToPDFCallback(ids.request_id, nullptr);
+}
+
+void PrintPreviewMessageHandler::PrintToPDF(
+    const base::DictionaryValue& options,
+    const PrintToPDFCallback& callback) {
+  int request_id;
+  options.GetInteger(printing::kPreviewRequestID, &request_id);
+  print_to_pdf_callback_map_[request_id] = callback;
+
+  auto* focused_frame = web_contents()->GetFocusedFrame();
+  auto* rfh = focused_frame && focused_frame->HasSelection()
+                  ? focused_frame
+                  : web_contents()->GetMainFrame();
+  rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), options));
+}
+
+void PrintPreviewMessageHandler::RunPrintToPDFCallback(
+    int request_id,
+    scoped_refptr<base::RefCountedMemory> data_bytes) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::Locker locker(isolate);
+  v8::HandleScope handle_scope(isolate);
+  if (data_bytes && data_bytes->size()) {
+    v8::Local<v8::Value> buffer =
+        node::Buffer::Copy(isolate,
+                           reinterpret_cast<const char*>(data_bytes->front()),
+                           data_bytes->size())
+            .ToLocalChecked();
+    print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer);
+  } else {
+    v8::Local<v8::String> error_message =
+        v8::String::NewFromUtf8(isolate, "Failed to generate PDF");
+    print_to_pdf_callback_map_[request_id].Run(
+        v8::Exception::Error(error_message), v8::Null(isolate));
+  }
+  print_to_pdf_callback_map_.erase(request_id);
+}
+
+}  // namespace atom

+ 74 - 0
atom/browser/printing/print_preview_message_handler.h

@@ -0,0 +1,74 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_
+#define ATOM_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_
+
+#include <map>
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "v8/include/v8.h"
+
+struct PrintHostMsg_DidPreviewDocument_Params;
+struct PrintHostMsg_PreviewIds;
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace atom {
+
+// Manages the print preview handling for a WebContents.
+class PrintPreviewMessageHandler
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<PrintPreviewMessageHandler> {
+ public:
+  using PrintToPDFCallback =
+      base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>;
+
+  ~PrintPreviewMessageHandler() override;
+
+  void PrintToPDF(const base::DictionaryValue& options,
+                  const PrintToPDFCallback& callback);
+
+ protected:
+  // content::WebContentsObserver implementation.
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* render_frame_host) override;
+
+ private:
+  friend class content::WebContentsUserData<PrintPreviewMessageHandler>;
+
+  explicit PrintPreviewMessageHandler(content::WebContents* web_contents);
+
+  void OnMetafileReadyForPrinting(
+      content::RenderFrameHost* render_frame_host,
+      const PrintHostMsg_DidPreviewDocument_Params& params,
+      const PrintHostMsg_PreviewIds& ids);
+  void OnCompositePdfDocumentDone(int page_number,
+                                  const PrintHostMsg_PreviewIds& ids,
+                                  printing::mojom::PdfCompositor::Status status,
+                                  base::ReadOnlySharedMemoryRegion region);
+  void OnPrintPreviewFailed(int document_cookie,
+                            const PrintHostMsg_PreviewIds& ids);
+  void OnPrintPreviewCancelled(int document_cookie,
+                               const PrintHostMsg_PreviewIds& ids);
+  void RunPrintToPDFCallback(int request_id,
+                             scoped_refptr<base::RefCountedMemory> data_bytes);
+
+  using PrintToPDFCallbackMap = std::map<int, PrintToPDFCallback>;
+  PrintToPDFCallbackMap print_to_pdf_callback_map_;
+
+  base::WeakPtrFactory<PrintPreviewMessageHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintPreviewMessageHandler);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_

+ 6 - 0
atom/common/api/features.cc

@@ -4,6 +4,7 @@
 
 #include "electron/buildflags/buildflags.h"
 #include "native_mate/dictionary.h"
+#include "printing/buildflags/buildflags.h"
 // clang-format off
 #include "atom/common/node_includes.h"  // NOLINT(build/include_alpha)
 // clang-format on
@@ -34,6 +35,10 @@ bool IsTtsEnabled() {
   return BUILDFLAG(ENABLE_TTS);
 }
 
+bool IsPrintingEnabled() {
+  return BUILDFLAG(ENABLE_PRINTING);
+}
+
 void Initialize(v8::Local<v8::Object> exports,
                 v8::Local<v8::Value> unused,
                 v8::Local<v8::Context> context,
@@ -46,6 +51,7 @@ void Initialize(v8::Local<v8::Object> exports,
                  &IsFakeLocationProviderEnabled);
   dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
   dict.SetMethod("isTtsEnabled", &IsTtsEnabled);
+  dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled);
 }
 
 }  // namespace

+ 37 - 0
atom/renderer/printing/print_render_frame_helper_delegate.cc

@@ -0,0 +1,37 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/renderer/printing/print_render_frame_helper_delegate.h"
+
+#include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace atom {
+
+PrintRenderFrameHelperDelegate::PrintRenderFrameHelperDelegate() = default;
+
+PrintRenderFrameHelperDelegate::~PrintRenderFrameHelperDelegate() = default;
+
+bool PrintRenderFrameHelperDelegate::CancelPrerender(
+    content::RenderFrame* render_frame) {
+  return false;
+}
+
+// Return the PDF object element if |frame| is the out of process PDF extension.
+blink::WebElement PrintRenderFrameHelperDelegate::GetPdfElement(
+    blink::WebLocalFrame* frame) {
+  return blink::WebElement();
+}
+
+bool PrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() {
+  return false;
+}
+
+bool PrintRenderFrameHelperDelegate::OverridePrint(
+    blink::WebLocalFrame* frame) {
+  return false;
+}
+
+}  // namespace atom

+ 31 - 0
atom/renderer/printing/print_render_frame_helper_delegate.h

@@ -0,0 +1,31 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_RENDERER_PRINTING_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
+#define ATOM_RENDERER_PRINTING_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
+
+#include "base/macros.h"
+#include "components/printing/renderer/print_render_frame_helper.h"
+
+namespace atom {
+
+class PrintRenderFrameHelperDelegate
+    : public printing::PrintRenderFrameHelper::Delegate {
+ public:
+  PrintRenderFrameHelperDelegate();
+  ~PrintRenderFrameHelperDelegate() override;
+
+ private:
+  // printing::PrintRenderFrameHelper::Delegate:
+  bool CancelPrerender(content::RenderFrame* render_frame) override;
+  blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override;
+  bool IsPrintPreviewEnabled() override;
+  bool OverridePrint(blink::WebLocalFrame* frame) override;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintRenderFrameHelperDelegate);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_RENDERER_PRINTING_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_

+ 2 - 2
atom/renderer/renderer_client_base.cc

@@ -55,7 +55,7 @@
 #endif  // BUILDFLAG(ENABLE_TTS)
 
 #if BUILDFLAG(ENABLE_PRINTING)
-#include "chrome/renderer/printing/chrome_print_render_frame_helper_delegate.h"
+#include "atom/renderer/printing/print_render_frame_helper_delegate.h"
 #include "components/printing/renderer/print_render_frame_helper.h"
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 
@@ -192,7 +192,7 @@ void RendererClientBase::RenderFrameCreated(
   new ContentSettingsObserver(render_frame);
 #if BUILDFLAG(ENABLE_PRINTING)
   new printing::PrintRenderFrameHelper(
-      render_frame, std::make_unique<ChromePrintRenderFrameHelperDelegate>());
+      render_frame, std::make_unique<atom::PrintRenderFrameHelperDelegate>());
 #endif
 
 #if BUILDFLAG(ENABLE_PDF_VIEWER)

+ 8 - 0
atom/utility/atom_content_utility_client.cc

@@ -4,6 +4,7 @@
 
 #include "atom/utility/atom_content_utility_client.h"
 
+#include <string>
 #include <utility>
 
 #include "base/command_line.h"
@@ -17,6 +18,8 @@
 #if BUILDFLAG(ENABLE_PRINTING)
 #include "chrome/services/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
 
 #if defined(OS_WIN)
 #include "chrome/services/printing/pdf_to_emf_converter_factory.h"
@@ -92,6 +95,11 @@ void AtomContentUtilityClient::RegisterServices(StaticServiceMap* services) {
                     proxy_resolver_info);
 
 #if BUILDFLAG(ENABLE_PRINTING)
+  service_manager::EmbeddedServiceInfo pdf_compositor_info;
+  pdf_compositor_info.factory =
+      base::BindRepeating(&printing::CreatePdfCompositorService, std::string());
+  services->emplace(printing::mojom::kServiceName, pdf_compositor_info);
+
   service_manager::EmbeddedServiceInfo printing_info;
   printing_info.factory =
       base::BindRepeating(&printing::PrintingService::CreateService);

+ 39 - 0
chromium_src/BUILD.gn

@@ -4,6 +4,7 @@
 
 import("//build/config/ui.gni")
 import("//electron/buildflags/buildflags.gni")
+import("//printing/buildflags/buildflags.gni")
 import("//third_party/widevine/cdm/widevine.gni")
 
 # Builds some of the chrome sources that Electron depends on.
@@ -139,4 +140,42 @@ static_library("chrome") {
     ]
     deps += [ "//components/cdm/renderer" ]
   }
+
+  if (enable_basic_printing) {
+    sources += [
+      "//chrome/browser/printing/print_job.cc",
+      "//chrome/browser/printing/print_job.h",
+      "//chrome/browser/printing/print_job_manager.cc",
+      "//chrome/browser/printing/print_job_manager.h",
+      "//chrome/browser/printing/print_job_worker.cc",
+      "//chrome/browser/printing/print_job_worker.h",
+      "//chrome/browser/printing/print_view_manager_base.cc",
+      "//chrome/browser/printing/print_view_manager_base.h",
+      "//chrome/browser/printing/print_view_manager_basic.cc",
+      "//chrome/browser/printing/print_view_manager_basic.h",
+      "//chrome/browser/printing/printer_query.cc",
+      "//chrome/browser/printing/printer_query.h",
+      "//chrome/browser/printing/printing_message_filter.cc",
+      "//chrome/browser/printing/printing_message_filter.h",
+    ]
+    deps += [
+      "//chrome/services/printing:lib",
+      "//components/printing/browser",
+      "//components/printing/common",
+      "//components/printing/renderer",
+      "//components/services/pdf_compositor",
+      "//components/services/pdf_compositor/public/cpp:factory",
+      "//components/services/pdf_compositor/public/interfaces",
+      "//printing",
+    ]
+
+    if (is_win) {
+      sources += [
+        "//chrome/browser/printing/pdf_to_emf_converter.cc",
+        "//chrome/browser/printing/pdf_to_emf_converter.h",
+        "//chrome/utility/printing_handler.cc",
+        "//chrome/utility/printing_handler.h",
+      ]
+    }
+  }
 }

+ 5 - 0
electron_strings.grdp

@@ -5,6 +5,11 @@
     V8 Proxy Resolver
   </message>
 
+  <!-- PDF Compositor Service -->
+  <message name="IDS_UTILITY_PROCESS_PDF_COMPOSITOR_SERVICE_NAME" desc="The name of the utility process used for PDF compositing.">
+    PDF Compositor Service
+  </message>
+
   <!-- Printing Service -->
   <message name="IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME" desc="The name of the utility process used for printing conversions.">
     Printing Service

+ 23 - 1
lib/browser/api/web-contents.js

@@ -1,5 +1,6 @@
 'use strict'
 
+const features = process.atomBinding('features')
 const { EventEmitter } = require('events')
 const electron = require('electron')
 const path = require('path')
@@ -70,6 +71,7 @@ const defaultPrintingSetting = {
   marginsType: 0,
   isFirstRequest: false,
   requestID: getNextId(),
+  previewUIID: 0,
   previewModifiable: true,
   printToPDF: true,
   printWithCloudPrint: false,
@@ -251,7 +253,27 @@ WebContents.prototype.printToPDF = function (options, callback) {
 
   // Chromium expects this in a 0-100 range number, not as float
   printingSetting.scaleFactor *= 100
-  this._printToPDF(printingSetting, callback)
+  if (features.isPrintingEnabled()) {
+    this._printToPDF(printingSetting, callback)
+  } else {
+    console.error('Error: Printing feature is disabled.')
+  }
+}
+
+WebContents.prototype.print = function (...args) {
+  if (features.isPrintingEnabled()) {
+    this._print(args)
+  } else {
+    console.error('Error: Printing feature is disabled.')
+  }
+}
+
+WebContents.prototype.getPrinters = function () {
+  if (features.isPrintingEnabled()) {
+    return this._getPrinters()
+  } else {
+    console.error('Error: Printing feature is disabled.')
+  }
 }
 
 WebContents.prototype.getZoomLevel = function (callback) {

+ 2 - 1
manifests/electron_content_browser_manifest_overlay.json

@@ -6,7 +6,8 @@
       "requires": {
         "device": [ "device:geolocation_control" ],
         "proxy_resolver": [ "factory" ],
-        "chrome_printing": [ "converter" ]
+        "chrome_printing": [ "converter" ],
+        "pdf_compositor": [ "compositor"]
       }
     }
   }

File diff suppressed because it is too large
+ 197 - 587
patches/common/chromium/printing.patch


+ 0 - 37
spec/api-browser-window-spec.js

@@ -1694,43 +1694,6 @@ describe('BrowserWindow module', () => {
         })
       })
 
-      it('can get printer list', (done) => {
-        w.destroy()
-        w = new BrowserWindow({
-          show: false,
-          webPreferences: {
-            sandbox: true,
-            preload: preload
-          }
-        })
-        w.loadURL('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E')
-        w.webContents.once('did-finish-load', () => {
-          const printers = w.webContents.getPrinters()
-          assert.strictEqual(Array.isArray(printers), true)
-          done()
-        })
-      })
-
-      it('can print to PDF', (done) => {
-        w.destroy()
-        w = new BrowserWindow({
-          show: false,
-          webPreferences: {
-            sandbox: true,
-            preload: preload
-          }
-        })
-        w.loadURL('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E')
-        w.webContents.once('did-finish-load', () => {
-          w.webContents.printToPDF({}, function (error, data) {
-            assert.strictEqual(error, null)
-            assert.strictEqual(data instanceof Buffer, true)
-            assert.notStrictEqual(data.length, 0)
-            done()
-          })
-        })
-      })
-
       it('supports calling preventDefault on new-window events', (done) => {
         w.destroy()
         w = new BrowserWindow({

+ 58 - 0
spec/api-web-contents-spec.js

@@ -10,6 +10,7 @@ const { emittedOnce } = require('./events-helpers')
 const chai = require('chai')
 const dirtyChai = require('dirty-chai')
 
+const features = process.atomBinding('features')
 const { ipcRenderer, remote, clipboard } = require('electron')
 const { BrowserWindow, webContents, ipcMain, session } = remote
 const { expect } = chai
@@ -943,4 +944,61 @@ describe('webContents module', () => {
       done()
     })
   })
+
+  describe('getPrinterList()', () => {
+    before(function () {
+      if (!features.isPrintingEnabled()) {
+        return closeWindow(w).then(() => {
+          w = null
+          this.skip()
+        })
+      }
+    })
+
+    it('can get printer list', (done) => {
+      w.destroy()
+      w = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          sandbox: true
+        }
+      })
+      w.loadURL('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E')
+      w.webContents.once('did-finish-load', () => {
+        const printers = w.webContents.getPrinters()
+        assert.strictEqual(Array.isArray(printers), true)
+        done()
+      })
+    })
+  })
+
+  describe('printToPDF()', () => {
+    before(function () {
+      if (!features.isPrintingEnabled()) {
+        return closeWindow(w).then(() => {
+          w = null
+          this.skip()
+        })
+      }
+    })
+
+    it('can print to PDF', (done) => {
+      w.destroy()
+      w = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          sandbox: true
+        }
+      })
+      w.loadURL('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E')
+      w.webContents.once('did-finish-load', () => {
+        w.webContents.printToPDF({}, function (error, data) {
+          assert.strictEqual(error, null)
+          assert.strictEqual(data instanceof Buffer, true)
+          assert.notStrictEqual(data.length, 0)
+          done()
+        })
+      })
+    })
+  })
 })

Some files were not shown because too many files changed in this diff