зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1549808: Add favicons to login list items. r=jaws,fluent-reviewers,flod
Differential Revision: https://phabricator.services.mozilla.com/D38401 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
31fe00df28
Коммит
ce8bda124b
|
@ -154,6 +154,7 @@ let LEGACY_ACTORS = {
|
|||
"AboutLogins:LoginModified",
|
||||
"AboutLogins:LoginRemoved",
|
||||
"AboutLogins:MasterPasswordResponse",
|
||||
"AboutLogins:SendFavicons",
|
||||
"AboutLogins:SyncState",
|
||||
"AboutLogins:UpdateBreaches",
|
||||
],
|
||||
|
|
|
@ -170,6 +170,10 @@ class AboutLoginsChild extends ActorChild {
|
|||
if (masterPasswordPromise) {
|
||||
masterPasswordPromise.resolve(message.data);
|
||||
}
|
||||
break;
|
||||
case "AboutLogins:SendFavicons":
|
||||
this.sendToContent("SendFavicons", message.data);
|
||||
break;
|
||||
case "AboutLogins:SyncState":
|
||||
this.sendToContent("SyncState", message.data);
|
||||
break;
|
||||
|
|
|
@ -35,6 +35,12 @@ ChromeUtils.defineModuleGetter(
|
|||
"resource://services-sync/UIState.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
return LoginHelper.createLogger("AboutLoginsParent");
|
||||
});
|
||||
|
@ -262,16 +268,19 @@ var AboutLoginsParent = {
|
|||
messageManager.sendAsyncMessage("AboutLogins:SyncState", syncState);
|
||||
this.updatePasswordSyncNotificationState();
|
||||
|
||||
if (!BREACH_ALERTS_ENABLED) {
|
||||
return;
|
||||
if (BREACH_ALERTS_ENABLED) {
|
||||
const breachesByLoginGUID = await LoginHelper.getBreachesForLogins(
|
||||
logins
|
||||
);
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:UpdateBreaches",
|
||||
breachesByLoginGUID
|
||||
);
|
||||
}
|
||||
|
||||
const breachesByLoginGUID = await LoginHelper.getBreachesForLogins(
|
||||
logins
|
||||
);
|
||||
messageManager.sendAsyncMessage(
|
||||
"AboutLogins:UpdateBreaches",
|
||||
breachesByLoginGUID
|
||||
"AboutLogins:SendFavicons",
|
||||
await this.getAllFavicons(logins)
|
||||
);
|
||||
} catch (ex) {
|
||||
if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED) {
|
||||
|
@ -374,6 +383,38 @@ var AboutLoginsParent = {
|
|||
}
|
||||
},
|
||||
|
||||
async getFavicon(login) {
|
||||
try {
|
||||
const faviconData = await PlacesUtils.promiseFaviconData(login.hostname);
|
||||
return {
|
||||
faviconData,
|
||||
guid: login.guid,
|
||||
};
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async getAllFavicons(logins) {
|
||||
let favicons = await Promise.all(
|
||||
logins.map(login => this.getFavicon(login))
|
||||
);
|
||||
let vanillaFavicons = {};
|
||||
for (let favicon of favicons) {
|
||||
if (!favicon) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
vanillaFavicons[favicon.guid] = {
|
||||
data: favicon.faviconData.data,
|
||||
dataLen: favicon.faviconData.dataLen,
|
||||
mimeType: favicon.faviconData.mimeType,
|
||||
};
|
||||
} catch (ex) {}
|
||||
}
|
||||
return vanillaFavicons;
|
||||
},
|
||||
|
||||
showMasterPasswordLoginNotifications() {
|
||||
this.showNotifications({
|
||||
id: MASTER_PASSWORD_NOTIFICATION_ID,
|
||||
|
|
|
@ -99,9 +99,14 @@
|
|||
</template>
|
||||
|
||||
<template id="login-list-item-template">
|
||||
<li class="login-list-item" role="option">
|
||||
<span class="title"></span>
|
||||
<span class="username"></span>
|
||||
<li class="login-list-item">
|
||||
<div class="favicon-wrapper">
|
||||
<img class="favicon" src="" alt=""/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="title"></span>
|
||||
<span class="username"></span>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
|
@ -133,6 +138,9 @@
|
|||
<a class="breach-alert-link" data-l10n-id="breach-alert-link" href="#" rel="noopener noreferer" target="_blank"></a>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="login-item-favicon-wrapper">
|
||||
<img class="login-item-favicon" src="" alt=""/>
|
||||
</div>
|
||||
<h2 class="title">
|
||||
<span class="login-item-title"></span>
|
||||
<span class="new-login-title" data-l10n-id="login-item-new-login-title"></span>
|
||||
|
|
|
@ -60,6 +60,10 @@ window.addEventListener("AboutLoginsChromeToContent", event => {
|
|||
updateNoLogins();
|
||||
break;
|
||||
}
|
||||
case "SendFavicons": {
|
||||
gElements.loginList.addFavicons(event.detail.value);
|
||||
break;
|
||||
}
|
||||
case "SyncState": {
|
||||
gElements.fxAccountsButton.updateState(event.detail.value);
|
||||
break;
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
@ -190,6 +191,24 @@
|
|||
box-shadow: 0 0 0 4px var(--in-content-border-active-shadow);
|
||||
}
|
||||
|
||||
.login-item-favicon {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.login-item-favicon-wrapper {
|
||||
margin-inline-end: 12px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
-moz-context-properties: fill;
|
||||
}
|
||||
|
||||
.login-item-favicon-wrapper.hide-default-favicon {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.breach-alert {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--in-content-border-color);
|
||||
|
@ -232,5 +251,9 @@ a.breach-alert-link {
|
|||
box-shadow: 0 2px 8px 0 rgba(249,249,250,0.1);
|
||||
color: #0C0C0D;
|
||||
}
|
||||
|
||||
.login-item-favicon-wrapper {
|
||||
fill: var(--in-content-border-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ export default class LoginItem extends HTMLElement {
|
|||
this._saveChangesButton = this.shadowRoot.querySelector(
|
||||
".save-changes-button"
|
||||
);
|
||||
this._favicon = this.shadowRoot.querySelector(".login-item-favicon");
|
||||
this._faviconWrapper = this.shadowRoot.querySelector(
|
||||
".login-item-favicon-wrapper"
|
||||
);
|
||||
this._title = this.shadowRoot.querySelector(".login-item-title");
|
||||
this._timeCreated = this.shadowRoot.querySelector(".time-created");
|
||||
this._timeChanged = this.shadowRoot.querySelector(".time-changed");
|
||||
|
@ -73,6 +77,7 @@ export default class LoginItem extends HTMLElement {
|
|||
this._originInput.addEventListener("click", this);
|
||||
this._revealCheckbox.addEventListener("click", this);
|
||||
window.addEventListener("AboutLoginsInitialLoginSelected", this);
|
||||
window.addEventListener("AboutLoginsLoadInitialFavicon", this);
|
||||
window.addEventListener("AboutLoginsLoginSelected", this);
|
||||
window.addEventListener("AboutLoginsShowBlankLogin", this);
|
||||
}
|
||||
|
@ -97,6 +102,19 @@ export default class LoginItem extends HTMLElement {
|
|||
timeUsed: this._login.timeLastUsed || "",
|
||||
});
|
||||
|
||||
if (this._login.faviconDataURI) {
|
||||
this._faviconWrapper.classList.add("hide-default-favicon");
|
||||
this._favicon.src = this._login.faviconDataURI;
|
||||
document.l10n.setAttributes(this._favicon, "login-favicon", {
|
||||
title: this._login.title,
|
||||
});
|
||||
this._favicon.hidden = false;
|
||||
} else {
|
||||
// reset the src and alt attributes if the currently selected favicon doesn't have a favicon
|
||||
this._favicon.hidden = true;
|
||||
this._faviconWrapper.classList.remove("hide-default-favicon");
|
||||
}
|
||||
|
||||
this._title.textContent = this._login.title;
|
||||
this._originInput.defaultValue = this._login.origin || "";
|
||||
this._usernameInput.defaultValue = this._login.username || "";
|
||||
|
@ -121,6 +139,10 @@ export default class LoginItem extends HTMLElement {
|
|||
this.setLogin(event.detail, { skipFocusChange: true });
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsLoadInitialFavicon": {
|
||||
this.render();
|
||||
break;
|
||||
}
|
||||
case "AboutLoginsLoginSelected": {
|
||||
let login = event.detail;
|
||||
if (this.hasPendingChanges()) {
|
||||
|
|
|
@ -22,6 +22,9 @@ export default class LoginListItemFactory {
|
|||
static update(listItem, login) {
|
||||
let title = listItem.querySelector(".title");
|
||||
let username = listItem.querySelector(".username");
|
||||
let favicon = listItem.querySelector(".favicon");
|
||||
let faviconWrapper = listItem.querySelector(".favicon-wrapper");
|
||||
|
||||
if (!login.guid) {
|
||||
listItem.id = "new-login-list-item";
|
||||
document.l10n.setAttributes(title, "login-list-item-title-new-login");
|
||||
|
@ -53,5 +56,13 @@ export default class LoginListItemFactory {
|
|||
"login-list-item-subtitle-missing-username"
|
||||
);
|
||||
}
|
||||
|
||||
if (login.faviconDataURI) {
|
||||
faviconWrapper.classList.add("hide-default-favicon");
|
||||
favicon.src = login.faviconDataURI;
|
||||
document.l10n.setAttributes(favicon, "login-favicon", {
|
||||
title: login.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 18px;
|
||||
padding: 5px 16px;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
background-color: var(--in-content-box-background);
|
||||
color: var(--in-content-deemphasized-text);
|
||||
|
@ -74,10 +74,11 @@ ol {
|
|||
}
|
||||
|
||||
.login-list-item {
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
padding-inline-end: 18px;
|
||||
padding-inline-start: 14px;
|
||||
padding-inline-start: 12px;
|
||||
border-inline-start: 4px solid transparent;
|
||||
border-bottom: 1px solid var(--in-content-box-border-color);
|
||||
}
|
||||
|
@ -112,6 +113,23 @@ ol {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.favicon-wrapper {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
|
||||
background-repeat: no-repeat;
|
||||
margin-inline-end: 12px;
|
||||
-moz-context-properties: fill;
|
||||
}
|
||||
|
||||
.favicon-wrapper.hide-default-favicon {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.favicon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 0.85em;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
|
@ -135,5 +153,9 @@ ol {
|
|||
.login-list-item.breached {
|
||||
fill: var(--red-60);
|
||||
}
|
||||
|
||||
.favicon-wrapper {
|
||||
fill: var(--in-content-border-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,6 +216,33 @@ export default class LoginList extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
addFavicons(favicons) {
|
||||
for (let favicon in favicons) {
|
||||
let { login, listItem } = this._logins[favicon];
|
||||
favicon = favicons[favicon];
|
||||
if (favicon && login.title) {
|
||||
favicon.uri = btoa(
|
||||
new Uint8Array(favicon.data).reduce(
|
||||
(data, byte) => data + String.fromCharCode(byte),
|
||||
""
|
||||
)
|
||||
);
|
||||
let faviconDataURI = `data:${favicon.mimeType};base64,${favicon.uri}`;
|
||||
login.faviconDataURI = faviconDataURI;
|
||||
}
|
||||
LoginListItemFactory.update(listItem, login);
|
||||
}
|
||||
let selectedListItem = this._list.querySelector(
|
||||
".login-list-item.selected"
|
||||
);
|
||||
if (selectedListItem) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsLoadInitialFavicon", {})
|
||||
);
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map} breachesByLoginGUID A Map of breaches by login GUIDs used
|
||||
* for displaying breached login indicators.
|
||||
|
|
|
@ -9,6 +9,12 @@ login-filter =
|
|||
|
||||
create-login-button = Create New Login
|
||||
|
||||
# This string is used as alternative text for favicon images.
|
||||
# Variables:
|
||||
# $title (String) - The title of the website associated with the favicon.
|
||||
login-favicon =
|
||||
.alt = Favicon for { $title }
|
||||
|
||||
fxaccounts-sign-in-text = Get your passwords on your other devices
|
||||
fxaccounts-sign-in-button = Sign in to { -sync-brand-short-name }
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче