Bug 1189846 Part 9: Add ability to print from the parent process with settings and progress listener. r=jimm, r=smaug
authorBob Owen <bobowencode@gmail.com>
Mon, 16 May 2016 10:40:54 +0100
changeset 297525 5324db36f2395612f56751de883bff47916c6e27
parent 297524 a68ad13d4957b7e8fdbac532104ee20bac2f2c8d
child 297526 54ff8765b3d2c3383a05d321a755653fb009746d
push id30262
push userkwierso@gmail.com
push dateMon, 16 May 2016 21:19:59 +0000
treeherdermozilla-central@a884b96685aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, smaug
bugs1189846
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1189846 Part 9: Add ability to print from the parent process with settings and progress listener. r=jimm, r=smaug MozReview-Commit-ID: 7IEMByPmC0n
dom/base/nsFrameLoader.cpp
dom/base/nsIFrameLoader.idl
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
embedding/components/printingui/ipc/PrintingParent.cpp
embedding/components/printingui/ipc/PrintingParent.h
layout/printing/nsPrintEngine.cpp
toolkit/content/widgets/browser.xml
widget/nsPrintOptionsImpl.cpp
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -98,16 +98,21 @@
 #include "mozilla/WebBrowserPersistLocalDocument.h"
 
 #include "nsPrincipal.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
+#ifdef NS_PRINTING
+#include "mozilla/embedding/printingui/PrintingParent.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::hal;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
@@ -3097,16 +3102,56 @@ nsFrameLoader::RequestNotifyLayerTreeCle
     new AsyncEventDispatcher(mOwnerContent,
                              NS_LITERAL_STRING("MozLayerTreeCleared"),
                              true, false);
   event->PostDOMEvent();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::Print(nsIPrintSettings* aPrintSettings,
+                     nsIWebProgressListener* aProgressListener)
+{
+#if defined(NS_PRINTING)
+  if (mRemoteBrowser) {
+    RefPtr<embedding::PrintingParent> printingParent =
+      mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
+
+    embedding::PrintData printData;
+    nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob(
+      aPrintSettings, aProgressListener, nullptr, &printData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool success = mRemoteBrowser->SendPrint(printData);
+    return success ? NS_OK : NS_ERROR_FAILURE;
+  }
+
+  if (mDocShell) {
+    nsCOMPtr<nsIContentViewer> viewer;
+    mDocShell->GetContentViewer(getter_AddRefs(viewer));
+    if (!viewer) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(viewer);
+    if (!webBrowserPrint) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return webBrowserPrint->Print(aPrintSettings, aProgressListener);
+  }
+
+  return NS_ERROR_FAILURE;
+#endif
+  return NS_OK;
+}
+
 /* [infallible] */ NS_IMETHODIMP
 nsFrameLoader::SetVisible(bool aVisible)
 {
   if (mVisible == aVisible) {
     return NS_OK;
   }
 
   mVisible = aVisible;
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -11,16 +11,18 @@ interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
 interface nsITabParent;
 interface nsILoadContext;
+interface nsIPrintSettings;
+interface nsIWebProgressListener;
 
 [scriptable, builtinclass, uuid(1645af04-1bc7-4363-8f2c-eb9679220ab1)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
@@ -135,16 +137,26 @@ interface nsIFrameLoader : nsISupports
    * Request an event when the layer tree from the remote tab becomes
    * available or unavailable. When this happens, a mozLayerTreeReady
    * or mozLayerTreeCleared event is fired.
    */
   void requestNotifyLayerTreeReady();
   void requestNotifyLayerTreeCleared();
 
   /**
+   * Print the current document.
+   *
+   * @param aPrintSettings optional print settings to use; printSilent can be
+   *                       set to prevent prompting.
+   * @param aProgressListener optional print progress listener.
+   */
+  void print(in nsIPrintSettings aPrintSettings,
+             in nsIWebProgressListener aProgressListener);
+
+  /**
    * The default event mode automatically forwards the events
    * handled in EventStateManager::HandleCrossProcessEvent to
    * the child content process when these events are targeted to
    * the remote browser element.
    *
    * Used primarly for input events (mouse, keyboard)
    */
   const unsigned long EVENT_MODE_NORMAL_DISPATCH = 0x00000000;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3841,16 +3841,27 @@ ContentParent::DeallocPPrintingParent(PP
 
   mPrintingParent = nullptr;
 #else
   MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing.");
 #endif
   return true;
 }
 
+#ifdef NS_PRINTING
+already_AddRefed<embedding::PrintingParent>
+ContentParent::GetPrintingParent()
+{
+  MOZ_ASSERT(mPrintingParent);
+
+  RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
+  return printingParent.forget();
+}
+#endif
+
 PSendStreamParent*
 ContentParent::AllocPSendStreamParent()
 {
   return mozilla::ipc::AllocPSendStreamParent();
 }
 
 bool
 ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -381,16 +381,23 @@ public:
   {
     return PContentParent::RecvPNeckoConstructor(aActor);
   }
 
   virtual PPrintingParent* AllocPPrintingParent() override;
 
   virtual bool DeallocPPrintingParent(PPrintingParent* aActor) override;
 
+#if defined(NS_PRINTING)
+  /**
+   * @return the PrintingParent for this ContentParent.
+   */
+  already_AddRefed<embedding::PrintingParent> GetPrintingParent();
+#endif
+
   virtual PSendStreamParent* AllocPSendStreamParent() override;
   virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
 
   virtual PScreenManagerParent*
   AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                             float* aSystemDefaultScale,
                             bool* aSuccess) override;
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -10,20 +10,22 @@ include protocol PColorPicker;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PDocAccessible;
 include protocol PDocumentRenderer;
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
+include protocol PRemotePrintJob;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
 include BrowserConfiguration;
+include PPrintingTypes;
 include PTabContext;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using CSSSize from "Units.h";
@@ -777,16 +779,23 @@ child:
      * @param aKeyEventData      The key event which was posted to the parent
      *                           process.
      * @param aIsConsumed        true if aKeyEventData is consumed in the
      *                           parent process.  Otherwise, false.
      */
     async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
                                         bool aIsConsumed);
 
+    /**
+     * Tell the child to print the current page with the given settings.
+     *
+     * @param aPrintData the serialized settings to print with
+     */
+    async Print(PrintData aPrintData);
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -107,16 +107,23 @@
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIURILoader.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
 #include "FrameLayerBuilder.h"
 
+#ifdef NS_PRINTING
+#include "nsIPrintSession.h"
+#include "nsIPrintSettings.h"
+#include "nsIPrintSettingsService.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -2431,16 +2438,55 @@ TabChild::RecvSetUseGlobalHistory(const 
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to set UseGlobalHistory on TabChild docShell");
   }
 
   return true;
 }
 
 bool
