Backed out 8 changesets (bug 1549803, bug 1550095) for browser-chrome failuire at browser/components/aboutlogins/tests/browser/browser_updateLogin.js
authorDaniel Varga <dvarga@mozilla.com>
Sat, 11 May 2019 01:36:28 +0300
changeset 535368 54f367c785cbd4cf3ccb3f4d1478aea7c636d6cb
parent 535367 15d5a90bad2de0ff603734a83471a2de2435461e
child 535369 45ff6c2d30e505ca7f6d1060f119ed3e713fc244
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1549803, 1550095
milestone68.0a1
backs out7e08cf4753a79d765acc9721c65104c229a3f5fd
452720b78ca75587668030394cfcb806aa48cbda
e450e9d4671c01b57531b20ef2c347ed3f4ad905
e062410531b12d49ebb71840b40bf5a11da5b647
2d13d407f562a49cdd391c83b6d5eaa6f1598e0c
741b2f74227b86fff0d7c000fe73c0770c305ac1
9f3df400296282c8f0319bc322138dba94d4d8c6
b1519a245b748b1f8b0e938f97e4f20980125c0f
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 1549803, bug 1550095) for browser-chrome failuire at browser/components/aboutlogins/tests/browser/browser_updateLogin.js Backed out changeset 7e08cf4753a7 (bug 1549803) Backed out changeset 452720b78ca7 (bug 1549803) Backed out changeset e450e9d4671c (bug 1549803) Backed out changeset e062410531b1 (bug 1549803) Backed out changeset 2d13d407f562 (bug 1549803) Backed out changeset 741b2f74227b (bug 1550095) Backed out changeset 9f3df4002962 (bug 1550095) Backed out changeset b1519a245b74 (bug 1550095)
browser/components/BrowserGlue.jsm
browser/components/aboutlogins/AboutLoginsChild.jsm
browser/components/aboutlogins/AboutLoginsParent.jsm
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/aboutLogins.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.css
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/jar.mn
browser/components/aboutlogins/tests/browser/browser.ini
browser/components/aboutlogins/tests/browser/browser_loginChanges.js
browser/components/aboutlogins/tests/browser/browser_loginListChanges.js
browser/components/aboutlogins/tests/browser/browser_updateLogin.js
browser/components/aboutlogins/tests/mochitest/aboutlogins_common.js
browser/components/aboutlogins/tests/mochitest/test_login_item.html
browser/components/aboutlogins/tests/mochitest/test_login_list.html
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -31,17 +31,16 @@ let ACTORS = {
 
 let LEGACY_ACTORS = {
   AboutLogins: {
     child: {
       matches: ["about:logins"],
       module: "resource:///actors/AboutLoginsChild.jsm",
       events: {
         "AboutLoginsDeleteLogin": {wantUntrusted: true},
-        "AboutLoginsUpdateLogin": {wantUntrusted: true},
         "AboutLoginsInit": {wantUntrusted: true},
       },
       messages: [
         "AboutLogins:AllLogins",
         "AboutLogins:LoginAdded",
         "AboutLogins:LoginModified",
         "AboutLogins:LoginRemoved",
       ],
@@ -541,17 +540,16 @@ const listeners = {
     // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init
 
     "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
     "webrtc:UpdatingIndicators": ["webrtcUI"],
   },
 
   mm: {
     "AboutLogins:DeleteLogin": ["AboutLoginsParent"],
-    "AboutLogins:UpdateLogin": ["AboutLoginsParent"],
     "AboutLogins:Subscribe": ["AboutLoginsParent"],
     "Content:Click": ["ContentClick"],
     "ContentSearch": ["ContentSearch"],
     "FormValidation:ShowPopup": ["FormValidationHandler"],
     "FormValidation:HidePopup": ["FormValidationHandler"],
     "PictureInPicture:Request": ["PictureInPicture"],
     "PictureInPicture:Close": ["PictureInPicture"],
     "PictureInPicture:Playing": ["PictureInPicture"],
--- a/browser/components/aboutlogins/AboutLoginsChild.jsm
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -25,20 +25,16 @@ class AboutLoginsChild extends ActorChil
           cloneFunctions: true,
         });
         break;
       }
       case "AboutLoginsDeleteLogin": {
         this.mm.sendAsyncMessage("AboutLogins:DeleteLogin", {login: event.detail});
         break;
       }
-      case "AboutLoginsUpdateLogin": {
-        this.mm.sendAsyncMessage("AboutLogins:UpdateLogin", {login: event.detail});
-        break;
-      }
     }
   }
 
   receiveMessage(message) {
     switch (message.name) {
       case "AboutLogins:AllLogins":
         this.sendToContent("AllLogins", message.data);
         break;
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -1,28 +1,21 @@
 /* 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/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["AboutLoginsParent"];
 
-const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "E10SUtils",
-                               "resource://gre/modules/E10SUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "LoginHelper",
                                "resource://gre/modules/LoginHelper.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "log", () => {
-  return LoginHelper.createLogger("AboutLoginsParent");
-});
-
 const ABOUT_LOGINS_ORIGIN = "about:logins";
 
 const isValidLogin = login => {
   return !(login.hostname || "").startsWith("chrome://");
 };
 
 const convertSubjectToLogin = subject => {
     subject.QueryInterface(Ci.nsILoginMetaInfo).QueryInterface(Ci.nsILoginInfo);
@@ -34,18 +27,17 @@ const convertSubjectToLogin = subject =>
 };
 
 var AboutLoginsParent = {
   _subscribers: new WeakSet(),
 
   // Listeners are added in BrowserGlue.jsm
   receiveMessage(message) {
     // Only respond to messages sent from about:logins.
-    if (message.target.remoteType != E10SUtils.PRIVILEGED_REMOTE_TYPE ||
-        message.target.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
+    if (message.target.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
       return;
     }
 
     switch (message.name) {
       case "AboutLogins:DeleteLogin": {
         let login = LoginHelper.vanillaObjectToLogin(message.data.login);
         Services.logins.removeLogin(login);
         break;
@@ -55,35 +47,16 @@ var AboutLoginsParent = {
           Services.obs.addObserver(this, "passwordmgr-storage-changed");
         }
         this._subscribers.add(message.target);
 
         let messageManager = message.target.messageManager;
         messageManager.sendAsyncMessage("AboutLogins:AllLogins", this.getAllLogins());
         break;
       }
-      case "AboutLogins:UpdateLogin": {
-        let loginUpdates = message.data.login;
-        let logins = LoginHelper.searchLoginsWithObject({guid: loginUpdates.guid});
-        if (!logins || logins.length != 1) {
-          log.warn(`AboutLogins:UpdateLogin: expected to find a login for guid: ${loginUpdates.guid} but found ${(logins || []).length}`);
-          return;
-        }
-
-        let modifiedLogin = logins[0].clone();
-        if (loginUpdates.hasOwnProperty("username")) {
-          modifiedLogin.username = loginUpdates.username;
-        }
-        if (loginUpdates.hasOwnProperty("password")) {
-          modifiedLogin.password = loginUpdates.password;
-        }
-
-        Services.logins.modifyLogin(logins[0], modifiedLogin);
-        break;
-      }
     }
   },
 
   observe(subject, topic, type) {
     if (!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length) {
       Services.obs.removeObserver(this, "passwordmgr-storage-changed");
       return;
     }
@@ -117,19 +90,17 @@ var AboutLoginsParent = {
         break;
       }
     }
   },
 
   messageSubscribers(name, details) {
     let subscribers = ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers);
     for (let subscriber of subscribers) {
-      if (subscriber.remoteType != E10SUtils.PRIVILEGED_REMOTE_TYPE ||
-          !subscriber.contentPrincipal ||
-          subscriber.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
+      if (subscriber.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
         this._subscribers.delete(subscriber);
         continue;
       }
       try {
         subscriber.messageManager.sendAsyncMessage(name, details);
       } catch (ex) {}
     }
   },
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -11,17 +11,13 @@
 ### descendant after translation.
 
 about-logins-page-title = Login Manager
 
 login-list =
   .login-list-header = Logins
 
 login-item =
-  .cancel-button = Cancel
-  .delete-button = Delete
-  .hostname-label = Website Address
-  .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
+  .login-item-hostname = Hostname
+  .login-item-password = Password
+  .login-item-username = Username
+  .login-item-time-created = Time Created
+  .login-item-delete = Delete
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -8,61 +8,53 @@
     <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/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/aboutLogins.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
   </head>
   <body>
     <login-list data-l10n-id="login-list"
                 data-l10n-attrs="login-list-header"></login-list>
     <login-item data-l10n-id="login-item"
-                data-l10n-attrs="cancel-button,
-                                 delete-button,
-                                 hostname-label,
-                                 password-label,
-                                 save-changes-button,
-                                 time-created,
-                                 time-changed,
-                                 time-used,
-                                 username-label"></login-item>
+                data-l10n-attrs="login-item-hostname, login-item-password, login-item-username, login-item-time-created, login-item-delete"></login-item>
 
     <template id="login-list-template">
-      <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-list.css">
       <h2></h2>
-      <ol>
-      </ol>
+      <pre>
+      </pre>
     </template>
 
     <template id="login-list-item-template">
-      <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-list-item.css">
-      <span class="hostname"></span>
-      <span class="username"></span>
+      <style>
+        :host(.selected) {
+          font-weight: bold;
+        }
+      </style>
+      <span class="login-list-item-hostname"></span>
+      <span class="login-list-item-username"></span>
     </template>
 
     <template id="login-item-template">
-      <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/login-item.css">
-      <h2 class="header"></h2>
-      <button class="delete-button"></button>
+      <h2 data-l10n-id="login-item-header"></h2>
       <label>
-        <span class="hostname-label field-label"></span>
-        <span class="hostname"/>
+        <span class="hostname-label"></span>
+        <input name="hostname"/>
       </label>
       <label>
-        <span class="username-label field-label"></span>
+        <span class="username-label"></span>
         <input name="username"/>
       </label>
       <label>
-        <span class="password-label field-label"></span>
+        <span class="password-label"></span>
         <input type="password" name="password"/>
       </label>
-      <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>
+      <p>
+        <span class="time-created-label"></span>
+        <span class="time-created"></span>
+      </p>
+      <button class="delete-button"></button>
     </template>
   </body>
 </html>
--- a/browser/components/aboutlogins/content/aboutLogins.js
+++ b/browser/components/aboutlogins/content/aboutLogins.js
@@ -14,16 +14,17 @@ document.addEventListener("DOMContentLoa
 window.addEventListener("AboutLoginsChromeToContent", event => {
   switch (event.detail.messageType) {
     case "AllLogins": {
       gElements.loginList.setLogins(event.detail.value);
       break;
     }
     case "LoginAdded": {
       gElements.loginList.loginAdded(event.detail.value);
+      gElements.loginItem.loginAdded(event.detail.value);
       break;
     }
     case "LoginModified": {
       gElements.loginList.loginModified(event.detail.value);
       gElements.loginItem.loginModified(event.detail.value);
       break;
     }
     case "LoginRemoved": {
deleted file mode 100644
--- a/browser/components/aboutlogins/content/components/login-item.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/* 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/. */
-
-h2 {
-  border-bottom: 1px solid var(--grey-30);
-}
-
-.field-label {
-  display: block;
-}
-
-.meta-info {
-  font-size: smaller;
-}
-
-.meta-info:not(:first-of-type) {
-  margin-top: 0;
-}
-
-.meta-info:not(:last-of-type) {
-  margin-bottom: 0;
-}
-
-.meta-info:first-of-type {
-  border-top: 1px solid var(--grey-30);
-}
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -13,116 +13,111 @@ class LoginItem extends HTMLElement {
       this.render();
       return;
     }
 
     let loginItemTemplate = document.querySelector("#login-item-template");
     this.attachShadow({mode: "open"})
         .appendChild(loginItemTemplate.content.cloneNode(true));
 
-    for (let selector of [
-      ".delete-button",
-      ".save-changes-button",
-      ".cancel-button",
-    ]) {
-      let button = this.shadowRoot.querySelector(selector);
-      button.addEventListener("click", this);
-    }
+    let deleteButton = this.shadowRoot.querySelector(".delete-button");
+    deleteButton.addEventListener("click", this);
 
     window.addEventListener("AboutLoginsLoginSelected", this);
 
     this.render();
   }
 
   static get observedAttributes() {
     return [
-      "cancel-button",
-      "delete-button",
-      "hostname-label",
-      "password-label",
-      "save-changes-button",
-      "time-created",
-      "time-changed",
-      "time-used",
-      "username-label",
+      "login-item-delete",
+      "login-item-hostname",
+      "login-item-password",
+      "login-item-time-created",
+      "login-item-username",
     ];
   }
 
   /* Fluent doesn't handle localizing into Shadow DOM yet so strings
      need to get reflected in to their targeted element. */
   attributeChangedCallback(attr, oldValue, newValue) {
     if (!this.shadowRoot) {
       return;
     }
 
-    // Strings that are reflected to their shadowed element are assigned
-    // to an attribute name that matches a className on the element.
-    let shadowedElement = this.shadowRoot.querySelector("." + attr);
-    shadowedElement.textContent = newValue;
+    switch (attr) {
+      case "login-item-delete":
+        this.shadowRoot.querySelector(".delete-button").textContent = newValue;
+        break;
+      case "login-item-hostname":
+        this.shadowRoot.querySelector(".hostname-label").textContent = newValue;
+        break;
+      case "login-item-password":
+        this.shadowRoot.querySelector(".password-label").textContent = newValue;
+        break;
+      case "login-item-time-created":
+        this.shadowRoot.querySelector(".time-created-label").textContent = newValue;
+        break;
+      case "login-item-username":
+        this.shadowRoot.querySelector(".username-label").textContent = newValue;
+        break;
+    }
   }
 
   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;
-    this.shadowRoot.querySelector(".header").textContent = hostnameNoScheme || "";
-    this.shadowRoot.querySelector(".hostname").textContent = this._login.hostname || "";
+    this.shadowRoot.querySelector("input[name='hostname']").value = this._login.hostname || "";
     this.shadowRoot.querySelector("input[name='username']").value = this._login.username || "";
     this.shadowRoot.querySelector("input[name='password']").value = this._login.password || "";
+    this.shadowRoot.querySelector(".time-created").textContent = this._login.timeCreated || "";
   }
 
   handleEvent(event) {
     switch (event.type) {
       case "AboutLoginsLoginSelected": {
         this.setLogin(event.detail);
         break;
       }
       case "click": {
         if (event.target.classList.contains("delete-button")) {
           document.dispatchEvent(new CustomEvent("AboutLoginsDeleteLogin", {
             bubbles: true,
             detail: this._login,
           }));
-          return;
-        }
-        if (event.target.classList.contains("save-changes-button")) {
-          let loginUpdates = {
-            guid: this._login.guid,
-          };
-          let formUsername = this.shadowRoot.querySelector("input[name='username']").value.trim();
-          if (formUsername != this._login.username) {
-            loginUpdates.username = formUsername;
-          }
-          let formPassword = this.shadowRoot.querySelector("input[name='password']").value.trim();
-          if (formPassword != this._login.password) {
-            loginUpdates.password = formPassword;
-          }
-          document.dispatchEvent(new CustomEvent("AboutLoginsUpdateLogin", {
-            bubbles: true,
-            detail: loginUpdates,
-          }));
-          return;
-        }
-        if (event.target.classList.contains("cancel-button")) {
-          this.render();
         }
         break;
       }
     }
   }
 
   setLogin(login) {
     this._login = login;
     this.render();
   }
 
