Bug 1399787 - Part 10. Report and handle errors while generating EMF. draft
authorcku <cku@mozilla.com>
Tue, 24 Oct 2017 11:48:20 +0800
changeset 686702 afed73ca323a4a03c7a4518fdb37b03d9b573eda
parent 686701 5117679f9f4aa97609f991d34be5073f1c36fdd5
child 686703 47a02fba1ee29c9dadbc913b8c0af36453d527d3
child 688809 72cf0d365965f2ef36a200b4c0ad133a4f10abb3
push id86258
push usercku@mozilla.com
push dateThu, 26 Oct 2017 08:49:17 +0000
bugs1399787
milestone58.0a1
Bug 1399787 - Part 10. Report and handle errors while generating EMF. 1. Send EMF conversion result from the pdfium process back to the parent process by PDFium::FinishedEMFConversions(bool aSuccess). 2. When the the parent process receive EMF conversion failure, notify RemotePrintJobParent or nsPrintEngine to popup an dialog of error message. MozReview-Commit-ID: 9nC1PSXLANQ
layout/printing/ipc/RemotePrintJobParent.cpp
layout/printing/nsPrintEngine.cpp
widget/nsDeviceContextSpecProxy.cpp
widget/nsDeviceContextSpecProxy.h
widget/nsIDeviceContextSpec.h
widget/windows/PDFiumChild.cpp
widget/windows/PDFiumParent.cpp
widget/windows/PDFiumParent.h
widget/windows/PPDFium.ipdl
widget/windows/nsDeviceContextSpecWin.cpp
widget/windows/nsDeviceContextSpecWin.h
--- a/layout/printing/ipc/RemotePrintJobParent.cpp
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -66,17 +66,29 @@ RemotePrintJobParent::InitializePrintDev
 {
   nsresult rv;
   nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
   do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+#ifdef MOZ_ENABLE_SKIA_PDF
+  rv = deviceContextSpec->Init(nullptr, mPrintSettings, false,
+                               [this](nsresult aResult) {
+                                 if (NS_FAILED(aResult)) {
+                                   Unused << SendAbortPrint(aResult);
+                                }
+
+                                Unused << Send__delete__(this);
+                              });
+#else
   rv = deviceContextSpec->Init(nullptr, mPrintSettings, false);
+#endif
+
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mPrintDeviceContext = new nsDeviceContext();
   rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -158,18 +170,20 @@ RemotePrintJobParent::RecvFinalizePrint(
   // 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");
   }
 
+#ifndef MOZ_ENABLE_SKIA_PDF
+  Unused << Send__delete__(this);
+#endif
 
-  Unused << Send__delete__(this);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 RemotePrintJobParent::RecvAbortPrint(const nsresult& aRv)
 {
   if (mPrintDeviceContext) {
     Unused << mPrintDeviceContext->AbortDocument();
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -231,17 +231,23 @@ void nsPrintEngine::Destroy()
   mIsDestroying = true;
 
   mPrt = nullptr;
 
 #ifdef NS_PRINT_PREVIEW
   mPrtPreview = nullptr;
   mOldPrtPreview = nullptr;
 #endif
+
+#ifndef MOZ_ENABLE_SKIA_PDF
+  // Do not clear mDocViewerPrint when MOZ_ENABLE_SKIA_PDF is defined, since
+  // we still need it to report EMF conversion status after printing job
+  // was done.
   mDocViewerPrint = nullptr;
+#endif
 }
 
 //-------------------------------------------------------
 void nsPrintEngine::DestroyPrintingData()
 {
   mPrt = nullptr;
 }
 
@@ -659,18 +665,28 @@ nsPrintEngine::DoCommonPrint(bool       
       // Call any code that requires a run of the event loop.
       rv = printData->mPrintSettings->SetupSilentPrinting();
     }
     // Check explicitly for abort because it's expected
     if (rv == NS_ERROR_ABORT)
       return rv;
     NS_ENSURE_SUCCESS(rv, rv);
   }
-
+#ifdef MOZ_ENABLE_SKIA_PDF
+  RefPtr<nsPrintEngine> thisObject = this;
+  rv = devspec->Init(nullptr, printData->mPrintSettings, aIsPrintPreview,
+                     [thisObject](nsresult aResult) {
+                       if (NS_FAILED(aResult)) {
+                         thisObject->CleanupOnFailure(aResult, true);
+                       }
+                     });
+#else
   rv = devspec->Init(nullptr, printData->mPrintSettings, aIsPrintPreview);
+#endif
+
   NS_ENSURE_SUCCESS(rv, rv);
 
   printData->mPrintDC = new nsDeviceContext();
   rv = printData->mPrintDC->InitForPrinting(devspec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aIsPrintPreview) {
     printData->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -26,26 +26,28 @@ using mozilla::Unused;
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec)
 
 NS_IMETHODIMP
 nsDeviceContextSpecProxy::Init(nsIWidget* aWidget,
                                nsIPrintSettings* aPrintSettings,
-                               bool aIsPrintPreview)
+                               bool aIsPrintPreview,
+                               ErrorCallback&& aCallback)
 {
   nsresult rv;
   mRealDeviceContextSpec =
     do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mRealDeviceContextSpec->Init(nullptr, aPrintSettings, aIsPrintPreview);
+  mRealDeviceContextSpec->Init(nullptr, aPrintSettings, aIsPrintPreview,
+                               Move(aCallback));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mRealDeviceContextSpec = nullptr;
     return rv;
   }
 
   mPrintSettings = aPrintSettings;
 
   if (aIsPrintPreview) {
--- a/widget/nsDeviceContextSpecProxy.h
+++ b/widget/nsDeviceContextSpecProxy.h
@@ -23,17 +23,18 @@ class RemotePrintJobChild;
 }
 
 class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD Init(nsIWidget* aWidget, nsIPrintSettings* aPrintSettings,
-                 bool aIsPrintPreview) final;
+                 bool aIsPrintPreview,
+                 ErrorCallback&& aCallback) final;
 
   virtual already_AddRefed<PrintTarget> MakePrintTarget() final;
 
   NS_IMETHOD GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder) final;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
