diff options
Diffstat (limited to 'toolkit/components/prompts/test')
-rw-r--r-- | toolkit/components/prompts/test/.eslintrc.js | 8 | ||||
-rw-r--r-- | toolkit/components/prompts/test/PromptTestUtils.jsm | 248 | ||||
-rw-r--r-- | toolkit/components/prompts/test/bug619644_inner.html | 7 | ||||
-rw-r--r-- | toolkit/components/prompts/test/bug625187_iframe.html | 16 | ||||
-rw-r--r-- | toolkit/components/prompts/test/chrome.ini | 11 | ||||
-rw-r--r-- | toolkit/components/prompts/test/chromeScript.js | 354 | ||||
-rw-r--r-- | toolkit/components/prompts/test/mochitest.ini | 17 | ||||
-rw-r--r-- | toolkit/components/prompts/test/prompt_common.js | 444 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_bug619644.html | 74 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_bug620145.html | 96 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_dom_prompts.html | 207 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_modal_prompts.html | 1311 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_modal_select.html | 138 | ||||
-rw-r--r-- | toolkit/components/prompts/test/test_subresources_prompts.html | 200 |
14 files changed, 3131 insertions, 0 deletions
diff --git a/toolkit/components/prompts/test/.eslintrc.js b/toolkit/components/prompts/test/.eslintrc.js new file mode 100644 index 0000000000..af973e82fe --- /dev/null +++ b/toolkit/components/prompts/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + rules: { + // ownerGlobal doesn't exist in content privileged windows. + "mozilla/use-ownerGlobal": "off", + }, +}; diff --git a/toolkit/components/prompts/test/PromptTestUtils.jsm b/toolkit/components/prompts/test/PromptTestUtils.jsm new file mode 100644 index 0000000000..53c9da01cd --- /dev/null +++ b/toolkit/components/prompts/test/PromptTestUtils.jsm @@ -0,0 +1,248 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Utility module for tests to interact with prompts spawned by nsIPrompt or + * nsIPromptService. + */ + +"use strict"; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +const { BrowserTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const EXPORTED_SYMBOLS = ["PromptTestUtils"]; + +const kPrefs = {}; + +// Whether prompts with modal type TAB are shown as SubDialog (true) or +// TabModalPrompt (false). +XPCOMUtils.defineLazyPreferenceGetter( + kPrefs, + "tabPromptSubDialogEnabled", + "prompts.tabChromePromptSubDialog", + false +); + +// Whether web content prompts (alert etc.) are shown as SubDialog (true) +// or TabModalPrompt (false) +XPCOMUtils.defineLazyPreferenceGetter( + kPrefs, + "contentPromptSubDialogEnabled", + "prompts.contentPromptSubDialog", + false +); + +function isCommonDialog(modalType) { + return ( + modalType === Services.prompt.MODAL_TYPE_WINDOW || + (kPrefs.tabPromptSubDialogEnabled && + modalType === Services.prompt.MODAL_TYPE_TAB) || + (kPrefs.contentPromptSubDialogEnabled && + modalType === Services.prompt.MODAL_TYPE_CONTENT) + ); +} + +let PromptTestUtils = { + /** + * Wait for a prompt from nsIPrompt or nsIPromptsService, interact with it and + * click the specified button to close it. + * @param {Browser|Window} [parent] - Parent of the prompt. This can be + * either the parent window or the browser. For tab prompts, if given a + * window, the currently selected browser in that window will be used. + * @param {Object} promptOptions - @see waitForPrompt + * @param {Object} promptActions - @see handlePrompt + * @returns {Promise} - A promise which resolves once the prompt has been + * closed. + */ + async handleNextPrompt(parent, promptOptions, promptActions) { + let dialog = await this.waitForPrompt(parent, promptOptions); + return this.handlePrompt(dialog, promptActions); + }, + + /** + * Interact with an existing prompt and close it. + * @param {Dialog} dialog - The dialog instance associated with the prompt. + * @param {Object} [actions] - Options on how to interact with the + * prompt and how to close it. + * @param {Boolean} [actions.checkboxState] - Set the checkbox state. + * true = checked, false = unchecked. + * @param {Number} [actions.buttonNumClick] - Which button to click to close + * the prompt. + * @param {String} [actions.loginInput] - Input text for the login text field. + * This field is also used for text input for the "prompt" type. + * @param {String} [actions.passwordInput] - Input text for the password text + * field. + * @returns {Promise} - A promise which resolves once the prompt has been + * closed. + */ + handlePrompt( + dialog, + { + checkboxState = null, + buttonNumClick = 0, + loginInput = null, + passwordInput = null, + } = {} + ) { + let promptClosePromise; + + // Get parent window to listen for prompt close event + let win; + if (isCommonDialog(dialog.args.modalType)) { + win = dialog.ui.prompt?.opener; + } else { + // Tab prompts should always have a parent window + win = dialog.ui.prompt.win; + } + + if (win) { + promptClosePromise = BrowserTestUtils.waitForEvent( + win, + "DOMModalDialogClosed" + ); + } else { + // We don't have a parent, wait for window close instead + promptClosePromise = BrowserTestUtils.windowClosed(dialog.ui.prompt); + } + + if (typeof checkboxState == "boolean") { + dialog.ui.checkbox.checked = checkboxState; + } + + if (loginInput != null) { + dialog.ui.loginTextbox.value = loginInput; + } + + if (passwordInput != null) { + dialog.ui.password1Textbox.value = passwordInput; + } + + let button = dialog.ui["button" + buttonNumClick]; + if (!button) { + throw new Error("Could not find button with index " + buttonNumClick); + } + button.click(); + + return promptClosePromise; + }, + + /** + * Wait for a prompt from nsIPrompt or nsIPromptsService to open. + * @param {Browser|Window} [parent] - Parent of the prompt. This can be either + * the parent window or the browser. For tab prompts, if given a window, the + * currently selected browser in that window will be used. + * If not given a parent, the method will return on prompts of any window. + * @param {Object} attrs - The prompt attributes to filter for. + * @param {Number} attrs.modalType - Whether the expected prompt is a content, tab or window prompt. + * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT> + * @param {String} [attrs.promptType] - Common dialog type of the prompt to filter for. + * @see {@link CommonDialog} for possible prompt types. + * @returns {Promise<CommonDialog>} - A Promise which resolves with a dialog + * object once the prompt has loaded. + */ + async waitForPrompt(parent, { modalType, promptType = null } = {}) { + if (!modalType) { + throw new Error("modalType is mandatory"); + } + + // Get window by browser or browser by window, depending on what is passed + // via the parent arg. If the caller passes parent=null, both will be null. + let parentWindow; + let parentBrowser; + if (parent) { + if (Element.isInstance(parent)) { + // Parent is browser + parentBrowser = parent; + parentWindow = parentBrowser.ownerGlobal; + } else if (parent instanceof Ci.nsIDOMChromeWindow) { + // Parent is window + parentWindow = parent; + parentBrowser = parentWindow.gBrowser?.selectedBrowser; + } else { + throw new Error("Invalid parent. Expected browser or dom window"); + } + } + + let topic = isCommonDialog(modalType) + ? "common-dialog-loaded" + : "tabmodal-dialog-loaded"; + + let dialog; + await TestUtils.topicObserved(topic, subject => { + // If we are not given a browser, use the currently selected browser of the window + let browser = + parentBrowser || subject.ownerGlobal.gBrowser?.selectedBrowser; + if (isCommonDialog(modalType)) { + // Is not associated with given parent window, skip + if (parentWindow && subject.opener !== parentWindow) { + return false; + } + + // For tab prompts, ensure that the associated browser matches. + if (browser && modalType == Services.prompt.MODAL_TYPE_TAB) { + let dialogBox = parentWindow.gBrowser.getTabDialogBox(browser); + let hasMatchingDialog = dialogBox + .getTabDialogManager() + ._dialogs.some( + d => d._frame?.browsingContext == subject.browsingContext + ); + if (!hasMatchingDialog) { + return false; + } + } + + if (browser && modalType == Services.prompt.MODAL_TYPE_CONTENT) { + let dialogBox = parentWindow.gBrowser.getTabDialogBox(browser); + let hasMatchingDialog = dialogBox + .getContentDialogManager() + ._dialogs.some( + d => d._frame?.browsingContext == subject.browsingContext + ); + if (!hasMatchingDialog) { + return false; + } + } + + // subject is the window object of the prompt which has a Dialog object + // attached. + dialog = subject.Dialog; + } else { + // subject is the tabprompt dom node + // Get the full prompt object which has the dialog object + let prompt = browser.tabModalPromptBox.getPrompt(subject); + + // Is not associated with given parent browser, skip. + if (!prompt) { + return false; + } + + dialog = prompt.Dialog; + } + + // Not the modalType we're looking for. + // For window prompts dialog.args.modalType is undefined. + if (isCommonDialog(modalType) && dialog.args.modalType !== modalType) { + return false; + } + + // Not the promptType we're looking for. + if (promptType && dialog.args.promptType !== promptType) { + return false; + } + + // Prompt found + return true; + }); + + return dialog; + }, +}; diff --git a/toolkit/components/prompts/test/bug619644_inner.html b/toolkit/components/prompts/test/bug619644_inner.html new file mode 100644 index 0000000000..cecfa78bad --- /dev/null +++ b/toolkit/components/prompts/test/bug619644_inner.html @@ -0,0 +1,7 @@ +<head></head><body><p>Original content</p> +<script> + window.opener.postMessage("", "*"); + confirm("Message"); + document.write("Extra content"); + window.opener.postMessage(document.documentElement.innerHTML, "*"); +</script></body> diff --git a/toolkit/components/prompts/test/bug625187_iframe.html b/toolkit/components/prompts/test/bug625187_iframe.html new file mode 100644 index 0000000000..740d59a617 --- /dev/null +++ b/toolkit/components/prompts/test/bug625187_iframe.html @@ -0,0 +1,16 @@ +<html> +<head> + <title>Test for Bug 625187 - the iframe</title> +<!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + - + - Contributor(s): + - Mihai Sucan <mihai.sucan@gmail.com> + --> +</head> +<body> +<p><button id="btn1" onclick="alert('hello world 2')">Button 2</button></p> +<p><button id="btn2" onclick="window.parent.alert('hello world 3')">Button 3</button></p> +</body> +</html> diff --git a/toolkit/components/prompts/test/chrome.ini b/toolkit/components/prompts/test/chrome.ini new file mode 100644 index 0000000000..90eee3a761 --- /dev/null +++ b/toolkit/components/prompts/test/chrome.ini @@ -0,0 +1,11 @@ +[DEFAULT] +support-files = + prompt_common.js + chromeScript.js + +[test_modal_prompts.html] +skip-if = + toolkit == 'android' #android: TIMED_OUT + os == 'linux' && (debug || asan || tsan) +[test_modal_select.html] +skip-if = toolkit == 'android' #android: TIMED_OUT diff --git a/toolkit/components/prompts/test/chromeScript.js b/toolkit/components/prompts/test/chromeScript.js new file mode 100644 index 0000000000..e31fbdc851 --- /dev/null +++ b/toolkit/components/prompts/test/chromeScript.js @@ -0,0 +1,354 @@ +/* eslint-env mozilla/chrome-script */ + +const { clearInterval, setInterval, setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +const { BrowserTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +var tabSubDialogsEnabled = Services.prefs.getBoolPref( + "prompts.tabChromePromptSubDialog", + false +); + +var contentPromptSubdialogsEnabled = Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); + +// Define these to make EventUtils happy. +let window = this; +let parent = {}; + +let EventUtils = {}; +Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils +); + +addMessageListener("handlePrompt", msg => { + info("Received handlePrompt message"); + handlePromptWhenItAppears(msg.action, msg.modalType, msg.isSelect); +}); + +async function handlePromptWhenItAppears(action, modalType, isSelect) { + try { + if (!(await handlePrompt(action, modalType, isSelect))) { + setTimeout( + () => this.handlePromptWhenItAppears(action, modalType, isSelect), + 100 + ); + } + } catch (e) { + info(`handlePromptWhenItAppears: exception: ${e}`); + } +} + +function checkTabModal(prompt, browser) { + let doc = browser.ownerDocument; + + let { bottom: toolboxBottom } = doc + .getElementById("navigator-toolbox") + .getBoundingClientRect(); + + let { mainContainer } = prompt.ui; + + let { x, y } = mainContainer.getBoundingClientRect(); + ok(y > 0, "Container should have y > 0"); + // Inset by 1px since the corner point doesn't return the frame due to the + // border-radius. + is( + doc.elementFromPoint(x + 1, y + 1).parentNode, + mainContainer, + "Check tabmodalprompt is visible" + ); + + info("Click to the left of the dialog over the content area"); + isnot( + doc.elementFromPoint(x - 10, y + 50), + browser, + "Check clicks on the content area don't go to the browser" + ); + is( + doc.elementFromPoint(x - 10, y + 50), + prompt.element, + "Check clicks on the content area go to the prompt dialog background" + ); + + if (prompt.args.modalType == Ci.nsIPrompt.MODAL_TYPE_TAB) { + ok( + y <= toolboxBottom - 5, + "Dialog should overlap the toolbox by at least 5px" + ); + } else { + ok(y >= toolboxBottom, "Dialog must not overlap with toolbox."); + } + + ok( + browser.hasAttribute("tabmodalPromptShowing"), + "Check browser has @tabmodalPromptShowing" + ); +} + +async function handlePrompt(action, modalType, isSelect) { + info(`handlePrompt: modalType=${modalType}`); + + let ui; + let browserWin = Services.wm.getMostRecentWindow("navigator:browser"); + + if ( + (!contentPromptSubdialogsEnabled && + modalType === Services.prompt.MODAL_TYPE_CONTENT) || + (!tabSubDialogsEnabled && modalType === Services.prompt.MODAL_TYPE_TAB) + ) { + let gBrowser = browserWin.gBrowser; + let promptManager = gBrowser.getTabModalPromptBox(gBrowser.selectedBrowser); + let prompts = promptManager.listPrompts(); + if (!prompts.length) { + info("handlePrompt: no prompt found. retrying..."); + return false; // try again in a bit + } + + ui = prompts[0].Dialog.ui; + checkTabModal(prompts[0], gBrowser.selectedBrowser); + } else { + let doc = getDialogDoc(); + if (!doc) { + info("handlePrompt: no document found. retrying..."); + return false; // try again in a bit + } + + if (isSelect) { + ui = doc; + } else { + ui = doc.defaultView.Dialog.ui; + } + } + + let dialogClosed = BrowserTestUtils.waitForEvent( + browserWin, + "DOMModalDialogClosed" + ); + + let promptState; + if (isSelect) { + promptState = getSelectState(ui); + dismissSelect(ui, action); + } else { + promptState = getPromptState(ui); + dismissPrompt(ui, action); + } + + // Wait until the prompt has been closed before sending callback msg. + // Unless the test explicitly doesn't request a button click. + if (action.buttonClick !== "none") { + info(`handlePrompt: wait for dialogClosed`); + await dialogClosed; + } + + info(`handlePrompt: send promptHandled`); + sendAsyncMessage("promptHandled", { promptState }); + return true; +} + +function getSelectState(ui) { + let listbox = ui.getElementById("list"); + + let state = {}; + state.msg = ui.getElementById("info.txt").value; + state.selectedIndex = listbox.selectedIndex; + state.items = []; + + for (let i = 0; i < listbox.itemCount; i++) { + let item = listbox.getItemAtIndex(i).label; + state.items.push(item); + } + + return state; +} + +function getPromptState(ui) { + let state = {}; + state.msg = ui.infoBody.textContent; + state.infoRowHidden = ui.infoRow?.hidden || false; + state.titleHidden = ui.infoTitle.hidden; + state.textHidden = ui.loginContainer.hidden; + state.passHidden = ui.password1Container.hidden; + state.checkHidden = ui.checkboxContainer.hidden; + state.checkMsg = state.checkHidden ? "" : ui.checkbox.label; + state.checked = state.checkHidden ? false : ui.checkbox.checked; + // TabModalPrompts don't have an infoIcon + state.iconClass = ui.infoIcon ? ui.infoIcon.className : null; + state.textValue = ui.loginTextbox.value; + state.passValue = ui.password1Textbox.value; + + state.butt0Label = ui.button0.label; + state.butt1Label = ui.button1.label; + state.butt2Label = ui.button2.label; + + state.butt0Disabled = ui.button0.disabled; + state.butt1Disabled = ui.button1.disabled; + state.butt2Disabled = ui.button2.disabled; + + function isDefaultButton(b) { + return b.hasAttribute("default") && b.getAttribute("default") == "true"; + } + state.defButton0 = isDefaultButton(ui.button0); + state.defButton1 = isDefaultButton(ui.button1); + state.defButton2 = isDefaultButton(ui.button2); + + let e = Services.focus.focusedElement; + + if (e == null) { + state.focused = null; + } else if (ui.button0.isSameNode(e)) { + state.focused = "button0"; + } else if (ui.button1.isSameNode(e)) { + state.focused = "button1"; + } else if (ui.button2.isSameNode(e)) { + state.focused = "button2"; + } else if (e.isSameNode(ui.loginTextbox)) { + state.focused = "textField"; + } else if (e.isSameNode(ui.password1Textbox)) { + state.focused = "passField"; + } else if (ui.infoBody.isSameNode(e)) { + state.focused = "infoBody"; + } else { + state.focused = + "ERROR: unexpected element focused: " + (e ? e.localName : "<null>"); + } + + let treeOwner = + ui.prompt && ui.prompt.docShell && ui.prompt.docShell.treeOwner; + if (treeOwner && treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)) { + // Check that the dialog is modal, chrome and dependent; + // We can't just check window.opener because that'll be + // a content window, which therefore isn't exposed (it'll lie and + // be null). + let flags = treeOwner.getInterface(Ci.nsIAppWindow).chromeFlags; + state.chrome = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0; + state.dialog = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) != 0; + state.chromeDependent = + (flags & Ci.nsIWebBrowserChrome.CHROME_DEPENDENT) != 0; + let wbc = treeOwner.getInterface(Ci.nsIWebBrowserChrome); + state.isWindowModal = wbc.isWindowModal(); + } + + // Check the dialog is a common dialog document and has been embedded. + let isEmbedded = !!ui.prompt?.docShell?.chromeEventHandler; + let isCommonDialogDoc = getDialogDoc()?.location.href.includes( + "commonDialog.xhtml" + ); + state.isSubDialogPrompt = isCommonDialogDoc && isEmbedded; + state.showCallerOrigin = ui.prompt.args.showCallerOrigin; + + return state; +} + +function dismissSelect(ui, action) { + let dialog = ui.getElementsByTagName("dialog")[0]; + let listbox = ui.getElementById("list"); + + if (action.selectItem) { + listbox.selectedIndex = 1; + } + + if (action.buttonClick == "ok") { + dialog.acceptDialog(); + } else if (action.buttonClick == "cancel") { + dialog.cancelDialog(); + } +} + +function dismissPrompt(ui, action) { + info(`dismissPrompt: action=${JSON.stringify(action)}`); + if (action.setCheckbox) { + // Annoyingly, the prompt code is driven by oncommand. + ui.checkbox.checked = true; + ui.checkbox.doCommand(); + } + + if ("textField" in action) { + ui.loginTextbox.setAttribute("value", action.textField); + } + + if ("passField" in action) { + ui.password1Textbox.setAttribute("value", action.passField); + } + + switch (action.buttonClick) { + case "ok": + case 0: + ui.button0.click(); + break; + case "cancel": + case 1: + ui.button1.click(); + break; + case 2: + ui.button2.click(); + break; + case "ESC": + // XXX This is assuming tab-modal. + let browserWin = Services.wm.getMostRecentWindow("navigator:browser"); + EventUtils.synthesizeKey("KEY_Escape", {}, browserWin); + break; + case "pollOK": + // Buttons are disabled at the moment, poll until they're reenabled. + // Can't use setInterval here, because the window's in a modal state + // and thus DOM events are suppressed. + let interval = setInterval(() => { + if (ui.button0.disabled) { + return; + } + ui.button0.click(); + clearInterval(interval); + }, 100); + break; + case "none": + break; + + default: + throw new Error("dismissPrompt action listed unknown button."); + } +} + +function getDialogDoc() { + // Trudge through all the open windows, until we find the one + // that has either commonDialog.xhtml or selectDialog.xhtml loaded. + // var enumerator = Services.wm.getEnumerator("navigator:browser"); + for (let { docShell } of Services.wm.getEnumerator(null)) { + var containedDocShells = docShell.getAllDocShellsInSubtree( + docShell.typeChrome, + docShell.ENUMERATE_FORWARDS + ); + for (let childDocShell of containedDocShells) { + // Get the corresponding document for this docshell + // We don't want it if it's not done loading. + if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) { + continue; + } + var childDoc = childDocShell.contentViewer.DOMDocument; + + if ( + childDoc.location.href != + "chrome://global/content/commonDialog.xhtml" && + childDoc.location.href != "chrome://global/content/selectDialog.xhtml" + ) { + continue; + } + + // We're expecting the dialog to be focused. If it's not yet, try later. + // (In particular, this is needed on Linux to reliably check focused elements.) + if (Services.focus.focusedWindow != childDoc.defaultView) { + continue; + } + + return childDoc; + } + } + + return null; +} diff --git a/toolkit/components/prompts/test/mochitest.ini b/toolkit/components/prompts/test/mochitest.ini new file mode 100644 index 0000000000..6f00eb3815 --- /dev/null +++ b/toolkit/components/prompts/test/mochitest.ini @@ -0,0 +1,17 @@ +[DEFAULT] +support-files = + ../../passwordmgr/test/authenticate.sjs + bug619644_inner.html + bug625187_iframe.html + prompt_common.js + chromeScript.js + +[test_bug619644.html] +skip-if = toolkit == 'android' # No tab prompts on android +[test_bug620145.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_subresources_prompts.html] +skip-if = toolkit == 'android' || verify +fail-if = xorigin +[test_dom_prompts.html] +skip-if = toolkit == 'android' #android: bug 1267092 diff --git a/toolkit/components/prompts/test/prompt_common.js b/toolkit/components/prompts/test/prompt_common.js new file mode 100644 index 0000000000..a47d421d3c --- /dev/null +++ b/toolkit/components/prompts/test/prompt_common.js @@ -0,0 +1,444 @@ +const { Cc, Ci, Cu: ChromeUtils } = SpecialPowers; + +/** + * Converts a property bag to object. + * @param {nsIPropertyBag} bag - The property bag to convert + * @returns {Object} - The object representation of the nsIPropertyBag + */ +function propBagToObject(bag) { + if (!(bag instanceof Ci.nsIPropertyBag)) { + throw new TypeError("Not a property bag"); + } + let result = {}; + for (let { name, value } of bag.enumerator) { + result[name] = value; + } + return result; +} + +var modalType; +var tabSubDialogsEnabled = SpecialPowers.Services.prefs.getBoolPref( + "prompts.tabChromePromptSubDialog", + false +); +var contentSubDialogsEnabled = SpecialPowers.Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); +var isSelectDialog = false; +var isOSX = "nsILocalFileMac" in SpecialPowers.Ci; +var isE10S = SpecialPowers.Services.appinfo.processType == 2; + +var gChromeScript = SpecialPowers.loadChromeScript( + SimpleTest.getTestFileURL("chromeScript.js") +); +SimpleTest.registerCleanupFunction(() => gChromeScript.destroy()); + +async function runPromptCombinations(window, testFunc) { + let util = new PromptTestUtil(window); + let run = () => { + info( + `Running tests (modalType=${modalType}, usePromptService=${util.usePromptService}, useBrowsingContext=${util.useBrowsingContext}, useAsync=${util.useAsync})` + ); + return testFunc(util); + }; + + // Prompt service with dom window parent only supports window prompts + util.usePromptService = true; + util.useBrowsingContext = false; + util.modalType = Ci.nsIPrompt.MODAL_TYPE_WINDOW; + modalType = util.modalType; + util.useAsync = false; + await run(); + + let modalTypes = [ + Ci.nsIPrompt.MODAL_TYPE_WINDOW, + Ci.nsIPrompt.MODAL_TYPE_TAB, + Ci.nsIPrompt.MODAL_TYPE_CONTENT, + ]; + + for (let type of modalTypes) { + util.modalType = type; + modalType = type; + + // Prompt service with browsing context sync + util.usePromptService = true; + util.useBrowsingContext = true; + util.useAsync = false; + await run(); + + // Prompt service with browsing context async + util.usePromptService = true; + util.useBrowsingContext = true; + util.useAsync = true; + await run(); + + // nsIPrompt + // modalType is set via nsIWritablePropertyBag (legacy) + util.usePromptService = false; + util.useBrowsingContext = false; + util.useAsync = false; + await run(); + } +} + +class PromptTestUtil { + constructor(window) { + this.window = window; + this.browsingContext = SpecialPowers.wrap( + window + ).windowGlobalChild.browsingContext; + this.promptService = SpecialPowers.Services.prompt; + this.nsPrompt = Cc["@mozilla.org/prompter;1"] + .getService(Ci.nsIPromptFactory) + .getPrompt(window, Ci.nsIPrompt); + + this.usePromptService = null; + this.useBrowsingContext = null; + this.useAsync = null; + this.modalType = null; + } + + get _prompter() { + if (this.usePromptService) { + return this.promptService; + } + return this.nsPrompt; + } + + async prompt(funcName, promptArgs) { + if ( + this.useBrowsingContext == null || + this.usePromptService == null || + this.useAsync == null || + this.modalType == null + ) { + throw new Error("Not initialized"); + } + let args = []; + if (this.usePromptService) { + if (this.useBrowsingContext) { + if (this.useAsync) { + funcName = `async${funcName[0].toUpperCase()}${funcName.substring( + 1 + )}`; + } else { + funcName += "BC"; + } + args = [this.browsingContext, this.modalType]; + } else { + args = [this.window]; + } + } else { + let bag = this.nsPrompt.QueryInterface(Ci.nsIWritablePropertyBag2); + bag.setPropertyAsUint32("modalType", this.modalType); + } + // Append the prompt arguments + args = args.concat(promptArgs); + + let interfaceName = this.usePromptService ? "Services.prompt" : "prompt"; + ok( + this._prompter[funcName], + `${interfaceName} should have method ${funcName}.` + ); + + info(`Calling ${interfaceName}.${funcName}(${args})`); + let result = this._prompter[funcName](...args); + is( + this.useAsync, + result != null && + result.constructor != null && + result.constructor.name === "Promise", + "If method is async it should return a promise." + ); + + if (this.useAsync) { + let propBag = await result; + return propBag && propBagToObject(propBag); + } + return result; + } +} + +function onloadPromiseFor(id) { + var iframe = document.getElementById(id); + return new Promise(resolve => { + iframe.addEventListener( + "load", + function(e) { + resolve(true); + }, + { once: true } + ); + }); +} + +/** + * Take an action on the next prompt that appears without checking the state in advance. + * This is useful when the action doesn't depend on which prompt is shown and you + * are expecting multiple prompts at once in an indeterminate order. + * If you know the state of the prompt you expect you should use `handlePrompt` instead. + * @param {object} action defining how to handle the prompt + * @returns {Promise} resolving with the prompt state. + */ +function handlePromptWithoutChecks(action) { + return new Promise(resolve => { + gChromeScript.addMessageListener("promptHandled", function handled(msg) { + gChromeScript.removeMessageListener("promptHandled", handled); + resolve(msg.promptState); + }); + gChromeScript.sendAsyncMessage("handlePrompt", { action, modalType }); + }); +} + +async function handlePrompt(state, action) { + let actualState = await handlePromptWithoutChecks(action); + checkPromptState(actualState, state); +} + +function checkPromptState(promptState, expectedState) { + info(`checkPromptState: Expected: ${expectedState.msg}`); + // XXX check title? OS X has title in content + is(promptState.msg, expectedState.msg, "Checking expected message"); + + let isOldContentPrompt = + !promptState.isSubDialogPrompt && + modalType === Ci.nsIPrompt.MODAL_TYPE_CONTENT; + + if (isOldContentPrompt && !promptState.showCallerOrigin) { + ok( + promptState.titleHidden, + "The title should be hidden for content prompts opened with tab modal prompt." + ); + } else if ( + isOSX || + promptState.isSubDialogPrompt || + promptState.showCallerOrigin + ) { + ok( + !promptState.titleHidden, + "Checking title always visible on OS X or when opened with common dialog" + ); + } else { + is( + promptState.titleHidden, + expectedState.titleHidden, + "Checking title visibility" + ); + } + is( + promptState.textHidden, + expectedState.textHidden, + "Checking textbox visibility" + ); + is( + promptState.passHidden, + expectedState.passHidden, + "Checking passbox visibility" + ); + is( + promptState.checkHidden, + expectedState.checkHidden, + "Checking checkbox visibility" + ); + is(promptState.checkMsg, expectedState.checkMsg, "Checking checkbox label"); + is(promptState.checked, expectedState.checked, "Checking checkbox checked"); + if ( + modalType === Ci.nsIPrompt.MODAL_TYPE_WINDOW || + (modalType === Ci.nsIPrompt.MODAL_TYPE_TAB && tabSubDialogsEnabled) + ) { + is( + promptState.iconClass, + expectedState.iconClass, + "Checking expected icon CSS class" + ); + } + is(promptState.textValue, expectedState.textValue, "Checking textbox value"); + is(promptState.passValue, expectedState.passValue, "Checking passbox value"); + + if (expectedState.butt0Label) { + is( + promptState.butt0Label, + expectedState.butt0Label, + "Checking accept-button label" + ); + } + if (expectedState.butt1Label) { + is( + promptState.butt1Label, + expectedState.butt1Label, + "Checking cancel-button label" + ); + } + if (expectedState.butt2Label) { + is( + promptState.butt2Label, + expectedState.butt2Label, + "Checking extra1-button label" + ); + } + + // For prompts with a time-delay button. + if (expectedState.butt0Disabled) { + is(promptState.butt0Disabled, true, "Checking accept-button is disabled"); + is( + promptState.butt1Disabled, + false, + "Checking cancel-button isn't disabled" + ); + } + + is( + promptState.defButton0, + expectedState.defButton == "button0", + "checking button0 default" + ); + is( + promptState.defButton1, + expectedState.defButton == "button1", + "checking button1 default" + ); + is( + promptState.defButton2, + expectedState.defButton == "button2", + "checking button2 default" + ); + + if ( + isOSX && + expectedState.focused && + expectedState.focused.startsWith("button") && + !promptState.infoRowHidden + ) { + is( + promptState.focused, + "infoBody", + "buttons don't focus on OS X, but infoBody does instead" + ); + } else { + is(promptState.focused, expectedState.focused, "Checking focused element"); + } + + if (expectedState.hasOwnProperty("chrome")) { + is( + promptState.chrome, + expectedState.chrome, + "Dialog should be opened as chrome" + ); + } + if (expectedState.hasOwnProperty("dialog")) { + is( + promptState.dialog, + expectedState.dialog, + "Dialog should be opened as a dialog" + ); + } + if (expectedState.hasOwnProperty("chromeDependent")) { + is( + promptState.chromeDependent, + expectedState.chromeDependent, + "Dialog should be opened as dependent" + ); + } + if (expectedState.hasOwnProperty("isWindowModal")) { + is( + promptState.isWindowModal, + expectedState.isWindowModal, + "Dialog should be modal" + ); + } +} + +function checkEchoedAuthInfo(expectedState, browsingContext) { + return SpecialPowers.spawn( + browsingContext, + [expectedState.user, expectedState.pass], + (expectedUser, expectedPass) => { + let doc = this.content.document; + + // The server echos back the HTTP auth info it received. + let username = doc.getElementById("user").textContent; + let password = doc.getElementById("pass").textContent; + let authok = doc.getElementById("ok").textContent; + + Assert.equal(authok, "PASS", "Checking for successful authentication"); + Assert.equal(username, expectedUser, "Checking for echoed username"); + Assert.equal(password, expectedPass, "Checking for echoed password"); + } + ); +} + +/** + * Create a Proxy to relay method calls on an nsIAuthPrompt[2] prompter to a chrome script which can + * perform the calls in the parent. Out and inout params will be copied back from the parent to + * content. + * + * @param chromeScript The reference to the chrome script that will listen to `proxyPrompter` + * messages in the parent and call the `methodName` method. + * The return value from the message handler should be an object with properties: + * `rv` - containing the return value of the method call. + * `args` - containing the array of arguments passed to the method since out or inout ones could have + * been modified. + */ +function PrompterProxy(chromeScript) { + return new Proxy( + {}, + { + get(target, prop, receiver) { + return (...args) => { + // Array of indices of out/inout params to copy from the parent back to the caller. + let outParams = []; + + switch (prop) { + case "prompt": { + outParams = [/* result */ 5]; + break; + } + case "promptAuth": { + outParams = []; + break; + } + case "promptPassword": { + outParams = [/* pwd */ 4]; + break; + } + case "promptUsernameAndPassword": { + outParams = [/* user */ 4, /* pwd */ 5]; + break; + } + default: { + throw new Error("Unknown nsIAuthPrompt method"); + } + } + + let result; + chromeScript + .sendQuery("proxyPrompter", { + args, + methodName: prop, + }) + .then(val => { + result = val; + }); + SpecialPowers.Services.tm.spinEventLoopUntil( + "Test(prompt_common.js:get)", + () => result + ); + + for (let outParam of outParams) { + // Copy the out or inout param value over the original + args[outParam].value = result.args[outParam].value; + } + + if (prop == "promptAuth") { + args[2].username = result.args[2].username; + args[2].password = result.args[2].password; + args[2].domain = result.args[2].domain; + } + + return result.rv; + }; + }, + } + ); +} diff --git a/toolkit/components/prompts/test/test_bug619644.html b/toolkit/components/prompts/test/test_bug619644.html new file mode 100644 index 0000000000..2b424c71a6 --- /dev/null +++ b/toolkit/components/prompts/test/test_bug619644.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=619644 +--> +<head> + <title>Test for Bug 619644</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619644">Mozilla Bug 619644</a> +<pre id="test"> +<script class="testbody" type="text/javascript"> +// This is a little yucky, but it works +// The contents of bug619644_inner.html +const expectedFinalDoc = +"<head><\/head><body><p>Original content<\/p>\n<script>\n window.opener.postMessage(\"\", \"*\");\n confirm(\"Message\");\n document.write(\"Extra content\");\n window.opener.postMessage(document.documentElement.innerHTML, \"*\");\n<\/script>Extra content<\/body>"; + +inittest(); + +var promptDone; + +function inittest() { + window.addEventListener("message", runtest); + window.open("bug619644_inner.html", "619644"); + + SimpleTest.waitForExplicitFinish(); +} + +function runtest(e) { + modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT; + + window.removeEventListener("message", runtest); + window.addEventListener("message", checktest); + + let state = { + msg: "Message", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + let action = { + buttonClick: "ESC", + }; + + promptDone = handlePrompt(state, action); +} + +function checktest(e) { + is(e.data, expectedFinalDoc, "ESC press should not abort document load"); + e.source.close(); + promptDone.then(endtest); +} + +function endtest() { + info("Ending test"); + SimpleTest.finish(); +} +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/prompts/test/test_bug620145.html b/toolkit/components/prompts/test/test_bug620145.html new file mode 100644 index 0000000000..3894a528ae --- /dev/null +++ b/toolkit/components/prompts/test/test_bug620145.html @@ -0,0 +1,96 @@ +<html> +<head> + <title>Test for Bug 620145</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620145">Mozilla Bug 620145</a> +<pre id="test"> +</pre> + +<div id="text" style="max-width: 100px" onmouseup="openAlert()"> + This is a short piece of text used for testing that mouse selecting is + stopped when an alert appears. +</div> +<div id="text2" style="max-width: 100px"> + This is another short piece of text used for testing that mouse selecting is + stopped when an alert appears. +</div> +<button id="button" onmouseup="openAlert()">Button</button> + +<script class="testbody" type="text/javascript"> + +function openAlert() { + info("opening alert..."); + alert("hello!"); + info("...alert done."); +} + +add_task(async function runTest() { + var state, action; + // The <button> in this test's HTML opens a prompt when clicked. + // Here we send the events to simulate clicking it. + modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT; + + state = { + msg: "hello!", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + var button = $("button"); + dispatchMouseEvent(button, "mousedown"); + dispatchMouseEvent(button, "mouseup"); + // alert appears at this point, to be closed by the chrome script. + + await promptDone; + checkSelection(); + + // using same state and action. + promptDone = handlePrompt(state, action); + + var text = $("text"); + dispatchMouseEvent(text, "mousedown"); + dispatchMouseEvent(text, "mouseup"); + // alert appears at this point, to be closed by the chrome script. + + await promptDone; + checkSelection(); +}); + +function dispatchMouseEvent(target, type) { + var win = target.ownerDocument.defaultView; + let e = document.createEvent("MouseEvent"); + e.initEvent(type, false, false, win, 0, 1, 1, 1, 1, + false, false, false, false, 0, null); + var utils = SpecialPowers.getDOMWindowUtils(win); + utils.dispatchDOMEventViaPresShellForTesting(target, e); + ok(true, type + " sent to " + target.id); +} + +function checkSelection() { + synthesizeMouse($("text"), 25, 55, { type: "mousemove" }); + is(window.getSelection().toString(), "", "selection not made"); +} +</script> + +</body> +</html> diff --git a/toolkit/components/prompts/test/test_dom_prompts.html b/toolkit/components/prompts/test/test_dom_prompts.html new file mode 100644 index 0000000000..95595b8df2 --- /dev/null +++ b/toolkit/components/prompts/test/test_dom_prompts.html @@ -0,0 +1,207 @@ +<html> +<head> + <title>Test for DOM prompts</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +</pre> + +<script class="testbody" type="text/javascript"> +var rv; +var state, action; +modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT; + +add_task(async function test_alert_ok() { + info("Starting test: Alert"); + state = { + msg: "This is the alert text.", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + alert("This is the alert text."); + + await promptDone; +}); + +// bug 861605 made the arguments to alert/confirm optional (prompt already was). +add_task(async function test_alert_noargs() { + info("Starting test: Alert with no args"); + state = { + msg: "", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + try { + alert(); + ok(true, "alert() without arguments should not throw!"); + } catch (e) { + ok(false, "alert() without arguments should not throw!"); + } + + await promptDone; +}); + +add_task(async function test_confirm_ok() { + info("Starting test: Confirm"); + state = { + msg: "This is the confirm text.", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + rv = confirm("This is the confirm text."); + is(rv, true, "check prompt return value"); + + await promptDone; +}); + +// bug 861605 made the arguments to alert/confirm optional (prompt already was). +add_task(async function test_confirm_noargs() { + info("Starting test: Confirm with no args"); + state = { + msg: "", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + try { + rv = confirm(); + ok(true, "confirm() without arguments should not throw!"); + } catch (e) { + ok(false, "confirm() without arguments should not throw!"); + } + is(rv, true, "check prompt return value"); + + await promptDone; +}); + + +add_task(async function test_prompt_ok() { + info("Starting test: Prompt"); + state = { + msg: "This is the Prompt text.", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + rv = prompt("This is the Prompt text."); + is(rv, "", "check prompt return value"); + + await promptDone; +}); + +// bug 861605 made the arguments to alert/confirm optional (prompt already was). +add_task(async function test_prompt_noargs() { + info("Starting test: Prompt with no args"); + state = { + msg: "", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + try { + rv = prompt(); + ok(true, "prompt() without arguments should not throw!"); + } catch (e) { + ok(false, "prompt() without arguments should not throw!"); + } + is(rv, "", "check prompt return value"); + + await promptDone; +}); + +</script> + +</body> +</html> diff --git a/toolkit/components/prompts/test/test_modal_prompts.html b/toolkit/components/prompts/test/test_modal_prompts.html new file mode 100644 index 0000000000..df3438dbba --- /dev/null +++ b/toolkit/components/prompts/test/test_modal_prompts.html @@ -0,0 +1,1311 @@ + +<!DOCTYPE HTML> +<html> +<head> + <title>Modal Prompts Test</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> +</head> +<body> +Prompter tests: modal prompts +<p id="display"></p> + +<div id="content" style="display: none"> + <iframe id="iframe"></iframe> +</div> + +<pre id="test"> +<script class="testbody" type="text/javascript"> +/* eslint-disable complexity */ +async function runTests(util) { + const { NetUtil } = SpecialPowers.ChromeUtils.import( + "resource://gre/modules/NetUtil.jsm" + ); + + // The ConfirmEx + delay test has slightly different behavior with the focus + // fixup rule vs. without. + await SpecialPowers.pushPrefEnv({ + set: [["dom.focus.fixup", true]], + }); + + let state, action, promptDone; + + let checkVal = {}; + let textVal = {}; + let passVal = {}; + let flags; + let isOK; + + // ===== + info("Starting test: Alert"); + state = { + msg: "This is the alert text.", + title: "TestTitle", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = ["TestTitle", "This is the alert text."]; + await util.prompt("alert", promptArgs); + + await promptDone; + + // ===== + info("Starting test: AlertCheck (null checkbox label, so it's hidden)"); + state = { + msg: "This is the alertCheck text.", + title: "TestTitle", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = [ + "TestTitle", + "This is the alertCheck text.", + null, + util.useAsync ? false : {}, + ]; + util.prompt("alertCheck", promptArgs); + + await promptDone; + + // ===== + info("Starting test: AlertCheck"); + state = { + msg: "This is the alertCheck text.", + title: "TestTitle", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the alertCheck text.", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + let result = await util.prompt("alertCheck", promptArgs); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "checkbox was checked" + ); + + await promptDone; + + // ===== + info("Starting test: Confirm (ok)"); + state = { + msg: "This is the confirm text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = ["TestTitle", "This is the confirm text."]; + result = await util.prompt("confirm", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + + await promptDone; + + // ===== + info("Starting test: Confirm (cancel)"); + state = { + msg: "This is the confirm text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = ["TestTitle", "This is the confirm text."]; + result = await util.prompt("confirm", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + + await promptDone; + + // ===== + info("Starting test: ConfirmCheck (ok, null checkbox label)"); + state = { + msg: "This is the confirmCheck text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = [ + "TestTitle", + "This is the confirmCheck text.", + null, + util.useAsync ? false : {}, + ]; + result = await util.prompt("confirmCheck", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + + await promptDone; + + // ===== + info("Starting test: ConfirmCheck (cancel, null checkbox label)"); + state = { + msg: "This is the confirmCheck text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + }; + + promptDone = handlePrompt(state, action); + + promptArgs = [ + "TestTitle", + "This is the confirmCheck text.", + null, + util.useAsync ? false : {}, + ]; + result = await util.prompt("confirmCheck", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + + await promptDone; + + // ===== + info("Starting test: ConfirmCheck (ok)"); + state = { + msg: "This is the confirmCheck text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the confirmCheck text.", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("confirmCheck", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmCheck (cancel)"); + state = { + msg: "This is the confirmCheck text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the confirmCheck text.", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("confirmCheck", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (ok, no default text)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "bacon", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = ""; + promptArgs = ["TestTitle", "This is the prompt text.", util.useAsync ? textVal.value : textVal , null, util.useAsync ? false : {}]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "bacon", + "checking expected text value" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (ok, default text)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "kittens", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "kittens"; + promptArgs = ["TestTitle", "This is the prompt text.", util.useAsync ? textVal.value : textVal, null, util.useAsync ? false : {}]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "kittens", + "checking expected text value" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (cancel, default text)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "puppies", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "puppies"; + promptArgs = ["TestTitle", "This is the prompt text.", util.useAsync ? textVal.value : textVal, null, util.useAsync ? false : {}]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "puppies", + "checking expected text value" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (cancel, default text modified)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: true, + textValue: "puppies", + passValue: "", + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + textField: "bacon", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "puppies"; + promptArgs = ["TestTitle", "This is the prompt text.", util.useAsync ? textVal.value : textVal, null, util.useAsync ? false : {}]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "puppies", + "checking expected text value" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (ok, with checkbox)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: false, + textValue: "tribbles", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "tribbles"; + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the prompt text.", + util.useAsync ? textVal.value : textVal, + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "tribbles", + "checking expected text value" + ); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + info("Starting test: Prompt (cancel, with checkbox)"); + state = { + msg: "This is the prompt text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: false, + passHidden: true, + checkHidden: false, + textValue: "tribbles", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + setCheckbox: true, + }; + promptDone = handlePrompt(state, action); + + textVal.value = "tribbles"; + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the prompt text.", + util.useAsync ? textVal.value : textVal, + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("prompt", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + is( + util.useAsync ? result.value : textVal.value, + "tribbles", + "checking expected text value" + ); + ok( + util.useAsync ? result.checked : !checkVal.value, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + // Just two tests for this, since password manager already tests this extensively. + info("Starting test: PromptUsernameAndPassword (ok)"); + state = { + msg: "This is the pUAP text.", + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "usr", + passValue: "ssh", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "newusr", + passField: "newssh", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "usr"; + passVal.value = "ssh"; + promptArgs = [ + "TestTitle", + "This is the pUAP text.", + util.useAsync ? textVal.value : textVal, + util.useAsync ? passVal.value : passVal + ]; + result = await util.prompt("promptUsernameAndPassword", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.user : textVal.value, + "newusr", + "checking expected text value" + ); + is( + util.useAsync ? result.pass : passVal.value, + "newssh", + "checking expected pass value" + ); + + await promptDone; + + // ===== + info("Starting test: PromptUsernameAndPassword (cancel)"); + state = { + msg: "This is the pUAP text.", + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "usr", + passValue: "ssh", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + textField: "newusr", + passField: "newssh", + }; + + promptDone = handlePrompt(state, action); + + textVal.value = "usr"; + passVal.value = "ssh"; + promptArgs = [ + "TestTitle", + "This is the pUAP text.", + util.useAsync ? textVal.value : textVal, + util.useAsync ? passVal.value : passVal + ]; + result = await util.prompt("promptUsernameAndPassword", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + ok( + (util.useAsync && result.user == "newusr") || textVal.value == "usr", + "checking expected text value" + ); + ok( + (util.useAsync && result.pass == "newpass") || passVal.value == "ssh", + "checking expected pass value" + ); + + await promptDone; + + // ===== + info("Starting test: PromptPassword (ok)"); + state = { + msg: "This is the promptPassword text.", + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: true, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "ssh", + focused: "passField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + passField: "newssh", + }; + + promptDone = handlePrompt(state, action); + + passVal.value = "ssh"; + promptArgs = [ + "TestTitle", + "This is the promptPassword text.", + util.useAsync ? passVal.value : passVal + ]; + result = await util.prompt("promptPassword", promptArgs); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is( + util.useAsync ? result.pass : passVal.value, + "newssh", + "checking expected pass value" + ); + + await promptDone; + + // ===== + info("Starting test: PromptPassword (cancel)"); + state = { + msg: "This is the promptPassword text.", + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: true, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "ssh", + focused: "passField", + defButton: "button0", + }; + action = { + buttonClick: "cancel", + passField: "newssh", + }; + + promptDone = handlePrompt(state, action); + + passVal.value = "ssh"; + promptArgs = [ + "TestTitle", + "This is the promptPassword text.", + util.useAsync ? passVal.value : passVal + ]; + result = await util.prompt("promptPassword", promptArgs); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + ok( + (util.useAsync && result.pass == "newssh") || passVal.value == "ssh", + "checking expected pass value" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmEx (ok/cancel, ok)"); + state = { + msg: "This is the confirmEx text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + butt0Label: "OK", + butt1Label: "Cancel", + }; + action = { + buttonClick: "ok", + }; + + promptDone = handlePrompt(state, action); + + flags = Ci.nsIPromptService.STD_OK_CANCEL_BUTTONS; + promptArgs = [ + "TestTitle", + "This is the confirmEx text.", + flags, + null, + null, + null, + null, + util.useAsync ? false : {}, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 0, + "checked expected button num click" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmEx (yes/no, cancel)"); + state = { + msg: "This is the confirmEx text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + butt0Label: "Yes", + butt1Label: "No", + }; + action = { + buttonClick: "cancel", + }; + + promptDone = handlePrompt(state, action); + + flags = Ci.nsIPromptService.STD_YES_NO_BUTTONS; + promptArgs = [ + "TestTitle", + "This is the confirmEx text.", + flags, + null, + null, + null, + null, + util.useAsync ? false : {}, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 1, + "checked expected button num click" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmEx (buttons from args, checkbox, ok)"); + state = { + msg: "This is the confirmEx text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button0", + defButton: "button0", + butt0Label: "butt0", + butt1Label: "butt1", + butt2Label: "butt2", + }; + action = { + buttonClick: "ok", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + let b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING; + flags = + b * Ci.nsIPromptService.BUTTON_POS_2 + + b * Ci.nsIPromptService.BUTTON_POS_1 + + b * Ci.nsIPromptService.BUTTON_POS_0; + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the confirmEx text.", + flags, + "butt0", + "butt1", + "butt2", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 0, + "checked expected button num click" + ); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmEx (buttons from args, checkbox, cancel)"); + state = { + msg: "This is the confirmEx text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button1", // Default changed! + defButton: "button1", + butt0Label: "butt0", + butt1Label: "butt1", + butt2Label: "butt2", + }; + action = { + buttonClick: "cancel", + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING; + flags = + b * Ci.nsIPromptService.BUTTON_POS_2 + + b * Ci.nsIPromptService.BUTTON_POS_1 + + b * Ci.nsIPromptService.BUTTON_POS_0; + flags ^= Ci.nsIPromptService.BUTTON_POS_1_DEFAULT; + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the confirmEx text.", + flags, + "butt0", + "butt1", + "butt2", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 1, + "checked expected button num click" + ); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + info("Starting test: ConfirmEx (buttons from args, checkbox, button3)"); + state = { + msg: "This is the confirmEx text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: false, + textValue: "", + passValue: "", + checkMsg: "Check me out!", + checked: false, + focused: "button2", // Default changed! + defButton: "button2", + butt0Label: "butt0", + butt1Label: "butt1", + butt2Label: "butt2", + }; + action = { + buttonClick: 2, + setCheckbox: true, + }; + + promptDone = handlePrompt(state, action); + + b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING; + flags = + b * Ci.nsIPromptService.BUTTON_POS_2 + + b * Ci.nsIPromptService.BUTTON_POS_1 + + b * Ci.nsIPromptService.BUTTON_POS_0; + flags ^= Ci.nsIPromptService.BUTTON_POS_2_DEFAULT; + checkVal.value = false; + promptArgs = [ + "TestTitle", + "This is the confirmEx text.", + flags, + "butt0", + "butt1", + "butt2", + "Check me out!", + util.useAsync ? checkVal.value : checkVal, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 2, + "checked expected button num click" + ); + is( + util.useAsync ? result.checked : checkVal.value, + true, + "expected checkbox setting" + ); + + await promptDone; + + // ===== + // (skipped for E10S and tabmodal tests: window is required) + info("Starting test: Alert, no window"); + state = { + msg: "This is the alert text.", + title: "TestTitle", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + if (util.modalType === Ci.nsIPrompt.MODAL_TYPE_WINDOW && !isE10S) { + promptDone = handlePrompt(state, action); + + promptArgs = ["TestTitle", "This is the alert text."]; + await util.prompt("alert", promptArgs); + + await promptDone; + } + + // ===== + // (skipped for tabmodal tests: delay not supported) + info("Starting test: ConfirmEx (delay, ok)"); + state = { + msg: "This is the confirmEx delay text.", + title: "TestTitle", + iconClass: "question-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: null, // Nothing focused until the delay triggers. + defButton: "button0", + butt0Label: "OK", + butt1Label: "Cancel", + butt0Disabled: true, + }; + + if (isOSX) { + // OS X doesn't initially focus the button, but rather the infoBody. + // The focus stays there even after the button-enable delay has fired. + state.focused = "infoBody"; + } + + action = { + buttonClick: "pollOK", + }; + if (util.modalType === Ci.nsIPrompt.MODAL_TYPE_WINDOW) { + promptDone = handlePrompt(state, action); + + flags = + Ci.nsIPromptService.STD_OK_CANCEL_BUTTONS | + Ci.nsIPromptService.BUTTON_DELAY_ENABLE; + promptArgs = [ + "TestTitle", + "This is the confirmEx delay text.", + flags, + null, + null, + null, + null, + util.useAsync ? false : {}, + ]; + result = await util.prompt("confirmEx", promptArgs); + is( + util.useAsync ? result.buttonNumClicked : result, + 0, + "checked expected button num click" + ); + + await promptDone; + } + + // promptAuth already tested via password manager but do a few specific things here. + var channel = NetUtil.newChannel({ + uri: "http://example.com", + loadUsingSystemPrincipal: true, + }); + + var level = Ci.nsIAuthPrompt2.LEVEL_NONE; + var authinfo = { + username: "", + password: "", + domain: "", + flags: Ci.nsIAuthInformation.AUTH_HOST, + authenticationScheme: "basic", + realm: "", + }; + + let msg = + util.modalType == Ci.nsIPrompt.MODAL_TYPE_TAB + ? "This site is asking you to sign in." + : "http://example.com is requesting your username and password."; + // ===== + // (promptAuth is only accessible from the prompt service) + info("Starting test: promptAuth with empty realm"); + state = { + msg, + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "username", + passField: "password", + }; + if (util.usePromptService && !util.useAsync) { + promptDone = handlePrompt(state, action); + + promptArgs = [channel, level, authinfo]; + isOK = await util.prompt("promptAuth", promptArgs); + is(isOK, true, "checked expected retval"); + is(authinfo.username, "username", "checking filled username"); + is(authinfo.password, "password", "checking filled password"); + + await promptDone; + } + + // ===== + // (promptAuth is only accessible from the prompt service) + msg = + util.modalType == Ci.nsIPrompt.MODAL_TYPE_TAB + ? "This site is asking you to sign in." + : "http://example.com is requesting your username and password. The site " + + "says: \u201cabcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi " + + "abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi " + + "abcdefghi \u2026\u201d"; + + info("Starting test: promptAuth with long realm"); + state = { + msg, + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "username", + passField: "password", + }; + if (util.usePromptService && !util.useAsync) { + promptDone = handlePrompt(state, action); + + var longString = ""; + for (var i = 0; i < 20; i++) longString += "abcdefghi "; // 200 chars long + authinfo.realm = longString; + authinfo.username = ""; + authinfo.password = ""; + promptArgs = [channel, level, authinfo]; + isOK = await util.prompt("promptAuth", promptArgs); + is(isOK, true, "checked expected retval"); + is(authinfo.username, "username", "checking filled username"); + is(authinfo.password, "password", "checking filled password"); + + await promptDone; + } + + msg = + util.modalType == Ci.nsIPrompt.MODAL_TYPE_TAB + ? ("This site is asking you to sign in. Warning: Your login information " + + "will be shared with example.com, not the website you are currently visiting.") + : ("http://example.com is requesting your username and password. " + + "WARNING: Your password will not be sent to the website you are currently visiting!"); + info("Starting test: promptAuth for a cross-origin and a empty realm"); + authinfo = { + username: "", + password: "", + domain: "", + flags: + Ci.nsIAuthInformation.AUTH_HOST | + Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE, + authenticationScheme: "basic", + realm: "", + }; + state = { + msg, + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "username", + passField: "password", + }; + if (util.usePromptService && !util.useAsync) { + promptDone = handlePrompt(state, action); + promptArgs = [channel, level, authinfo]; + isOK = await util.prompt("promptAuth", promptArgs); + is(isOK, true, "checked expected retval"); + is(authinfo.username, "username", "checking filled username"); + is(authinfo.password, "password", "checking filled password"); + + await promptDone; + } + + info("Starting test: promptAuth for a cross-origin with realm"); + authinfo = { + username: "", + password: "", + domain: "", + flags: + Ci.nsIAuthInformation.AUTH_HOST | + Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE, + authenticationScheme: "basic", + realm: "Something!!!", + }; + state = { + msg, // Same as previous test, see above. + title: "TestTitle", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + textValue: "", + passValue: "", + focused: "textField", + defButton: "button0", + }; + action = { + buttonClick: "ok", + textField: "username", + passField: "password", + }; + if (util.usePromptService && !util.useAsync) { + promptDone = handlePrompt(state, action); + + promptArgs = [channel, level, authinfo]; + isOK = await util.prompt("promptAuth", promptArgs); + is(isOK, true, "checked expected retval"); + is(authinfo.username, "username", "checking filled username"); + is(authinfo.password, "password", "checking filled password"); + + await promptDone; + } +} + +let promptArgs; + +add_task(async function runPromptTests() { + await runPromptCombinations(window, runTests); +}); +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/prompts/test/test_modal_select.html b/toolkit/components/prompts/test/test_modal_select.html new file mode 100644 index 0000000000..27688cf329 --- /dev/null +++ b/toolkit/components/prompts/test/test_modal_select.html @@ -0,0 +1,138 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Modal Prompts Test</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> +</head> +<body> +Prompter tests: modal prompts +<p id="display"></p> + +<div id="content" style="display: none"> + <iframe id="iframe"></iframe> +</div> + +<pre id="test"> +<script class="testbody" type="text/javascript"> +function checkPromptState(promptState, expectedState) { + // XXX check title? OS X has title in content + // XXX check focused element + // XXX check button labels? + + is(promptState.msg, expectedState.msg, "Checking expected message"); + + // Compare listbox contents + is(promptState.items.length, expectedState.items.length, "Checking listbox length"); + + if (promptState.items.length) + is(promptState.selectedIndex, 0, "Checking selected index"); + + for (let i = 0; i < promptState.items; i++) { + is(promptState.items[i], expectedState.items[i], "Checking list item #" + i); + } +} + +let selectVal = {}; +let isOK; + +function handlePrompt(state, action) { + return new Promise(resolve => { + gChromeScript.addMessageListener("promptHandled", function handled(msg) { + gChromeScript.removeMessageListener("promptHandled", handled); + checkPromptState(msg.promptState, state); + resolve(true); + }); + gChromeScript.sendAsyncMessage("handlePrompt", { action, isSelect: true}); + }); +} + +async function runTests(util) { + // Select prompt does not support tab or content prompts yet. See Bug 1622817. + if(util.modalType != Ci.nsIPrompt.MODAL_TYPE_WINDOW) { + info('Skipping modal type for select prompt...'); + return; + } + + // Empty list + info("Starting test: Select (0 items, ok)"); + let state = { + msg: "This is the select text.", + title: "TestTitle", + items: [], + }; + let action = { + buttonClick: "ok", + }; + let promptDone = handlePrompt(state, action); + let items = []; + selectVal.value = null; // outparam, just making sure. + let result = await util.prompt("select", ["TestTitle", "This is the select text.", items, util.useAsync ? false : selectVal]); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is(util.useAsync ? result.selected : selectVal.value, -1, "checking selected index"); + await promptDone; + + // ok + info("Starting test: Select (3 items, ok)"); + state = { + msg: "This is the select text.", + title: "TestTitle", + items: ["one", "two", "three"], + }; + action = { + buttonClick: "ok", + }; + promptDone = handlePrompt(state, action); + items = ["one", "two", "three"]; + selectVal.value = null; // outparam, just making sure. + result = await util.prompt("select", ["TestTitle", "This is the select text.", items, util.useAsync ? false : selectVal]); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is(util.useAsync ? result.selected : selectVal.value, 0, "checking selected index"); + await promptDone; + + // select item + info("Starting test: Select (3 items, selection changed, ok)"); + state = { + msg: "This is the select text.", + title: "TestTitle", + items: ["one", "two", "three"], + }; + action = { + buttonClick: "ok", + selectItem: 1, + }; + promptDone = handlePrompt(state, action); + items = ["one", "two", "three"]; + selectVal.value = null; // outparam, just making sure. + result = await util.prompt("select", ["TestTitle", "This is the select text.", items, util.useAsync ? false : selectVal]); + is(util.useAsync ? result.ok : result, true, "checked expected retval"); + is(util.useAsync ? result.selected : selectVal.value, 1, "checking selected index"); + await promptDone; + + // cancel prompt + info("Starting test: Select (3 items, cancel)"); + state = { + msg: "This is the select text.", + title: "TestTitle", + items: ["one", "two", "three"], + }; + action = { + buttonClick: "cancel", + }; + promptDone = handlePrompt(state, action); + items = ["one", "two", "three"]; + selectVal.value = null; // outparam, just making sure. + result = await util.prompt("select", ["TestTitle", "This is the select text.", items, util.useAsync ? false : selectVal]); + is(util.useAsync ? result.ok : result, false, "checked expected retval"); + ok(util.useAsync && result.selected == -1 || selectVal.value == 0, "checking selected index"); + await promptDone; +} + +add_task(async function runPromptTests() { + await runPromptCombinations(window, runTests); +}); + +</script> +</pre> +</body> +</html> diff --git a/toolkit/components/prompts/test/test_subresources_prompts.html b/toolkit/components/prompts/test/test_subresources_prompts.html new file mode 100644 index 0000000000..b71ad0694e --- /dev/null +++ b/toolkit/components/prompts/test/test_subresources_prompts.html @@ -0,0 +1,200 @@ +<html> +<head> + <title>Test subresources prompts (Bug 625187 and bug 1230462)</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +<!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + - + - Contributor(s): + - Mihai Sucan <mihai.sucan@gmail.com> + --> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=625187">Mozilla Bug 625187</a> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230462">Mozilla Bug 1230462</a> + +<p><button id="topbutton" onclick="alert('hello world')">Button</button></p> + +<iframe id="iframe_diff_origin" src="http://example.com/tests/toolkit/components/prompts/test/bug625187_iframe.html"></iframe> + +<iframe id="iframe_same_origin" src="bug625187_iframe.html"></iframe> + +<iframe id="iframe_prompt"></iframe> + +<pre id="test"></pre> + +<script class="testbody" type="text/javascript"> +var iframe1Loaded = onloadPromiseFor("iframe_diff_origin"); +var iframe2Loaded = onloadPromiseFor("iframe_same_origin"); +var iframe_prompt = document.getElementById("iframe_prompt"); + +// Depending on pref state we either show auth prompts as windows or on tab level. +let authPromptModalType = SpecialPowers.Services.prefs.getIntPref( + "prompts.modalType.httpAuth" +); + +add_task(async function runTest() { + modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT; + + info("Ensuring iframe1 has loaded..."); + await iframe1Loaded; + info("Ensuring iframe2 has loaded..."); + await iframe2Loaded; + let state, action; + + state = { + msg: "hello world", + iconClass: "alert-icon", + titleHidden: true, + textHidden: true, + passHidden: true, + checkHidden: true, + textValue: "", + passValue: "", + checkMsg: "", + checked: false, + focused: "button0", + defButton: "button0", + }; + action = { + buttonClick: "ok", + }; + + let promptDone = handlePrompt(state, action); + + await SpecialPowers.spawn(window, ["topbutton", "click"], dispatchMouseEvent); + + await promptDone; + + // mostly reusing same state/action + state.titleHidden = false; + state.msg = "hello world 2"; + promptDone = handlePrompt(state, action); + + var iframe = document.getElementById("iframe_diff_origin"); + await SpecialPowers.spawn(iframe.contentWindow, ["btn1", "click"], dispatchMouseEvent); + + await promptDone; + + // mostly reusing same state/action + state.titleHidden = true; + state.msg = "hello world 2"; + promptDone = handlePrompt(state, action); + + iframe = document.getElementById("iframe_same_origin"); + await SpecialPowers.spawn(iframe.contentWindow, ["btn1", "click"], dispatchMouseEvent); + + await promptDone; + + // mostly reusing same state/action + state.msg = "hello world 3"; + promptDone = handlePrompt(state, action); + await SpecialPowers.spawn(iframe.contentWindow, ["btn2", "click"], dispatchMouseEvent); + + await promptDone; +}); + +add_task(async function runTestAuth() { + // Following tests check prompt message for a cross-origin and not + // cross-origin subresources load + + // Let prompt_common know what kind of modal type is enabled for auth prompts. + modalType = authPromptModalType; + + let state, action; + + state = { + msg: "This site is asking you to sign in.", + title: "Authentication Required", + textValue: "", + passValue: "", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + + action = { + buttonClick: "ok", + setCheckbox: false, + textField: "mochiuser1", + passField: "mochipass1", + }; + + let promptDone = handlePrompt(state, action); + + var iframe3Loaded = onloadPromiseFor("iframe_prompt"); + iframe_prompt.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1"; + await promptDone; + await iframe3Loaded; + await checkEchoedAuthInfo({user: "mochiuser1", pass: "mochipass1"}, + iframe_prompt); + + // Cross-origin subresourse test. + state = { + msg: "This site is asking you to sign in. Warning: Your login information " + + "will be shared with example.com, not the website you are currently visiting.", + title: "Authentication Required", + textValue: "", + passValue: "", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + + action = { + buttonClick: "ok", + setCheckbox: false, + textField: "mochiuser2", + passField: "mochipass2", + }; + + promptDone = handlePrompt(state, action); + + iframe3Loaded = onloadPromiseFor("iframe_prompt"); + iframe_prompt.src = "http://example.com/tests/toolkit/components/prompts/test/authenticate.sjs?user=mochiuser2&pass=mochipass2&realm=mochitest"; + await promptDone; + await iframe3Loaded; + await checkEchoedAuthInfo({user: "mochiuser2", pass: "mochipass2"}, + iframe_prompt); +}); + +/** + * Function to be passed to SpecialPowers.spawn that dispatches a MouseEvent + * of a certain type to some element in a subframe. + * + * @param {String} targetID The ID of the element that will have the event + * dispatched on. + * @param {String} type The type of MouseEvent. + * @returns Promise + * @resolves Once the event has been dispatched. + */ +async function dispatchMouseEvent(targetID, type) { + /* eslint-disable no-undef */ + let document = content.document; + let element = document.getElementById(targetID); + let event = document.createEvent("MouseEvent"); + event.initEvent(type, false, false, content, 0, 1, 1, 1, 1, + false, false, false, false, 0, null); + content.windowUtils.dispatchDOMEventViaPresShellForTesting(element, event); + /* eslint-enable no-undef */ +} +</script> +</body> +</html> |