Bug 1399787 - Part 11.a. Use PrintTargetEMF to print content documents. r=jwatt
authorcku <cku@mozilla.com>
Sun, 05 Nov 2017 04:36:37 +0800
changeset 396014 716a2077fc0cb1ec78a405d2eb313d319e10dd48
parent 396013 bb67ef211bf28257906d081372d09a09320329d5
child 396015 72a4344a41971d2a11af812a80c04346a1778557
push id33067
push usertoros@mozilla.com
push dateMon, 11 Dec 2017 09:54:39 +0000
treeherdermozilla-central@6d926a50dcf5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1399787
milestone59.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 1399787 - Part 11.a. Use PrintTargetEMF to print content documents. r=jwatt Before we introduce PrintTargetEMF, all PrintTargets finish page printing task before the end of PrintTarget::EndPage(). Unlike others, a page printing in PrintTargetEMF is done after receiving an async callback from the pdfium process. So we have both async and sync page printing behavior now. This patch is trying to make both of them work correctly while priting a content document. MozReview-Commit-ID: 2PHJToFlvtu
gfx/src/nsDeviceContext.cpp
gfx/src/nsDeviceContext.h
gfx/thebes/PrintTarget.cpp
gfx/thebes/PrintTarget.h
gfx/thebes/PrintTargetEMF.cpp
gfx/thebes/PrintTargetEMF.h
layout/printing/ipc/RemotePrintJobParent.cpp
layout/printing/ipc/RemotePrintJobParent.h
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -730,8 +730,29 @@ nsDeviceContext::GetDesktopToDeviceScale
     if (screen) {
         double scale;
         screen->GetContentsScaleFactor(&scale);
         return DesktopToLayoutDeviceScale(scale);
     }
 
     return DesktopToLayoutDeviceScale(1.0);
 }
+
+bool
+nsDeviceContext::IsSyncPagePrinting() const
+{
+  MOZ_ASSERT(mPrintTarget);
+  return mPrintTarget->IsSyncPagePrinting();
+}
+
+void
+nsDeviceContext::RegisterPageDoneCallback(PrintTarget::PageDoneCallback&& aCallback)
+{
+  MOZ_ASSERT(mPrintTarget && aCallback && !IsSyncPagePrinting());
+  mPrintTarget->RegisterPageDoneCallback(Move(aCallback));
+}
+void
+nsDeviceContext::UnregisterPageDoneCallback()
+{
+  if (mPrintTarget) {
+    mPrintTarget->UnregisterPageDoneCallback();
+  }
+}
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -15,35 +15,30 @@
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
 #include "nsISupports.h"                // for NS_INLINE_DECL_REFCOUNTING
 #include "nsMathUtils.h"                // for NS_round
 #include "nscore.h"                     // for char16_t, nsAString
 #include "mozilla/AppUnits.h"           // for AppUnits
 #include "nsFontMetrics.h"              // for nsFontMetrics::Params
+#include "mozilla/gfx/PrintTarget.h"    // for PrintTarget::PageDoneCallback
 
 class gfxContext;
 class gfxTextPerfMetrics;
 class gfxUserFontSet;
 struct nsFont;
 class nsFontCache;
 class nsAtom;
 class nsIDeviceContextSpec;
 class nsIScreen;
 class nsIScreenManager;
 class nsIWidget;
 struct nsRect;
 
-namespace mozilla {
-namespace gfx {
-class PrintTarget;
-}
-}
-
 class nsDeviceContext final
 {
 public:
     typedef mozilla::gfx::PrintTarget PrintTarget;
 
     nsDeviceContext();
 
     NS_INLINE_DECL_REFCOUNTING(nsDeviceContext)
@@ -275,16 +270,19 @@ public:
 
     /**
      * True if this device context was created for printing.
      */
     bool IsPrinterContext();
 
     mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale();
 
+    bool IsSyncPagePrinting() const;
+    void RegisterPageDoneCallback(PrintTarget::PageDoneCallback&& aCallback);
+    void UnregisterPageDoneCallback();
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~nsDeviceContext();
 
     /**
      * Implementation shared by CreateRenderingContext and
      * CreateReferenceRenderingContext.
      */
--- a/gfx/thebes/PrintTarget.cpp
+++ b/gfx/thebes/PrintTarget.cpp
@@ -224,10 +224,23 @@ PrintTarget::Finish()
     return;
   }
   mIsFinished = true;
 
   // null surfaces are allowed here
   cairo_surface_finish(mCairoSurface);
 }
 
