Backed out 8 changesets (bug 1592467) for multiple bc login related failures. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 27 Mar 2020 06:31:51 +0200
changeset 520695 62b434d3602a043ae16ddb098f3b01c46a883156
parent 520694 0e08a1d7fb078cc36882b737f00da2f48f9349a6
child 520696 e608cbaf20d451fe0dd63e778ffa3f9408b8c43d
push id111257
push userncsoregi@mozilla.com
push dateFri, 27 Mar 2020 04:33:01 +0000
treeherderautoland@62b434d3602a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1592467
milestone76.0a1
backs out7f78c8fb5c6ea19dead7752c068f00d96658e3a0
b353abb5f07fd48e7957653dbc4c05ddfdd1544f
c64620a2abf9294639555543dd83b94408a51cb2
25f9704e31e1412186685e832df8752d427ad6e6
86f9d59bb2216b06a7c59209fc93c1c0c7714fc6
242b4157658fb75206075a8fe522fab70e857d44
464d4a60bb1da1d6be9e6bc73970be2dc320dcb3
b25a1f80fe1d7d17885cf15b02fb30622b4d6bc0
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
Backed out 8 changesets (bug 1592467) for multiple bc login related failures. CLOSED TREE Backed out changeset 7f78c8fb5c6e (bug 1592467) Backed out changeset b353abb5f07f (bug 1592467) Backed out changeset c64620a2abf9 (bug 1592467) Backed out changeset 25f9704e31e1 (bug 1592467) Backed out changeset 86f9d59bb221 (bug 1592467) Backed out changeset 242b4157658f (bug 1592467) Backed out changeset 464d4a60bb1d (bug 1592467) Backed out changeset b25a1f80fe1d (bug 1592467)
browser/components/aboutlogins/AboutLoginsChild.jsm
browser/components/aboutlogins/AboutLoginsParent.jsm
browser/components/aboutlogins/LoginBreaches.jsm
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/login-intro.js
browser/components/aboutlogins/content/components/login-item.css
browser/components/aboutlogins/content/components/login-item.js
browser/components/aboutlogins/content/components/login-list-item.js
browser/components/aboutlogins/content/components/login-list.js
browser/components/aboutlogins/tests/browser/browser.ini
browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js
browser/components/aboutlogins/tests/browser/browser_breachAlertDismissals.js
browser/components/aboutlogins/tests/browser/browser_createLogin.js
browser/components/aboutlogins/tests/browser/browser_openSite.js
browser/components/aboutlogins/tests/browser/head.js
browser/components/aboutlogins/tests/chrome/test_login_item.html
browser/locales/en-US/browser/aboutLogins.ftl
--- a/browser/components/aboutlogins/AboutLoginsChild.jsm
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -41,18 +41,17 @@ class AboutLoginsChild extends JSWindowA
         this.sendAsyncMessage("AboutLogins:Subscribe");
 
         let documentElement = this.document.documentElement;
         documentElement.classList.toggle(
           "official-branding",
           AppConstants.MOZILLA_OFFICIAL
         );
 
-        let win = this.browsingContext.window;
-        let waivedContent = Cu.waiveXrays(win);
+        let waivedContent = Cu.waiveXrays(this.browsingContext.window);
         let that = this;
         let AboutLoginsUtils = {
           doLoginsMatch(loginA, loginB) {
             return LoginHelper.doLoginsMatch(loginA, loginB, {});
           },
           getLoginOrigin(uriString) {
             return LoginHelper.getLoginOrigin(uriString);
           },
@@ -81,20 +80,24 @@ class AboutLoginsChild extends JSWindowA
         };
         waivedContent.AboutLoginsUtils = Cu.cloneInto(
           AboutLoginsUtils,
           waivedContent,
           {
             cloneFunctions: true,
           }
         );
-        let aboutLoginsUtilsReadyEvent = new win.CustomEvent(
-          "AboutLoginsUtilsReady"
+
+        const SUPPORT_URL =
+          Services.urlFormatter.formatURLPref("app.support.baseURL") +
+          "firefox-lockwise";
+        let loginIntro = Cu.waiveXrays(
+          this.document.querySelector("login-intro")
         );
-        win.dispatchEvent(aboutLoginsUtilsReadyEvent);
+        loginIntro.supportURL = SUPPORT_URL;
         break;
       }
       case "AboutLoginsCopyLoginDetail": {
         ClipboardHelper.copyString(event.detail);
         break;
       }
       case "AboutLoginsCreateLogin": {
         this.sendAsyncMessage("AboutLogins:CreateLogin", {
@@ -103,16 +106,22 @@ class AboutLoginsChild extends JSWindowA
         break;
       }
       case "AboutLoginsDeleteLogin": {
         this.sendAsyncMessage("AboutLogins:DeleteLogin", {
           login: event.detail,
         });
         break;
       }
+      case "AboutLoginsDismissBreachAlert": {
+        this.sendAsyncMessage("AboutLogins:DismissBreachAlert", {
+          login: event.detail,
+        });
+        break;
+      }
       case "AboutLoginsGetHelp": {
         this.sendAsyncMessage("AboutLogins:GetHelp");
         break;
       }
       case "AboutLoginsHideFooter": {
         this.sendAsyncMessage("AboutLogins:HideFooter");
         break;
       }
@@ -204,19 +213,16 @@ class AboutLoginsChild extends JSWindowA
       case "AboutLogins:Setup":
         let waivedContent = Cu.waiveXrays(this.browsingContext.window);
         waivedContent.AboutLoginsUtils.masterPasswordEnabled =
           message.data.masterPasswordEnabled;
         waivedContent.AboutLoginsUtils.passwordRevealVisible =
           message.data.passwordRevealVisible;
         waivedContent.AboutLoginsUtils.importVisible =
           message.data.importVisible;
-        waivedContent.AboutLoginsUtils.supportBaseURL = Services.urlFormatter.formatURLPref(
-          "app.support.baseURL"
-        );
         this.sendToContent("Setup", message.data);
         break;
       default:
         this.passMessageDataToContent(message);
     }
   }
 
   passMessageDataToContent(message) {
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -240,16 +240,27 @@ class AboutLoginsParent extends JSWindow
         }
         break;
       }
       case "AboutLogins:DeleteLogin": {
         let login = LoginHelper.vanillaObjectToLogin(message.data.login);
         Services.logins.removeLogin(login);
         break;
       }
