toolkit/components/prompts/src/nsPrompter.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial