Bug 1550099 - Add reveal button for modal-input[type=password]. r=MattN,Pike
authorJared Wein <jwein@mozilla.com>
Tue, 14 May 2019 20:07:20 +0000
changeset 473861 044fba1f3da7b9400382184918a9d325db3f3c90
parent 473860 48567f3c473a35b8320f060181d32cb570deacb0
child 473862 6e1256ea2dd73c29e9c4147c4afd2566062080a8
push id36017
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 09:25:56 +0000
treeherdermozilla-central@76bbedc1ec1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, Pike
bugs1550099
milestone68.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 1550099 - Add reveal button for modal-input[type=password]. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D30961
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/content/components/modal-input.css
browser/components/aboutlogins/content/components/modal-input.js
browser/components/aboutlogins/tests/mochitest/test_modal_input.html
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -22,14 +22,15 @@ login-list =
        *[other] { $count } entries
     }
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
   .edit-button = Edit
   .hostname-label = Website Address
+  .modal-input-reveal-button = Toggle password visibility
   .password-label = Password
   .save-changes-button = Save Changes
   .time-created = Created: { DATETIME($timeCreated, day: "numeric", month: "long", year: "numeric") }
   .time-changed = Last changed: { DATETIME($timeChanged, day: "numeric", month: "long", year: "numeric") }
   .time-used = Last used: { DATETIME($timeUsed, day: "numeric", month: "long", year: "numeric") }
   .username-label = Username
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -28,16 +28,17 @@
                 data-l10n-attrs="count"
                 data-l10n-args='{"count": 0}'></login-list>
     <login-item data-l10n-id="login-item"
                 data-l10n-args='{"timeCreated": 0, "timeChanged": 0, "timeUsed": 0}'
                 data-l10n-attrs="cancel-button,
                                  delete-button,
                                  edit-button,
                                  hostname-label,
+                                 modal-input-reveal-button,
                                  password-label,
                                  save-changes-button,
                                  time-created,
                                  time-changed,
                                  time-used,
                                  username-label"></login-item>
 
     <template id="login-list-template">
@@ -90,11 +91,12 @@
       <input type="text"/>
     </template>
 
     <template id="modal-input-template">
       <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/modal-input.css">
       <span class="locked-value"></span>
       <input type="text" class="unlocked-value"/>
+      <button class="reveal-button"/>
     </template>
   </body>
 </html>
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -38,29 +38,40 @@ class LoginItem extends ReflectedFluentE
   }
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
       "delete-button",
       "edit-button",
       "hostname-label",
+      "modal-input-reveal-button",
       "password-label",
       "save-changes-button",
       "time-created",
       "time-changed",
       "time-used",
       "username-label",
     ];
   }
 
   static get observedAttributes() {
     return this.reflectedFluentIDs;
   }
 
+  handleSpecialCaseFluentString(attrName) {
+    if (attrName != "modal-input-reveal-button") {
+      return false;
+    }
+
+    this.shadowRoot.querySelector("modal-input[name='password']")
+                   .setAttribute("reveal-button", this.getAttribute(attrName));
+    return true;
+  }
+
   render() {
     let l10nArgs = {
       timeCreated: this._login.timeCreated || "",
       timeChanged: this._login.timePasswordChanged || "",
       timeUsed: this._login.timeLastUsed || "",
     };
     document.l10n.setAttributes(this, "login-item", l10nArgs);
     let hostnameNoScheme = this._login.hostname && new URL(this._login.hostname).hostname;
--- a/browser/components/aboutlogins/content/components/modal-input.css
+++ b/browser/components/aboutlogins/content/components/modal-input.css
@@ -1,8 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :host([editing]) .locked-value,
 :host(:not([editing])) .unlocked-value {
   display: none;
 }
+
+:host(:not([type="password"])) .reveal-button {
+  display: none;
+}
--- a/browser/components/aboutlogins/content/components/modal-input.js
+++ b/browser/components/aboutlogins/content/components/modal-input.js
@@ -1,13 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-class ModalInput extends HTMLElement {
+/* globals ReflectedFluentElement */
+
+class ModalInput extends ReflectedFluentElement {
   static get LOCKED_PASSWORD_DISPLAY() {
     return "••••••••";
   }
 
   connectedCallback() {
     if (this.children.length) {
       return;
     }
@@ -19,23 +21,31 @@ class ModalInput extends HTMLElement {
     if (this.hasAttribute("value")) {
       this.value = this.getAttribute("value");
     }
 
     if (this.getAttribute("type") == "password") {
       let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
       unlockedValue.setAttribute("type", "password");
     }
+
+    this.shadowRoot.querySelector(".reveal-button").addEventListener("click", this);
+  }
+
+  static get reflectedFluentIDs() {
+    return ["reveal-button"];
   }
 
   static get observedAttributes() {
-    return ["editing", "type", "value"];
+    return ["editing", "type", "value"].concat(ModalInput.reflectedFluentIDs);
   }
 
   attributeChangedCallback(attr, oldValue, newValue) {
+    super.attributeChangedCallback(attr, oldValue, newValue);
+
     if (!this.shadowRoot) {
       return;
     }
 
     let lockedValue = this.shadowRoot.querySelector(".locked-value");
     let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
 
     switch (attr) {
@@ -58,16 +68,37 @@ class ModalInput extends HTMLElement {
       }
       case "value": {
         this.value = newValue;
         break;
       }
     }
   }
 
+  handleEvent(event) {
+    switch (event.type) {
+      case "click": {
+        if (event.target.classList.contains("reveal-button")) {
+          let lockedValue = this.shadowRoot.querySelector(".locked-value");
+          let unlockedValue = this.shadowRoot.querySelector(".unlocked-value");
+          let editing = this.hasAttribute("editing");
+          if ((editing && unlockedValue.getAttribute("type") == "password") ||
+              (!editing && lockedValue.textContent == this.constructor.LOCKED_PASSWORD_DISPLAY)) {
+            lockedValue.textContent = this.value;
+            unlockedValue.setAttribute("type", "text");
+          } else {
+            lockedValue.textContent = this.constructor.LOCKED_PASSWORD_DISPLAY;
+            unlockedValue.setAttribute("type", "password");
+          }
+        }
+        break;
+      }
+    }
+  }
+
   get value() {
     return this.hasAttribute("editing") ? this.shadowRoot.querySelector(".unlocked-value").value.trim()
                                         : this.getAttribute("value") || "";
   }
 
   set value(val) {
     if (this.getAttribute("value") != val) {
       this.setAttribute("value", val);
--- a/browser/components/aboutlogins/tests/mochitest/test_modal_input.html
+++ b/browser/components/aboutlogins/tests/mochitest/test_modal_input.html
@@ -2,16 +2,17 @@
 <html>
 <!--
 Test the modal-input component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the modal-input component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="reflected-fluent-element.js"></script>
   <script src="modal-input.js"></script>
   <script src="aboutlogins_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
   </p>