--- a/widget/nsIDeviceContextSpec.h
+++ b/widget/nsIDeviceContextSpec.h
@@ -21,29 +21,32 @@ class PrintTarget;
 #define NS_IDEVICE_CONTEXT_SPEC_IID   \
 { 0xf407cfba, 0xbe28, 0x46c9, \
   { 0x8a, 0xba, 0x04, 0x2d, 0xae, 0xbb, 0x4f, 0x23 } }
 
 class nsIDeviceContextSpec : public nsISupports
 {
 public:
   typedef mozilla::gfx::PrintTarget PrintTarget;
+  typedef std::function<void(nsresult)> ErrorCallback;
 
    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDEVICE_CONTEXT_SPEC_IID)
 
    /**
     * Initialize the device context spec.
     * @param aWidget         A widget a dialog can be hosted in
     * @param aPrintSettings  Print settings for the print operation
     * @param aIsPrintPreview True if creating Spec for PrintPreview
+    * @param aCallback A callback function to report printing error.
     * @return NS_OK or a suitable error code.
     */
    NS_IMETHOD Init(nsIWidget *aWidget,
                    nsIPrintSettings* aPrintSettings,
-                   bool aIsPrintPreview) = 0;
+                   bool aIsPrintPreview,
+                   ErrorCallback&& aCallback = [](nsresult) { }) = 0;
 
    virtual already_AddRefed<PrintTarget> MakePrintTarget() = 0;
 
    /**
     * If required override to return a recorder to record the print.
     *
     * @param aDrawEventRecorder out param for the recorder to use
     * @return NS_OK or a suitable error code
--- a/widget/windows/PDFiumChild.cpp
+++ b/widget/windows/PDFiumChild.cpp
@@ -35,33 +35,38 @@ PDFiumChild::RecvConvertToEMF(const nsSt
                               const int& aPageWidth,
                               const int& aPageHeight)
 {
   MOZ_ASSERT(aPDFFilePath.Length() && aPageWidth != 0 && aPageHeight != 0);
 
   PDFViaEMFPrintHelper helper;
   if (NS_FAILED(helper.OpenDocument(NS_ConvertUTF16toUTF8(aPDFFilePath).get()))) {
     MOZ_ASSERT_UNREACHABLE("Why the parent process passed an invalid PDF?");
-    SendFinishedEMFConversions();
+    SendFinishedEMFConversions(true);
     return IPC_OK();
   }
 
   const int pageCount = helper.GetPageCount();
   if (pageCount <= 0) {
-    SendFinishedEMFConversions();
+    SendFinishedEMFConversions(true);
     return IPC_OK();
   }
 
   nsAutoString destFolder;
   aPDFFilePath.Left(destFolder, aPDFFilePath.RFind("\\"));
   for (int i = 0; i < pageCount; i++) {
     nsAutoString emfFile = destFolder;
     emfFile.AppendPrintf("\\pdfium_%d.emf", i);
-    helper.DrawPageToFile(emfFile.get(), i, aPageWidth, aPageHeight);
+
+    if (!helper.DrawPageToFile(emfFile.get(), i, aPageWidth, aPageHeight)) {
+      SendFinishedEMFConversions(false);
+      return IPC_OK();
+    }
+
     SendDoneConvertingToEMF(emfFile);
   }
 
-  SendFinishedEMFConversions();
+  SendFinishedEMFConversions(true);
   return IPC_OK();
 }
 
 } // namespace widget
 } // namespace mozilla
--- a/widget/windows/PDFiumParent.cpp
+++ b/widget/windows/PDFiumParent.cpp
@@ -11,39 +11,43 @@ namespace widget {
 
 PDFiumParent::PDFiumParent(nsDeviceContextSpecWin* aDevSpec)
   : mDevSpecWin(aDevSpec)
 {
 }
 
 PDFiumParent::~PDFiumParent()
 {
-  Close();
 }
 
 bool
 PDFiumParent::Init(IPC::Channel* aChannel, base::ProcessId aPid)
 {
   if (NS_WARN_IF(!Open(aChannel, aPid))) {
     return false;
   }
 
   return true;
 }
 
 void
 PDFiumParent::ActorDestroy(ActorDestroyReason aWhy)
 {
-  mDevSpecWin->FinishPrint();
+  if (mDevSpecWin) {
+    mDevSpecWin->FinishPrint(false);
+  }
 }
 
 mozilla::ipc::IPCResult
-PDFiumParent::RecvFinishedEMFConversions()
+PDFiumParent::RecvFinishedEMFConversions(const bool& aSuccess)
 {
-  mDevSpecWin->FinishPrint();
+  mDevSpecWin->FinishPrint(aSuccess);
+  mDevSpecWin = nullptr;
+  Close();
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 PDFiumParent::RecvDoneConvertingToEMF(const nsString& aEMFFilePath)
 {
   mDevSpecWin->PrintEMF(aEMFFilePath);
   return IPC_OK();
--- a/widget/windows/PDFiumParent.h
+++ b/widget/windows/PDFiumParent.h
@@ -23,17 +23,17 @@ public:
 
 private:
   ~PDFiumParent();
 
   // PPDFiumParent functions.
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   mozilla::ipc::IPCResult RecvDoneConvertingToEMF(const nsString& aEMFFilePath) override;
-  mozilla::ipc::IPCResult RecvFinishedEMFConversions() override;
+  mozilla::ipc::IPCResult RecvFinishedEMFConversions(const bool& aSuccess) override;
 
   RefPtr<nsDeviceContextSpecWin> mDevSpecWin;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // PDFIUMPARENT_H_
--- a/widget/windows/PPDFium.ipdl
+++ b/widget/windows/PPDFium.ipdl
@@ -19,17 +19,17 @@ parent:
    * once it is done with it.
    **/
   async DoneConvertingToEMF(nsString aEMFFilePath);
 
   /**
    * The PDFium process use this function to notify the parent that all  the
    * pdf to EMF conversion jobs are done.
    **/
