Bug 1147231 - Part 2 - Add a skeleton of the login fill doorhanger. draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Sun, 03 May 2015 12:21:54 +0100
changeset 826844 35abae6c46616b0f4e4a994118dba872e890d985
parent 826843 35d4096bb926a5e73e89c7234eb8fd9ad2830b32
push id118390
push userpaolo.mozmail@amadzone.org
push dateSun, 05 Aug 2018 15:07:34 +0000
bugs1147231
milestone63.0a1
Bug 1147231 - Part 2 - Add a skeleton of the login fill doorhanger. MozReview-Commit-ID: 9lRqNoOsuOh
browser/base/content/browser.css
browser/base/content/popup-notifications.inc
browser/themes/shared/notification-icons.inc.css
toolkit/components/passwordmgr/LoginDoorhangers.jsm
toolkit/components/passwordmgr/content/login.xml
toolkit/components/passwordmgr/jar.mn
toolkit/components/passwordmgr/moz.build
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -896,16 +896,22 @@ window[chromehidden~="toolbar"] toolbar:
               center left / 64px 128px no-repeat transparent;
 }
 #historySwipeAnimationNextArrow {
   background: url("chrome://browser/content/history-swipe-arrow.svg")
               center left / 64px 128px no-repeat transparent;
   transform: rotate(180deg);
 }
 
+/* Logins UI */
+
+.login-fill-item {
+  -moz-binding: url("chrome://passwordmgr/content/login.xml#login");
+}
+
 /*  Full Screen UI */
 
 #fullscr-toggler {
   height: 1px;
   background: black;
 }
 
 html|*.pointerlockfswarning {
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -88,8 +88,13 @@
       </popupnotificationcontent>
     </popupnotification>
 
     <popupnotification id="addon-installed-notification" hidden="true">
       <popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
         <description>&addonPostInstallMessage.label;</description>
       </popupnotificationcontent>
     </popupnotification>
+
+    <vbox id="login-fill-doorhanger" hidden="true">
+      <textbox id="login-fill-filter"/>
+      <richlistbox id="login-fill-list"/>
+    </vbox>
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -65,16 +65,17 @@
 .autoplay-media-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-blocked.svg);
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url(chrome://browser/skin/notification-icons/geo-detailed.svg);
 }
 
+#login-fill-notification-icon,
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
 }
 
 .login-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/login.svg);
 }
