diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 1353b11bd219..c48bb197c7d8 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -10765,6 +10765,8 @@ void Document::DoUnblockOnload() { if (mAsyncOnloadBlockCount != 0) { // We need to wait until the async onload block has been handled. + // + // FIXME(emilio): Shouldn't we return here?? PostUnblockOnloadEvent(); } diff --git a/layout/base/tests/chrome/printpreview_helper.xhtml b/layout/base/tests/chrome/printpreview_helper.xhtml index 8dde9a2412ea..c5cf93629cb9 100644 --- a/layout/base/tests/chrome/printpreview_helper.xhtml +++ b/layout/base/tests/chrome/printpreview_helper.xhtml @@ -16,6 +16,7 @@ var is = window.arguments[0].is; var isnot = window.arguments[0].isnot; var ok = window.arguments[0].ok; var todo = window.arguments[0].todo; +var info = window.arguments[0].info; var SimpleTest = window.arguments[0].SimpleTest; var gWbp; var ctx1; @@ -29,15 +30,28 @@ filePath = file.path; function printpreview(hasMozPrintCallback) { gWbp = frameElts[1].docShell.initOrReusePrintPreviewViewer(); + let resolve; + let promise = new Promise(r => { resolve = r }); var listener = { onLocationChange: function(webProgress, request, location, flags) { }, onProgressChange: function(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, - maxTotalProgress) { }, + maxTotalProgress) { + info("onProgressChange", [...arguments].join(", ")); + }, onSecurityChange: function(webProgress, request, state) { }, - onStateChange: function(webProgress, request, stateFlags, status) { }, - onStatusChange: function(webProgress, request, status, message) { }, - onContentBlockingEvent: function(webProgress, request, event) { }, + onStateChange: function(webProgress, request, stateFlags, status) { + info("onStateChange", [...arguments].join(", ")); + if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + setTimeout(resolve, 0); + } + }, + onStatusChange: function(webProgress, request, status, message) { + info("onStatusChange", [...arguments].join(", ")); + }, + onContentBlockingEvent: function(webProgress, request, event) { + info("onContentBlockingEvent", [...arguments].join(", ")); + }, QueryInterface: function(iid) { if (iid.equals(Ci.nsIWebProgressListener) || iid.equals(Ci.nsISupportsWeakReference)) @@ -64,6 +78,7 @@ function printpreview(hasMozPrintCallback) { } frameElts[0].contentWindow.removeEventListener("beforeprint", beforeprint, true); frameElts[0].contentWindow.removeEventListener("afterprint", afterprint, true); + return promise; } function exitprintpreview() { @@ -124,7 +139,7 @@ function startTest1() { frameElts[0].contentWindow.setTimeout(frameElts[0].contentWindow.counterTimeout, 0); frameElts[0].contentDocument.body.firstChild.innerHTML = "Print preview"; - printpreview(); + let ppfinished = printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); frameElts[0].contentDocument.body.firstChild.innerHTML = "Galley presentation"; @@ -135,10 +150,13 @@ function startTest1() { // And readd. addHTMLContent(frameElts[0].contentDocument.body.lastChild); - setTimeout(finalizeTest1, 1000); + setTimeout(function() { + finalizeTest1(ppfinished) + }, 1000); } -function finalizeTest1() { +async function finalizeTest1(ppfinished) { + await ppfinished; ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); exitprintpreview(); ok(compareCanvases(), "Canvas should be the same!"); @@ -204,17 +222,17 @@ function runTest3() { setTimeout(runTest4, 0) } -function compareFormElementPrint(el1, el2, equals) { +async function compareFormElementPrint(el1, el2, equals) { frameElts[0].contentDocument.body.innerHTML = el1; frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); - printpreview(); + await printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); exitprintpreview(); frameElts[0].contentDocument.body.innerHTML = el2; frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); - printpreview(); + await printpreview(); ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)"); exitprintpreview(); is(compareCanvases(), equals, @@ -229,19 +247,19 @@ function runTest4() { setTimeout(runTest4end, 500); } -function runTest4end() { - printpreview(); +async function runTest4end() { + await printpreview(); exitprintpreview(); runTest5(); } // This is a crash test for bug 595337 -function runTest5() { +async function runTest5() { frameElts[0].contentDocument.body.innerHTML = '' + ''; - printpreview(); + await printpreview(); exitprintpreview(); setTimeout(runTest6, 0); @@ -256,28 +274,28 @@ function runTest6() { setTimeout(runTest6end, 500); } -function runTest6end() { - printpreview(); +async function runTest6end() { + await printpreview(); exitprintpreview(); requestAnimationFrame(function() { setTimeout(runTest7); } ); } -function runTest7() { +async function runTest7() { var contentText = "mozillatest"; // Create normal content frameElts[0].contentDocument.body.innerHTML = "
" + contentText + "
"; frameElts[0].contentDocument.body.firstChild.value = frameElts[0].contentDocument.body.firstChild.getAttribute('value'); - printpreview(); + await printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); frameElts[0].contentDocument.body.innerHTML = "
"; var sr = frameElts[0].contentDocument.body.firstChild.attachShadow({mode: "open"}); sr.innerHTML = contentText; - printpreview(); + await printpreview(); ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); ok(compareCanvases(), "Printing light DOM and shadow DOM should create same output"); @@ -294,7 +312,7 @@ async function runTest8() { iframeElement.addEventListener("load", resolve, { capture: true, once: true }); iframeElement.setAttribute("src", "printpreview_font_api_ref.html"); }); - printpreview(); + await printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); @@ -303,7 +321,7 @@ async function runTest8() { iframeElement.addEventListener("message", resolve, { capture: true, once: true }); iframeElement.setAttribute("src", "printpreview_font_api.html"); }); - printpreview(); + await printpreview(); ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); ok(compareCanvases(), "Printing pages with fonts loaded from CSS and JS should be the same."); @@ -319,7 +337,7 @@ async function runTest9() { `; - printpreview(); + await printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); @@ -341,7 +359,7 @@ async function runTest9() { // Ensure the shadow tree is created so we test what we want to test. frameElts[0].contentDocument.body.offsetTop; - printpreview(); + await printpreview(); ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); ok(compareCanvases(), "Printing subtrees should create same output"); @@ -363,7 +381,7 @@ async function runTest10() { let mozPrintCallbackDone = new Promise((resolve) => { iframeElement.addEventListener("message", resolve, { capture: true, once: true }); }); - printpreview(true); + await printpreview(true); await mozPrintCallbackDone; ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); @@ -376,7 +394,7 @@ async function runTest10() { mozPrintCallbackDone = new Promise((resolve) => { iframeElement.addEventListener("message", resolve, { capture: true, once: true }); }); - printpreview(true); + await printpreview(true); // Wait for the mozprintcallback to finish. await mozPrintCallbackDone; ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); @@ -388,12 +406,13 @@ async function runTest10() { } async function compareFiles(src1, src2) { + info(`Comparing ${src1} with ${src2}`); const iframeElement = document.getElementsByTagName("iframe")[0]; await new Promise((resolve) => { iframeElement.addEventListener("load", resolve, { capture: true, once: true }); iframeElement.setAttribute("src", src1); }); - printpreview(); + await printpreview(); ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); @@ -402,7 +421,7 @@ async function compareFiles(src1, src2) { iframeElement.setAttribute("src", src2); }); - printpreview(); + await printpreview(); ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)"); exitprintpreview(); @@ -442,8 +461,8 @@ async function runTest15() { // XXX Is there a more reliable way to wait for the background-image to load? await new Promise(resolve => setTimeout(resolve, 500)); - printpreview(); - exitprintpreview(); + await printpreview(); + await exitprintpreview(); requestAnimationFrame(function() { setTimeout(runTest16); } ); } diff --git a/layout/printing/nsPrintJob.cpp b/layout/printing/nsPrintJob.cpp index ffb2f9299f57..c066c9327fd6 100644 --- a/layout/printing/nsPrintJob.cpp +++ b/layout/printing/nsPrintJob.cpp @@ -6,6 +6,7 @@ #include "nsPrintJob.h" +#include "nsDebug.h" #include "nsDocShell.h" #include "nsReadableUtils.h" #include "nsCRT.h" @@ -16,6 +17,7 @@ #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/CustomEvent.h" +#include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/StaticPrefs_print.h" #include "mozilla/Telemetry.h" @@ -24,6 +26,8 @@ #include "nsIScriptGlobalObject.h" #include "nsIStringBundle.h" #include "nsPIDOMWindow.h" +#include "nsPrintData.h" +#include "nsPrintObject.h" #include "nsIDocShell.h" #include "nsIURI.h" #include "nsITextToSubURI.h" @@ -910,14 +914,13 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, } // Attach progressListener to catch network requests. + mDidLoadDataForPrinting = false; + nsCOMPtr webProgress = do_QueryInterface(printData->mPrintObject->mDocShell); webProgress->AddProgressListener(static_cast(this), nsIWebProgress::NOTIFY_STATE_REQUEST); - mLoadCounter = 0; - mDidLoadDataForPrinting = false; - if (mIsCreatingPrintPreview) { bool notifyOnInit = false; ShowPrintProgress(false, notifyOnInit); @@ -1681,18 +1684,33 @@ nsresult nsPrintJob::InitPrintDocConstruction(bool aHandleError) { // Guarantee that mPrt->mPrintObject won't be deleted. It's owned by mPrt. // So, we should grab it with local variable. RefPtr printData = mPrt; + rv = ReflowDocList(printData->mPrintObject, DoSetPixelScale()); NS_ENSURE_SUCCESS(rv, rv); FirePrintPreviewUpdateEvent(); - if (mLoadCounter == 0) { - ResumePrintAfterResourcesLoaded(aHandleError); - } + MaybeResumePrintAfterResourcesLoaded(aHandleError); return rv; } -nsresult nsPrintJob::ResumePrintAfterResourcesLoaded(bool aCleanupOnError) { +bool nsPrintJob::ShouldResumePrint() const { + Document* doc = mPrt->mPrintObject->mDocument; + MOZ_ASSERT(doc); + NS_ENSURE_TRUE(doc, true); + nsCOMPtr lg = doc->GetDocumentLoadGroup(); + NS_ENSURE_TRUE(lg, true); + bool pending = false; + nsresult rv = lg->IsPending(&pending); + NS_ENSURE_SUCCESS(rv, true); + return !pending; +} + +nsresult nsPrintJob::MaybeResumePrintAfterResourcesLoaded(bool aCleanupOnError) { + if (!ShouldResumePrint()) { + mDidLoadDataForPrinting = true; + return NS_OK; + } // If Destroy() has already been called, mPtr is nullptr. Then, the instance // needs to do nothing anymore in this method. // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we @@ -1733,22 +1751,9 @@ nsresult nsPrintJob::ResumePrintAfterResourcesLoaded(bool aCleanupOnError) { MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsPrintJob::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aStateFlags, nsresult aStatus) { - nsAutoCString name; - aRequest->GetName(name); - if (name.EqualsLiteral("about:document-onload-blocker")) { - return NS_OK; - } - if (aStateFlags & STATE_START) { - ++mLoadCounter; - } else if (aStateFlags & STATE_STOP) { - mDidLoadDataForPrinting = true; - --mLoadCounter; - - // If all resources are loaded, then do a small timeout and if there - // are still no new requests, then another reflow. - if (mLoadCounter == 0) { - ResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true); - } + if (aStateFlags & STATE_STOP) { + // If all resources are loaded, then finish and reflow. + MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true); } return NS_OK; } diff --git a/layout/printing/nsPrintJob.h b/layout/printing/nsPrintJob.h index eb2d84ebd39b..3ddb222b2285 100644 --- a/layout/printing/nsPrintJob.h +++ b/layout/printing/nsPrintJob.h @@ -243,7 +243,9 @@ class nsPrintJob final : public nsIObserver, * (if it has a 'print' style sheet, for example). */ MOZ_CAN_RUN_SCRIPT nsresult - ResumePrintAfterResourcesLoaded(bool aCleanupOnError); + MaybeResumePrintAfterResourcesLoaded(bool aCleanupOnError); + + bool ShouldResumePrint() const; nsresult SetRootView(nsPrintObject* aPO, bool& aDoReturn, bool& aDocumentIsTopLevel, nsSize& aAdjSize); @@ -287,7 +289,6 @@ class nsPrintJob final : public nsIObserver, nsPagePrintTimer* mPagePrintTimer = nullptr; float mScreenDPI = 115.0f; - int32_t mLoadCounter = 0; bool mIsCreatingPrintPreview = false; bool mIsDoingPrinting = false;