-  async FinishedEMFConversions();
+  async FinishedEMFConversions(bool aSuccess);
 
 child:
   /**
    * Start to convert a PDF file into page-base EMF files.
    */
   async ConvertToEMF(nsString aPDFFilePath, int aPageWidth, int aPageHeight);
 };
 
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -131,28 +131,31 @@ nsDeviceContextSpecWin::~nsDeviceContext
 #endif
   // Free them, we won't need them for a while
   GlobalPrinters::GetInstance()->FreeGlobalPrinters();
 }
 
 //----------------------------------------------------------------------------------
 NS_IMETHODIMP nsDeviceContextSpecWin::Init(nsIWidget* aWidget,
                                            nsIPrintSettings* aPrintSettings,
-                                           bool aIsPrintPreview)
+                                           bool aIsPrintPreview,
+                                           ErrorCallback&& aCallback)
 {
   mPrintSettings = aPrintSettings;
 
   nsresult rv = NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
   if (aPrintSettings) {
 #ifdef MOZ_ENABLE_SKIA_PDF
     nsAutoString printViaPdf;
     Preferences::GetString("print.print_via_pdf_encoder", printViaPdf);
     if (printViaPdf.EqualsLiteral("skia-pdf")) {
       mPrintViaSkPDF = true;
     }
+
+    mCallback = Move(aCallback);
 #endif
 
     // If we're in the child and printing via the parent or we're printing to
     // PDF we only need information from the print settings.
     mPrintSettings->GetOutputFormat(&mOutputFormat);
     if ((XRE_IsContentProcess() &&
          Preferences::GetBool("print.print_via_parent")) ||
         mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
@@ -351,22 +354,38 @@ nsDeviceContextSpecWin::GetPrintingScale
   // The print settings will have the resolution stored from the real device.
   int32_t resolution;
   mPrintSettings->GetResolution(&resolution);
   return float(resolution) / GetDPI();
 }
 
 #ifdef MOZ_ENABLE_SKIA_PDF
 void
-nsDeviceContextSpecWin::FinishPrint()
+nsDeviceContextSpecWin::FinishPrintInternal(bool aSuccess)
 {
-  NS_DispatchToMainThread(NewRunnableMethod(
-    "nsDeviceContextSpecWin::CleanupPrintViaPDF",
+  mCallback(aSuccess ? NS_OK : NS_ERROR_FAILURE);
+  CleanupPrintViaPDF();
+
+  if (!aSuccess) {
+    nsCOMPtr<nsIFile> output = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+    if (output) {
+      output->InitWithPath(mOutputFileName);
+      output->Remove(/* aRecursive */ false);
+    }
+  }
+}
+
+void
+nsDeviceContextSpecWin::FinishPrint(bool aSuccess)
+{
+  NS_DispatchToMainThread(NewRunnableMethod<bool>(
+    "nsDeviceContextSpecWin::FinishPrintInternal",
     this,
-    &nsDeviceContextSpecWin::CleanupPrintViaPDF));
+    &nsDeviceContextSpecWin::FinishPrintInternal,
+    aSuccess));
 }
 
 void
 nsDeviceContextSpecWin::CleanupPrintViaPDF()
 {
   if (mPDFTempFile) {
     mPDFTempFile->Remove(/* aRecursive */ false);
     mPDFTempFile = nullptr;
@@ -454,16 +473,18 @@ nsDeviceContextSpecWin::BeginDocument(co
     di.fwType = 0;
 
     if (::StartDocW(mDC, &di) <= 0) {
       // Defer calling CleanupPrintViaPDF() in destructor because PDF temp file
       // is not ready yet.
       return NS_ERROR_FAILURE;
     }
 
+    mOutputFileName = printToFileName;
+
     MOZ_ASSERT(!mPDFiumProcess && mDC);
     mPDFiumProcess = new PDFiumProcessParent();
     NS_ENSURE_TRUE(mPDFiumProcess->Launch(this), NS_ERROR_FAILURE);
   }
 #endif
 
   return NS_OK;
 }
--- a/widget/windows/nsDeviceContextSpecWin.h
+++ b/widget/windows/nsDeviceContextSpecWin.h
@@ -42,17 +42,19 @@ 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 Init(nsIWidget* aWidget, nsIPrintSettings* aPS, bool aIsPrintPreview) override;
+  NS_IMETHOD Init(nsIWidget* aWidget, nsIPrintSettings* aPS,
+                  bool aIsPrintPreview,
+                  ErrorCallback&& aCallback) override;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
 
   void GetDriverName(nsAString& aDriverName) const { aDriverName = mDriverName; }
   void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; }
 
@@ -63,17 +65,17 @@ public:
   void GetDevMode(LPDEVMODEW &aDevMode);
 
   // helper functions
   nsresult GetDataFromPrinter(const nsAString& aName,
                               nsIPrintSettings* aPS = nullptr);
 
 #ifdef MOZ_ENABLE_SKIA_PDF
   void PrintEMF(const nsString& aEMFFilePath);
-  void FinishPrint();
+  void FinishPrint(bool aSuccess);
 #endif
 
 protected:
 
   void SetDeviceName(const nsAString& aDeviceName);
   void SetDriverName(const nsAString& aDriverName);
   void SetDevMode(LPDEVMODEW aDevMode);
 
@@ -83,26 +85,29 @@ protected:
   nsString mDeviceName;
   LPDEVMODEW mDevMode;
 
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   int16_t mOutputFormat = nsIPrintSettings::kOutputFormatNative;
 
 #ifdef MOZ_ENABLE_SKIA_PDF
   void PrintEMFInternal(nsString aEMFFilePath);
+  void FinishPrintInternal(bool aSuccess);
   void CleanupPrintViaPDF();
 
   // This variable is independant of nsIPrintSettings::kOutputFormatPDF.
   // It controls both whether normal printing is done via PDF using Skia and
   // whether print-to-PDF uses Skia.
   bool mPrintViaSkPDF;
   nsCOMPtr<nsIFile> mPDFTempFile;
   HDC mDC;
 
   PDFiumProcessParent* mPDFiumProcess;
+  ErrorCallback mCallback;
+  nsString mOutputFileName;
 #endif
 };
 
 
 //-------------------------------------------------------------------------
 // Printer Enumerator
 //-------------------------------------------------------------------------
 class nsPrinterEnumeratorWin final : public nsIPrinterEnumerator