summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/tabdialogs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/tabdialogs')
-rw-r--r--browser/base/content/test/tabdialogs/.eslintrc.js5
-rw-r--r--browser/base/content/test/tabdialogs/browser.ini10
-rw-r--r--browser/base/content/test/tabdialogs/browser_subdialog_esc.js118
-rw-r--r--browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js76
-rw-r--r--browser/base/content/test/tabdialogs/browser_tabdialogbox_navigation.js162
-rw-r--r--browser/base/content/test/tabdialogs/browser_tabdialogbox_tab_switch_focus.js131
-rw-r--r--browser/base/content/test/tabdialogs/loadDelayedReply.sjs22
-rw-r--r--browser/base/content/test/tabdialogs/subdialog.xhtml33
8 files changed, 557 insertions, 0 deletions
diff --git a/browser/base/content/test/tabdialogs/.eslintrc.js b/browser/base/content/test/tabdialogs/.eslintrc.js
new file mode 100644
index 0000000000..1779fd7f1c
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/browser-test"],
+};
diff --git a/browser/base/content/test/tabdialogs/browser.ini b/browser/base/content/test/tabdialogs/browser.ini
new file mode 100644
index 0000000000..4c3369b022
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+ subdialog.xhtml
+
+[browser_tabdialogbox_content_prompts.js]
+[browser_tabdialogbox_navigation.js]
+[browser_tabdialogbox_tab_switch_focus.js]
+[browser_subdialog_esc.js]
+support-files =
+ loadDelayedReply.sjs
diff --git a/browser/base/content/test/tabdialogs/browser_subdialog_esc.js b/browser/base/content/test/tabdialogs/browser_subdialog_esc.js
new file mode 100644
index 0000000000..5ad3335b50
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/browser_subdialog_esc.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_ROOT_CHROME = getRootDirectory(gTestPath);
+const TEST_DIALOG_PATH = TEST_ROOT_CHROME + "subdialog.xhtml";
+
+const WEB_ROOT = TEST_ROOT_CHROME.replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+const TEST_LOAD_PAGE = WEB_ROOT + "loadDelayedReply.sjs";
+
+/**
+ * Tests that ESC on a SubDialog does not cancel ongoing loads in the parent.
+ */
+add_task(async function test_subdialog_esc_does_not_cancel_load() {
+ await BrowserTestUtils.withNewTab("http://example.com", async function(
+ browser
+ ) {
+ // Start loading a page
+ let loadStartedPromise = BrowserTestUtils.loadURI(browser, TEST_LOAD_PAGE);
+ let loadedPromise = BrowserTestUtils.browserLoaded(browser);
+ await loadStartedPromise;
+
+ // Open a dialog
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let dialogClose = dialogBox.open(TEST_DIALOG_PATH, {
+ keepOpenSameOriginNav: true,
+ });
+
+ let dialogs = dialogBox.getTabDialogManager()._dialogs;
+
+ is(dialogs.length, 1, "Dialog manager has a dialog.");
+
+ info("Waiting for dialogs to open.");
+ await dialogs[0]._dialogReady;
+
+ // Close the dialog with esc key
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ info("Waiting for dialog to close.");
+ await dialogClose;
+
+ info("Triggering load complete");
+ fetch(TEST_LOAD_PAGE, {
+ method: "POST",
+ });
+
+ // Load must complete
+ info("Waiting for load to complete");
+ await loadedPromise;
+ ok(true, "Load completed");
+ });
+});
+
+/**
+ * Tests that ESC on a SubDialog with an open dropdown doesn't close the dialog.
+ */
+add_task(async function test_subdialog_esc_on_dropdown_does_not_close_dialog() {
+ await BrowserTestUtils.withNewTab("http://example.com", async function(
+ browser
+ ) {
+ // Open the test dialog
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let dialogClose = dialogBox.open(TEST_DIALOG_PATH, {
+ keepOpenSameOriginNav: true,
+ });
+
+ let dialogs = dialogBox.getTabDialogManager()._dialogs;
+
+ is(dialogs.length, 1, "Dialog manager has a dialog.");
+
+ let dialog = dialogs[0];
+
+ info("Waiting for dialog to open.");
+ await dialog._dialogReady;
+
+ // Open dropdown
+ let select = dialog._frame.contentDocument.getElementById("select");
+ let shownPromise = BrowserTestUtils.waitForEvent(
+ select,
+ "popupshowing",
+ true
+ );
+
+ info("Opening dropdown");
+ select.focus();
+ EventUtils.synthesizeKey("VK_SPACE", {}, dialog._frame.contentWindow);
+
+ await shownPromise;
+
+ let hiddenPromise = BrowserTestUtils.waitForEvent(
+ select,
+ "popuphiding",
+ true
+ );
+
+ // Race dropdown closing vs SubDialog close
+ let race = Promise.race([
+ hiddenPromise.then(() => true),
+ dialogClose.then(() => false),
+ ]);
+
+ // Close the dropdown with esc key
+ info("Hitting escape key.");
+ await EventUtils.synthesizeKey("KEY_Escape");
+
+ let result = await race;
+ ok(result, "Select closed first");
+
+ await new Promise(resolve => executeSoon(resolve));
+
+ ok(!dialog._isClosing, "Dialog is not closing");
+ ok(dialog._openedURL, "Dialog is open");
+ });
+});
diff --git a/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js b/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js
new file mode 100644
index 0000000000..5544853c91
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const CONTENT_PROMPT_PREF = "prompts.contentPromptSubDialog";
+const TEST_ROOT_CHROME = getRootDirectory(gTestPath);
+const TEST_DIALOG_PATH = TEST_ROOT_CHROME + "subdialog.xhtml";
+const TEST_URL = "data:text/html,<body onload='alert(1)'>";
+
+// Setup.
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[CONTENT_PROMPT_PREF, true]],
+ });
+});
+
+/**
+ * Test that a manager for content prompts is added to tab dialog box.
+ */
+add_task(async function test_tabdialog_content_prompts() {
+ await BrowserTestUtils.withNewTab("http://example.com", async function(
+ browser
+ ) {
+ info("Open a tab prompt.");
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ dialogBox.open(TEST_DIALOG_PATH);
+
+ info("Check the content prompt dialog is only created when needed.");
+ let contentPromptDialog = document.querySelector(".content-prompt-dialog");
+ ok(!contentPromptDialog, "Content prompt dialog should not be created.");
+
+ info("Open a content prompt");
+ dialogBox.open(TEST_DIALOG_PATH, {
+ modalType: Ci.nsIPrompt.MODAL_TYPE_CONTENT,
+ });
+
+ contentPromptDialog = document.querySelector(".content-prompt-dialog");
+ ok(contentPromptDialog, "Content prompt dialog should be created.");
+ let contentPromptManager = dialogBox.getContentDialogManager();
+
+ is(
+ contentPromptManager._dialogs.length,
+ 1,
+ "Content prompt manager should have 1 dialog box."
+ );
+ });
+});
+
+/**
+ * Test that title text is shown in tabmodal alert/confirm/prompt dialogs.
+ */
+add_task(async function test_tabdialog_show_title() {
+ let dialogShown = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "DOMWillOpenModalDialog"
+ );
+
+ await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
+ info("Waiting for dialog to open.");
+ await dialogShown;
+
+ info("Check the title is visible.");
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let contentPromptManager = dialogBox.getContentDialogManager();
+ let dialog = contentPromptManager._dialogs[0];
+
+ info("Waiting for dialog frame to be ready.");
+ await dialog._dialogReady;
+
+ let dialogDoc = dialog._frame.contentWindow.document;
+ let infoTitle = dialogDoc.querySelector("#infoTitle");
+
+ ok(BrowserTestUtils.is_visible(infoTitle), "Title text is visible");
+ });
+});
diff --git a/browser/base/content/test/tabdialogs/browser_tabdialogbox_navigation.js b/browser/base/content/test/tabdialogs/browser_tabdialogbox_navigation.js
new file mode 100644
index 0000000000..7520875afd
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/browser_tabdialogbox_navigation.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_ROOT_CHROME = getRootDirectory(gTestPath);
+const TEST_DIALOG_PATH = TEST_ROOT_CHROME + "subdialog.xhtml";
+
+/**
+ * Tests that all tab dialogs are closed on navigation.
+ */
+add_task(async function test_tabdialogbox_multiple_close_on_nav() {
+ await BrowserTestUtils.withNewTab("https://example.com", async function(
+ browser
+ ) {
+ // Open two dialogs and wait for them to be ready.
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let closedPromises = [
+ dialogBox.open(TEST_DIALOG_PATH),
+ dialogBox.open(TEST_DIALOG_PATH),
+ ];
+
+ let dialogs = dialogBox.getTabDialogManager()._dialogs;
+
+ is(dialogs.length, 2, "Dialog manager has two dialogs.");
+
+ info("Waiting for dialogs to open.");
+ await Promise.all(dialogs.map(dialog => dialog._dialogReady));
+
+ // Navigate to a different page
+ BrowserTestUtils.loadURI(browser, "https://example.org");
+
+ info("Waiting for dialogs to close.");
+ await closedPromises;
+
+ ok(true, "All open dialogs should close on navigation");
+ });
+});
+
+/**
+ * Tests dialog close on navigation triggered by web content.
+ */
+add_task(async function test_tabdialogbox_close_on_content_nav() {
+ await BrowserTestUtils.withNewTab("https://example.com", async function(
+ browser
+ ) {
+ // Open a dialog and wait for it to be ready
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let closedPromise = dialogBox.open(TEST_DIALOG_PATH);
+
+ let dialog = dialogBox.getTabDialogManager()._topDialog;
+
+ is(
+ dialogBox.getTabDialogManager()._dialogs.length,
+ 1,
+ "Dialog manager has one dialog."
+ );
+
+ info("Waiting for dialog to open.");
+ await dialog._dialogReady;
+
+ // Trigger a same origin navigation by the content
+ await ContentTask.spawn(browser, {}, () => {
+ content.location = "http://example.com/1";
+ });
+
+ info("Waiting for dialog to close.");
+ await closedPromise;
+ ok(true, "Dialog should close for same origin navigation by the content.");
+
+ // Open a new dialog
+ closedPromise = dialogBox.open(TEST_DIALOG_PATH, {
+ keepOpenSameOriginNav: true,
+ });
+
+ info("Waiting for dialog to open.");
+ await dialog._dialogReady;
+
+ SimpleTest.requestFlakyTimeout("Waiting to ensure dialog does not close");
+ let race = Promise.race([
+ closedPromise,
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ new Promise(resolve => setTimeout(() => resolve("success"), 1000)),
+ ]);
+
+ // Trigger a same origin navigation by the content
+ await ContentTask.spawn(browser, {}, () => {
+ content.location = "http://example.com/test";
+ });
+
+ is(
+ await race,
+ "success",
+ "Dialog should not close for same origin navigation by the content."
+ );
+
+ // Trigger a cross origin navigation by the content
+ await ContentTask.spawn(browser, {}, () => {
+ content.location = "http://example.org/test2";
+ });
+
+ info("Waiting for dialog to close");
+ await closedPromise;
+
+ ok(true, "Dialog should close for cross origin navigation by the content.");
+ });
+});
+
+/**
+ * Hides a dialog stack and tests that behavior doesn't change. Ensures
+ * navigation triggered by web content still closes all dialogs.
+ */
+add_task(async function test_tabdialogbox_hide() {
+ await BrowserTestUtils.withNewTab("https://example.com", async function(
+ browser
+ ) {
+ // Open a dialog and wait for it to be ready
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let dialogBoxManager = dialogBox.getTabDialogManager();
+ let closedPromises = [
+ dialogBox.open(TEST_DIALOG_PATH),
+ dialogBox.open(TEST_DIALOG_PATH),
+ ];
+
+ let dialogs = dialogBox.getTabDialogManager()._dialogs;
+
+ is(
+ dialogBox.getTabDialogManager()._dialogs.length,
+ 2,
+ "Dialog manager has two dialogs."
+ );
+
+ info("Waiting for dialogs to open.");
+ await Promise.all(dialogs.map(dialog => dialog._dialogReady));
+
+ ok(
+ !BrowserTestUtils.is_hidden(dialogBoxManager._dialogStack),
+ "Dialog stack is showing"
+ );
+
+ dialogBoxManager.hideDialog(browser);
+
+ is(
+ dialogBoxManager._dialogs.length,
+ 2,
+ "Dialog manager still has two dialogs."
+ );
+
+ ok(
+ BrowserTestUtils.is_hidden(dialogBoxManager._dialogStack),
+ "Dialog stack is hidden"
+ );
+
+ // Navigate to a different page
+ BrowserTestUtils.loadURI(browser, "https://example.org");
+
+ info("Waiting for dialogs to close.");
+ await closedPromises;
+
+ ok(true, "All open dialogs should still close on navigation");
+ });
+});
diff --git a/browser/base/content/test/tabdialogs/browser_tabdialogbox_tab_switch_focus.js b/browser/base/content/test/tabdialogs/browser_tabdialogbox_tab_switch_focus.js
new file mode 100644
index 0000000000..2f19d48022
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/browser_tabdialogbox_tab_switch_focus.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_ROOT_CHROME = getRootDirectory(gTestPath);
+const TEST_DIALOG_PATH = TEST_ROOT_CHROME + "subdialog.xhtml";
+
+/**
+ * Tests that tab dialogs are focused when switching tabs.
+ */
+add_task(async function test_tabdialogbox_tab_switch_focus() {
+ // Open 3 tabs
+ let tabPromises = [];
+ for (let i = 0; i < 3; i += 1) {
+ tabPromises.push(
+ BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com",
+ true
+ )
+ );
+ }
+
+ // Wait for tabs to be ready
+ let tabs = await Promise.all(tabPromises);
+
+ // Open subdialog in first two tabs
+ let dialogs = [];
+ for (let i = 0; i < 2; i += 1) {
+ let dialogBox = gBrowser.getTabDialogBox(tabs[i].linkedBrowser);
+ dialogBox.open(TEST_DIALOG_PATH);
+ dialogs.push(dialogBox.getTabDialogManager()._topDialog);
+ }
+
+ // Wait for dialogs to be ready
+ await Promise.all([dialogs[0]._dialogReady, dialogs[1]._dialogReady]);
+
+ // Switch to first tab which has dialog
+ await BrowserTestUtils.switchTab(gBrowser, tabs[0]);
+
+ // The textbox in the dialogs content window should be focused
+ let dialogTextbox = dialogs[0]._frame.contentDocument.querySelector(
+ "#textbox"
+ );
+ is(Services.focus.focusedElement, dialogTextbox, "Dialog textbox is focused");
+
+ // Switch to second tab which has dialog
+ await BrowserTestUtils.switchTab(gBrowser, tabs[1]);
+
+ // The textbox in the dialogs content window should be focused
+ let dialogTextbox2 = dialogs[1]._frame.contentDocument.querySelector(
+ "#textbox"
+ );
+ is(
+ Services.focus.focusedElement,
+ dialogTextbox2,
+ "Dialog2 textbox is focused"
+ );
+
+ // Switch to third tab which does not have a dialog
+ await BrowserTestUtils.switchTab(gBrowser, tabs[2]);
+
+ // Test that content is focused
+ is(
+ Services.focus.focusedElement,
+ tabs[2].linkedBrowser,
+ "Top level browser is focused"
+ );
+
+ // Cleanup
+ tabs.forEach(tab => {
+ BrowserTestUtils.removeTab(tab);
+ });
+});
+
+/**
+ * Tests that other dialogs are still visible if one dialog is hidden.
+ */
+add_task(async function test_tabdialogbox_tab_switch_hidden() {
+ // Open 2 tabs
+ let tabPromises = [];
+ for (let i = 0; i < 2; i += 1) {
+ tabPromises.push(
+ BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com",
+ true
+ )
+ );
+ }
+
+ // Wait for tabs to be ready
+ let tabs = await Promise.all(tabPromises);
+
+ // Open subdialog in tabs
+ let dialogs = [];
+ let dialogBox, dialogBoxManager, browser;
+ for (let i = 0; i < 2; i += 1) {
+ dialogBox = gBrowser.getTabDialogBox(tabs[i].linkedBrowser);
+ browser = tabs[i].linkedBrowser;
+ dialogBox.open(TEST_DIALOG_PATH);
+ dialogBoxManager = dialogBox.getTabDialogManager();
+ dialogs.push(dialogBoxManager._topDialog);
+ }
+
+ // Wait for dialogs to be ready
+ await Promise.all([dialogs[0]._dialogReady, dialogs[1]._dialogReady]);
+
+ // Hide the top dialog
+ dialogBoxManager.hideDialog(browser);
+
+ ok(
+ BrowserTestUtils.is_hidden(dialogBoxManager._dialogStack),
+ "Dialog stack is hidden"
+ );
+
+ // Switch to first tab
+ await BrowserTestUtils.switchTab(gBrowser, tabs[0]);
+
+ // Check the dialog stack is showing in first tab
+ dialogBoxManager = gBrowser
+ .getTabDialogBox(tabs[0].linkedBrowser)
+ .getTabDialogManager();
+ is(dialogBoxManager._dialogStack.hidden, false, "Dialog stack is showing");
+
+ // Cleanup
+ tabs.forEach(tab => {
+ BrowserTestUtils.removeTab(tab);
+ });
+});
diff --git a/browser/base/content/test/tabdialogs/loadDelayedReply.sjs b/browser/base/content/test/tabdialogs/loadDelayedReply.sjs
new file mode 100644
index 0000000000..cf046967bf
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/loadDelayedReply.sjs
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response) {
+ response.processAsync();
+ if (request.method === "POST") {
+ getObjectState("wait", queryResponse => {
+ if (!queryResponse) {
+ throw new Error("Wrong call order");
+ }
+ queryResponse.finish();
+
+ response.setStatusLine(request.httpVersion, 200);
+ response.write("OK");
+ response.finish();
+ });
+ return;
+ }
+ response.setStatusLine(request.httpVersion, 200);
+ response.write("OK");
+ setObjectState("wait", response);
+}
diff --git a/browser/base/content/test/tabdialogs/subdialog.xhtml b/browser/base/content/test/tabdialogs/subdialog.xhtml
new file mode 100644
index 0000000000..3dfe537f47
--- /dev/null
+++ b/browser/base/content/test/tabdialogs/subdialog.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Sample sub-dialog">
+<dialog id="subDialog">
+ <script>
+ document.addEventListener("dialogaccept", acceptSubdialog);
+ function acceptSubdialog() {
+ window.arguments[0].acceptCount++;
+ }
+ </script>
+
+ <description id="desc">A sample sub-dialog for testing</description>
+
+ <html:input id="textbox" value="Default text" />
+
+ <html:select id="select">
+ <html:option>Foo</html:option>
+ <html:option>Bar</html:option>
+ </html:select>
+
+ <separator class="thin"/>
+
+ <button oncommand="window.close();" label="Close" />
+
+</dialog>
+</window>