+TabChild::RecvPrint(const PrintData& aPrintData)
+{
+#ifdef NS_PRINTING
+  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_GetInterface(mWebNav);
+  if (NS_WARN_IF(!webBrowserPrint)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+    do_GetService("@mozilla.org/gfx/printsettings-service;1");
+  if (NS_WARN_IF(!printSettingsSvc)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSettings> printSettings;
+  nsresult rv =
+    printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSession>  printSession =
+    do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  printSettings->SetPrintSession(printSession);
+  printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+  rv = webBrowserPrint->Print(printSettings, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+#endif
+  return true;
+}
+
+bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
   nsTArray<PContentPermissionRequestChild*> childArray =
       nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -575,16 +575,18 @@ public:
                                                   const bool& aMuted) override;
 
   virtual bool RecvSetUseGlobalHistory(const bool& aUse) override;
 
   virtual bool RecvHandledWindowedPluginKeyEvent(
                  const mozilla::NativeEventData& aKeyEventData,
                  const bool& aIsConsumed) override;
 
+  virtual bool RecvPrint(const PrintData& aPrintData) override;
+
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   PPluginWidgetChild* AllocPPluginWidgetChild() override;
 
   bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
 
   nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
--- a/embedding/components/printingui/ipc/PrintingParent.cpp
+++ b/embedding/components/printingui/ipc/PrintingParent.cpp
@@ -6,17 +6,16 @@
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIPrintingPromptService.h"
-#include "nsIPrintOptions.h"
 #include "nsIPrintProgressParams.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIServiceManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIWebProgressListener.h"
 #include "PrintingParent.h"
 #include "PrintDataUtils.h"
 #include "PrintProgressDialogParent.h"
@@ -94,36 +93,32 @@ PrintingParent::ShowPrintDialog(PBrowser
     return NS_ERROR_FAILURE;
   }
 
   // The initSettings we got can be wrapped using
   // PrintDataUtils' MockWebBrowserPrint, which implements enough of
   // nsIWebBrowserPrint to keep the dialogs happy.
   nsCOMPtr<nsIWebBrowserPrint> wbp = new MockWebBrowserPrint(aData);
 
-  nsresult rv;
-  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
-    do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
+  nsCOMPtr<nsIPrintSettings> settings;
+  nsresult rv = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIPrintSettings> settings;
-  rv = printSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = printSettingsSvc->DeserializeToPrintSettings(aData, settings);
+  rv = mPrintSettingsSvc->DeserializeToPrintSettings(aData, settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = pps->ShowPrintDialog(parentWin, wbp, settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // And send it back.
-  rv = printSettingsSvc->SerializeToPrintData(settings, nullptr, aResult);
-
-  PRemotePrintJobParent* remotePrintJob = new RemotePrintJobParent(settings);
-  aResult->remotePrintJobParent() = SendPRemotePrintJobConstructor(remotePrintJob);
+  // Serialize back to aResult. Use the existing RemotePrintJob if we have one
+  // otherwise SerializeAndEnsureRemotePrintJob() will create a new one.
+  RemotePrintJobParent* remotePrintJob =
+    static_cast<RemotePrintJobParent*>(aData.remotePrintJobParent());
+  rv = SerializeAndEnsureRemotePrintJob(settings, nullptr, remotePrintJob,
+                                        aResult);
 
   return rv;
 }
 
 bool
 PrintingParent::RecvShowPrintDialog(PPrintSettingsDialogParent* aDialog,
                                     PBrowserParent* aParent,
                                     const PrintData& aData)
@@ -144,28 +139,24 @@ PrintingParent::RecvShowPrintDialog(PPri
 }
 
 bool
 PrintingParent::RecvSavePrintSettings(const PrintData& aData,
                                       const bool& aUsePrinterNamePrefix,
                                       const uint32_t& aFlags,
                                       nsresult* aResult)
 {
-  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
-    do_GetService("@mozilla.org/gfx/printsettings-service;1", aResult);
+  nsCOMPtr<nsIPrintSettings> settings;
+  *aResult = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
   NS_ENSURE_SUCCESS(*aResult, true);
 
-  nsCOMPtr<nsIPrintSettings> settings;
-  *aResult = printSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
+  *aResult = mPrintSettingsSvc->DeserializeToPrintSettings(aData, settings);
   NS_ENSURE_SUCCESS(*aResult, true);
 
-  *aResult = printSettingsSvc->DeserializeToPrintSettings(aData, settings);
-  NS_ENSURE_SUCCESS(*aResult, true);
-
-  *aResult = printSettingsSvc->SavePrintSettingsToPrefs(settings,
+  *aResult = mPrintSettingsSvc->SavePrintSettingsToPrefs(settings,
                                                         aUsePrinterNamePrefix,
                                                         aFlags);
 
   return true;
 }
 
 PPrintProgressDialogParent*
 PrintingParent::AllocPPrintProgressDialogParent()
@@ -244,21 +235,65 @@ PrintingParent::DOMWindowFromBrowserPare
   nsCOMPtr<nsPIDOMWindowOuter> parentWin = frame->OwnerDoc()->GetWindow();
   if (!parentWin) {
     return nullptr;
   }
 
   return parentWin;
 }
 
+nsresult
+PrintingParent::SerializeAndEnsureRemotePrintJob(
+  nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aListener,
+  layout::RemotePrintJobParent* aRemotePrintJob, PrintData* aPrintData)
+{
+  MOZ_ASSERT(aPrintData);
+
+  nsresult rv;
+  nsCOMPtr<nsIPrintSettings> printSettings;
+  if (aPrintSettings) {
+    printSettings = aPrintSettings;
+  } else {
+    rv = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = mPrintSettingsSvc->SerializeToPrintData(aPrintSettings, nullptr,
+                                               aPrintData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  RemotePrintJobParent* remotePrintJob;
+  if (aRemotePrintJob) {
+    remotePrintJob = aRemotePrintJob;
+    aPrintData->remotePrintJobParent() = remotePrintJob;
+  } else {
+    remotePrintJob = new RemotePrintJobParent(aPrintSettings);
+    aPrintData->remotePrintJobParent() =
+      SendPRemotePrintJobConstructor(remotePrintJob);
+  }
+  if (aListener) {
+    remotePrintJob->RegisterListener(aListener);
+  }
+
+  return NS_OK;
+}
+
 PrintingParent::PrintingParent()
 {
-    MOZ_COUNT_CTOR(PrintingParent);
+  MOZ_COUNT_CTOR(PrintingParent);
+
+  mPrintSettingsSvc =
+    do_GetService("@mozilla.org/gfx/printsettings-service;1");
+  MOZ_ASSERT(mPrintSettingsSvc);
 }
 
 PrintingParent::~PrintingParent()
 {
-    MOZ_COUNT_DTOR(PrintingParent);
+  MOZ_COUNT_DTOR(PrintingParent);
 }
 
 } // namespace embedding
 } // namespace mozilla
 
--- a/embedding/components/printingui/ipc/PrintingParent.h
+++ b/embedding/components/printingui/ipc/PrintingParent.h
@@ -5,23 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_embedding_PrintingParent_h
 #define mozilla_embedding_PrintingParent_h
 
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/embedding/PPrintingParent.h"
 
+class nsIPrintSettingsService;
+class nsIWebProgressListener;
 class nsPIDOMWindowOuter;
 class PPrintProgressDialogParent;
 class PPrintSettingsDialogParent;
 
 namespace mozilla {
 namespace layout {
 class PRemotePrintJobParent;
+class RemotePrintJobParent;
 }
 
 namespace embedding {
 
 class PrintingParent final : public PPrintingParent
 {
 public:
     NS_INLINE_DECL_REFCOUNTING(PrintingParent)
@@ -62,24 +65,45 @@ public:
     virtual bool
     DeallocPRemotePrintJobParent(PRemotePrintJobParent* aActor);
 
     virtual void
     ActorDestroy(ActorDestroyReason aWhy);
 
     MOZ_IMPLICIT PrintingParent();
 
+    /**
+     * Serialize nsIPrintSettings to PrintData ready for sending to a child
+     * process. A RemotePrintJob will be created and added to the PrintData.
+     * An optional progress listener can be given, which will be registered
+     * with the RemotePrintJob, so that progress can be tracked in the parent.
+     *
+     * @param aPrintSettings optional print settings to serialize, otherwise a
+     *                       default print settings will be used.
+     * @param aProgressListener optional print progress listener.
+     * @param aRemotePrintJob optional remote print job, so that an existing
+     *                        one can be used.
+     * @param aPrintData PrintData to populate.
+     */
+    nsresult
+    SerializeAndEnsureRemotePrintJob(nsIPrintSettings* aPrintSettings,
+                                     nsIWebProgressListener* aListener,
+                                     layout::RemotePrintJobParent* aRemotePrintJob,
+                                     PrintData* aPrintData);
+
 private:
     virtual ~PrintingParent();
 
     nsPIDOMWindowOuter*
     DOMWindowFromBrowserParent(PBrowserParent* parent);
 
     nsresult
     ShowPrintDialog(PBrowserParent* parent,
                     const PrintData& data,
                     PrintData* result);
+
+    nsCOMPtr<nsIPrintSettingsService> mPrintSettingsSvc;
 };
 
 } // namespace embedding
 } // namespace mozilla
 
 #endif
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -470,24 +470,39 @@ nsPrintEngine::DoCommonPrint(bool       
     if (viewer) {
       viewer->SetTextZoom(1.0f);
       viewer->SetFullZoom(1.0f);
       viewer->SetMinFontSize(0);
     }
   }
 
   // Create a print session and let the print settings know about it.
+  // Don't overwrite an existing print session.
   // The print settings hold an nsWeakPtr to the session so it does not
   // need to be cleared from the settings at the end of the job.
   // XXX What lifetime does the printSession need to have?
   nsCOMPtr<nsIPrintSession> printSession;
+  bool remotePrintJobListening = false;
   if (!aIsPrintPreview) {
-    printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mPrt->mPrintSettings->SetPrintSession(printSession);
+    rv = mPrt->mPrintSettings->GetPrintSession(getter_AddRefs(printSession));
+    if (NS_FAILED(rv) || !printSession) {
+      printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      mPrt->mPrintSettings->SetPrintSession(printSession);
+    } else {
+      RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+      printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+      if (NS_SUCCEEDED(rv) && remotePrintJob) {
+        // If we have a RemotePrintJob add it to the print progress listeners,
+        // so it can forward to the parent.
+        mPrt->mPrintProgressListeners.AppendElement(remotePrintJob);
+        remotePrintJobListening = true;
+      }
+    }
+
   }
 
   if (aWebProgressListener != nullptr) {
     mPrt->mPrintProgressListeners.AppendObject(aWebProgressListener);
   }
 
   // Get the currently focused window and cache it
   // because the Print Dialog will "steal" focus and later when you try
@@ -608,21 +623,25 @@ nsPrintEngine::DoCommonPrint(bool       
           // since we got the dialog and it worked then make sure we 
           // are telling GFX we want to print silent
           printSilently = true;
 
           if (mPrt->mPrintSettings) {
             // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
             mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
 
-            // Add thee RemotePrintJob as a listener if there is one.
-            RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
-            printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
-            if (NS_SUCCEEDED(rv) && remotePrintJob) {
-              mPrt->mPrintProgressListeners.AppendElement(remotePrintJob);
+            // If we haven't already added the RemotePrintJob as a listener,
+            // add it now if there is one.
+            if (!remotePrintJobListening) {
+              RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+              printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+              if (NS_SUCCEEDED(rv) && remotePrintJob) {
+                mPrt->mPrintProgressListeners.AppendElement(remotePrintJob);
+                remotePrintJobListening = true;
+              }
             }
           }
         } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
           // This means the Dialog service was there,
           // but they choose not to implement this dialog and
           // are looking for default behavior from the toolkit
           rv = NS_OK;
         }
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1321,16 +1321,32 @@
           if (!this.docShell || !this.docShell.contentViewer) {
             return true;
           }
           return {permitUnload: this.docShell.contentViewer.permitUnload(), timedOut: false};
         ]]>
         </body>
       </method>
 
+      <method name="print">
+        <parameter name="aPrintSettings"/>
+        <parameter name="aPrintProgressListener"/>
+        <body>
+          <![CDATA[
+            var owner = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
+            if (!owner.frameLoader) {
+              throw Components.Exception("No frame loader.",
+                                         Components.results.NS_ERROR_FAILURE);
+            }
+
+            owner.frameLoader.print(aPrintSettings, aPrintProgressListener);
+          ]]>
+        </body>
+      </method>
+
       <!-- This will go away if the binding has been removed for some reason. -->
       <field name="_alive">true</field>
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_F7" group="system">
         <![CDATA[
           if (event.defaultPrevented || !event.isTrusted)
--- a/widget/nsPrintOptionsImpl.cpp
+++ b/widget/nsPrintOptionsImpl.cpp
@@ -100,16 +100,26 @@ nsPrintOptions::Init()
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings,
                                      nsIWebBrowserPrint* aWBP,
                                      PrintData* data)
 {
+  nsCOMPtr<nsIPrintSession> session;
+  nsresult rv = aSettings->GetPrintSession(getter_AddRefs(session));
+  if (NS_SUCCEEDED(rv) && session) {
+    RefPtr<RemotePrintJobChild> remotePrintJob;
+    rv = session->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+    if (NS_SUCCEEDED(rv)) {
+      data->remotePrintJobChild() = remotePrintJob;
+    }
+  }
+
   aSettings->GetStartPageRange(&data->startPageRange());
   aSettings->GetEndPageRange(&data->endPageRange());
 
   aSettings->GetEdgeTop(&data->edgeTop());
   aSettings->GetEdgeLeft(&data->edgeLeft());
   aSettings->GetEdgeBottom(&data->edgeBottom());
   aSettings->GetEdgeRight(&data->edgeRight());