Bug 1329881 - part1: send PDF file path and printing setting to Chrome process draft
authorFarmer Tseng <fatseng@mozilla.com>
Wed, 10 May 2017 13:03:06 +0800
changeset 576341 e287fe9f345db76ed1ed8868b6f5d72d867e11d5
parent 572425 4a6a71f4aa22e4dc3961884ce505ce34bdd799a2
child 576342 b7bafc8bfb7a03e3f901c355584d1d9508742e65
push id58323
push userbmo:fatseng@mozilla.com
push dateThu, 11 May 2017 16:31:31 +0000
bugs1329881
milestone55.0a1
Bug 1329881 - part1: send PDF file path and printing setting to Chrome process MozReview-Commit-ID: A0w0CqSoaoH
layout/base/nsDocumentViewer.cpp
layout/printing/ipc/PRemotePrintJob.ipdl
layout/printing/ipc/RemotePrintJobChild.cpp
layout/printing/ipc/RemotePrintJobChild.h
layout/printing/ipc/RemotePrintJobParent.cpp
layout/printing/ipc/RemotePrintJobParent.h
layout/printing/nsPrintEngine.cpp
layout/printing/nsPrintEngine.h
toolkit/components/browser/nsIWebBrowserPrint.idl
toolkit/components/printingui/ipc/PrintDataUtils.cpp
widget/android/nsDeviceContextAndroid.cpp
widget/android/nsDeviceContextAndroid.h
widget/cocoa/nsDeviceContextSpecX.h
widget/cocoa/nsDeviceContextSpecX.mm
widget/gtk/nsDeviceContextSpecG.cpp
widget/gtk/nsDeviceContextSpecG.h
widget/nsDeviceContextSpecProxy.cpp
widget/nsDeviceContextSpecProxy.h
widget/nsIDeviceContextSpec.h
widget/windows/nsDeviceContextSpecWin.h
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -319,16 +319,18 @@ protected:
   nsIPresShell* GetPresShell();
   nsPresContext* GetPresContext();
   nsViewManager* GetViewManager();
 
   void DetachFromTopLevelWidget();
 
   void SetPrintRelated();
 
+  nsresult EnsurePrintEngineInitialized(nsIDocument* aDocument);
+
   // IMPORTANT: The ownership implicit in the following member
   // variables has been explicitly checked and set using nsCOMPtr
   // for owning pointers and raw COM interface pointers for weak
   // (ie, non owning) references. If you add any members to this
   // class, please make the ownership explicit (pinkerton, scc).
 
   WeakPtr<nsDocShell> mContainer; // it owns me!
   nsWeakPtr mTopContainerWhilePrinting;
