Bug 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike
☠☠ backed out by 366a49e70140 ☠ ☠
authorJared Wein <jwein@mozilla.com>
Fri, 17 May 2019 18:26:01 +0000
changeset 474393 faf4415303fb85890222c1ca3b4a975ddd33b22a
parent 474392 e3e9a5ca05997babb20fb7eee0e85dcb921844a3
child 474394 510a686041bb31f70684c29a72e8a5f1163836b2
push id113152
push userdluca@mozilla.com
push dateSat, 18 May 2019 10:33:03 +0000
treeherdermozilla-inbound@9b2f851979cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, Pike
bugs1550093
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 1550093 - Create CustomElement for copy-to-clipboard. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D31519
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/copy-to-clipboard-button.css
browser/components/aboutlogins/content/components/copy-to-clipboard-button.js
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/jar.mn
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -24,15 +24,19 @@ login-list =
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
   .edit-button = Edit
   .hostname-label = Website Address
   .modal-input-reveal-checkbox-hide = Hide password
   .modal-input-reveal-checkbox-show = Show password
+  .copied-password-button = Copied!
+  .copied-username-button = Copied!
+  .copy-password-button = Copy
+  .copy-username-button = Copy
   .open-site-button = Launch
   .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
@@ -5,16 +5,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"/>
     <title data-l10n-id="about-logins-page-title"></title>
     <link rel="localization" href="browser/aboutLogins.ftl">
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/reflected-fluent-element.js"></script>
+    <script defer="defer" src="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-filter.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list-item.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/components/modal-input.js"></script>
     <script defer="defer" src="chrome://browser/content/aboutlogins/aboutLogins.js"></script>
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
     <link rel="stylesheet" href="chrome://browser/content/aboutlogins/aboutLogins.css">
@@ -26,16 +27,20 @@
                     data-l10n-attrs="placeholder"></login-filter>
     </header>
     <login-list data-l10n-id="login-list"
                 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,
+                                 copy-password-button,
+                                 copy-username-button,
+                                 copied-password-button,
+                                 copied-username-button,
                                  delete-button,
                                  edit-button,
                                  hostname-label,
                                  modal-input-reveal-checkbox-hide,
                                  modal-input-reveal-checkbox-show,
                                  open-site-button,
                                  password-label,
                                  save-changes-button,
@@ -76,22 +81,24 @@
         </label>
         <button class="open-site-button"></button>
       </div>
       <div class="detail-row">
         <label>
           <span class="username-label field-label"></span>
           <modal-input name="username"/>
         </label>
+        <copy-to-clipboard-button class="copy-username-button"></copy-to-clipboard-button>
       </div>
       <div class="detail-row">
         <label>
           <span class="password-label field-label"></span>
           <modal-input type="password" name="password"/>
         </label>
+        <copy-to-clipboard-button class="copy-password-button"></copy-to-clipboard-button>
       </div>
       <p class="time-created meta-info"></p>
       <p class="time-changed meta-info"></p>
       <p class="time-used meta-info"></p>
       <button class="save-changes-button"></button>
       <button class="cancel-button"></button>
     </template>
 
@@ -103,10 +110,19 @@
 
     <template id="modal-input-template">
       <link rel="stylesheet" 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"/>
       <input type="checkbox" class="reveal-checkbox"/>
     </template>