+  loginAdded(login) {
+    if (!this._login.guid) {
+      let tempLogin = {
+        username: this.shadowRoot.querySelector("input[name='username']").value,
+        formSubmitURL: "", // Use the wildcard since the user doesn't supply it.
+        hostname: this.shadowRoot.querySelector("input[name='hostname']").value,
+        password: this.shadowRoot.querySelector("input[name='password']").value,
+      };
+      // Need to use LoginHelper.doLoginsMatch() to see if the login
+      // that was added is the login that was being edited, so we
+      // can update time-created, etc.
+      if (window.AboutLoginsUtils.doLoginsMatch(tempLogin, login)) {
+        this._login = login;
+        this.render();
+      }
+    } else if (login.guid == this._login.guid) {
+      this._login = login;
+      this.render();
+    }
+  }
+
   loginModified(login) {
     if (login.guid != this._login.guid) {
       return;
     }
 
     this._login = login;
     this.render();
   }
deleted file mode 100644
--- a/browser/components/aboutlogins/content/components/login-list-item.css
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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 {
-  border-inline-start: 4px solid transparent;
-  border-bottom: 1px solid var(--grey-30);
-  display: block;
-  padding: 10px;
-}
-
-:host(:hover) {
-  background-color: var(--grey-90-a10);
-}
-
-:host(:hover:active) {
-  background-color: var(--grey-90-a20);
-}
-
-:host(.selected) {
-  border-inline-start-color: var(--blue-40);
-  background-color: var(--grey-20);
-}
-
-:host(.selected:hover) {
-}
-
-:host(.selected:hover:active) {
-  background-color: var(--grey-30);
-}
-
-.hostname {
-  font-weight: bold;
-}
-
-.hostname,
-.username {
-  display: block;
-}
--- a/browser/components/aboutlogins/content/components/login-list-item.js
+++ b/browser/components/aboutlogins/content/components/login-list-item.js
@@ -1,40 +1,50 @@
 /* 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 LoginListItem extends HTMLElement {
   constructor(login) {
     super();
     this._login = login;
+    this._selected = false;
   }
 
   connectedCallback() {
     if (this.children.length) {
       this.render();
       return;
     }
 
     let loginListItemTemplate = document.querySelector("#login-list-item-template");
     this.attachShadow({mode: "open"})
         .appendChild(loginListItemTemplate.content.cloneNode(true));
     this.render();
 
     this.addEventListener("click", this);
+    window.addEventListener("AboutLoginsLoginSelected", this);
   }
 
   render() {
+    this.classList.toggle("selected", this._selected);
     this.setAttribute("guid", this._login.guid);
-    this.shadowRoot.querySelector(".hostname").textContent = this._login.hostname;
-    this.shadowRoot.querySelector(".username").textContent = this._login.username;
+    this.shadowRoot.querySelector(".login-list-item-hostname").textContent = this._login.hostname;
+    this.shadowRoot.querySelector(".login-list-item-username").textContent = this._login.username;
   }
 
   handleEvent(event) {
     switch (event.type) {
+      case "AboutLoginsLoginSelected": {
+        if (this._selected != (event.detail.guid == this._login.guid)) {
+          this._selected = event.detail.guid == this._login.guid;
+          this.render();
+        }
+        break;
+      }
       case "click": {
         this.dispatchEvent(new CustomEvent("AboutLoginsLoginSelected", {
           bubbles: true,
           composed: true,
           detail: this._login,
         }));
       }
     }
deleted file mode 100644
--- a/browser/components/aboutlogins/content/components/login-list.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/* 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/. */
-
-ol {
-  padding-inline-start: 0;
-}
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -3,51 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* globals LoginListItem */
 
 class LoginList extends HTMLElement {
   constructor() {
     super();
     this._logins = [];
-    this._selectedItem = null;
   }
 
   connectedCallback() {
     if (this.children.length) {
       return;
     }
     let loginListTemplate = document.querySelector("#login-list-template");
     this.attachShadow({mode: "open"})
         .appendChild(loginListTemplate.content.cloneNode(true));
     this.render();
-
-    window.addEventListener("AboutLoginsLoginSelected", this);
   }
 
   render() {
-    let list = this.shadowRoot.querySelector("ol");
+    let pre = this.shadowRoot.querySelector("pre");
     for (let login of this._logins) {
-      list.append(new LoginListItem(login));
-    }
-  }
-
-  handleEvent(event) {
-    switch (event.type) {
-      case "AboutLoginsLoginSelected": {
-        if (this._selectedItem) {
-          if (this._selectedItem.getAttribute("guid") == event.detail.guid) {
-            return;
-          }
-          this._selectedItem.classList.toggle("selected", false);
-        }
-        this._selectedItem = this.shadowRoot.querySelector(`login-list-item[guid="${event.detail.guid}"]`);
-        this._selectedItem.classList.toggle("selected", true);
-        break;
-      }
+      pre.append(new LoginListItem(login));
     }
   }
 
   static get observedAttributes() {
     return ["login-list-header"];
   }
 
   /* Fluent doesn't handle localizing into Shadow DOM yet so strings
@@ -60,48 +41,48 @@ class LoginList extends HTMLElement {
     switch (attr) {
       case "login-list-header":
         this.shadowRoot.querySelector("h2").textContent = newValue;
         break;
     }
   }
 
   setLogins(logins) {
-    let list = this.shadowRoot.querySelector("ol");
-    list.textContent = "";
+    let pre = this.shadowRoot.querySelector("pre");
+    pre.textContent = "";
     this._logins = logins;
     this.render();
   }
 
   loginAdded(login) {
     this._logins.push(login);
-    let list = this.shadowRoot.querySelector("ol");
-    list.append(new LoginListItem(login));
+    let pre = this.shadowRoot.querySelector("pre");
+    pre.append(new LoginListItem(login));
   }
 
   loginModified(login) {
     for (let i = 0; i < this._logins.length; i++) {
       if (this._logins[i].guid == login.guid) {
         this._logins[i] = login;
         break;
       }
     }
-    let list = this.shadowRoot.querySelector("ol");
-    for (let loginListItem of list.children) {
+    let pre = this.shadowRoot.querySelector("pre");
+    for (let loginListItem of pre.children) {
       if (loginListItem.getAttribute("guid") == login.guid) {
         loginListItem.update(login);
         break;
       }
     }
   }
 
   loginRemoved(login) {
     this._logins = this._logins.filter(l => l.guid != login.guid);
-    let list = this.shadowRoot.querySelector("ol");
-    for (let loginListItem of list.children) {
+    let pre = this.shadowRoot.querySelector("pre");
+    for (let loginListItem of pre.children) {
       if (loginListItem.getAttribute("guid") == login.guid) {
         loginListItem.remove();
         break;
       }
     }
   }
 }
 customElements.define("login-list", LoginList);
--- a/browser/components/aboutlogins/jar.mn
+++ b/browser/components/aboutlogins/jar.mn
@@ -1,13 +1,10 @@
 # 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/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)
   content/browser/aboutlogins/aboutLogins.js    (content/aboutLogins.js)
   content/browser/aboutlogins/aboutLogins.html  (content/aboutLogins.html)
--- a/browser/components/aboutlogins/tests/browser/browser.ini
+++ b/browser/components/aboutlogins/tests/browser/browser.ini
@@ -1,7 +1,6 @@
 [DEFAULT]
 prefs =
   signon.management.page.enabled=true
 
 [browser_deleteLogin.js]
-[browser_loginListChanges.js]
-[browser_updateLogin.js]
+[browser_loginChanges.js]
rename from browser/components/aboutlogins/tests/browser/browser_loginListChanges.js
rename to browser/components/aboutlogins/tests/browser/browser_loginChanges.js
deleted file mode 100644
--- a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
-                                             Ci.nsILoginInfo, "init");
-const LOGIN_URL = "https://www.example.com";
-let TEST_LOGIN1 = new nsLoginInfo(LOGIN_URL, LOGIN_URL, null, "user1", "pass1", "username", "password");
-
-add_task(async function setup() {
-  TEST_LOGIN1 = Services.logins.addLogin(TEST_LOGIN1);
-  await BrowserTestUtils.openNewForegroundTab({gBrowser, url: "about:logins"});
-  registerCleanupFunction(() => {
-    BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  });
-});
-
-add_task(async function test_show_logins() {
-  let browser = gBrowser.selectedBrowser;
-  await ContentTask.spawn(browser, TEST_LOGIN1.guid, async (loginGuid) => {
-    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
-    let loginFound = await ContentTaskUtils.waitForCondition(() => {
-      return loginList._logins.length == 1 &&
-             loginList._logins[0].guid == loginGuid;
-    }, "Waiting for login to be displayed");
-    ok(loginFound, "Stored logins should be displayed upon loading the page");
-  });
-});
-
-add_task(async function test_login_item() {
-  let browser = gBrowser.selectedBrowser;
-  await ContentTask.spawn(browser, LoginHelper.loginToVanillaObject(TEST_LOGIN1), async (login) => {
-    let loginList = content.document.querySelector("login-list");
-    let loginListItem = Cu.waiveXrays(loginList.shadowRoot.querySelector("login-list-item"));
-    loginListItem.click();
-
-    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
-    let loginItemPopulated = await ContentTaskUtils.waitForCondition(() => {
-      return loginItem._login.guid == loginListItem.getAttribute("guid") &&
-             loginItem._login.guid == login.guid;
-    }, "Waiting for login item to get populated");
-    ok(loginItemPopulated, "The login item should get populated");
-
-    let usernameInput = loginItem.shadowRoot.querySelector("input[name='username']");
-    let passwordInput = loginItem.shadowRoot.querySelector("input[name='password']");
-
-    usernameInput.value += "-undome";
-    passwordInput.value += "-undome";
-
-    let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
-    cancelButton.click();
-    await Promise.resolve();
-    is(usernameInput.value, login.username, "Username change should be reverted");
-    is(passwordInput.value, login.password, "Password change should be reverted");
-
-    usernameInput.value += "-saveme";
-    passwordInput.value += "-saveme";
-
-    let saveChangesButton = loginItem.shadowRoot.querySelector(".save-changes-button");
-    saveChangesButton.click();
-
-    await ContentTaskUtils.waitForCondition(() => {
-      return loginListItem._login.username == usernameInput.value &&
-             loginListItem._login.password == passwordInput.value;
-    }, "Waiting for corresponding login in login list to update");
-  });
-});
--- a/browser/components/aboutlogins/tests/mochitest/aboutlogins_common.js
+++ b/browser/components/aboutlogins/tests/mochitest/aboutlogins_common.js
@@ -1,11 +1,11 @@
 "use strict";
 
-/* exported asyncElementRendered, importDependencies, stubFluentL10n */
+/* exported asyncElementRendered, importDependencies */
 
 /**
  * A helper to await on while waiting for an asynchronous rendering of a Custom
  * Element.
  * @returns {Promise}
  */
 function asyncElementRendered() {
   return Promise.resolve();
@@ -19,20 +19,8 @@ function asyncElementRendered() {
 function importDependencies(templateFrame, destinationEl) {
   let templates = templateFrame.contentDocument.querySelectorAll("template");
   isnot(templates, null, "Check some templates found");
   for (let template of templates) {
     let imported = document.importNode(template, true);
     destinationEl.appendChild(imported);
   }
 }
-
-function stubFluentL10n(argsMap) {
-  document.l10n = {
-    setAttributes(element, id, args) {
-      element.setAttribute("data-l10n-id", id);
-      for (let attrName of Object.keys(argsMap)) {
-        let varName = argsMap[attrName];
-        element.setAttribute(attrName, args[varName]);
-      }
-    },
-  };
-}
--- a/browser/components/aboutlogins/tests/mochitest/test_login_item.html
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_item.html
@@ -26,104 +26,84 @@ Test the login-item component
 
 let gLoginItem;
 const TEST_LOGIN_1 = {
   guid: "123456789",
   hostname: "https://example.com",
   username: "user1",
   password: "pass1",
   timeCreated: "1000",
-  timePasswordChanged: "2000",
-  timeLastUsed: "4000",
 };
 
 add_task(async function setup() {
-  stubFluentL10n({
-    "time-created": "timeCreated",
-    "time-changed": "timeChanged",
-    "time-used": "timeUsed",
-  });
-
   let templateFrame = document.getElementById("templateFrame");
   let displayEl = document.getElementById("display");
   importDependencies(templateFrame, displayEl);
 
   gLoginItem = document.createElement("login-item");
   displayEl.appendChild(gLoginItem);
 });
 
 add_task(async function test_empty_item() {
   ok(gLoginItem, "loginItem exists");
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, "", "hostname should be blank");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, "", "hostname should be blank");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be blank");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be blank");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be blank");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, "", "time-changed should be blank");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, "", "time-used should be blank");
 });
 
 add_task(async function test_set_login() {
   gLoginItem.setLogin(TEST_LOGIN_1);
   await asyncElementRendered();
 
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname, "hostname should be populated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be populated");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be populated");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be populated");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be populated");
 });
 
 add_task(async function test_different_login_modified() {
   let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
   gLoginItem.loginModified(otherLogin);
   await asyncElementRendered();
 
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname, "hostname should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be unchanged");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
 });
 
 add_task(async function test_different_login_removed() {
   let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
   gLoginItem.loginRemoved(otherLogin);
   await asyncElementRendered();
 
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname, "hostname should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be unchanged");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, TEST_LOGIN_1.timePasswordChanged, "time-changed should be unchanged");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, TEST_LOGIN_1.timeLastUsed, "time-used should be unchanged");
 });
 
 add_task(async function test_login_modified() {
   let modifiedLogin = Object.assign({}, TEST_LOGIN_1, {username: "updateduser"});
   gLoginItem.loginModified(modifiedLogin);
   await asyncElementRendered();
 
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, modifiedLogin.hostname, "hostname should be updated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, modifiedLogin.hostname, "hostname should be updated");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, modifiedLogin.username, "username should be updated");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, modifiedLogin.password, "password should be updated");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, modifiedLogin.timeCreated, "time-created should be updated");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, modifiedLogin.timePasswordChanged, "time-changed should be updated");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, modifiedLogin.timeLastUsed, "time-used should be updated");
 });
 
 add_task(async function test_login_removed() {
   gLoginItem.loginRemoved(TEST_LOGIN_1);
   await asyncElementRendered();
 
-  is(gLoginItem.shadowRoot.querySelector(".hostname").textContent, "", "hostname should be cleared");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, "", "hostname should be cleared");
   is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be cleared");
   is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be cleared");
   is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be cleared");
-  is(gLoginItem.shadowRoot.querySelector(".time-changed").textContent, "", "time-changed should be cleared");
-  is(gLoginItem.shadowRoot.querySelector(".time-used").textContent, "", "time-used should be cleared");
 });
 
 </script>
 
 </body>
 </html>
