mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
184 lines
6.0 KiB
JavaScript
184 lines
6.0 KiB
JavaScript
/* 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 = ["LoginManagerContextMenu"];
|
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
|
|
"resource://gre/modules/LoginManagerParent.jsm");
|
|
|
|
/*
|
|
* Password manager object for the browser contextual menu.
|
|
*/
|
|
var LoginManagerContextMenu = {
|
|
dateAndTimeFormatter: new Intl.DateTimeFormat(undefined,
|
|
{ day: "numeric", month: "short", year: "numeric" }),
|
|
/**
|
|
* Look for login items and add them to the contextual menu.
|
|
*
|
|
* @param {HTMLInputElement} inputElement
|
|
* The target input element of the context menu click.
|
|
* @param {xul:browser} browser
|
|
* The browser for the document the context menu was open on.
|
|
* @param {nsIURI} documentURI
|
|
* The URI of the document that the context menu was activated from.
|
|
* This isn't the same as the browser's top-level document URI
|
|
* when subframes are involved.
|
|
* @returns {DocumentFragment} a document fragment with all the login items.
|
|
*/
|
|
addLoginsToMenu(inputElement, browser, documentURI) {
|
|
|
|
let foundLogins = this._findLogins(documentURI);
|
|
|
|
if (!foundLogins.length) {
|
|
return null;
|
|
}
|
|
|
|
let fragment = browser.ownerDocument.createDocumentFragment();
|
|
let duplicateUsernames = this._findDuplicates(foundLogins);
|
|
for (let login of foundLogins) {
|
|
let item = fragment.ownerDocument.createElement("menuitem");
|
|
|
|
let username = login.username;
|
|
// If login is empty or duplicated we want to append a modification date to it.
|
|
if (!username || duplicateUsernames.has(username)){
|
|
if (!username) {
|
|
username = this._getLocalizedString("noUsername");
|
|
}
|
|
let meta = login.QueryInterface(Ci.nsILoginMetaInfo);
|
|
let time = this.dateAndTimeFormatter.format(new Date(meta.timePasswordChanged));
|
|
username = this._getLocalizedString("loginHostAge", [username, time]);
|
|
}
|
|
item.setAttribute("label", username);
|
|
item.setAttribute("class", "context-login-item");
|
|
|
|
// login is bound so we can keep the reference to each object.
|
|
item.addEventListener("command", function(login, event) {
|
|
this._fillTargetField(login, inputElement, browser, documentURI);
|
|
}.bind(this, login));
|
|
|
|
fragment.appendChild(item);
|
|
}
|
|
|
|
return fragment;
|
|
},
|
|
|
|
/**
|
|
* Undoes the work of addLoginsToMenu for the same menu.
|
|
*
|
|
* @param {Document}
|
|
* The context menu owner document.
|
|
*/
|
|
clearLoginsFromMenu(document) {
|
|
let loginItems = document.getElementsByClassName("context-login-item");
|
|
while (loginItems.item(0)) {
|
|
loginItems.item(0).remove();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Find logins for the current URI.
|
|
*
|
|
* @param {nsIURI} documentURI
|
|
* URI object with the hostname of the logins we want to find.
|
|
* This isn't the same as the browser's top-level document URI
|
|
* when subframes are involved.
|
|
*
|
|
* @returns {nsILoginInfo[]} a login list
|
|
*/
|
|
_findLogins(documentURI) {
|
|
let logins = Services.logins.findLogins({}, documentURI.prePath, "", "");
|
|
|
|
// Sort logins in alphabetical order and by date.
|
|
logins.sort((loginA, loginB) => {
|
|
// Sort alphabetically
|
|
let result = loginA.username.localeCompare(loginB.username);
|
|
if (result) {
|
|
// Forces empty logins to be at the end
|
|
if (!loginA.username) {
|
|
return 1;
|
|
}
|
|
if (!loginB.username) {
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Same username logins are sorted by last change date
|
|
let metaA = loginA.QueryInterface(Ci.nsILoginMetaInfo);
|
|
let metaB = loginB.QueryInterface(Ci.nsILoginMetaInfo);
|
|
return metaB.timePasswordChanged - metaA.timePasswordChanged;
|
|
});
|
|
|
|
return logins;
|
|
},
|
|
|
|
/**
|
|
* Find duplicate usernames in a login list.
|
|
*
|
|
* @param {nsILoginInfo[]} loginList
|
|
* A list of logins we want to look for duplicate usernames.
|
|
*
|
|
* @returns {Set} a set with the duplicate usernames.
|
|
*/
|
|
_findDuplicates(loginList) {
|
|
let seen = new Set();
|
|
let duplicates = new Set();
|
|
for (let login of loginList) {
|
|
if (seen.has(login.username)) {
|
|
duplicates.add(login.username);
|
|
}
|
|
seen.add(login.username);
|
|
}
|
|
return duplicates;
|
|
},
|
|
|
|
/**
|
|
* @param {nsILoginInfo} login
|
|
* The login we want to fill the form with.
|
|
* @param {Element} inputElement
|
|
* The target input element we want to fill.
|
|
* @param {xul:browser} browser
|
|
* The target tab browser.
|
|
* @param {nsIURI} documentURI
|
|
* URI of the document owning the form we want to fill.
|
|
* This isn't the same as the browser's top-level
|
|
* document URI when subframes are involved.
|
|
*/
|
|
_fillTargetField(login, inputElement, browser, documentURI) {
|
|
LoginManagerParent.fillForm({
|
|
browser: browser,
|
|
loginFormOrigin: documentURI.prePath,
|
|
login: login,
|
|
inputElement: inputElement,
|
|
}).catch(Cu.reportError);
|
|
},
|
|
|
|
/**
|
|
* @param {string} key
|
|
* The localized string key
|
|
* @param {string[]} formatArgs
|
|
* An array of formatting argument string
|
|
*
|
|
* @returns {string} the localized string for the specified key,
|
|
* formatted with arguments if required.
|
|
*/
|
|
_getLocalizedString(key, formatArgs) {
|
|
if (formatArgs) {
|
|
return this._stringBundle.formatStringFromName(key, formatArgs, formatArgs.length);
|
|
}
|
|
return this._stringBundle.GetStringFromName(key);
|
|
},
|
|
};
|
|
|
|
XPCOMUtils.defineLazyGetter(LoginManagerContextMenu, "_stringBundle", function() {
|
|
return Services.strings.
|
|
createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
|
|
});
|