+void
+PrintTarget::RegisterPageDoneCallback(PageDoneCallback&& aCallback)
+{
+  MOZ_ASSERT(aCallback && !IsSyncPagePrinting());
+  mPageDoneCallback = Move(aCallback);
+}
+
+void
+PrintTarget::UnregisterPageDoneCallback()
+{
+  mPageDoneCallback = nullptr;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/PrintTarget.h
+++ b/gfx/thebes/PrintTarget.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PRINTTARGET_H
 #define MOZILLA_GFX_PRINTTARGET_H
 
+#include <functional>
+
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/2D.h"
 #include "nsISupportsImpl.h"
 #include "nsStringFwd.h"
 
 namespace mozilla {
 namespace gfx {
 
@@ -21,16 +23,17 @@ class DrawEventRecorder;
  * preview.
  *
  * This class wraps a cairo_surface_t* and provides access to it via a
  * DrawTarget.  The various checkpointing methods manage the state of the
  * platform specific cairo_surface_t*.
  */
 class PrintTarget {
 public:
+  typedef std::function<void(nsresult)> PageDoneCallback;
 
   NS_INLINE_DECL_REFCOUNTING(PrintTarget);
 
   /// Must be matched 1:1 by an EndPrinting/AbortPrinting call.
   virtual nsresult BeginPrinting(const nsAString& aTitle,
                                  const nsAString& aPrintToFileName,
                                  int32_t aStartPage,
                                  int32_t aEndPage) {
@@ -131,16 +134,27 @@ public:
   /**
    * Returns a reference DrawTarget. Unlike MakeDrawTarget, this method is not
    * restricted to being called between BeginPage()/EndPage() calls, and the
    * returned DrawTarget it is still valid to use after EndPage() has been
    * called.
    */
   virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder);
 
+  /**
+   * If IsSyncPagePrinting returns true, then a user can assume the content of
+   * a page was already printed after EndPage().
+   * If IsSyncPagePrinting returns false, then a user should register a
+   * callback function using RegisterPageDoneCallback to receive page print
+   * done notifications.
+   */
+  virtual bool IsSyncPagePrinting() const { return true; }
+  void RegisterPageDoneCallback(PageDoneCallback&& aCallback);
+  void UnregisterPageDoneCallback();
+
   static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
                                        nsCString& aAdjustedJobName);
   static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
                                        nsString& aAdjustedJobName);
 
 protected:
 
   // Only created via subclass's constructors
@@ -164,14 +178,16 @@ protected:
 
   IntSize mSize;
   bool mIsFinished;
 #ifdef DEBUG
   bool mHasActivePage;
   // owned by mRecordingRefDT, so kept alive for our entire lifetime if set:
   DrawEventRecorder* mRecorder;
 #endif
+
+  PageDoneCallback mPageDoneCallback;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_PRINTTARGET_H */
--- a/gfx/thebes/PrintTargetEMF.cpp
+++ b/gfx/thebes/PrintTargetEMF.cpp
@@ -171,13 +171,15 @@ PrintTargetEMF::ConvertToEMFDone(const n
     }
 
     mPDFiumProcess->GetActor()->DeallocShmem(aEMF);
   }
 
   mPDFFileForOnePage->Remove(/* aRecursive */ false);
   mPDFFileForOnePage = nullptr;
 
-  // TBD: We should call RemotePrintJobChild::SendPageProcessed here.
+  if (mPageDoneCallback) {
+    mPageDoneCallback(aResult);
+  }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/PrintTargetEMF.h
+++ b/gfx/thebes/PrintTargetEMF.h
@@ -50,16 +50,17 @@ public:
   already_AddRefed<DrawTarget>
   MakeDrawTarget(const IntSize& aSize,
                  DrawEventRecorder* aRecorder = nullptr) final;
 
   already_AddRefed<DrawTarget>
   GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
 
   void ConvertToEMFDone(const nsresult& aResult, mozilla::ipc::Shmem&& aEMF);
+  bool IsSyncPagePrinting() const final { return false; }
 
 private:
   PrintTargetEMF(HDC aDC, const IntSize& aSize);
   ~PrintTargetEMF() override;
 
   nsString mTitle;
   RefPtr<PrintTargetSkPDF> mTargetForCurrentPage;
   nsCOMPtr<nsIFile>        mPDFFileForOnePage;
--- a/layout/printing/ipc/RemotePrintJobParent.cpp
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -83,16 +83,20 @@ RemotePrintJobParent::InitializePrintDev
   }
 
   rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, aPrintToFile,
                                           aStartPage, aEndPage);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (!mPrintDeviceContext->IsSyncPagePrinting()) {
+    mPrintDeviceContext->RegisterPageDoneCallback([this](nsresult aResult) { PageDone(aResult); });
+  }
+
   return NS_OK;
 }
 
 nsresult
 RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd)
 {
   PRFileDesc* prFd = nullptr;
   nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd);
