Files
tubestation/browser/components/aboutlogins/AboutLoginsParent.sys.mjs
Sandor Molnar 5095328e2d Backed out 5 changesets (bug 1918702) for causing bc failures @ browser_aaa_eventTelemetry_run_first.js CLOSED TREE
Backed out changeset f55480885aeb (bug 1918702)
Backed out changeset db1eaf09430d (bug 1918702)
Backed out changeset a9179f6e684d (bug 1918702)
Backed out changeset f9d3349e6fe3 (bug 1918702)
Backed out changeset 10d2957bbfe8 (bug 1918702)
2024-09-25 12:38:05 +03:00

888 lines
26 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/. */
// _AboutLogins is only exported for testing
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { E10SUtils } from "resource://gre/modules/E10SUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
LoginBreaches: "resource:///modules/LoginBreaches.sys.mjs",
LoginCSVImport: "resource://gre/modules/LoginCSVImport.sys.mjs",
LoginExport: "resource://gre/modules/LoginExport.sys.mjs",
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
UIState: "resource://services-sync/UIState.sys.mjs",
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "log", () => {
return lazy.LoginHelper.createLogger("AboutLoginsParent");
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"BREACH_ALERTS_ENABLED",
"signon.management.page.breach-alerts.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"FXA_ENABLED",
"identity.fxaccounts.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"VULNERABLE_PASSWORDS_ENABLED",
"signon.management.page.vulnerable-passwords.enabled",
false
);
ChromeUtils.defineLazyGetter(lazy, "AboutLoginsL10n", () => {
return new Localization(["branding/brand.ftl", "browser/aboutLogins.ftl"]);
});
const ABOUT_LOGINS_ORIGIN = "about:logins";
const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
const PRIMARY_PASSWORD_NOTIFICATION_ID = "primary-password-login-required";
// about:logins will always use the privileged content process,
// even if it is disabled for other consumers such as about:newtab.
const EXPECTED_ABOUTLOGINS_REMOTE_TYPE = E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
let _gPasswordRemaskTimeout = null;
const convertSubjectToLogin = subject => {
subject.QueryInterface(Ci.nsILoginMetaInfo).QueryInterface(Ci.nsILoginInfo);
const login = lazy.LoginHelper.loginToVanillaObject(subject);
if (!lazy.LoginHelper.isUserFacingLogin(login)) {
return null;
}
return augmentVanillaLoginObject(login);
};
const SUBDOMAIN_REGEX = new RegExp(/^www\d*\./);
const augmentVanillaLoginObject = login => {
// Note that `displayOrigin` can also include a httpRealm.
let title = login.displayOrigin.replace(SUBDOMAIN_REGEX, "");
return Object.assign({}, login, {
title,
});
};
const EXPORT_PASSWORD_OS_AUTH_DIALOG_MESSAGE_IDS = {
win: "about-logins-export-password-os-auth-dialog-message2-win",
macosx: "about-logins-export-password-os-auth-dialog-message2-macosx",
};
export class AboutLoginsParent extends JSWindowActorParent {
async receiveMessage(message) {
if (!this.browsingContext.embedderElement) {
return;
}
// Only respond to messages sent from a privlegedabout process. Ideally
// we would also check the contentPrincipal.originNoSuffix but this
// check has been removed due to bug 1576722.
if (
this.browsingContext.embedderElement.remoteType !=
EXPECTED_ABOUTLOGINS_REMOTE_TYPE
) {
throw new Error(
`AboutLoginsParent: Received ${message.name} message the remote type didn't match expectations: ${this.browsingContext.embedderElement.remoteType} == ${EXPECTED_ABOUTLOGINS_REMOTE_TYPE}`
);
}
AboutLogins.subscribers.add(this.browsingContext);
switch (message.name) {
case "AboutLogins:CreateLogin": {
await this.#createLogin(message.data.login);
break;
}
case "AboutLogins:DeleteLogin": {
this.#deleteLogin(message.data.login);
break;
}
case "AboutLogins:SortChanged": {
this.#sortChanged(message.data);
break;
}
case "AboutLogins:SyncEnable": {
this.#syncEnable();
break;
}
case "AboutLogins:ImportFromBrowser": {
this.#importFromBrowser();
break;
}
case "AboutLogins:ImportReportInit": {
this.#importReportInit();
break;
}
case "AboutLogins:GetHelp": {
this.#getHelp();
break;
}
case "AboutLogins:OpenPreferences": {
this.#openPreferences();
break;
}
case "AboutLogins:PrimaryPasswordRequest": {
await this.#primaryPasswordRequest(message.data);
break;
}
case "AboutLogins:Subscribe": {
await this.#subscribe();
break;
}
case "AboutLogins:UpdateLogin": {
this.#updateLogin(message.data.login);
break;
}
case "AboutLogins:ExportPasswords": {
await this.#exportPasswords();
break;
}
case "AboutLogins:ImportFromFile": {
await this.#importFromFile();
break;
}
case "AboutLogins:RemoveAllLogins": {
this.#removeAllLogins();
break;
}
}
}
get #ownerGlobal() {
return this.browsingContext.embedderElement?.ownerGlobal;
}
async #createLogin(newLogin) {
if (!Services.policies.isAllowed("removeMasterPassword")) {
if (!lazy.LoginHelper.isPrimaryPasswordSet()) {
this.#ownerGlobal.openDialog(
"chrome://mozapps/content/preferences/changemp.xhtml",
"",
"centerscreen,chrome,modal,titlebar"
);
if (!lazy.LoginHelper.isPrimaryPasswordSet()) {
return;
}
}
}
// Remove the path from the origin, if it was provided.
let origin = lazy.LoginHelper.getLoginOrigin(newLogin.origin);
if (!origin) {
console.error(
"AboutLogins:CreateLogin: Unable to get an origin from the login details."
);
return;
}
newLogin.origin = origin;
Object.assign(newLogin, {
formActionOrigin: "",
usernameField: "",
passwordField: "",
});
newLogin = lazy.LoginHelper.vanillaObjectToLogin(newLogin);
try {
await Services.logins.addLoginAsync(newLogin);
} catch (error) {
this.#handleLoginStorageErrors(newLogin, error);
}
}
get preselectedLogin() {
const preselectedLogin =
this.#ownerGlobal?.gBrowser.selectedTab.getAttribute("preselect-login") ||
this.browsingContext.currentURI?.ref;
this.#ownerGlobal?.gBrowser.selectedTab.removeAttribute("preselect-login");
return preselectedLogin || null;
}
#deleteLogin(loginObject) {
let login = lazy.LoginHelper.vanillaObjectToLogin(loginObject);
Services.logins.removeLogin(login);
}
#sortChanged(sort) {
Services.prefs.setCharPref("signon.management.page.sort", sort);
}
#syncEnable() {
this.#ownerGlobal.gSync.openFxAEmailFirstPage("password-manager");
}
#importFromBrowser() {
try {
lazy.MigrationUtils.showMigrationWizard(this.#ownerGlobal, {
entrypoint: lazy.MigrationUtils.MIGRATION_ENTRYPOINTS.PASSWORDS,
});
} catch (ex) {
console.error(ex);
}
}
#importReportInit() {
let reportData = lazy.LoginCSVImport.lastImportReport;
this.sendAsyncMessage("AboutLogins:ImportReportData", reportData);
}
#getHelp() {
const SUPPORT_URL =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"password-manager-remember-delete-edit-logins";
this.#ownerGlobal.openWebLinkIn(SUPPORT_URL, "tab", {
relatedToCurrent: true,
});
}
#openPreferences() {
this.#ownerGlobal.openPreferences("privacy-logins");
}
async #primaryPasswordRequest(messageId) {
if (!messageId) {
throw new Error("AboutLogins:PrimaryPasswordRequest: no messageId.");
}
let messageText = { value: "NOT SUPPORTED" };
let captionText = { value: "" };
const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF
);
// This feature is only supported on Windows and macOS
// but we still call in to OSKeyStore on Linux to get
// the proper auth_details for Telemetry.
// See bug 1614874 for Linux support.
if (isOSAuthEnabled) {
messageId += "-" + AppConstants.platform;
[messageText, captionText] = await lazy.AboutLoginsL10n.formatMessages([
{
id: messageId,
},
{
id: "about-logins-os-auth-dialog-caption",
},
]);
}
let { isAuthorized, telemetryEvent } = await lazy.LoginHelper.requestReauth(
this.browsingContext.embedderElement,
isOSAuthEnabled,
AboutLogins._authExpirationTime,
messageText.value,
captionText.value
);
this.sendAsyncMessage("AboutLogins:PrimaryPasswordResponse", {
result: isAuthorized,
telemetryEvent,
});
if (isAuthorized) {
AboutLogins._authExpirationTime = Date.now() + AUTH_TIMEOUT_MS;
const remaskPasswords = () => {
this.sendAsyncMessage("AboutLogins:RemaskPassword");
};
clearTimeout(_gPasswordRemaskTimeout);
_gPasswordRemaskTimeout = setTimeout(remaskPasswords, AUTH_TIMEOUT_MS);
}
}
async #subscribe() {
AboutLogins._authExpirationTime = Number.NEGATIVE_INFINITY;
AboutLogins.addObservers();
const logins = await AboutLogins.getAllLogins();
try {
let syncState = await AboutLogins.getSyncState();
let selectedSort = Services.prefs.getCharPref(
"signon.management.page.sort",
"name"
);
if (selectedSort == "breached") {
// The "breached" value was used since Firefox 70 and
// replaced with "alerts" in Firefox 76.
selectedSort = "alerts";
}
this.sendAsyncMessage("AboutLogins:Setup", {
logins,
selectedSort,
syncState,
primaryPasswordEnabled: lazy.LoginHelper.isPrimaryPasswordSet(),
passwordRevealVisible: Services.policies.isAllowed("passwordReveal"),
importVisible:
Services.policies.isAllowed("profileImport") &&
AppConstants.platform != "linux",
preselectedLogin: this.preselectedLogin,
});
await AboutLogins.sendAllLoginRelatedObjects(
logins,
this.browsingContext
);
} catch (ex) {
if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED) {
throw ex;
}
// The message manager may be destroyed before the replies can be sent.
lazy.log.debug(
"AboutLogins:Subscribe: exception when replying with logins",
ex
);
}
}
#updateLogin(loginUpdates) {
let logins = lazy.LoginHelper.searchLoginsWithObject({
guid: loginUpdates.guid,
});
if (logins.length != 1) {
lazy.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;
}
try {
Services.logins.modifyLogin(logins[0], modifiedLogin);
} catch (error) {
this.#handleLoginStorageErrors(modifiedLogin, error);
}
}
async #exportPasswords() {
let messageText = { value: "NOT SUPPORTED" };
let captionText = { value: "" };
const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF
);
// This feature is only supported on Windows and macOS
// but we still call in to OSKeyStore on Linux to get
// the proper auth_details for Telemetry.
// See bug 1614874 for Linux support.
if (isOSAuthEnabled) {
const messageId =
EXPORT_PASSWORD_OS_AUTH_DIALOG_MESSAGE_IDS[AppConstants.platform];
if (!messageId) {
throw new Error(
`AboutLoginsParent: Cannot find l10n id for platform ${AppConstants.platform} for export passwords os auth dialog message`
);
}
[messageText, captionText] = await lazy.AboutLoginsL10n.formatMessages([
{
id: messageId,
},
{
id: "about-logins-os-auth-dialog-caption",
},
]);
}
let { isAuthorized, telemetryEvent } = await lazy.LoginHelper.requestReauth(
this.browsingContext.embedderElement,
true,
null, // Prompt regardless of a recent prompt
messageText.value,
captionText.value
);
let { method, object, extra = {}, value = null } = telemetryEvent;
Services.telemetry.recordEvent("pwmgr", method, object, value, extra);
if (!isAuthorized) {
return;
}
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
function fpCallback(aResult) {
if (aResult != Ci.nsIFilePicker.returnCancel) {
lazy.LoginExport.exportAsCSV(fp.file.path);
Services.telemetry.recordEvent(
"pwmgr",
"mgmt_menu_item_used",
"export_complete"
);
}
}
let [title, defaultFilename, okButtonLabel, csvFilterTitle] =
await lazy.AboutLoginsL10n.formatValues([
{
id: "about-logins-export-file-picker-title2",
},
{
id: "about-logins-export-file-picker-default-filename2",
},
{
id: "about-logins-export-file-picker-export-button",
},
{
id: "about-logins-export-file-picker-csv-filter-title",
},
]);
fp.init(this.browsingContext, title, Ci.nsIFilePicker.modeSave);
fp.appendFilter(csvFilterTitle, "*.csv");
fp.appendFilters(Ci.nsIFilePicker.filterAll);
fp.defaultString = defaultFilename;
fp.defaultExtension = "csv";
fp.okButtonLabel = okButtonLabel;
fp.open(fpCallback);
}
async #importFromFile() {
let [title, okButtonLabel, csvFilterTitle, tsvFilterTitle] =
await lazy.AboutLoginsL10n.formatValues([
{
id: "about-logins-import-file-picker-title2",
},
{
id: "about-logins-import-file-picker-import-button",
},
{
id: "about-logins-import-file-picker-csv-filter-title",
},
{
id: "about-logins-import-file-picker-tsv-filter-title",
},
]);
let { result, path } = await this.openFilePickerDialog(
title,
okButtonLabel,
[
{
title: csvFilterTitle,
extensionPattern: "*.csv",
},
{
title: tsvFilterTitle,
extensionPattern: "*.tsv",
},
]
);
if (result != Ci.nsIFilePicker.returnCancel) {
let summary;
try {
summary = await lazy.LoginCSVImport.importFromCSV(path);
} catch (e) {
console.error(e);
this.sendAsyncMessage(
"AboutLogins:ImportPasswordsErrorDialog",
e.errorType
);
}
if (summary) {
this.sendAsyncMessage("AboutLogins:ImportPasswordsDialog", summary);
Services.telemetry.recordEvent(
"pwmgr",
"mgmt_menu_item_used",
"import_csv_complete"
);
}
}
}
#removeAllLogins() {
Services.logins.removeAllUserFacingLogins();
}
#handleLoginStorageErrors(login, error) {
let messageObject = {
login: augmentVanillaLoginObject(
lazy.LoginHelper.loginToVanillaObject(login)
),
errorMessage: error.message,
};
if (error.message.includes("This login already exists")) {
// See comment in LoginHelper.createLoginAlreadyExistsError as to
// why we need to call .toString() on the nsISupportsString.
messageObject.existingLoginGuid = error.data.toString();
}
this.sendAsyncMessage("AboutLogins:ShowLoginItemError", messageObject);
}
async openFilePickerDialog(title, okButtonLabel, appendFilters) {
return new Promise(resolve => {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(this.browsingContext, title, Ci.nsIFilePicker.modeOpen);
for (const appendFilter of appendFilters) {
fp.appendFilter(appendFilter.title, appendFilter.extensionPattern);
}
fp.appendFilters(Ci.nsIFilePicker.filterAll);
fp.okButtonLabel = okButtonLabel;
fp.open(async result => {
resolve({ result, path: fp.file.path });
});
});
}
}
class AboutLoginsInternal {
subscribers = new WeakSet();
#observersAdded = false;
authExpirationTime = Number.NEGATIVE_INFINITY;
async observe(subject, topic, type) {
if (!ChromeUtils.nondeterministicGetWeakSetKeys(this.subscribers).length) {
this.#removeObservers();
return;
}
switch (topic) {
case "passwordmgr-reload-all": {
await this.#reloadAllLogins();
break;
}
case "passwordmgr-crypto-login": {
this.#removeNotifications(PRIMARY_PASSWORD_NOTIFICATION_ID);
await this.#reloadAllLogins();
break;
}
case "passwordmgr-crypto-loginCanceled": {
this.#showPrimaryPasswordLoginNotifications();
break;
}
case lazy.UIState.ON_UPDATE: {
this.#messageSubscribers(
"AboutLogins:SyncState",
await this.getSyncState()
);
break;
}
case "passwordmgr-storage-changed": {
switch (type) {
case "addLogin": {
await this.#addLogin(subject);
break;
}
case "modifyLogin": {
this.#modifyLogin(subject);
break;
}
case "removeLogin": {
this.#removeLogin(subject);
break;
}
case "removeAllLogins": {
this.#removeAllLogins();
break;
}
}
}
}
}
async #addLogin(subject) {
const login = convertSubjectToLogin(subject);
if (!login) {
return;
}
if (lazy.BREACH_ALERTS_ENABLED) {
this.#messageSubscribers(
"AboutLogins:UpdateBreaches",
await lazy.LoginBreaches.getPotentialBreachesByLoginGUID([login])
);
if (lazy.VULNERABLE_PASSWORDS_ENABLED) {
this.#messageSubscribers(
"AboutLogins:UpdateVulnerableLogins",
await lazy.LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID(
[login]
)
);
}
}
this.#messageSubscribers("AboutLogins:LoginAdded", login);
}
async #modifyLogin(subject) {
subject.QueryInterface(Ci.nsIArrayExtensions);
const login = convertSubjectToLogin(subject.GetElementAt(1));
if (!login) {
return;
}
if (lazy.BREACH_ALERTS_ENABLED) {
let breachesForThisLogin =
await lazy.LoginBreaches.getPotentialBreachesByLoginGUID([login]);
let breachData = breachesForThisLogin.size
? breachesForThisLogin.get(login.guid)
: false;
this.#messageSubscribers(
"AboutLogins:UpdateBreaches",
new Map([[login.guid, breachData]])
);
if (lazy.VULNERABLE_PASSWORDS_ENABLED) {
let vulnerablePasswordsForThisLogin =
await lazy.LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID(
[login]
);
let isLoginVulnerable = !!vulnerablePasswordsForThisLogin.size;
this.#messageSubscribers(
"AboutLogins:UpdateVulnerableLogins",
new Map([[login.guid, isLoginVulnerable]])
);
}
}
this.#messageSubscribers("AboutLogins:LoginModified", login);
}
#removeLogin(subject) {
const login = convertSubjectToLogin(subject);
if (!login) {
return;
}
this.#messageSubscribers("AboutLogins:LoginRemoved", login);
}
#removeAllLogins() {
this.#messageSubscribers("AboutLogins:RemoveAllLogins", []);
}
async #reloadAllLogins() {
let logins = await this.getAllLogins();
this.#messageSubscribers("AboutLogins:AllLogins", logins);
await this.sendAllLoginRelatedObjects(logins);
}
#showPrimaryPasswordLoginNotifications() {
this.#showNotifications({
id: PRIMARY_PASSWORD_NOTIFICATION_ID,
priority: "PRIORITY_WARNING_MEDIUM",
iconURL: "chrome://browser/skin/login.svg",
messageId: "about-logins-primary-password-notification-message",
buttonIds: ["master-password-reload-button"],
onClicks: [
function onReloadClick(browser) {
browser.reload();
},
],
});
this.#messageSubscribers("AboutLogins:PrimaryPasswordAuthRequired");
}
#showNotifications({
id,
priority,
iconURL,
messageId,
buttonIds,
onClicks,
extraFtl = [],
} = {}) {
for (let subscriber of this.#subscriberIterator()) {
let browser = subscriber.embedderElement;
let MozXULElement = browser.ownerGlobal.MozXULElement;
MozXULElement.insertFTLIfNeeded("browser/aboutLogins.ftl");
for (let ftl of extraFtl) {
MozXULElement.insertFTLIfNeeded(ftl);
}
// If there's already an existing notification bar, don't do anything.
let { gBrowser } = browser.ownerGlobal;
let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.getNotificationWithValue(id);
if (notification) {
continue;
}
let buttons = [];
for (let i = 0; i < buttonIds.length; i++) {
buttons[i] = {
"l10n-id": buttonIds[i],
popup: null,
callback: () => {
onClicks[i](browser);
},
};
}
notification = notificationBox.appendNotification(
id,
{
label: { "l10n-id": messageId },
image: iconURL,
priority: notificationBox[priority],
},
buttons
);
}
}
#removeNotifications(notificationId) {
for (let subscriber of this.#subscriberIterator()) {
let browser = subscriber.embedderElement;
let { gBrowser } = browser.ownerGlobal;
let notificationBox = gBrowser.getNotificationBox(browser);
let notification =
notificationBox.getNotificationWithValue(notificationId);
if (!notification) {
continue;
}
notificationBox.removeNotification(notification);
}
}
*#subscriberIterator() {
let subscribers = ChromeUtils.nondeterministicGetWeakSetKeys(
this.subscribers
);
for (let subscriber of subscribers) {
let browser = subscriber.embedderElement;
if (
browser?.remoteType != EXPECTED_ABOUTLOGINS_REMOTE_TYPE ||
browser?.contentPrincipal?.originNoSuffix != ABOUT_LOGINS_ORIGIN
) {
this.subscribers.delete(subscriber);
continue;
}
yield subscriber;
}
}
#messageSubscribers(name, details) {
for (let subscriber of this.#subscriberIterator()) {
try {
if (subscriber.currentWindowGlobal) {
let actor = subscriber.currentWindowGlobal.getActor("AboutLogins");
actor.sendAsyncMessage(name, details);
}
} catch (ex) {
if (ex.result == Cr.NS_ERROR_NOT_INITIALIZED) {
// The actor may be destroyed before the message is sent.
lazy.log.debug(
"messageSubscribers: exception when calling sendAsyncMessage",
ex
);
} else {
throw ex;
}
}
}
}
async getAllLogins() {
try {
let logins = await lazy.LoginHelper.getAllUserFacingLogins();
return logins
.map(lazy.LoginHelper.loginToVanillaObject)
.map(augmentVanillaLoginObject);
} catch (e) {
if (e.result == Cr.NS_ERROR_ABORT) {
// If the user cancels the MP prompt then return no logins.
return [];
}
throw e;
}
}
async sendAllLoginRelatedObjects(logins, browsingContext) {
let sendMessageFn = (name, details) => {
if (browsingContext?.currentWindowGlobal) {
let actor = browsingContext.currentWindowGlobal.getActor("AboutLogins");
actor.sendAsyncMessage(name, details);
} else {
this.#messageSubscribers(name, details);
}
};
if (lazy.BREACH_ALERTS_ENABLED) {
sendMessageFn(
"AboutLogins:SetBreaches",
await lazy.LoginBreaches.getPotentialBreachesByLoginGUID(logins)
);
if (lazy.VULNERABLE_PASSWORDS_ENABLED) {
sendMessageFn(
"AboutLogins:SetVulnerableLogins",
await lazy.LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID(
logins
)
);
}
}
}
async getSyncState() {
const state = lazy.UIState.get();
// As long as Sync is configured, about:logins will treat it as
// authenticated. More diagnostics and error states can be handled
// by other more Sync-specific pages.
const loggedIn = state.status != lazy.UIState.STATUS_NOT_CONFIGURED;
const passwordSyncEnabled = state.syncEnabled && lazy.PASSWORD_SYNC_ENABLED;
const accountURL = await lazy.FxAccounts.config.promiseManageURI(
"password-manager"
);
return {
loggedIn,
email: state.email,
avatarURL: state.avatarURL,
fxAccountsEnabled: lazy.FXA_ENABLED,
passwordSyncEnabled,
accountURL,
};
}
async onPasswordSyncEnabledPreferenceChange(_data, _previous, _latest) {
this.#messageSubscribers(
"AboutLogins:SyncState",
await this.getSyncState()
);
}
#observedTopics = [
"passwordmgr-crypto-login",
"passwordmgr-crypto-loginCanceled",
"passwordmgr-storage-changed",
"passwordmgr-reload-all",
lazy.UIState.ON_UPDATE,
];
addObservers() {
if (!this.#observersAdded) {
for (const topic of this.#observedTopics) {
Services.obs.addObserver(this, topic);
}
this.#observersAdded = true;
}
}
#removeObservers() {
for (const topic of this.#observedTopics) {
Services.obs.removeObserver(this, topic);
}
this.#observersAdded = false;
}
}
let AboutLogins = new AboutLoginsInternal();
export var _AboutLogins = AboutLogins;
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"PASSWORD_SYNC_ENABLED",
"services.sync.engine.passwords",
false,
AboutLogins.onPasswordSyncEnabledPreferenceChange.bind(AboutLogins)
);