Bug 1648064 - Make print preview documents wait properly for the document to be loaded. r=smaug

Otherwise my test fails intermittently on CI. We need to block on all
the load blockers because stuff like responsive images doesn't fire the
load directly but they do that as a micro task (blocking the load
event).

Differential Revision: https://phabricator.services.mozilla.com/D81989
This commit is contained in:
Emilio Cobos Álvarez 2020-07-22 14:39:47 +00:00
Родитель c1dbabcffa
Коммит 636792f039
4 изменённых файлов: 81 добавлений и 54 удалений

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

@ -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();
}

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

@ -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 =
'<iframe style="position: fixed; visibility: hidden; bottom: 10em;"></iframe>' +
'<input contenteditable="true" style="display: table; page-break-before: left; width: 10000px;">';
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 = "<a href='#'>mozilla</a><input>test<select><option>option1</option></select>";
// Create normal content
frameElts[0].contentDocument.body.innerHTML =
"<div>" + contentText + "</div>";
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 = "<div></div>";
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() {
</svg>
`;
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 <use> 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 <use> 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); } );
}

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

@ -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<nsIWebProgress> webProgress =
do_QueryInterface(printData->mPrintObject->mDocShell);
webProgress->AddProgressListener(static_cast<nsIWebProgressListener*>(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<nsPrintData> 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<nsILoadGroup> 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;
}

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

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