Bug 1145789 - Unify code paths for the password save and change notifications. r=MattN
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Mon, 23 Mar 2015 13:59:39 -0700
changeset 265601 06f1b43f5a7b8f5cba9bb4ae18cdc488946154ff
parent 265600 c521d136b0f3d0c1e401ed502de5a2be9756f2cb
child 265602 a12de055e090438061e91c3731041de672be0fe6
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1145789
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1145789 - Unify code paths for the password save and change notifications. r=MattN
toolkit/components/passwordmgr/moz.build
toolkit/components/passwordmgr/nsLoginManagerPrompter.js
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -38,17 +38,16 @@ EXTRA_PP_COMPONENTS += [
 EXTRA_PP_JS_MODULES += [
     'LoginManagerParent.jsm',
 ]
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
-    'LoginManagerParent.jsm',
     'LoginRecipes.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     EXTRA_COMPONENTS += [
         'storage-mozStorage.js',
     ]
 else:
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -9,22 +9,20 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
 
 /* Constants for password prompt telemetry.
  * Mirrored in mobile/android/components/LoginManagerPrompter.js */
 const PROMPT_DISPLAYED = 0;
 
-const PROMPT_ADD = 1;
+const PROMPT_ADD_OR_UPDATE = 1;
 const PROMPT_NOTNOW = 2;
 const PROMPT_NEVER = 3;
 
