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
This commit is contained in:
Jonathan Watt 2020-07-11 14:14:03 +00:00
Родитель c5e2dceec6
Коммит eee2d4c223
2 изменённых файлов: 32 добавлений и 86 удалений

Просмотреть файл

@ -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<nsIWebProgressListener> mCachedPrintWebProgressListner;
RefPtr<nsPrintJob> mPrintJob;
UniquePtr<AutoPrintEventDispatcher> 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<nsCOMPtr<Document>>& 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<nsCOMPtr<Document>> targets;
if (mTop) {
CollectDocuments(*mTop, targets);
}
for (nsCOMPtr<Document>& doc : targets) {
nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent,
CanBubble::eNo, Cancelable::eNo,
nullptr);
}
}
nsCOMPtr<Document> mTop;
};
} // namespace mozilla
class nsDocumentShownDispatcher : public Runnable {
public:
explicit nsDocumentShownDispatcher(nsCOMPtr<Document> 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<AutoPrintEventDispatcher>(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<Document> 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<AutoPrintEventDispatcher> autoBeforeAndAfterPrint;
if (!mAutoBeforeAndAfterPrint) {
autoBeforeAndAfterPrint = MakeUnique<AutoPrintEventDispatcher>(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.

Просмотреть файл

@ -168,6 +168,26 @@ static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
# define DUMP_DOC_TREELAYOUT
#endif
static CallState CollectDocuments(Document& aDoc,
nsTArray<nsCOMPtr<Document>>& 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<nsCOMPtr<Document>> targets;
CollectDocuments(aDoc, targets);
for (nsCOMPtr<Document>& 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<nsIDocShell> 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<nsPrintObject>();
@ -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