+      case "AboutLogins:DismissBreachAlert": {
+        const login = message.data.login;
+
+        await LoginBreaches.recordDismissal(login.guid);
+        const logins = await AboutLogins.getAllLogins();
+        const breachesByLoginGUID = await LoginBreaches.getPotentialBreachesByLoginGUID(
+          logins
+        );
+        this.sendAsyncMessage("AboutLogins:SetBreaches", breachesByLoginGUID);
+        break;
+      }
       case "AboutLogins:HideFooter": {
         Services.prefs.setBoolPref(HIDE_MOBILE_FOOTER_PREF, true);
         break;
       }
       case "AboutLogins:SortChanged": {
         Services.prefs.setCharPref("signon.management.page.sort", message.data);
         break;
       }
@@ -555,40 +566,16 @@ var AboutLogins = {
         break;
       }
       case "modifyLogin": {
         subject.QueryInterface(Ci.nsIArrayExtensions);
         const login = convertSubjectToLogin(subject.GetElementAt(1));
         if (!login) {
           return;
         }
-
-        if (BREACH_ALERTS_ENABLED) {
-          let breachesForThisLogin = await LoginBreaches.getPotentialBreachesByLoginGUID(
-            [login]
-          );
-          let breachData = breachesForThisLogin.size
-            ? breachesForThisLogin.get(login.guid)
-            : false;
-          this.messageSubscribers(
-            "AboutLogins:UpdateBreaches",
-            new Map([[login.guid, breachData]])
-          );
-          if (VULNERABLE_PASSWORDS_ENABLED) {
-            let vulnerablePasswordsForThisLogin = await LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID(
-              [login]
-            );
-            let isLoginVulnerable = !!vulnerablePasswordsForThisLogin.size;
-            this.messageSubscribers(
-              "AboutLogins:UpdateVulnerableLogins",
-              new Map([[login.guid, isLoginVulnerable]])
-            );
-          }
-        }
-
         this.messageSubscribers("AboutLogins:LoginModified", login);
         break;
       }
       case "removeLogin": {
         const login = convertSubjectToLogin(subject);
         if (!login) {
           return;
         }
--- a/browser/components/aboutlogins/LoginBreaches.jsm
+++ b/browser/components/aboutlogins/LoginBreaches.jsm
@@ -20,16 +20,24 @@ XPCOMUtils.defineLazyModuleGetters(this,
   LoginHelper: "resource://gre/modules/LoginHelper.jsm",
   RemoteSettings: "resource://services-settings/remote-settings.js",
   RemoteSettingsClient: "resource://services-settings/RemoteSettingsClient.jsm",
 });
 
 this.LoginBreaches = {
   REMOTE_SETTINGS_COLLECTION: "fxmonitor-breaches",
 
+  async recordDismissal(loginGuid) {
+    await Services.logins.initializationPromise;
+    const storageJSON =
+      Services.logins.wrappedJSObject._storage.wrappedJSObject;
+
+    return storageJSON.recordBreachAlertDismissal(loginGuid);
+  },
+
   async update(breaches = null) {
     const logins = await LoginHelper.getAllUserFacingLogins();
     await this.getPotentialBreachesByLoginGUID(logins, breaches);
   },
 
   /**
    * Return a Map of login GUIDs to a potential breach affecting that login
    * by considering only breaches affecting passwords.
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -155,29 +155,22 @@
         <div class="column">
           <div class="error-message">
             <span class="error-message-text" data-l10n-id="about-logins-error-message-default"></span>
             <span class="error-message-link">
               <a data-l10n-name="duplicate-link" href=""></a>
             </span>
           </div>
           <div class="breach-alert">
-            <h3 class="alert-title" data-l10n-id="about-logins-breach-alert-title"></h3>
-            <img class="alert-icon" src="chrome://global/skin/icons/warning.svg"/>
-            <span class="alert-date" data-l10n-id="about-logins-breach-alert-date" data-l10n-args='{"date": 0}'></span>
-            <span class="alert-text" data-l10n-id="breach-alert-text"></span>
-            <a class="alert-link" data-l10n-id="about-logins-breach-alert-link" data-l10n-args='{"hostname": ""}' href="#" rel="noreferrer" target="_blank"></a>
-            <a class="alert-learn-more-link" data-l10n-id="about-logins-breach-alert-learn-more-link" href="#" rel="noreferrer" target="_blank"></a>
-          </div>
-          <div class="vulnerable-alert">
-            <h3 class="alert-title" data-l10n-id="about-logins-vulnerable-alert-title"></h3>
-            <img class="alert-icon" src="chrome://browser/content/aboutlogins/icons/vulnerable-password.svg"/>
-            <span class="alert-text" data-l10n-id="about-logins-vulnerable-alert-text"></span>
-            <a class="alert-link" data-l10n-id="about-logins-vulnerable-alert-link" data-l10n-args='{"hostname": ""}' href="#" rel="noreferrer" target="_blank"></a>
-            <a class="alert-learn-more-link" data-l10n-id="about-logins-vulnerable-alert-learn-more-link" href="#" rel="noreferrer" target="_blank"></a>
+            <img class="breach-icon" src="chrome://global/skin/icons/warning.svg"/>
+            <span class="breach-alert-text" data-l10n-id="breach-alert-text"></span>
+            <a class="breach-alert-link" data-l10n-id="breach-alert-link" href="#" rel="noreferrer" target="_blank"></a>
+            <button class="dismiss-breach-alert" data-l10n-id="breach-alert-dismiss">
+              <img class="dismiss-breach-alert-icon" src="chrome://global/skin/icons/close.svg"/>
+            </button>
           </div>
           <div class="header">
             <div class="login-item-favicon-wrapper">
               <img class="login-item-favicon" src=""/>
             </div>
             <h2 class="title">
               <span class="login-item-title"></span>
               <span class="new-login-title" data-l10n-id="login-item-new-login-title"></span>
--- a/browser/components/aboutlogins/content/components/login-intro.js
+++ b/browser/components/aboutlogins/content/components/login-intro.js
@@ -10,45 +10,41 @@ export default class LoginIntro extends 
 
     let loginIntroTemplate = document.querySelector("#login-intro-template");
     let shadowRoot = this.attachShadow({ mode: "open" });
     document.l10n.connectRoot(shadowRoot);
     shadowRoot.appendChild(loginIntroTemplate.content.cloneNode(true));
 
     this._importText = shadowRoot.querySelector(".intro-import-text");
     this._importText.addEventListener("click", this);
-
-    this.addEventListener("AboutLoginsUtilsReady", this);
   }
 
   focus() {
     let helpLink = this.shadowRoot.querySelector(".intro-help-link");
     helpLink.focus();
   }
 
   handleEvent(event) {
-    if (event.type == "AboutLoginsUtilsReady") {
-      let supportURL =
-        window.AboutLoginsUtils.supportBaseURL + "firefox-lockwise";
-      this.shadowRoot
-        .querySelector(".intro-help-link")
-        .setAttribute("href", supportURL);
-    } else if (
+    if (
       event.currentTarget.classList.contains("intro-import-text") &&
       event.target.localName == "a"
     ) {
       document.dispatchEvent(
         new CustomEvent("AboutLoginsImport", {
           bubbles: true,
         })
       );
     }
     event.preventDefault();
   }
 
+  set supportURL(val) {
+    this.shadowRoot.querySelector(".intro-help-link").setAttribute("href", val);
+  }
+
   updateState(syncState) {
     let l10nId = syncState.loggedIn
       ? "about-logins-login-intro-heading-logged-in"
       : "login-intro-heading";
     document.l10n.setAttributes(
       this.shadowRoot.querySelector(".heading"),
       l10nId
     );
--- a/browser/components/aboutlogins/content/components/login-item.css
+++ b/browser/components/aboutlogins/content/components/login-item.css
@@ -290,86 +290,76 @@ input[name="password"] {
   fill: currentColor;
   fill-opacity: 0.8;
 }
 
 .login-item-favicon-wrapper.hide-default-favicon {
   background-image: none;
 }
 
-.vulnerable-alert,
 .breach-alert {
-  border-radius: var(--panel-border-radius);
+  border-radius: 8px;
   border: 1px solid var(--in-content-border-color);
-  box-shadow: 0 2px 8px 0 var(--grey-90-a10);
+  background-color: var(--yellow-10);
+  color: #0C0C0D;
+  box-shadow: 0 2px 8px 0 rgba(12,12,13,0.1);
   font-size: .9em;
   font-weight: 300;
   line-height: 1.4;
   padding-block: 12px;
-  padding-inline-start: 64px;
-  padding-inline-end: 32px;
+  padding-inline-start: 36px;
+  padding-inline-end: 92px;
   margin-block-end: 40px;
   position: relative;
 }
 
-.breach-alert {
-  background-color: var(--red-70);
-  color: #fff;
-}
-
-.vulnerable-alert {
-  background-color: var(--in-content-box-background);
-  color: var(--in-content-text-color);
-}
-
-.alert-title {
-  font-size: 22px;
-  font-weight: normal;
-  line-height: 1em;
-  margin-top: 0;
-  margin-bottom: 12px;
-}
-
-.alert-date {
-  display: block;
-  font-weight: 600;
-}
-
-.alert-link:visited,
-.alert-link {
+.breach-alert-link {
+  color: inherit;
+  text-decoration: underline;
   font-weight: 600;
 }
 
-.breach-alert > .alert-link:visited,
-.breach-alert > .alert-link {
-  color: inherit;
-  text-decoration: underline;
+.breach-icon {
+  position: absolute;
+  inset-block-start: 10px;
+  inset-inline-start: 10px;
+  -moz-context-properties: fill;
+  fill: var(--red-90);
 }
 
-.alert-icon {
+.dismiss-breach-alert {
+  border: none;
+  padding: 0;
+  margin: 0;
   position: absolute;
-  inset-block-start: 16px;
-  inset-inline-start: 32px;
-  -moz-context-properties: fill;
-  fill: currentColor;
-  width: 24px;
+  inset-inline-end: 12px;
+  inset-block-start: 12px;
+  min-width: auto;
+  min-height: auto;
+  line-height: 0;
 }
 
-.alert-learn-more-link:hover,
-.alert-learn-more-link:visited,
-.alert-learn-more-link {
-  position: absolute;
-  inset-block-start: 16px;
-  inset-inline-end: 32px;
-  color: inherit;
-  font-size: 13px;
+.dismiss-breach-alert {
+  background-color: transparent;
+}
+
+.dismiss-breach-alert:enabled:hover {
+  background-color: var(--grey-90-a20);
 }
 
-.vulnerable-alert > .alert-learn-more-link {
-  color: var(--in-content-deemphasized-text);
+.dismiss-breach-alert:enabled:hover:active {
+  background-color: var(--grey-90-a30);
+}
+
+.dismiss-breach-alert-icon {
+  -moz-context-properties: fill, fill-opacity;
+  fill-opacity: 0;
+  fill: currentColor;
+  width: 16px;
+  height: 16px;
 }
 
 .error-message {
   color: #fff;
   background-color: var(--red-60);
   border: 1px solid transparent;
   padding-block: 6px;
   display: inline-block;
@@ -391,29 +381,28 @@ input[name="password"] {
 .error-message-link > a:hover,
 .error-message-link > a:hover:active {
   color: currentColor;
   text-decoration: underline;
   font-weight: 600;
 }
 
 @media (-moz-windows-default-theme: 0) {
-  .alert-icon {
+  .breach-icon {
     fill: currentColor;
   }
 }
 
 @supports -moz-bool-pref("browser.in-content.dark-mode") {
   @media (prefers-color-scheme: dark) {
     :host {
       --reveal-checkbox-opacity: .8;
       --reveal-checkbox-opacity-hover: 1;
       --reveal-checkbox-opacity-active: .6;
       --success-color: #86DE74;
       --edit-delete-button-color: #cfcfd1;
     }
 
-    .vulnerable-alert,
     .breach-alert {
       box-shadow: 0 2px 8px 0 rgba(249,249,250,0.1);
     }
   }
 }
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -76,36 +76,31 @@ export default class LoginItem extends H
     this._faviconWrapper = this.shadowRoot.querySelector(
       ".login-item-favicon-wrapper"
     );
     this._title = this.shadowRoot.querySelector(".login-item-title");
     this._timeCreated = this.shadowRoot.querySelector(".time-created");
     this._timeChanged = this.shadowRoot.querySelector(".time-changed");
     this._timeUsed = this.shadowRoot.querySelector(".time-used");
     this._breachAlert = this.shadowRoot.querySelector(".breach-alert");
-    this._breachAlertLink = this._breachAlert.querySelector(".alert-link");
-    this._breachAlertDate = this._breachAlert.querySelector(".alert-date");
-    this._breachAlertLearnMoreLink = this._breachAlert.querySelector(
-      ".alert-learn-more-link"
+    this._breachAlertLink = this._breachAlert.querySelector(
+      ".breach-alert-link"
     );
-    this._vulnerableAlert = this.shadowRoot.querySelector(".vulnerable-alert");
-    this._vulnerableAlertLink = this._vulnerableAlert.querySelector(
-      ".alert-link"
-    );
-    this._vulnerableAlertLearnMoreLink = this._vulnerableAlert.querySelector(
-      ".alert-learn-more-link"
+    this._dismissBreachAlert = this.shadowRoot.querySelector(
+      ".dismiss-breach-alert"
     );
 
     this.render();
 
     this._breachAlertLink.addEventListener("click", this);
     this._cancelButton.addEventListener("click", this);
     this._copyPasswordButton.addEventListener("click", this);
     this._copyUsernameButton.addEventListener("click", this);
     this._deleteButton.addEventListener("click", this);
+    this._dismissBreachAlert.addEventListener("click", this);
     this._editButton.addEventListener("click", this);
     this._errorMessageLink.addEventListener("click", this);
     this._form.addEventListener("submit", this);
     this._originInput.addEventListener("blur", this);
     this._originInput.addEventListener("click", this);
     this._originInput.addEventListener("mousedown", this, true);
     this._originInput.addEventListener("auxclick", this);
     this._originDisplayInput.addEventListener("click", this);
@@ -114,30 +109,26 @@ export default class LoginItem extends H
     window.addEventListener("AboutLoginsLoadInitialFavicon", this);
     window.addEventListener("AboutLoginsLoginSelected", this);
     window.addEventListener("AboutLoginsShowBlankLogin", this);
   }
 
   focus() {
     if (!this._breachAlert.hidden) {
       this._breachAlertLink.focus();
-    } else if (!this._vulnerableAlert.hidden) {
-      this._vulnerableAlertLink.focus();
     } else if (!this._editButton.disabled) {
       this._editButton.focus();
     } else if (!this._deleteButton.disabled) {
       this._deleteButton.focus();
     } else {
       this._originInput.focus();
     }
   }
 
-  async render(
-    { onlyUpdateErrorsAndAlerts } = { onlyUpdateErrorsAndAlerts: false }
-  ) {
+  async render() {
     if (this._error) {
       if (this._error.errorMessage.includes("This login already exists")) {
         document.l10n.setAttributes(
           this._errorMessageLink,
           "about-logins-error-message-duplicate-login-with-link",
           {
             loginTitle: this._error.login.title,
           }
@@ -151,57 +142,17 @@ export default class LoginItem extends H
       }
     }
     this._errorMessage.hidden = !this._error;
 
     this._breachAlert.hidden =
       !this._breachesMap || !this._breachesMap.has(this._login.guid);
     if (!this._breachAlert.hidden) {
       const breachDetails = this._breachesMap.get(this._login.guid);
-      this._breachAlertLearnMoreLink.href = breachDetails.breachAlertURL;
-      this._breachAlertLink.href = this._login.origin;
-      document.l10n.setAttributes(
-        this._breachAlertLink,
-        "about-logins-breach-alert-link",
-        { hostname: this._login.displayOrigin }
-      );
-      if (breachDetails.BreachDate) {
-        let breachDate = new Date(breachDetails.BreachDate);
-        this._breachAlertDate.hidden = isNaN(breachDate);
-        if (!isNaN(breachDate)) {
-          document.l10n.setAttributes(
-            this._breachAlertDate,
-            "about-logins-breach-alert-date",
-            {
-              date: breachDate.getTime(),
-            }
-          );
-        }
-      }
-    }
-    this._vulnerableAlert.hidden =
-      !this._vulnerableLoginsMap ||
-      !this._vulnerableLoginsMap.has(this._login.guid) ||
-      !this._breachAlert.hidden;
-    if (!this._vulnerableAlert.hidden) {
-      this._vulnerableAlertLink.href = this._login.origin;
-      document.l10n.setAttributes(
-        this._vulnerableAlertLink,
-        "about-logins-vulnerable-alert-link",
-        {
-          hostname: this._login.displayOrigin,
-        }
-      );
-      this._vulnerableAlertLearnMoreLink.setAttribute(
-        "href",
-        window.AboutLoginsUtils.supportBaseURL + "lockwise-alerts"
-      );
-    }
-    if (onlyUpdateErrorsAndAlerts) {
-      return;
+      this._breachAlertLink.href = breachDetails.breachAlertURL;
     }
     document.l10n.setAttributes(this._timeCreated, "login-item-time-created", {
       timeCreated: this._login.timeCreated || "",
     });
     document.l10n.setAttributes(this._timeChanged, "login-item-time-changed", {
       timeChanged: this._login.timePasswordChanged || "",
     });
     document.l10n.setAttributes(this._timeUsed, "login-item-time-used", {
@@ -285,33 +236,42 @@ export default class LoginItem extends H
     this._internalUpdateMonitorData(
       "_vulnerableLoginsMap",
       vulnerableLoginsByLoginGUID
     );
   }
 
   _internalSetMonitorData(internalMemberName, mapByLoginGUID) {
     this[internalMemberName] = mapByLoginGUID;
-    this.render({ onlyUpdateErrorsAndAlerts: true });
+    this.render();
   }
 
   _internalUpdateMonitorData(internalMemberName, mapByLoginGUID) {
     if (!this[internalMemberName]) {
       this[internalMemberName] = new Map();
     }
     for (const [guid, data] of [...mapByLoginGUID]) {
-      if (data) {
-        this[internalMemberName].set(guid, data);
-      } else {
-        this[internalMemberName].delete(guid);
-      }
+      this[internalMemberName].set(guid, data);
     }
     this._internalSetMonitorData(internalMemberName, this[internalMemberName]);
   }
 
+  dismissBreachAlert() {
+    document.dispatchEvent(
+      new CustomEvent("AboutLoginsDismissBreachAlert", {
+        bubbles: true,
+        detail: this._login,
+      })
+    );
+    this._recordTelemetryEvent({
+      object: "existing_login",
+      method: "dismiss_breach_alert",
+    });
+  }
+
   showLoginItemError(error) {
     this._error = error;
     this.render();
   }
 
   async handleEvent(event) {
     switch (event.type) {
       case "AboutLoginsInitialLoginSelected": {
@@ -453,16 +413,20 @@ export default class LoginItem extends H
               new CustomEvent("AboutLoginsDeleteLogin", {
                 bubbles: true,
                 detail: this._login,
               })
             );
           });
           return;
         }
+        if (classList.contains("dismiss-breach-alert")) {
+          this.dismissBreachAlert();
+          return;
+        }
         if (classList.contains("edit-button")) {
           let masterPasswordAuth = await new Promise(resolve => {
             window.AboutLoginsUtils.promptForMasterPassword(
               resolve,
               "about-logins-edit-login-os-auth-dialog-message"
             );
           });
           if (!masterPasswordAuth) {
@@ -491,25 +455,21 @@ export default class LoginItem extends H
               cancelable: true,
             })
           );
           return;
         }
         if (classList.contains("origin-input")) {
           this._handleOriginClick();
         }
-        if (classList.contains("alert-link")) {
-          if (event.currentTarget.closest(".breach-alert")) {
-            this._recordTelemetryEvent({
-              object: "existing_login",
-              method: "learn_more_breach",
-            });
-          } else if (event.currentTarget.closest(".vulnerable-alert")) {
-            // TODO: Add telemetry event
-          }
+        if (classList.contains("breach-alert-link")) {
+          this._recordTelemetryEvent({
+            object: "existing_login",
+            method: "learn_more_breach",
+          });
         }
         break;
       }
       case "submit": {
         // Prevent page navigation form submit behavior.
         event.preventDefault();
         if (!this._isFormValid({ reportErrors: true })) {
           return;
--- a/browser/components/aboutlogins/content/components/login-list-item.js
+++ b/browser/components/aboutlogins/content/components/login-list-item.js
@@ -72,13 +72,11 @@ export default class LoginListItemFactor
     } 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"
       );
-    } else {
-      alertIcon.src = "";
     }
   }
 }
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -383,54 +383,41 @@ export default class LoginList extends H
 
   updateVulnerableLogins(vulnerableLoginsByLoginGUID) {
     this._internalUpdateMonitorData(
       "_vulnerableLoginsByLoginGUID",
       vulnerableLoginsByLoginGUID
     );
   }
 
-  _internalSetMonitorData(
-    internalMemberName,
-    mapByLoginGUID,
-    updateSortAndSelectedLogin = true
-  ) {
+  _internalSetMonitorData(internalMemberName, mapByLoginGUID) {
     this[internalMemberName] = mapByLoginGUID;
-    if (this[internalMemberName].size) {
-      for (let [loginGuid] of mapByLoginGUID) {
-        let { login, listItem } = this._logins[loginGuid];
-        LoginListItemFactory.update(listItem, login);
-      }
-      if (updateSortAndSelectedLogin) {
-        const alertsSortOptionElement = this._sortSelect.namedItem("alerts");
-        alertsSortOptionElement.hidden = false;
-        this._sortSelect.selectedIndex = alertsSortOptionElement.index;
-        this._applySortAndScrollToTop();
-        this._selectFirstVisibleLogin();
-      }
+    if (this[internalMemberName].size === 0) {
+      this.render();
+      return;
     }
-    this.render();
+    for (let [loginGuid] of mapByLoginGUID) {
+      let { login, listItem } = this._logins[loginGuid];
+      LoginListItemFactory.update(listItem, login);
+    }
+    const alertsSortOptionElement = this._sortSelect.namedItem("alerts");
+    alertsSortOptionElement.hidden = false;
+    this._sortSelect.selectedIndex = alertsSortOptionElement.index;
+    this._applySortAndScrollToTop();
+    this._selectFirstVisibleLogin();
   }
 
   _internalUpdateMonitorData(internalMemberName, mapByLoginGUID) {
     if (!this[internalMemberName]) {
       this[internalMemberName] = new Map();
     }
     for (const [guid, data] of [...mapByLoginGUID]) {
-      if (data) {
-        this[internalMemberName].set(guid, data);
-      } else {
-        this[internalMemberName].delete(guid);
-      }
+      this[internalMemberName].set(guid, data);
     }
-    this._internalSetMonitorData(
-      internalMemberName,
-      this[internalMemberName],
-      false
-    );
+    this._internalSetMonitorData(internalMemberName, this[internalMemberName]);
   }
 
   setSortDirection(sortDirection) {
     this._sortSelect.value = sortDirection;
     this._applySortAndScrollToTop();
     this._selectFirstVisibleLogin();
   }
 
--- a/browser/components/aboutlogins/tests/browser/browser.ini
+++ b/browser/components/aboutlogins/tests/browser/browser.ini
@@ -3,17 +3,18 @@ support-files =
   head.js
 prefs =
   signon.management.page.vulnerable-passwords.enabled=true
 
 # Run first so content events from previous tests won't trickle in.
 # Skip ASAN and debug since waiting for content events is already slow.
 [browser_aaa_eventTelemetry_run_first.js]
 skip-if = asan || ccov || debug # bug 1605494 is more prevalent on linux
-[browser_alertDismissedAfterChangingPassword.js]
+[browser_breachAlertDismissals.js]
+skip-if = asan || debug || verify # bug 1574023
 [browser_breachAlertShowingForAddedLogin.js]
 [browser_confirmDeleteDialog.js]
 [browser_contextmenuFillLogins.js]
 [browser_copyToClipboardButton.js]
 [browser_createLogin.js]
 [browser_deleteLogin.js]
 [browser_dismissFooter.js]
 [browser_fxAccounts.js]
deleted file mode 100644
--- a/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-EXPECTED_BREACH = {
-  AddedDate: "2018-12-20T23:56:26Z",
-  BreachDate: "2018-12-16",
-  Domain: "breached.example.com",
-  Name: "Breached",
-  PwnCount: 1643100,
-  DataClasses: ["Email addresses", "Usernames", "Passwords", "IP addresses"],
-  _status: "synced",
-  id: "047940fe-d2fd-4314-b636-b4a952ee0043",
-  last_modified: "1541615610052",
-  schema: "1541615609018",
-};
-
-let VULNERABLE_TEST_LOGIN2 = new nsLoginInfo(
-  "https://2.example.com",
-  "https://2.example.com",
-  null,
-  "user2",
-  "pass3",
-  "username",
-  "password"
-);
-
-add_task(async function setup() {
-  TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
-  VULNERABLE_TEST_LOGIN2 = await addLogin(VULNERABLE_TEST_LOGIN2);
-  TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
-  await BrowserTestUtils.openNewForegroundTab({
-    gBrowser,
-    url: "about:logins",
-  });
-  registerCleanupFunction(() => {
-    BrowserTestUtils.removeTab(gBrowser.selectedTab);
-    Services.logins.removeAllLogins();
-  });
-});
-
-add_task(async function test_added_login_shows_breach_warning() {
-  let browser = gBrowser.selectedBrowser;
-  await SpecialPowers.spawn(
-    browser,
-    [[TEST_LOGIN1.guid, VULNERABLE_TEST_LOGIN2.guid, TEST_LOGIN3.guid]],
-    async ([regularLoginGuid, vulnerableLoginGuid, breachedLoginGuid]) => {
-      let { OSKeyStoreTestUtils } = ChromeUtils.import(
-        "resource://testing-common/OSKeyStoreTestUtils.jsm"
-      );
-      let loginList = Cu.waiveXrays(
-        content.document.querySelector("login-list")
-      );
-      let { listItem: regularListItem } = loginList._logins[regularLoginGuid];
-      ok(
-        !regularListItem.classList.contains("breached") &&
-          !regularListItem.classList.contains("vulnerable"),
-        "regular login should not be marked breached or vulnerable: " +
-          regularLoginGuid.className
-      );
-      let { listItem: vulnerableListItem } = loginList._logins[
-        vulnerableLoginGuid
-      ];
-      ok(
-        !vulnerableListItem.classList.contains("breached") &&
-          vulnerableListItem.classList.contains("vulnerable"),
-        "vulnerable login should be marked vulnerable: " +
-          vulnerableListItem.className
-      );
-      let { listItem: breachedListItem } = loginList._logins[breachedLoginGuid];
-      ok(
-        breachedListItem.classList.contains("breached") &&
-          !breachedListItem.classList.contains("vulnerable"),
-        "breached login should be marked breached: " +
-          breachedListItem.className
-      );
-      if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
-        info(
-          "leaving test early since the remaining part of the test requires 'edit' mode which requires 'oskeystore' login"
-        );
-        return;
-      }
-
-      // Change the password on the breached login and check that the
-      // login is no longer marked as breached. The vulnerable login
-      // should still be marked as vulnerable afterwards.
-      breachedListItem.click();
-      let loginItem = Cu.waiveXrays(
-        content.document.querySelector("login-item")
-      );
-      await ContentTaskUtils.waitForCondition(() => {
-        return loginItem._login && loginItem._login.guid == breachedLoginGuid;
-      }, "waiting for breached login to get selected");
-      ok(
-        !ContentTaskUtils.is_hidden(
-          loginItem.shadowRoot.querySelector(".breach-alert")
-        ),
-        "the breach alert should be visible"
-      );
-    }
-  );
-
-  let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({
-    loginResult: true,
-  });
-  await SpecialPowers.spawn(browser, [], () => {
-    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
-    loginItem.shadowRoot.querySelector(".edit-button").click();
-  });
-  await reauthObserved;
-  await SpecialPowers.spawn(
-    browser,
-    [[TEST_LOGIN1.guid, VULNERABLE_TEST_LOGIN2.guid, TEST_LOGIN3.guid]],
-    async ([regularLoginGuid, vulnerableLoginGuid, breachedLoginGuid]) => {
-      let loginList = Cu.waiveXrays(
-        content.document.querySelector("login-list")
-      );
-      let loginItem = Cu.waiveXrays(
-        content.document.querySelector("login-item")
-      );
-      await ContentTaskUtils.waitForCondition(
-        () => loginItem.dataset.editing == "true",
-        "waiting for login-item to enter edit mode"
-      );
-      let passwordInput = loginItem.shadowRoot.querySelector(
-        "input[type='password']"
-      );
-      const CHANGED_PASSWORD_VALUE = "changedPassword";
-      passwordInput.value = CHANGED_PASSWORD_VALUE;
-      let saveChangesButton = loginItem.shadowRoot.querySelector(
-        ".save-changes-button"
-      );
-      saveChangesButton.click();
-
-      await ContentTaskUtils.waitForCondition(() => {
-        return (
-          loginList._logins[breachedLoginGuid].login.password ==
-          CHANGED_PASSWORD_VALUE
-        );
-      }, "waiting for stored login to get updated");
-
-      ok(
-        ContentTaskUtils.is_hidden(
-          loginItem.shadowRoot.querySelector(".breach-alert")
-        ),
-        "the breach alert should be hidden now"
-      );
-
-      let { listItem: breachedListItem } = loginList._logins[breachedLoginGuid];
-      let { listItem: vulnerableListItem } = loginList._logins[
-        vulnerableLoginGuid
-      ];
-      ok(
-        !breachedListItem.classList.contains("breached") &&
-          !breachedListItem.classList.contains("vulnerable"),
-        "the originally breached login should no longer be marked as breached"
-      );
-      ok(
-        !vulnerableListItem.classList.contains("breached") &&
-          vulnerableListItem.classList.contains("vulnerable"),
-        "vulnerable login should still be marked vulnerable: " +
-          vulnerableListItem.className
-      );
-
-      // Change the password on the vulnerable login and check that the
-      // login is no longer marked as vulnerable.
-      vulnerableListItem.click();
-      await ContentTaskUtils.waitForCondition(() => {
-        return loginItem._login && loginItem._login.guid == vulnerableLoginGuid;
-      }, "waiting for vulnerable login to get selected");
-      ok(
-        !ContentTaskUtils.is_hidden(
-          loginItem.shadowRoot.querySelector(".vulnerable-alert")
-        ),
-        "the vulnerable alert should be visible"
-      );
-      loginItem.shadowRoot.querySelector(".edit-button").click();
-      await ContentTaskUtils.waitForCondition(
-        () => loginItem.dataset.editing == "true",
-        "waiting for login-item to enter edit mode"
-      );
-      passwordInput = loginItem.shadowRoot.querySelector(
-        "input[type='password']"
-      );
-      passwordInput.value = CHANGED_PASSWORD_VALUE;
-      saveChangesButton.click();
-
-      await ContentTaskUtils.waitForCondition(() => {
-        return (
-          loginList._logins[vulnerableLoginGuid].login.password ==
-          CHANGED_PASSWORD_VALUE
-        );
-      }, "waiting for stored login to get updated");
-
-      ok(
-        ContentTaskUtils.is_hidden(
-          loginItem.shadowRoot.querySelector(".vulnerable-alert")
-        ),
-        "the vulnerable alert should be hidden now"
-      );
-      is(
-        vulnerableListItem.querySelector(".alert-icon").src,
-        "",
-        ".alert-icon for the vulnerable list item should have its source removed"
-      );
-      vulnerableListItem = loginList._logins[vulnerableLoginGuid].listItem;
-      ok(
-        !vulnerableListItem.classList.contains("breached") &&
-          !vulnerableListItem.classList.contains("vulnerable"),
-        "vulnerable login should no longer be marked vulnerable: " +
-          vulnerableListItem.className
-      );
-    }
-  );
-});
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser_breachAlertDismissals.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+EXPECTED_BREACH = {
+  AddedDate: "2018-12-20T23:56:26Z",
+  BreachDate: "2018-12-16",
+  Domain: "breached.example.com",
+  Name: "Breached",
+  PwnCount: 1643100,
+  DataClasses: ["Email addresses", "Usernames", "Passwords", "IP addresses"],
+  _status: "synced",
+  id: "047940fe-d2fd-4314-b636-b4a952ee0043",
+  last_modified: "1541615610052",
+  schema: "1541615609018",
+};
+
+add_task(async function setup() {
+  Services.prefs.setCharPref("signon.management.page.sort", "last-changed");
+  TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
+  TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
+  await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    url: "about:logins",
+  });
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("signon.management.page.sort");
+    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+    Services.logins.removeAllLogins();
+  });
+});
+
+add_task(async function test_show_login() {
+  let browser = gBrowser.selectedBrowser;
+  await SpecialPowers.spawn(browser, [], async () => {
+    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+    let breachAlert = loginItem.shadowRoot.querySelector(".breach-alert");
+    let breachAlertVisible = await ContentTaskUtils.waitForCondition(() => {
+      return !breachAlert.hidden;
+    }, "Waiting for breach alert to be visible");
+    ok(
+      breachAlertVisible,
+      "Breach alert should be visible for a breached login."
+    );
+
+    let breachAlertDismissalButton = breachAlert.querySelector(
+      ".dismiss-breach-alert"
+    );
+    breachAlertDismissalButton.click();
+
+    let breachAlertDismissed = await ContentTaskUtils.waitForCondition(() => {
+      return breachAlert.hidden;
+    }, "Waiting for breach alert to be dismissed");
+    ok(
+      breachAlertDismissed,
+      "Breach alert should not be visible after alert dismissal."
+    );
+  });
+});
--- a/browser/components/aboutlogins/tests/browser/browser_createLogin.js
+++ b/browser/components/aboutlogins/tests/browser/browser_createLogin.js
@@ -184,20 +184,16 @@ add_task(async function test_create_logi
         login.password,
         "testpass1",
         "Stored login should have password provided during creation"
       );
 
       let usernameInput = loginItem.shadowRoot.querySelector(
         "input[name='username']"
       );
-      await ContentTaskUtils.waitForCondition(
-        () => usernameInput.placeholder,
-        "waiting for placeholder to get set"
-      );
       ok(
         usernameInput.placeholder,
         "there should be a placeholder on the username input when not in edit mode"
       );
     });
 
     if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
       continue;
@@ -246,23 +242,19 @@ add_task(async function test_create_logi
     info("waiting for login to get modified in storage");
     await storageChangedPromised;
     info("login modified in storage");
 
     await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => {
       let loginList = Cu.waiveXrays(
         content.document.querySelector("login-list")
       );
-      let login;
-      await ContentTaskUtils.waitForCondition(() => {
-        login = Object.values(loginList._logins).find(
-          obj => obj.login.origin == aOriginTuple[1]
-        ).login;
-        return login.origin == aOriginTuple[1];
-      }, "waiting for the login origin to get updated");
+      let login = Object.values(loginList._logins).find(
+        obj => obj.login.origin == aOriginTuple[1]
+      ).login;
       is(
         login.origin,
         aOriginTuple[1],
         "Stored login should only include the origin of the URL provided during creation"
       );
       is(
         login.username,
         "testuser2",
--- a/browser/components/aboutlogins/tests/browser/browser_openSite.js
+++ b/browser/components/aboutlogins/tests/browser/browser_openSite.js
@@ -80,17 +80,14 @@ add_task(async function test_launch_logi
     (_, data) => data == "modifyLogin"
   );
   Services.logins.modifyLogin(TEST_LOGIN1, modifiedLogin);
   await storageChangedPromised;
 
   BrowserTestUtils.removeTab(newTab);
 
   await SpecialPowers.spawn(browser, [], async () => {
-    await ContentTaskUtils.waitForCondition(() => {
-      return !content.document.querySelector("confirmation-dialog").hidden;
-    }, "waiting for confirmation-dialog to appear");
     ok(
       !content.document.querySelector("confirmation-dialog").hidden,
       "discard-changes confirmation-dialog should be visible after logging in to a site with a modified login present in the form"
     );
   });
 });
--- a/browser/components/aboutlogins/tests/browser/head.js
+++ b/browser/components/aboutlogins/tests/browser/head.js
@@ -60,26 +60,20 @@ async function addLogin(login) {
   );
   login = Services.logins.addLogin(login);
   await storageChangedPromised;
   registerCleanupFunction(() => {
     let matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
       Ci.nsIWritablePropertyBag2
     );
     matchData.setPropertyAsAUTF8String("guid", login.guid);
-
-    let logins = Services.logins.searchLogins(matchData);
-    if (!logins.length) {
+    if (!Services.logins.searchLogins(matchData).length) {
       return;
     }
-    // Use the login that was returned from searchLogins
-    // in case the initial login object was changed by the test code,
-    // since removeLogin makes sure that the login argument exactly
-    // matches the login that it will be removing.
-    Services.logins.removeLogin(logins[0]);
+    Services.logins.removeLogin(login);
   });
   return login;
 }
 
 let EXPECTED_BREACH = null;
 let EXPECTED_ERROR_MESSAGE = null;
 add_task(async function setup() {
   const db = await RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).db;
--- a/browser/components/aboutlogins/tests/chrome/test_login_item.html
+++ b/browser/components/aboutlogins/tests/chrome/test_login_item.html
@@ -50,19 +50,16 @@ const TEST_LOGIN_2 = {
 const TEST_BREACH = {
   Name: "Test-Breach",
   breachAlertURL: "https://monitor.firefox.com/breach-details/Test-Breach",
 };
 
 const TEST_BREACHES_MAP = new Map();
 TEST_BREACHES_MAP.set(TEST_LOGIN_1.guid, TEST_BREACH);
 
-const TEST_VULNERABLE_MAP = new Map();
-TEST_VULNERABLE_MAP.set(TEST_LOGIN_2.guid, true);
-
 add_task(async function setup() {
   let templateFrame = document.getElementById("templateFrame");
   let displayEl = document.getElementById("display");
   importDependencies(templateFrame, displayEl);
 
   gLoginItem = document.createElement("login-item");
   displayEl.appendChild(gLoginItem);
 
@@ -133,55 +130,26 @@ add_task(async function test_set_login()
 add_task(async function test_update_breaches() {
   gLoginItem.setLogin(TEST_LOGIN_1);
   gLoginItem.setBreaches(TEST_BREACHES_MAP);
   await asyncElementRendered();
 
   let correspondingBreach = TEST_BREACHES_MAP.get(gLoginItem._login.guid);
   let breachAlert = gLoginItem.shadowRoot.querySelector(".breach-alert");
   ok(!isHidden(breachAlert), "Breach alert should be visible");
-  is(breachAlert.querySelector(".alert-learn-more-link").getAttribute("href"), correspondingBreach.breachAlertURL, "Breach alert link should be equal to the correspondingBreach.breachAlertURL.");
-  is(breachAlert.querySelector(".alert-link").href, TEST_LOGIN_1.origin + "/", "Link in the text should point to the login origin");
-  let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-alert");
-  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
+  is(breachAlert.querySelector(".breach-alert-link").getAttribute("href"), correspondingBreach.breachAlertURL, "Breach alert link should be equal to the correspondingBreach.breachAlertURL.");
 });
 
 add_task(async function test_breach_alert_is_correctly_hidden() {
   gLoginItem.setLogin(TEST_LOGIN_2);
   gLoginItem.setBreaches(TEST_BREACHES_MAP);
   await asyncElementRendered();
 
   let breachAlert = gLoginItem.shadowRoot.querySelector(".breach-alert");
   ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
-  let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-alert");
-  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
-});
-
-add_task(async function test_update_vulnerable() {
-  gLoginItem.setLogin(TEST_LOGIN_2);
-  gLoginItem.setVulnerableLogins(TEST_VULNERABLE_MAP);
-  await asyncElementRendered();
-
-  let breachAlert = gLoginItem.shadowRoot.querySelector(".breach-alert");
-  ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
-  let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-alert");
-  ok(!isHidden(vulernableAlert), "Vulnerable alert should be visible");
-  is(vulernableAlert.querySelector(".alert-link").href, TEST_LOGIN_2.origin + "/", "Link in the text should point to the login origin");
-});
-
-add_task(async function test_vulnerable_alert_is_correctly_hidden() {
-  gLoginItem.setLogin(TEST_LOGIN_1);
-  gLoginItem.setVulnerableLogins(TEST_VULNERABLE_MAP);
-  gLoginItem.setBreaches(new Map());
-  await asyncElementRendered();
-
-  let breachAlert = gLoginItem.shadowRoot.querySelector(".breach-alert");
-  ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
-  let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-alert");
-  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
 });
 
 add_task(async function test_edit_login() {
   gLoginItem.setLogin(TEST_LOGIN_1);
   let usernameInput = gLoginItem.shadowRoot.querySelector("input[name='username']");
   usernameInput.placeholder = "dummy placeholder";
   gLoginItem.shadowRoot.querySelector(".edit-button").click();
   await asyncElementRendered();
--- a/browser/locales/en-US/browser/aboutLogins.ftl
+++ b/browser/locales/en-US/browser/aboutLogins.ftl
@@ -162,32 +162,20 @@ confirm-delete-dialog-message = This act
 about-logins-confirm-remove-dialog-confirm-button = Remove
 
 confirm-discard-changes-dialog-title = Discard unsaved changes?
 confirm-discard-changes-dialog-message = All unsaved changes will be lost.
 confirm-discard-changes-dialog-confirm-button = Discard
 
 ## Breach Alert notification
 
-about-logins-breach-alert-title = Website Breach
 breach-alert-text = Passwords were leaked or stolen from this website since you last updated your login details. Change your password to protect your account.
-about-logins-breach-alert-date = This breach occurred on { DATETIME($date, day: "numeric", month: "long", year: "numeric") }
-# Variables:
-#   $hostname (String) - The hostname of the website associated with the login, e.g. "example.com"
-about-logins-breach-alert-link = Go to { $hostname }
-about-logins-breach-alert-learn-more-link = Learn more
-
-## Vulnerable Password notification
-
-about-logins-vulnerable-alert-title = Vulnerable Password
-about-logins-vulnerable-alert-text = This password was leaked or stolen in another company’s data breach. Reusing credentials puts all of your accounts at risk. To improve your online security, change this password.
-# Variables:
-#   $hostname (String) - The hostname of the website associated with the login, e.g. "example.com"
-about-logins-vulnerable-alert-link = Go to { $hostname }
-about-logins-vulnerable-alert-learn-more-link = Learn more
+breach-alert-link = Learn more about this breach.
+breach-alert-dismiss =
+    .title = Close this alert
 
 ## Error Messages
 
 # This is an error message that appears when a user attempts to save
 # a new login that is identical to an existing saved login.
 # Variables:
 #   $loginTitle (String) - The title of the website associated with the login.
 about-logins-error-message-duplicate-login-with-link = An entry for { $loginTitle } with that username already exists. <a data-l10n-name="duplicate-link">Go to existing entry?</a>