--- a/browser/components/aboutlogins/tests/mochitest/test_login_list.html
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_list.html
@@ -54,55 +54,49 @@ add_task(async function test_empty_list(
 });
 
 add_task(async function test_populated_list() {
   gLoginList.setLogins([TEST_LOGIN_1, TEST_LOGIN_2]);
   await asyncElementRendered();
   let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
   is(loginListItems.length, 2, "Both logins should be displayed");
   is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
-  is(loginListItems[0].shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname,
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-hostname").textContent, TEST_LOGIN_1.hostname,
      "login-list-item hostname should match");
-  is(loginListItems[0].shadowRoot.querySelector(".username").textContent, TEST_LOGIN_1.username,
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-username").textContent, TEST_LOGIN_1.username,
      "login-list-item username should match");
-
-  ok(!loginListItems[0].classList.contains("selected"), "The first item should not be selected by default");
-  ok(!loginListItems[1].classList.contains("selected"), "The second item should not be selected by default");
-  loginListItems[0].click();
-  ok(loginListItems[0].classList.contains("selected"), "The first item should be selected");
-  ok(!loginListItems[1].classList.contains("selected"), "The second item should still not be selected");
 });
 
 add_task(async function test_login_modified() {
   let modifiedLogin = Object.assign(TEST_LOGIN_1, {username: "user11"});
   gLoginList.loginModified(modifiedLogin);
   await asyncElementRendered();
   let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
   is(loginListItems.length, 2, "Both logins should be displayed");
   is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
-  is(loginListItems[0].shadowRoot.querySelector(".hostname").textContent, TEST_LOGIN_1.hostname,
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-hostname").textContent, TEST_LOGIN_1.hostname,
      "login-list-item hostname should match");
-  is(loginListItems[0].shadowRoot.querySelector(".username").textContent, modifiedLogin.username,
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-username").textContent, modifiedLogin.username,
      "login-list-item username should have been updated");
-  is(loginListItems[1].shadowRoot.querySelector(".username").textContent, TEST_LOGIN_2.username,
+  is(loginListItems[1].shadowRoot.querySelector(".login-list-item-username").textContent, TEST_LOGIN_2.username,
      "login-list-item2 username should remain unchanged");
 });
 
 add_task(async function test_login_added() {
   let newLogin = Object.assign({}, TEST_LOGIN_1, {username: "user22", guid: "111222"});
   gLoginList.loginAdded(newLogin);
   await asyncElementRendered();
   let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
   is(loginListItems.length, 3, "New login should be added to the list");
   is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
   is(loginListItems[1].getAttribute("guid"), TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
   is(loginListItems[2].getAttribute("guid"), newLogin.guid, "login-list-item3 should have correct guid attribute");
-  is(loginListItems[2].shadowRoot.querySelector(".hostname").textContent, newLogin.hostname,
+  is(loginListItems[2].shadowRoot.querySelector(".login-list-item-hostname").textContent, newLogin.hostname,
      "login-list-item hostname should match");
-  is(loginListItems[2].shadowRoot.querySelector(".username").textContent, newLogin.username,
+  is(loginListItems[2].shadowRoot.querySelector(".login-list-item-username").textContent, newLogin.username,
      "login-list-item username should have been updated");
 });
 
 add_task(async function test_login_removed() {
   gLoginList.loginRemoved({guid: "111222"});
   await asyncElementRendered();
   let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
   is(loginListItems.length, 2, "New login should be removed from the list");