From eee2d4c2230fe19764e252330d4a501c34a04ba5 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Sat, 11 Jul 2020 14:14:03 +0000 Subject: [PATCH] Bug 1557983. Significantly simplify and fix the 'beforeprint'/'afterprint' dispatching code. This fixes two issue. First, the code shouldn't be dispatching these events every time it gets a new Print or PrintPreview call. It only needs to dispatch the events to the original document that we're going to clone from. When cloning from existing static clones any changes made by 'beforeprint' will be present in the existing static clone. Second, the code tries to delay the 'afterprint' event until after mozPrintCallback callbacks have been invoked, but those callbacks are invoked in the cloned document, whereas the events are sent to the original document! So there is no reason to do this. Differential Revision: https://phabricator.services.mozilla.com/D34280 --- layout/base/nsDocumentViewer.cpp | 86 -------------------------------- layout/printing/nsPrintJob.cpp | 32 ++++++++++++ 2 files changed, 32 insertions(+), 86 deletions(-) diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 59b8bcfc4386..46cc1399a6e6 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -149,9 +149,6 @@ static mozilla::LazyLogModule gPrintingLog("printing"); //----------------------------------------------------- class nsDocumentViewer; -namespace mozilla { -class AutoPrintEventDispatcher; -} // a small delegate class used to avoid circular references @@ -467,7 +464,6 @@ class nsDocumentViewer final : public nsIContentViewer, nsCOMPtr mCachedPrintWebProgressListner; RefPtr mPrintJob; - UniquePtr mAutoBeforeAndAfterPrint; # endif // NS_PRINT_PREVIEW #endif // NS_PRINTING @@ -481,50 +477,6 @@ class nsDocumentViewer final : public nsIContentViewer, bool mHidden; }; -namespace mozilla { - -/** - * A RAII class for automatic dispatch of the 'beforeprint' and 'afterprint' - * events ('beforeprint' on construction, 'afterprint' on destruction). - * - * https://developer.mozilla.org/en-US/docs/Web/Events/beforeprint - * https://developer.mozilla.org/en-US/docs/Web/Events/afterprint - */ -class AutoPrintEventDispatcher { - public: - explicit AutoPrintEventDispatcher(Document* aTop) : mTop(aTop) { - DispatchEventToWindowTree(u"beforeprint"_ns); - } - ~AutoPrintEventDispatcher() { DispatchEventToWindowTree(u"afterprint"_ns); } - - private: - static CallState CollectDocuments(Document& aDoc, - nsTArray>& aDocs) { - aDocs.AppendElement(&aDoc); - auto recurse = [&aDocs](Document& aSubDoc) { - return CollectDocuments(aSubDoc, aDocs); - }; - aDoc.EnumerateSubDocuments(recurse); - return CallState::Continue; - } - - void DispatchEventToWindowTree(const nsAString& aEvent) { - nsTArray> targets; - if (mTop) { - CollectDocuments(*mTop, targets); - } - for (nsCOMPtr& doc : targets) { - nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent, - CanBubble::eNo, Cancelable::eNo, - nullptr); - } - } - - nsCOMPtr mTop; -}; - -} // namespace mozilla - class nsDocumentShownDispatcher : public Runnable { public: explicit nsDocumentShownDispatcher(nsCOMPtr aDocument) @@ -1660,8 +1612,6 @@ nsDocumentViewer::Destroy() { return NS_OK; } } - // Dispatch the 'afterprint' event now, if pending: - mAutoBeforeAndAfterPrint = nullptr; #endif // We want to make sure to disconnect mBFCachePreventionObserver before we @@ -3205,11 +3155,6 @@ nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings, return rv; } - // Dispatch 'beforeprint' event and ensure 'afterprint' will be dispatched: - MOZ_ASSERT(!mAutoBeforeAndAfterPrint, - "We don't want to dispatch nested beforeprint/afterprint"); - auto autoBeforeAndAfterPrint = - MakeUnique(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. @@ -3233,11 +3178,6 @@ nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings, } mPrintJob = printJob; } - if (printJob->HasPrintCallbackCanvas()) { - // Postpone the 'afterprint' event until after the mozPrintCallback - // callbacks have been called: - mAutoBeforeAndAfterPrint = std::move(autoBeforeAndAfterPrint); - } rv = printJob->Print(mDocument, aPrintSettings, aWebProgressListener); if (NS_FAILED(rv)) { OnDonePrinting(); @@ -3273,18 +3213,6 @@ nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings, nsCOMPtr doc = window->GetDoc(); NS_ENSURE_STATE(doc); - // Dispatch 'beforeprint' event and ensure 'afterprint' will be dispatched: - // XXX Currently[1] when the user switches between portrait and landscape - // mode in print preview, we re-enter this function before - // mAutoBeforeAndAfterPrint (if set) is cleared to dispatch the 'afterprint' - // event. To avoid sending multiple 'beforeprint'/'afterprint' events we - // must avoid creating a new AutoPrintEventDispatcher object here if we - // already have one saved in mAutoBeforeAndAfterPrint. - // [1] Until PDF.js is removed (though, maybe after that as well). - UniquePtr autoBeforeAndAfterPrint; - if (!mAutoBeforeAndAfterPrint) { - autoBeforeAndAfterPrint = MakeUnique(doc); - } NS_ENSURE_STATE(!GetIsPrinting()); // beforeprint event may have caused ContentViewer to be shutdown. NS_ENSURE_STATE(mContainer); @@ -3308,11 +3236,6 @@ nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings, Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_PREVIEW_OPENED, 1); } - if (autoBeforeAndAfterPrint && printJob->HasPrintCallbackCanvas()) { - // Postpone the 'afterprint' event until after the mozPrintCallback - // callbacks have been called: - mAutoBeforeAndAfterPrint = std::move(autoBeforeAndAfterPrint); - } rv = printJob->PrintPreview(doc, aPrintSettings, aWebProgressListener); if (NS_FAILED(rv)) { OnDonePrinting(); @@ -3647,11 +3570,6 @@ void nsDocumentViewer::SetIsPrinting(bool aIsPrinting) { } else { NS_WARNING("Did you close a window before printing?"); } - - if (!aIsPrinting) { - // Dispatch the 'afterprint' event now, if pending: - mAutoBeforeAndAfterPrint = nullptr; - } #endif } @@ -3678,10 +3596,6 @@ void nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview) { if (docShell || !aIsPrintPreview) { SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true); } - if (!aIsPrintPreview) { - // Dispatch the 'afterprint' event now, if pending: - mAutoBeforeAndAfterPrint = nullptr; - } #endif // Protect against pres shell destruction running scripts. diff --git a/layout/printing/nsPrintJob.cpp b/layout/printing/nsPrintJob.cpp index b5cae9d83fbe..6563a7be6215 100644 --- a/layout/printing/nsPrintJob.cpp +++ b/layout/printing/nsPrintJob.cpp @@ -168,6 +168,26 @@ static void DumpPrintObjectsTreeLayout(const UniquePtr& aPO, # define DUMP_DOC_TREELAYOUT #endif +static CallState CollectDocuments(Document& aDoc, + nsTArray>& aDocs) { + aDocs.AppendElement(&aDoc); + auto recurse = [&aDocs](Document& aSubDoc) { + return CollectDocuments(aSubDoc, aDocs); + }; + aDoc.EnumerateSubDocuments(recurse); + return CallState::Continue; +} + +static void DispatchEventToWindowTree(Document& aDoc, const nsAString& aEvent) { + nsTArray> targets; + CollectDocuments(aDoc, targets); + for (nsCOMPtr& doc : targets) { + nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent, + CanBubble::eNo, Cancelable::eNo, + nullptr); + } +} + class nsScriptSuppressor { public: explicit nsScriptSuppressor(nsPrintJob* aPrintJob) @@ -663,6 +683,14 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, nsCOMPtr docShell(do_QueryReferent(mDocShell, &rv)); NS_ENSURE_SUCCESS(rv, rv); + if (!aSourceDoc->IsStaticDocument()) { + // This is the original document. We must send 'beforeprint' and + // 'afterprint' events to give the document the chance to make changes + // for print output. (Obviously if `aSourceDoc` is a clone, it already + // has these changes.) + DispatchEventToWindowTree(*aSourceDoc, u"beforeprint"_ns); + } + { nsAutoScriptBlocker scriptBlocker; printData->mPrintObject = MakeUnique(); @@ -680,6 +708,10 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, printData->mPrintObject, &printData->mPrintDocList); } + if (!aSourceDoc->IsStaticDocument()) { + DispatchEventToWindowTree(*aSourceDoc, u"afterprint"_ns); + } + // The nsAutoScriptBlocker above will now have been destroyed, which may // cause our print/print-preview operation to finish. In this case, we // should immediately return an error code so that the root caller knows