mobile/android/components/PromptService.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/. */
     4 const Ci = Components.interfaces;
     5 const Cc = Components.classes;
     6 const Cr = Components.results;
     7 const Cu = Components.utils;
     9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    10 Cu.import("resource://gre/modules/Services.jsm");
    11 Cu.import("resource://gre/modules/Prompt.jsm");
    13 var gPromptService = null;
    15 function PromptService() {
    16   gPromptService = this;
    17 }
    19 PromptService.prototype = {
    20   classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"),
    22   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
    24   /* ----------  nsIPromptFactory  ---------- */
    25   // XXX Copied from nsPrompter.js.
    26   getPrompt: function getPrompt(domWin, iid) {
    27     let doc = this.getDocument();
    28     if (!doc) {
    29       let fallback = this._getFallbackService();
    30       return fallback.QueryInterface(Ci.nsIPromptFactory).getPrompt(domWin, iid);
    31     }
    33     let p = new InternalPrompt(domWin, doc);
    34     p.QueryInterface(iid);
    35     return p;
    36   },
    38   /* ----------  private memebers  ---------- */
    40   _getFallbackService: function _getFallbackService() {
    41     return Components.classesByID["{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"]
    42                      .getService(Ci.nsIPromptService);
    43   },
    45   getDocument: function getDocument() {
    46     let win = Services.wm.getMostRecentWindow("navigator:browser");
    47     return win ? win.document : null;
    48   },
    50   // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class
    51   // if we can show in-document popups, or to the fallback service otherwise.
    52   callProxy: function(aMethod, aArguments) {
    53     let prompt;
    54     let doc = this.getDocument();
    55     if (!doc) {
    56       let fallback = this._getFallbackService();
    57       return fallback[aMethod].apply(fallback, aArguments);
    58     }
    59     let domWin = aArguments[0];
    60     prompt = new InternalPrompt(domWin, doc);
    61     return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1));
    62   },
    64   /* ----------  nsIPromptService  ---------- */
    66   alert: function() {
    67     return this.callProxy("alert", arguments);
    68   },
    69   alertCheck: function() {
    70     return this.callProxy("alertCheck", arguments);
    71   },
    72   confirm: function() {
    73     return this.callProxy("confirm", arguments);
    74   },
    75   confirmCheck: function() {
    76     return this.callProxy("confirmCheck", arguments);
    77   },
    78   confirmEx: function() {
    79     return this.callProxy("confirmEx", arguments);
    80   },
    81   prompt: function() {
    82     return this.callProxy("prompt", arguments);
    83   },
    84   promptUsernameAndPassword: function() {
    85     return this.callProxy("promptUsernameAndPassword", arguments);
    86   },
    87   promptPassword: function() {
    88     return this.callProxy("promptPassword", arguments);
    89   },
    90   select: function() {
    91     return this.callProxy("select", arguments);
    92   },
    94   /* ----------  nsIPromptService2  ---------- */
    95   promptAuth: function() {
    96     return this.callProxy("promptAuth", arguments);
    97   },
    98   asyncPromptAuth: function() {
    99     return this.callProxy("asyncPromptAuth", arguments);
   100   }
   101 };
   103 function InternalPrompt(aDomWin, aDocument) {
   104   this._domWin = aDomWin;
   105   this._doc = aDocument;
   106 }
   108 InternalPrompt.prototype = {
   109   _domWin: null,
   110   _doc: null,
   112   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]),
   114   /* ---------- internal methods ---------- */
   115   _getPrompt: function _getPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState) {
   116     let p = new Prompt({
   117       window: this._domWin,
   118       title: aTitle,
   119       message: aText,
   120       buttons: aButtons || [
   121         PromptUtils.getLocaleString("OK"),
   122         PromptUtils.getLocaleString("Cancel")
   123       ]
   124     });
   125     return p;
   126   },
   128   addCheckbox: function addCheckbox(aPrompt, aCheckMsg, aCheckState) {
   129     // Don't bother to check for aCheckSate. For nsIPomptService interfaces, aCheckState is an
   130     // out param and is required to be defined. If we've gotten here without it, something
   131     // has probably gone wrong and we should fail
   132     if (aCheckMsg) {
   133       aPrompt.addCheckbox({
   134         label: PromptUtils.cleanUpLabel(aCheckMsg),
   135         checked: aCheckState.value
   136       });
   137     }
   139     return aPrompt;
   140   },
   142   addTextbox: function(prompt, value, autofocus, hint) {
   143     prompt.addTextbox({
   144       value: (value !== null) ? value : "",
   145       autofocus: autofocus,
   146       hint: hint
   147     });
   148   },
   150   addPassword: function(prompt, value, autofocus, hint) {
   151     prompt.addPassword({
   152       value: (value !== null) ? value : "",
   153       autofocus: autofocus,
   154       hint: hint
   155     });
   156   },
   158   /* Shows a native prompt, and then spins the event loop for this thread while we wait
   159    * for a response
   160    */
   161   showPrompt: function showPrompt(aPrompt) {
   162     if (this._domWin) {
   163       PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog");
   164       let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   165       winUtils.enterModalState();
   166     }
   168     let retval = null;
   169     aPrompt.show(function(data) {
   170       retval = data;
   171     });
   173     // Spin this thread while we wait for a result
   174     let thread = Services.tm.currentThread;
   175     while (retval == null)
   176       thread.processNextEvent(true);
   178     if (this._domWin) {
   179       let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   180       winUtils.leaveModalState();
   181       PromptUtils.fireDialogEvent(this._domWin, "DOMModalDialogClosed");
   182     }
   184     return retval;
   185   },
   187   /*
   188    * ---------- interface disambiguation ----------
   189    *
   190    * XXX Copied from nsPrompter.js.
   191    *
   192    * nsIPrompt and nsIAuthPrompt share 3 method names with slightly
   193    * different arguments. All but prompt() have the same number of
   194    * arguments, so look at the arg types to figure out how we're being
   195    * called. :-(
   196    */
   197   prompt: function prompt() {
   198     if (gPromptService.inContentProcess)
   199       return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments)));
   201     // also, the nsIPrompt flavor has 5 args instead of 6.
   202     if (typeof arguments[2] == "object")
   203       return this.nsIPrompt_prompt.apply(this, arguments);
   204     else
   205       return this.nsIAuthPrompt_prompt.apply(this, arguments);
   206   },
   208   promptUsernameAndPassword: function promptUsernameAndPassword() {
   209     // Both have 6 args, so use types.
   210     if (typeof arguments[2] == "object")
   211       return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments);
   212     else
   213       return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments);
   214   },
   216   promptPassword: function promptPassword() {
   217     // Both have 5 args, so use types.
   218     if (typeof arguments[2] == "object")
   219       return this.nsIPrompt_promptPassword.apply(this, arguments);
   220     else
   221       return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
   222   },
   224   /* ----------  nsIPrompt  ---------- */
   226   alert: function alert(aTitle, aText) {
   227     let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
   228     p.setHint("alert");
   229     this.showPrompt(p);
   230   },
   232   alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) {
   233     let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
   234     this.addCheckbox(p, aCheckMsg, aCheckState);
   235     let data = this.showPrompt(p);
   236     if (aCheckState && data.button > -1)
   237       aCheckState.value = data.checkbox0;
   238   },
   240   confirm: function confirm(aTitle, aText) {
   241     let p = this._getPrompt(aTitle, aText);
   242     p.setHint("confirm");
   243     let data = this.showPrompt(p);
   244     return (data.button == 0);
   245   },
   247   confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) {
   248     let p = this._getPrompt(aTitle, aText, null);
   249     this.addCheckbox(p, aCheckMsg, aCheckState);
   250     let data = this.showPrompt(p);
   251     let ok = data.button == 0;
   252     if (aCheckState && data.button > -1)
   253       aCheckState.value = data.checkbox0;
   254     return ok;
   255   },
   257   confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0,
   258                       aButton1, aButton2, aCheckMsg, aCheckState) {
   259     let buttons = [];
   260     let titles = [aButton0, aButton1, aButton2];
   261     for (let i = 0; i < 3; i++) {
   262       let bTitle = null;
   263       switch (aButtonFlags & 0xff) {
   264         case Ci.nsIPromptService.BUTTON_TITLE_OK :
   265           bTitle = PromptUtils.getLocaleString("OK");
   266           break;
   267         case Ci.nsIPromptService.BUTTON_TITLE_CANCEL :
   268           bTitle = PromptUtils.getLocaleString("Cancel");
   269           break;
   270         case Ci.nsIPromptService.BUTTON_TITLE_YES :
   271           bTitle = PromptUtils.getLocaleString("Yes");
   272           break;
   273         case Ci.nsIPromptService.BUTTON_TITLE_NO :
   274           bTitle = PromptUtils.getLocaleString("No");
   275           break;
   276         case Ci.nsIPromptService.BUTTON_TITLE_SAVE :
   277           bTitle = PromptUtils.getLocaleString("Save");
   278           break;
   279         case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE :
   280           bTitle = PromptUtils.getLocaleString("DontSave");
   281           break;
   282         case Ci.nsIPromptService.BUTTON_TITLE_REVERT :
   283           bTitle = PromptUtils.getLocaleString("Revert");
   284           break;
   285         case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING :
   286           bTitle = PromptUtils.cleanUpLabel(titles[i]);
   287         break;
   288       }
   290       if (bTitle)
   291         buttons.push(bTitle);
   293       aButtonFlags >>= 8;
   294     }
   296     let p = this._getPrompt(aTitle, aText, buttons);
   297     this.addCheckbox(p, aCheckMsg, aCheckState);
   298     let data = this.showPrompt(p);
   299     if (aCheckState && data.button > -1)
   300       aCheckState.value = data.checkbox0;
   301     return data.button;
   302   },
   304   nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) {
   305     let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
   306     p.setHint("prompt");
   307     this.addTextbox(p, aValue.value, true);
   308     this.addCheckbox(p, aCheckMsg, aCheckState);
   309     let data = this.showPrompt(p);
   311     let ok = data.button == 0;
   312     if (aCheckState && data.button > -1)
   313       aCheckState.value = data.checkbox0;
   314     if (ok)
   315       aValue.value = data.textbox0;
   316     return ok;
   317   },
   319   nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
   320       aTitle, aText, aPassword, aCheckMsg, aCheckState) {
   321     let p = this._getPrompt(aTitle, aText, null);
   322     this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr"));
   323     this.addCheckbox(p, aCheckMsg, aCheckState);
   324     let data = this.showPrompt(p);
   326     let ok = data.button == 0;
   327     if (aCheckState && data.button > -1)
   328       aCheckState.value = data.checkbox0;
   329     if (ok)
   330       aPassword.value = data.password0;
   331     return ok;
   332   },
   334   nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword(
   335       aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) {
   336     let p = this._getPrompt(aTitle, aText, null);
   337     this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr"));
   338     this.addPassword(p, aPassword.value, false, PromptUtils.getLocaleString("password", "passwdmgr"));
   339     this.addCheckbox(p, aCheckMsg, aCheckState);
   340     let data = this.showPrompt(p);
   342     let ok = data.button == 0;
   343     if (aCheckState && data.button > -1)
   344       aCheckState.value = data.checkbox0;
   346     if (ok) {
   347       aUsername.value = data.textbox0;
   348       aPassword.value = data.password0;
   349     }
   350     return ok;
   351   },
   353   select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) {
   354     let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
   355     p.addMenulist({ values: aSelectList });
   356     let data = this.showPrompt(p);
   358     let ok = data.button == 0;
   359     if (ok)
   360       aOutSelection.value = data.menulist0;
   362     return ok;
   363   },
   365   /* ----------  nsIAuthPrompt  ---------- */
   367   nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
   368     // TODO: Port functions from nsLoginManagerPrompter.js to here
   369     if (defaultText)
   370       result.value = defaultText;
   371     return this.nsIPrompt_prompt(title, text, result, null, {});
   372   },
   374   nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) {
   375     return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass);
   376   },
   378   nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) {
   379     return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass);
   380   },
   382   nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) {
   383     let checkMsg = null;
   384     let check = { value: false };
   385     let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm);
   387     let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword);
   388     if (canSave) {
   389       // Look for existing logins.
   390       let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm);
   391       [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass);
   392     }
   394     let ok = false;
   395     if (aUser)
   396       ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check);
   397     else
   398       ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check);
   400     if (ok && canSave && check.value)
   401       PromptUtils.savePassword(hostname, realm, aUser, aPass);
   403     return ok;
   404   },
   406   /* ----------  nsIAuthPrompt2  ---------- */
   408   promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) {
   409     let checkMsg = null;
   410     let check = { value: false };
   411     let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
   412     let [username, password] = PromptUtils.getAuthInfo(aAuthInfo);
   413     let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
   414     let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
   416     let canSave = PromptUtils.canSaveLogin(hostname, null);
   417     if (canSave)
   418       [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password);
   420     if (username.value && password.value) {
   421       PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
   422     }
   424     let canAutologin = false;
   425     if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY &&
   426         !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) &&
   427         Services.prefs.getBoolPref("signon.autologin.proxy"))
   428       canAutologin = true;
   430     let ok = canAutologin;
   431     if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
   432       ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check);
   433     else if (!ok)
   434       ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check);
   436     PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
   438     if (ok && canSave && check.value)
   439       PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm);
   441     return ok;
   442   },
   444   _asyncPrompts: {},
   445   _asyncPromptInProgress: false,
   447   _doAsyncPrompt : function() {
   448     if (this._asyncPromptInProgress)
   449       return;
   451     // Find the first prompt key we have in the queue
   452     let hashKey = null;
   453     for (hashKey in this._asyncPrompts)
   454       break;
   456     if (!hashKey)
   457       return;
   459     // If login manger has logins for this host, defer prompting if we're
   460     // already waiting on a master password entry.
   461     let prompt = this._asyncPrompts[hashKey];
   462     let prompter = prompt.prompter;
   463     let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo);
   464     let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
   465     if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy)
   466       return;
   468     this._asyncPromptInProgress = true;
   469     prompt.inProgress = true;
   471     let self = this;
   473     let runnable = {
   474       run: function() {
   475         let ok = false;
   476         try {
   477           ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo);
   478         } catch (e) {
   479           Cu.reportError("_doAsyncPrompt:run: " + e + "\n");
   480         }
   482         delete self._asyncPrompts[hashKey];
   483         prompt.inProgress = false;
   484         self._asyncPromptInProgress = false;
   486         for (let consumer of prompt.consumers) {
   487           if (!consumer.callback)
   488             // Not having a callback means that consumer didn't provide it
   489             // or canceled the notification
   490             continue;
   492           try {
   493             if (ok)
   494               consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo);
   495             else
   496               consumer.callback.onAuthCancelled(consumer.context, true);
   497           } catch (e) { /* Throw away exceptions caused by callback */ }
   498         }
   499         self._doAsyncPrompt();
   500       }
   501     }
   503     Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL);
   504   },
   506   asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) {
   507     let cancelable = null;
   508     try {
   509       // If the user submits a login but it fails, we need to remove the
   510       // notification bar that was displayed. Conveniently, the user will
   511       // be prompted for authentication again, which brings us here.
   512       //this._removeLoginNotifications();
   514       cancelable = {
   515         QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
   516         callback: aCallback,
   517         context: aContext,
   518         cancel: function() {
   519           this.callback.onAuthCancelled(this.context, false);
   520           this.callback = null;
   521           this.context = null;
   522         }
   523       };
   524       let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
   525       let hashKey = aLevel + "|" + hostname + "|" + httpRealm;
   526       let asyncPrompt = this._asyncPrompts[hashKey];
   527       if (asyncPrompt) {
   528         asyncPrompt.consumers.push(cancelable);
   529         return cancelable;
   530       }
   532       asyncPrompt = {
   533         consumers: [cancelable],
   534         channel: aChannel,
   535         authInfo: aAuthInfo,
   536         level: aLevel,
   537         inProgress : false,
   538         prompter: this
   539       }
   541       this._asyncPrompts[hashKey] = asyncPrompt;
   542       this._doAsyncPrompt();
   543     } catch (e) {
   544       Cu.reportError("PromptService: " + e + "\n");
   545       throw e;
   546     }
   547     return cancelable;
   548   }
   549 };
   551 let PromptUtils = {
   552   getLocaleString: function pu_getLocaleString(aKey, aService) {
   553     if (aService == "passwdmgr")
   554       return this.cleanUpLabel(this.passwdBundle.GetStringFromName(aKey));
   556     return this.cleanUpLabel(this.bundle.GetStringFromName(aKey));
   557   },
   559   //
   560   // Copied from chrome://global/content/commonDialog.js
   561   //
   562   cleanUpLabel: function cleanUpLabel(aLabel) {
   563     // This is for labels which may contain embedded access keys.
   564     // If we end in (&X) where X represents the access key, optionally preceded
   565     // by spaces and/or followed by the ':' character,
   566     // remove the access key placeholder + leading spaces from the label.
   567     // Otherwise a character preceded by one but not two &s is the access key.
   569     // Note that if you change the following code, see the comment of
   570     // nsTextBoxFrame::UpdateAccessTitle.
   571     if (!aLabel)
   572       return "";
   574     if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) {
   575       aLabel = RegExp.leftContext + RegExp.$2;
   576     } else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) {
   577       aLabel = RegExp.$1 + RegExp.$2;
   578     }
   580     // Special code for using that & symbol
   581     aLabel = aLabel.replace(/\&\&/g, "&");
   583     return aLabel;
   584   },
   586   get pwmgr() {
   587     delete this.pwmgr;
   588     return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
   589   },
   591   getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) {
   592     let httpRealm = /^.+ \(.+\)$/;
   593     if (httpRealm.test(aRealmString))
   594       return [null, null, null];
   596     let uri = Services.io.newURI(aRealmString, null, null);
   597     let pathname = "";
   599     if (uri.path != "/")
   600       pathname = uri.path;
   602     let formattedHostname = this._getFormattedHostname(uri);
   603     return [formattedHostname, formattedHostname + pathname, uri.username];
   604   },
   606   canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) {
   607     let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname)
   608     if (aSavePassword)
   609       canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY)
   610     return canSave;
   611   },
   613   getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) {
   614     let checkLabel = null;
   615     let check = { value: false };
   616     let selectedLogin;
   618     checkLabel = this.getLocaleString("saveButton", "passwdmgr");
   620     // XXX Like the original code, we can't deal with multiple
   621     // account selection. (bug 227632)
   622     if (aFoundLogins.length > 0) {
   623       selectedLogin = aFoundLogins[0];
   625       // If the caller provided a username, try to use it. If they
   626       // provided only a password, this will try to find a password-only
   627       // login (or return null if none exists).
   628       if (aUser.value)
   629         selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value);
   631       if (selectedLogin) {
   632         check.value = true;
   633         aUser.value = selectedLogin.username;
   634         // If the caller provided a password, prefer it.
   635         if (!aPass.value)
   636           aPass.value = selectedLogin.password;
   637       }
   638     }
   640     return [checkLabel, check];
   641   },
   643   findLogin: function pu_findLogin(aLogins, aName, aValue) {
   644     for (let i = 0; i < aLogins.length; i++)
   645       if (aLogins[i][aName] == aValue)
   646         return aLogins[i];
   647     return null;
   648   },
   650   savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) {
   651     let selectedLogin = this.findLogin(aLogins, "username", aUser.value);
   653     // If we didn't find an existing login, or if the username
   654     // changed, save as a new login.
   655     if (!selectedLogin) {
   656       // add as new
   657       var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
   658       newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", "");
   659       this.pwmgr.addLogin(newLogin);
   660     } else if (aPass.value != selectedLogin.password) {
   661       // update password
   662       this.updateLogin(selectedLogin, aPass.value);
   663     } else {
   664       this.updateLogin(selectedLogin);
   665     }
   666   },
   668   updateLogin: function pu_updateLogin(aLogin, aPassword) {
   669     let now = Date.now();
   670     let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
   671     if (aPassword) {
   672       propBag.setProperty("password", aPassword);
   673       // Explicitly set the password change time here (even though it would
   674       // be changed automatically), to ensure that it's exactly the same
   675       // value as timeLastUsed.
   676       propBag.setProperty("timePasswordChanged", now);
   677     }
   678     propBag.setProperty("timeLastUsed", now);
   679     propBag.setProperty("timesUsedIncrement", 1);
   681     this.pwmgr.modifyLogin(aLogin, propBag);
   682   },
   684   // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388
   685   makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) {
   686     let isProxy    = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
   687     let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
   689     let username = aAuthInfo.username;
   690     let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo);
   692     // Suppress "the site says: $realm" when we synthesized a missing realm.
   693     if (!aAuthInfo.realm && !isProxy)
   694     realm = "";
   696     // Trim obnoxiously long realms.
   697     if (realm.length > 150) {
   698       realm = realm.substring(0, 150);
   699       // Append "..." (or localized equivalent).
   700       realm += this.ellipsis;
   701     }
   703     let text;
   704     if (isProxy)
   705       text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2);
   706     else if (isPassOnly)
   707       text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
   708     else if (!realm)
   709       text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1);
   710     else
   711       text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2);
   713     return text;
   714   },
   716   // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89
   717   getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) {
   718     let uri = aChannel.URI;
   719     let res = { host: null, port: -1 };
   720     if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) {
   721       let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel);
   722       res.host = proxy.proxyInfo.host;
   723       res.port = proxy.proxyInfo.port;
   724     } else {
   725       res.host = uri.host;
   726       res.port = uri.port;
   727     }
   728     return res;
   729   },
   731   getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) {
   732     let hostname, realm;
   733     // If our proxy is demanding authentication, don't use the
   734     // channel's actual destination.
   735     if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
   736         if (!(aChannel instanceof Ci.nsIProxiedChannel))
   737           throw "proxy auth needs nsIProxiedChannel";
   739       let info = aChannel.proxyInfo;
   740       if (!info)
   741         throw "proxy auth needs nsIProxyInfo";
   743       // Proxies don't have a scheme, but we'll use "moz-proxy://"
   744       // so that it's more obvious what the login is for.
   745       let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
   746       hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port;
   747       realm = aAuthInfo.realm;
   748       if (!realm)
   749         realm = hostname;
   751       return [hostname, realm];
   752     }
   753     hostname = this.getFormattedHostname(aChannel.URI);
   755     // If a HTTP WWW-Authenticate header specified a realm, that value
   756     // will be available here. If it wasn't set or wasn't HTTP, we'll use
   757     // the formatted hostname instead.
   758     realm = aAuthInfo.realm;
   759     if (!realm)
   760       realm = hostname;
   762     return [hostname, realm];
   763   },
   765   getAuthInfo : function pu_getAuthInfo(aAuthInfo) {
   766     let flags = aAuthInfo.flags;
   767     let username = {value: ""};
   768     let password = {value: ""};
   770     if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain)
   771       username.value = aAuthInfo.domain + "\\" + aAuthInfo.username;
   772     else
   773       username.value = aAuthInfo.username;
   775     password.value = aAuthInfo.password
   777     return [username, password];
   778   },
   780   setAuthInfo : function (aAuthInfo, username, password) {
   781     var flags = aAuthInfo.flags;
   782     if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
   783       // Domain is separated from username by a backslash
   784       var idx = username.indexOf("\\");
   785       if (idx == -1) {
   786         aAuthInfo.username = username;
   787       } else {
   788         aAuthInfo.domain   =  username.substring(0, idx);
   789         aAuthInfo.username =  username.substring(idx+1);
   790       }
   791     } else {
   792       aAuthInfo.username = username;
   793     }
   794     aAuthInfo.password = password;
   795   },
   797   getFormattedHostname : function pu_getFormattedHostname(uri) {
   798     let scheme = uri.scheme;
   799     let hostname = scheme + "://" + uri.host;
   801     // If the URI explicitly specified a port, only include it when
   802     // it's not the default. (We never want "http://foo.com:80")
   803     port = uri.port;
   804     if (port != -1) {
   805       let handler = Services.io.getProtocolHandler(scheme);
   806       if (port != handler.defaultPort)
   807         hostname += ":" + port;
   808     }
   809     return hostname;
   810   },
   812   fireDialogEvent: function(aDomWin, aEventName) {
   813     // accessing the document object can throw if this window no longer exists. See bug 789888.
   814     try {
   815       if (!aDomWin.document)
   816         return;
   817       let event = aDomWin.document.createEvent("Events");
   818       event.initEvent(aEventName, true, true);
   819       let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor)
   820                            .getInterface(Ci.nsIDOMWindowUtils);
   821       winUtils.dispatchEventToChromeOnly(aDomWin, event);
   822     } catch(ex) {
   823     }
   824   }
   825 };
   827 XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () {
   828   return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
   829 });
   831 XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () {
   832   return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties");
   833 });
   835 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]);

mercurial