summaryrefslogtreecommitdiffstats
path: root/toolkit/components/prompts/src
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /toolkit/components/prompts/src
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/components/prompts/src')
-rw-r--r--toolkit/components/prompts/src/CommonDialog.jsm308
-rw-r--r--toolkit/components/prompts/src/SharedPromptUtils.jsm157
-rw-r--r--toolkit/components/prompts/src/moz.build16
-rw-r--r--toolkit/components/prompts/src/nsPrompter.js958
-rw-r--r--toolkit/components/prompts/src/nsPrompter.manifest6
5 files changed, 1445 insertions, 0 deletions
diff --git a/toolkit/components/prompts/src/CommonDialog.jsm b/toolkit/components/prompts/src/CommonDialog.jsm
new file mode 100644
index 000000000..c4200feb3
--- /dev/null
+++ b/toolkit/components/prompts/src/CommonDialog.jsm
@@ -0,0 +1,308 @@
+/* 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/. */
+
+this.EXPORTED_SYMBOLS = ["CommonDialog"];
+
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "EnableDelayHelper",
+ "resource://gre/modules/SharedPromptUtils.jsm");
+
+
+this.CommonDialog = function CommonDialog(args, ui) {
+ this.args = args;
+ this.ui = ui;
+}
+
+CommonDialog.prototype = {
+ args : null,
+ ui : null,
+
+ hasInputField : true,
+ numButtons : undefined,
+ iconClass : undefined,
+ soundID : undefined,
+ focusTimer : null,
+
+ onLoad : function(xulDialog) {
+ switch (this.args.promptType) {
+ case "alert":
+ case "alertCheck":
+ this.hasInputField = false;
+ this.numButtons = 1;
+ this.iconClass = ["alert-icon"];
+ this.soundID = Ci.nsISound.EVENT_ALERT_DIALOG_OPEN;
+ break;
+ case "confirmCheck":
+ case "confirm":
+ this.hasInputField = false;
+ this.numButtons = 2;
+ this.iconClass = ["question-icon"];
+ this.soundID = Ci.nsISound.EVENT_CONFIRM_DIALOG_OPEN;
+ break;
+ case "confirmEx":
+ var numButtons = 0;
+ if (this.args.button0Label)
+ numButtons++;
+ if (this.args.button1Label)
+ numButtons++;
+ if (this.args.button2Label)
+ numButtons++;
+ if (this.args.button3Label)
+ numButtons++;
+ if (numButtons == 0)
+ throw "A dialog with no buttons? Can not haz.";
+ this.numButtons = numButtons;
+ this.hasInputField = false;
+ this.iconClass = ["question-icon"];
+ this.soundID = Ci.nsISound.EVENT_CONFIRM_DIALOG_OPEN;
+ break;
+ case "prompt":
+ this.numButtons = 2;
+ this.iconClass = ["question-icon"];
+ this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
+ this.initTextbox("login", this.args.value);
+ // Clear the label, since this isn't really a username prompt.
+ this.ui.loginLabel.setAttribute("value", "");
+ break;
+ case "promptUserAndPass":
+ this.numButtons = 2;
+ this.iconClass = ["authentication-icon", "question-icon"];
+ this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
+ this.initTextbox("login", this.args.user);
+ this.initTextbox("password1", this.args.pass);
+ break;
+ case "promptPassword":
+ this.numButtons = 2;
+ this.iconClass = ["authentication-icon", "question-icon"];
+ this.soundID = Ci.nsISound.EVENT_PROMPT_DIALOG_OPEN;
+ this.initTextbox("password1", this.args.pass);
+ // Clear the label, since the message presumably indicates its purpose.
+ this.ui.password1Label.setAttribute("value", "");
+ break;
+ default:
+ Cu.reportError("commonDialog opened for unknown type: " + this.args.promptType);
+ throw "unknown dialog type";
+ }
+
+ // set the document title
+ let title = this.args.title;
+ // OS X doesn't have a title on modal dialogs, this is hidden on other platforms.
+ let infoTitle = this.ui.infoTitle;
+ infoTitle.appendChild(infoTitle.ownerDocument.createTextNode(title));
+ if (xulDialog)
+ xulDialog.ownerDocument.title = title;
+
+ // Set button labels and visibility
+ //
+ // This assumes that button0 defaults to a visible "ok" button, and
+ // button1 defaults to a visible "cancel" button. The other 2 buttons
+ // have no default labels (and are hidden).
+ switch (this.numButtons) {
+ case 4:
+ this.setLabelForNode(this.ui.button3, this.args.button3Label);
+ this.ui.button3.hidden = false;
+ // fall through
+ case 3:
+ this.setLabelForNode(this.ui.button2, this.args.button2Label);
+ this.ui.button2.hidden = false;
+ // fall through
+ case 2:
+ // Defaults to a visible "cancel" button
+ if (this.args.button1Label)
+ this.setLabelForNode(this.ui.button1, this.args.button1Label);
+ break;
+
+ case 1:
+ this.ui.button1.hidden = true;
+ break;
+ }
+ // Defaults to a visible "ok" button
+ if (this.args.button0Label)
+ this.setLabelForNode(this.ui.button0, this.args.button0Label);
+
+ // display the main text
+ let croppedMessage = "";
+ if (this.args.text) {
+ // Bug 317334 - crop string length as a workaround.
+ croppedMessage = this.args.text.substr(0, 10000);
+ }
+ let infoBody = this.ui.infoBody;
+ infoBody.appendChild(infoBody.ownerDocument.createTextNode(croppedMessage));
+
+ let label = this.args.checkLabel;
+ if (label) {
+ // Only show the checkbox if label has a value.
+ this.ui.checkboxContainer.hidden = false;
+ this.setLabelForNode(this.ui.checkbox, label);
+ this.ui.checkbox.checked = this.args.checked;
+ }
+
+ // set the icon
+ let icon = this.ui.infoIcon;
+ if (icon)
+ this.iconClass.forEach((el, idx, arr) => icon.classList.add(el));
+
+ // set default result to cancelled
+ this.args.ok = false;
+ this.args.buttonNumClicked = 1;
+
+
+ // Set the default button
+ let b = (this.args.defaultButtonNum || 0);
+ let button = this.ui["button" + b];
+
+ if (xulDialog)
+ xulDialog.defaultButton = ['accept', 'cancel', 'extra1', 'extra2'][b];
+ else
+ button.setAttribute("default", "true");
+
+ // Set default focus / selection.
+ this.setDefaultFocus(true);
+
+ if (this.args.enableDelay) {
+ this.delayHelper = new EnableDelayHelper({
+ disableDialog: () => this.setButtonsEnabledState(false),
+ enableDialog: () => this.setButtonsEnabledState(true),
+ focusTarget: this.ui.focusTarget
+ });
+ }
+
+ // Play a sound (unless we're tab-modal -- don't want those to feel like OS prompts).
+ try {
+ if (xulDialog && this.soundID) {
+ Cc["@mozilla.org/sound;1"].
+ createInstance(Ci.nsISound).
+ playEventSound(this.soundID);
+ }
+ } catch (e) {
+ Cu.reportError("Couldn't play common dialog event sound: " + e);
+ }
+
+ let topic = "common-dialog-loaded";
+ if (!xulDialog)
+ topic = "tabmodal-dialog-loaded";
+ Services.obs.notifyObservers(this.ui.prompt, topic, null);
+ },
+
+ setLabelForNode: function(aNode, aLabel) {
+ // This is for labels which may contain embedded access keys.
+ // If we end in (&X) where X represents the access key, optionally preceded
+ // by spaces and/or followed by the ':' character, store the access key and
+ // remove the access key placeholder + leading spaces from the label.
+ // Otherwise a character preceded by one but not two &s is the access key.
+ // Store it and remove the &.
+
+ // Note that if you change the following code, see the comment of
+ // nsTextBoxFrame::UpdateAccessTitle.
+ var accessKey = null;
+ if (/ *\(\&([^&])\)(:?)$/.test(aLabel)) {
+ aLabel = RegExp.leftContext + RegExp.$2;
+ accessKey = RegExp.$1;
+ } else if (/^([^&]*)\&(([^&]).*$)/.test(aLabel)) {
+ aLabel = RegExp.$1 + RegExp.$2;
+ accessKey = RegExp.$3;
+ }
+
+ // && is the magic sequence to embed an & in your label.
+ aLabel = aLabel.replace(/\&\&/g, "&");
+ aNode.label = aLabel;
+
+ // XXXjag bug 325251
+ // Need to set this after aNode.setAttribute("value", aLabel);
+ if (accessKey)
+ aNode.accessKey = accessKey;
+ },
+
+
+ initTextbox : function (aName, aValue) {
+ this.ui[aName + "Container"].hidden = false;
+ this.ui[aName + "Textbox"].setAttribute("value",
+ aValue !== null ? aValue : "");
+ },
+
+ setButtonsEnabledState : function(enabled) {
+ this.ui.button0.disabled = !enabled;
+ // button1 (cancel) remains enabled.
+ this.ui.button2.disabled = !enabled;
+ this.ui.button3.disabled = !enabled;
+ },
+
+ setDefaultFocus : function(isInitialLoad) {
+ let b = (this.args.defaultButtonNum || 0);
+ let button = this.ui["button" + b];
+
+ if (!this.hasInputField) {
+ let isOSX = ("nsILocalFileMac" in Components.interfaces);
+ if (isOSX)
+ this.ui.infoBody.focus();
+ else
+ button.focus();
+ } else if (this.args.promptType == "promptPassword") {
+ // When the prompt is initialized, focus and select the textbox
+ // contents. Afterwards, only focus the textbox.
+ if (isInitialLoad)
+ this.ui.password1Textbox.select();
+ else
+ this.ui.password1Textbox.focus();
+ } else if (isInitialLoad) {
+ this.ui.loginTextbox.select();
+ } else {
+ this.ui.loginTextbox.focus();
+ }
+ },
+
+ onCheckbox : function() {
+ this.args.checked = this.ui.checkbox.checked;
+ },
+
+ onButton0 : function() {
+ this.args.promptActive = false;
+ this.args.ok = true;
+ this.args.buttonNumClicked = 0;
+
+ let username = this.ui.loginTextbox.value;
+ let password = this.ui.password1Textbox.value;
+
+ // Return textfield values
+ switch (this.args.promptType) {
+ case "prompt":
+ this.args.value = username;
+ break;
+ case "promptUserAndPass":
+ this.args.user = username;
+ this.args.pass = password;
+ break;
+ case "promptPassword":
+ this.args.pass = password;
+ break;
+ }
+ },
+
+ onButton1 : function() {
+ this.args.promptActive = false;
+ this.args.buttonNumClicked = 1;
+ },
+
+ onButton2 : function() {
+ this.args.promptActive = false;
+ this.args.buttonNumClicked = 2;
+ },
+
+ onButton3 : function() {
+ this.args.promptActive = false;
+ this.args.buttonNumClicked = 3;
+ },
+
+ abortPrompt : function() {
+ this.args.promptActive = false;
+ this.args.promptAborted = true;
+ },
+
+};
diff --git a/toolkit/components/prompts/src/SharedPromptUtils.jsm b/toolkit/components/prompts/src/SharedPromptUtils.jsm
new file mode 100644
index 000000000..b27096ac2
--- /dev/null
+++ b/toolkit/components/prompts/src/SharedPromptUtils.jsm
@@ -0,0 +1,157 @@
+this.EXPORTED_SYMBOLS = [ "PromptUtils", "EnableDelayHelper" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.PromptUtils = {
+ // Fire a dialog open/close event. Used by tabbrowser to focus the
+ // tab which is triggering a prompt.
+ // For remote dialogs, we pass in a different DOM window and a separate
+ // target. If the caller doesn't pass in the target, then we'll simply use
+ // the passed-in DOM window.
+ // The detail may contain information about the principal on which the
+ // prompt is triggered, as well as whether or not this is a tabprompt
+ // (ie tabmodal alert/prompt/confirm and friends)
+ fireDialogEvent : function (domWin, eventName, maybeTarget, detail) {
+ let target = maybeTarget || domWin;
+ let eventOptions = {cancelable: true, bubbles: true};
+ if (detail) {
+ eventOptions.detail = detail;
+ }
+ let event = new domWin.CustomEvent(eventName, eventOptions);
+ let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.dispatchEventToChromeOnly(target, event);
+ },
+
+ objectToPropBag : function (obj) {
+ let bag = Cc["@mozilla.org/hash-property-bag;1"].
+ createInstance(Ci.nsIWritablePropertyBag2);
+ bag.QueryInterface(Ci.nsIWritablePropertyBag);
+
+ for (let propName in obj)
+ bag.setProperty(propName, obj[propName]);
+
+ return bag;
+ },
+
+ propBagToObject : function (propBag, obj) {
+ // Here we iterate over the object's original properties, not the bag
+ // (ie, the prompt can't return more/different properties than were
+ // passed in). This just helps ensure that the caller provides default
+ // values, lest the prompt forget to set them.
+ for (let propName in obj)
+ obj[propName] = propBag.getProperty(propName);
+ },
+};
+
+/**
+ * This helper handles the enabling/disabling of dialogs that might
+ * be subject to fast-clicking attacks. It handles the initial delayed
+ * enabling of the dialog, as well as disabling it on blur and reapplying
+ * the delay when the dialog regains focus.
+ *
+ * @param enableDialog A custom function to be called when the dialog
+ * is to be enabled.
+ * @param diableDialog A custom function to be called when the dialog
+ * is to be disabled.
+ * @param focusTarget The window used to watch focus/blur events.
+ */
+this.EnableDelayHelper = function({enableDialog, disableDialog, focusTarget}) {
+ this.enableDialog = makeSafe(enableDialog);
+ this.disableDialog = makeSafe(disableDialog);
+ this.focusTarget = focusTarget;
+
+ this.disableDialog();
+
+ this.focusTarget.addEventListener("blur", this, false);
+ this.focusTarget.addEventListener("focus", this, false);
+ this.focusTarget.document.addEventListener("unload", this, false);
+
+ this.startOnFocusDelay();
+};
+
+this.EnableDelayHelper.prototype = {
+ get delayTime() {
+ return Services.prefs.getIntPref("security.dialog_enable_delay");
+ },
+
+ handleEvent : function(event) {
+ if (event.target != this.focusTarget &&
+ event.target != this.focusTarget.document)
+ return;
+
+ switch (event.type) {
+ case "blur":
+ this.onBlur();
+ break;
+
+ case "focus":
+ this.onFocus();
+ break;
+
+ case "unload":
+ this.onUnload();
+ break;
+ }
+ },
+
+ onBlur : function () {
+ this.disableDialog();
+ // If we blur while waiting to enable the buttons, just cancel the
+ // timer to ensure the delay doesn't fire while not focused.
+ if (this._focusTimer) {
+ this._focusTimer.cancel();
+ this._focusTimer = null;
+ }
+ },
+
+ onFocus : function () {
+ this.startOnFocusDelay();
+ },
+
+ onUnload: function() {
+ this.focusTarget.removeEventListener("blur", this, false);
+ this.focusTarget.removeEventListener("focus", this, false);
+ this.focusTarget.document.removeEventListener("unload", this, false);
+
+ if (this._focusTimer) {
+ this._focusTimer.cancel();
+ this._focusTimer = null;
+ }
+
+ this.focusTarget = this.enableDialog = this.disableDialog = null;
+ },
+
+ startOnFocusDelay : function() {
+ if (this._focusTimer)
+ return;
+
+ this._focusTimer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ this._focusTimer.initWithCallback(
+ () => { this.onFocusTimeout(); },
+ this.delayTime,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+ },
+
+ onFocusTimeout : function() {
+ this._focusTimer = null;
+ this.enableDialog();
+ },
+};
+
+function makeSafe(fn) {
+ return function () {
+ // The dialog could be gone by now (if the user closed it),
+ // which makes it likely that the given fn might throw.
+ try {
+ fn();
+ } catch (e) { }
+ };
+}
diff --git a/toolkit/components/prompts/src/moz.build b/toolkit/components/prompts/src/moz.build
new file mode 100644
index 000000000..b13e47b42
--- /dev/null
+++ b/toolkit/components/prompts/src/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_COMPONENTS += [
+ 'nsPrompter.js',
+ 'nsPrompter.manifest',
+]
+
+EXTRA_JS_MODULES += [
+ 'CommonDialog.jsm',
+ 'SharedPromptUtils.jsm',
+]
+
diff --git a/toolkit/components/prompts/src/nsPrompter.js b/toolkit/components/prompts/src/nsPrompter.js
new file mode 100644
index 000000000..26efe28cc
--- /dev/null
+++ b/toolkit/components/prompts/src/nsPrompter.js
@@ -0,0 +1,958 @@
+/* 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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
+
+function Prompter() {
+ // Note that EmbedPrompter clones this implementation.
+}
+
+Prompter.prototype = {
+ classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"),
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
+
+
+ /* ---------- private members ---------- */
+
+ pickPrompter : function (domWin) {
+ return new ModalPrompter(domWin);
+ },
+
+
+ /* ---------- nsIPromptFactory ---------- */
+
+
+ getPrompt : function (domWin, iid) {
+ // This is still kind of dumb; the C++ code delegated to login manager
+ // here, which in turn calls back into us via nsIPromptService2.
+ 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) {
+ Cu.reportError("nsPrompter: Delegation to password manager failed: " + e);
+ }
+ }
+
+ let p = new ModalPrompter(domWin);
+ p.QueryInterface(iid);
+ return p;
+ },
+
+
+ /* ---------- nsIPromptService ---------- */
+
+
+ alert : function (domWin, title, text) {
+ let p = this.pickPrompter(domWin);
+ p.alert(title, text);
+ },
+
+ alertCheck : function (domWin, title, text, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ p.alertCheck(title, text, checkLabel, checkValue);
+ },
+
+ confirm : function (domWin, title, text) {
+ let p = this.pickPrompter(domWin);
+ return p.confirm(title, text);
+ },
+
+ confirmCheck : function (domWin, title, text, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.confirmCheck(title, text, checkLabel, checkValue);
+ },
+
+ confirmEx : function (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);
+ },
+
+ prompt : function (domWin, title, text, value, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue);
+ },
+
+ promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue);
+ },
+
+ promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue);
+ },
+
+ select : function (domWin, title, text, count, list, selected) {
+ let p = this.pickPrompter(domWin);
+ return p.select(title, text, count, list, selected);
+ },
+
+
+ /* ---------- nsIPromptService2 ---------- */
+
+
+ promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.promptAuth(channel, level, authInfo, checkLabel, checkValue);
+ },
+
+ asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) {
+ let p = this.pickPrompter(domWin);
+ return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue);
+ },
+
+};
+
+
+// Common utils not specific to a particular prompter style.
+var PromptUtilsTemp = {
+ __proto__ : PromptUtils,
+
+ getLocalizedString : function (key, formatArgs) {
+ if (formatArgs)
+ return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length);
+ return this.strBundle.GetStringFromName(key);
+ },
+
+ confirmExHelper : function (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 = PromptUtils.getLocalizedString("OK");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_CANCEL:
+ buttonLabel = PromptUtils.getLocalizedString("Cancel");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_YES:
+ buttonLabel = PromptUtils.getLocalizedString("Yes");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_NO:
+ buttonLabel = PromptUtils.getLocalizedString("No");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_SAVE:
+ buttonLabel = PromptUtils.getLocalizedString("Save");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE:
+ buttonLabel = PromptUtils.getLocalizedString("DontSave");
+ break;
+ case Ci.nsIPrompt.BUTTON_TITLE_REVERT:
+ buttonLabel = PromptUtils.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 : function (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 : function (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 : function (uri) {
+ return uri.scheme + "://" + uri.hostPort;
+ },
+
+ // Copied from login manager
+ getAuthTarget : function (aChannel, aAuthInfo) {
+ let hostname, 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 "proxy auth needs nsIProxiedChannel";
+
+ let info = aChannel.proxyInfo;
+ if (!info)
+ throw "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);
+ hostname = "moz-proxy://" +
+ idnService.convertUTF8toACE(info.host) +
+ ":" + info.port;
+ realm = aAuthInfo.realm;
+ if (!realm)
+ realm = hostname;
+
+ return [hostname, realm];
+ }
+
+ hostname = this.getFormattedHostname(aChannel.URI);
+
+ // 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 = hostname;
+
+ return [hostname, realm];
+ },
+
+
+ makeAuthMessage : function (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 = "";
+
+ // Trim obnoxiously long realms.
+ if (realm.length > 150) {
+ realm = realm.substring(0, 150);
+ // Append "..." (or localized equivalent).
+ realm += this.ellipsis;
+ }
+
+ let text;
+ if (isProxy) {
+ text = PromptUtils.getLocalizedString("EnterLoginForProxy3", [realm, displayHost]);
+ } else if (isPassOnly) {
+ text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]);
+ } else if (isCrossOrig) {
+ text = PromptUtils.getLocalizedString("EnterUserPasswordForCrossOrigin2", [displayHost]);
+ } else if (!realm) {
+ text = PromptUtils.getLocalizedString("EnterUserPasswordFor2", [displayHost]);
+ } else {
+ text = PromptUtils.getLocalizedString("EnterLoginForRealm3", [realm, displayHost]);
+ }
+
+ return text;
+ },
+
+ getTabModalPrompt : function (domWin) {
+ var promptBox = null;
+
+ try {
+ // Get the topmost window, in case we're in a frame.
+ var promptWin = domWin.top;
+
+ // Get the chrome window for the content window we're using.
+ // (Unwrap because we need a non-IDL property below.)
+ var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler.ownerDocument
+ .defaultView.wrappedJSObject;
+
+ if (chromeWin.getTabModalPromptBox)
+ promptBox = chromeWin.getTabModalPromptBox(promptWin);
+ } catch (e) {
+ // If any errors happen, just assume no tabmodal prompter.
+ }
+
+ return promptBox;
+ },
+};
+
+PromptUtils = PromptUtilsTemp;
+
+XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () {
+ let bunService = Cc["@mozilla.org/intl/stringbundle;1"].
+ getService(Ci.nsIStringBundleService);
+ let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties");
+ if (!bundle)
+ throw "String bundle for Prompter not present!";
+ return bundle;
+});
+
+XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () {
+ let ellipsis = "\u2026";
+ try {
+ ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
+ } catch (e) { }
+ return ellipsis;
+});
+
+
+
+function openModalWindow(domWin, uri, args) {
+ // 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.
+ // There's also a requirement for prompts to be blocked if a window is
+ // passed and that window is hidden (eg, auth prompts are supressed if the
+ // passed window is the hidden window).
+ // See bug 875157 comment 30 for more...
+ if (domWin) {
+ // a domWin was passed, so we can apply the check for it being hidden.
+ let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+
+ if (winUtils && !winUtils.isParentWindowMainWidgetVisible) {
+ throw Components.Exception("Cannot call openModalWindow on a hidden window",
+ Cr.NS_ERROR_NOT_AVAILABLE);
+ }
+ } else {
+ // We try and find a window to use as the parent, but don't consider
+ // if that is visible before showing the prompt.
+ domWin = Services.ww.activeWindow;
+ // domWin may still be null here if there are _no_ windows open.
+ }
+ // Note that we don't need to fire DOMWillOpenModalDialog and
+ // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal
+ // will do that. Similarly for enterModalState / leaveModalState.
+
+ Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args);
+}
+
+function openTabPrompt(domWin, tabPrompt, args) {
+ let docShell = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell);
+ let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
+ let eventDetail = Cu.cloneInto({tabPrompt: true, inPermitUnload}, domWin);
+ PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog", null, eventDetail);
+
+ let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.enterModalState();
+
+ let frameMM = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+ frameMM.QueryInterface(Ci.nsIDOMEventTarget);
+
+ // We provide a callback so the prompt can close itself. We don't want to
+ // wait for this event loop to return... Otherwise the presence of other
+ // prompts on the call stack would in this dialog appearing unresponsive
+ // until the other prompts had been closed.
+ let callbackInvoked = false;
+ let newPrompt;
+ function onPromptClose(forceCleanup) {
+ if (!newPrompt && !forceCleanup)
+ return;
+ callbackInvoked = true;
+ if (newPrompt)
+ tabPrompt.removePrompt(newPrompt);
+
+ frameMM.removeEventListener("pagehide", pagehide, true);
+
+ winUtils.leaveModalState();
+
+ PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
+ }
+
+ frameMM.addEventListener("pagehide", pagehide, true);
+ function pagehide(e) {
+ // Check whether the event relates to our window or its ancestors
+ let window = domWin;
+ let eventWindow = e.target.defaultView;
+ while (window != eventWindow && window.parent != window) {
+ window = window.parent;
+ }
+ if (window != eventWindow) {
+ return;
+ }
+ frameMM.removeEventListener("pagehide", pagehide, true);
+
+ if (newPrompt) {
+ newPrompt.abortPrompt();
+ }
+ }
+
+ try {
+ let topPrincipal = domWin.top.document.nodePrincipal;
+ let promptPrincipal = domWin.document.nodePrincipal;
+ args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
+ args.promptActive = true;
+
+ newPrompt = tabPrompt.appendPrompt(args, onPromptClose);
+
+ // TODO since we don't actually open a window, need to check if
+ // there's other stuff in nsWindowWatcher::OpenWindowInternal
+ // that we might need to do here as well.
+
+ let thread = Services.tm.currentThread;
+ while (args.promptActive)
+ thread.processNextEvent(true);
+ delete args.promptActive;
+
+ if (args.promptAborted)
+ throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE);
+ } finally {
+ // If the prompt unexpectedly failed to invoke the callback, do so here.
+ if (!callbackInvoked)
+ onPromptClose(true);
+ }
+}
+
+function openRemotePrompt(domWin, args, tabPrompt) {
+ let docShell = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell);
+ let messageManager = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsITabChild)
+ .messageManager;
+
+ let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
+ let eventDetail = Cu.cloneInto({tabPrompt, inPermitUnload}, domWin);
+ PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog", null, eventDetail);
+
+ let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.enterModalState();
+ let closed = false;
+
+ let frameMM = docShell.getInterface(Ci.nsIContentFrameMessageManager);
+ frameMM.QueryInterface(Ci.nsIDOMEventTarget);
+
+ // It should be hard or impossible to cause a window to create multiple
+ // prompts, but just in case, give our prompt an ID.
+ let id = "id" + Cc["@mozilla.org/uuid-generator;1"]
+ .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+
+ messageManager.addMessageListener("Prompt:Close", function listener(message) {
+ if (message.data._remoteId !== id) {
+ return;
+ }
+
+ messageManager.removeMessageListener("Prompt:Close", listener);
+ frameMM.removeEventListener("pagehide", pagehide, true);
+
+ winUtils.leaveModalState();
+ PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
+
+ // Copy the response from the closed prompt into our args, it will be
+ // read by our caller.
+ if (message.data) {
+ for (let key in message.data) {
+ args[key] = message.data[key];
+ }
+ }
+
+ // Exit our nested event loop when we unwind.
+ closed = true;
+ });
+
+ frameMM.addEventListener("pagehide", pagehide, true);
+ function pagehide(e) {
+ // Check whether the event relates to our window or its ancestors
+ let window = domWin;
+ let eventWindow = e.target.defaultView;
+ while (window != eventWindow && window.parent != window) {
+ window = window.parent;
+ }
+ if (window != eventWindow) {
+ return;
+ }
+ frameMM.removeEventListener("pagehide", pagehide, true);
+ messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id });
+ }
+
+ let topPrincipal = domWin.top.document.nodePrincipal;
+ let promptPrincipal = domWin.document.nodePrincipal;
+ args.promptPrincipal = promptPrincipal;
+ args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
+ args.inPermitUnload = inPermitUnload;
+
+ args._remoteId = id;
+
+ messageManager.sendAsyncMessage("Prompt:Open", args, {});
+
+ let thread = Services.tm.currentThread;
+ while (!closed) {
+ thread.processNextEvent(true);
+ }
+}
+
+function ModalPrompter(domWin) {
+ this.domWin = domWin;
+}
+ModalPrompter.prototype = {
+ domWin : null,
+ /*
+ * Default to not using a tab-modal prompt, unless the caller opts in by
+ * QIing to nsIWritablePropertyBag and setting the value of this property
+ * to true.
+ */
+ allowTabModal : false,
+
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt,
+ Ci.nsIAuthPrompt2,
+ Ci.nsIWritablePropertyBag2]),
+
+
+ /* ---------- internal methods ---------- */
+
+
+ openPrompt : function (args) {
+ // Check pref, if false/missing do not ever allow tab-modal prompts.
+ const prefName = "prompts.tab_modal.enabled";
+ let prefValue = false;
+ if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL)
+ prefValue = Services.prefs.getBoolPref(prefName);
+
+ let allowTabModal = this.allowTabModal && prefValue;
+
+ if (allowTabModal && this.domWin) {
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ openRemotePrompt(this.domWin, args, true);
+ return;
+ }
+
+ let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin);
+ if (tabPrompt) {
+ openTabPrompt(this.domWin, tabPrompt, args);
+ return;
+ }
+ }
+
+ // If we can't do a tab modal prompt, fallback to using a window-modal dialog.
+ const COMMON_DIALOG = "chrome://global/content/commonDialog.xul";
+ const SELECT_DIALOG = "chrome://global/content/selectDialog.xul";
+
+ let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG;
+
+ if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
+ args.uri = uri;
+ openRemotePrompt(this.domWin, args);
+ return;
+ }
+
+ let propBag = PromptUtils.objectToPropBag(args);
+ openModalWindow(this.domWin, uri, propBag);
+ PromptUtils.propBagToObject(propBag, args);
+ },
+
+
+
+ /*
+ * ---------- 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 : function() {
+ // 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 : function() {
+ // Both have 6 args, so use types.
+ if (typeof arguments[2] == "object")
+ return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
+ return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
+ },
+
+ promptPassword : function() {
+ // Both have 5 args, so use types.
+ if (typeof arguments[2] == "object")
+ return this.nsIPrompt_promptPassword.apply(this, arguments);
+ return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
+ },
+
+
+ /* ---------- nsIPrompt ---------- */
+
+
+ alert : function (title, text) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("Alert");
+
+ let args = {
+ promptType: "alert",
+ title: title,
+ text: text,
+ };
+
+ this.openPrompt(args);
+ },
+
+ alertCheck : function (title, text, checkLabel, checkValue) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("Alert");
+
+ let args = {
+ promptType: "alertCheck",
+ title: title,
+ text: text,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ };
+
+ this.openPrompt(args);
+
+ // Checkbox state always returned, even if cancel clicked.
+ checkValue.value = args.checked;
+ },
+
+ confirm : function (title, text) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("Confirm");
+
+ let args = {
+ promptType: "confirm",
+ title: title,
+ text: text,
+ ok: false,
+ };
+
+ this.openPrompt(args);
+
+ // Did user click Ok or Cancel?
+ return args.ok;
+ },
+
+ confirmCheck : function (title, text, checkLabel, checkValue) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("ConfirmCheck");
+
+ let args = {
+ promptType: "confirmCheck",
+ title: title,
+ text: text,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ ok: false,
+ };
+
+ this.openPrompt(args);
+
+ // Checkbox state always returned, even if cancel clicked.
+ checkValue.value = args.checked;
+
+ // Did user click Ok or Cancel?
+ return args.ok;
+ },
+
+ confirmEx : function (title, text, flags, button0, button1, button2,
+ checkLabel, checkValue) {
+
+ if (!title)
+ title = PromptUtils.getLocalizedString("Confirm");
+
+ let args = {
+ promptType: "confirmEx",
+ title: title,
+ text: text,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ ok: false,
+ buttonNumClicked: 1,
+ };
+
+ let [label0, label1, label2, defaultButtonNum, isDelayEnabled] =
+ PromptUtils.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;
+ }
+ }
+ }
+
+ this.openPrompt(args);
+
+ // Checkbox state always returned, even if cancel clicked.
+ checkValue.value = args.checked;
+
+ // Get the number of the button the user clicked.
+ return args.buttonNumClicked;
+ },
+
+ nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("Prompt");
+
+ let args = {
+ promptType: "prompt",
+ title: title,
+ text: text,
+ value: value.value,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ ok: false,
+ };
+
+ this.openPrompt(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 : function (title, text, user, pass, checkLabel, checkValue) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2");
+
+ let args = {
+ promptType: "promptUserAndPass",
+ title: title,
+ text: text,
+ user: user.value,
+ pass: pass.value,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ ok: false,
+ };
+
+ this.openPrompt(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ checkValue.value = args.checked;
+ user.value = args.user;
+ pass.value = args.pass;
+ }
+
+ return ok;
+ },
+
+ nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("PromptPassword2");
+
+ let args = {
+ promptType: "promptPassword",
+ title: title,
+ text: text,
+ pass: pass.value,
+ checkLabel: checkLabel,
+ checked: checkValue.value,
+ ok: false,
+ }
+
+ this.openPrompt(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok) {
+ checkValue.value = args.checked;
+ pass.value = args.pass;
+ }
+
+ return ok;
+ },
+
+ select : function (title, text, count, list, selected) {
+ if (!title)
+ title = PromptUtils.getLocalizedString("Select");
+
+ let args = {
+ promptType: "select",
+ title: title,
+ text: text,
+ list: list,
+ selected: -1,
+ ok: false,
+ };
+
+ this.openPrompt(args);
+
+ // Did user click Ok or Cancel?
+ let ok = args.ok;
+ if (ok)
+ selected.value = args.selected;
+
+ return ok;
+ },
+
+
+ /* ---------- nsIAuthPrompt ---------- */
+
+
+ nsIAuthPrompt_prompt : function (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 : function (title, text, passwordRealm, savePassword, user, pass) {
+ // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
+ return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {});
+ },
+
+ nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) {
+ // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
+ return this.nsIPrompt_promptPassword(title, text, pass, null, {});
+ },
+
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+
+ promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
+ let message = PromptUtils.makeAuthMessage(channel, authInfo);
+
+ let [username, password] = PromptUtils.getAuthInfo(authInfo);
+
+ let userParam = { value: username };
+ let passParam = { value: password };
+
+ let ok;
+ if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
+ ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue);
+ else
+ ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue);
+
+ if (ok)
+ PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
+ return ok;
+ },
+
+ asyncPromptAuth : function (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 Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ /* ---------- nsIWritablePropertyBag2 ---------- */
+
+ // Only a partial implementation, for one specific use case...
+
+ setPropertyAsBool : function(name, value) {
+ if (name == "allowTabModal")
+ this.allowTabModal = value;
+ else
+ throw Cr.NS_ERROR_ILLEGAL_VALUE;
+ },
+};
+
+
+function AuthPromptAdapterFactory() {
+}
+AuthPromptAdapterFactory.prototype = {
+ classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"),
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]),
+
+ /* ---------- nsIAuthPromptAdapterFactory ---------- */
+
+ createAdapter : function (oldPrompter) {
+ return new AuthPromptAdapter(oldPrompter);
+ }
+};
+
+
+// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell.
+function AuthPromptAdapter(oldPrompter) {
+ this.oldPrompter = oldPrompter;
+}
+AuthPromptAdapter.prototype = {
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
+ oldPrompter : null,
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+ promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
+ let message = PromptUtils.makeAuthMessage(channel, authInfo);
+
+ let [username, password] = PromptUtils.getAuthInfo(authInfo);
+ let userParam = { value: username };
+ let passParam = { value: password };
+
+ let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo);
+ let authTarget = host + " (" + 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)
+ PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
+ return ok;
+ },
+
+ asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ }
+};
+
+
+// Wrapper using the old embedding contractID, since it's already common in
+// the addon ecosystem.
+function EmbedPrompter() {
+}
+EmbedPrompter.prototype = new Prompter();
+EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}");
+
+var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory];
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
diff --git a/toolkit/components/prompts/src/nsPrompter.manifest b/toolkit/components/prompts/src/nsPrompter.manifest
new file mode 100644
index 000000000..582f6bf59
--- /dev/null
+++ b/toolkit/components/prompts/src/nsPrompter.manifest
@@ -0,0 +1,6 @@
+component {1c978d25-b37f-43a8-a2d6-0c7a239ead87} nsPrompter.js
+contract @mozilla.org/prompter;1 {1c978d25-b37f-43a8-a2d6-0c7a239ead87}
+component {6e134924-6c3a-4d86-81ac-69432dd971dc} nsPrompter.js
+contract @mozilla.org/network/authprompt-adapter-factory;1 {6e134924-6c3a-4d86-81ac-69432dd971dc}
+component {7ad1b327-6dfa-46ec-9234-f2a620ea7e00} nsPrompter.js
+contract @mozilla.org/embedcomp/prompt-service;1 {7ad1b327-6dfa-46ec-9234-f2a620ea7e00}