new file mode 100755
--- /dev/null
+++ b/toolkit/components/passwordmgr/LoginDoorhangers.jsm
@@ -0,0 +1,155 @@
+/* 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/. */
+
+/*
+ * Doorhanger-style user interfaces to work with logins.
+ */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+  "LoginDoorhangers",
+];
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/Doorhangers.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+this.LoginDoorhangers = {};
+
+/**
+ * Doorhanger for selecting and filling logins.
+ *
+ * @param {Object} properties
+ *        Properties from this object will be applied to the new instance.
+ */
+this.LoginDoorhangers.FillDoorhanger = function (properties) {
+  this.onFilterInput = this.onFilterInput.bind(this);
+  this.onListDblClick = this.onListDblClick.bind(this);
+  this.onListKeyPress = this.onListKeyPress.bind(this);
+  Doorhangers.DoorhangerBase.call(this, properties);
+};
+
+this.LoginDoorhangers.FillDoorhanger.prototype = {
+  __proto__: Doorhangers.DoorhangerBase.prototype,
+
+  // DoorhangerBase
+  anchorType: "login-fill-notification-icon",
+
+  /**
+   * User-editable string used to filter the list of all logins.
+   */
+  filterString: "",
+
+  /**
+   * Handles text changes in the filter textbox.
+   */
+  onFilterInput() {
+    this.filterString = this.filter.value;
+    this.refreshList();
+  },
+
+  /**
+   * Handles the action associated to a login item.
+   */
+  onListDblClick(event) {
+    if (event.button != 0 || !this.list.selectedItem) {
+      return;
+    }
+    this.fillLogin();
+  },
+  onListKeyPress(event) {
+    if (event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_RETURN ||
+        !this.list.selectedItem) {
+      return;
+    }
+    this.fillLogin();
+  },
+  fillLogin() {
+    // Remove the doorhanger for testing.
+    this.remove();
+  },
+
+  /**
+   * Rebuilds the list of logins.
+   */
+  refreshList() {
+    this.clearList();
+    let formLogins = Services.logins.findLogins({}, "", "", null);
+    let filterToUse = this.filterString.trim().toLowerCase();
+    if (filterToUse) {
+      formLogins = formLogins.filter(login => {
+        return login.hostname.toLowerCase().indexOf(filterToUse) != -1 ||
+               login.username.toLowerCase().indexOf(filterToUse) != -1 ;
+      });
+    }
+    for (let { hostname, username } of formLogins) {
+      let item = this.chomeDocument.createElementNS(XUL_NS, "richlistitem");
+      item.classList.add("login-fill-item");
+      item.setAttribute("hostname", hostname);
+      item.setAttribute("username", username);
+      this.list.appendChild(item);
+    }
+  },
+
+  /**
+   * Clears the list of logins.
+   */
+  clearList() {
+    while (this.list.firstChild) {
+      this.list.removeChild(this.list.firstChild);
+    }
+  },
+
+  // DoorhangerBase
+  bind() {
+    this.element = this.chomeDocument.getElementById("login-fill-doorhanger");
+    this.list = this.chomeDocument.getElementById("login-fill-list");
+    this.filter = this.chomeDocument.getElementById("login-fill-filter");
+
+    this.filter.setAttribute("value", this.filterString);
+
+    this.refreshList();
+
+    this.filter.addEventListener("input", this.onFilterInput);
+    this.list.addEventListener("dblclick", this.onListDblClick);
+    this.list.addEventListener("keypress", this.onListKeyPress);
+
+    this.element.hidden = false;
+  },
+
+  // DoorhangerBase
+  unbind() {
+    this.filter.removeEventListener("input", this.onFilterInput);
+    this.list.removeEventListener("dblclick", this.onListDblClick);
+    this.list.removeEventListener("keypress", this.onListKeyPress);
+
+    this.clearList();
+
+    // Place the element back in the document for the next time we need it.
+    this.element.hidden = true;
+    this.chomeDocument.getElementById("mainPopupSet").appendChild(this.element);
+    Doorhangers.DoorhangerBase.prototype.unbind.call(this);
+  },
+};
+
+/**
+ * Retrieves an existing FillDoorhanger associated with a browser, or null if an
+ * associated doorhanger of that type cannot be found.
+ *
+ * @param An object with the following properties:
+ *        {
+ *          browser:
+ *            The <browser> element to which the doorhanger is associated.
+ *        }
+ */
+this.LoginDoorhangers.FillDoorhanger.find = function ({ browser }) {
+  return Doorhangers.find({
+    browser,
+    anchorType: "login-fill-notification-icon",
+  });
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/content/login.xml
@@ -0,0 +1,22 @@
+<?xml version="1.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/. -->
+
+<!DOCTYPE bindings SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
+
+<bindings id="login-bindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+
+  <binding id="login"
+           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <content orient="vertical">
+      <xul:label class="login-hostname" crop="end"
+                 xbl:inherits="value=hostname"/>
+      <xul:label class="login-username" crop="end"
+                 xbl:inherits="value=username"/>
+    </content>
+  </binding>
+</bindings>
--- a/toolkit/components/passwordmgr/jar.mn
+++ b/toolkit/components/passwordmgr/jar.mn
@@ -1,9 +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/.
 
 toolkit.jar:
 %   content passwordmgr %content/passwordmgr/
+    content/passwordmgr/login.xml                      (content/login.xml)
 *   content/passwordmgr/passwordManager.xul            (content/passwordManager.xul)
     content/passwordmgr/passwordManager.js             (content/passwordManager.js)
     content/passwordmgr/recipes.json                   (content/recipes.json)
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -37,16 +37,17 @@ EXTRA_COMPONENTS += [
 ]
 
 EXTRA_PP_COMPONENTS += [
     'passwordmgr.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
+    'LoginDoorhangers.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
     'LoginManagerParent.jsm',
     'LoginRecipes.jsm',
     'OSCrypto.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':