Bug 1592464 - Add Vulnerable Password Alert icon in sidebar list (and tooltip). r=jaws,fluent-reviewers,flod
authorlesleynorton <lnorton@mozilla.com>
Wed, 25 Mar 2020 17:10:09 +0000
changeset 520411 e734d66df767efe23209437c8f0d848c32fccfae
parent 520410 ec542d9a0338166e2442a77b8f052c3b054a7fd7
child 520412 5ab5d42214b1eebab36360184589cfd84749dd60
push id37249
push userdvarga@mozilla.com
push dateWed, 25 Mar 2020 21:39:06 +0000
treeherdermozilla-central@b3c3f7d0f044 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, fluent-reviewers, flod
bugs1592464
milestone76.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 1592464 - Add Vulnerable Password Alert icon in sidebar list (and tooltip). r=jaws,fluent-reviewers,flod Differential Revision: https://phabricator.services.mozilla.com/D68075
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/login-list-item.js
browser/components/aboutlogins/content/components/login-list.css
browser/components/aboutlogins/content/components/login-list.js
browser/components/aboutlogins/content/icons/vulnerable-password.svg
browser/components/aboutlogins/jar.mn
browser/components/aboutlogins/tests/chrome/test_login_list.html
browser/locales/en-US/browser/aboutLogins.ftl
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -116,17 +116,17 @@
       <li class="login-list-item" role="option">
         <div class="favicon-wrapper">
           <img class="favicon" src=""/>
         </div>
         <div class="labels">
           <span class="title" dir="auto"></span>
           <span class="username" dir="ltr"></span>
         </div>
-        <img data-l10n-id="about-logins-list-item-breach-icon" class="list-item-warning-icon" title="" src="chrome://global/skin/icons/warning.svg"/>
+        <img class="alert-icon" title="" src=""/>
       </li>
     </template>
 
     <template id="login-intro-template">
       <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-intro.css">
 
--- a/browser/components/aboutlogins/content/components/login-list-item.js
+++ b/browser/components/aboutlogins/content/components/login-list-item.js
@@ -19,16 +19,17 @@ export default class LoginListItemFactor
     return listItem;
   }
 
   static update(listItem, login) {
     let title = listItem.querySelector(".title");
     let username = listItem.querySelector(".username");
     let favicon = listItem.querySelector(".favicon");
     let faviconWrapper = listItem.querySelector(".favicon-wrapper");
+    let alertIcon = listItem.querySelector(".alert-icon");
 
     if (!login.guid) {
       listItem.id = "new-login-list-item";
       document.l10n.setAttributes(title, "login-list-item-title-new-login");
       document.l10n.setAttributes(
         username,
         "login-list-item-subtitle-new-login"
       );
@@ -56,10 +57,26 @@ export default class LoginListItemFactor
         "login-list-item-subtitle-missing-username"
       );
     }
 
     if (login.faviconDataURI) {
       faviconWrapper.classList.add("hide-default-favicon");
       favicon.src = login.faviconDataURI;
     }
+
+    if (listItem.classList.contains("breached")) {
+      alertIcon.src = "chrome://global/skin/icons/warning.svg";
+      document.l10n.setAttributes(
+        alertIcon,
+        "about-logins-list-item-breach-icon"
+      );
+    } else if (listItem.classList.contains("vulnerable")) {
+      alertIcon.src =
+        "chrome://browser/content/aboutlogins/icons/vulnerable-password.svg";
+
+      document.l10n.setAttributes(
+        alertIcon,
+        "about-logins-list-item-vulnerable-password-icon"
+      );
+    }
   }
 }
--- a/browser/components/aboutlogins/content/components/login-list.css
+++ b/browser/components/aboutlogins/content/components/login-list.css
@@ -173,30 +173,28 @@ ol {
   width: 16px;
 }
 
 .username {
   font-size: 0.85em;
   color: var(--in-content-deemphasized-text);
 }
 
