toolkit/components/prompts/src/nsPrompter.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5
michael@0 6 const Cc = Components.classes;
michael@0 7 const Ci = Components.interfaces;
michael@0 8 const Cr = Components.results;
michael@0 9 const Cu = Components.utils;
michael@0 10
michael@0 11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 12 Cu.import("resource://gre/modules/Services.jsm");
michael@0 13 Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
michael@0 14
michael@0 15 function Prompter() {
michael@0 16 // Note that EmbedPrompter clones this implementation.
michael@0 17 }
michael@0 18
michael@0 19 Prompter.prototype = {
michael@0 20 classID : Components.ID("{1c978d25-b37f-43a8-a2d6-0c7a239ead87}"),
michael@0 21 QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
michael@0 22
michael@0 23
michael@0 24 /* ---------- private memebers ---------- */
michael@0 25
michael@0 26 pickPrompter : function (domWin) {
michael@0 27 return new ModalPrompter(domWin);
michael@0 28 },
michael@0 29
michael@0 30
michael@0 31 /* ---------- nsIPromptFactory ---------- */
michael@0 32
michael@0 33
michael@0 34 getPrompt : function (domWin, iid) {
michael@0 35 // This is still kind of dumb; the C++ code delegated to login manager
michael@0 36 // here, which in turn calls back into us via nsIPromptService2.
michael@0 37 if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) {
michael@0 38 try {
michael@0 39 let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
michael@0 40 getService(Ci.nsIPromptFactory);
michael@0 41 return pwmgr.getPrompt(domWin, iid);
michael@0 42 } catch (e) {
michael@0 43 Cu.reportError("nsPrompter: Delegation to password manager failed: " + e);
michael@0 44 }
michael@0 45 }
michael@0 46
michael@0 47 let p = new ModalPrompter(domWin);
michael@0 48 p.QueryInterface(iid);
michael@0 49 return p;
michael@0 50 },
michael@0 51
michael@0 52
michael@0 53 /* ---------- nsIPromptService ---------- */
michael@0 54
michael@0 55
michael@0 56 alert : function (domWin, title, text) {
michael@0 57 let p = this.pickPrompter(domWin);
michael@0 58 p.alert(title, text);
michael@0 59 },
michael@0 60
michael@0 61 alertCheck : function (domWin, title, text, checkLabel, checkValue) {
michael@0 62 let p = this.pickPrompter(domWin);
michael@0 63 p.alertCheck(title, text, checkLabel, checkValue);
michael@0 64 },
michael@0 65
michael@0 66 confirm : function (domWin, title, text) {
michael@0 67 let p = this.pickPrompter(domWin);
michael@0 68 return p.confirm(title, text);
michael@0 69 },
michael@0 70
michael@0 71 confirmCheck : function (domWin, title, text, checkLabel, checkValue) {
michael@0 72 let p = this.pickPrompter(domWin);
michael@0 73 return p.confirmCheck(title, text, checkLabel, checkValue);
michael@0 74 },
michael@0 75
michael@0 76 confirmEx : function (domWin, title, text, flags, button0, button1, button2, checkLabel, checkValue) {
michael@0 77 let p = this.pickPrompter(domWin);
michael@0 78 return p.confirmEx(title, text, flags, button0, button1, button2, checkLabel, checkValue);
michael@0 79 },
michael@0 80
michael@0 81 prompt : function (domWin, title, text, value, checkLabel, checkValue) {
michael@0 82 let p = this.pickPrompter(domWin);
michael@0 83 return p.nsIPrompt_prompt(title, text, value, checkLabel, checkValue);
michael@0 84 },
michael@0 85
michael@0 86 promptUsernameAndPassword : function (domWin, title, text, user, pass, checkLabel, checkValue) {
michael@0 87 let p = this.pickPrompter(domWin);
michael@0 88 return p.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, checkLabel, checkValue);
michael@0 89 },
michael@0 90
michael@0 91 promptPassword : function (domWin, title, text, pass, checkLabel, checkValue) {
michael@0 92 let p = this.pickPrompter(domWin);
michael@0 93 return p.nsIPrompt_promptPassword(title, text, pass, checkLabel, checkValue);
michael@0 94 },
michael@0 95
michael@0 96 select : function (domWin, title, text, count, list, selected) {
michael@0 97 let p = this.pickPrompter(domWin);
michael@0 98 return p.select(title, text, count, list, selected);
michael@0 99 },
michael@0 100
michael@0 101
michael@0 102 /* ---------- nsIPromptService2 ---------- */
michael@0 103
michael@0 104
michael@0 105 promptAuth : function (domWin, channel, level, authInfo, checkLabel, checkValue) {
michael@0 106 let p = this.pickPrompter(domWin);
michael@0 107 return p.promptAuth(channel, level, authInfo, checkLabel, checkValue);
michael@0 108 },
michael@0 109
michael@0 110 asyncPromptAuth : function (domWin, channel, callback, context, level, authInfo, checkLabel, checkValue) {
michael@0 111 let p = this.pickPrompter(domWin);
michael@0 112 return p.asyncPromptAuth(channel, callback, context, level, authInfo, checkLabel, checkValue);
michael@0 113 },
michael@0 114
michael@0 115 };
michael@0 116
michael@0 117
michael@0 118 // Common utils not specific to a particular prompter style.
michael@0 119 let PromptUtilsTemp = {
michael@0 120 __proto__ : PromptUtils,
michael@0 121
michael@0 122 getLocalizedString : function (key, formatArgs) {
michael@0 123 if (formatArgs)
michael@0 124 return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length);
michael@0 125 else
michael@0 126 return this.strBundle.GetStringFromName(key);
michael@0 127 },
michael@0 128
michael@0 129 confirmExHelper : function (flags, button0, button1, button2) {
michael@0 130 const BUTTON_DEFAULT_MASK = 0x03000000;
michael@0 131 let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24;
michael@0 132 let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE);
michael@0 133
michael@0 134 // Flags can be used to select a specific pre-defined button label or
michael@0 135 // a caller-supplied string (button0/button1/button2). If no flags are
michael@0 136 // set for a button, then the button won't be shown.
michael@0 137 let argText = [button0, button1, button2];
michael@0 138 let buttonLabels = [null, null, null];
michael@0 139 for (let i = 0; i < 3; i++) {
michael@0 140 let buttonLabel;
michael@0 141 switch (flags & 0xff) {
michael@0 142 case Ci.nsIPrompt.BUTTON_TITLE_OK:
michael@0 143 buttonLabel = PromptUtils.getLocalizedString("OK");
michael@0 144 break;
michael@0 145 case Ci.nsIPrompt.BUTTON_TITLE_CANCEL:
michael@0 146 buttonLabel = PromptUtils.getLocalizedString("Cancel");
michael@0 147 break;
michael@0 148 case Ci.nsIPrompt.BUTTON_TITLE_YES:
michael@0 149 buttonLabel = PromptUtils.getLocalizedString("Yes");
michael@0 150 break;
michael@0 151 case Ci.nsIPrompt.BUTTON_TITLE_NO:
michael@0 152 buttonLabel = PromptUtils.getLocalizedString("No");
michael@0 153 break;
michael@0 154 case Ci.nsIPrompt.BUTTON_TITLE_SAVE:
michael@0 155 buttonLabel = PromptUtils.getLocalizedString("Save");
michael@0 156 break;
michael@0 157 case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE:
michael@0 158 buttonLabel = PromptUtils.getLocalizedString("DontSave");
michael@0 159 break;
michael@0 160 case Ci.nsIPrompt.BUTTON_TITLE_REVERT:
michael@0 161 buttonLabel = PromptUtils.getLocalizedString("Revert");
michael@0 162 break;
michael@0 163 case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING:
michael@0 164 buttonLabel = argText[i];
michael@0 165 break;
michael@0 166 }
michael@0 167 if (buttonLabel)
michael@0 168 buttonLabels[i] = buttonLabel;
michael@0 169 flags >>= 8;
michael@0 170 }
michael@0 171
michael@0 172 return [buttonLabels[0], buttonLabels[1], buttonLabels[2], defaultButtonNum, isDelayEnabled];
michael@0 173 },
michael@0 174
michael@0 175 getAuthInfo : function (authInfo) {
michael@0 176 let username, password;
michael@0 177
michael@0 178 let flags = authInfo.flags;
michael@0 179 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && authInfo.domain)
michael@0 180 username = authInfo.domain + "\\" + authInfo.username;
michael@0 181 else
michael@0 182 username = authInfo.username;
michael@0 183
michael@0 184 password = authInfo.password;
michael@0 185
michael@0 186 return [username, password];
michael@0 187 },
michael@0 188
michael@0 189 setAuthInfo : function (authInfo, username, password) {
michael@0 190 let flags = authInfo.flags;
michael@0 191 if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
michael@0 192 // Domain is separated from username by a backslash
michael@0 193 let idx = username.indexOf("\\");
michael@0 194 if (idx == -1) {
michael@0 195 authInfo.username = username;
michael@0 196 } else {
michael@0 197 authInfo.domain = username.substring(0, idx);
michael@0 198 authInfo.username = username.substring(idx+1);
michael@0 199 }
michael@0 200 } else {
michael@0 201 authInfo.username = username;
michael@0 202 }
michael@0 203 authInfo.password = password;
michael@0 204 },
michael@0 205
michael@0 206 // Copied from login manager
michael@0 207 getFormattedHostname : function (uri) {
michael@0 208 let scheme = uri.scheme;
michael@0 209 let hostname = scheme + "://" + uri.host;
michael@0 210
michael@0 211 // If the URI explicitly specified a port, only include it when
michael@0 212 // it's not the default. (We never want "http://foo.com:80")
michael@0 213 port = uri.port;
michael@0 214 if (port != -1) {
michael@0 215 let handler = Services.io.getProtocolHandler(scheme);
michael@0 216 if (port != handler.defaultPort)
michael@0 217 hostname += ":" + port;
michael@0 218 }
michael@0 219
michael@0 220 return hostname;
michael@0 221 },
michael@0 222
michael@0 223 // Copied from login manager
michael@0 224 getAuthTarget : function (aChannel, aAuthInfo) {
michael@0 225 let hostname, realm;
michael@0 226
michael@0 227 // If our proxy is demanding authentication, don't use the
michael@0 228 // channel's actual destination.
michael@0 229 if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
michael@0 230 if (!(aChannel instanceof Ci.nsIProxiedChannel))
michael@0 231 throw "proxy auth needs nsIProxiedChannel";
michael@0 232
michael@0 233 let info = aChannel.proxyInfo;
michael@0 234 if (!info)
michael@0 235 throw "proxy auth needs nsIProxyInfo";
michael@0 236
michael@0 237 // Proxies don't have a scheme, but we'll use "moz-proxy://"
michael@0 238 // so that it's more obvious what the login is for.
michael@0 239 let idnService = Cc["@mozilla.org/network/idn-service;1"].
michael@0 240 getService(Ci.nsIIDNService);
michael@0 241 hostname = "moz-proxy://" +
michael@0 242 idnService.convertUTF8toACE(info.host) +
michael@0 243 ":" + info.port;
michael@0 244 realm = aAuthInfo.realm;
michael@0 245 if (!realm)
michael@0 246 realm = hostname;
michael@0 247
michael@0 248 return [hostname, realm];
michael@0 249 }
michael@0 250
michael@0 251 hostname = this.getFormattedHostname(aChannel.URI);
michael@0 252
michael@0 253 // If a HTTP WWW-Authenticate header specified a realm, that value
michael@0 254 // will be available here. If it wasn't set or wasn't HTTP, we'll use
michael@0 255 // the formatted hostname instead.
michael@0 256 realm = aAuthInfo.realm;
michael@0 257 if (!realm)
michael@0 258 realm = hostname;
michael@0 259
michael@0 260 return [hostname, realm];
michael@0 261 },
michael@0 262
michael@0 263
michael@0 264 makeAuthMessage : function (channel, authInfo) {
michael@0 265 let isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
michael@0 266 let isPassOnly = (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
michael@0 267
michael@0 268 let username = authInfo.username;
michael@0 269 let [displayHost, realm] = this.getAuthTarget(channel, authInfo);
michael@0 270
michael@0 271 // Suppress "the site says: $realm" when we synthesized a missing realm.
michael@0 272 if (!authInfo.realm && !isProxy)
michael@0 273 realm = "";
michael@0 274
michael@0 275 // Trim obnoxiously long realms.
michael@0 276 if (realm.length > 150) {
michael@0 277 realm = realm.substring(0, 150);
michael@0 278 // Append "..." (or localized equivalent).
michael@0 279 realm += this.ellipsis;
michael@0 280 }
michael@0 281
michael@0 282 let text;
michael@0 283 if (isProxy)
michael@0 284 text = PromptUtils.getLocalizedString("EnterLoginForProxy", [realm, displayHost]);
michael@0 285 else if (isPassOnly)
michael@0 286 text = PromptUtils.getLocalizedString("EnterPasswordFor", [username, displayHost]);
michael@0 287 else if (!realm)
michael@0 288 text = PromptUtils.getLocalizedString("EnterUserPasswordFor", [displayHost]);
michael@0 289 else
michael@0 290 text = PromptUtils.getLocalizedString("EnterLoginForRealm", [realm, displayHost]);
michael@0 291
michael@0 292 return text;
michael@0 293 },
michael@0 294
michael@0 295 getTabModalPrompt : function (domWin) {
michael@0 296 var promptBox = null;
michael@0 297
michael@0 298 try {
michael@0 299 // Get the topmost window, in case we're in a frame.
michael@0 300 var promptWin = domWin.top;
michael@0 301
michael@0 302 // Get the chrome window for the content window we're using.
michael@0 303 // (Unwrap because we need a non-IDL property below.)
michael@0 304 var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 305 .getInterface(Ci.nsIWebNavigation)
michael@0 306 .QueryInterface(Ci.nsIDocShell)
michael@0 307 .chromeEventHandler.ownerDocument
michael@0 308 .defaultView.wrappedJSObject;
michael@0 309
michael@0 310 if (chromeWin.getTabModalPromptBox)
michael@0 311 promptBox = chromeWin.getTabModalPromptBox(promptWin);
michael@0 312 } catch (e) {
michael@0 313 // If any errors happen, just assume no tabmodal prompter.
michael@0 314 }
michael@0 315
michael@0 316 return promptBox;
michael@0 317 },
michael@0 318 };
michael@0 319
michael@0 320 PromptUtils = PromptUtilsTemp;
michael@0 321
michael@0 322 XPCOMUtils.defineLazyGetter(PromptUtils, "strBundle", function () {
michael@0 323 let bunService = Cc["@mozilla.org/intl/stringbundle;1"].
michael@0 324 getService(Ci.nsIStringBundleService);
michael@0 325 let bundle = bunService.createBundle("chrome://global/locale/commonDialogs.properties");
michael@0 326 if (!bundle)
michael@0 327 throw "String bundle for Prompter not present!";
michael@0 328 return bundle;
michael@0 329 });
michael@0 330
michael@0 331 XPCOMUtils.defineLazyGetter(PromptUtils, "ellipsis", function () {
michael@0 332 let ellipsis = "\u2026";
michael@0 333 try {
michael@0 334 ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
michael@0 335 } catch (e) { }
michael@0 336 return ellipsis;
michael@0 337 });
michael@0 338
michael@0 339
michael@0 340
michael@0 341 function openModalWindow(domWin, uri, args) {
michael@0 342 // There's an implied contract that says modal prompts should still work
michael@0 343 // when no "parent" window is passed for the dialog (eg, the "Master
michael@0 344 // Password" dialog does this). These prompts must be shown even if there
michael@0 345 // are *no* visible windows at all.
michael@0 346 // There's also a requirement for prompts to be blocked if a window is
michael@0 347 // passed and that window is hidden (eg, auth prompts are supressed if the
michael@0 348 // passed window is the hidden window).
michael@0 349 // See bug 875157 comment 30 for more...
michael@0 350 if (domWin) {
michael@0 351 // a domWin was passed, so we can apply the check for it being hidden.
michael@0 352 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 353 .getInterface(Ci.nsIDOMWindowUtils);
michael@0 354
michael@0 355 if (winUtils && !winUtils.isParentWindowMainWidgetVisible) {
michael@0 356 throw Components.Exception("Cannot call openModalWindow on a hidden window",
michael@0 357 Cr.NS_ERROR_NOT_AVAILABLE);
michael@0 358 }
michael@0 359 } else {
michael@0 360 // We try and find a window to use as the parent, but don't consider
michael@0 361 // if that is visible before showing the prompt.
michael@0 362 domWin = Services.ww.activeWindow;
michael@0 363 // domWin may still be null here if there are _no_ windows open.
michael@0 364 }
michael@0 365 // Note that we don't need to fire DOMWillOpenModalDialog and
michael@0 366 // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal
michael@0 367 // will do that. Similarly for enterModalState / leaveModalState.
michael@0 368
michael@0 369 Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args);
michael@0 370 }
michael@0 371
michael@0 372 function openTabPrompt(domWin, tabPrompt, args) {
michael@0 373 PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog");
michael@0 374
michael@0 375 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 376 .getInterface(Ci.nsIDOMWindowUtils);
michael@0 377 winUtils.enterModalState();
michael@0 378
michael@0 379 // We provide a callback so the prompt can close itself. We don't want to
michael@0 380 // wait for this event loop to return... Otherwise the presence of other
michael@0 381 // prompts on the call stack would in this dialog appearing unresponsive
michael@0 382 // until the other prompts had been closed.
michael@0 383 let callbackInvoked = false;
michael@0 384 let newPrompt;
michael@0 385 function onPromptClose(forceCleanup) {
michael@0 386 if (!newPrompt && !forceCleanup)
michael@0 387 return;
michael@0 388 callbackInvoked = true;
michael@0 389 if (newPrompt)
michael@0 390 tabPrompt.removePrompt(newPrompt);
michael@0 391
michael@0 392 domWin.removeEventListener("pagehide", pagehide);
michael@0 393
michael@0 394 winUtils.leaveModalState();
michael@0 395
michael@0 396 PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
michael@0 397 }
michael@0 398
michael@0 399 domWin.addEventListener("pagehide", pagehide);
michael@0 400 function pagehide() {
michael@0 401 domWin.removeEventListener("pagehide", pagehide);
michael@0 402
michael@0 403 if (newPrompt) {
michael@0 404 newPrompt.abortPrompt();
michael@0 405 }
michael@0 406 }
michael@0 407
michael@0 408 try {
michael@0 409 let topPrincipal = domWin.top.document.nodePrincipal;
michael@0 410 let promptPrincipal = domWin.document.nodePrincipal;
michael@0 411 args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
michael@0 412 args.promptActive = true;
michael@0 413
michael@0 414 newPrompt = tabPrompt.appendPrompt(args, onPromptClose);
michael@0 415
michael@0 416 // TODO since we don't actually open a window, need to check if
michael@0 417 // there's other stuff in nsWindowWatcher::OpenWindowInternal
michael@0 418 // that we might need to do here as well.
michael@0 419
michael@0 420 let thread = Services.tm.currentThread;
michael@0 421 while (args.promptActive)
michael@0 422 thread.processNextEvent(true);
michael@0 423 delete args.promptActive;
michael@0 424
michael@0 425 if (args.promptAborted)
michael@0 426 throw Components.Exception("prompt aborted by user", Cr.NS_ERROR_NOT_AVAILABLE);
michael@0 427 } finally {
michael@0 428 // If the prompt unexpectedly failed to invoke the callback, do so here.
michael@0 429 if (!callbackInvoked)
michael@0 430 onPromptClose(true);
michael@0 431 }
michael@0 432 }
michael@0 433
michael@0 434 function openRemotePrompt(domWin, args, tabPrompt) {
michael@0 435 let messageManager = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 436 .getInterface(Ci.nsIWebNavigation)
michael@0 437 .QueryInterface(Ci.nsIDocShell)
michael@0 438 .QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 439 .getInterface(Ci.nsITabChild)
michael@0 440 .messageManager;
michael@0 441
michael@0 442 PromptUtils.fireDialogEvent(domWin, "DOMWillOpenModalDialog");
michael@0 443
michael@0 444 let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 445 .getInterface(Ci.nsIDOMWindowUtils);
michael@0 446 winUtils.enterModalState();
michael@0 447 let closed = false;
michael@0 448
michael@0 449 // It should be hard or impossible to cause a window to create multiple
michael@0 450 // prompts, but just in case, give our prompt an ID.
michael@0 451 let id = "id" + Cc["@mozilla.org/uuid-generator;1"]
michael@0 452 .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
michael@0 453
michael@0 454 messageManager.addMessageListener("Prompt:Close", function listener(message) {
michael@0 455 if (message.data._remoteId !== id) {
michael@0 456 return;
michael@0 457 }
michael@0 458
michael@0 459 messageManager.removeMessageListener("Prompt:Close", listener);
michael@0 460 domWin.removeEventListener("pagehide", pagehide);
michael@0 461
michael@0 462 winUtils.leaveModalState();
michael@0 463 PromptUtils.fireDialogEvent(domWin, "DOMModalDialogClosed");
michael@0 464
michael@0 465 // Copy the response from the closed prompt into our args, it will be
michael@0 466 // read by our caller.
michael@0 467 if (message.data) {
michael@0 468 for (let key in message.data) {
michael@0 469 args[key] = message.data[key];
michael@0 470 }
michael@0 471 }
michael@0 472
michael@0 473 // Exit our nested event loop when we unwind.
michael@0 474 closed = true;
michael@0 475 });
michael@0 476
michael@0 477 domWin.addEventListener("pagehide", pagehide);
michael@0 478 function pagehide() {
michael@0 479 domWin.removeEventListener("pagehide", pagehide);
michael@0 480 messageManager.sendAsyncMessage("Prompt:ForceClose", { _remoteId: id });
michael@0 481 }
michael@0 482
michael@0 483 let topPrincipal = domWin.top.document.nodePrincipal;
michael@0 484 let promptPrincipal = domWin.document.nodePrincipal;
michael@0 485 args.showAlertOrigin = topPrincipal.equals(promptPrincipal);
michael@0 486
michael@0 487 args._remoteId = id;
michael@0 488
michael@0 489 messageManager.sendAsyncMessage("Prompt:Open", args, {});
michael@0 490
michael@0 491 let thread = Services.tm.currentThread;
michael@0 492 while (!closed) {
michael@0 493 thread.processNextEvent(true);
michael@0 494 }
michael@0 495 }
michael@0 496
michael@0 497 function ModalPrompter(domWin) {
michael@0 498 this.domWin = domWin;
michael@0 499 }
michael@0 500 ModalPrompter.prototype = {
michael@0 501 domWin : null,
michael@0 502 /*
michael@0 503 * Default to not using a tab-modal prompt, unless the caller opts in by
michael@0 504 * QIing to nsIWritablePropertyBag and setting the value of this property
michael@0 505 * to true.
michael@0 506 */
michael@0 507 allowTabModal : false,
michael@0 508
michael@0 509 QueryInterface : XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt,
michael@0 510 Ci.nsIAuthPrompt2,
michael@0 511 Ci.nsIWritablePropertyBag2]),
michael@0 512
michael@0 513
michael@0 514 /* ---------- internal methods ---------- */
michael@0 515
michael@0 516
michael@0 517 openPrompt : function (args) {
michael@0 518 // Check pref, if false/missing do not ever allow tab-modal prompts.
michael@0 519 const prefName = "prompts.tab_modal.enabled";
michael@0 520 let prefValue = false;
michael@0 521 if (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL)
michael@0 522 prefValue = Services.prefs.getBoolPref(prefName);
michael@0 523
michael@0 524 let allowTabModal = this.allowTabModal && prefValue;
michael@0 525
michael@0 526 if (allowTabModal && this.domWin) {
michael@0 527 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
michael@0 528 openRemotePrompt(this.domWin, args, true);
michael@0 529 return;
michael@0 530 }
michael@0 531
michael@0 532 let tabPrompt = PromptUtils.getTabModalPrompt(this.domWin);
michael@0 533 if (tabPrompt) {
michael@0 534 openTabPrompt(this.domWin, tabPrompt, args);
michael@0 535 return;
michael@0 536 }
michael@0 537 }
michael@0 538
michael@0 539 // If we can't do a tab modal prompt, fallback to using a window-modal dialog.
michael@0 540 const COMMON_DIALOG = "chrome://global/content/commonDialog.xul";
michael@0 541 const SELECT_DIALOG = "chrome://global/content/selectDialog.xul";
michael@0 542
michael@0 543 let uri = (args.promptType == "select") ? SELECT_DIALOG : COMMON_DIALOG;
michael@0 544
michael@0 545 if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
michael@0 546 args.uri = uri;
michael@0 547 openRemotePrompt(this.domWin, args);
michael@0 548 return;
michael@0 549 }
michael@0 550
michael@0 551 let propBag = PromptUtils.objectToPropBag(args);
michael@0 552 openModalWindow(this.domWin, uri, propBag);
michael@0 553 PromptUtils.propBagToObject(propBag, args);
michael@0 554 },
michael@0 555
michael@0 556
michael@0 557
michael@0 558 /*
michael@0 559 * ---------- interface disambiguation ----------
michael@0 560 *
michael@0 561 * nsIPrompt and nsIAuthPrompt share 3 method names with slightly
michael@0 562 * different arguments. All but prompt() have the same number of
michael@0 563 * arguments, so look at the arg types to figure out how we're being
michael@0 564 * called. :-(
michael@0 565 */
michael@0 566 prompt : function() {
michael@0 567 // also, the nsIPrompt flavor has 5 args instead of 6.
michael@0 568 if (typeof arguments[2] == "object")
michael@0 569 return this.nsIPrompt_prompt.apply(this, arguments);
michael@0 570 else
michael@0 571 return this.nsIAuthPrompt_prompt.apply(this, arguments);
michael@0 572 },
michael@0 573
michael@0 574 promptUsernameAndPassword : function() {
michael@0 575 // Both have 6 args, so use types.
michael@0 576 if (typeof arguments[2] == "object")
michael@0 577 return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
michael@0 578 else
michael@0 579 return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
michael@0 580 },
michael@0 581
michael@0 582 promptPassword : function() {
michael@0 583 // Both have 5 args, so use types.
michael@0 584 if (typeof arguments[2] == "object")
michael@0 585 return this.nsIPrompt_promptPassword.apply(this, arguments);
michael@0 586 else
michael@0 587 return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
michael@0 588 },
michael@0 589
michael@0 590
michael@0 591 /* ---------- nsIPrompt ---------- */
michael@0 592
michael@0 593
michael@0 594 alert : function (title, text) {
michael@0 595 if (!title)
michael@0 596 title = PromptUtils.getLocalizedString("Alert");
michael@0 597
michael@0 598 let args = {
michael@0 599 promptType: "alert",
michael@0 600 title: title,
michael@0 601 text: text,
michael@0 602 };
michael@0 603
michael@0 604 this.openPrompt(args);
michael@0 605 },
michael@0 606
michael@0 607 alertCheck : function (title, text, checkLabel, checkValue) {
michael@0 608 if (!title)
michael@0 609 title = PromptUtils.getLocalizedString("Alert");
michael@0 610
michael@0 611 let args = {
michael@0 612 promptType: "alertCheck",
michael@0 613 title: title,
michael@0 614 text: text,
michael@0 615 checkLabel: checkLabel,
michael@0 616 checked: checkValue.value,
michael@0 617 };
michael@0 618
michael@0 619 this.openPrompt(args);
michael@0 620
michael@0 621 // Checkbox state always returned, even if cancel clicked.
michael@0 622 checkValue.value = args.checked;
michael@0 623 },
michael@0 624
michael@0 625 confirm : function (title, text) {
michael@0 626 if (!title)
michael@0 627 title = PromptUtils.getLocalizedString("Confirm");
michael@0 628
michael@0 629 let args = {
michael@0 630 promptType: "confirm",
michael@0 631 title: title,
michael@0 632 text: text,
michael@0 633 ok: false,
michael@0 634 };
michael@0 635
michael@0 636 this.openPrompt(args);
michael@0 637
michael@0 638 // Did user click Ok or Cancel?
michael@0 639 return args.ok;
michael@0 640 },
michael@0 641
michael@0 642 confirmCheck : function (title, text, checkLabel, checkValue) {
michael@0 643 if (!title)
michael@0 644 title = PromptUtils.getLocalizedString("ConfirmCheck");
michael@0 645
michael@0 646 let args = {
michael@0 647 promptType: "confirmCheck",
michael@0 648 title: title,
michael@0 649 text: text,
michael@0 650 checkLabel: checkLabel,
michael@0 651 checked: checkValue.value,
michael@0 652 ok: false,
michael@0 653 };
michael@0 654
michael@0 655 this.openPrompt(args);
michael@0 656
michael@0 657 // Checkbox state always returned, even if cancel clicked.
michael@0 658 checkValue.value = args.checked;
michael@0 659
michael@0 660 // Did user click Ok or Cancel?
michael@0 661 return args.ok;
michael@0 662 },
michael@0 663
michael@0 664 confirmEx : function (title, text, flags, button0, button1, button2,
michael@0 665 checkLabel, checkValue) {
michael@0 666
michael@0 667 if (!title)
michael@0 668 title = PromptUtils.getLocalizedString("Confirm");
michael@0 669
michael@0 670 let args = {
michael@0 671 promptType: "confirmEx",
michael@0 672 title: title,
michael@0 673 text: text,
michael@0 674 checkLabel: checkLabel,
michael@0 675 checked: checkValue.value,
michael@0 676 ok: false,
michael@0 677 buttonNumClicked: 1,
michael@0 678 };
michael@0 679
michael@0 680 let [label0, label1, label2, defaultButtonNum, isDelayEnabled] =
michael@0 681 PromptUtils.confirmExHelper(flags, button0, button1, button2);
michael@0 682
michael@0 683 args.defaultButtonNum = defaultButtonNum;
michael@0 684 args.enableDelay = isDelayEnabled;
michael@0 685
michael@0 686 if (label0) {
michael@0 687 args.button0Label = label0;
michael@0 688 if (label1) {
michael@0 689 args.button1Label = label1;
michael@0 690 if (label2) {
michael@0 691 args.button2Label = label2;
michael@0 692 }
michael@0 693 }
michael@0 694 }
michael@0 695
michael@0 696 this.openPrompt(args);
michael@0 697
michael@0 698 // Checkbox state always returned, even if cancel clicked.
michael@0 699 checkValue.value = args.checked;
michael@0 700
michael@0 701 // Get the number of the button the user clicked.
michael@0 702 return args.buttonNumClicked;
michael@0 703 },
michael@0 704
michael@0 705 nsIPrompt_prompt : function (title, text, value, checkLabel, checkValue) {
michael@0 706 if (!title)
michael@0 707 title = PromptUtils.getLocalizedString("Prompt");
michael@0 708
michael@0 709 let args = {
michael@0 710 promptType: "prompt",
michael@0 711 title: title,
michael@0 712 text: text,
michael@0 713 value: value.value,
michael@0 714 checkLabel: checkLabel,
michael@0 715 checked: checkValue.value,
michael@0 716 ok: false,
michael@0 717 };
michael@0 718
michael@0 719 this.openPrompt(args);
michael@0 720
michael@0 721 // Did user click Ok or Cancel?
michael@0 722 let ok = args.ok;
michael@0 723 if (ok) {
michael@0 724 checkValue.value = args.checked;
michael@0 725 value.value = args.value;
michael@0 726 }
michael@0 727
michael@0 728 return ok;
michael@0 729 },
michael@0 730
michael@0 731 nsIPrompt_promptUsernameAndPassword : function (title, text, user, pass, checkLabel, checkValue) {
michael@0 732 if (!title)
michael@0 733 title = PromptUtils.getLocalizedString("PromptUsernameAndPassword2");
michael@0 734
michael@0 735 let args = {
michael@0 736 promptType: "promptUserAndPass",
michael@0 737 title: title,
michael@0 738 text: text,
michael@0 739 user: user.value,
michael@0 740 pass: pass.value,
michael@0 741 checkLabel: checkLabel,
michael@0 742 checked: checkValue.value,
michael@0 743 ok: false,
michael@0 744 };
michael@0 745
michael@0 746 this.openPrompt(args);
michael@0 747
michael@0 748 // Did user click Ok or Cancel?
michael@0 749 let ok = args.ok;
michael@0 750 if (ok) {
michael@0 751 checkValue.value = args.checked;
michael@0 752 user.value = args.user;
michael@0 753 pass.value = args.pass;
michael@0 754 }
michael@0 755
michael@0 756 return ok;
michael@0 757 },
michael@0 758
michael@0 759 nsIPrompt_promptPassword : function (title, text, pass, checkLabel, checkValue) {
michael@0 760 if (!title)
michael@0 761 title = PromptUtils.getLocalizedString("PromptPassword2");
michael@0 762
michael@0 763 let args = {
michael@0 764 promptType: "promptPassword",
michael@0 765 title: title,
michael@0 766 text: text,
michael@0 767 pass: pass.value,
michael@0 768 checkLabel: checkLabel,
michael@0 769 checked: checkValue.value,
michael@0 770 ok: false,
michael@0 771 }
michael@0 772
michael@0 773 this.openPrompt(args);
michael@0 774
michael@0 775 // Did user click Ok or Cancel?
michael@0 776 let ok = args.ok;
michael@0 777 if (ok) {
michael@0 778 checkValue.value = args.checked;
michael@0 779 pass.value = args.pass;
michael@0 780 }
michael@0 781
michael@0 782 return ok;
michael@0 783 },
michael@0 784
michael@0 785 select : function (title, text, count, list, selected) {
michael@0 786 if (!title)
michael@0 787 title = PromptUtils.getLocalizedString("Select");
michael@0 788
michael@0 789 let args = {
michael@0 790 promptType: "select",
michael@0 791 title: title,
michael@0 792 text: text,
michael@0 793 list: list,
michael@0 794 selected: -1,
michael@0 795 ok: false,
michael@0 796 };
michael@0 797
michael@0 798 this.openPrompt(args);
michael@0 799
michael@0 800 // Did user click Ok or Cancel?
michael@0 801 let ok = args.ok;
michael@0 802 if (ok)
michael@0 803 selected.value = args.selected;
michael@0 804
michael@0 805 return ok;
michael@0 806 },
michael@0 807
michael@0 808
michael@0 809 /* ---------- nsIAuthPrompt ---------- */
michael@0 810
michael@0 811
michael@0 812 nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
michael@0 813 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
michael@0 814 if (defaultText)
michael@0 815 result.value = defaultText;
michael@0 816 return this.nsIPrompt_prompt(title, text, result, null, {});
michael@0 817 },
michael@0 818
michael@0 819 nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) {
michael@0 820 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
michael@0 821 return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {});
michael@0 822 },
michael@0 823
michael@0 824 nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) {
michael@0 825 // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
michael@0 826 return this.nsIPrompt_promptPassword(title, text, pass, null, {});
michael@0 827 },
michael@0 828
michael@0 829
michael@0 830 /* ---------- nsIAuthPrompt2 ---------- */
michael@0 831
michael@0 832
michael@0 833 promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
michael@0 834 let message = PromptUtils.makeAuthMessage(channel, authInfo);
michael@0 835
michael@0 836 let [username, password] = PromptUtils.getAuthInfo(authInfo);
michael@0 837
michael@0 838 let userParam = { value: username };
michael@0 839 let passParam = { value: password };
michael@0 840
michael@0 841 let ok;
michael@0 842 if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
michael@0 843 ok = this.nsIPrompt_promptPassword(null, message, passParam, checkLabel, checkValue);
michael@0 844 else
michael@0 845 ok = this.nsIPrompt_promptUsernameAndPassword(null, message, userParam, passParam, checkLabel, checkValue);
michael@0 846
michael@0 847 if (ok)
michael@0 848 PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
michael@0 849 return ok;
michael@0 850 },
michael@0 851
michael@0 852 asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) {
michael@0 853 // Nothing calls this directly; netwerk ends up going through
michael@0 854 // nsIPromptService::GetPrompt, which delegates to login manager.
michael@0 855 // Login manger handles the async bits itself, and only calls out
michael@0 856 // promptAuth, never asyncPromptAuth.
michael@0 857 //
michael@0 858 // Bug 565582 will change this.
michael@0 859 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
michael@0 860 },
michael@0 861
michael@0 862 /* ---------- nsIWritablePropertyBag2 ---------- */
michael@0 863
michael@0 864 // Only a partial implementation, for one specific use case...
michael@0 865
michael@0 866 setPropertyAsBool : function(name, value) {
michael@0 867 if (name == "allowTabModal")
michael@0 868 this.allowTabModal = value;
michael@0 869 else
michael@0 870 throw Cr.NS_ERROR_ILLEGAL_VALUE;
michael@0 871 },
michael@0 872 };
michael@0 873
michael@0 874
michael@0 875 function AuthPromptAdapterFactory() {
michael@0 876 }
michael@0 877 AuthPromptAdapterFactory.prototype = {
michael@0 878 classID : Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"),
michael@0 879 QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]),
michael@0 880
michael@0 881 /* ---------- nsIAuthPromptAdapterFactory ---------- */
michael@0 882
michael@0 883 createAdapter : function (oldPrompter) {
michael@0 884 return new AuthPromptAdapter(oldPrompter);
michael@0 885 }
michael@0 886 };
michael@0 887
michael@0 888
michael@0 889 // Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell.
michael@0 890 function AuthPromptAdapter(oldPrompter) {
michael@0 891 this.oldPrompter = oldPrompter;
michael@0 892 }
michael@0 893 AuthPromptAdapter.prototype = {
michael@0 894 QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
michael@0 895 oldPrompter : null,
michael@0 896
michael@0 897 /* ---------- nsIAuthPrompt2 ---------- */
michael@0 898
michael@0 899 promptAuth : function (channel, level, authInfo, checkLabel, checkValue) {
michael@0 900 let message = PromptUtils.makeAuthMessage(channel, authInfo);
michael@0 901
michael@0 902 let [username, password] = PromptUtils.getAuthInfo(authInfo);
michael@0 903 let userParam = { value: username };
michael@0 904 let passParam = { value: password };
michael@0 905
michael@0 906 let [host, realm] = PromptUtils.getAuthTarget(channel, authInfo);
michael@0 907 let authTarget = host + " (" + realm + ")";
michael@0 908
michael@0 909 let ok;
michael@0 910 if (authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
michael@0 911 ok = this.oldPrompter.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, passParam);
michael@0 912 else
michael@0 913 ok = this.oldPrompter.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, userParam, passParam);
michael@0 914
michael@0 915 if (ok)
michael@0 916 PromptUtils.setAuthInfo(authInfo, userParam.value, passParam.value);
michael@0 917 return ok;
michael@0 918 },
michael@0 919
michael@0 920 asyncPromptAuth : function (channel, callback, context, level, authInfo, checkLabel, checkValue) {
michael@0 921 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
michael@0 922 }
michael@0 923 };
michael@0 924
michael@0 925
michael@0 926 // Wrapper using the old embedding contractID, since it's already common in
michael@0 927 // the addon ecosystem.
michael@0 928 function EmbedPrompter() {
michael@0 929 }
michael@0 930 EmbedPrompter.prototype = new Prompter();
michael@0 931 EmbedPrompter.prototype.classID = Components.ID("{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}");
michael@0 932
michael@0 933 var component = [Prompter, EmbedPrompter, AuthPromptAdapterFactory];
michael@0 934 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);

mercurial