Bug 1153217 - Allow editing the password in the capture doorhanger. r=MattN, a=sledru
authorBernardo P. Rittmeyer <bernardo@rittme.com>
Sat, 13 Jun 2015 12:35:05 -0700
changeset 275084 984d1cdbaf25f8ddd19483a07ca75bb013cca808
parent 275083 5923fecfc3cd16016215efba2c6eb54378c6b142
child 275085 06984d7e828ebc6f206cf829e840197e1e6c918d
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, sledru
bugs1153217
milestone40.0a2
Bug 1153217 - Allow editing the password in the capture doorhanger. r=MattN, a=sledru The main action button is disabled if the password is empty since empty passwords aren't allowed.
browser/base/content/browser.css
browser/base/content/popup-notifications.inc
toolkit/components/passwordmgr/nsLoginManagerPrompter.js
toolkit/themes/linux/global/notification.css
toolkit/themes/osx/global/notification.css
toolkit/themes/windows/global/notification.css
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1304,8 +1304,16 @@ toolbarpaletteitem[place="palette"][hidd
 .popup-notification-footer[popupid="bad-content"] {
   display: none;
 }
 
 .popup-notification-footer[popupid="bad-content"][mixedblockdisabled],
 .popup-notification-footer[popupid="bad-content"][trackingblockdisabled] {
   display: block;
 }
+
+.popup-notification-invalid-input {
+  box-shadow: 0 0 1.5px 1px red;
+}
+
+.popup-notification-invalid-input[focused] {
+  box-shadow: 0 0 2px 2px rgba(255,0,0,0.4);
+}
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -52,18 +52,17 @@
       <popupnotificationcontent orient="vertical" align="start">
         <label id="pointerLock-cancel">&pointerLock.notification.message;</label>
       </popupnotificationcontent>
     </popupnotification>
 
     <popupnotification id="password-notification" hidden="true">
       <popupnotificationcontent orient="vertical">
         <textbox id="password-notification-username"/>
-        <textbox id="password-notification-password" type="password"
-                 disabled="true"/>
+        <textbox id="password-notification-password" type="password"/>
       </popupnotificationcontent>
     </popupnotification>
 
     <vbox id="login-fill-doorhanger" hidden="true">
       <description id="login-fill-testing"
                    value="Thanks for testing the login fill doorhanger!"/>
       <textbox id="login-fill-filter"/>
       <richlistbox id="login-fill-list"/>
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -817,16 +817,30 @@ LoginManagerPrompter.prototype = {
                                                 : "PWMGR_PROMPT_UPDATE_ACTION";
     let histogram = Services.telemetry.getHistogramById(histogramName);
     histogram.add(PROMPT_DISPLAYED);
 
     let chromeDoc = browser.ownerDocument;
 
     let currentNotification;
 
+    let updateButtonStatus = (element) => {
+      let mainActionButton = chromeDoc.getAnonymousElementByAttribute(element.button, "anonid", "button");
+      // Disable the main button inside the menu-button if the password field is empty.
+      if (login.password.length == 0) {
+        mainActionButton.setAttribute("disabled", true);
+        chromeDoc.getElementById("password-notification-password")
+                 .classList.add("popup-notification-invalid-input");
+      } else {
+        mainActionButton.removeAttribute("disabled");
+        chromeDoc.getElementById("password-notification-password")
+                 .classList.remove("popup-notification-invalid-input");
+      }
+    };
+
     let updateButtonLabel = () => {
       let foundLogins = Services.logins.findLogins({}, login.hostname,
                                                    login.formSubmitURL,
                                                    login.httpRealm);
       let logins = foundLogins.filter(l => l.username == login.username);
       let msgNames = (logins.length == 0) ? saveMsgNames : changeMsgNames;
 
       // Update the label based on whether this will be a new login or not.
@@ -838,16 +852,17 @@ LoginManagerPrompter.prototype = {
       currentNotification.mainAction.accessKey = accessKey;
 
       // Update the labels in real time if the notification is displayed.
       let element = [...currentNotification.owner.panel.childNodes]
                     .find(n => n.notification == currentNotification);
       if (element) {
         element.setAttribute("buttonlabel", label);
         element.setAttribute("buttonaccesskey", accessKey);
+        updateButtonStatus(element);
       }
     };
 
     let writeDataToUI = () => {
       chromeDoc.getElementById("password-notification-username")
                .setAttribute("placeholder", usernamePlaceholder);
       chromeDoc.getElementById("password-notification-username")
                .setAttribute("value", login.username);
@@ -858,17 +873,17 @@ LoginManagerPrompter.prototype = {
 
     let readDataFromUI = () => {
       login.username =
         chromeDoc.getElementById("password-notification-username").value;
       login.password =
         chromeDoc.getElementById("password-notification-password").value;
     };
 
-    let onUsernameInput = () => {
+    let onInput = () => {
       readDataFromUI();
       updateButtonLabel();
     };
 
     let persistData = () => {
       let foundLogins = Services.logins.findLogins({}, login.hostname,
                                                    login.formSubmitURL,
                                                    login.httpRealm);
@@ -879,17 +894,22 @@ LoginManagerPrompter.prototype = {
         Services.logins.addLogin(new LoginInfo(login.hostname,
                                                login.formSubmitURL,
                                                login.httpRealm,
                                                login.username,
                                                login.password,
                                                login.usernameField,
                                                login.passwordField));
       } else if (logins.length == 1) {
-        this._updateLogin(logins[0], login.password);
+        if (logins[0].password == login.password) {
+          // We only want to touch the login's use count and last used time.
+          this._updateLogin(logins[0], null);
+        } else {
+          this._updateLogin(logins[0], login.password);
+        }
       } else {
         Cu.reportError("Unexpected match of multiple logins.");
       }
     };
 
     // The main action is the "Remember" or "Update" button.
     let mainAction = {
       label: this._getLocalizedString(initialMsgNames.buttonLabel),
@@ -930,27 +950,33 @@ LoginManagerPrompter.prototype = {
       {
         timeout: Date.now() + 10000,
         persistWhileVisible: true,
         passwordNotificationType: type,
         eventCallback: function (topic) {
           switch (topic) {
             case "showing":
               currentNotification = this;
+              chromeDoc.getElementById("password-notification-username")
+                       .addEventListener("input", onInput);
+              chromeDoc.getElementById("password-notification-password")
+                       .addEventListener("input", onInput);
+              break;
+            case "shown":
               writeDataToUI();
-              chromeDoc.getElementById("password-notification-username")
-                       .addEventListener("input", onUsernameInput);
               break;
             case "dismissed":
               readDataFromUI();
               // Fall through.
             case "removed":
               currentNotification = null;
               chromeDoc.getElementById("password-notification-username")
-                       .removeEventListener("input", onUsernameInput);
+                       .removeEventListener("input", onInput);
+              chromeDoc.getElementById("password-notification-password")
+                       .removeEventListener("input", onInput);
               break;
           }
           return false;
         },
       }
     );
   },
 
--- a/toolkit/themes/linux/global/notification.css
+++ b/toolkit/themes/linux/global/notification.css
@@ -78,8 +78,12 @@ notification[type="critical"] {
 
 .popup-notification-learnmore-link {
   margin-top: .5em !important;
 }
 
 .popup-notification-button-container {
   margin-top: 17px;
 }
+
+.popup-notification-menubutton > .button-menubutton-button[disabled] {
+  opacity: 0.5;
+}
--- a/toolkit/themes/osx/global/notification.css
+++ b/toolkit/themes/osx/global/notification.css
@@ -189,8 +189,12 @@ notification[type="info"]:not([value="tr
 .popup-notification-closebutton {
   -moz-margin-end: -12px;
   margin-top: -13px;
 }
 
 .popup-notification-closeitem > .menu-iconic-left {
   display: none;
 }
+
+.popup-notification-menubutton > .button-menubutton-button[disabled] {
+  opacity: 0.5;
+}
--- a/toolkit/themes/windows/global/notification.css
+++ b/toolkit/themes/windows/global/notification.css
@@ -188,8 +188,12 @@ XXX: apply styles to all themes until bu
   }
 /*}*/
 %endif
 
 .popup-notification-closebutton {
   -moz-margin-end: -14px;
   margin-top: -10px;
 }
+
+.popup-notification-menubutton > .button-menubutton-button[disabled] {
+  opacity: 0.5;
+}