summaryrefslogtreecommitdiffstats
path: root/toolkit/components/prompts/src/Prompter.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/prompts/src/Prompter.sys.mjs')
-rw-r--r--toolkit/components/prompts/src/Prompter.sys.mjs1824
1 files changed, 1824 insertions, 0 deletions
diff --git a/toolkit/components/prompts/src/Prompter.sys.mjs b/toolkit/components/prompts/src/Prompter.sys.mjs
new file mode 100644
index 0000000000..fe44992378
--- /dev/null
+++ b/toolkit/components/prompts/src/Prompter.sys.mjs
@@ -0,0 +1,1824 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+// This is redefined below, for strange and unfortunate reasons.
+import { PromptUtils } from "resource://gre/modules/PromptUtils.sys.mjs";
+
+const {
+ MODAL_TYPE_TAB,
+ MODAL_TYPE_CONTENT,
+ MODAL_TYPE_WINDOW,
+ MODAL_TYPE_INTERNAL_WINDOW,
+} = Ci.nsIPrompt;
+
+const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
+const SELECT_DIALOG = "chrome://global/content/selectDialog.xhtml";
+
+export function Prompter() {
+ // Note that EmbedPrompter clones this implementation.
+}
+
+/**
+ * Implements nsIPromptService and nsIPromptFactory
+ * @class Prompter
+ */
+Prompter.prototype = {
+ classID: Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"),
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIPromptFactory",
+ "nsIPromptService",
+ ]),
+
+ /* ---------- private members ---------- */
+
+ pickPrompter(options) {
+ return new ModalPrompter(options);
+ },
+
+ /* ---------- nsIPromptFactory ---------- */
+
+ getPrompt(domWin, iid) {
+ // This is still kind of dumb; the C++ code delegated to login manager
+ // here, which in turn calls back into us via nsIPromptService.
+ if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) {
+ try {
+ let pwmgr = Cc[
+ "@mozilla.org/passwordmanager/authpromptfactory;1"
+ ].getService(Ci.nsIPromptFactory);
+ return pwmgr.getPrompt(domWin, iid);
+ } catch (e) {
+ console.error("nsPrompter: Delegation to password manager failed: ", e);
+ }
+ }
+
+ let p = new ModalPrompter({ domWin });
+ p.QueryInterface(iid);
+ return p;
+ },
+
+ /* ---------- nsIPromptService ---------- */
+
+ /**
+ * Puts up an alert dialog with an OK button.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ */
+ alert(domWin, title, text) {
+ let p = this.pickPrompter({ domWin });
+ p.alert(title, text);
+ },
+
+ /**
+ * Puts up an alert dialog with an OK button.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ */
+ alertBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ p.alert(...promptArgs);
+ },
+
+ /**
+ * Puts up an alert dialog with an OK button.
+ *
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @returns {Promise} A promise which resolves when the prompt is dismissed.
+ */
+ asyncAlert(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.alert(...promptArgs);
+ },
+
+ /**
+ * Puts up an alert dialog with an OK button and a labeled checkbox.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ */
+ alertCheck(domWin, title, text, checkLabel, checkValue) {
+ let p = this.pickPrompter({ domWin });
+ p.alertCheck(title, text, checkLabel, checkValue);
+ },
+
+ /**
+ * Puts up an alert dialog with an OK button and a labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ */
+ alertCheckBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ p.alertCheck(...promptArgs);
+ },
+
+ /**
+ * Puts up an alert dialog with an OK button and a labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Boolean} checkValue - The initial checked state of the checkbox.
+ * @returns {Promise<nsIPropertyBag<{ checked: Boolean }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncAlertCheck(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.alertCheck(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ confirm(domWin, title, text) {
+ let p = this.pickPrompter({ domWin });
+ return p.confirm(title, text);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ confirmBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.confirm(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncConfirm(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.confirm(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons and a labeled checkbox.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ */
+ confirmCheck(domWin, title, text, checkLabel, checkValue) {
+ let p = this.pickPrompter({ domWin });
+ return p.confirmCheck(title, text, checkLabel, checkValue);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons and a labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ * @returns {Boolean} true for OK, false for Cancel
+ */
+ confirmCheckBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.confirmCheck(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with OK and Cancel buttons and a labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * @param {Boolean} checkValue - The initial checked state of the checkbox.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean, checked: Boolean }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncConfirmCheck(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.confirmCheck(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with up to 3 buttons and an optional, labeled checkbox.
+ *
+ * Buttons are numbered 0 - 2. Button 0 is the default button unless one of
+ * the Button Default Flags is specified.
+ *
+ * A button may use a predefined title, specified by one of the Button Title
+ * Flags values. Each title value can be multiplied by a position value to
+ * assign the title to a particular button. If BUTTON_TITLE_IS_STRING is
+ * used for a button, the string parameter for that button will be used. If
+ * the value for a button position is zero, the button will not be shown.
+ *
+ * In general, flags is constructed per the following example:
+ *
+ * flags = (BUTTON_POS_0) * (BUTTON_TITLE_AAA) +
+ * (BUTTON_POS_1) * (BUTTON_TITLE_BBB) +
+ * BUTTON_POS_1_DEFAULT;
+ *
+ * where "AAA" and "BBB" correspond to one of the button titles.
+ *
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Number} flags - A combination of Button Flags.
+ * @param {String} button0 - Used when button 0 uses TITLE_IS_STRING.
+ * @param {String} button1 - Used when button 1 uses TITLE_IS_STRING.
+ * @param {String} button2 - Used when button 2 uses TITLE_IS_STRING.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * Null if no checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method
+ * is called and the final checked state after this method returns.
+ * @returns {Number} The index of the button pressed.
+ */
+ confirmEx(
+ domWin,
+ title,
+ text,
+ flags,
+ button0,
+ button1,
+ button2,
+ checkLabel,
+ checkValue
+ ) {
+ let p = this.pickPrompter({ domWin });
+ return p.confirmEx(
+ title,
+ text,
+ flags,
+ button0,
+ button1,
+ button2,
+ checkLabel,
+ checkValue
+ );
+ },
+
+ /**
+ * Puts up a dialog with up to 3 buttons and an optional, labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Number} flags - A combination of Button Flags.
+ * @param {String} button0 - Used when button 0 uses TITLE_IS_STRING.
+ * @param {String} button1 - Used when button 1 uses TITLE_IS_STRING.
+ * @param {String} button2 - Used when button 2 uses TITLE_IS_STRING.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * Null if no checkbox.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ * @returns {Number} The index of the button pressed.
+ */
+ confirmExBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.confirmEx(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with up to 3 buttons and an optional, labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Number} flags - A combination of Button Flags.
+ * @param {String} button0 - Used when button 0 uses TITLE_IS_STRING.
+ * @param {String} button1 - Used when button 1 uses TITLE_IS_STRING.
+ * @param {String} button2 - Used when button 2 uses TITLE_IS_STRING.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * Null if no checkbox.
+ * @param {Boolean} checkValue - The initial checked state of the checkbox.
+ * @param {Object} [extraArgs] - Extra arguments for the prompt metadata.
+ * @returns {Promise<nsIPropertyBag<{ buttonNumClicked: Number, checked: Boolean }>>}
+ */
+ asyncConfirmEx(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.confirmEx(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and an optional, labeled checkbox.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} value - Contains the default value for the dialog field
+ * when this method is called (null value is ok). Upon return, if
+ * the user pressed OK, then this parameter contains a newly
+ * allocated string value.
+ * Otherwise, the parameter's value is unmodified.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * If null, check box will not be shown.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ prompt(domWin, title, text, value, checkLabel, checkValue) {
+ let p = this.pickPrompter({ domWin });
+ return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and an optional, labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} value - Contains the default value for the dialog field
+ * when this method is called (null value is ok). Upon return, if
+ * the user pressed OK, then this parameter contains a newly
+ * allocated string value.
+ * Otherwise, the parameter's value is unmodified.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * If null, check box will not be shown.
+ * @param {Object} checkValue - Contains the initial checked state of the
+ * checkbox when this method is called and the final checked state
+ * after this method returns.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ promptBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.nsIPrompt_prompt(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and an optional, labeled checkbox.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} value - The default value for the dialog text field.
+ * @param {String} checkLabel - Text to appear with the checkbox.
+ * If null, check box will not be shown.
+ * @param {Boolean} checkValue - The initial checked state of the checkbox.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean, checked: Boolean, value: String }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncPrompt(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.nsIPrompt_prompt(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and a password field.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} user - Contains the default value for the username
+ * field when this method is called (null value is ok).
+ * Upon return, if the user pressed OK, then this parameter contains
+ * a newly allocated string value. Otherwise, the parameter's value
+ * is unmodified.
+ * @param {Object} pass - Contains the default value for the password field
+ * when this method is called (null value is ok). Upon return, if the
+ * user pressed OK, this parameter contains a newly allocated string
+ * value. Otherwise, the parameter's value is unmodified.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ promptUsernameAndPassword(domWin, title, text, user, pass) {
+ let p = this.pickPrompter({ domWin });
+ return p.nsIPrompt_promptUsernameAndPassword(null, title, text, user, pass);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and a password field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} user - Contains the default value for the username
+ * field when this method is called (null value is ok).
+ * Upon return, if the user pressed OK, then this parameter contains
+ * a newly allocated string value. Otherwise, the parameter's value
+ * is unmodified.
+ * @param {Object} pass - Contains the default value for the password field
+ * when this method is called (null value is ok). Upon return, if the
+ * user pressed OK, this parameter contains a newly allocated string
+ * value. Otherwise, the parameter's value is unmodified.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ promptUsernameAndPasswordBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.nsIPrompt_promptUsernameAndPassword(null, ...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with an edit field and a password field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} user - Default value for the username field.
+ * @param {String} pass - Contains the default value for the password field.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean, user: String, pass: String }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncPromptUsernameAndPassword(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.nsIPrompt_promptUsernameAndPassword(null, ...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with a password field.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} pass - Contains the default value for the password field
+ * when this method is called (null value is ok). Upon return, if the
+ * user pressed OK, this parameter contains a newly allocated string
+ * value. Otherwise, the parameter's value is unmodified.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ promptPassword(domWin, title, text, pass) {
+ let p = this.pickPrompter({ domWin });
+ return p.nsIPrompt_promptPassword(
+ null, // no channel.
+ title,
+ text,
+ pass
+ );
+ },
+
+ /**
+ * Puts up a dialog with a password field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {Object} pass - Contains the default value for the password field
+ * when this method is called (null value is ok). Upon return, if the
+ * user pressed OK, this parameter contains a newly allocated string
+ * value. Otherwise, the parameter's value is unmodified.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ promptPasswordBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.nsIPrompt_promptPassword(null, ...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog with a password field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String} pass - Contains the default value for the password field.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean, pass: String }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncPromptPassword(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.nsIPrompt_promptPassword(null, ...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog box which has a list box of strings from which the user
+ * may make a single selection.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String[]} list - The list of strings to display.
+ * @param {Object} selected - Contains the index of the selected item in the
+ * list when this method returns true.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ select(domWin, title, text, list, selected) {
+ let p = this.pickPrompter({ domWin });
+ return p.select(title, text, list, selected);
+ },
+
+ /**
+ * Puts up a dialog box which has a list box of strings from which the user
+ * may make a single selection.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String[]} list - The list of strings to display.
+ * @param {Object} selected - Contains the index of the selected item in the
+ * list when this method returns true.
+ * @returns {Boolean} true for OK, false for Cancel.
+ */
+ selectBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.select(...promptArgs);
+ },
+
+ /**
+ * Puts up a dialog box which has a list box of strings from which the user
+ * may make a single selection.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {String} title - Text to appear in the title of the dialog.
+ * @param {String} text - Text to appear in the body of the dialog.
+ * @param {String[]} list - The list of strings to display.
+ * @returns {Promise<nsIPropertyBag<{ selected: Number, ok: Boolean }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncSelect(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.select(...promptArgs);
+ },
+
+ /**
+ * Requests a username and a password. Shows a dialog with username and
+ * password field, depending on flags also a domain field.
+ * @param {mozIDOMWindowProxy} domWin - The parent window or null.
+ * @param {nsIChannel} channel - The channel that requires authentication.
+ * @param {Number} level - Security level of the credential transmission.
+ * Any of nsIAuthPrompt2.<LEVEL_NONE|LEVEL_PW_ENCRYPTED|LEVEL_SECURE>
+ * @param {nsIAuthInformation} authInfo - Authentication information object.
+ * @returns {Boolean}
+ * true: Authentication can proceed using the values
+ * in the authInfo object.
+ * false: Authentication should be cancelled, usually because the
+ * user did not provide username/password.
+ */
+ promptAuth(domWin, channel, level, authInfo) {
+ let p = this.pickPrompter({ domWin });
+ return p.promptAuth(channel, level, authInfo);
+ },
+
+ /**
+ * Requests a username and a password. Shows a dialog with username and
+ * password field, depending on flags also a domain field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {nsIChannel} channel - The channel that requires authentication.
+ * @param {Number} level - Security level of the credential transmission.
+ * Any of nsIAuthPrompt2.<LEVEL_NONE|LEVEL_PW_ENCRYPTED|LEVEL_SECURE>
+ * @param {nsIAuthInformation} authInfo - Authentication information object.
+ * @returns {Boolean}
+ * true: Authentication can proceed using the values
+ * in the authInfo object.
+ * false: Authentication should be cancelled, usually because the
+ * user did not provide username/password.
+ */
+ promptAuthBC(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType });
+ return p.promptAuth(...promptArgs);
+ },
+
+ /**
+ * Requests a username and a password. Shows a dialog with username and
+ * password field, depending on flags also a domain field.
+ * @param {BrowsingContext} browsingContext - The browsing context the
+ * prompt should be opened for.
+ * @param {Number} modalType - The modal type of the prompt.
+ * nsIPromptService.<MODAL_TYPE_WINDOW|MODAL_TYPE_TAB|MODAL_TYPE_CONTENT>
+ * @param {nsIChannel} channel - The channel that requires authentication.
+ * @param {Number} level - Security level of the credential transmission.
+ * Any of nsIAuthPrompt2.<LEVEL_NONE|LEVEL_PW_ENCRYPTED|LEVEL_SECURE>
+ * @param {nsIAuthInformation} authInfo - Authentication information object.
+ * @returns {Promise<nsIPropertyBag<{ ok: Boolean }>>}
+ * A promise which resolves when the prompt is dismissed.
+ */
+ asyncPromptAuth(browsingContext, modalType, ...promptArgs) {
+ let p = this.pickPrompter({ browsingContext, modalType, async: true });
+ return p.promptAuth(...promptArgs);
+ },
+};
+
+// Common utils not specific to a particular prompter style.
+var InternalPromptUtils = {
+ getLocalizedString(key, formatArgs) {
+ if (formatArgs) {
+ return this.strBundle.formatStringFromName(key, formatArgs);
+ }
+ return this.strBundle.GetStringFromName(key);
+ },
+
+ confirmExHelper(flags, button0, button1, button2) {
+ const BUTTON_DEFAULT_MASK = 0x03000000;
+ let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24;
+ let isDelayEnabled = flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE;
+
+ // Flags can be used to select a specific pre-defined button label or
+ // a caller-supplied string (button0/button1/button2). If no flags are
+ // set for a button, then the button won't be shown.
+ let argText = [button0, button1, button2];
+ let buttonLabels = [null, null, null];
+ for (let i = 0; i < 3; i++) {
+ let buttonLabel;
+ switch (flags & 0xff) {
+ case Ci.nsIPrompt.BUTTON_TITLE_OK:
+ buttonLabel = this.getLocalizedString("OK");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_CANCEL:
+ buttonLabel = this.getLocalizedString("Cancel");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_YES:
+ buttonLabel = this.getLocalizedString("Yes");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_NO:
+ buttonLabel = this.getLocalizedString("No");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_SAVE:
+ buttonLabel = this.getLocalizedString("Save");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE:
+ buttonLabel = this.getLocalizedString("DontSave");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_REVERT:
+ buttonLabel = this.getLocalizedString("Revert");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING:
+ buttonLabel = argText[i];
+ break;
+ }
+ if (buttonLabel) {
+ buttonLabels[i] = buttonLabel;
+ }
+ flags >>= 8;
+ }
+
+ return [
+ buttonLabels[0],
+ buttonLabels[1],
+ buttonLabels[2],
+ defaultButtonNum,
+ isDelayEnabled,
+ ];
+ },
+
+ getAuthInfo(authInfo) {
+ let username, password;
+
+ let flags = authInfo.flags;
+ if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain) {
+ username = authInfo.domain + "\\" + authInfo.username;
+ } else {
+ username = authInfo.username;
+ }
+
+ password = authInfo.password;
+
+ return [username, password];
+ },
+
+ setAuthInfo(authInfo, username, password) {
+ let flags = authInfo.flags;
+ if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
+ // Domain is separated from username by a backslash
+ let idx = username.indexOf("\\");
+ if (idx == -1) {
+ authInfo.username = username;
+ } else {
+ authInfo.domain = username.substring(0, idx);
+ authInfo.username = username.substring(idx + 1);
+ }
+ } else {
+ authInfo.username = username;
+ }
+ authInfo.password = password;
+ },
+
+ /**
+ * Strip out things like userPass and path for display.
+ */
+ getFormattedHostname(uri) {
+ return uri.scheme + "://" + uri.hostPort;
+ },
+
+ // Note: there's a similar implementation in the login manager.
+ getAuthTarget(aChannel, aAuthInfo) {
+ let displayHost, realm;
+
+ // If our proxy is demanding authentication, don't use the
+ // channel's actual destination.
+ if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
+ if (!(aChannel instanceof Ci.nsIProxiedChannel)) {
+ throw new Error("proxy auth needs nsIProxiedChannel");
+ }
+
+ let info = aChannel.proxyInfo;
+ if (!info) {
+ throw new Error("proxy auth needs nsIProxyInfo");
+ }
+
+ // Proxies don't have a scheme, but we'll use "moz-proxy://"
+ // so that it's more obvious what the login is for.
+ let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(
+ Ci.nsIIDNService
+ );
+ displayHost =
+ "moz-proxy://" +
+ idnService.convertUTF8toACE(info.host) +
+ ":" +
+ info.port;
+ realm = aAuthInfo.realm;
+ if (!realm) {
+ realm = displayHost;
+ }
+
+ return { realm, displayHost };
+ }
+
+ displayHost = this.getFormattedHostname(aChannel.URI);
+ let displayHostOnly = aChannel.URI.hostPort;
+
+ // If a HTTP WWW-Authenticate header specified a realm, that value
+ // will be available here. If it wasn't set or wasn't HTTP, we'll use
+ // the formatted hostname instead.
+ realm = aAuthInfo.realm;
+ if (!realm) {
+ realm = displayHost;
+ }
+
+ return { realm, displayHostOnly, displayHost };
+ },
+
+ makeAuthMessage(prompt, channel, authInfo) {
+ if (prompt.modalType != MODAL_TYPE_TAB) {
+ return this._legacyMakeAuthMessage(channel, authInfo);
+ }
+
+ let isProxy = authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY;
+ let isPassOnly = authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD;
+ let isCrossOrig =
+ authInfo.flags & Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE;
+ let username = authInfo.username;
+
+ // We use the realm and displayHost only for proxy auth,
+ // and the displayHostOnly (hostPort) only for x-origin auth prompts.
+ // Otherwise we rely on the title of the dialog displaying the correct
+ // title.
+ let { displayHost, realm, displayHostOnly } = this.getAuthTarget(
+ channel,
+ authInfo
+ );
+
+ if (isProxy) {
+ // The realm is server-controlled. Trim it if it's very long, to
+ // avoid the dialog becoming unusable.
+ // For background, see https://bugzilla.mozilla.org/show_bug.cgi?id=244273
+ if (realm.length > 150) {
+ realm = realm.substring(0, 150);
+ // Append "..." (or localized equivalent).
+ realm += this.ellipsis;
+ }
+
+ return this.getLocalizedString("EnterLoginForProxy3", [
+ realm,
+ displayHost,
+ ]);
+ }
+ if (isPassOnly) {
+ return this.getLocalizedString("EnterPasswordOnlyFor", [username]);
+ }
+ if (isCrossOrig) {
+ return this.getLocalizedString("EnterCredentialsCrossOrigin", [
+ displayHostOnly,
+ ]);
+ }
+ return this.getLocalizedString("EnterCredentials");
+ },
+
+ _legacyMakeAuthMessage(channel, authInfo) {
+ let isProxy = authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY;
+ let isPassOnly = authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD;
+ let isCrossOrig =
+ authInfo.flags & Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE;
+
+ let username = authInfo.username;
+ let { displayHost, realm } = this.getAuthTarget(channel, authInfo);
+
+ // Suppress "the site says: $realm" when we synthesized a missing realm.
+ if (!authInfo.realm && !isProxy) {
+ realm = "";
+ }
+
+ // The realm is server-controlled. Trim it if it's very long, to
+ // avoid the dialog becoming unusable.
+ // For background, see https://bugzilla.mozilla.org/show_bug.cgi?id=244273
+ if (realm.length > 150) {
+ realm = realm.substring(0, 150);
+ // Append "..." (or localized equivalent).
+ realm += this.ellipsis;
+ }
+
+ let text;
+ if (isProxy) {
+ text = this.getLocalizedString("EnterLoginForProxy3", [
+ realm,
+ displayHost,
+ ]);
+ } else if (isPassOnly) {
+ text = this.getLocalizedString("EnterPasswordFor", [
+ username,
+ displayHost,
+ ]);
+ } else if (isCrossOrig) {
+ text = this.getLocalizedString("EnterUserPasswordForCrossOrigin2", [
+ displayHost,
+ ]);
+ } else if (!realm) {
+ text = this.getLocalizedString("EnterUserPasswordFor2", [displayHost]);
+ } else {
+ text = this.getLocalizedString("EnterLoginForRealm3", [
+ realm,
+ displayHost,
+ ]);
+ }
+
+ return text;
+ },
+
+ getBrandFullName() {
+ return this.brandBundle.GetStringFromName("brandFullName");
+ },
+};
+
+XPCOMUtils.defineLazyGetter(InternalPromptUtils, "strBundle", function () {
+ let bundle = Services.strings.createBundle(
+ "chrome://global/locale/commonDialogs.properties"
+ );
+ if (!bundle) {
+ throw new Error("String bundle for Prompter not present!");
+ }
+ return bundle;
+});
+
+XPCOMUtils.defineLazyGetter(InternalPromptUtils, "brandBundle", function () {
+ let bundle = Services.strings.createBundle(
+ "chrome://branding/locale/brand.properties"
+ );
+ if (!bundle) {
+ throw new Error("String bundle for branding not present!");
+ }
+ return bundle;
+});
+
+XPCOMUtils.defineLazyGetter(InternalPromptUtils, "ellipsis", function () {
+ let ellipsis = "\u2026";
+ try {
+ ellipsis = Services.prefs.getComplexValue(
+ "intl.ellipsis",
+ Ci.nsIPrefLocalizedString
+ ).data;
+ } catch (e) {}
+ return ellipsis;
+});
+
+class ModalPrompter {
+ constructor({
+ browsingContext = null,
+ domWin = null,
+ modalType = null,
+ async = false,
+ }) {
+ if (browsingContext && domWin) {
+ throw new Error("Pass either browsingContext or domWin");
+ }
+
+ if (domWin) {
+ // We have a domWin, get the associated browsing context
+ this.browsingContext = BrowsingContext.getFromWindow(domWin);
+ } else {
+ this.browsingContext = browsingContext;
+ }
+
+ if (
+ domWin &&
+ (!modalType || modalType == MODAL_TYPE_WINDOW) &&
+ !this.browsingContext?.isContent &&
+ this.browsingContext?.associatedWindow?.gDialogBox
+ ) {
+ modalType = MODAL_TYPE_INTERNAL_WINDOW;
+ }
+
+ // Use given modal type or fallback to default
+ this.modalType = modalType || ModalPrompter.defaultModalType;
+
+ this.async = async;
+
+ this.QueryInterface = ChromeUtils.generateQI([
+ "nsIPrompt",
+ "nsIAuthPrompt",
+ "nsIAuthPrompt2",
+ "nsIWritablePropertyBag2",
+ ]);
+ }
+
+ set modalType(modalType) {
+ // Setting modal type window is always allowed
+ if (modalType == MODAL_TYPE_WINDOW) {
+ this._modalType = modalType;
+ return;
+ }
+
+ // For content prompts for non-content windows, use window prompts:
+ if (modalType == MODAL_TYPE_CONTENT && !this.browsingContext?.isContent) {
+ this._modalType = MODAL_TYPE_WINDOW;
+ return;
+ }
+
+ // We can't use content / tab prompts if we don't have a suitable parent.
+ if (
+ !this.browsingContext?.isContent &&
+ modalType != MODAL_TYPE_INTERNAL_WINDOW
+ ) {
+ // Only show this error if we're not about to fall back again and show a different one.
+ if (this.browsingContext?.associatedWindow?.gDialogBox) {
+ console.error(
+ "Prompter: Browser not available. Falling back to internal window prompt."
+ );
+ }
+ modalType = MODAL_TYPE_INTERNAL_WINDOW;
+ }
+
+ if (
+ modalType == MODAL_TYPE_INTERNAL_WINDOW &&
+ (this.browsingContext?.isContent ||
+ !this.browsingContext?.associatedWindow?.gDialogBox)
+ ) {
+ console.error(
+ "Prompter: internal dialogs not available in this context. Falling back to window prompt."
+ );
+ modalType = MODAL_TYPE_WINDOW;
+ }
+
+ this._modalType = modalType;
+ }
+
+ get modalType() {
+ return this._modalType;
+ }
+
+ /* ---------- internal methods ---------- */
+
+ /**
+ * Synchronous wrapper around {@link ModalPrompter#openPrompt}
+ * @param {Object} args Prompt arguments. When prompt has been closed, they are updated to reflect the result state.
+ */
+ openPromptSync(args) {
+ let closed = false;
+ this.openPrompt(args)
+ .then(returnedArgs => {
+ if (returnedArgs) {
+ for (let key in returnedArgs) {
+ args[key] = returnedArgs[key];
+ }
+ }
+ })
+ .finally(() => {
+ closed = true;
+ });
+ Services.tm.spinEventLoopUntilOrQuit(
+ "prompts/Prompter.jsm:openPromptSync",
+ () => closed
+ );
+ }
+
+ async openPrompt(args) {
+ if (!this.browsingContext) {
+ // We don't have a browsing context, fallback to a window prompt.
+ args.modalType = MODAL_TYPE_WINDOW;
+ this.openWindowPrompt(null, args);
+ return args;
+ }
+
+ if (this._modalType == MODAL_TYPE_INTERNAL_WINDOW) {
+ await this.openInternalWindowPrompt(
+ this.browsingContext.associatedWindow,
+ args
+ );
+ return args;
+ }
+
+ // Select prompts are not part of CommonDialog
+ // and thus not supported as tab or content prompts yet. See Bug 1622817.
+ // Once they are integrated this override should be removed.
+ if (args.promptType == "select" && this.modalType !== MODAL_TYPE_WINDOW) {
+ console.error(
+ "Prompter: 'select' prompts do not support tab/content prompting. Falling back to window prompt."
+ );
+ args.modalType = MODAL_TYPE_WINDOW;
+ } else {
+ args.modalType = this.modalType;
+ }
+
+ const IS_CONTENT =
+ Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+
+ let actor;
+ try {
+ if (IS_CONTENT) {
+ // When in the content, get the PromptChild actor.
+ actor =
+ this.browsingContext.window.windowGlobalChild.getActor("Prompt");
+ } else {
+ // When in the parent, get the PromptParent actor.
+ actor = this.browsingContext.currentWindowGlobal.getActor("Prompt");
+ }
+ } catch (_) {
+ // We can't get the prompt actor, fallback to window prompt.
+ let parentWin;
+ // If given a chrome BC we can try to get its window
+ if (!this.browsingContext.isContent && this.browsingContext.window) {
+ parentWin = this.browsingContext.window;
+ } else {
+ // Try to get the window which is the browsers parent
+ parentWin = this.browsingContext.top?.embedderElement?.ownerGlobal;
+ }
+ this.openWindowPrompt(parentWin, args);
+ return args;
+ }
+
+ /* For prompts with a channel, we want to show the origin requesting
+ * authentication. This is different from the prompt principal,
+ * which is based on the document loaded in the browsing context over
+ * which the prompt appears. So if page foo.com loads bar.com, and the
+ * latter asks for auth, we want that bar.com's origin, not foo.com.
+ * To avoid confusion, we use different properties
+ * (authOrigin / promptPrincipal) to track this information.
+ */
+ if (args.channel) {
+ try {
+ args.authOrigin = args.channel.URI.hostPort;
+ } catch (ex) {
+ args.authOrigin = args.channel.URI.prePath;
+ }
+ args.isInsecureAuth =
+ args.channel.URI.schemeIs("http") &&
+ !args.channel.loadInfo.isTopLevelLoad;
+ // whether we are going to prompt the user for their credentials for a different base domain.
+ // When true, auth prompt spoofing protection mechanisms will be triggered (see bug 791594).
+ args.isTopLevelCrossDomainAuth = false;
+ // We don't support auth prompt spoofing protections for sub resources and window prompts
+ if (
+ args.modalType == MODAL_TYPE_TAB &&
+ args.channel.loadInfo.isTopLevelLoad
+ ) {
+ // check if this is a request from a third party
+ try {
+ args.isTopLevelCrossDomainAuth =
+ this.browsingContext.currentWindowGlobal?.documentPrincipal?.isThirdPartyURI(
+ args.channel.URI
+ );
+ } catch (e) {
+ // isThirdPartyURI failes for about:/blob/data URIs
+ console.warn("nsPrompter: isThirdPartyURI failed: " + e);
+ }
+ }
+ } else {
+ args.promptPrincipal =
+ this.browsingContext.window?.document.nodePrincipal;
+ }
+ if (IS_CONTENT) {
+ let docShell = this.browsingContext.docShell;
+ let inPermitUnload = docShell?.contentViewer?.inPermitUnload;
+ args.inPermitUnload = inPermitUnload;
+ let eventDetail = Cu.cloneInto(
+ {
+ tabPrompt: this.modalType != MODAL_TYPE_WINDOW,
+ inPermitUnload,
+ },
+ this.browsingContext.window
+ );
+ PromptUtils.fireDialogEvent(
+ this.browsingContext.window,
+ "DOMWillOpenModalDialog",
+ null,
+ eventDetail
+ );
+
+ // Put content window in the modal state while the prompt is open.
+ let windowUtils = this.browsingContext.window?.windowUtils;
+ if (windowUtils) {
+ windowUtils.enterModalState();
+ }
+ } else if (args.inPermitUnload) {
+ args.promptPrincipal =
+ this.browsingContext.currentWindowGlobal.documentPrincipal;
+ }
+
+ // It is technically possible for multiple prompts to be sent from a single
+ // BrowsingContext. See bug 1266353. We use a randomly generated UUID to
+ // differentiate between the different prompts.
+ let id = "id" + Services.uuid.generateUUID().toString();
+
+ args._remoteId = id;
+
+ let returnedArgs;
+ try {
+ if (IS_CONTENT) {
+ // If we're in the content process, send a message to the PromptParent
+ // window actor.
+ returnedArgs = await actor.sendQuery("Prompt:Open", args);
+ } else {
+ // If we're in the parent process we already have the parent actor.
+ // We can call its message handler directly.
+ returnedArgs = await actor.receiveMessage({
+ name: "Prompt:Open",
+ data: args,
+ });
+ }
+
+ if (returnedArgs?.promptAborted) {
+ throw Components.Exception(
+ "prompt aborted by user",
+ Cr.NS_ERROR_NOT_AVAILABLE
+ );
+ }
+ } finally {
+ if (IS_CONTENT) {
+ let windowUtils = this.browsingContext.window?.windowUtils;
+ if (windowUtils) {
+ windowUtils.leaveModalState();
+ }
+ PromptUtils.fireDialogEvent(
+ this.browsingContext.window,
+ "DOMModalDialogClosed"
+ );
+ }
+ }
+ return returnedArgs;
+ }
+
+ /**
+ * Open a window modal prompt
+ *
+ * There's an implied contract that says modal prompts should still work when
+ * no "parent" window is passed for the dialog (eg, the "Master Password"
+ * dialog does this). These prompts must be shown even if there are *no*
+ * visible windows at all.
+ * We try and find a window to use as the parent, but don't consider if that
+ * is visible before showing the prompt. parentWindow may still be null if
+ * there are _no_ windows open.
+ * @param {Window} [parentWindow] - The parent window for the prompt, may be
+ * null.
+ * @param {Object} args - Prompt options and return values.
+ */
+ openWindowPrompt(parentWindow = null, args) {
+ let uri = args.promptType == "select" ? SELECT_DIALOG : COMMON_DIALOG;
+ let propBag = PromptUtils.objectToPropBag(args);
+ Services.ww.openWindow(
+ parentWindow || Services.ww.activeWindow,
+ uri,
+ "_blank",
+ "centerscreen,chrome,modal,titlebar",
+ propBag
+ );
+ PromptUtils.propBagToObject(propBag, args);
+ }
+
+ async openInternalWindowPrompt(parentWindow, args) {
+ if (!parentWindow?.gDialogBox || !ModalPrompter.windowPromptSubDialog) {
+ this.openWindowPrompt(parentWindow, args);
+ return;
+ }
+ let propBag = PromptUtils.objectToPropBag(args);
+ propBag.setProperty("async", this.async);
+ let uri = args.promptType == "select" ? SELECT_DIALOG : COMMON_DIALOG;
+ await parentWindow.gDialogBox.open(uri, propBag);
+ propBag.deleteProperty("async");
+ PromptUtils.propBagToObject(propBag, args);
+ }
+
+ /**
+ * Calls async prompt method and optionally runs promise chained task on
+ * result data. Converts result data to nsIPropertyBag.
+ * @param {Object} args - Prompt arguments.
+ * @param {Function} [task] - Function which is called with the modified
+ * prompt args object once the prompt has been closed. Must return a
+ * result object for the prompt caller.
+ * @returns {Promise<nsIPropertyBag>} - Resolves with a property bag holding the
+ * prompt result properties. Resolves once prompt has been closed.
+ */
+ async openPromptAsync(args, task) {
+ let result = await this.openPrompt(args);
+ // If task is not defined, the prompt method does not return
+ // anything. In this case we can resolve without value.
+ if (!task) {
+ return undefined;
+ }
+ // Convert task result to nsIPropertyBag and resolve
+ let taskResult = task(result);
+ if (!(taskResult instanceof Object)) {
+ throw new Error("task must return object");
+ }
+ return PromptUtils.objectToPropBag(taskResult);
+ }
+
+ /*
+ * ---------- interface disambiguation ----------
+ *
+ * nsIPrompt and nsIAuthPrompt share 3 method names with slightly
+ * different arguments. All but prompt() have the same number of
+ * arguments, so look at the arg types to figure out how we're being
+ * called. :-(
+ */
+ prompt() {
+ // also, the nsIPrompt flavor has 5 args instead of 6.
+ if (typeof arguments[2] == "object") {
+ return this.nsIPrompt_prompt.apply(this, arguments);
+ }
+ return this.nsIAuthPrompt_prompt.apply(this, arguments);
+ }
+
+ promptUsernameAndPassword() {
+ // Both have 6 args, so use types.
+ if (typeof arguments[2] == "object") {
+ // Add the null channel:
+ let args = Array.from(arguments);
+ args.unshift(null);
+ return this.nsIPrompt_promptUsernameAndPassword.apply(this, args);
+ }
+ return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
+ }
+
+ promptPassword() {
+ // Both have 5 args, so use types.
+ if (typeof arguments[2] == "object") {
+ // Add the null channel:
+ let args = Array.from(arguments);
+ args.unshift(null);
+ return this.nsIPrompt_promptPassword.apply(this, args);
+ }
+ return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
+ }
+
+ /* ---------- nsIPrompt ---------- */
+
+ alert(title, text) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Alert");
+ }
+
+ let args = {
+ promptType: "alert",
+ title,
+ text,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args);
+ }
+
+ return this.openPromptSync(args);
+ }
+
+ alertCheck(title, text, checkLabel, checkValue) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Alert");
+ }
+
+ // For sync calls checkValue is an XPCOM inout. XPCOM wraps primitves in
+ // objects for call by reference.
+ // The async version of this method uses call by value.
+ let checked = this.async ? checkValue : checkValue.value;
+
+ let args = {
+ promptType: "alertCheck",
+ title,
+ text,
+ checkLabel,
+ checked,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ checked: result.checked,
+ }));
+ }
+
+ this.openPromptSync(args);
+ checkValue.value = args.checked;
+ return undefined;
+ }
+
+ confirm(title, text) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Confirm");
+ }
+
+ let args = {
+ promptType: "confirm",
+ title,
+ text,
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({ ok: result.ok }));
+ }
+
+ this.openPromptSync(args);
+ return args.ok;
+ }
+
+ confirmCheck(title, text, checkLabel, checkValue) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("ConfirmCheck");
+ }
+
+ let checked = this.async ? checkValue : checkValue.value;
+
+ let args = {
+ promptType: "confirmCheck",
+ title,
+ text,
+ checkLabel,
+ checked,
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ // Checkbox state always returned, even if cancel clicked.
+ checked: result.checked,
+ // Did user click Ok or Cancel?
+ ok: result.ok,
+ }));
+ }
+
+ this.openPromptSync(args);
+ checkValue.value = args.checked;
+ return args.ok;
+ }
+
+ confirmEx(
+ title,
+ text,
+ flags,
+ button0,
+ button1,
+ button2,
+ checkLabel,
+ checkValue,
+ extraArgs = {}
+ ) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Confirm");
+ }
+
+ let args = {
+ promptType: "confirmEx",
+ title,
+ text,
+ checkLabel,
+ checked: this.async ? checkValue : checkValue.value,
+ ok: false,
+ buttonNumClicked: 1,
+ ...extraArgs,
+ };
+
+ let [label0, label1, label2, defaultButtonNum, isDelayEnabled] =
+ InternalPromptUtils.confirmExHelper(flags, button0, button1, button2);
+
+ args.defaultButtonNum = defaultButtonNum;
+ args.enableDelay = isDelayEnabled;
+
+ if (label0) {
+ args.button0Label = label0;
+ if (label1) {
+ args.button1Label = label1;
+ if (label2) {
+ args.button2Label = label2;
+ }
+ }
+ }
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ checked: !!result.checked,
+ buttonNumClicked: result.buttonNumClicked,
+ }));
+ }
+
+ this.openPromptSync(args);
+ checkValue.value = args.checked;
+ return args.buttonNumClicked;
+ }
+
+ nsIPrompt_prompt(title, text, value, checkLabel, checkValue) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Prompt");
+ }
+
+ let args = {
+ promptType: "prompt",
+ title,
+ text,
+ value: this.async ? value : value.value,
+ checkLabel,
+ checked: this.async ? checkValue : checkValue.value,
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ checked: !!result.checked,
+ value: result.value,
+ ok: result.ok,
+ }));
+ }
+
+ this.openPromptSync(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ checkValue.value = args.checked;
+ value.value = args.value;
+ }
+
+ return ok;
+ }
+
+ nsIPrompt_promptUsernameAndPassword(channel, title, text, user, pass) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString(
+ "PromptUsernameAndPassword3",
+ [InternalPromptUtils.getBrandFullName()]
+ );
+ }
+
+ let args = {
+ channel,
+ promptType: "promptUserAndPass",
+ title,
+ text,
+ user: this.async ? user : user.value,
+ pass: this.async ? pass : pass.value,
+ button0Label: InternalPromptUtils.getLocalizedString("SignIn"),
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ user: result.user,
+ pass: result.pass,
+ ok: result.ok,
+ }));
+ }
+
+ this.openPromptSync(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ user.value = args.user;
+ pass.value = args.pass;
+ }
+
+ return ok;
+ }
+
+ nsIPrompt_promptPassword(channel, title, text, pass) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("PromptPassword3", [
+ InternalPromptUtils.getBrandFullName(),
+ ]);
+ }
+
+ let args = {
+ channel,
+ promptType: "promptPassword",
+ title,
+ text,
+ pass: this.async ? pass : pass.value,
+ button0Label: InternalPromptUtils.getLocalizedString("SignIn"),
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ pass: result.pass,
+ ok: result.ok,
+ }));
+ }
+
+ this.openPromptSync(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ pass.value = args.pass;
+ }
+
+ return ok;
+ }
+
+ select(title, text, list, selected) {
+ if (!title) {
+ title = InternalPromptUtils.getLocalizedString("Select");
+ }
+
+ let args = {
+ promptType: "select",
+ title,
+ text,
+ list,
+ selected: -1,
+ ok: false,
+ };
+
+ if (this.async) {
+ return this.openPromptAsync(args, result => ({
+ selected: result.selected,
+ ok: result.ok,
+ }));
+ }
+
+ this.openPromptSync(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ selected.value = args.selected;
+ }
+
+ return ok;
+ }
+
+ /* ---------- nsIAuthPrompt ---------- */
+
+ nsIAuthPrompt_prompt(
+ title,
+ text,
+ passwordRealm,
+ savePassword,
+ defaultText,
+ result
+ ) {
+ // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
+ if (defaultText) {
+ result.value = defaultText;
+ }
+ return this.nsIPrompt_prompt(title, text, result, null, {});
+ }
+
+ nsIAuthPrompt_promptUsernameAndPassword(
+ title,
+ text,
+ passwordRealm,
+ savePassword,
+ user,
+ pass
+ ) {
+ // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
+ return this.nsIPrompt_promptUsernameAndPassword(
+ null,
+ title,
+ text,
+ user,
+ pass
+ );
+ }
+
+ nsIAuthPrompt_promptPassword(title, text, passwordRealm, savePassword, pass) {
+ // The passwordRealm and savePassword args were ignored by nsPrompt.cpp,
+ // and we don't have a channel here.
+ return this.nsIPrompt_promptPassword(null, title, text, pass);
+ }
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+ promptAuth(channel, level, authInfo) {
+ let message = InternalPromptUtils.makeAuthMessage(this, channel, authInfo);
+
+ let [username, password] = InternalPromptUtils.getAuthInfo(authInfo);
+
+ let userParam = this.async ? username : { value: username };
+ let passParam = this.async ? password : { value: password };
+
+ let result;
+ if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) {
+ result = this.nsIPrompt_promptPassword(channel, null, message, passParam);
+ } else {
+ result = this.nsIPrompt_promptUsernameAndPassword(
+ channel,
+ null,
+ message,
+ userParam,
+ passParam
+ );
+ }
+
+ // For the async case result is an nsIPropertyBag with prompt results.
+ if (this.async) {
+ return result.then(bag => {
+ let ok = bag.getProperty("ok");
+ if (ok) {
+ let username = bag.getProperty("user");
+ let password = bag.getProperty("pass");
+ InternalPromptUtils.setAuthInfo(authInfo, username, password);
+ }
+ return ok;
+ });
+ }
+
+ // For the sync case result is the "ok" boolean which indicates whether
+ // the user has confirmed the dialog.
+ if (result) {
+ InternalPromptUtils.setAuthInfo(
+ authInfo,
+ userParam.value,
+ passParam.value
+ );
+ }
+ return result;
+ }
+
+ asyncPromptAuth(
+ channel,
+ callback,
+ context,
+ level,
+ authInfo,
+ checkLabel,
+ checkValue
+ ) {
+ // Nothing calls this directly; netwerk ends up going through
+ // nsIPromptService::GetPrompt, which delegates to login manager.
+ // Login manger handles the async bits itself, and only calls out
+ // promptAuth, never asyncPromptAuth.
+ //
+ // Bug 565582 will change this.
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ /* ---------- nsIWritablePropertyBag2 ---------- */
+ // Legacy way to set modal type when prompting via nsIPrompt.
+ // Please prompt via nsIPromptService. This will be removed in the future.
+ setPropertyAsUint32(name, value) {
+ if (name == "modalType") {
+ this.modalType = value;
+ } else {
+ throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
+ }
+ }
+}
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ ModalPrompter,
+ "defaultModalType",
+ "prompts.defaultModalType",
+ MODAL_TYPE_WINDOW
+);
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ ModalPrompter,
+ "windowPromptSubDialog",
+ "prompts.windowPromptSubDialog",
+ false
+);
+
+export function AuthPromptAdapterFactory() {}
+AuthPromptAdapterFactory.prototype = {
+ classID: Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"),
+ QueryInterface: ChromeUtils.generateQI(["nsIAuthPromptAdapterFactory"]),
+
+ /* ---------- nsIAuthPromptAdapterFactory ---------- */
+
+ createAdapter(oldPrompter) {
+ return new AuthPromptAdapter(oldPrompter);
+ },
+};
+
+// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell.
+function AuthPromptAdapter(oldPrompter) {
+ this.oldPrompter = oldPrompter;
+}
+AuthPromptAdapter.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]),
+ oldPrompter: null,
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+ promptAuth(channel, level, authInfo, checkLabel, checkValue) {
+ let message = InternalPromptUtils.makeAuthMessage(
+ this.oldPrompter,
+ channel,
+ authInfo
+ );
+
+ let [username, password] = InternalPromptUtils.getAuthInfo(authInfo);
+ let userParam = { value: username };
+ let passParam = { value: password };
+
+ let { displayHost, realm } = InternalPromptUtils.getAuthTarget(
+ channel,
+ authInfo
+ );
+ let authTarget = displayHost + " (" + realm + ")";
+
+ let ok;
+ if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) {
+ ok = this.oldPrompter.promptPassword(
+ null,
+ message,
+ authTarget,
+ Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY,
+ passParam
+ );
+ } else {
+ ok = this.oldPrompter.promptUsernameAndPassword(
+ null,
+ message,
+ authTarget,
+ Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY,
+ userParam,
+ passParam
+ );
+ }
+
+ if (ok) {
+ InternalPromptUtils.setAuthInfo(
+ authInfo,
+ userParam.value,
+ passParam.value
+ );
+ }
+ return ok;
+ },
+
+ asyncPromptAuth(
+ channel,
+ callback,
+ context,
+ level,
+ authInfo,
+ checkLabel,
+ checkValue
+ ) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+};