Bug 1583796 - Re-sort and render the login list when breach notifications are updated without acting like a user-initiated sort. r=sfoster
authorJared Wein <jwein@mozilla.com>
Thu, 03 Oct 2019 16:04:39 +0000
changeset 496203 685cadf88d086be58c4808d68633680dfa4a1e53
parent 496202 5af44a2f82019894c42bc56909fafbd0b07d5c14
child 496204 2e1bfb7458de41a36432671c405eee62d897a761
child 496205 da487c9c5d6c20e6c5f5f017d08104be0312f7c6
push id36646
push usernerli@mozilla.com
push dateThu, 03 Oct 2019 21:48:01 +0000
treeherdermozilla-central@2e1bfb7458de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs1583796
milestone71.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 1583796 - Re-sort and render the login list when breach notifications are updated without acting like a user-initiated sort. r=sfoster Differential Revision: https://phabricator.services.mozilla.com/D47982
browser/components/aboutlogins/content/components/login-list.js
browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
browser/components/aboutlogins/tests/browser/browser_breachAlertDismissals.js
browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js
browser/components/aboutlogins/tests/browser/head.js
toolkit/components/passwordmgr/test/LoginTestUtils.jsm
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -166,19 +166,17 @@ export default class LoginList extends H
         recordTelemetryEvent({
           object: "existing_login",
           method: "select",
           extra,
         });
         break;
       }
       case "change": {
-        this._applySort();
-        this.render();
-        this._list.scrollTop = 0;
+        this._applySortAndScrollToTop();
         const extra = { sort_key: this._sortSelect.value };
         recordTelemetryEvent({ object: "list", method: "sort", extra });
         break;
       }
       case "AboutLoginsClearSelection": {
         if (!this._loginGuidsSortedOrder.length) {
           return;
         }
@@ -343,21 +341,17 @@ export default class LoginList extends H
     this._breachesByLoginGUID = breachesByLoginGUID;
     if (this._breachesByLoginGUID.size === 0) {
       this.render();
       return;
     }
     const breachedSortOptionElement = this._sortSelect.namedItem("breached");
     breachedSortOptionElement.hidden = false;
     this._sortSelect.selectedIndex = breachedSortOptionElement.index;
-    this._sortSelect.dispatchEvent(new CustomEvent("input", { bubbles: true }));
-    this._sortSelect.dispatchEvent(
-      new CustomEvent("change", { bubbles: true })
-    );
-    this.render();
+    this._applySortAndScrollToTop();
   }
 
   /**
    * @param {Map} breachesByLoginGUID A Map of breaches by login GUIDs that
    *                                  should be added to the local cache of
    *                                  breaches.
    */
   updateBreaches(breachesByLoginGUID) {
@@ -472,16 +466,22 @@ export default class LoginList extends H
     const sort = this._sortSelect.value;
     this._loginGuidsSortedOrder = this._loginGuidsSortedOrder.sort((a, b) => {
       let loginA = this._logins[a].login;
       let loginB = this._logins[b].login;
       return sortFnOptions[sort](loginA, loginB, this._breachesByLoginGUID);
     });
   }
 
+  _applySortAndScrollToTop() {
+    this._applySort();
+    this.render();
+    this._list.scrollTop = 0;
+  }
+
   _updateVisibleLoginCount(count) {
     if (count != document.l10n.getAttributes(this._count).args.count) {
       document.l10n.setAttributes(this._count, "login-list-count", {
         count,
       });
     }
   }
 
--- a/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
+++ b/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js
@@ -1,29 +1,33 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 requestLongerTimeout(2);
 
 ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm", this);
 ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
 
