Bug 1153217 - Allow editing the password in the capture doorhanger. r=MattN, a=lizzard
authorBernardo P. Rittmeyer <bernardo@rittme.com>
Tue, 16 Jun 2015 18:39:46 -0700
changeset 267738 c62540b3f0e5c1b2787349998f7031c5b34449e8
parent 267737 9b8f7ed4e0d2a93f2b144c9673c58bf2e8ded176
child 267739 9ca933b3b236e9010a25ce7196837d6a2549cb4a
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, lizzard
bugs1153217
milestone39.0
Bug 1153217 - Allow editing the password in the capture doorhanger. r=MattN, a=lizzard 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
@@ -1300,8 +1300,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>
 
 #ifdef E10S_TESTING_ONLY
     <popupnotification id="enable-e10s-notification" hidden="true">
       <popupnotificationcontent orient="vertical"/>
     </popupnotification>
 #endif
--- 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
@@ -192,8 +192,12 @@ XXX: apply styles to all themes until bu
 %endif
 /*}*/
 %endif
 
 .popup-notification-closebutton {
   -moz-margin-end: -14px;
   margin-top: -10px;
 }
+
+.popup-notification-menubutton > .button-menubutton-button[disabled] {
+  opacity: 0.5;
+}