-const PROMPT_UPDATE = 1;
-
 /*
  * LoginManagerPromptFactory
  *
  * Implements nsIPromptFactory
  *
  * Invoked by [toolkit/components/prompts/src/nsPrompter.js]
  */
 function LoginManagerPromptFactory() {
@@ -737,18 +735,16 @@ LoginManagerPrompter.prototype = {
 
 
   /*
    * promptToSavePassword
    *
    */
   promptToSavePassword : function (aLogin) {
     var notifyObj = this._getPopupNote() || this._getNotifyBox();
-    Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED);
-
     if (notifyObj)
       this._showSaveLoginNotification(notifyObj, aLogin);
     else
       this._showSaveLoginDialog(aLogin);
   },
 
 
   /*
@@ -778,16 +774,110 @@ LoginManagerPrompter.prototype = {
     newBar.timeout = Date.now() + 20000; // 20 seconds
 
     if (oldBar) {
       this.log("(...and removing old " + aName + " notification bar)");
       aNotifyBox.removeNotification(oldBar);
     }
   },
 
+  /**
+   * Displays the PopupNotifications.jsm doorhanger for password save or change.
+   *
+   * @param {nsILoginInfo} login
+   *        Login to save or change. For changes, this login should contain the
+   *        new password.
+   * @param {string} type
+   *        This is "password-save" or "password-change" depending on the
+   *        original notification type. This is used for telemetry and tests.
+   */
+  _showLoginCaptureDoorhanger(login, type) {
+    let { browser } = this._getNotifyWindow();
+
+    let msgNames = type == "password-save" ? {
+      prompt: "rememberPasswordMsgNoUsername",
+      buttonLabel: "notifyBarRememberPasswordButtonText",
+      buttonAccessKey: "notifyBarRememberPasswordButtonAccessKey",
+    } : {
+      // We reuse the existing message, even if it expects a username, until we
+      // switch to the final terminology in bug 1144856.
+      prompt: "updatePasswordMsg",
+      buttonLabel: "notifyBarUpdateButtonText",
+      buttonAccessKey: "notifyBarUpdateButtonAccessKey",
+    };
+
+    let histogramName = type == "password-save" ? "PWMGR_PROMPT_REMEMBER_ACTION"
+                                                : "PWMGR_PROMPT_UPDATE_ACTION";
+    let histogram = Services.telemetry.getHistogramById(histogramName);
+    histogram.add(PROMPT_DISPLAYED);
+
+    // The main action is the "Remember" or "Update" button.
+    let mainAction = {
+      label: this._getLocalizedString(msgNames.buttonLabel),
+      accessKey: this._getLocalizedString(msgNames.buttonAccessKey),
+      callback: () => {
+        histogram.add(PROMPT_ADD_OR_UPDATE);
+        let foundLogins = Services.logins.findLogins({}, login.hostname,
+                                                     login.formSubmitURL,
+                                                     login.httpRealm);
+        let logins = foundLogins.filter(l => l.username == login.username);
+        if (logins.length == 0) {
+          Services.logins.addLogin(login);
+        } else if (logins.length == 1) {
+          this._updateLogin(logins[0], login.password);
+        } else {
+          Cu.reportError("Unexpected match of multiple logins.");
+        }
+        browser.focus();
+      }
+    };
+
+    // Include a "Never for this site" button when saving a new password.
+    let secondaryActions = type == "password-save" ? [{
+      label: this._getLocalizedString("notifyBarNeverRememberButtonText"),
+      accessKey: this._getLocalizedString("notifyBarNeverRememberButtonAccessKey"),
+      callback: () => {
+        histogram.add(PROMPT_NEVER);
+        Services.logins.setLoginSavingEnabled(login.hostname, false);
+        browser.focus();
+      }
+    }] : null;
+
+    let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder");
+    let displayHost = this._getShortDisplayHost(login.hostname);
+
+    this._getPopupNote().show(
+      browser,
+      "password",
+      this._getLocalizedString(msgNames.prompt, [displayHost]),
+      "password-notification-icon",
+      mainAction,
+      secondaryActions,
+      {
+        timeout: Date.now() + 10000,
+        persistWhileVisible: true,
+        passwordNotificationType: type,
+        eventCallback: function (topic) {
+          if (topic != "showing") {
+            return false;
+          }
+
+          let chromeDoc = this.browser.ownerDocument;
+
+          chromeDoc.getElementById("password-notification-username")
+                   .setAttribute("placeholder", usernamePlaceholder);
+          chromeDoc.getElementById("password-notification-username")
+                   .setAttribute("value", login.username);
+          chromeDoc.getElementById("password-notification-password")
+                   .setAttribute("value", login.password);
+        },
+      }
+    );
+  },
+
   /*
    * _showSaveLoginNotification
    *
    * Displays a notification bar or a popup notification, to allow the user
    * to save the specified login. This allows the user to see the results of
    * their login, and only save a login which they know worked.
    *
    * @param aNotifyObj
@@ -801,80 +891,30 @@ LoginManagerPrompter.prototype = {
     var neverButtonText =
           this._getLocalizedString("notifyBarNeverRememberButtonText");
     var neverButtonAccessKey =
           this._getLocalizedString("notifyBarNeverRememberButtonAccessKey");
     var rememberButtonText =
           this._getLocalizedString("notifyBarRememberPasswordButtonText");
     var rememberButtonAccessKey =
           this._getLocalizedString("notifyBarRememberPasswordButtonAccessKey");
-    var usernamePlaceholder =
-          this._getLocalizedString("noUsernamePlaceholder");
 
     var displayHost = this._getShortDisplayHost(aLogin.hostname);
     var notificationText = this._getLocalizedString(
                                   "rememberPasswordMsgNoUsername",
                                   [displayHost]);
 
     // The callbacks in |buttons| have a closure to access the variables
     // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
     // without a getService() call.
     var pwmgr = this._pwmgr;
-    let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION");
 
     // Notification is a PopupNotification
     if (aNotifyObj == this._getPopupNote()) {
-      // "Remember" button
-      var mainAction = {
-        label:     rememberButtonText,
-        accessKey: rememberButtonAccessKey,
-        callback: function(aNotifyObj, aButton) {
-          promptHistogram.add(PROMPT_ADD);
-          pwmgr.addLogin(aLogin);
-          browser.focus();
-        }
-      };
-
-      var secondaryActions = [
-        // "Never for this site" button
-        {
-          label:     neverButtonText,
-          accessKey: neverButtonAccessKey,
-          callback: function(aNotifyObj, aButton) {
-            promptHistogram.add(PROMPT_NEVER);
-            pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
-            browser.focus();
-          }
-        }
-      ];
-
-      var { browser } = this._getNotifyWindow();
-
-      let eventCallback = function (topic) {
-        if (topic != "showing") {
-          return false;
-        }
-
-        let chromeDoc = this.browser.ownerDocument;
-
-        chromeDoc.getElementById("password-notification-username")
-                 .setAttribute("placeholder", usernamePlaceholder);
-        chromeDoc.getElementById("password-notification-username")
-                 .setAttribute("value", aLogin.username);
-        chromeDoc.getElementById("password-notification-password")
-                 .setAttribute("value", aLogin.password);
-      };
-
-      aNotifyObj.show(browser, "password", notificationText,
-                      "password-notification-icon", mainAction,
-                      secondaryActions,
-                      { timeout: Date.now() + 10000,
-                        persistWhileVisible: true,
-                        passwordNotificationType: "password-save",
-                        eventCallback });
+      this._showLoginCaptureDoorhanger(aLogin, "password-save");
     } else {
       var notNowButtonText =
             this._getLocalizedString("notifyBarNotNowButtonText");
       var notNowButtonAccessKey =
             this._getLocalizedString("notifyBarNotNowButtonAccessKey");
       var buttons = [
         // "Remember" button
         {
@@ -1025,68 +1065,32 @@ LoginManagerPrompter.prototype = {
    * @param aNotifyObj
    *        A notification box or a popup notification.
    */
   _showChangeLoginNotification : function (aNotifyObj, aOldLogin, aNewPassword) {
     var changeButtonText =
           this._getLocalizedString("notifyBarUpdateButtonText");
     var changeButtonAccessKey =
           this._getLocalizedString("notifyBarUpdateButtonAccessKey");
-    var usernamePlaceholder =
-          this._getLocalizedString("noUsernamePlaceholder");
 
     // We reuse the existing message, even if it expects a username, until we
     // switch to the final terminology in bug 1144856.
     var displayHost = this._getShortDisplayHost(aOldLogin.hostname);
     var notificationText = this._getLocalizedString("updatePasswordMsg",
                                                     [displayHost]);
 
     // The callbacks in |buttons| have a closure to access the variables
     // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
     // without a getService() call.
     var self = this;
 
-    let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
     // Notification is a PopupNotification
     if (aNotifyObj == this._getPopupNote()) {
-      // "Yes" button
-      var mainAction = {
-        label:     changeButtonText,
-        accessKey: changeButtonAccessKey,
-        popup:     null,
-        callback:  function(aNotifyObj, aButton) {
-          self._updateLogin(aOldLogin, aNewPassword);
-          promptHistogram.add(PROMPT_UPDATE);
-        }
-      };
-
-      var { browser } = this._getNotifyWindow();
-
-      let eventCallback = function (topic) {
-        if (topic != "showing") {
-          return false;
-        }
-
-        let chromeDoc = this.browser.ownerDocument;
-
-        chromeDoc.getElementById("password-notification-username")
-                 .setAttribute("placeholder", usernamePlaceholder);
-        chromeDoc.getElementById("password-notification-username")
-                 .setAttribute("value", aOldLogin.username);
-        chromeDoc.getElementById("password-notification-password")
-                 .setAttribute("value", aNewPassword);
-      };
-
-      Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION").add(PROMPT_DISPLAYED);
-      aNotifyObj.show(browser, "password", notificationText,
-                      "password-notification-icon", mainAction,
-                      null, { timeout: Date.now() + 10000,
-                              persistWhileVisible: true,
-                              passwordNotificationType: "password-change",
-                              eventCallback });
+      aOldLogin.password = aNewPassword;
+      this._showLoginCaptureDoorhanger(aOldLogin, "password-change");
     } else {
       var dontChangeButtonText =
             this._getLocalizedString("notifyBarDontChangeButtonText");
       var dontChangeButtonAccessKey =
             this._getLocalizedString("notifyBarDontChangeButtonAccessKey");
       var buttons = [
         // "Yes" button
         {