+
+    <template id="copy-to-clipboard-button-template">
+      <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
+      <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.css">
+      <button class="copy-button">
+        <span class="copied-button-text"></span>
+        <span class="copy-button-text"></span>
+      </button>
+    </template>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/copy-to-clipboard-button.css
@@ -0,0 +1,8 @@
+/* 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(:not([copied])) .copied-button-text,
+:host([copied]) .copy-button-text {
+  display: none;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/copy-to-clipboard-button.js
@@ -0,0 +1,47 @@
+/* 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/. */
+
+/* globals ReflectedFluentElement */
+
+class CopyToClipboardButton extends ReflectedFluentElement {
+  connectedCallback() {
+    if (this.children.length) {
+      return;
+    }
+
+    let CopyToClipboardButtonTemplate = document.querySelector("#copy-to-clipboard-button-template");
+    this.attachShadow({mode: "open"})
+        .appendChild(CopyToClipboardButtonTemplate.content.cloneNode(true));
+
+    this.shadowRoot.querySelector(".copy-button").addEventListener("click", this);
+  }
+
+  static get reflectedFluentIDs() {
+    return ["copy-button-text", "copied-button-text"];
+  }
+
+  static get observedAttributes() {
+    return CopyToClipboardButton.reflectedFluentIDs;
+  }
+
+  handleSpecialCaseFluentString(attrName) {
+    if (attrName != "copied-button-text" &&
+        attrName != "copy-button-text") {
+      return false;
+    }
+
+    let span = this.shadowRoot.querySelector("." + attrName);
+    span.textContent = this.getAttribute(attrName);
+    return true;
+  }
+
+  handleEvent(event) {
+    if (event.type != "click") {
+      return;
+    }
+
+    this.setAttribute("copied", "");
+  }
+}
+customElements.define("copy-to-clipboard-button", CopyToClipboardButton);
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -18,16 +18,18 @@ class LoginItem extends ReflectedFluentE
 
     let loginItemTemplate = document.querySelector("#login-item-template");
     this.attachShadow({mode: "open"})
         .appendChild(loginItemTemplate.content.cloneNode(true));
 
     this.reflectFluentStrings();
 
     for (let selector of [
+      ".copy-password-button",
+      ".copy-username-button",
       ".delete-button",
       ".edit-button",
       ".open-site-button",
       ".save-changes-button",
       ".cancel-button",
     ]) {
       let button = this.shadowRoot.querySelector(selector);
       button.addEventListener("click", this);
@@ -36,16 +38,20 @@ class LoginItem extends ReflectedFluentE
     window.addEventListener("AboutLoginsLoginSelected", this);
 
     this.render();
   }
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
+      "copied-password-button",
+      "copied-username-button",
+      "copy-password-button",
+      "copy-username-button",
       "delete-button",
       "edit-button",
       "hostname-label",
       "modal-input-reveal-checkbox-hide",
       "modal-input-reveal-checkbox-show",
       "open-site-button",
       "password-label",
       "save-changes-button",
@@ -57,16 +63,30 @@ class LoginItem extends ReflectedFluentE
   }
 
   static get observedAttributes() {
     return this.reflectedFluentIDs;
   }
 
   handleSpecialCaseFluentString(attrName) {
     switch (attrName) {
+      case "copied-password-button":
+      case "copy-password-button": {
+        let copyPasswordButton = this.shadowRoot.querySelector(".copy-password-button");
+        let newAttrName = attrName.substr(0, attrName.indexOf("-")) + "-button-text";
+        copyPasswordButton.setAttribute(newAttrName, this.getAttribute(attrName));
+        break;
+      }
+      case "copied-username-button":
+      case "copy-username-button": {
+        let copyUsernameButton = this.shadowRoot.querySelector(".copy-username-button");
+        let newAttrName = attrName.substr(0, attrName.indexOf("-")) + "-button-text";
+        copyUsernameButton.setAttribute(newAttrName, this.getAttribute(attrName));
+        break;
+      }
       case "modal-input-reveal-checkbox-hide": {
         this.shadowRoot.querySelector("modal-input[name='password']")
                        .setAttribute("reveal-checkbox-hide", this.getAttribute(attrName));
         break;
       }
       case "modal-input-reveal-checkbox-show": {
         this.shadowRoot.querySelector("modal-input[name='password']")
                        .setAttribute("reveal-checkbox-show", this.getAttribute(attrName));
@@ -99,16 +119,22 @@ class LoginItem extends ReflectedFluentE
         break;
       }
       case "click": {
         if (event.target.classList.contains("cancel-button")) {
           this.toggleEditing();
           this.render();
           return;
         }
+        if (event.target.classList.contains("copy-password-button")) {
+          return;
+        }
+        if (event.target.classList.contains("copy-username-button")) {
+          return;
+        }
         if (event.target.classList.contains("delete-button")) {
           document.dispatchEvent(new CustomEvent("AboutLoginsDeleteLogin", {
             bubbles: true,
             detail: this._login,
           }));
           return;
         }
         if (event.target.classList.contains("edit-button")) {
--- a/browser/components/aboutlogins/jar.mn
+++ b/browser/components/aboutlogins/jar.mn
@@ -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/.
 
 browser.jar:
+  content/browser/aboutlogins/components/copy-to-clipboard-button.css (content/components/copy-to-clipboard-button.css)
+  content/browser/aboutlogins/components/copy-to-clipboard-button.js  (content/components/copy-to-clipboard-button.js)
   content/browser/aboutlogins/components/login-filter.css      (content/components/login-filter.css)
   content/browser/aboutlogins/components/login-filter.js       (content/components/login-filter.js)
   content/browser/aboutlogins/components/login-item.css        (content/components/login-item.css)
   content/browser/aboutlogins/components/login-item.js         (content/components/login-item.js)
   content/browser/aboutlogins/components/login-list.css        (content/components/login-list.css)
   content/browser/aboutlogins/components/login-list.js         (content/components/login-list.js)
   content/browser/aboutlogins/components/login-list-item.css   (content/components/login-list-item.css)
   content/browser/aboutlogins/components/login-list-item.js    (content/components/login-list-item.js)