@@ -111,29 +115,20 @@ RemotePrintJobParent::RecvProcessPage()
   if (!mCurrentPageStream.IsOpen()) {
     Unused << SendAbortPrint(NS_ERROR_FAILURE);
     return IPC_OK();
   }
   mCurrentPageStream.Seek(0, PR_SEEK_SET);
   nsresult rv = PrintPage(mCurrentPageStream);
   mCurrentPageStream.Close();
 
-  if (NS_FAILED(rv)) {
-    Unused << SendAbortPrint(rv);
-    return IPC_OK();
+  if (mPrintDeviceContext->IsSyncPagePrinting()) {
+    PageDone(rv);
   }
 
-  FileDescriptor fd;
-  rv = PrepareNextPageFD(&fd);
-  if (NS_FAILED(rv)) {
-    Unused << SendAbortPrint(rv);
-    return IPC_OK();
-  }
-
-  Unused << SendPageProcessed(fd);
   return IPC_OK();
 }
 
 nsresult
 RemotePrintJobParent::PrintPage(PRFileDescStream& aRecording)
 {
   MOZ_ASSERT(mPrintDeviceContext);
 
@@ -148,38 +143,59 @@ RemotePrintJobParent::PrintPage(PRFileDe
   rv = mPrintDeviceContext->EndPage();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+void
+RemotePrintJobParent::PageDone(nsresult aResult)
+{
+  if (NS_FAILED(aResult)) {
+    Unused << SendAbortPrint(aResult);
+  } else {
+    FileDescriptor fd;
+    aResult = PrepareNextPageFD(&fd);
+    if (NS_FAILED(aResult)) {
+      Unused << SendAbortPrint(aResult);
+    }
+
+    Unused << SendPageProcessed(fd);
+  }
+}
+
 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.
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EndDocument failed");
+
+    // Since RecvFinalizePrint is called after all page printed, there should
+    // be no more page-done callbacks after that, in theory. Unregistering
+    // page-done callback is not must have, but we still do this for safety.
+    mPrintDeviceContext->UnregisterPageDoneCallback();
   }
 
-
   Unused << Send__delete__(this);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 RemotePrintJobParent::RecvAbortPrint(const nsresult& aRv)
 {
   if (mPrintDeviceContext) {
     Unused << mPrintDeviceContext->AbortDocument();
+    mPrintDeviceContext->UnregisterPageDoneCallback();
   }
 
   Unused << Send__delete__(this);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 RemotePrintJobParent::RecvStateChange(const long& aStateFlags,
@@ -241,12 +257,15 @@ RemotePrintJobParent::GetPrintSettings()
 RemotePrintJobParent::~RemotePrintJobParent()
 {
   MOZ_COUNT_DTOR(RemotePrintJobParent);
 }
 
 void
 RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  if (mPrintDeviceContext) {
+    mPrintDeviceContext->UnregisterPageDoneCallback();
+  }
 }
 
 } // namespace layout
 } // namespace mozilla
--- a/layout/printing/ipc/RemotePrintJobParent.h
+++ b/layout/printing/ipc/RemotePrintJobParent.h
@@ -71,16 +71,22 @@ private:
                                  const nsString& aPrintToFile,
                                  const int32_t& aStartPage,
                                  const int32_t& aEndPage);
 
   nsresult PrepareNextPageFD(FileDescriptor* aFd);
 
   nsresult PrintPage(PRFileDescStream& aRecording);
 
+  /**
+   * Called to notify our corresponding RemotePrintJobChild once we've
+   * finished printing a page.
+   */
+  void PageDone(nsresult aResult);
+
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   RefPtr<nsDeviceContext> mPrintDeviceContext;
   UniquePtr<PrintTranslator> mPrintTranslator;
   nsCOMArray<nsIWebProgressListener> mPrintProgressListeners;
   PRFileDescStream mCurrentPageStream;
 };
 
 } // namespace layout