summaryrefslogtreecommitdiffstats
path: root/mobile/android/components/PromptService.js
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/components/PromptService.js')
-rw-r--r--mobile/android/components/PromptService.js878
1 files changed, 878 insertions, 0 deletions
diff --git a/mobile/android/components/PromptService.js b/mobile/android/components/PromptService.js
new file mode 100644
index 000000000..93aff67ee
--- /dev/null
+++ b/mobile/android/components/PromptService.js
@@ -0,0 +1,878 @@
+/* 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/. */
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
+ "resource://gre/modules/Prompt.jsm");
+
+var gPromptService = null;
+
+function PromptService() {
+ gPromptService = this;
+}
+
+PromptService.prototype = {
+ classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
+
+ /* ---------- nsIPromptFactory ---------- */
+ // XXX Copied from nsPrompter.js.
+ getPrompt: function 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 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 InternalPrompt(domWin);
+ p.QueryInterface(iid);
+ return p;
+ },
+
+ /* ---------- private memebers ---------- */
+
+ // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class
+ callProxy: function(aMethod, aArguments) {
+ let prompt;
+ let domWin = aArguments[0];
+ prompt = new InternalPrompt(domWin);
+ return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1));
+ },
+
+ /* ---------- nsIPromptService ---------- */
+
+ alert: function() {
+ return this.callProxy("alert", arguments);
+ },
+ alertCheck: function() {
+ return this.callProxy("alertCheck", arguments);
+ },
+ confirm: function() {
+ return this.callProxy("confirm", arguments);
+ },
+ confirmCheck: function() {
+ return this.callProxy("confirmCheck", arguments);
+ },
+ confirmEx: function() {
+ return this.callProxy("confirmEx", arguments);
+ },
+ prompt: function() {
+ return this.callProxy("prompt", arguments);
+ },
+ promptUsernameAndPassword: function() {
+ return this.callProxy("promptUsernameAndPassword", arguments);
+ },
+ promptPassword: function() {
+ return this.callProxy("promptPassword", arguments);
+ },
+ select: function() {
+ return this.callProxy("select", arguments);
+ },
+
+ /* ---------- nsIPromptService2 ---------- */
+ promptAuth: function() {
+ return this.callProxy("promptAuth", arguments);
+ },
+ asyncPromptAuth: function() {
+ return this.callProxy("asyncPromptAuth", arguments);
+ }
+};
+
+function InternalPrompt(aDomWin) {
+ this._domWin = aDomWin;
+}
+
+InternalPrompt.prototype = {
+ _domWin: null,
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]),
+
+ /* ---------- internal methods ---------- */
+ _getPrompt: function _getPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState) {
+ let p = new Prompt({
+ window: this._domWin,
+ title: aTitle,
+ message: aText,
+ buttons: aButtons || [
+ PromptUtils.getLocaleString("OK"),
+ PromptUtils.getLocaleString("Cancel")
+ ]
+ });
+ return p;
+ },
+
+ addCheckbox: function addCheckbox(aPrompt, aCheckMsg, aCheckState) {
+ // Don't bother to check for aCheckSate. For nsIPomptService interfaces, aCheckState is an
+ // out param and is required to be defined. If we've gotten here without it, something
+ // has probably gone wrong and we should fail
+ if (aCheckMsg) {
+ aPrompt.addCheckbox({
+ label: PromptUtils.cleanUpLabel(aCheckMsg),
+ checked: aCheckState.value
+ });
+ }
+
+ return aPrompt;
+ },
+
+ addTextbox: function(prompt, value, autofocus, hint) {
+ prompt.addTextbox({
+ value: (value !== null) ? value : "",
+ autofocus: autofocus,
+ hint: hint
+ });
+ },
+
+ addPassword: function(prompt, value, autofocus, hint) {
+ prompt.addPassword({
+ value: (value !== null) ? value : "",
+ autofocus: autofocus,
+ hint: hint
+ });
+ },
+
+ /* Shows a native prompt, and then spins the event loop for this thread while we wait
+ * for a response
+ */
+ showPrompt: function showPrompt(aPrompt) {
+ if (this._domWin) {
+ PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog");
+ let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.enterModalState();
+ }
+
+ let retval = null;
+ aPrompt.show(function(data) {
+ retval = data;
+ });
+
+ // Spin this thread while we wait for a result
+ let thread = Services.tm.currentThread;
+ while (retval == null)
+ thread.processNextEvent(true);
+
+ if (this._domWin) {
+ let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.leaveModalState();
+ PromptUtils.fireDialogEvent(this._domWin, "DOMModalDialogClosed");
+ }
+
+ return retval;
+ },
+
+ /*
+ * ---------- interface disambiguation ----------
+ *
+ * XXX Copied from nsPrompter.js.
+ *
+ * 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 prompt() {
+ if (gPromptService.inContentProcess)
+ return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments)));
+
+ // also, the nsIPrompt flavor has 5 args instead of 6.
+ if (typeof arguments[2] == "object")
+ return this.nsIPrompt_prompt.apply(this, arguments);
+ else
+ return this.nsIAuthPrompt_prompt.apply(this, arguments);
+ },
+
+ promptUsernameAndPassword: function promptUsernameAndPassword() {
+ // Both have 6 args, so use types.
+ if (typeof arguments[2] == "object")
+ return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
+ else
+ return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
+ },
+
+ promptPassword: function promptPassword() {
+ // Both have 5 args, so use types.
+ if (typeof arguments[2] == "object")
+ return this.nsIPrompt_promptPassword.apply(this, arguments);
+ else
+ return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
+ },
+
+ /* ---------- nsIPrompt ---------- */
+
+ alert: function alert(aTitle, aText) {
+ let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
+ p.setHint("alert");
+ this.showPrompt(p);
+ },
+
+ alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) {
+ let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+ },
+
+ confirm: function confirm(aTitle, aText) {
+ let p = this._getPrompt(aTitle, aText);
+ p.setHint("confirm");
+ let data = this.showPrompt(p);
+ return (data.button == 0);
+ },
+
+ confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) {
+ let p = this._getPrompt(aTitle, aText, null);
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+ let ok = data.button == 0;
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+ return ok;
+ },
+
+ confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0,
+ aButton1, aButton2, aCheckMsg, aCheckState) {
+ let buttons = [];
+ let titles = [aButton0, aButton1, aButton2];
+ for (let i = 0; i < 3; i++) {
+ let bTitle = null;
+ switch (aButtonFlags & 0xff) {
+ case Ci.nsIPromptService.BUTTON_TITLE_OK :
+ bTitle = PromptUtils.getLocaleString("OK");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_CANCEL :
+ bTitle = PromptUtils.getLocaleString("Cancel");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_YES :
+ bTitle = PromptUtils.getLocaleString("Yes");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_NO :
+ bTitle = PromptUtils.getLocaleString("No");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_SAVE :
+ bTitle = PromptUtils.getLocaleString("Save");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE :
+ bTitle = PromptUtils.getLocaleString("DontSave");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_REVERT :
+ bTitle = PromptUtils.getLocaleString("Revert");
+ break;
+ case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING :
+ bTitle = PromptUtils.cleanUpLabel(titles[i]);
+ break;
+ }
+
+ if (bTitle)
+ buttons.push(bTitle);
+
+ aButtonFlags >>= 8;
+ }
+
+ let p = this._getPrompt(aTitle, aText, buttons);
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+ return data.button;
+ },
+
+ nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) {
+ let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
+ p.setHint("prompt");
+ this.addTextbox(p, aValue.value, true);
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+
+ let ok = data.button == 0;
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+ if (ok)
+ aValue.value = data.textbox0;
+ return ok;
+ },
+
+ nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
+ aTitle, aText, aPassword, aCheckMsg, aCheckState) {
+ let p = this._getPrompt(aTitle, aText, null);
+ this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr"));
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+
+ let ok = data.button == 0;
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+ if (ok)
+ aPassword.value = data.password0;
+ return ok;
+ },
+
+ nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword(
+ aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) {
+ let p = this._getPrompt(aTitle, aText, null);
+ this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr"));
+ this.addPassword(p, aPassword.value, false, PromptUtils.getLocaleString("password", "passwdmgr"));
+ this.addCheckbox(p, aCheckMsg, aCheckState);
+ let data = this.showPrompt(p);
+
+ let ok = data.button == 0;
+ if (aCheckState && data.button > -1)
+ aCheckState.value = data.checkbox0;
+
+ if (ok) {
+ aUsername.value = data.textbox0;
+ aPassword.value = data.password0;
+ }
+ return ok;
+ },
+
+ select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) {
+ let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
+ p.addMenulist({ values: aSelectList });
+ let data = this.showPrompt(p);
+
+ let ok = data.button == 0;
+ if (ok)
+ aOutSelection.value = data.menulist0;
+
+ return ok;
+ },
+
+ /* ---------- nsIAuthPrompt ---------- */
+
+ nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
+ // TODO: Port functions from nsLoginManagerPrompter.js to here
+ if (defaultText)
+ result.value = defaultText;
+ return this.nsIPrompt_prompt(title, text, result, null, {});
+ },
+
+ nsIAuthPrompt_promptUsernameAndPassword : function(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) {
+ return this.nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass);
+ },
+
+ nsIAuthPrompt_promptPassword : function(aTitle, aText, aPasswordRealm, aSavePassword, aPass) {
+ return this.nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass);
+ },
+
+ nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) {
+ let checkMsg = null;
+ let check = { value: false };
+ let hostname, realm;
+ [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm);
+
+ let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword);
+ if (canSave) {
+ // Look for existing logins.
+ let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm);
+ [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass);
+ }
+
+ // (eslint-disable: see bug 1177904)
+ let ok = false;
+ if (aUser)
+ ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check); // eslint-disable-line no-undef
+ else
+ ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check); // eslint-disable-line no-undef
+
+ if (ok && canSave && check.value)
+ PromptUtils.savePassword(hostname, realm, aUser, aPass);
+
+ return ok;
+ },
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+ promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) {
+ let checkMsg = null;
+ let check = { value: false };
+ let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
+ let [username, password] = PromptUtils.getAuthInfo(aAuthInfo);
+ let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
+ let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
+
+ let canSave = PromptUtils.canSaveLogin(hostname, null);
+ if (canSave)
+ [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password);
+
+ if (username.value && password.value) {
+ PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
+ }
+
+ let canAutologin = false;
+ if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY &&
+ !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) &&
+ Services.prefs.getBoolPref("signon.autologin.proxy"))
+ canAutologin = true;
+
+ let ok = canAutologin;
+ if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
+ ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check);
+ else if (!ok)
+ ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check);
+
+ PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
+
+ if (ok && canSave && check.value)
+ PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm);
+
+ return ok;
+ },
+
+ _asyncPrompts: {},
+ _asyncPromptInProgress: false,
+
+ _doAsyncPrompt : function() {
+ if (this._asyncPromptInProgress)
+ return;
+
+ // Find the first prompt key we have in the queue
+ let hashKey = null;
+ for (hashKey in this._asyncPrompts)
+ break;
+
+ if (!hashKey)
+ return;
+
+ // If login manger has logins for this host, defer prompting if we're
+ // already waiting on a master password entry.
+ let prompt = this._asyncPrompts[hashKey];
+ let prompter = prompt.prompter;
+ let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo);
+ let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
+ if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy)
+ return;
+
+ this._asyncPromptInProgress = true;
+ prompt.inProgress = true;
+
+ let self = this;
+
+ let runnable = {
+ run: function() {
+ let ok = false;
+ try {
+ ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo);
+ } catch (e) {
+ Cu.reportError("_doAsyncPrompt:run: " + e + "\n");
+ }
+
+ delete self._asyncPrompts[hashKey];
+ prompt.inProgress = false;
+ self._asyncPromptInProgress = false;
+
+ for (let consumer of prompt.consumers) {
+ if (!consumer.callback)
+ // Not having a callback means that consumer didn't provide it
+ // or canceled the notification
+ continue;
+
+ try {
+ if (ok)
+ consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo);
+ else
+ consumer.callback.onAuthCancelled(consumer.context, true);
+ } catch (e) { /* Throw away exceptions caused by callback */ }
+ }
+ self._doAsyncPrompt();
+ }
+ }
+
+ Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL);
+ },
+
+ asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) {
+ let cancelable = null;
+ try {
+ // If the user submits a login but it fails, we need to remove the
+ // notification bar that was displayed. Conveniently, the user will
+ // be prompted for authentication again, which brings us here.
+ //this._removeLoginNotifications();
+
+ cancelable = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+ callback: aCallback,
+ context: aContext,
+ cancel: function() {
+ this.callback.onAuthCancelled(this.context, false);
+ this.callback = null;
+ this.context = null;
+ }
+ };
+ let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
+ let hashKey = aLevel + "|" + hostname + "|" + httpRealm;
+ let asyncPrompt = this._asyncPrompts[hashKey];
+ if (asyncPrompt) {
+ asyncPrompt.consumers.push(cancelable);
+ return cancelable;
+ }
+
+ asyncPrompt = {
+ consumers: [cancelable],
+ channel: aChannel,
+ authInfo: aAuthInfo,
+ level: aLevel,
+ inProgress : false,
+ prompter: this
+ }
+
+ this._asyncPrompts[hashKey] = asyncPrompt;
+ this._doAsyncPrompt();
+ } catch (e) {
+ Cu.reportError("PromptService: " + e + "\n");
+ throw e;
+ }
+ return cancelable;
+ }
+};
+
+var PromptUtils = {
+ getLocaleString: function pu_getLocaleString(aKey, aService) {
+ if (aService == "passwdmgr")
+ return this.cleanUpLabel(this.passwdBundle.GetStringFromName(aKey));
+
+ return this.cleanUpLabel(this.bundle.GetStringFromName(aKey));
+ },
+
+ //
+ // Copied from chrome://global/content/commonDialog.js
+ //
+ cleanUpLabel: function cleanUpLabel(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,
+ // remove the access key placeholder + leading spaces from the label.
+ // Otherwise a character preceded by one but not two &s is the access key.
+
+ // Note that if you change the following code, see the comment of
+ // nsTextBoxFrame::UpdateAccessTitle.
+ if (!aLabel)
+ return "";
+
+ if (/ *\(\&([^&])\)(:?)$/.test(aLabel)) {
+ aLabel = RegExp.leftContext + RegExp.$2;
+ } else if (/^([^&]*)\&(([^&]).*$)/.test(aLabel)) {
+ aLabel = RegExp.$1 + RegExp.$2;
+ }
+
+ // Special code for using that & symbol
+ aLabel = aLabel.replace(/\&\&/g, "&");
+
+ return aLabel;
+ },
+
+ get pwmgr() {
+ delete this.pwmgr;
+ return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+ },
+
+ getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) {
+ let httpRealm = /^.+ \(.+\)$/;
+ if (httpRealm.test(aRealmString))
+ return [null, null, null];
+
+ let uri = Services.io.newURI(aRealmString, null, null);
+ let pathname = "";
+
+ if (uri.path != "/")
+ pathname = uri.path;
+
+ let formattedHostname = this._getFormattedHostname(uri);
+ return [formattedHostname, formattedHostname + pathname, uri.username];
+ },
+
+ canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) {
+ let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname)
+ if (aSavePassword)
+ canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY)
+ return canSave;
+ },
+
+ getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) {
+ let checkLabel = null;
+ let check = { value: false };
+ let selectedLogin;
+
+ checkLabel = this.getLocaleString("rememberButton", "passwdmgr");
+
+ // XXX Like the original code, we can't deal with multiple
+ // account selection. (bug 227632)
+ if (aFoundLogins.length > 0) {
+ selectedLogin = aFoundLogins[0];
+
+ // If the caller provided a username, try to use it. If they
+ // provided only a password, this will try to find a password-only
+ // login (or return null if none exists).
+ if (aUser.value)
+ selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value);
+
+ if (selectedLogin) {
+ check.value = true;
+ aUser.value = selectedLogin.username;
+ // If the caller provided a password, prefer it.
+ if (!aPass.value)
+ aPass.value = selectedLogin.password;
+ }
+ }
+
+ return [checkLabel, check];
+ },
+
+ findLogin: function pu_findLogin(aLogins, aName, aValue) {
+ for (let i = 0; i < aLogins.length; i++)
+ if (aLogins[i][aName] == aValue)
+ return aLogins[i];
+ return null;
+ },
+
+ savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) {
+ let selectedLogin = this.findLogin(aLogins, "username", aUser.value);
+
+ // If we didn't find an existing login, or if the username
+ // changed, save as a new login.
+ if (!selectedLogin) {
+ // add as new
+ var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+ newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", "");
+ this.pwmgr.addLogin(newLogin);
+ } else if (aPass.value != selectedLogin.password) {
+ // update password
+ this.updateLogin(selectedLogin, aPass.value);
+ } else {
+ this.updateLogin(selectedLogin);
+ }
+ },
+
+ updateLogin: function pu_updateLogin(aLogin, aPassword) {
+ let now = Date.now();
+ let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
+ if (aPassword) {
+ propBag.setProperty("password", aPassword);
+ // Explicitly set the password change time here (even though it would
+ // be changed automatically), to ensure that it's exactly the same
+ // value as timeLastUsed.
+ propBag.setProperty("timePasswordChanged", now);
+ }
+ propBag.setProperty("timeLastUsed", now);
+ propBag.setProperty("timesUsedIncrement", 1);
+
+ this.pwmgr.modifyLogin(aLogin, propBag);
+ },
+
+ // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/nsPrompt.cpp#388
+ makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) {
+ let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
+ let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
+ let isCrossOrig = (aAuthInfo.flags &
+ Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE);
+
+ let username = aAuthInfo.username;
+ let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo);
+
+ // Suppress "the site says: $realm" when we synthesized a missing realm.
+ if (!aAuthInfo.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 = this.bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost], 2);
+ } else if (isPassOnly) {
+ text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
+ } else if (isCrossOrig) {
+ text = this.bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost], 1);
+ } else if (!realm) {
+ text = this.bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost], 1);
+ } else {
+ text = this.bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost], 2);
+ }
+
+ return text;
+ },
+
+ // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/nsPromptUtils.h#89
+ getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) {
+ let uri = aChannel.URI;
+ let res = { host: null, port: -1 };
+ if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) {
+ let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel);
+ res.host = proxy.proxyInfo.host;
+ res.port = proxy.proxyInfo.port;
+ } else {
+ res.host = uri.host;
+ res.port = uri.port;
+ }
+ return res;
+ },
+
+ getAuthTarget : function pu_getAuthTarget(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];
+ },
+
+ getAuthInfo : function pu_getAuthInfo(aAuthInfo) {
+ let flags = aAuthInfo.flags;
+ let username = {value: ""};
+ let password = {value: ""};
+
+ if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain)
+ username.value = aAuthInfo.domain + "\\" + aAuthInfo.username;
+ else
+ username.value = aAuthInfo.username;
+
+ password.value = aAuthInfo.password
+
+ return [username, password];
+ },
+
+ setAuthInfo : function (aAuthInfo, username, password) {
+ var flags = aAuthInfo.flags;
+ if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
+ // Domain is separated from username by a backslash
+ var idx = username.indexOf("\\");
+ if (idx == -1) {
+ aAuthInfo.username = username;
+ } else {
+ aAuthInfo.domain = username.substring(0, idx);
+ aAuthInfo.username = username.substring(idx+1);
+ }
+ } else {
+ aAuthInfo.username = username;
+ }
+ aAuthInfo.password = password;
+ },
+
+ /**
+ * Strip out things like userPass and path for display.
+ */
+ getFormattedHostname : function pu_getFormattedHostname(uri) {
+ return uri.scheme + "://" + uri.hostPort;
+ },
+
+ fireDialogEvent: function(aDomWin, aEventName) {
+ // accessing the document object can throw if this window no longer exists. See bug 789888.
+ try {
+ if (!aDomWin.document)
+ return;
+ let event = aDomWin.document.createEvent("Events");
+ event.initEvent(aEventName, true, true);
+ let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ winUtils.dispatchEventToChromeOnly(aDomWin, event);
+ } catch(ex) {
+ }
+ }
+};
+
+XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () {
+ return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
+});
+
+XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () {
+ return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties");
+});
+
+
+// Factory for wrapping nsIAuthPrompt interfaces to make them usable via an nsIAuthPrompt2 interface.
+// XXX Copied from nsPrompter.js.
+function AuthPromptAdapterFactory() {
+}
+
+AuthPromptAdapterFactory.prototype = {
+ classID: Components.ID("{80dae1e9-e0d2-4974-915f-f97050fa8068}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]),
+
+ /* ---------- nsIAuthPromptAdapterFactory ---------- */
+
+ createAdapter: function(aPrompt) {
+ return new AuthPromptAdapter(aPrompt);
+ }
+};
+
+
+// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell.
+// XXX Copied from nsPrompter.js.
+function AuthPromptAdapter(aPrompt) {
+ this.prompt = aPrompt;
+}
+
+AuthPromptAdapter.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
+ prompt: null,
+
+ /* ---------- nsIAuthPrompt2 ---------- */
+
+ promptAuth: function(aChannel, aLevel, aAuthInfo, aCheckLabel, aCheckValue) {
+ let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
+
+ let [username, password] = PromptUtils.getAuthInfo(aAuthInfo);
+ let [host, realm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
+ let authTarget = host + " (" + realm + ")";
+
+ let ok;
+ if (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) {
+ ok = this.prompt.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, password);
+ } else {
+ ok = this.prompt.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, username, password);
+ }
+
+ if (ok) {
+ PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
+ }
+ return ok;
+ },
+
+ asyncPromptAuth: function(aChannel, aCallback, aContext, aLevel, aAuthInfo, aCheckLabel, aCheckValue) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService, AuthPromptAdapterFactory]);