@@ -3819,18 +3821,66 @@ nsDocViewerFocusListener::Init(nsDocumen
 
 /** ---------------------------------------------------
  *  From nsIWebBrowserPrint
  */
 
 #ifdef NS_PRINTING
 
 NS_IMETHODIMP
+nsDocumentViewer::ShowPrintDialog(nsIPrintSettings* aPrintSettings)
+{
+  MOZ_ASSERT(aPrintSettings);
+
+  nsresult rv = EnsurePrintEngineInitialized(mDocument);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mPrintEngine->ShowPrintDialog(aPrintSettings);
+  return rv;
+}
+
+NS_IMETHODIMP
+nsDocumentViewer::PrintPDF(const nsAString& aPDFFilePath)
+{
+  NS_ENSURE_STATE(mPrintEngine);
+
+  // if we are printing another URL, then exit
+  // the reason we check here is because this method can be called while
+  // another is still in here (the printing dialog is a good example).
+  // the only time we can print more than one job at a time is the regression tests
+  if (GetIsPrinting()) {
+    // Let the user know we are not ready to print.
+    mPrintEngine->FirePrintingErrorEvent(NS_ERROR_NOT_AVAILABLE);
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // Dispatch 'beforeprint' event and ensure 'afterprint' will be dispatched.
+  // Script listens the 'afterprint' event to remove the PDF file which is used
+  // for printing. In the future, we will replace this event by promise.
+  MOZ_ASSERT(!mAutoBeforeAndAfterPrint,
+             "We don't want to dispatch nested beforeprint/afterprint");
+  nsAutoPtr<AutoPrintEventDispatcher> autoBeforeAndAfterPrint(
+    new AutoPrintEventDispatcher(mDocument));
+  NS_ENSURE_STATE(!GetIsPrinting());
+
+  // Postpone the 'afterprint' event until printing has finished or aborted
+  mAutoBeforeAndAfterPrint = autoBeforeAndAfterPrint;
+  mPrintEngine->SetDisallowSelectionPrint(true);
+  nsresult rv = mPrintEngine->PrintPDF(aPDFFilePath);
+  if (NS_FAILED(rv)) {
+    mPrintEngine->SetIsPrinting(false);
+    OnDonePrinting();
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
 nsDocumentViewer::Print(nsIPrintSettings*       aPrintSettings,
-                          nsIWebProgressListener* aWebProgressListener)
+                        nsIWebProgressListener* aWebProgressListener)
 {
   SetPrintRelated();
 
   // Printing XUL documents is not supported.
   nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
   if (xulDoc) {
     return NS_ERROR_FAILURE;
   }
@@ -3888,36 +3938,19 @@ nsDocumentViewer::Print(nsIPrintSettings
     new AutoPrintEventDispatcher(mDocument));
   NS_ENSURE_STATE(!GetIsPrinting());
   // If we are hosting a full-page plugin, tell it to print
   // first. It shows its own native print UI.
   nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(mDocument));
   if (pDoc)
     return pDoc->Print();
 
-  if (!mPrintEngine) {
-    NS_ENSURE_STATE(mDeviceContext);
-    mPrintEngine = new nsPrintEngine();
-
-    rv = mPrintEngine->Initialize(this, mContainer, mDocument, 
-                                  float(mDeviceContext->AppUnitsPerCSSInch()) /
-                                  float(mDeviceContext->AppUnitsPerDevPixel()) /
-                                  mPageZoom,
-#ifdef DEBUG
-                                  mDebugFile
-#else
-                                  nullptr
-#endif
-                                  );
-    if (NS_FAILED(rv)) {
-      mPrintEngine->Destroy();
-      mPrintEngine = nullptr;
-      return rv;
-    }
-  }
+  rv = EnsurePrintEngineInitialized(mDocument);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (mPrintEngine->HasPrintCallbackCanvas()) {
     // Postpone the 'afterprint' event until after the mozPrintCallback
     // callbacks have been called:
     mAutoBeforeAndAfterPrint = autoBeforeAndAfterPrint;
   }
   dom::Element* root = mDocument->GetRootElement();
   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
     mPrintEngine->SetDisallowSelectionPrint(true);
@@ -3977,36 +4010,19 @@ nsDocumentViewer::PrintPreview(nsIPrintS
   // [1] Until PDF.js is removed (though, maybe after that as well).
   nsAutoPtr<AutoPrintEventDispatcher> autoBeforeAndAfterPrint;
   if (!mAutoBeforeAndAfterPrint) {
     autoBeforeAndAfterPrint = new AutoPrintEventDispatcher(doc);
   }
   NS_ENSURE_STATE(!GetIsPrinting());
   // beforeprint event may have caused ContentViewer to be shutdown.
   NS_ENSURE_STATE(mContainer);
-  NS_ENSURE_STATE(mDeviceContext);
-  if (!mPrintEngine) {
-    mPrintEngine = new nsPrintEngine();
-
-    rv = mPrintEngine->Initialize(this, mContainer, doc,
-                                  float(mDeviceContext->AppUnitsPerCSSInch()) /
-                                  float(mDeviceContext->AppUnitsPerDevPixel()) /
-                                  mPageZoom,
-#ifdef DEBUG
-                                  mDebugFile
-#else
-                                  nullptr
-#endif
-                                  );
-    if (NS_FAILED(rv)) {
-      mPrintEngine->Destroy();
-      mPrintEngine = nullptr;
-      return rv;
-    }
-  }
+  rv = EnsurePrintEngineInitialized(doc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (autoBeforeAndAfterPrint &&
       mPrintEngine->HasPrintCallbackCanvas()) {
     // Postpone the 'afterprint' event until after the mozPrintCallback
     // callbacks have been called:
     mAutoBeforeAndAfterPrint = autoBeforeAndAfterPrint;
   }
   dom::Element* root = doc->GetRootElement();
   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
@@ -4317,16 +4333,43 @@ nsDocumentViewer::SetIsPrintingInDocShel
     aParentNode->GetChildAt(i, getter_AddRefs(child));
     NS_ASSERTION(child, "child isn't nsIDocShell");
     if (child) {
       SetIsPrintingInDocShellTree(child, aIsPrintingOrPP, false);
     }
   }
 
 }
+
+nsresult
+nsDocumentViewer::EnsurePrintEngineInitialized(nsIDocument* aDocument)
+{
+  if (mPrintEngine)
+    return NS_OK;
+
+  NS_ENSURE_STATE(mDeviceContext);
+
+  mPrintEngine = new nsPrintEngine();
+  nsresult rv = mPrintEngine->Initialize(this, mContainer, aDocument,
+    float(mDeviceContext->AppUnitsPerCSSInch()) /
+    float(mDeviceContext->AppUnitsPerDevPixel()) /
+    mPageZoom,
+#ifdef DEBUG
+    mDebugFile
+#else
+    nullptr
+#endif
+    );
+  if (NS_FAILED(rv)) {
+    mPrintEngine->Destroy();
+    mPrintEngine = nullptr;
+    return rv;
+  }
+  return NS_OK;
+}
 #endif // NS_PRINTING
 
 bool
 nsDocumentViewer::ShouldAttachToTopLevel()
 {
   if (!mParentWidget)
     return false;
 
--- a/layout/printing/ipc/PRemotePrintJob.ipdl
+++ b/layout/printing/ipc/PRemotePrintJob.ipdl
@@ -38,21 +38,27 @@ parent:
   async ProgressChange(long aCurSelfProgress,
                        long aMaxSelfProgress,
                        long aCurTotalProgress,
                        long aMaxTotalProgress);
 
   // Report a status change to listeners in the parent process.
   async StatusChange(nsresult aStatus);
 
+  // Send PDF file to printer
+  async PrintPDF(nsString aPDFFilePath);
+
 child:
   // Inform the child that the print has been initialized in the parent or has
   // failed with result aRv.
   async PrintInitializationResult(nsresult aRv);
 
   // Inform the child that the latest page has been processed remotely.
   async PageProcessed();
 
+  // Inform the child that PDF has been printed remotely.
+  async DonePrintingPDF();
+
   async __delete__();
 };
 
 } // namespace layout
 } // namespace mozilla
--- a/layout/printing/ipc/RemotePrintJobChild.cpp
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -58,16 +58,31 @@ mozilla::ipc::IPCResult
 RemotePrintJobChild::RecvPageProcessed()
 {
   MOZ_ASSERT(mPagePrintTimer);
 
   mPagePrintTimer->RemotePrintFinished();
   return IPC_OK();
 }
 
+void
+RemotePrintJobChild::PrintPDF(const nsString& aPDFFilePath)
+{
+  Unused << SendPrintPDF(aPDFFilePath);
+}
+
+mozilla::ipc::IPCResult
+RemotePrintJobChild::RecvDonePrintingPDF()
+{
+  MOZ_ASSERT(mPrintEngine);
+
+  mPrintEngine->DonePrintingPDF();
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult
 RemotePrintJobChild::RecvAbortPrint(const nsresult& aRv)
 {
   MOZ_ASSERT(mPrintEngine);
 
   mPrintEngine->CleanupOnFailure(aRv, true);
   return IPC_OK();
 }
--- a/layout/printing/ipc/RemotePrintJobChild.h
+++ b/layout/printing/ipc/RemotePrintJobChild.h
@@ -41,16 +41,20 @@ public:
   mozilla::ipc::IPCResult RecvPageProcessed() final;
 
   mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final;
 
   void SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer);
 
   void SetPrintEngine(nsPrintEngine* aPrintEngine);
 
+  void PrintPDF(const nsString& aPDFFilePath);
+
+  mozilla::ipc::IPCResult RecvDonePrintingPDF() final;
+
 private:
   ~RemotePrintJobChild() final;
 
   bool mPrintInitialized = false;
   nsresult mInitializationResult = NS_OK;
   RefPtr<nsPagePrintTimer> mPagePrintTimer;
   RefPtr<nsPrintEngine> mPrintEngine;
 };
