diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /toolkit/components/prompts/src/nsPrompter.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/components/prompts/src/nsPrompter.js')
-rw-r--r-- | toolkit/components/prompts/src/nsPrompter.js | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/toolkit/components/prompts/src/nsPrompter.js b/toolkit/components/prompts/src/nsPrompter.js new file mode 100644 index 000000000..26efe28cc --- /dev/null +++ b/toolkit/components/prompts/src/nsPrompter.js @@ -0,0 +1,958 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/SharedPromptUtils.jsm"); + +function Prompter() { + // Note that EmbedPrompter clones this implementation. +} + +Prompter.prototype = { + classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"), + QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), + + + /* ---------- private members ---------- */ + + pickPrompter : function (domWin) { + return new ModalPrompter(domWin); + }, + + + /* ---------- nsIPromptFactory ---------- */ + + + getPrompt : function (domWin, iid) { + // This is still kind of dumb; the C++ code delegated to login manager + // here, which in turn calls back into us via nsIPromptService2. + if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) { + try { + let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"]. + getService(Ci.nsIPromptFactory); + return pwmgr.getPrompt(domWin, iid); + } catch (e) { + Cu.reportError("nsPrompter: Delegation to password manager failed: " + e); + } + } + + let p = new ModalPrompter(domWin); + p.QueryInterface(iid); + return p; + }, + + + /* ---------- nsIPromptService ---------- */ + + + alert : function (domWin, title, text) { + let p = this.pickPrompter(domWin); + p.alert(title, text); + }, + + alertCheck : function (domWin, title, text, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + p.alertCheck(title, text, checkLabel, checkValue); + }, + + confirm : function (domWin, title, text) { + let p = this.pickPrompter(domWin); + return p.confirm(title, text); + }, + + confirmCheck : function (domWin, title, text, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.confirmCheck(title, text, checkLabel, checkValue); + }, + + confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.confirmEx(title, text, flags, button0, button1, button2, checkLabel, checkValue); + }, + + prompt : function (domWin, title, text, value, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue); + }, + + promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue); + }, + + promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue); + }, + + select : function (domWin, title, text, count, list, selected) { + let p = this.pickPrompter(domWin); + return p.select(title, text, count, list, selected); + }, + + + /* ---------- nsIPromptService2 ---------- */ + + + promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.promptAuth(channel, level, authInfo, checkLabel, checkValue); + }, + + asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) { + let p = this.pickPrompter(domWin); + return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue); + }, + +}; + + +// Common utils not specific to a particular prompter style. +var PromptUtilsTemp = { + __proto__ : PromptUtils, + + getLocalizedString : function (key, formatArgs) { + if (formatArgs) + return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length); + return this.strBundle.GetStringFromName(key); + }, + + confirmExHelper : function (flags, button0, button1, button2) { + const BUTTON_DEFAULT_MASK = 0x03000000; + let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24; + let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE); + + // Flags can be used to select a specific pre-defined button label or + // a caller-supplied string (button0/button1/button2). If no flags are + // set for a button, then the button won't be shown. + let argText = [button0, button1, button2]; + let buttonLabels = [null, null, null]; + for (let i = 0; i < 3; i++) { + let buttonLabel; + switch (flags & 0xff) { + case Ci.nsIPrompt.BUTTON_TITLE_OK: + buttonLabel = PromptUtils.getLocalizedString("OK"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_CANCEL: + buttonLabel = PromptUtils.getLocalizedString("Cancel"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_YES: + buttonLabel = PromptUtils.getLocalizedString("Yes"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_NO: + buttonLabel = PromptUtils.getLocalizedString("No"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_SAVE: + buttonLabel = PromptUtils.getLocalizedString("Save"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE: + buttonLabel = PromptUtils.getLocalizedString("DontSave"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_REVERT: + buttonLabel = PromptUtils.getLocalizedString("Revert"); + break; + case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING: + buttonLabel = argText[i]; + break; + } + if (buttonLabel) + buttonLabels[i] = buttonLabel; + flags >>= 8; + } + + return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled]; + }, + + getAuthInfo : function (authInfo) { + let username, password; + + let flags = authInfo.flags; + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain) + username = authInfo.domain + "\\" + authInfo.username; + else + username = authInfo.username; + + password = authInfo.password; + + return [username, password]; + }, + + setAuthInfo : function (authInfo, username, password) { + let flags = authInfo.flags; + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { + // Domain is separated from username by a backslash + let idx = username.indexOf("\\"); + if (idx == -1) { + authInfo.username = username; + } else { + authInfo.domain = username.substring(0, idx); + authInfo.username = username.substring(idx+1); + } + } else { + authInfo.username = username; + } + authInfo.password = password; + }, + + /** + * Strip out things like userPass and path for display. + */ + getFormattedHostname : function (uri) { + return uri.scheme + "://" + uri.hostPort; + }, + + // Copied from login manager + getAuthTarget : function (aChannel, aAuthInfo) { + let hostname, realm; + + // If our proxy is demanding authentication, don't use the + // channel's actual destination. + if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { + if (!(aChannel instanceof Ci.nsIProxiedChannel)) + throw "proxy auth needs nsIProxiedChannel"; + + let info = aChannel.proxyInfo; + if (!info) + throw "proxy auth needs nsIProxyInfo"; + + // Proxies don't have a scheme, but we'll use "moz-proxy://" + // so that it's more obvious what the login is for. + let idnService = Cc["@mozilla.org/network/idn-service;1"]. + getService(Ci.nsIIDNService); + hostname = "moz-proxy://" + + idnService.convertUTF8toACE(info.host) + + ":" + info.port; + realm = aAuthInfo.realm; + if (!realm) + realm = hostname; + + return [hostname, realm]; + } + + hostname = this.getFormattedHostname(aChannel.URI); + + // If a HTTP WWW-Authenticate header specified a realm, that value + // will be available here. If it wasn't set or wasn't HTTP, we'll use + // the formatted hostname instead. + realm = aAuthInfo.realm; + if (!realm) + realm = hostname; + + return [hostname, realm]; + }, + + + makeAuthMessage : function (channel, authInfo) { + let isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); + let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); + let isCrossOrig = (authInfo.flags & + Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE); + + let username = authInfo.username; + let [displayHost, realm] = this.getAuthTarget(channel, authInfo); + + // Suppress "the site says: $realm" when we synthesized a missing realm. + if (!authInfo.realm && !isProxy) + realm = ""; + + // Trim obnoxiously long realms. + if (realm.length > 150) { + realm = realm.substring(0, 150); + // Append "..." (or localized equivalent). + realm += this.ellipsis; + } + + let text; + if (isProxy) { + text = PromptUtils.getLocalizedString("EnterLoginForProxy3", [realm, displayHost]); + } else if (isPassOnly) { + text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]); + } else if (isCrossOrig) { + text = PromptUtils.getLocalizedString("EnterUserPasswordForCrossOrigin2", [displayHost]); + } else if (!realm) { + text = PromptUtils.getLocalizedString("EnterUserPasswordFor2", [displayHost]); + } else { + text = PromptUtils.getLocalizedString("EnterLoginForRealm3", [realm, displayHost]); + } + + return text; + }, + + getTabModalPrompt : function (domWin) { + var promptBox = null; + + try { + // Get the topmost window, in case we're in a frame. + var promptWin = domWin.top; + + // Get the chrome window for the content window we're using. + // (Unwrap because we need a non-IDL property below.) + var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler.ownerDocument + .defaultView.wrappedJSObject; + + if (chromeWin.getTabModalPromptBox) + promptBox = chromeWin.getTabModalPromptBox(promptWin); + } catch (e) { + // If any errors happen, just assume no tabmodal prompter. + } + + return promptBox; + }, +}; + +PromptUtils = PromptUtilsTemp; + +XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () { + let bunService = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService); + let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties"); + if (!bundle) + throw "String bundle for Prompter not present!"; + return bundle; +}); + +XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () { + let ellipsis = "\u2026"; + try { + ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; + } catch (e) { } + return ellipsis; +}); + + + +function openModalWindow(domWin, uri, args) { + // There's an implied contract that says modal prompts should still work + // when no "parent" window is passed for the dialog (eg, the "Master + // Password" dialog does this). These prompts must be shown even if there + // are *no* visible windows at all. + // There's also a requirement for prompts to be blocked if a window is + // passed and that window is hidden (eg, auth prompts are supressed if the + // passed window is the hidden window). + // See bug 875157 comment 30 for more... + if (domWin) { + // a domWin was passed, so we can apply the check for it being hidden. + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + + if (winUtils && !winUtils.isParentWindowMainWidgetVisible) { + throw Components.Exception("Cannot call openModalWindow on a hidden window", + Cr.NS_ERROR_NOT_AVAILABLE); + } + } else { + // We try and find a window to use as the parent, but don't consider + // if that is visible before showing the prompt. + domWin = Services.ww.activeWindow; + // domWin may still be null here if there are _no_ windows open. + } + // Note that we don't need to fire DOMWillOpenModalDialog and + // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal + // will do that. Similarly for enterModalState / leaveModalState. + + Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args); +} + +function openTabPrompt(domWin, tabPrompt, args) { + let docShell = domWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell); + let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload; + let eventDetail = Cu.cloneInto({tabPrompt: true, inPermitUnload}, domWin); + PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog", null, eventDetail); + + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.enterModalState(); + + let frameMM = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIContentFrameMessageManager); + frameMM.QueryInterface(Ci.nsIDOMEventTarget); + + // We provide a callback so the prompt can close itself. We don't want to + // wait for this event loop to return... Otherwise the presence of other + // prompts on the call stack would in this dialog appearing unresponsive + // until the other prompts had been closed. + let callbackInvoked = false; + let newPrompt; + function onPromptClose(forceCleanup) { + if (!newPrompt && !forceCleanup) + return; + callbackInvoked = true; + if (newPrompt) + tabPrompt.removePrompt(newPrompt); + + frameMM.removeEventListener("pagehide", pagehide, true); + + winUtils.leaveModalState(); + + PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); + } + + frameMM.addEventListener("pagehide", pagehide, true); + function pagehide(e) { + // Check whether the event relates to our window or its ancestors + let window = domWin; + let eventWindow = e.target.defaultView; + while (window != eventWindow && window.parent != window) { + window = window.parent; + } + if (window != eventWindow) { + return; + } + frameMM.removeEventListener("pagehide", pagehide, true); + + if (newPrompt) { + newPrompt.abortPrompt(); + } + } + + try { + let topPrincipal = domWin.top.document.nodePrincipal; + let promptPrincipal = domWin.document.nodePrincipal; + args.showAlertOrigin = topPrincipal.equals(promptPrincipal); + args.promptActive = true; + + newPrompt = tabPrompt.appendPrompt(args, onPromptClose); + + // TODO since we don't actually open a window, need to check if + // there's other stuff in nsWindowWatcher::OpenWindowInternal + // that we might need to do here as well. + + let thread = Services.tm.currentThread; + while (args.promptActive) + thread.processNextEvent(true); + delete args.promptActive; + + if (args.promptAborted) + throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE); + } finally { + // If the prompt unexpectedly failed to invoke the callback, do so here. + if (!callbackInvoked) + onPromptClose(true); + } +} + +function openRemotePrompt(domWin, args, tabPrompt) { + let docShell = domWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell); + let messageManager = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsITabChild) + .messageManager; + + let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload; + let eventDetail = Cu.cloneInto({tabPrompt, inPermitUnload}, domWin); + PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog", null, eventDetail); + + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.enterModalState(); + let closed = false; + + let frameMM = docShell.getInterface(Ci.nsIContentFrameMessageManager); + frameMM.QueryInterface(Ci.nsIDOMEventTarget); + + // It should be hard or impossible to cause a window to create multiple + // prompts, but just in case, give our prompt an ID. + let id = "id" + Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); + + messageManager.addMessageListener("Prompt:Close", function listener(message) { + if (message.data._remoteId !== id) { + return; + } + + messageManager.removeMessageListener("Prompt:Close", listener); + frameMM.removeEventListener("pagehide", pagehide, true); + + winUtils.leaveModalState(); + PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); + + // Copy the response from the closed prompt into our args, it will be + // read by our caller. + if (message.data) { + for (let key in message.data) { + args[key] = message.data[key]; + } + } + + // Exit our nested event loop when we unwind. + closed = true; + }); + + frameMM.addEventListener("pagehide", pagehide, true); + function pagehide(e) { + // Check whether the event relates to our window or its ancestors + let window = domWin; + let eventWindow = e.target.defaultView; + while (window != eventWindow && window.parent != window) { + window = window.parent; + } + if (window != eventWindow) { + return; + } + frameMM.removeEventListener("pagehide", pagehide, true); + messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id }); + } + + let topPrincipal = domWin.top.document.nodePrincipal; + let promptPrincipal = domWin.document.nodePrincipal; + args.promptPrincipal = promptPrincipal; + args.showAlertOrigin = topPrincipal.equals(promptPrincipal); + args.inPermitUnload = inPermitUnload; + + args._remoteId = id; + + messageManager.sendAsyncMessage("Prompt:Open", args, {}); + + let thread = Services.tm.currentThread; + while (!closed) { + thread.processNextEvent(true); + } +} + +function ModalPrompter(domWin) { + this.domWin = domWin; +} +ModalPrompter.prototype = { + domWin : null, + /* + * Default to not using a tab-modal prompt, unless the caller opts in by + * QIing to nsIWritablePropertyBag and setting the value of this property + * to true. + */ + allowTabModal : false, + + QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, + Ci.nsIAuthPrompt2, + Ci.nsIWritablePropertyBag2]), + + + /* ---------- internal methods ---------- */ + + + openPrompt : function (args) { + // Check pref, if false/missing do not ever allow tab-modal prompts. + const prefName = "prompts.tab_modal.enabled"; + let prefValue = false; + if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL) + prefValue = Services.prefs.getBoolPref(prefName); + + let allowTabModal = this.allowTabModal && prefValue; + + if (allowTabModal && this.domWin) { + if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + openRemotePrompt(this.domWin, args, true); + return; + } + + let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin); + if (tabPrompt) { + openTabPrompt(this.domWin, tabPrompt, args); + return; + } + } + + // If we can't do a tab modal prompt, fallback to using a window-modal dialog. + const COMMON_DIALOG = "chrome://global/content/commonDialog.xul"; + const SELECT_DIALOG = "chrome://global/content/selectDialog.xul"; + + let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG; + + if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { + args.uri = uri; + openRemotePrompt(this.domWin, args); + return; + } + + let propBag = PromptUtils.objectToPropBag(args); + openModalWindow(this.domWin, uri, propBag); + PromptUtils.propBagToObject(propBag, args); + }, + + + + /* + * ---------- interface disambiguation ---------- + * + * nsIPrompt and nsIAuthPrompt share 3 method names with slightly + * different arguments. All but prompt() have the same number of + * arguments, so look at the arg types to figure out how we're being + * called. :-( + */ + prompt : function() { + // also, the nsIPrompt flavor has 5 args instead of 6. + if (typeof arguments[2] == "object") + return this.nsIPrompt_prompt.apply(this, arguments); + return this.nsIAuthPrompt_prompt.apply(this, arguments); + }, + + promptUsernameAndPassword : function() { + // Both have 6 args, so use types. + if (typeof arguments[2] == "object") + return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); + return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); + }, + + promptPassword : function() { + // Both have 5 args, so use types. + if (typeof arguments[2] == "object") + return this.nsIPrompt_promptPassword.apply(this, arguments); + return this.nsIAuthPrompt_promptPassword.apply(this, arguments); + }, + + + /* ---------- nsIPrompt ---------- */ + + + alert : function (title, text) { + if (!title) + title = PromptUtils.getLocalizedString("Alert"); + + let args = { + promptType: "alert", + title: title, + text: text, + }; + + this.openPrompt(args); + }, + + alertCheck : function (title, text, checkLabel, checkValue) { + if (!title) + title = PromptUtils.getLocalizedString("Alert"); + + let args = { + promptType: "alertCheck", + title: title, + text: text, + checkLabel: checkLabel, + checked: checkValue.value, + }; + + this.openPrompt(args); + + // Checkbox state always returned, even if cancel clicked. + checkValue.value = args.checked; + }, + + confirm : function (title, text) { + if (!title) + title = PromptUtils.getLocalizedString("Confirm"); + + let args = { + promptType: "confirm", + title: title, + text: text, + ok: false, + }; + + this.openPrompt(args); + + // Did user click Ok or Cancel? + return args.ok; + }, + + confirmCheck : function (title, text, checkLabel, checkValue) { + if (!title) + title = PromptUtils.getLocalizedString("ConfirmCheck"); + + let args = { + promptType: "confirmCheck", + title: title, + text: text, + checkLabel: checkLabel, + checked: checkValue.value, + ok: false, + }; + + this.openPrompt(args); + + // Checkbox state always returned, even if cancel clicked. + checkValue.value = args.checked; + + // Did user click Ok or Cancel? + return args.ok; + }, + + confirmEx : function (title, text, flags, button0, button1, button2, + checkLabel, checkValue) { + + if (!title) + title = PromptUtils.getLocalizedString("Confirm"); + + let args = { + promptType: "confirmEx", + title: title, + text: text, + checkLabel: checkLabel, + checked: checkValue.value, + ok: false, + buttonNumClicked: 1, + }; + + let [label0, label1, label2, defaultButtonNum, isDelayEnabled] = + PromptUtils.confirmExHelper(flags, button0, button1, button2); + + args.defaultButtonNum = defaultButtonNum; + args.enableDelay = isDelayEnabled; + + if (label0) { + args.button0Label = label0; + if (label1) { + args.button1Label = label1; + if (label2) { + args.button2Label = label2; + } + } + } + + this.openPrompt(args); + + // Checkbox state always returned, even if cancel clicked. + checkValue.value = args.checked; + + // Get the number of the button the user clicked. + return args.buttonNumClicked; + }, + + nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) { + if (!title) + title = PromptUtils.getLocalizedString("Prompt"); + + let args = { + promptType: "prompt", + title: title, + text: text, + value: value.value, + checkLabel: checkLabel, + checked: checkValue.value, + ok: false, + }; + + this.openPrompt(args); + + // Did user click Ok or Cancel? + let ok = args.ok; + if (ok) { + checkValue.value = args.checked; + value.value = args.value; + } + + return ok; + }, + + nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) { + if (!title) + title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2"); + + let args = { + promptType: "promptUserAndPass", + title: title, + text: text, + user: user.value, + pass: pass.value, + checkLabel: checkLabel, + checked: checkValue.value, + ok: false, + }; + + this.openPrompt(args); + + // Did user click Ok or Cancel? + let ok = args.ok; + if (ok) { + checkValue.value = args.checked; + user.value = args.user; + pass.value = args.pass; + } + + return ok; + }, + + nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) { + if (!title) + title = PromptUtils.getLocalizedString("PromptPassword2"); + + let args = { + promptType: "promptPassword", + title: title, + text: text, + pass: pass.value, + checkLabel: checkLabel, + checked: checkValue.value, + ok: false, + } + + this.openPrompt(args); + + // Did user click Ok or Cancel? + let ok = args.ok; + if (ok) { + checkValue.value = args.checked; + pass.value = args.pass; + } + + return ok; + }, + + select : function (title, text, count, list, selected) { + if (!title) + title = PromptUtils.getLocalizedString("Select"); + + let args = { + promptType: "select", + title: title, + text: text, + list: list, + selected: -1, + ok: false, + }; + + this.openPrompt(args); + + // Did user click Ok or Cancel? + let ok = args.ok; + if (ok) + selected.value = args.selected; + + return ok; + }, + + + /* ---------- nsIAuthPrompt ---------- */ + + + nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp + if (defaultText) + result.value = defaultText; + return this.nsIPrompt_prompt(title, text, result, null, {}); + }, + + nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) { + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp + return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {}); + }, + + nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) { + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp + return this.nsIPrompt_promptPassword(title, text, pass, null, {}); + }, + + + /* ---------- nsIAuthPrompt2 ---------- */ + + + promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { + let message = PromptUtils.makeAuthMessage(channel, authInfo); + + let [username, password] = PromptUtils.getAuthInfo(authInfo); + + let userParam = { value: username }; + let passParam = { value: password }; + + let ok; + if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) + ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue); + else + ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue); + + if (ok) + PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); + return ok; + }, + + asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { + // Nothing calls this directly; netwerk ends up going through + // nsIPromptService::GetPrompt, which delegates to login manager. + // Login manger handles the async bits itself, and only calls out + // promptAuth, never asyncPromptAuth. + // + // Bug 565582 will change this. + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + + /* ---------- nsIWritablePropertyBag2 ---------- */ + + // Only a partial implementation, for one specific use case... + + setPropertyAsBool : function(name, value) { + if (name == "allowTabModal") + this.allowTabModal = value; + else + throw Cr.NS_ERROR_ILLEGAL_VALUE; + }, +}; + + +function AuthPromptAdapterFactory() { +} +AuthPromptAdapterFactory.prototype = { + classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"), + QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]), + + /* ---------- nsIAuthPromptAdapterFactory ---------- */ + + createAdapter : function (oldPrompter) { + return new AuthPromptAdapter(oldPrompter); + } +}; + + +// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell. +function AuthPromptAdapter(oldPrompter) { + this.oldPrompter = oldPrompter; +} +AuthPromptAdapter.prototype = { + QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]), + oldPrompter : null, + + /* ---------- nsIAuthPrompt2 ---------- */ + + promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { + let message = PromptUtils.makeAuthMessage(channel, authInfo); + + let [username, password] = PromptUtils.getAuthInfo(authInfo); + let userParam = { value: username }; + let passParam = { value: password }; + + let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo); + let authTarget = host + " (" + realm + ")"; + + let ok; + if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) + ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam); + else + ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam); + + if (ok) + PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); + return ok; + }, + + asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + } +}; + + +// Wrapper using the old embedding contractID, since it's already common in +// the addon ecosystem. +function EmbedPrompter() { +} +EmbedPrompter.prototype = new Prompter(); +EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"); + +var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |