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: michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; 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/SharedPromptUtils.jsm"); michael@0: michael@0: function Prompter() { michael@0: // Note that EmbedPrompter clones this implementation. michael@0: } michael@0: michael@0: Prompter.prototype = { michael@0: classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"), michael@0: QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), michael@0: michael@0: michael@0: /* ---------- private memebers ---------- */ michael@0: michael@0: pickPrompter : function (domWin) { michael@0: return new ModalPrompter(domWin); michael@0: }, michael@0: michael@0: michael@0: /* ---------- nsIPromptFactory ---------- */ michael@0: michael@0: michael@0: getPrompt : function (domWin, iid) { michael@0: // This is still kind of dumb; the C++ code delegated to login manager michael@0: // here, which in turn calls back into us via nsIPromptService2. michael@0: if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) { michael@0: try { michael@0: let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"]. michael@0: getService(Ci.nsIPromptFactory); michael@0: return pwmgr.getPrompt(domWin, iid); michael@0: } catch (e) { michael@0: Cu.reportError("nsPrompter: Delegation to password manager failed: " + e); michael@0: } michael@0: } michael@0: michael@0: let p = new ModalPrompter(domWin); michael@0: p.QueryInterface(iid); michael@0: return p; michael@0: }, michael@0: michael@0: michael@0: /* ---------- nsIPromptService ---------- */ michael@0: michael@0: michael@0: alert : function (domWin, title, text) { michael@0: let p = this.pickPrompter(domWin); michael@0: p.alert(title, text); michael@0: }, michael@0: michael@0: alertCheck : function (domWin, title, text, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: p.alertCheck(title, text, checkLabel, checkValue); michael@0: }, michael@0: michael@0: confirm : function (domWin, title, text) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.confirm(title, text); michael@0: }, michael@0: michael@0: confirmCheck : function (domWin, title, text, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.confirmCheck(title, text, checkLabel, checkValue); michael@0: }, michael@0: michael@0: confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.confirmEx(title, text, flags, button0, button1, button2, checkLabel, checkValue); michael@0: }, michael@0: michael@0: prompt : function (domWin, title, text, value, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue); michael@0: }, michael@0: michael@0: promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue); michael@0: }, michael@0: michael@0: promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue); michael@0: }, michael@0: michael@0: select : function (domWin, title, text, count, list, selected) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.select(title, text, count, list, selected); michael@0: }, michael@0: michael@0: michael@0: /* ---------- nsIPromptService2 ---------- */ michael@0: michael@0: michael@0: promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.promptAuth(channel, level, authInfo, checkLabel, checkValue); michael@0: }, michael@0: michael@0: asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) { michael@0: let p = this.pickPrompter(domWin); michael@0: return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue); michael@0: }, michael@0: michael@0: }; michael@0: michael@0: michael@0: // Common utils not specific to a particular prompter style. michael@0: let PromptUtilsTemp = { michael@0: __proto__ : PromptUtils, michael@0: michael@0: getLocalizedString : function (key, formatArgs) { michael@0: if (formatArgs) michael@0: return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length); michael@0: else michael@0: return this.strBundle.GetStringFromName(key); michael@0: }, michael@0: michael@0: confirmExHelper : function (flags, button0, button1, button2) { michael@0: const BUTTON_DEFAULT_MASK = 0x03000000; michael@0: let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24; michael@0: let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE); michael@0: michael@0: // Flags can be used to select a specific pre-defined button label or michael@0: // a caller-supplied string (button0/button1/button2). If no flags are michael@0: // set for a button, then the button won't be shown. michael@0: let argText = [button0, button1, button2]; michael@0: let buttonLabels = [null, null, null]; michael@0: for (let i = 0; i < 3; i++) { michael@0: let buttonLabel; michael@0: switch (flags & 0xff) { michael@0: case Ci.nsIPrompt.BUTTON_TITLE_OK: michael@0: buttonLabel = PromptUtils.getLocalizedString("OK"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_CANCEL: michael@0: buttonLabel = PromptUtils.getLocalizedString("Cancel"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_YES: michael@0: buttonLabel = PromptUtils.getLocalizedString("Yes"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_NO: michael@0: buttonLabel = PromptUtils.getLocalizedString("No"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_SAVE: michael@0: buttonLabel = PromptUtils.getLocalizedString("Save"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE: michael@0: buttonLabel = PromptUtils.getLocalizedString("DontSave"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_REVERT: michael@0: buttonLabel = PromptUtils.getLocalizedString("Revert"); michael@0: break; michael@0: case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING: michael@0: buttonLabel = argText[i]; michael@0: break; michael@0: } michael@0: if (buttonLabel) michael@0: buttonLabels[i] = buttonLabel; michael@0: flags >>= 8; michael@0: } michael@0: michael@0: return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled]; michael@0: }, michael@0: michael@0: getAuthInfo : function (authInfo) { michael@0: let username, password; michael@0: michael@0: let flags = authInfo.flags; michael@0: if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain) michael@0: username = authInfo.domain + "\\" + authInfo.username; michael@0: else michael@0: username = authInfo.username; michael@0: michael@0: password = authInfo.password; michael@0: michael@0: return [username, password]; michael@0: }, michael@0: michael@0: setAuthInfo : function (authInfo, username, password) { michael@0: let flags = authInfo.flags; michael@0: if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { michael@0: // Domain is separated from username by a backslash michael@0: let idx = username.indexOf("\\"); michael@0: if (idx == -1) { michael@0: authInfo.username = username; michael@0: } else { michael@0: authInfo.domain = username.substring(0, idx); michael@0: authInfo.username = username.substring(idx+1); michael@0: } michael@0: } else { michael@0: authInfo.username = username; michael@0: } michael@0: authInfo.password = password; michael@0: }, michael@0: michael@0: // Copied from login manager michael@0: getFormattedHostname : function (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: michael@0: return hostname; michael@0: }, michael@0: michael@0: // Copied from login manager michael@0: getAuthTarget : function (aChannel, aAuthInfo) { michael@0: let hostname, realm; michael@0: 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"]. michael@0: getService(Ci.nsIIDNService); michael@0: hostname = "moz-proxy://" + michael@0: idnService.convertUTF8toACE(info.host) + michael@0: ":" + 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: 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: michael@0: makeAuthMessage : function (channel, authInfo) { michael@0: let isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); michael@0: let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); michael@0: michael@0: let username = authInfo.username; michael@0: let [displayHost, realm] = this.getAuthTarget(channel, authInfo); michael@0: michael@0: // Suppress "the site says: $realm" when we synthesized a missing realm. michael@0: if (!authInfo.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 = PromptUtils.getLocalizedString("EnterLoginForProxy", [realm, displayHost]); michael@0: else if (isPassOnly) michael@0: text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]); michael@0: else if (!realm) michael@0: text = PromptUtils.getLocalizedString("EnterUserPasswordFor", [displayHost]); michael@0: else michael@0: text = PromptUtils.getLocalizedString("EnterLoginForRealm", [realm, displayHost]); michael@0: michael@0: return text; michael@0: }, michael@0: michael@0: getTabModalPrompt : function (domWin) { michael@0: var promptBox = null; michael@0: michael@0: try { michael@0: // Get the topmost window, in case we're in a frame. michael@0: var promptWin = domWin.top; michael@0: michael@0: // Get the chrome window for the content window we're using. michael@0: // (Unwrap because we need a non-IDL property below.) michael@0: var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIDocShell) michael@0: .chromeEventHandler.ownerDocument michael@0: .defaultView.wrappedJSObject; michael@0: michael@0: if (chromeWin.getTabModalPromptBox) michael@0: promptBox = chromeWin.getTabModalPromptBox(promptWin); michael@0: } catch (e) { michael@0: // If any errors happen, just assume no tabmodal prompter. michael@0: } michael@0: michael@0: return promptBox; michael@0: }, michael@0: }; michael@0: michael@0: PromptUtils = PromptUtilsTemp; michael@0: michael@0: XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () { michael@0: let bunService = Cc["@mozilla.org/intl/stringbundle;1"]. michael@0: getService(Ci.nsIStringBundleService); michael@0: let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties"); michael@0: if (!bundle) michael@0: throw "String bundle for Prompter not present!"; michael@0: return bundle; michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () { michael@0: let ellipsis = "\u2026"; michael@0: try { michael@0: ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; michael@0: } catch (e) { } michael@0: return ellipsis; michael@0: }); michael@0: michael@0: michael@0: michael@0: function openModalWindow(domWin, uri, args) { michael@0: // There's an implied contract that says modal prompts should still work michael@0: // when no "parent" window is passed for the dialog (eg, the "Master michael@0: // Password" dialog does this). These prompts must be shown even if there michael@0: // are *no* visible windows at all. michael@0: // There's also a requirement for prompts to be blocked if a window is michael@0: // passed and that window is hidden (eg, auth prompts are supressed if the michael@0: // passed window is the hidden window). michael@0: // See bug 875157 comment 30 for more... michael@0: if (domWin) { michael@0: // a domWin was passed, so we can apply the check for it being hidden. michael@0: let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: michael@0: if (winUtils && !winUtils.isParentWindowMainWidgetVisible) { michael@0: throw Components.Exception("Cannot call openModalWindow on a hidden window", michael@0: Cr.NS_ERROR_NOT_AVAILABLE); michael@0: } michael@0: } else { michael@0: // We try and find a window to use as the parent, but don't consider michael@0: // if that is visible before showing the prompt. michael@0: domWin = Services.ww.activeWindow; michael@0: // domWin may still be null here if there are _no_ windows open. michael@0: } michael@0: // Note that we don't need to fire DOMWillOpenModalDialog and michael@0: // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal michael@0: // will do that. Similarly for enterModalState / leaveModalState. michael@0: michael@0: Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args); michael@0: } michael@0: michael@0: function openTabPrompt(domWin, tabPrompt, args) { michael@0: PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); michael@0: michael@0: let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.enterModalState(); michael@0: michael@0: // We provide a callback so the prompt can close itself. We don't want to michael@0: // wait for this event loop to return... Otherwise the presence of other michael@0: // prompts on the call stack would in this dialog appearing unresponsive michael@0: // until the other prompts had been closed. michael@0: let callbackInvoked = false; michael@0: let newPrompt; michael@0: function onPromptClose(forceCleanup) { michael@0: if (!newPrompt && !forceCleanup) michael@0: return; michael@0: callbackInvoked = true; michael@0: if (newPrompt) michael@0: tabPrompt.removePrompt(newPrompt); michael@0: michael@0: domWin.removeEventListener("pagehide", pagehide); michael@0: michael@0: winUtils.leaveModalState(); michael@0: michael@0: PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); michael@0: } michael@0: michael@0: domWin.addEventListener("pagehide", pagehide); michael@0: function pagehide() { michael@0: domWin.removeEventListener("pagehide", pagehide); michael@0: michael@0: if (newPrompt) { michael@0: newPrompt.abortPrompt(); michael@0: } michael@0: } michael@0: michael@0: try { michael@0: let topPrincipal = domWin.top.document.nodePrincipal; michael@0: let promptPrincipal = domWin.document.nodePrincipal; michael@0: args.showAlertOrigin = topPrincipal.equals(promptPrincipal); michael@0: args.promptActive = true; michael@0: michael@0: newPrompt = tabPrompt.appendPrompt(args, onPromptClose); michael@0: michael@0: // TODO since we don't actually open a window, need to check if michael@0: // there's other stuff in nsWindowWatcher::OpenWindowInternal michael@0: // that we might need to do here as well. michael@0: michael@0: let thread = Services.tm.currentThread; michael@0: while (args.promptActive) michael@0: thread.processNextEvent(true); michael@0: delete args.promptActive; michael@0: michael@0: if (args.promptAborted) michael@0: throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE); michael@0: } finally { michael@0: // If the prompt unexpectedly failed to invoke the callback, do so here. michael@0: if (!callbackInvoked) michael@0: onPromptClose(true); michael@0: } michael@0: } michael@0: michael@0: function openRemotePrompt(domWin, args, tabPrompt) { michael@0: let messageManager = domWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIDocShell) michael@0: .QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsITabChild) michael@0: .messageManager; michael@0: michael@0: PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); michael@0: michael@0: let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.enterModalState(); michael@0: let closed = false; michael@0: michael@0: // It should be hard or impossible to cause a window to create multiple michael@0: // prompts, but just in case, give our prompt an ID. michael@0: let id = "id" + Cc["@mozilla.org/uuid-generator;1"] michael@0: .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); michael@0: michael@0: messageManager.addMessageListener("Prompt:Close", function listener(message) { michael@0: if (message.data._remoteId !== id) { michael@0: return; michael@0: } michael@0: michael@0: messageManager.removeMessageListener("Prompt:Close", listener); michael@0: domWin.removeEventListener("pagehide", pagehide); michael@0: michael@0: winUtils.leaveModalState(); michael@0: PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); michael@0: michael@0: // Copy the response from the closed prompt into our args, it will be michael@0: // read by our caller. michael@0: if (message.data) { michael@0: for (let key in message.data) { michael@0: args[key] = message.data[key]; michael@0: } michael@0: } michael@0: michael@0: // Exit our nested event loop when we unwind. michael@0: closed = true; michael@0: }); michael@0: michael@0: domWin.addEventListener("pagehide", pagehide); michael@0: function pagehide() { michael@0: domWin.removeEventListener("pagehide", pagehide); michael@0: messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id }); michael@0: } michael@0: michael@0: let topPrincipal = domWin.top.document.nodePrincipal; michael@0: let promptPrincipal = domWin.document.nodePrincipal; michael@0: args.showAlertOrigin = topPrincipal.equals(promptPrincipal); michael@0: michael@0: args._remoteId = id; michael@0: michael@0: messageManager.sendAsyncMessage("Prompt:Open", args, {}); michael@0: michael@0: let thread = Services.tm.currentThread; michael@0: while (!closed) { michael@0: thread.processNextEvent(true); michael@0: } michael@0: } michael@0: michael@0: function ModalPrompter(domWin) { michael@0: this.domWin = domWin; michael@0: } michael@0: ModalPrompter.prototype = { michael@0: domWin : null, michael@0: /* michael@0: * Default to not using a tab-modal prompt, unless the caller opts in by michael@0: * QIing to nsIWritablePropertyBag and setting the value of this property michael@0: * to true. michael@0: */ michael@0: allowTabModal : false, michael@0: michael@0: QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, michael@0: Ci.nsIAuthPrompt2, michael@0: Ci.nsIWritablePropertyBag2]), michael@0: michael@0: michael@0: /* ---------- internal methods ---------- */ michael@0: michael@0: michael@0: openPrompt : function (args) { michael@0: // Check pref, if false/missing do not ever allow tab-modal prompts. michael@0: const prefName = "prompts.tab_modal.enabled"; michael@0: let prefValue = false; michael@0: if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL) michael@0: prefValue = Services.prefs.getBoolPref(prefName); michael@0: michael@0: let allowTabModal = this.allowTabModal && prefValue; michael@0: michael@0: if (allowTabModal && this.domWin) { michael@0: if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { michael@0: openRemotePrompt(this.domWin, args, true); michael@0: return; michael@0: } michael@0: michael@0: let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin); michael@0: if (tabPrompt) { michael@0: openTabPrompt(this.domWin, tabPrompt, args); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // If we can't do a tab modal prompt, fallback to using a window-modal dialog. michael@0: const COMMON_DIALOG = "chrome://global/content/commonDialog.xul"; michael@0: const SELECT_DIALOG = "chrome://global/content/selectDialog.xul"; michael@0: michael@0: let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG; michael@0: michael@0: if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { michael@0: args.uri = uri; michael@0: openRemotePrompt(this.domWin, args); michael@0: return; michael@0: } michael@0: michael@0: let propBag = PromptUtils.objectToPropBag(args); michael@0: openModalWindow(this.domWin, uri, propBag); michael@0: PromptUtils.propBagToObject(propBag, args); michael@0: }, michael@0: michael@0: michael@0: michael@0: /* michael@0: * ---------- interface disambiguation ---------- 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() { 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() { 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() { 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: michael@0: /* ---------- nsIPrompt ---------- */ michael@0: michael@0: michael@0: alert : function (title, text) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Alert"); michael@0: michael@0: let args = { michael@0: promptType: "alert", michael@0: title: title, michael@0: text: text, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: }, michael@0: michael@0: alertCheck : function (title, text, checkLabel, checkValue) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Alert"); michael@0: michael@0: let args = { michael@0: promptType: "alertCheck", michael@0: title: title, michael@0: text: text, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Checkbox state always returned, even if cancel clicked. michael@0: checkValue.value = args.checked; michael@0: }, michael@0: michael@0: confirm : function (title, text) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Confirm"); michael@0: michael@0: let args = { michael@0: promptType: "confirm", michael@0: title: title, michael@0: text: text, michael@0: ok: false, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Did user click Ok or Cancel? michael@0: return args.ok; michael@0: }, michael@0: michael@0: confirmCheck : function (title, text, checkLabel, checkValue) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("ConfirmCheck"); michael@0: michael@0: let args = { michael@0: promptType: "confirmCheck", michael@0: title: title, michael@0: text: text, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: ok: false, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Checkbox state always returned, even if cancel clicked. michael@0: checkValue.value = args.checked; michael@0: michael@0: // Did user click Ok or Cancel? michael@0: return args.ok; michael@0: }, michael@0: michael@0: confirmEx : function (title, text, flags, button0, button1, button2, michael@0: checkLabel, checkValue) { michael@0: michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Confirm"); michael@0: michael@0: let args = { michael@0: promptType: "confirmEx", michael@0: title: title, michael@0: text: text, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: ok: false, michael@0: buttonNumClicked: 1, michael@0: }; michael@0: michael@0: let [label0, label1, label2, defaultButtonNum, isDelayEnabled] = michael@0: PromptUtils.confirmExHelper(flags, button0, button1, button2); michael@0: michael@0: args.defaultButtonNum = defaultButtonNum; michael@0: args.enableDelay = isDelayEnabled; michael@0: michael@0: if (label0) { michael@0: args.button0Label = label0; michael@0: if (label1) { michael@0: args.button1Label = label1; michael@0: if (label2) { michael@0: args.button2Label = label2; michael@0: } michael@0: } michael@0: } michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Checkbox state always returned, even if cancel clicked. michael@0: checkValue.value = args.checked; michael@0: michael@0: // Get the number of the button the user clicked. michael@0: return args.buttonNumClicked; michael@0: }, michael@0: michael@0: nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Prompt"); michael@0: michael@0: let args = { michael@0: promptType: "prompt", michael@0: title: title, michael@0: text: text, michael@0: value: value.value, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: ok: false, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Did user click Ok or Cancel? michael@0: let ok = args.ok; michael@0: if (ok) { michael@0: checkValue.value = args.checked; michael@0: value.value = args.value; michael@0: } michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2"); michael@0: michael@0: let args = { michael@0: promptType: "promptUserAndPass", michael@0: title: title, michael@0: text: text, michael@0: user: user.value, michael@0: pass: pass.value, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: ok: false, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Did user click Ok or Cancel? michael@0: let ok = args.ok; michael@0: if (ok) { michael@0: checkValue.value = args.checked; michael@0: user.value = args.user; michael@0: pass.value = args.pass; michael@0: } michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("PromptPassword2"); michael@0: michael@0: let args = { michael@0: promptType: "promptPassword", michael@0: title: title, michael@0: text: text, michael@0: pass: pass.value, michael@0: checkLabel: checkLabel, michael@0: checked: checkValue.value, michael@0: ok: false, michael@0: } michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Did user click Ok or Cancel? michael@0: let ok = args.ok; michael@0: if (ok) { michael@0: checkValue.value = args.checked; michael@0: pass.value = args.pass; michael@0: } michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: select : function (title, text, count, list, selected) { michael@0: if (!title) michael@0: title = PromptUtils.getLocalizedString("Select"); michael@0: michael@0: let args = { michael@0: promptType: "select", michael@0: title: title, michael@0: text: text, michael@0: list: list, michael@0: selected: -1, michael@0: ok: false, michael@0: }; michael@0: michael@0: this.openPrompt(args); michael@0: michael@0: // Did user click Ok or Cancel? michael@0: let ok = args.ok; michael@0: if (ok) michael@0: selected.value = args.selected; michael@0: michael@0: return ok; michael@0: }, michael@0: michael@0: michael@0: /* ---------- nsIAuthPrompt ---------- */ michael@0: michael@0: michael@0: nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { michael@0: // The passwordRealm and savePassword args were ignored by nsPrompt.cpp 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 (title, text, passwordRealm, savePassword, user, pass) { michael@0: // The passwordRealm and savePassword args were ignored by nsPrompt.cpp michael@0: return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {}); michael@0: }, michael@0: michael@0: nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) { michael@0: // The passwordRealm and savePassword args were ignored by nsPrompt.cpp michael@0: return this.nsIPrompt_promptPassword(title, text, pass, null, {}); michael@0: }, michael@0: michael@0: michael@0: /* ---------- nsIAuthPrompt2 ---------- */ michael@0: michael@0: michael@0: promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { michael@0: let message = PromptUtils.makeAuthMessage(channel, authInfo); michael@0: michael@0: let [username, password] = PromptUtils.getAuthInfo(authInfo); michael@0: michael@0: let userParam = { value: username }; michael@0: let passParam = { value: password }; michael@0: michael@0: let ok; michael@0: if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) michael@0: ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue); michael@0: else michael@0: ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue); michael@0: michael@0: if (ok) michael@0: PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); michael@0: return ok; michael@0: }, michael@0: michael@0: asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { michael@0: // Nothing calls this directly; netwerk ends up going through michael@0: // nsIPromptService::GetPrompt, which delegates to login manager. michael@0: // Login manger handles the async bits itself, and only calls out michael@0: // promptAuth, never asyncPromptAuth. michael@0: // michael@0: // Bug 565582 will change this. michael@0: throw Cr.NS_ERROR_NOT_IMPLEMENTED; michael@0: }, michael@0: michael@0: /* ---------- nsIWritablePropertyBag2 ---------- */ michael@0: michael@0: // Only a partial implementation, for one specific use case... michael@0: michael@0: setPropertyAsBool : function(name, value) { michael@0: if (name == "allowTabModal") michael@0: this.allowTabModal = value; michael@0: else michael@0: throw Cr.NS_ERROR_ILLEGAL_VALUE; michael@0: }, michael@0: }; michael@0: michael@0: michael@0: function AuthPromptAdapterFactory() { michael@0: } michael@0: AuthPromptAdapterFactory.prototype = { michael@0: classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"), michael@0: QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]), michael@0: michael@0: /* ---------- nsIAuthPromptAdapterFactory ---------- */ michael@0: michael@0: createAdapter : function (oldPrompter) { michael@0: return new AuthPromptAdapter(oldPrompter); michael@0: } michael@0: }; michael@0: michael@0: michael@0: // Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell. michael@0: function AuthPromptAdapter(oldPrompter) { michael@0: this.oldPrompter = oldPrompter; michael@0: } michael@0: AuthPromptAdapter.prototype = { michael@0: QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]), michael@0: oldPrompter : null, michael@0: michael@0: /* ---------- nsIAuthPrompt2 ---------- */ michael@0: michael@0: promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { michael@0: let message = PromptUtils.makeAuthMessage(channel, authInfo); michael@0: michael@0: let [username, password] = PromptUtils.getAuthInfo(authInfo); michael@0: let userParam = { value: username }; michael@0: let passParam = { value: password }; michael@0: michael@0: let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo); michael@0: let authTarget = host + " (" + realm + ")"; michael@0: michael@0: let ok; michael@0: if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) michael@0: ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam); michael@0: else michael@0: ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam); michael@0: michael@0: if (ok) michael@0: PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); michael@0: return ok; michael@0: }, michael@0: michael@0: asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { michael@0: throw Cr.NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: }; michael@0: michael@0: michael@0: // Wrapper using the old embedding contractID, since it's already common in michael@0: // the addon ecosystem. michael@0: function EmbedPrompter() { michael@0: } michael@0: EmbedPrompter.prototype = new Prompter(); michael@0: EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"); michael@0: michael@0: var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory]; michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);