--- a/layout/printing/ipc/RemotePrintJobParent.cpp
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -139,16 +139,26 @@ RemotePrintJobParent::PrintPage(const ns
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult
+RemotePrintJobParent::RecvPrintPDF(const nsString& aPDFFilePath)
+{
+  // TODO: Actually print the PDF before we call SendDonePrintingPDF()
+  // Fixed in a later patch.
+  Unused << SendDonePrintingPDF();
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 RemotePrintJobParent::RecvFinalizePrint()
 {
   // EndDocument is sometimes called in the child even when BeginDocument has
   // not been called. See bug 1223332.
   if (mPrintDeviceContext) {
     DebugOnly<nsresult> rv = mPrintDeviceContext->EndDocument();
 
     // Too late to abort the child just log.
--- a/layout/printing/ipc/RemotePrintJobParent.h
+++ b/layout/printing/ipc/RemotePrintJobParent.h
@@ -31,16 +31,18 @@ public:
 
   mozilla::ipc::IPCResult RecvInitializePrint(const nsString& aDocumentTitle,
                                               const nsString& aPrintToFile,
                                               const int32_t& aStartPage,
                                               const int32_t& aEndPage) final;
 
   mozilla::ipc::IPCResult RecvProcessPage(const nsCString& aPageFileName) final;
 
+  mozilla::ipc::IPCResult RecvPrintPDF(const nsString& aPDFFilePath) final;
+
   mozilla::ipc::IPCResult RecvFinalizePrint() final;
 
   mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final;
 
   mozilla::ipc::IPCResult RecvStateChange(const long& aStateFlags,
                                           const nsresult& aStatus) final;
 
   mozilla::ipc::IPCResult RecvProgressChange(const long& aCurSelfProgress,
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -237,16 +237,18 @@ void nsPrintEngine::Destroy()
 {
   if (mIsDestroying) {
     return;
   }
   mIsDestroying = true;
 
   mPrt = nullptr;
 
+  mDevspec = nullptr;
+
 #ifdef NS_PRINT_PREVIEW
   mPrtPreview = nullptr;
   mOldPrtPreview = nullptr;
 #endif
   mDocViewerPrint = nullptr;
 }
 
 //-------------------------------------------------------
@@ -806,16 +808,64 @@ nsPrintEngine::PrintPreview(nsIPrintSett
   NS_ENSURE_STATE(doc);
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
   MOZ_ASSERT(domDoc);
 
   // Document is not busy -- go ahead with the Print Preview
   return CommonPrint(true, aPrintSettings, aWebProgressListener, domDoc);
 }
 
+NS_IMETHODIMP
+nsPrintEngine::ShowPrintDialog(nsIPrintSettings* aPrintSettings)
+{
+  nsCOMPtr<nsIPrintSession> printSession;
+  nsresult rv = aPrintSettings->GetPrintSession(getter_AddRefs(printSession));
+  if (NS_FAILED(rv) || !printSession) {
+    printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    aPrintSettings->SetPrintSession(printSession);
+  }
+
+  nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
+  NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
+  nsPIDOMWindowOuter* domWin = mDocument->GetWindow();
+  NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
+  NS_ENSURE_TRUE(printPromptService, NS_ERROR_FAILURE);
+  rv = printPromptService->ShowPrintDialog(domWin, wbp, aPrintSettings);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+  printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+  if (remotePrintJob) {
+    remotePrintJob->SetPrintEngine(this);
+  }
+
+  if (XRE_IsContentProcess()) {
+    mDevspec = new nsDeviceContextSpecProxy();
+  } else {
+    mDevspec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = mDevspec->Init(nullptr, aPrintSettings, false);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+nsPrintEngine::PrintPDF(const nsAString&  aPDFFilePath)
+{
+  NS_ENSURE_STATE(mDevspec);
+
+  SetIsPrinting(true);
+
+  return mDevspec->PrintPDF(aPDFFilePath);
+}
+
 //----------------------------------------------------------------------------------
 NS_IMETHODIMP
 nsPrintEngine::GetIsFramesetDocument(bool *aIsFramesetDocument)
 {
   nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
   *aIsFramesetDocument = IsParentAFrameSet(webContainer);
   return NS_OK;
 }
@@ -3080,16 +3130,27 @@ nsPrintEngine::DonePrintingPages(nsPrint
 
   // Release reference to mPagePrintTimer; the timer object destroys itself
   // after this returns true
   DisconnectPagePrintTimer();
 
   return true;
 }
 
+bool
+nsPrintEngine::DonePrintingPDF()
+{
+  PR_PL(("****** In DV::DonePrintingPDF \n"));
+
+  FirePrintCompletionEvent();
+  SetIsPrinting(false);
+
+  return true;
+}
+
 //-------------------------------------------------------
 // Recursively sets the PO items to be printed "As Is"
 // from the given item down into the tree
 void
 nsPrintEngine::SetPrintAsIs(nsPrintObject* aPO, bool aAsIs)
 {
   NS_ASSERTION(aPO, "Pointer is null!");
 
--- a/layout/printing/nsPrintEngine.h
+++ b/layout/printing/nsPrintEngine.h
@@ -60,16 +60,18 @@ public:
   NS_IMETHOD GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected);
   NS_IMETHOD GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages);
   NS_IMETHOD EnumerateDocumentNames(uint32_t* aCount, char16_t*** aResult);
   static nsresult GetGlobalPrintSettings(nsIPrintSettings** aPrintSettings);
   NS_IMETHOD GetDoingPrint(bool *aDoingPrint);
   NS_IMETHOD GetDoingPrintPreview(bool *aDoingPrintPreview);
   NS_IMETHOD GetCurrentPrintSettings(nsIPrintSettings **aCurrentPrintSettings);
 
+  NS_IMETHOD ShowPrintDialog(nsIPrintSettings* aPrintSettings);
+  NS_IMETHOD PrintPDF(const nsAString&  aPDFFilePath);
 
   // This enum tells indicates what the default should be for the title
   // if the title from the document is null
   enum eDocTitleDefault {
     eDocTitleDefBlank,
     eDocTitleDefURLDoc
   };
 
@@ -108,16 +110,18 @@ public:
   void InstallPrintPreviewListener();
 
   // nsIDocumentViewerPrint Printing Methods
   bool     HasPrintCallbackCanvas();
   bool     PrePrintPage();
   bool     PrintPage(nsPrintObject* aPOect, bool& aInRange);
   bool     DonePrintingPages(nsPrintObject* aPO, nsresult aResult);
 
+  bool     DonePrintingPDF();
+
   //---------------------------------------------------------------------
   void BuildDocTree(nsIDocShell *      aParentNode,
                     nsTArray<nsPrintObject*> * aDocList,
                     const mozilla::UniquePtr<nsPrintObject>& aPO);
   nsresult ReflowDocList(const mozilla::UniquePtr<nsPrintObject>& aPO,
                          bool aSetPixelScale);
 
   nsresult ReflowPrintObject(const mozilla::UniquePtr<nsPrintObject>& aPO);
@@ -277,16 +281,20 @@ protected:
 
   FILE* mDebugFile;
 
   int32_t mLoadCounter;
   bool mDidLoadDataForPrinting;
   bool mIsDestroying;
   bool mDisallowSelectionPrint;
 
+  // This is only used when we print a PDF via |PrintPDF|.
+  // It is allocated when PrintPDF is called and is deleted when destorying nsPrintEngine.
+  nsCOMPtr<nsIDeviceContextSpec> mDevspec;
+
   nsresult AfterNetworkPrint(bool aHandleError);
 
   nsresult SetRootView(nsPrintObject* aPO,
                        bool& aDoReturn,
                        bool& aDocumentIsTopLevel,
                        nsSize& aAdjSize);
   nsView* GetParentViewForRoot();
   bool DoSetPixelScale();
--- a/toolkit/components/browser/nsIWebBrowserPrint.idl
+++ b/toolkit/components/browser/nsIWebBrowserPrint.idl
@@ -143,10 +143,27 @@ interface nsIWebBrowserPrint : nsISuppor
   void enumerateDocumentNames(out uint32_t aCount,[retval, array, size_is(aCount)] out wstring aResult);
 
   /**
    * This exists PrintPreview mode and returns browser window to galley mode
    * @return void
    */
   void exitPrintPreview();
 
+  /**
+   * Show print dialog and setup a printing IPC between the Chrome process and
+   * the Content process
+   *
+   * @param aThePrintSettings - Printer Settings for the print job
+   * @return void
+   */
+  void showPrintDialog(in nsIPrintSettings aThePrintSettings);
+
+  /**
+   * Print the specified PDF file
+   *
+   * @param aPDFFilePath - file path
+   * @return void
+   */
+  void printPDF(in AString aPDFFilePath);
+
 };
 
--- a/toolkit/components/printingui/ipc/PrintDataUtils.cpp
+++ b/toolkit/components/printingui/ipc/PrintDataUtils.cpp
@@ -90,16 +90,28 @@ MockWebBrowserPrint::GetIsRangeSelection
 
 NS_IMETHODIMP
 MockWebBrowserPrint::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+MockWebBrowserPrint::ShowPrintDialog(nsIPrintSettings* aPrintSettings)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::PrintPDF(const nsAString& aPDFFilePath)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 MockWebBrowserPrint::Print(nsIPrintSettings* aThePrintSettings,
                            nsIWebProgressListener* aWPListener)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 MockWebBrowserPrint::PrintPreview(nsIPrintSettings* aThePrintSettings,
--- a/widget/android/nsDeviceContextAndroid.cpp
+++ b/widget/android/nsDeviceContextAndroid.cpp
@@ -76,8 +76,15 @@ nsDeviceContextSpecAndroid::EndDocument(
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mTempFile->MoveTo(destDir, destLeafName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   destFile->SetPermissions(0666);
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsDeviceContextSpecAndroid::PrintPDF(const nsAString& aPDFFilePath)
+{
+  MOZ_ASSERT_UNREACHABLE("On Android we download PDFs, so they're never printed by Firefox");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
\ No newline at end of file
--- a/widget/android/nsDeviceContextAndroid.h
+++ b/widget/android/nsDeviceContextAndroid.h
@@ -24,13 +24,15 @@ public:
     NS_IMETHOD BeginDocument(const nsAString& aTitle,
                              const nsAString& aPrintToFileName,
                              int32_t aStartPage,
                              int32_t aEndPage) override;
     NS_IMETHOD EndDocument() override;
     NS_IMETHOD BeginPage() override { return NS_OK; }
     NS_IMETHOD EndPage() override { return NS_OK; }
 
+    NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) override;
+
 private:
     nsCOMPtr<nsIPrintSettings> mPrintSettings;
     nsCOMPtr<nsIFile> mTempFile;
 };
 #endif // nsDeviceContextAndroid_h__
--- a/widget/cocoa/nsDeviceContextSpecX.h
+++ b/widget/cocoa/nsDeviceContextSpecX.h
@@ -26,16 +26,18 @@ public:
     NS_IMETHOD EndDocument() override;
     NS_IMETHOD BeginPage() override {
       return NS_OK;
     };
     NS_IMETHOD EndPage() override {
       return NS_OK;
     };
 
+    NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) override;
+
     void GetPaperRect(double* aTop, double* aLeft, double* aBottom, double* aRight);
 
 protected:
     virtual ~nsDeviceContextSpecX();
 
 protected:
     PMPrintSession    mPrintSession;              // printing context.
     PMPageFormat      mPageFormat;                // page format.
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -231,16 +231,26 @@ NS_IMETHODIMP nsDeviceContextSpecX::EndD
   }
 #endif
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+NS_IMETHODIMP nsDeviceContextSpecX::PrintPDF(const nsAString& aPDFFilePath)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  NS_NOTYETIMPLEMENTED("Doesn't implement yet, please write me");
+  return NS_ERROR_NOT_IMPLEMENTED;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
 void nsDeviceContextSpecX::GetPaperRect(double* aTop, double* aLeft, double* aBottom, double* aRight)
 {
     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
     PMRect paperRect;
     ::PMGetAdjustedPaperRect(mPageFormat, &paperRect);
 
     *aTop = paperRect.top;
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -342,16 +342,21 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::En
     mode_t mask = umask(0);
     umask(mask);
     // If you're not familiar with umasks, they contain the bits of what NOT to set in the permissions
     // (thats because files and directories have different numbers of bits for their permissions)
     destFile->SetPermissions(0666 & ~(mask));
   }
   return NS_OK;
 }
+NS_IMETHODIMP nsDeviceContextSpecGTK::PrintPDF(const nsAString& aPDFFilePath)
+{
+  NS_NOTYETIMPLEMENTED("Doesn't implement yet, please write me");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
 //  Printer Enumerator
 nsPrinterEnumeratorGTK::nsPrinterEnumeratorGTK()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsPrinterEnumeratorGTK, nsIPrinterEnumerator)
 
--- a/widget/gtk/nsDeviceContextSpecG.h
+++ b/widget/gtk/nsDeviceContextSpecG.h
@@ -36,16 +36,18 @@ public:
                   bool aIsPrintPreview) override;
   NS_IMETHOD BeginDocument(const nsAString& aTitle,
                            const nsAString& aPrintToFileName,
                            int32_t aStartPage, int32_t aEndPage) override;
   NS_IMETHOD EndDocument() override;
   NS_IMETHOD BeginPage() override { return NS_OK; }
   NS_IMETHOD EndPage() override { return NS_OK; }
 
+  NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) override;
+
 protected:
   virtual ~nsDeviceContextSpecGTK();
   nsCOMPtr<nsPrintSettingsGTK> mPrintSettings;
   bool mToPrinter : 1;      /* If true, print to printer */
   bool mIsPPreview : 1;     /* If true, is print preview */
   char   mPath[PATH_MAX];     /* If toPrinter = false, dest file */
   char   mPrinter[256];       /* Printer name */
   GtkPrintSettings* mGtkPrintSettings;
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -225,8 +225,15 @@ NS_IMETHODIMP
 nsDeviceContextSpecProxy::EndPage()
 {
   // Send the page recording to the parent.
   mRecorder->Close();
   mRemotePrintJob->ProcessPage(mRecordingFileName);
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsDeviceContextSpecProxy::PrintPDF(const nsAString& aPDFFilePath)
+{
+  mRemotePrintJob->PrintPDF(nsString(aPDFFilePath));
+  return NS_OK;
+}
--- a/widget/nsDeviceContextSpecProxy.h
+++ b/widget/nsDeviceContextSpecProxy.h
@@ -49,16 +49,18 @@ public:
   NS_IMETHOD EndDocument() final;
 
   NS_IMETHOD AbortDocument() final;
 
   NS_IMETHOD BeginPage() final;
 
   NS_IMETHOD EndPage() final;
 
+  NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) final;
+
 private:
   ~nsDeviceContextSpecProxy() {}
 
   nsresult CreateUniqueTempPath(nsACString& aFilePath);
 
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsIPrintSession> mPrintSession;
   nsCOMPtr<nsIDeviceContextSpec> mRealDeviceContextSpec;
--- a/widget/nsIDeviceContextSpec.h
+++ b/widget/nsIDeviceContextSpec.h
@@ -73,13 +73,15 @@ public:
                             const nsAString& aPrintToFileName,
                             int32_t          aStartPage,
                             int32_t          aEndPage) = 0;
 
    NS_IMETHOD EndDocument() = 0;
    NS_IMETHOD AbortDocument() { return EndDocument(); }
    NS_IMETHOD BeginPage() = 0;
    NS_IMETHOD EndPage() = 0;
+
+   NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDeviceContextSpec,
                               NS_IDEVICE_CONTEXT_SPEC_IID)
 #endif
--- a/widget/windows/nsDeviceContextSpecWin.h
+++ b/widget/windows/nsDeviceContextSpecWin.h
@@ -28,16 +28,20 @@ public:
   NS_IMETHOD BeginDocument(const nsAString& aTitle,
                            const nsAString& aPrintToFileName,
                            int32_t          aStartPage,
                            int32_t          aEndPage) override { return NS_OK; }
   NS_IMETHOD EndDocument() override { return NS_OK; }
   NS_IMETHOD BeginPage() override { return NS_OK; }
   NS_IMETHOD EndPage() override { return NS_OK; }
 
+  NS_IMETHOD PrintPDF(const nsAString& aPDFFilePath) override {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
   NS_IMETHOD Init(nsIWidget* aWidget, nsIPrintSettings* aPS, bool aIsPrintPreview) override;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
 
   void GetDriverName(wchar_t *&aDriverName) const   { aDriverName = mDriverName;     }
   void GetDeviceName(wchar_t *&aDeviceName) const   { aDeviceName = mDeviceName;     }