1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/prompts/src/nsPrompter.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,934 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 + 1.9 +const Cc = Components.classes; 1.10 +const Ci = Components.interfaces; 1.11 +const Cr = Components.results; 1.12 +const Cu = Components.utils; 1.13 + 1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.15 +Cu.import("resource://gre/modules/Services.jsm"); 1.16 +Cu.import("resource://gre/modules/SharedPromptUtils.jsm"); 1.17 + 1.18 +function Prompter() { 1.19 + // Note that EmbedPrompter clones this implementation. 1.20 +} 1.21 + 1.22 +Prompter.prototype = { 1.23 + classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"), 1.24 + QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), 1.25 + 1.26 + 1.27 + /* ---------- private memebers ---------- */ 1.28 + 1.29 + pickPrompter : function (domWin) { 1.30 + return new ModalPrompter(domWin); 1.31 + }, 1.32 + 1.33 + 1.34 + /* ---------- nsIPromptFactory ---------- */ 1.35 + 1.36 + 1.37 + getPrompt : function (domWin, iid) { 1.38 + // This is still kind of dumb; the C++ code delegated to login manager 1.39 + // here, which in turn calls back into us via nsIPromptService2. 1.40 + if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) { 1.41 + try { 1.42 + let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"]. 1.43 + getService(Ci.nsIPromptFactory); 1.44 + return pwmgr.getPrompt(domWin, iid); 1.45 + } catch (e) { 1.46 + Cu.reportError("nsPrompter: Delegation to password manager failed: " + e); 1.47 + } 1.48 + } 1.49 + 1.50 + let p = new ModalPrompter(domWin); 1.51 + p.QueryInterface(iid); 1.52 + return p; 1.53 + }, 1.54 + 1.55 + 1.56 + /* ---------- nsIPromptService ---------- */ 1.57 + 1.58 + 1.59 + alert : function (domWin, title, text) { 1.60 + let p = this.pickPrompter(domWin); 1.61 + p.alert(title, text); 1.62 + }, 1.63 + 1.64 + alertCheck : function (domWin, title, text, checkLabel, checkValue) { 1.65 + let p = this.pickPrompter(domWin); 1.66 + p.alertCheck(title, text, checkLabel, checkValue); 1.67 + }, 1.68 + 1.69 + confirm : function (domWin, title, text) { 1.70 + let p = this.pickPrompter(domWin); 1.71 + return p.confirm(title, text); 1.72 + }, 1.73 + 1.74 + confirmCheck : function (domWin, title, text, checkLabel, checkValue) { 1.75 + let p = this.pickPrompter(domWin); 1.76 + return p.confirmCheck(title, text, checkLabel, checkValue); 1.77 + }, 1.78 + 1.79 + confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) { 1.80 + let p = this.pickPrompter(domWin); 1.81 + return p.confirmEx(title, text, flags, button0, button1, button2, checkLabel, checkValue); 1.82 + }, 1.83 + 1.84 + prompt : function (domWin, title, text, value, checkLabel, checkValue) { 1.85 + let p = this.pickPrompter(domWin); 1.86 + return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue); 1.87 + }, 1.88 + 1.89 + promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) { 1.90 + let p = this.pickPrompter(domWin); 1.91 + return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue); 1.92 + }, 1.93 + 1.94 + promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) { 1.95 + let p = this.pickPrompter(domWin); 1.96 + return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue); 1.97 + }, 1.98 + 1.99 + select : function (domWin, title, text, count, list, selected) { 1.100 + let p = this.pickPrompter(domWin); 1.101 + return p.select(title, text, count, list, selected); 1.102 + }, 1.103 + 1.104 + 1.105 + /* ---------- nsIPromptService2 ---------- */ 1.106 + 1.107 + 1.108 + promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) { 1.109 + let p = this.pickPrompter(domWin); 1.110 + return p.promptAuth(channel, level, authInfo, checkLabel, checkValue); 1.111 + }, 1.112 + 1.113 + asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) { 1.114 + let p = this.pickPrompter(domWin); 1.115 + return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue); 1.116 + }, 1.117 + 1.118 +}; 1.119 + 1.120 + 1.121 +// Common utils not specific to a particular prompter style. 1.122 +let PromptUtilsTemp = { 1.123 + __proto__ : PromptUtils, 1.124 + 1.125 + getLocalizedString : function (key, formatArgs) { 1.126 + if (formatArgs) 1.127 + return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length); 1.128 + else 1.129 + return this.strBundle.GetStringFromName(key); 1.130 + }, 1.131 + 1.132 + confirmExHelper : function (flags, button0, button1, button2) { 1.133 + const BUTTON_DEFAULT_MASK = 0x03000000; 1.134 + let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24; 1.135 + let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE); 1.136 + 1.137 + // Flags can be used to select a specific pre-defined button label or 1.138 + // a caller-supplied string (button0/button1/button2). If no flags are 1.139 + // set for a button, then the button won't be shown. 1.140 + let argText = [button0, button1, button2]; 1.141 + let buttonLabels = [null, null, null]; 1.142 + for (let i = 0; i < 3; i++) { 1.143 + let buttonLabel; 1.144 + switch (flags & 0xff) { 1.145 + case Ci.nsIPrompt.BUTTON_TITLE_OK: 1.146 + buttonLabel = PromptUtils.getLocalizedString("OK"); 1.147 + break; 1.148 + case Ci.nsIPrompt.BUTTON_TITLE_CANCEL: 1.149 + buttonLabel = PromptUtils.getLocalizedString("Cancel"); 1.150 + break; 1.151 + case Ci.nsIPrompt.BUTTON_TITLE_YES: 1.152 + buttonLabel = PromptUtils.getLocalizedString("Yes"); 1.153 + break; 1.154 + case Ci.nsIPrompt.BUTTON_TITLE_NO: 1.155 + buttonLabel = PromptUtils.getLocalizedString("No"); 1.156 + break; 1.157 + case Ci.nsIPrompt.BUTTON_TITLE_SAVE: 1.158 + buttonLabel = PromptUtils.getLocalizedString("Save"); 1.159 + break; 1.160 + case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE: 1.161 + buttonLabel = PromptUtils.getLocalizedString("DontSave"); 1.162 + break; 1.163 + case Ci.nsIPrompt.BUTTON_TITLE_REVERT: 1.164 + buttonLabel = PromptUtils.getLocalizedString("Revert"); 1.165 + break; 1.166 + case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING: 1.167 + buttonLabel = argText[i]; 1.168 + break; 1.169 + } 1.170 + if (buttonLabel) 1.171 + buttonLabels[i] = buttonLabel; 1.172 + flags >>= 8; 1.173 + } 1.174 + 1.175 + return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled]; 1.176 + }, 1.177 + 1.178 + getAuthInfo : function (authInfo) { 1.179 + let username, password; 1.180 + 1.181 + let flags = authInfo.flags; 1.182 + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain) 1.183 + username = authInfo.domain + "\\" + authInfo.username; 1.184 + else 1.185 + username = authInfo.username; 1.186 + 1.187 + password = authInfo.password; 1.188 + 1.189 + return [username, password]; 1.190 + }, 1.191 + 1.192 + setAuthInfo : function (authInfo, username, password) { 1.193 + let flags = authInfo.flags; 1.194 + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { 1.195 + // Domain is separated from username by a backslash 1.196 + let idx = username.indexOf("\\"); 1.197 + if (idx == -1) { 1.198 + authInfo.username = username; 1.199 + } else { 1.200 + authInfo.domain = username.substring(0, idx); 1.201 + authInfo.username = username.substring(idx+1); 1.202 + } 1.203 + } else { 1.204 + authInfo.username = username; 1.205 + } 1.206 + authInfo.password = password; 1.207 + }, 1.208 + 1.209 + // Copied from login manager 1.210 + getFormattedHostname : function (uri) { 1.211 + let scheme = uri.scheme; 1.212 + let hostname = scheme + "://" + uri.host; 1.213 + 1.214 + // If the URI explicitly specified a port, only include it when 1.215 + // it's not the default. (We never want "http://foo.com:80") 1.216 + port = uri.port; 1.217 + if (port != -1) { 1.218 + let handler = Services.io.getProtocolHandler(scheme); 1.219 + if (port != handler.defaultPort) 1.220 + hostname += ":" + port; 1.221 + } 1.222 + 1.223 + return hostname; 1.224 + }, 1.225 + 1.226 + // Copied from login manager 1.227 + getAuthTarget : function (aChannel, aAuthInfo) { 1.228 + let hostname, realm; 1.229 + 1.230 + // If our proxy is demanding authentication, don't use the 1.231 + // channel's actual destination. 1.232 + if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { 1.233 + if (!(aChannel instanceof Ci.nsIProxiedChannel)) 1.234 + throw "proxy auth needs nsIProxiedChannel"; 1.235 + 1.236 + let info = aChannel.proxyInfo; 1.237 + if (!info) 1.238 + throw "proxy auth needs nsIProxyInfo"; 1.239 + 1.240 + // Proxies don't have a scheme, but we'll use "moz-proxy://" 1.241 + // so that it's more obvious what the login is for. 1.242 + let idnService = Cc["@mozilla.org/network/idn-service;1"]. 1.243 + getService(Ci.nsIIDNService); 1.244 + hostname = "moz-proxy://" + 1.245 + idnService.convertUTF8toACE(info.host) + 1.246 + ":" + info.port; 1.247 + realm = aAuthInfo.realm; 1.248 + if (!realm) 1.249 + realm = hostname; 1.250 + 1.251 + return [hostname, realm]; 1.252 + } 1.253 + 1.254 + hostname = this.getFormattedHostname(aChannel.URI); 1.255 + 1.256 + // If a HTTP WWW-Authenticate header specified a realm, that value 1.257 + // will be available here. If it wasn't set or wasn't HTTP, we'll use 1.258 + // the formatted hostname instead. 1.259 + realm = aAuthInfo.realm; 1.260 + if (!realm) 1.261 + realm = hostname; 1.262 + 1.263 + return [hostname, realm]; 1.264 + }, 1.265 + 1.266 + 1.267 + makeAuthMessage : function (channel, authInfo) { 1.268 + let isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); 1.269 + let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); 1.270 + 1.271 + let username = authInfo.username; 1.272 + let [displayHost, realm] = this.getAuthTarget(channel, authInfo); 1.273 + 1.274 + // Suppress "the site says: $realm" when we synthesized a missing realm. 1.275 + if (!authInfo.realm && !isProxy) 1.276 + realm = ""; 1.277 + 1.278 + // Trim obnoxiously long realms. 1.279 + if (realm.length > 150) { 1.280 + realm = realm.substring(0, 150); 1.281 + // Append "..." (or localized equivalent). 1.282 + realm += this.ellipsis; 1.283 + } 1.284 + 1.285 + let text; 1.286 + if (isProxy) 1.287 + text = PromptUtils.getLocalizedString("EnterLoginForProxy", [realm, displayHost]); 1.288 + else if (isPassOnly) 1.289 + text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]); 1.290 + else if (!realm) 1.291 + text = PromptUtils.getLocalizedString("EnterUserPasswordFor", [displayHost]); 1.292 + else 1.293 + text = PromptUtils.getLocalizedString("EnterLoginForRealm", [realm, displayHost]); 1.294 + 1.295 + return text; 1.296 + }, 1.297 + 1.298 + getTabModalPrompt : function (domWin) { 1.299 + var promptBox = null; 1.300 + 1.301 + try { 1.302 + // Get the topmost window, in case we're in a frame. 1.303 + var promptWin = domWin.top; 1.304 + 1.305 + // Get the chrome window for the content window we're using. 1.306 + // (Unwrap because we need a non-IDL property below.) 1.307 + var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor) 1.308 + .getInterface(Ci.nsIWebNavigation) 1.309 + .QueryInterface(Ci.nsIDocShell) 1.310 + .chromeEventHandler.ownerDocument 1.311 + .defaultView.wrappedJSObject; 1.312 + 1.313 + if (chromeWin.getTabModalPromptBox) 1.314 + promptBox = chromeWin.getTabModalPromptBox(promptWin); 1.315 + } catch (e) { 1.316 + // If any errors happen, just assume no tabmodal prompter. 1.317 + } 1.318 + 1.319 + return promptBox; 1.320 + }, 1.321 +}; 1.322 + 1.323 +PromptUtils = PromptUtilsTemp; 1.324 + 1.325 +XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () { 1.326 + let bunService = Cc["@mozilla.org/intl/stringbundle;1"]. 1.327 + getService(Ci.nsIStringBundleService); 1.328 + let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties"); 1.329 + if (!bundle) 1.330 + throw "String bundle for Prompter not present!"; 1.331 + return bundle; 1.332 +}); 1.333 + 1.334 +XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () { 1.335 + let ellipsis = "\u2026"; 1.336 + try { 1.337 + ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; 1.338 + } catch (e) { } 1.339 + return ellipsis; 1.340 +}); 1.341 + 1.342 + 1.343 + 1.344 +function openModalWindow(domWin, uri, args) { 1.345 + // There's an implied contract that says modal prompts should still work 1.346 + // when no "parent" window is passed for the dialog (eg, the "Master 1.347 + // Password" dialog does this). These prompts must be shown even if there 1.348 + // are *no* visible windows at all. 1.349 + // There's also a requirement for prompts to be blocked if a window is 1.350 + // passed and that window is hidden (eg, auth prompts are supressed if the 1.351 + // passed window is the hidden window). 1.352 + // See bug 875157 comment 30 for more... 1.353 + if (domWin) { 1.354 + // a domWin was passed, so we can apply the check for it being hidden. 1.355 + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) 1.356 + .getInterface(Ci.nsIDOMWindowUtils); 1.357 + 1.358 + if (winUtils && !winUtils.isParentWindowMainWidgetVisible) { 1.359 + throw Components.Exception("Cannot call openModalWindow on a hidden window", 1.360 + Cr.NS_ERROR_NOT_AVAILABLE); 1.361 + } 1.362 + } else { 1.363 + // We try and find a window to use as the parent, but don't consider 1.364 + // if that is visible before showing the prompt. 1.365 + domWin = Services.ww.activeWindow; 1.366 + // domWin may still be null here if there are _no_ windows open. 1.367 + } 1.368 + // Note that we don't need to fire DOMWillOpenModalDialog and 1.369 + // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal 1.370 + // will do that. Similarly for enterModalState / leaveModalState. 1.371 + 1.372 + Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args); 1.373 +} 1.374 + 1.375 +function openTabPrompt(domWin, tabPrompt, args) { 1.376 + PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); 1.377 + 1.378 + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) 1.379 + .getInterface(Ci.nsIDOMWindowUtils); 1.380 + winUtils.enterModalState(); 1.381 + 1.382 + // We provide a callback so the prompt can close itself. We don't want to 1.383 + // wait for this event loop to return... Otherwise the presence of other 1.384 + // prompts on the call stack would in this dialog appearing unresponsive 1.385 + // until the other prompts had been closed. 1.386 + let callbackInvoked = false; 1.387 + let newPrompt; 1.388 + function onPromptClose(forceCleanup) { 1.389 + if (!newPrompt && !forceCleanup) 1.390 + return; 1.391 + callbackInvoked = true; 1.392 + if (newPrompt) 1.393 + tabPrompt.removePrompt(newPrompt); 1.394 + 1.395 + domWin.removeEventListener("pagehide", pagehide); 1.396 + 1.397 + winUtils.leaveModalState(); 1.398 + 1.399 + PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); 1.400 + } 1.401 + 1.402 + domWin.addEventListener("pagehide", pagehide); 1.403 + function pagehide() { 1.404 + domWin.removeEventListener("pagehide", pagehide); 1.405 + 1.406 + if (newPrompt) { 1.407 + newPrompt.abortPrompt(); 1.408 + } 1.409 + } 1.410 + 1.411 + try { 1.412 + let topPrincipal = domWin.top.document.nodePrincipal; 1.413 + let promptPrincipal = domWin.document.nodePrincipal; 1.414 + args.showAlertOrigin = topPrincipal.equals(promptPrincipal); 1.415 + args.promptActive = true; 1.416 + 1.417 + newPrompt = tabPrompt.appendPrompt(args, onPromptClose); 1.418 + 1.419 + // TODO since we don't actually open a window, need to check if 1.420 + // there's other stuff in nsWindowWatcher::OpenWindowInternal 1.421 + // that we might need to do here as well. 1.422 + 1.423 + let thread = Services.tm.currentThread; 1.424 + while (args.promptActive) 1.425 + thread.processNextEvent(true); 1.426 + delete args.promptActive; 1.427 + 1.428 + if (args.promptAborted) 1.429 + throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE); 1.430 + } finally { 1.431 + // If the prompt unexpectedly failed to invoke the callback, do so here. 1.432 + if (!callbackInvoked) 1.433 + onPromptClose(true); 1.434 + } 1.435 +} 1.436 + 1.437 +function openRemotePrompt(domWin, args, tabPrompt) { 1.438 + let messageManager = domWin.QueryInterface(Ci.nsIInterfaceRequestor) 1.439 + .getInterface(Ci.nsIWebNavigation) 1.440 + .QueryInterface(Ci.nsIDocShell) 1.441 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.442 + .getInterface(Ci.nsITabChild) 1.443 + .messageManager; 1.444 + 1.445 + PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog"); 1.446 + 1.447 + let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor) 1.448 + .getInterface(Ci.nsIDOMWindowUtils); 1.449 + winUtils.enterModalState(); 1.450 + let closed = false; 1.451 + 1.452 + // It should be hard or impossible to cause a window to create multiple 1.453 + // prompts, but just in case, give our prompt an ID. 1.454 + let id = "id" + Cc["@mozilla.org/uuid-generator;1"] 1.455 + .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); 1.456 + 1.457 + messageManager.addMessageListener("Prompt:Close", function listener(message) { 1.458 + if (message.data._remoteId !== id) { 1.459 + return; 1.460 + } 1.461 + 1.462 + messageManager.removeMessageListener("Prompt:Close", listener); 1.463 + domWin.removeEventListener("pagehide", pagehide); 1.464 + 1.465 + winUtils.leaveModalState(); 1.466 + PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed"); 1.467 + 1.468 + // Copy the response from the closed prompt into our args, it will be 1.469 + // read by our caller. 1.470 + if (message.data) { 1.471 + for (let key in message.data) { 1.472 + args[key] = message.data[key]; 1.473 + } 1.474 + } 1.475 + 1.476 + // Exit our nested event loop when we unwind. 1.477 + closed = true; 1.478 + }); 1.479 + 1.480 + domWin.addEventListener("pagehide", pagehide); 1.481 + function pagehide() { 1.482 + domWin.removeEventListener("pagehide", pagehide); 1.483 + messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id }); 1.484 + } 1.485 + 1.486 + let topPrincipal = domWin.top.document.nodePrincipal; 1.487 + let promptPrincipal = domWin.document.nodePrincipal; 1.488 + args.showAlertOrigin = topPrincipal.equals(promptPrincipal); 1.489 + 1.490 + args._remoteId = id; 1.491 + 1.492 + messageManager.sendAsyncMessage("Prompt:Open", args, {}); 1.493 + 1.494 + let thread = Services.tm.currentThread; 1.495 + while (!closed) { 1.496 + thread.processNextEvent(true); 1.497 + } 1.498 +} 1.499 + 1.500 +function ModalPrompter(domWin) { 1.501 + this.domWin = domWin; 1.502 +} 1.503 +ModalPrompter.prototype = { 1.504 + domWin : null, 1.505 + /* 1.506 + * Default to not using a tab-modal prompt, unless the caller opts in by 1.507 + * QIing to nsIWritablePropertyBag and setting the value of this property 1.508 + * to true. 1.509 + */ 1.510 + allowTabModal : false, 1.511 + 1.512 + QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, 1.513 + Ci.nsIAuthPrompt2, 1.514 + Ci.nsIWritablePropertyBag2]), 1.515 + 1.516 + 1.517 + /* ---------- internal methods ---------- */ 1.518 + 1.519 + 1.520 + openPrompt : function (args) { 1.521 + // Check pref, if false/missing do not ever allow tab-modal prompts. 1.522 + const prefName = "prompts.tab_modal.enabled"; 1.523 + let prefValue = false; 1.524 + if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL) 1.525 + prefValue = Services.prefs.getBoolPref(prefName); 1.526 + 1.527 + let allowTabModal = this.allowTabModal && prefValue; 1.528 + 1.529 + if (allowTabModal && this.domWin) { 1.530 + if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { 1.531 + openRemotePrompt(this.domWin, args, true); 1.532 + return; 1.533 + } 1.534 + 1.535 + let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin); 1.536 + if (tabPrompt) { 1.537 + openTabPrompt(this.domWin, tabPrompt, args); 1.538 + return; 1.539 + } 1.540 + } 1.541 + 1.542 + // If we can't do a tab modal prompt, fallback to using a window-modal dialog. 1.543 + const COMMON_DIALOG = "chrome://global/content/commonDialog.xul"; 1.544 + const SELECT_DIALOG = "chrome://global/content/selectDialog.xul"; 1.545 + 1.546 + let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG; 1.547 + 1.548 + if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { 1.549 + args.uri = uri; 1.550 + openRemotePrompt(this.domWin, args); 1.551 + return; 1.552 + } 1.553 + 1.554 + let propBag = PromptUtils.objectToPropBag(args); 1.555 + openModalWindow(this.domWin, uri, propBag); 1.556 + PromptUtils.propBagToObject(propBag, args); 1.557 + }, 1.558 + 1.559 + 1.560 + 1.561 + /* 1.562 + * ---------- interface disambiguation ---------- 1.563 + * 1.564 + * nsIPrompt and nsIAuthPrompt share 3 method names with slightly 1.565 + * different arguments. All but prompt() have the same number of 1.566 + * arguments, so look at the arg types to figure out how we're being 1.567 + * called. :-( 1.568 + */ 1.569 + prompt : function() { 1.570 + // also, the nsIPrompt flavor has 5 args instead of 6. 1.571 + if (typeof arguments[2] == "object") 1.572 + return this.nsIPrompt_prompt.apply(this, arguments); 1.573 + else 1.574 + return this.nsIAuthPrompt_prompt.apply(this, arguments); 1.575 + }, 1.576 + 1.577 + promptUsernameAndPassword : function() { 1.578 + // Both have 6 args, so use types. 1.579 + if (typeof arguments[2] == "object") 1.580 + return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); 1.581 + else 1.582 + return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); 1.583 + }, 1.584 + 1.585 + promptPassword : function() { 1.586 + // Both have 5 args, so use types. 1.587 + if (typeof arguments[2] == "object") 1.588 + return this.nsIPrompt_promptPassword.apply(this, arguments); 1.589 + else 1.590 + return this.nsIAuthPrompt_promptPassword.apply(this, arguments); 1.591 + }, 1.592 + 1.593 + 1.594 + /* ---------- nsIPrompt ---------- */ 1.595 + 1.596 + 1.597 + alert : function (title, text) { 1.598 + if (!title) 1.599 + title = PromptUtils.getLocalizedString("Alert"); 1.600 + 1.601 + let args = { 1.602 + promptType: "alert", 1.603 + title: title, 1.604 + text: text, 1.605 + }; 1.606 + 1.607 + this.openPrompt(args); 1.608 + }, 1.609 + 1.610 + alertCheck : function (title, text, checkLabel, checkValue) { 1.611 + if (!title) 1.612 + title = PromptUtils.getLocalizedString("Alert"); 1.613 + 1.614 + let args = { 1.615 + promptType: "alertCheck", 1.616 + title: title, 1.617 + text: text, 1.618 + checkLabel: checkLabel, 1.619 + checked: checkValue.value, 1.620 + }; 1.621 + 1.622 + this.openPrompt(args); 1.623 + 1.624 + // Checkbox state always returned, even if cancel clicked. 1.625 + checkValue.value = args.checked; 1.626 + }, 1.627 + 1.628 + confirm : function (title, text) { 1.629 + if (!title) 1.630 + title = PromptUtils.getLocalizedString("Confirm"); 1.631 + 1.632 + let args = { 1.633 + promptType: "confirm", 1.634 + title: title, 1.635 + text: text, 1.636 + ok: false, 1.637 + }; 1.638 + 1.639 + this.openPrompt(args); 1.640 + 1.641 + // Did user click Ok or Cancel? 1.642 + return args.ok; 1.643 + }, 1.644 + 1.645 + confirmCheck : function (title, text, checkLabel, checkValue) { 1.646 + if (!title) 1.647 + title = PromptUtils.getLocalizedString("ConfirmCheck"); 1.648 + 1.649 + let args = { 1.650 + promptType: "confirmCheck", 1.651 + title: title, 1.652 + text: text, 1.653 + checkLabel: checkLabel, 1.654 + checked: checkValue.value, 1.655 + ok: false, 1.656 + }; 1.657 + 1.658 + this.openPrompt(args); 1.659 + 1.660 + // Checkbox state always returned, even if cancel clicked. 1.661 + checkValue.value = args.checked; 1.662 + 1.663 + // Did user click Ok or Cancel? 1.664 + return args.ok; 1.665 + }, 1.666 + 1.667 + confirmEx : function (title, text, flags, button0, button1, button2, 1.668 + checkLabel, checkValue) { 1.669 + 1.670 + if (!title) 1.671 + title = PromptUtils.getLocalizedString("Confirm"); 1.672 + 1.673 + let args = { 1.674 + promptType: "confirmEx", 1.675 + title: title, 1.676 + text: text, 1.677 + checkLabel: checkLabel, 1.678 + checked: checkValue.value, 1.679 + ok: false, 1.680 + buttonNumClicked: 1, 1.681 + }; 1.682 + 1.683 + let [label0, label1, label2, defaultButtonNum, isDelayEnabled] = 1.684 + PromptUtils.confirmExHelper(flags, button0, button1, button2); 1.685 + 1.686 + args.defaultButtonNum = defaultButtonNum; 1.687 + args.enableDelay = isDelayEnabled; 1.688 + 1.689 + if (label0) { 1.690 + args.button0Label = label0; 1.691 + if (label1) { 1.692 + args.button1Label = label1; 1.693 + if (label2) { 1.694 + args.button2Label = label2; 1.695 + } 1.696 + } 1.697 + } 1.698 + 1.699 + this.openPrompt(args); 1.700 + 1.701 + // Checkbox state always returned, even if cancel clicked. 1.702 + checkValue.value = args.checked; 1.703 + 1.704 + // Get the number of the button the user clicked. 1.705 + return args.buttonNumClicked; 1.706 + }, 1.707 + 1.708 + nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) { 1.709 + if (!title) 1.710 + title = PromptUtils.getLocalizedString("Prompt"); 1.711 + 1.712 + let args = { 1.713 + promptType: "prompt", 1.714 + title: title, 1.715 + text: text, 1.716 + value: value.value, 1.717 + checkLabel: checkLabel, 1.718 + checked: checkValue.value, 1.719 + ok: false, 1.720 + }; 1.721 + 1.722 + this.openPrompt(args); 1.723 + 1.724 + // Did user click Ok or Cancel? 1.725 + let ok = args.ok; 1.726 + if (ok) { 1.727 + checkValue.value = args.checked; 1.728 + value.value = args.value; 1.729 + } 1.730 + 1.731 + return ok; 1.732 + }, 1.733 + 1.734 + nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) { 1.735 + if (!title) 1.736 + title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2"); 1.737 + 1.738 + let args = { 1.739 + promptType: "promptUserAndPass", 1.740 + title: title, 1.741 + text: text, 1.742 + user: user.value, 1.743 + pass: pass.value, 1.744 + checkLabel: checkLabel, 1.745 + checked: checkValue.value, 1.746 + ok: false, 1.747 + }; 1.748 + 1.749 + this.openPrompt(args); 1.750 + 1.751 + // Did user click Ok or Cancel? 1.752 + let ok = args.ok; 1.753 + if (ok) { 1.754 + checkValue.value = args.checked; 1.755 + user.value = args.user; 1.756 + pass.value = args.pass; 1.757 + } 1.758 + 1.759 + return ok; 1.760 + }, 1.761 + 1.762 + nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) { 1.763 + if (!title) 1.764 + title = PromptUtils.getLocalizedString("PromptPassword2"); 1.765 + 1.766 + let args = { 1.767 + promptType: "promptPassword", 1.768 + title: title, 1.769 + text: text, 1.770 + pass: pass.value, 1.771 + checkLabel: checkLabel, 1.772 + checked: checkValue.value, 1.773 + ok: false, 1.774 + } 1.775 + 1.776 + this.openPrompt(args); 1.777 + 1.778 + // Did user click Ok or Cancel? 1.779 + let ok = args.ok; 1.780 + if (ok) { 1.781 + checkValue.value = args.checked; 1.782 + pass.value = args.pass; 1.783 + } 1.784 + 1.785 + return ok; 1.786 + }, 1.787 + 1.788 + select : function (title, text, count, list, selected) { 1.789 + if (!title) 1.790 + title = PromptUtils.getLocalizedString("Select"); 1.791 + 1.792 + let args = { 1.793 + promptType: "select", 1.794 + title: title, 1.795 + text: text, 1.796 + list: list, 1.797 + selected: -1, 1.798 + ok: false, 1.799 + }; 1.800 + 1.801 + this.openPrompt(args); 1.802 + 1.803 + // Did user click Ok or Cancel? 1.804 + let ok = args.ok; 1.805 + if (ok) 1.806 + selected.value = args.selected; 1.807 + 1.808 + return ok; 1.809 + }, 1.810 + 1.811 + 1.812 + /* ---------- nsIAuthPrompt ---------- */ 1.813 + 1.814 + 1.815 + nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { 1.816 + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp 1.817 + if (defaultText) 1.818 + result.value = defaultText; 1.819 + return this.nsIPrompt_prompt(title, text, result, null, {}); 1.820 + }, 1.821 + 1.822 + nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) { 1.823 + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp 1.824 + return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {}); 1.825 + }, 1.826 + 1.827 + nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) { 1.828 + // The passwordRealm and savePassword args were ignored by nsPrompt.cpp 1.829 + return this.nsIPrompt_promptPassword(title, text, pass, null, {}); 1.830 + }, 1.831 + 1.832 + 1.833 + /* ---------- nsIAuthPrompt2 ---------- */ 1.834 + 1.835 + 1.836 + promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { 1.837 + let message = PromptUtils.makeAuthMessage(channel, authInfo); 1.838 + 1.839 + let [username, password] = PromptUtils.getAuthInfo(authInfo); 1.840 + 1.841 + let userParam = { value: username }; 1.842 + let passParam = { value: password }; 1.843 + 1.844 + let ok; 1.845 + if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) 1.846 + ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue); 1.847 + else 1.848 + ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue); 1.849 + 1.850 + if (ok) 1.851 + PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); 1.852 + return ok; 1.853 + }, 1.854 + 1.855 + asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { 1.856 + // Nothing calls this directly; netwerk ends up going through 1.857 + // nsIPromptService::GetPrompt, which delegates to login manager. 1.858 + // Login manger handles the async bits itself, and only calls out 1.859 + // promptAuth, never asyncPromptAuth. 1.860 + // 1.861 + // Bug 565582 will change this. 1.862 + throw Cr.NS_ERROR_NOT_IMPLEMENTED; 1.863 + }, 1.864 + 1.865 + /* ---------- nsIWritablePropertyBag2 ---------- */ 1.866 + 1.867 + // Only a partial implementation, for one specific use case... 1.868 + 1.869 + setPropertyAsBool : function(name, value) { 1.870 + if (name == "allowTabModal") 1.871 + this.allowTabModal = value; 1.872 + else 1.873 + throw Cr.NS_ERROR_ILLEGAL_VALUE; 1.874 + }, 1.875 +}; 1.876 + 1.877 + 1.878 +function AuthPromptAdapterFactory() { 1.879 +} 1.880 +AuthPromptAdapterFactory.prototype = { 1.881 + classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"), 1.882 + QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]), 1.883 + 1.884 + /* ---------- nsIAuthPromptAdapterFactory ---------- */ 1.885 + 1.886 + createAdapter : function (oldPrompter) { 1.887 + return new AuthPromptAdapter(oldPrompter); 1.888 + } 1.889 +}; 1.890 + 1.891 + 1.892 +// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell. 1.893 +function AuthPromptAdapter(oldPrompter) { 1.894 + this.oldPrompter = oldPrompter; 1.895 +} 1.896 +AuthPromptAdapter.prototype = { 1.897 + QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]), 1.898 + oldPrompter : null, 1.899 + 1.900 + /* ---------- nsIAuthPrompt2 ---------- */ 1.901 + 1.902 + promptAuth : function (channel, level, authInfo, checkLabel, checkValue) { 1.903 + let message = PromptUtils.makeAuthMessage(channel, authInfo); 1.904 + 1.905 + let [username, password] = PromptUtils.getAuthInfo(authInfo); 1.906 + let userParam = { value: username }; 1.907 + let passParam = { value: password }; 1.908 + 1.909 + let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo); 1.910 + let authTarget = host + " (" + realm + ")"; 1.911 + 1.912 + let ok; 1.913 + if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) 1.914 + ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam); 1.915 + else 1.916 + ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam); 1.917 + 1.918 + if (ok) 1.919 + PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value); 1.920 + return ok; 1.921 + }, 1.922 + 1.923 + asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) { 1.924 + throw Cr.NS_ERROR_NOT_IMPLEMENTED; 1.925 + } 1.926 +}; 1.927 + 1.928 + 1.929 +// Wrapper using the old embedding contractID, since it's already common in 1.930 +// the addon ecosystem. 1.931 +function EmbedPrompter() { 1.932 +} 1.933 +EmbedPrompter.prototype = new Prompter(); 1.934 +EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"); 1.935 + 1.936 +var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory]; 1.937 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);