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
--- 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