Bug 1148026 - Add a skeleton of the login fill doorhanger. r=MattN

This commit is contained in:
Paolo Amadini 2015-05-08 13:13:54 +01:00
parent 70a8e89d01
commit ffc7ecd81c
11 changed files with 315 additions and 0 deletions

View File

@ -773,6 +773,14 @@ window[chromehidden~="toolbar"] toolbar:not(#nav-bar):not(#TabsToolbar):not(#pri
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#click-to-play-plugins-notification");
}
#password-fill-notification {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#password-fill-notification");
}
.login-fill-item {
-moz-binding: url("chrome://passwordmgr/content/login.xml#login");
}
.plugin-popupnotification-centeritem {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#plugin-popupnotification-center-item");
}

View File

@ -62,6 +62,13 @@
</popupnotificationcontent>
</popupnotification>
<vbox id="login-fill-doorhanger" hidden="true">
<description id="login-fill-testing"
value="Thanks for testing the login fill doorhanger!"/>
<textbox id="login-fill-filter"/>
<richlistbox id="login-fill-list"/>
</vbox>
#ifdef E10S_TESTING_ONLY
<popupnotification id="enable-e10s-notification" hidden="true">
<popupnotificationcontent orient="vertical"/>

View File

@ -2773,6 +2773,16 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
</handlers>
</binding>
<!-- This is the XBL notification definition for the login fill doorhanger,
which is empty because the actual panel is not implemented inside an XBL
binding, but made of elements added to the notification panel. This
allows accessing the full structure while the panel is hidden. -->
<binding id="password-fill-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
<content>
<children/>
</content>
</binding>
<binding id="splitmenu">
<content>
<xul:hbox anonid="menuitem" flex="1"

View File

@ -1862,6 +1862,7 @@ toolbarbutton.chevron > .toolbarbutton-icon {
%include ../shared/devtools/commandline.inc.css
%include ../shared/plugin-doorhanger.inc.css
%include ../shared/badcontent-doorhanger.inc.css
%include ../shared/login-doorhanger.inc.css
%include downloads/indicator.css

View File

@ -3895,6 +3895,7 @@ notification[value="loop-sharing-notification"] .messageImage {
%include ../shared/devtools/commandline.inc.css
%include ../shared/plugin-doorhanger.inc.css
%include ../shared/badcontent-doorhanger.inc.css
%include ../shared/login-doorhanger.inc.css
%include downloads/indicator.css

View File

@ -0,0 +1,19 @@
#login-fill-testing {
color: #b33;
font-weight: bold;
}
#login-fill-list {
border: 1px solid black;
max-height: 20em;
}
.login-hostname {
margin: 4px;
font-weight: bold;
}
.login-username {
margin: 4px;
color: #888;
}

View File

@ -2456,6 +2456,7 @@ notification[value="loop-sharing-notification"] .messageImage {
%include ../shared/devtools/commandline.inc.css
%include ../shared/plugin-doorhanger.inc.css
%include ../shared/badcontent-doorhanger.inc.css
%include ../shared/login-doorhanger.inc.css
%include downloads/indicator.css

View File

@ -0,0 +1,244 @@
/* 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";
this.EXPORTED_SYMBOLS = [
"LoginDoorhangers",
];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
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);
this.filterString = properties.filterString;
if (properties.browser) {
this.browser = properties.browser;
}
};
this.LoginDoorhangers.FillDoorhanger.prototype = {
/**
* Whether the elements for this doorhanger are currently in the document.
*/
bound: false,
/**
* Associates the doorhanger with its browser. When the tab associated to this
* browser is selected, the anchor icon for the doorhanger will appear.
*
* The browser may change during the lifetime of the doorhanger, in case the
* web page is moved to a different chrome window by the swapDocShells method.
*/
set browser(browser) {
this._browser = browser;
let doorhanger = this;
let PopupNotifications = this.chomeDocument.defaultView.PopupNotifications;
let notification = PopupNotifications.show(
browser,
"password-fill",
"",
"password-notification-icon",
null,
null,
{
dismissed: true,
persistWhileVisible: true,
eventCallback: function (topic, otherBrowser) {
switch (topic) {
case "shown":
// Since we specified the "dismissed" option, this event will only
// be called after the "show" method returns, so the reference to
// "this.notification" will be available at this point.
doorhanger.bound = true;
doorhanger.bind();
break;
case "dismissed":
case "removed":
if (doorhanger.bound) {
doorhanger.unbind();
}
break;
case "swapping":
this._browser = otherBrowser;
return true;
}
return false;
},
}
);
this.notification = notification;
notification.doorhanger = this;
},
get browser() {
return this._browser;
},
_browser: null,
/**
* DOM document to which the doorhanger is currently associated.
*
* This may change during the lifetime of the doorhanger, in case the web page
* is moved to a different chrome window by the swapDocShells method.
*/
get chomeDocument() {
return this.browser.ownerDocument;
},
/**
* Hides this notification, if the notification panel is currently open.
*/
hide() {
let PopupNotifications = this.chomeDocument.defaultView.PopupNotifications;
if (PopupNotifications.isPanelOpen) {
PopupNotifications.panel.hidePopup();
}
},
/**
* Removes the doorhanger from the browser.
*/
remove() {
this.notification.remove();
},
/**
* Binds this doorhanger to its UI controls.
*/
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);
// Move the main element to the notification panel for displaying.
this.notification.owner.panel.firstElementChild.appendChild(this.element);
this.element.hidden = false;
},
/**
* Unbinds this doorhanger from its UI controls.
*/
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);
},
/**
* 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();
},
/**
* 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);
}
},
/**
* 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() {
this.hide();
},
};
/**
* 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 }) {
let PopupNotifications = browser.ownerDocument.defaultView.PopupNotifications;
let notification = PopupNotifications.getNotification("password-fill",
browser);
return notification && notification.doorhanger;
};

View File

@ -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>

View File

@ -4,6 +4,7 @@
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/passwordManagerExceptions.js (content/passwordManagerExceptions.js)

View File

@ -41,6 +41,7 @@ EXTRA_PP_JS_MODULES += [
EXTRA_JS_MODULES += [
'InsecurePasswordUtils.jsm',
'LoginDoorhangers.jsm',
'LoginHelper.jsm',
'LoginManagerContent.jsm',
'LoginRecipes.jsm',