michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cr = Components.results; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/Prompt.jsm"); michael@0: michael@0: var gPromptService = null; michael@0: michael@0: function PromptService() { michael@0: gPromptService = this; michael@0: } michael@0: michael@0: PromptService.prototype = { michael@0: classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), michael@0: michael@0: /* ---------- nsIPromptFactory ---------- */ michael@0: // XXX Copied from nsPrompter.js. michael@0: getPrompt: function getPrompt(domWin, iid) { michael@0: let doc = this.getDocument(); michael@0: if (!doc) { michael@0: let fallback = this._getFallbackService(); michael@0: return fallback.QueryInterface(Ci.nsIPromptFactory).getPrompt(domWin, iid); michael@0: } michael@0: michael@0: let p = new InternalPrompt(domWin, doc); michael@0: p.QueryInterface(iid); michael@0: return p; michael@0: }, michael@0: michael@0: /* ---------- private memebers ---------- */ michael@0: michael@0: _getFallbackService: function _getFallbackService() { michael@0: return Components.classesByID["{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"] michael@0: .getService(Ci.nsIPromptService); michael@0: }, michael@0: michael@0: getDocument: function getDocument() { michael@0: let win = Services.wm.getMostRecentWindow("navigator:browser"); michael@0: return win ? win.document : null; michael@0: }, michael@0: michael@0: // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class michael@0: // if we can show in-document popups, or to the fallback service otherwise. michael@0: callProxy: function(aMethod, aArguments) { michael@0: let prompt; michael@0: let doc = this.getDocument(); michael@0: if (!doc) { michael@0: let fallback = this._getFallbackService(); michael@0: return fallback[aMethod].apply(fallback, aArguments); michael@0: } michael@0: let domWin = aArguments[0]; michael@0: prompt = new InternalPrompt(domWin, doc); michael@0: return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1)); michael@0: }, michael@0: michael@0: /* ---------- nsIPromptService ---------- */ michael@0: michael@0: alert: function() { michael@0: return this.callProxy("alert", arguments); michael@0: }, michael@0: alertCheck: function() { michael@0: return this.callProxy("alertCheck", arguments); michael@0: }, michael@0: confirm: function() { michael@0: return this.callProxy("confirm", arguments); michael@0: }, michael@0: confirmCheck: function() { michael@0: return this.callProxy("confirmCheck", arguments); michael@0: }, michael@0: confirmEx: function() { michael@0: return this.callProxy("confirmEx", arguments); michael@0: }, michael@0: prompt: function() { michael@0: return this.callProxy("prompt", arguments); michael@0: }, michael@0: promptUsernameAndPassword: function() { michael@0: return this.callProxy("promptUsernameAndPassword", arguments); michael@0: }, michael@0: promptPassword: function() { michael@0: return this.callProxy("promptPassword", arguments); michael@0: }, michael@0: select: function() { michael@0: return this.callProxy("select", arguments); michael@0: }, michael@0: michael@0: /* ---------- nsIPromptService2 ---------- */ michael@0: promptAuth: function() { michael@0: return this.callProxy("promptAuth", arguments); michael@0: }, michael@0: asyncPromptAuth: function() { michael@0: return this.callProxy("asyncPromptAuth", arguments); michael@0: } michael@0: }; michael@0: michael@0: function InternalPrompt(aDomWin, aDocument) { michael@0: this._domWin = aDomWin; michael@0: this._doc = aDocument; michael@0: } michael@0: michael@0: InternalPrompt.prototype = { michael@0: _domWin: null, michael@0: _doc: null, michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]), michael@0: michael@0: /* ---------- internal methods ---------- */ michael@0: _getPrompt: function _getPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState) { michael@0: let p = new Prompt({ michael@0: window: this._domWin, michael@0: title: aTitle, michael@0: message: aText, michael@0: buttons: aButtons || [ michael@0: PromptUtils.getLocaleString("OK"), michael@0: PromptUtils.getLocaleString("Cancel") michael@0: ] michael@0: }); michael@0: return p; michael@0: }, michael@0: michael@0: addCheckbox: function addCheckbox(aPrompt, aCheckMsg, aCheckState) { michael@0: // Don't bother to check for aCheckSate. For nsIPomptService interfaces, aCheckState is an michael@0: // out param and is required to be defined. If we've gotten here without it, something michael@0: // has probably gone wrong and we should fail michael@0: if (aCheckMsg) { michael@0: aPrompt.addCheckbox({ michael@0: label: PromptUtils.cleanUpLabel(aCheckMsg), michael@0: checked: aCheckState.value michael@0: }); michael@0: } michael@0: michael@0: return aPrompt; michael@0: }, michael@0: michael@0: addTextbox: function(prompt, value, autofocus, hint) { michael@0: prompt.addTextbox({ michael@0: value: (value !== null) ? value : "", michael@0: autofocus: autofocus, michael@0: hint: hint michael@0: }); michael@0: }, michael@0: michael@0: addPassword: function(prompt, value, autofocus, hint) { michael@0: prompt.addPassword({ michael@0: value: (value !== null) ? value : "", michael@0: autofocus: autofocus, michael@0: hint: hint michael@0: }); michael@0: }, michael@0: michael@0: /* Shows a native prompt, and then spins the event loop for this thread while we wait michael@0: * for a response michael@0: */ michael@0: showPrompt: function showPrompt(aPrompt) { michael@0: if (this._domWin) { michael@0: PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); michael@0: let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.enterModalState(); michael@0: } michael@0: michael@0: let retval = null; michael@0: aPrompt.show(function(data) { michael@0: retval = data; michael@0: }); michael@0: michael@0: // Spin this thread while we wait for a result michael@0: let thread = Services.tm.currentThread; michael@0: while (retval == null) michael@0: thread.processNextEvent(true); michael@0: michael@0: if (this._domWin) { michael@0: let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.leaveModalState(); michael@0: PromptUtils.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); michael@0: } michael@0: michael@0: return retval; michael@0: }, michael@0: michael@0: /* michael@0: * ---------- interface disambiguation ---------- michael@0: * michael@0: * XXX Copied from nsPrompter.js. michael@0: * michael@0: * nsIPrompt and nsIAuthPrompt share 3 method names with slightly michael@0: * different arguments. All but prompt() have the same number of michael@0: * arguments, so look at the arg types to figure out how we're being michael@0: * called. :-( michael@0: */ michael@0: prompt: function prompt() { michael@0: if (gPromptService.inContentProcess) michael@0: return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments))); michael@0: michael@0: // also, the nsIPrompt flavor has 5 args instead of 6. michael@0: if (typeof arguments[2] == "object") michael@0: return this.nsIPrompt_prompt.apply(this, arguments); michael@0: else michael@0: return this.nsIAuthPrompt_prompt.apply(this, arguments); michael@0: }, michael@0: michael@0: promptUsernameAndPassword: function promptUsernameAndPassword() { michael@0: // Both have 6 args, so use types. michael@0: if (typeof arguments[2] == "object") michael@0: return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); michael@0: else michael@0: return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); michael@0: }, michael@0: michael@0: promptPassword: function promptPassword() { michael@0: // Both have 5 args, so use types. michael@0: if (typeof arguments[2] == "object") michael@0: return this.nsIPrompt_promptPassword.apply(this, arguments); michael@0: else michael@0: return this.nsIAuthPrompt_promptPassword.apply(this, arguments); michael@0: }, michael@0: michael@0: /* ---------- nsIPrompt ---------- */ michael@0: michael@0: alert: function alert(aTitle, aText) { michael@0: let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); michael@0: p.setHint("alert"); michael@0: this.showPrompt(p); michael@0: }, michael@0: michael@0: alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) { michael@0: let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: }, michael@0: michael@0: confirm: function confirm(aTitle, aText) { michael@0: let p = this._getPrompt(aTitle, aText); michael@0: p.setHint("confirm"); michael@0: let data = this.showPrompt(p); michael@0: return (data.button == 0); michael@0: }, michael@0: michael@0: confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) { michael@0: let p = this._getPrompt(aTitle, aText, null); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: let ok = data.button == 0; michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: return ok; michael@0: }, michael@0: michael@0: confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0, michael@0: aButton1, aButton2, aCheckMsg, aCheckState) { michael@0: let buttons = []; michael@0: let titles = [aButton0, aButton1, aButton2]; michael@0: for (let i = 0; i < 3; i++) { michael@0: let bTitle = null; michael@0: switch (aButtonFlags & 0xff) { michael@0: case Ci.nsIPromptService.BUTTON_TITLE_OK : michael@0: bTitle = PromptUtils.getLocaleString("OK"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_CANCEL : michael@0: bTitle = PromptUtils.getLocaleString("Cancel"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_YES : michael@0: bTitle = PromptUtils.getLocaleString("Yes"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_NO : michael@0: bTitle = PromptUtils.getLocaleString("No"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_SAVE : michael@0: bTitle = PromptUtils.getLocaleString("Save"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE : michael@0: bTitle = PromptUtils.getLocaleString("DontSave"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_REVERT : michael@0: bTitle = PromptUtils.getLocaleString("Revert"); michael@0: break; michael@0: case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING : michael@0: bTitle = PromptUtils.cleanUpLabel(titles[i]); michael@0: break; michael@0: } michael@0: michael@0: if (bTitle) michael@0: buttons.push(bTitle); michael@0: michael@0: aButtonFlags >>= 8; michael@0: } michael@0: michael@0: let p = this._getPrompt(aTitle, aText, buttons); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: return data.button; michael@0: }, michael@0: michael@0: nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) { michael@0: let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState); michael@0: p.setHint("prompt"); michael@0: this.addTextbox(p, aValue.value, true); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: michael@0: let ok = data.button == 0; michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: if (ok) michael@0: aValue.value = data.textbox0; michael@0: return ok; michael@0: }, michael@0: michael@0: nsIPrompt_promptPassword: function nsIPrompt_promptPassword( michael@0: aTitle, aText, aPassword, aCheckMsg, aCheckState) { michael@0: let p = this._getPrompt(aTitle, aText, null); michael@0: this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr")); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: michael@0: let ok = data.button == 0; michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: if (ok) michael@0: aPassword.value = data.password0; michael@0: return ok; michael@0: }, michael@0: michael@0: nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword( michael@0: aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) { michael@0: let p = this._getPrompt(aTitle, aText, null); michael@0: this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr")); michael@0: this.addPassword(p, aPassword.value, false, PromptUtils.getLocaleString("password", "passwdmgr")); michael@0: this.addCheckbox(p, aCheckMsg, aCheckState); michael@0: let data = this.showPrompt(p); michael@0: michael@0: let ok = data.button == 0; michael@0: if (aCheckState && data.button > -1) michael@0: aCheckState.value = data.checkbox0; michael@0: michael@0: if (ok) { michael@0: aUsername.value = data.textbox0; michael@0: aPassword.value = data.password0; michael@0: } michael@0: return ok; michael@0: }, michael@0: michael@0: select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) { michael@0: let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); michael@0: p.addMenulist({ values: aSelectList }); michael@0: let data = this.showPrompt(p); michael@0: michael@0: let ok = data.button == 0; michael@0: if (ok) michael@0: aOutSelection.value = data.menulist0; michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: /* ---------- nsIAuthPrompt ---------- */ michael@0: michael@0: nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { michael@0: // TODO: Port functions from nsLoginManagerPrompter.js to here michael@0: if (defaultText) michael@0: result.value = defaultText; michael@0: return this.nsIPrompt_prompt(title, text, result, null, {}); michael@0: }, michael@0: michael@0: nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) { michael@0: return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass); michael@0: }, michael@0: michael@0: nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) { michael@0: return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass); michael@0: }, michael@0: michael@0: nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) { michael@0: let checkMsg = null; michael@0: let check = { value: false }; michael@0: let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm); michael@0: michael@0: let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword); michael@0: if (canSave) { michael@0: // Look for existing logins. michael@0: let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm); michael@0: [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass); michael@0: } michael@0: michael@0: let ok = false; michael@0: if (aUser) michael@0: ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check); michael@0: else michael@0: ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check); michael@0: michael@0: if (ok && canSave && check.value) michael@0: PromptUtils.savePassword(hostname, realm, aUser, aPass); michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: /* ---------- nsIAuthPrompt2 ---------- */ michael@0: michael@0: promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) { michael@0: let checkMsg = null; michael@0: let check = { value: false }; michael@0: let message = PromptUtils.makeDialogText(aChannel, aAuthInfo); michael@0: let [username, password] = PromptUtils.getAuthInfo(aAuthInfo); michael@0: let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); michael@0: let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); michael@0: michael@0: let canSave = PromptUtils.canSaveLogin(hostname, null); michael@0: if (canSave) michael@0: [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password); michael@0: michael@0: if (username.value && password.value) { michael@0: PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); michael@0: } michael@0: michael@0: let canAutologin = false; michael@0: if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY && michael@0: !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) && michael@0: Services.prefs.getBoolPref("signon.autologin.proxy")) michael@0: canAutologin = true; michael@0: michael@0: let ok = canAutologin; michael@0: if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) michael@0: ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check); michael@0: else if (!ok) michael@0: ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check); michael@0: michael@0: PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); michael@0: michael@0: if (ok && canSave && check.value) michael@0: PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm); michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: _asyncPrompts: {}, michael@0: _asyncPromptInProgress: false, michael@0: michael@0: _doAsyncPrompt : function() { michael@0: if (this._asyncPromptInProgress) michael@0: return; michael@0: michael@0: // Find the first prompt key we have in the queue michael@0: let hashKey = null; michael@0: for (hashKey in this._asyncPrompts) michael@0: break; michael@0: michael@0: if (!hashKey) michael@0: return; michael@0: michael@0: // If login manger has logins for this host, defer prompting if we're michael@0: // already waiting on a master password entry. michael@0: let prompt = this._asyncPrompts[hashKey]; michael@0: let prompter = prompt.prompter; michael@0: let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo); michael@0: let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); michael@0: if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy) michael@0: return; michael@0: michael@0: this._asyncPromptInProgress = true; michael@0: prompt.inProgress = true; michael@0: michael@0: let self = this; michael@0: michael@0: let runnable = { michael@0: run: function() { michael@0: let ok = false; michael@0: try { michael@0: ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo); michael@0: } catch (e) { michael@0: Cu.reportError("_doAsyncPrompt:run: " + e + "\n"); michael@0: } michael@0: michael@0: delete self._asyncPrompts[hashKey]; michael@0: prompt.inProgress = false; michael@0: self._asyncPromptInProgress = false; michael@0: michael@0: for (let consumer of prompt.consumers) { michael@0: if (!consumer.callback) michael@0: // Not having a callback means that consumer didn't provide it michael@0: // or canceled the notification michael@0: continue; michael@0: michael@0: try { michael@0: if (ok) michael@0: consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo); michael@0: else michael@0: consumer.callback.onAuthCancelled(consumer.context, true); michael@0: } catch (e) { /* Throw away exceptions caused by callback */ } michael@0: } michael@0: self._doAsyncPrompt(); michael@0: } michael@0: } michael@0: michael@0: Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL); michael@0: }, michael@0: michael@0: asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) { michael@0: let cancelable = null; michael@0: try { michael@0: // If the user submits a login but it fails, we need to remove the michael@0: // notification bar that was displayed. Conveniently, the user will michael@0: // be prompted for authentication again, which brings us here. michael@0: //this._removeLoginNotifications(); michael@0: michael@0: cancelable = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), michael@0: callback: aCallback, michael@0: context: aContext, michael@0: cancel: function() { michael@0: this.callback.onAuthCancelled(this.context, false); michael@0: this.callback = null; michael@0: this.context = null; michael@0: } michael@0: }; michael@0: let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); michael@0: let hashKey = aLevel + "|" + hostname + "|" + httpRealm; michael@0: let asyncPrompt = this._asyncPrompts[hashKey]; michael@0: if (asyncPrompt) { michael@0: asyncPrompt.consumers.push(cancelable); michael@0: return cancelable; michael@0: } michael@0: michael@0: asyncPrompt = { michael@0: consumers: [cancelable], michael@0: channel: aChannel, michael@0: authInfo: aAuthInfo, michael@0: level: aLevel, michael@0: inProgress : false, michael@0: prompter: this michael@0: } michael@0: michael@0: this._asyncPrompts[hashKey] = asyncPrompt; michael@0: this._doAsyncPrompt(); michael@0: } catch (e) { michael@0: Cu.reportError("PromptService: " + e + "\n"); michael@0: throw e; michael@0: } michael@0: return cancelable; michael@0: } michael@0: }; michael@0: michael@0: let PromptUtils = { michael@0: getLocaleString: function pu_getLocaleString(aKey, aService) { michael@0: if (aService == "passwdmgr") michael@0: return this.cleanUpLabel(this.passwdBundle.GetStringFromName(aKey)); michael@0: michael@0: return this.cleanUpLabel(this.bundle.GetStringFromName(aKey)); michael@0: }, michael@0: michael@0: // michael@0: // Copied from chrome://global/content/commonDialog.js michael@0: // michael@0: cleanUpLabel: function cleanUpLabel(aLabel) { michael@0: // This is for labels which may contain embedded access keys. michael@0: // If we end in (&X) where X represents the access key, optionally preceded michael@0: // by spaces and/or followed by the ':' character, michael@0: // remove the access key placeholder + leading spaces from the label. michael@0: // Otherwise a character preceded by one but not two &s is the access key. michael@0: michael@0: // Note that if you change the following code, see the comment of michael@0: // nsTextBoxFrame::UpdateAccessTitle. michael@0: if (!aLabel) michael@0: return ""; michael@0: michael@0: if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) { michael@0: aLabel = RegExp.leftContext + RegExp.$2; michael@0: } else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) { michael@0: aLabel = RegExp.$1 + RegExp.$2; michael@0: } michael@0: michael@0: // Special code for using that & symbol michael@0: aLabel = aLabel.replace(/\&\&/g, "&"); michael@0: michael@0: return aLabel; michael@0: }, michael@0: michael@0: get pwmgr() { michael@0: delete this.pwmgr; michael@0: return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); michael@0: }, michael@0: michael@0: getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) { michael@0: let httpRealm = /^.+ \(.+\)$/; michael@0: if (httpRealm.test(aRealmString)) michael@0: return [null, null, null]; michael@0: michael@0: let uri = Services.io.newURI(aRealmString, null, null); michael@0: let pathname = ""; michael@0: michael@0: if (uri.path != "/") michael@0: pathname = uri.path; michael@0: michael@0: let formattedHostname = this._getFormattedHostname(uri); michael@0: return [formattedHostname, formattedHostname + pathname, uri.username]; michael@0: }, michael@0: michael@0: canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) { michael@0: let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname) michael@0: if (aSavePassword) michael@0: canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) michael@0: return canSave; michael@0: }, michael@0: michael@0: getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) { michael@0: let checkLabel = null; michael@0: let check = { value: false }; michael@0: let selectedLogin; michael@0: michael@0: checkLabel = this.getLocaleString("saveButton", "passwdmgr"); michael@0: michael@0: // XXX Like the original code, we can't deal with multiple michael@0: // account selection. (bug 227632) michael@0: if (aFoundLogins.length > 0) { michael@0: selectedLogin = aFoundLogins[0]; michael@0: michael@0: // If the caller provided a username, try to use it. If they michael@0: // provided only a password, this will try to find a password-only michael@0: // login (or return null if none exists). michael@0: if (aUser.value) michael@0: selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value); michael@0: michael@0: if (selectedLogin) { michael@0: check.value = true; michael@0: aUser.value = selectedLogin.username; michael@0: // If the caller provided a password, prefer it. michael@0: if (!aPass.value) michael@0: aPass.value = selectedLogin.password; michael@0: } michael@0: } michael@0: michael@0: return [checkLabel, check]; michael@0: }, michael@0: michael@0: findLogin: function pu_findLogin(aLogins, aName, aValue) { michael@0: for (let i = 0; i < aLogins.length; i++) michael@0: if (aLogins[i][aName] == aValue) michael@0: return aLogins[i]; michael@0: return null; michael@0: }, michael@0: michael@0: savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) { michael@0: let selectedLogin = this.findLogin(aLogins, "username", aUser.value); michael@0: michael@0: // If we didn't find an existing login, or if the username michael@0: // changed, save as a new login. michael@0: if (!selectedLogin) { michael@0: // add as new michael@0: var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); michael@0: newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", ""); michael@0: this.pwmgr.addLogin(newLogin); michael@0: } else if (aPass.value != selectedLogin.password) { michael@0: // update password michael@0: this.updateLogin(selectedLogin, aPass.value); michael@0: } else { michael@0: this.updateLogin(selectedLogin); michael@0: } michael@0: }, michael@0: michael@0: updateLogin: function pu_updateLogin(aLogin, aPassword) { michael@0: let now = Date.now(); michael@0: let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag); michael@0: if (aPassword) { michael@0: propBag.setProperty("password", aPassword); michael@0: // Explicitly set the password change time here (even though it would michael@0: // be changed automatically), to ensure that it's exactly the same michael@0: // value as timeLastUsed. michael@0: propBag.setProperty("timePasswordChanged", now); michael@0: } michael@0: propBag.setProperty("timeLastUsed", now); michael@0: propBag.setProperty("timesUsedIncrement", 1); michael@0: michael@0: this.pwmgr.modifyLogin(aLogin, propBag); michael@0: }, michael@0: michael@0: // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388 michael@0: makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) { michael@0: let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); michael@0: let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); michael@0: michael@0: let username = aAuthInfo.username; michael@0: let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo); michael@0: michael@0: // Suppress "the site says: $realm" when we synthesized a missing realm. michael@0: if (!aAuthInfo.realm && !isProxy) michael@0: realm = ""; michael@0: michael@0: // Trim obnoxiously long realms. michael@0: if (realm.length > 150) { michael@0: realm = realm.substring(0, 150); michael@0: // Append "..." (or localized equivalent). michael@0: realm += this.ellipsis; michael@0: } michael@0: michael@0: let text; michael@0: if (isProxy) michael@0: text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2); michael@0: else if (isPassOnly) michael@0: text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2); michael@0: else if (!realm) michael@0: text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1); michael@0: else michael@0: text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2); michael@0: michael@0: return text; michael@0: }, michael@0: michael@0: // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89 michael@0: getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) { michael@0: let uri = aChannel.URI; michael@0: let res = { host: null, port: -1 }; michael@0: if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) { michael@0: let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel); michael@0: res.host = proxy.proxyInfo.host; michael@0: res.port = proxy.proxyInfo.port; michael@0: } else { michael@0: res.host = uri.host; michael@0: res.port = uri.port; michael@0: } michael@0: return res; michael@0: }, michael@0: michael@0: getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) { michael@0: let hostname, realm; michael@0: // If our proxy is demanding authentication, don't use the michael@0: // channel's actual destination. michael@0: if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { michael@0: if (!(aChannel instanceof Ci.nsIProxiedChannel)) michael@0: throw "proxy auth needs nsIProxiedChannel"; michael@0: michael@0: let info = aChannel.proxyInfo; michael@0: if (!info) michael@0: throw "proxy auth needs nsIProxyInfo"; michael@0: michael@0: // Proxies don't have a scheme, but we'll use "moz-proxy://" michael@0: // so that it's more obvious what the login is for. michael@0: let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService); michael@0: hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port; michael@0: realm = aAuthInfo.realm; michael@0: if (!realm) michael@0: realm = hostname; michael@0: michael@0: return [hostname, realm]; michael@0: } michael@0: hostname = this.getFormattedHostname(aChannel.URI); michael@0: michael@0: // If a HTTP WWW-Authenticate header specified a realm, that value michael@0: // will be available here. If it wasn't set or wasn't HTTP, we'll use michael@0: // the formatted hostname instead. michael@0: realm = aAuthInfo.realm; michael@0: if (!realm) michael@0: realm = hostname; michael@0: michael@0: return [hostname, realm]; michael@0: }, michael@0: michael@0: getAuthInfo : function pu_getAuthInfo(aAuthInfo) { michael@0: let flags = aAuthInfo.flags; michael@0: let username = {value: ""}; michael@0: let password = {value: ""}; michael@0: michael@0: if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain) michael@0: username.value = aAuthInfo.domain + "\\" + aAuthInfo.username; michael@0: else michael@0: username.value = aAuthInfo.username; michael@0: michael@0: password.value = aAuthInfo.password michael@0: michael@0: return [username, password]; michael@0: }, michael@0: michael@0: setAuthInfo : function (aAuthInfo, username, password) { michael@0: var flags = aAuthInfo.flags; michael@0: if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { michael@0: // Domain is separated from username by a backslash michael@0: var idx = username.indexOf("\\"); michael@0: if (idx == -1) { michael@0: aAuthInfo.username = username; michael@0: } else { michael@0: aAuthInfo.domain = username.substring(0, idx); michael@0: aAuthInfo.username = username.substring(idx+1); michael@0: } michael@0: } else { michael@0: aAuthInfo.username = username; michael@0: } michael@0: aAuthInfo.password = password; michael@0: }, michael@0: michael@0: getFormattedHostname : function pu_getFormattedHostname(uri) { michael@0: let scheme = uri.scheme; michael@0: let hostname = scheme + "://" + uri.host; michael@0: michael@0: // If the URI explicitly specified a port, only include it when michael@0: // it's not the default. (We never want "http://foo.com:80") michael@0: port = uri.port; michael@0: if (port != -1) { michael@0: let handler = Services.io.getProtocolHandler(scheme); michael@0: if (port != handler.defaultPort) michael@0: hostname += ":" + port; michael@0: } michael@0: return hostname; michael@0: }, michael@0: michael@0: fireDialogEvent: function(aDomWin, aEventName) { michael@0: // accessing the document object can throw if this window no longer exists. See bug 789888. michael@0: try { michael@0: if (!aDomWin.document) michael@0: return; michael@0: let event = aDomWin.document.createEvent("Events"); michael@0: event.initEvent(aEventName, true, true); michael@0: let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.dispatchEventToChromeOnly(aDomWin, event); michael@0: } catch(ex) { michael@0: } michael@0: } michael@0: }; michael@0: michael@0: XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () { michael@0: return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"); michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () { michael@0: return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties"); michael@0: }); michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]);