Bug 1412456 - Fix onbeforeunload test to no longer rely on interposition (r=Gijs)

MozReview-Commit-ID: DlIRAvXw6R4
This commit is contained in:
Bill McCloskey 2017-10-25 14:38:13 -07:00
Родитель be77cf4a01
Коммит 32656e2e1c
1 изменённых файлов: 164 добавлений и 124 удалений

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

@ -1,38 +1,23 @@
var contentWindow;
var originalLocation;
var currentTest = -1;
var stayingOnPage = true;
function contentTask() {
let finish;
let promise = new Promise(resolve => { finish = resolve; });
var TEST_PAGE = "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html";
var TARGETED_PAGE = "data:text/html," + encodeURIComponent("<body>Shouldn't be seeing this</body>");
let contentWindow;
let originalLocation;
let currentTest = -1;
let stayingOnPage = true;
SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
const TEST_PAGE = "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html";
const TARGETED_PAGE = "data:text/html," + encodeURIComponent("<body>Shouldn't be seeing this</body>");
var loadExpected = TEST_PAGE;
var testTab;
let loadExpected = TEST_PAGE;
var testsLength;
var loadStarted = false;
var tabStateListener = {
onStateChange: function(webprogress, request, stateFlags, status) {
let startDocumentFlags = Ci.nsIWebProgressListener.STATE_START |
Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
if ((stateFlags & startDocumentFlags) == startDocumentFlags) {
loadStarted = true;
}
},
onStatusChange: () => {},
onLocationChange: () => {},
onSecurityChange: () => {},
onProgressChange: () => {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener])
};
function onTabLoaded(event) {
info("A document loaded in a tab!");
let loadedPage = event.target.location.href;
if (loadedPage == "about:blank" ||
event.originalTarget != testTab.linkedBrowser.contentDocument) {
event.originalTarget != content.document) {
return;
}
@ -42,7 +27,7 @@ function onTabLoaded(event) {
}
if (!testsLength) {
testsLength = testTab.linkedBrowser.contentWindow.wrappedJSObject.testFns.length;
testsLength = content.wrappedJSObject.testFns.length;
}
is(loadedPage, loadExpected, "Loaded the expected page");
@ -56,14 +41,12 @@ function onTabLoaded(event) {
function onAfterTargetedPageLoad() {
ok(!stayingOnPage, "We should only fire if we're expecting to let the onbeforeunload dialog proceed to the new location");
is(testTab.linkedBrowser.currentURI.spec, TARGETED_PAGE, "Should have loaded the expected new page");
is(content.location.href, TARGETED_PAGE, "Should have loaded the expected new page");
runNextTest();
}
function onTabModalDialogLoaded(node) {
let content = testTab.linkedBrowser.contentWindow;
ok(!loadStarted, "No load should be started.");
function onTabModalDialogLoaded() {
info(content.location.href);
is(content, contentWindow, "Window should be the same still.");
is(content.location.href, originalLocation, "Page should not have changed.");
@ -72,25 +55,17 @@ function onTabModalDialogLoaded(node) {
ok(!content.dialogWasInvoked, "Dialog should only be invoked once per test.");
content.dialogWasInvoked = true;
addMessageListener("test-beforeunload:dialog-gone", function listener(msg) {
removeMessageListener(msg.name, listener);
// Now listen for the dialog going away again...
let observer = new MutationObserver(function(muts) {
if (!node.parentNode) {
info("Dialog is gone");
observer.disconnect();
observer = null;
// If we're staying on the page, run the next test from here
if (stayingOnPage) {
// Evil, but necessary: without this delay, we manage to still break our
// own onbeforeunload code, because we'll basically cause a new load to be
// started while processing the destruction of the dialog for the old one.
executeSoon(runNextTest);
Services.tm.dispatchToMainThread(runNextTest);
}
// if we accepted a page load in the dialog, the next test will get started
// by the load handler for that page loading
}
});
observer.observe(node.parentNode, {childList: true});
// If we're going to let the page load, set us up to listen for that happening:
if (!stayingOnPage) {
@ -98,14 +73,11 @@ function onTabModalDialogLoaded(node) {
onAfterPageLoad = onAfterTargetedPageLoad;
}
let button = stayingOnPage ? node.ui.button1 : node.ui.button0;
// ... and then actually make the dialog go away
info("Clicking button: " + button.label);
EventUtils.synthesizeMouseAtCenter(button, {});
sendAsyncMessage("test-beforeunload:dialog-response", stayingOnPage);
}
// Listen for the dialog being created
Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
addMessageListener("test-beforeunload:dialog", onTabModalDialogLoaded);
function runNextTest() {
currentTest++;
@ -130,7 +102,7 @@ function runNextTest() {
onAfterPageLoad = runCurrentTest;
loadExpected = TEST_PAGE;
testTab.linkedBrowser.loadURI(TEST_PAGE);
content.location = TEST_PAGE;
} else {
runCurrentTest();
}
@ -138,29 +110,24 @@ function runNextTest() {
function runCurrentTest() {
// Reset things so we're sure the previous tests failings don't influence this one:
contentWindow = testTab.linkedBrowser.contentWindow;
contentWindow = content;
contentWindow.mySuperSpecialMark = 42;
contentWindow.dialogWasInvoked = false;
originalLocation = contentWindow.location.href;
// And run this test:
info("Running test with onbeforeunload " + contentWindow.wrappedJSObject.testFns[currentTest].toSource());
contentWindow.onbeforeunload = contentWindow.wrappedJSObject.testFns[currentTest];
loadStarted = false;
testTab.linkedBrowser.loadURI(TARGETED_PAGE);
sendAsyncMessage("test-beforeunload:reset");
content.location = TARGETED_PAGE;
}
var onAfterPageLoad = runNextTest;
function test() {
waitForExplicitFinish();
gBrowser.addProgressListener(tabStateListener);
addEventListener("load", onTabLoaded, true);
testTab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
testTab.linkedBrowser.addEventListener("load", onTabLoaded, true);
testTab.linkedBrowser.loadURI(TEST_PAGE);
}
content.location = TEST_PAGE;
registerCleanupFunction(function() {
return promise.then(() => {
// Remove the handler, or closing this tab will prove tricky:
if (contentWindow) {
try {
@ -168,7 +135,80 @@ registerCleanupFunction(function() {
} catch (ex) {}
}
contentWindow = null;
testTab.linkedBrowser.removeEventListener("load", onTabLoaded, true);
});
}
SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
var testTab;
var loadStarted = false;
var tabStateListener = {
onStateChange: function(webprogress, request, stateFlags, status) {
let startDocumentFlags = Ci.nsIWebProgressListener.STATE_START |
Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
if ((stateFlags & startDocumentFlags) == startDocumentFlags) {
loadStarted = true;
}
},
onStatusChange: () => {},
onLocationChange: () => {},
onSecurityChange: () => {},
onProgressChange: () => {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener])
};
function onTabModalDialogLoaded(node) {
let mm = testTab.linkedBrowser.messageManager;
mm.sendAsyncMessage("test-beforeunload:dialog");
if (gMultiProcessBrowser) {
// In non-e10s, onTabModalDialogLoaded fires synchronously while
// the test-beforeunload:reset message is sent
// asynchronously. It's easier to simply disable this assertion in
// non-e10s than to make everything work correctly in both
// configurations.
ok(!loadStarted, "No load should be started.");
}
// Now listen for the dialog going away again...
let observer = new MutationObserver(function(muts) {
if (!node.parentNode) {
observer.disconnect();
observer = null;
Services.tm.dispatchToMainThread(() => {
mm.sendAsyncMessage("test-beforeunload:dialog-gone");
});
}
});
observer.observe(node.parentNode, {childList: true});
BrowserTestUtils.waitForMessage(mm, "test-beforeunload:dialog-response").then((stayingOnPage) => {
let button = stayingOnPage ? node.ui.button1 : node.ui.button0;
// ... and then actually make the dialog go away
info("Clicking button: " + button.label);
EventUtils.synthesizeMouseAtCenter(button, {});
});
}
// Listen for the dialog being created
Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
function test() {
waitForExplicitFinish();
gBrowser.addProgressListener(tabStateListener);
testTab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
testTab.linkedBrowser.messageManager.addMessageListener("test-beforeunload:reset", () => {
loadStarted = false;
});
ContentTask.spawn(testTab.linkedBrowser, null, contentTask).then(finish);
}
registerCleanupFunction(function() {
Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
gBrowser.removeProgressListener(tabStateListener);
gBrowser.removeTab(testTab);