-:not(.breached):not(.vulnerable) > .list-item-warning-icon {
-  display: none;
+.alert-icon {
+  width: 16px;
+  margin-inline-start: 12px;
+  -moz-context-properties: fill;
 }
 
-/* Temporary until bug 1592464 */
-.vulnerable > .list-item-warning-icon {
-  transform: rotateX(180deg);
+.breached > .alert-icon {
+  fill: var(--red-90);
 }
 
-.list-item-warning-icon {
-  -moz-context-properties: fill;
-  fill: var(--red-90);
-  min-width: 16px;
-  margin-inline-start: 12px;
+.vulnerable > .alert-icon {
+  fill: var(--grey-60);
 }
 
 @media (-moz-windows-default-theme: 0) {
   .list-item-warning-icon {
     fill: currentColor;
   }
 }
 
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -93,32 +93,38 @@ export default class LoginList extends H
         listItem,
       });
       fragment.appendChild(listItem);
     }
     this._list.appendChild(fragment);
 
     // Show, hide, and update state of the list items per the applied search filter.
     for (let guid of this._loginGuidsSortedOrder) {
-      let { listItem } = this._logins[guid];
+      let { listItem, login } = this._logins[guid];
 
       if (guid == this._selectedGuid) {
         this._setListItemAsSelected(listItem);
       }
       listItem.classList.toggle(
         "breached",
         !!this._breachesByLoginGUID &&
           this._breachesByLoginGUID.has(listItem.dataset.guid)
       );
       listItem.classList.toggle(
         "vulnerable",
         !!this._vulnerableLoginsByLoginGUID &&
           this._vulnerableLoginsByLoginGUID.has(listItem.dataset.guid) &&
           !listItem.classList.contains("breached")
       );
+      if (
+        listItem.classList.contains("breached") ||
+        listItem.classList.contains("vulnerable")
+      ) {
+        LoginListItemFactory.update(listItem, login);
+      }
       listItem.hidden = !visibleLoginGuids.has(listItem.dataset.guid);
     }
 
     // Re-arrange the login-list-items according to their sort
     for (let i = this._loginGuidsSortedOrder.length - 1; i >= 0; i--) {
       let guid = this._loginGuidsSortedOrder[i];
       let { listItem } = this._logins[guid];
       this._list.insertBefore(
@@ -377,16 +383,20 @@ export default class LoginList extends H
   }
 
   _internalSetMonitorData(internalMemberName, mapByLoginGUID) {
     this[internalMemberName] = mapByLoginGUID;
     if (this[internalMemberName].size === 0) {
       this.render();
       return;
     }
+    for (let [loginGuid] of mapByLoginGUID) {
+      let { login, listItem } = this._logins[loginGuid];
+      LoginListItemFactory.update(listItem, login);
+    }
     const breachedSortOptionElement = this._sortSelect.namedItem("breached");
     breachedSortOptionElement.hidden = false;
     this._sortSelect.selectedIndex = breachedSortOptionElement.index;
     this._applySortAndScrollToTop();
     this._selectFirstVisibleLogin();
   }
 
   _internalUpdateMonitorData(internalMemberName, mapByLoginGUID) {
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/icons/vulnerable-password.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M12 7a4 4 0 0 0-3.86 3H1a1 1 0 0 0 0 2v1a1 1 0 0 0 2 0v-1h1v1a1 1 0 0 0 2 0v-1h2.14A4 4 0 1 0 12 7zm0 6a2 2 0 1 1 2-2 2 2 0 0 1-2 2zM8.4 4.92a1 1 0 0 0 .92.61A1 1 0 0 0 9.7 5.46a1 1 0 0 0 .55-1.31L9.1 1.38a1 1 0 0 0-1.85.76zM5.84 7.53a1 1 0 0 0 .61.21 1 1 0 0 0 .79-.39A1 1 0 0 0 7.06 6L4.68 4.12a1 1 0 1 0-1.22 1.59zM12.78 5.05h.14a1 1 0 0 0 1-.87l.4-3a1 1 0 1 0-2-.26l-.39 3a1 1 0 0 0 .85 1.13z"/>
+</svg>
--- a/browser/components/aboutlogins/jar.mn
+++ b/browser/components/aboutlogins/jar.mn
@@ -19,15 +19,16 @@ browser.jar:
   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.js    (content/components/login-list-item.js)
   content/browser/aboutlogins/components/menu-button.css       (content/components/menu-button.css)
   content/browser/aboutlogins/components/menu-button.js        (content/components/menu-button.js)
   content/browser/aboutlogins/icons/favicon.svg       (content/icons/favicon.svg)
   content/browser/aboutlogins/icons/hide-password.svg (content/icons/hide-password.svg)
+  content/browser/aboutlogins/icons/vulnerable-password.svg (content/icons/vulnerable-password.svg)
   content/browser/aboutlogins/icons/show-password.svg (content/icons/show-password.svg)
   content/browser/aboutlogins/icons/intro-illustration.svg (content/icons/intro-illustration.svg)
   content/browser/aboutlogins/aboutLogins.css   (content/aboutLogins.css)
   content/browser/aboutlogins/aboutLogins.js    (content/aboutLogins.js)
   content/browser/aboutlogins/aboutLogins.html  (content/aboutLogins.html)
   content/browser/aboutlogins/aboutLoginsUtils.js (content/aboutLoginsUtils.js)
   content/browser/aboutlogins/common.css        (content/common.css)
--- a/browser/components/aboutlogins/tests/chrome/test_login_list.html
+++ b/browser/components/aboutlogins/tests/chrome/test_login_list.html
@@ -249,31 +249,31 @@ add_task(async function test_populated_l
 add_task(async function test_breach_indicator() {
   gLoginList.setLogins([TEST_LOGIN_1, TEST_LOGIN_2, Object.assign({}, TEST_LOGIN_3, {password: TEST_LOGIN_1.password})]);
   gLoginList.setBreaches(TEST_BREACHES_MAP);
   let vulnerableLogins = new Map();
   vulnerableLogins.set(TEST_LOGIN_1.guid, true);
   vulnerableLogins.set(TEST_LOGIN_3.guid, true);
   gLoginList.setVulnerableLogins(vulnerableLogins);
   let loginListItems = gLoginList.shadowRoot.querySelectorAll(".login-list-item:not(#new-login-list-item):not([hidden])");
-  let breachWarningIcon = loginListItems[0].querySelector(".list-item-warning-icon");
-  let hiddenWarningIcon = loginListItems[1].querySelector(".list-item-warning-icon");
-  let vulnerableWarningIcon = loginListItems[2].querySelector(".list-item-warning-icon");
+  let alertIcon = loginListItems[0].querySelector(".alert-icon");
   is(loginListItems[0].dataset.guid, TEST_LOGIN_1.guid, "The first login should be TEST_LOGIN_1");
   ok(!loginListItems[0].classList.contains("vulnerable"), "The first login should not have the .vulnerable class");
   ok(loginListItems[0].classList.contains("breached"), "The first login should have the .breached class.");
-  ok(!isHidden(breachWarningIcon), "The warning icon for a breached login should not be hidden.");
+  is(alertIcon.src, "chrome://global/skin/icons/warning.svg", "The alert icon should be the breach warning icon");
   is(loginListItems[1].dataset.guid, TEST_LOGIN_3.guid, "The second login should be TEST_LOGIN_3");
   ok(loginListItems[1].classList.contains("vulnerable"), "The second login should have the .vulnerable class");
   ok(!loginListItems[1].classList.contains("breached"), "The second login should not have the .breached class");
-  ok(!isHidden(hiddenWarningIcon), "The warning icon for a vulnerable login should not be hidden.");
+  alertIcon = loginListItems[1].querySelector(".alert-icon");
+  is(alertIcon.src, "chrome://browser/content/aboutlogins/icons/vulnerable-password.svg", "The alert icon should be the vulnerable password icon");
   is(loginListItems[2].dataset.guid, TEST_LOGIN_2.guid, "The third login should be TEST_LOGIN_2");
+  alertIcon = loginListItems[2].querySelector(".alert-icon");
   ok(!loginListItems[2].classList.contains("vulnerable"), "The third login should not have the .vulnerable class");
   ok(!loginListItems[2].classList.contains("breached"), "The third login should not have the .breached class");
-  ok(isHidden(vulnerableWarningIcon), "The warning icon for a login with a vulnerable password should be visible.");
+  is(alertIcon.src, "chrome://mochitests/content/chrome/browser/components/aboutlogins/tests/chrome/test_login_list.html",  "The alert icon src should be empty");
 });
 
 add_task(async function test_filtered_list() {
   gLoginList.setLogins([TEST_LOGIN_1, TEST_LOGIN_2]);
   let emptySearchText = gLoginList.shadowRoot.querySelector(".empty-search-message");
   ok(isHidden(emptySearchText), "The empty search text should be hidden when there are results in the list");
   is(gLoginList.shadowRoot.querySelectorAll(".login-list-item:not(#new-login-list-item):not([hidden])").length, 2, "Both logins should be visible");
   let countSpan = gLoginList.shadowRoot.querySelector(".count");
--- a/browser/locales/en-US/browser/aboutLogins.ftl
+++ b/browser/locales/en-US/browser/aboutLogins.ftl
@@ -57,16 +57,18 @@ login-list-intro-title = No logins found
 login-list-intro-description = When you save a password in { -brand-product-name }, it will show up here.
 about-logins-login-list-empty-search-title = No logins found
 about-logins-login-list-empty-search-description = There are no results matching your search.
 login-list-item-title-new-login = New Login
 login-list-item-subtitle-new-login = Enter your login credentials
 login-list-item-subtitle-missing-username = (no username)
 about-logins-list-item-breach-icon =
   .title = Breached website
+about-logins-list-item-vulnerable-password-icon =
+  .title = Vulnerable password
 
 ## Introduction screen
 
 login-intro-heading = Looking for your saved logins? Set up { -sync-brand-short-name }.
 about-logins-login-intro-heading-logged-in = No synced logins found.
 login-intro-description = If you saved your logins to { -brand-product-name } on a different device, here’s how to get them here:
 login-intro-instruction-fxa = Create or sign in to your { -fxaccount-brand-name } on the device where your logins are saved
 login-intro-instruction-fxa-settings = Make sure you’ve selected the Logins checkbox in { -sync-brand-short-name } Settings