+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() {
-  let storageChangedPromised = TestUtils.topicObserved(
-    "passwordmgr-storage-changed",
-    (_, data) => data == "addLogin"
-  );
-  TEST_LOGIN1 = Services.logins.addLogin(TEST_LOGIN1);
-  await storageChangedPromised;
-  storageChangedPromised = TestUtils.topicObserved(
-    "passwordmgr-storage-changed",
-    (_, data) => data == "addLogin"
-  );
-  TEST_LOGIN2 = Services.logins.addLogin(TEST_LOGIN2);
-  await storageChangedPromised;
+  TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
+  TEST_LOGIN2 = await addLogin(TEST_LOGIN2);
+  TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
   await BrowserTestUtils.openNewForegroundTab({
     gBrowser,
     url: "about:logins",
   });
   registerCleanupFunction(() => {
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
     Services.logins.removeAllLogins();
   });
@@ -63,25 +67,25 @@ add_task(async function test_telemetry_e
       ".copy-password-button"
     );
     copyButton.click();
   });
   await LoginTestUtils.telemetry.waitForEventCount(4);
 
   let promiseNewTab = BrowserTestUtils.waitForNewTab(
     gBrowser,
-    TEST_LOGIN2.origin + "/"
+    TEST_LOGIN3.origin + "/"
   );
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let loginItem = content.document.querySelector("login-item");
     let originInput = loginItem.shadowRoot.querySelector(".origin-input");
     originInput.click();
   });
   let newTab = await promiseNewTab;
-  ok(true, "New tab opened to " + TEST_LOGIN2.origin);
+  ok(true, "New tab opened to " + TEST_LOGIN3.origin);
   BrowserTestUtils.removeTab(newTab);
   await LoginTestUtils.telemetry.waitForEventCount(5);
 
   // Show the password
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let loginItem = content.document.querySelector("login-item");
     let revealCheckbox = loginItem.shadowRoot.querySelector(
       ".reveal-password-checkbox"
--- a/browser/components/aboutlogins/tests/browser/browser_breachAlertDismissals.js
+++ b/browser/components/aboutlogins/tests/browser/browser_breachAlertDismissals.js
@@ -10,17 +10,16 @@ EXPECTED_BREACH = {
   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() {
-  TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 123456;
   TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
   await BrowserTestUtils.openNewForegroundTab({
     gBrowser,
     url: "about:logins",
   });
   registerCleanupFunction(() => {
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
     Services.logins.removeAllLogins();
--- a/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js
+++ b/browser/components/aboutlogins/tests/browser/browser_breachAlertShowingForAddedLogin.js
@@ -31,17 +31,16 @@ add_task(async function test_added_login
     let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
     is(
       loginList._loginGuidsSortedOrder.length,
       0,
       "the login list should be empty"
     );
   });
 
-  TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 123456;
   TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
   await ContentTask.spawn(browser, TEST_LOGIN3.guid, async aTestLogin3Guid => {
     let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
     is(
       loginList._loginGuidsSortedOrder.length,
       1,
       "one login should be in the list"
     );
--- a/browser/components/aboutlogins/tests/browser/head.js
+++ b/browser/components/aboutlogins/tests/browser/head.js
@@ -37,16 +37,17 @@ let TEST_LOGIN3 = new nsLoginInfo(
   "https://breached.example.com",
   "https://breached.example.com",
   null,
   "breachedLogin1",
   "pass3",
   "breachedLogin",
   "password"
 );
+TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 123456;
 
 async function addLogin(login) {
   let storageChangedPromised = TestUtils.topicObserved(
     "passwordmgr-storage-changed",
     (_, data) => data == "addLogin"
   );
   login = Services.logins.addLogin(login);
   await storageChangedPromised;
--- a/toolkit/components/passwordmgr/test/LoginTestUtils.jsm
+++ b/toolkit/components/passwordmgr/test/LoginTestUtils.jsm
@@ -539,14 +539,15 @@ this.LoginTestUtils.telemetry = {
         false
       )[process];
 
       if (!events) {
         return null;
       }
 
       events = events.filter(e => e[1] == category);
+      dump(`Waiting for ${count} events, got ${events.length}\n`);
       return events.length == count ? events : null;
     }, "waiting for telemetry event count of: " + count);
     Assert.equal(events.length, count, "waiting for telemetry event count");
     return events;
   },
 };