From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/general/browser_double_close_tab.js | 120 +++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 browser/base/content/test/general/browser_double_close_tab.js (limited to 'browser/base/content/test/general/browser_double_close_tab.js') diff --git a/browser/base/content/test/general/browser_double_close_tab.js b/browser/base/content/test/general/browser_double_close_tab.js new file mode 100644 index 0000000000..554aeb8077 --- /dev/null +++ b/browser/base/content/test/general/browser_double_close_tab.js @@ -0,0 +1,120 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +"use strict"; +const TEST_PAGE = + "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; +var testTab; + +const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); + +function waitForDialog(callback) { + function onDialogLoaded(nodeOrDialogWindow) { + let node = CONTENT_PROMPT_SUBDIALOG + ? nodeOrDialogWindow.document.querySelector("dialog") + : nodeOrDialogWindow; + Services.obs.removeObserver(onDialogLoaded, "tabmodal-dialog-loaded"); + Services.obs.removeObserver(onDialogLoaded, "common-dialog-loaded"); + // Allow dialog's onLoad call to run to completion + Promise.resolve().then(() => callback(node)); + } + + // Listen for the dialog being created + Services.obs.addObserver(onDialogLoaded, "tabmodal-dialog-loaded"); + Services.obs.addObserver(onDialogLoaded, "common-dialog-loaded"); +} + +function waitForDialogDestroyed(node, callback) { + // Now listen for the dialog going away again... + let observer = new MutationObserver(function (muts) { + if (!node.parentNode) { + ok(true, "Dialog is gone"); + done(); + } + }); + observer.observe(node.parentNode, { childList: true }); + + if (CONTENT_PROMPT_SUBDIALOG) { + node.ownerGlobal.addEventListener("unload", done); + } + + let failureTimeout = setTimeout(function () { + ok(false, "Dialog should have been destroyed"); + done(); + }, 10000); + + function done() { + clearTimeout(failureTimeout); + observer.disconnect(); + observer = null; + + if (CONTENT_PROMPT_SUBDIALOG) { + node.ownerGlobal.removeEventListener("unload", done); + SimpleTest.executeSoon(callback); + } else { + callback(); + } + } +} + +add_task(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + testTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); + + // XXXgijs the reason this has nesting and callbacks rather than promises is + // that DOM promises resolve on the next tick. So they're scheduled + // in an event queue. So when we spin a new event queue for a modal dialog... + // everything gets messed up and the promise's .then callbacks never get + // called, despite resolve() being called just fine. + await new Promise(resolveOuter => { + waitForDialog(dialogNode => { + waitForDialogDestroyed(dialogNode, () => { + let doCompletion = () => setTimeout(resolveOuter, 0); + info("Now checking if dialog is destroyed"); + + if (CONTENT_PROMPT_SUBDIALOG) { + ok( + !dialogNode.ownerGlobal || dialogNode.ownerGlobal.closed, + "onbeforeunload dialog should be gone." + ); + if (dialogNode.ownerGlobal && !dialogNode.ownerGlobal.closed) { + dialogNode.acceptDialog(); + } + } else { + ok(!dialogNode.parentNode, "onbeforeunload dialog should be gone."); + if (dialogNode.parentNode) { + // Failed to remove onbeforeunload dialog, so do it ourselves: + let leaveBtn = dialogNode.querySelector(".tabmodalprompt-button0"); + waitForDialogDestroyed(dialogNode, doCompletion); + EventUtils.synthesizeMouseAtCenter(leaveBtn, {}); + return; + } + } + + doCompletion(); + }); + // Click again: + testTab.closeButton.click(); + }); + // Click once: + testTab.closeButton.click(); + }); + await TestUtils.waitForCondition(() => !testTab.parentNode); + ok(!testTab.parentNode, "Tab should be closed completely"); +}); + +registerCleanupFunction(async function () { + if (testTab.parentNode) { + // Remove the handler, or closing this tab will prove tricky: + try { + await SpecialPowers.spawn(testTab.linkedBrowser, [], function () { + content.window.onbeforeunload = null; + }); + } catch (ex) {} + gBrowser.removeTab(testTab); + } +}); -- cgit v1.2.3