Bug 1613620 - Provide an option to remove all logins at once from about:logins. r=prathiksha,fluent-reviewers,sfoster,markh

Depends on D89078

Differential Revision: https://phabricator.services.mozilla.com/D91198
This commit is contained in:
Tim Giles 2020-11-17 21:38:33 +00:00
Родитель f776631bec
Коммит 973bb4dad6
79 изменённых файлов: 938 добавлений и 112 удалений

Просмотреть файл

@ -155,6 +155,7 @@ let JSWINDOWACTORS = {
AboutLoginsOpenPreferences: { wantUntrusted: true },
AboutLoginsOpenSite: { wantUntrusted: true },
AboutLoginsRecordTelemetryEvent: { wantUntrusted: true },
AboutLoginsRemoveAllLogins: { wantUntrusted: true },
AboutLoginsSortChanged: { wantUntrusted: true },
AboutLoginsSyncEnable: { wantUntrusted: true },
AboutLoginsSyncOptions: { wantUntrusted: true },

Просмотреть файл

@ -180,6 +180,10 @@ class AboutLoginsChild extends JSWindowActorChild {
recordTelemetryEvent(event.detail);
break;
}
case "AboutLoginsRemoveAllLogins": {
this.sendAsyncMessage("AboutLogins:RemoveAllLogins");
break;
}
case "AboutLoginsSortChanged": {
this.sendAsyncMessage("AboutLogins:SortChanged", event.detail);
break;

Просмотреть файл

@ -426,6 +426,10 @@ class AboutLoginsParent extends JSWindowActorParent {
fp.open(fpCallback);
break;
}
case "AboutLogins:RemoveAllLogins": {
Services.logins.removeAllUserFacingLogins();
break;
}
}
}
@ -790,12 +794,14 @@ var AboutLogins = {
// authenticated. More diagnostics and error states can be handled
// by other more Sync-specific pages.
const loggedIn = state.status != UIState.STATUS_NOT_CONFIGURED;
const passwordSyncEnabled = state.syncEnabled && PASSWORD_SYNC_ENABLED;
return {
loggedIn,
email: state.email,
avatarURL: state.avatarURL,
fxAccountsEnabled: FXA_ENABLED,
passwordSyncEnabled,
};
},
@ -815,6 +821,7 @@ var AboutLogins = {
onPasswordSyncEnabledPreferenceChange(data, previous, latest) {
Services.prefs.clearUserPref(SHOW_PASSWORD_SYNC_NOTIFICATION_PREF);
this.updatePasswordSyncNotificationState(this.getSyncState(), latest);
this.messageSubscribers("AboutLogins:SyncState", this.getSyncState());
},
};
var _AboutLogins = AboutLogins;

Просмотреть файл

@ -13,6 +13,7 @@
<link rel="localization" href="browser/branding/brandings.ftl">
<link rel="localization" href="browser/aboutLogins.ftl">
<script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
<script type="module" src="chrome://browser/content/aboutlogins/components/remove-logins-dialog.js"></script>
<script type="module" src="chrome://browser/content/aboutlogins/components/fxaccounts-button.js"></script>
<script type="module" src="chrome://browser/content/aboutlogins/components/login-filter.js"></script>
<script type="module" src="chrome://browser/content/aboutlogins/components/login-intro.js"></script>
@ -37,6 +38,7 @@
<login-item></login-item>
<login-intro></login-intro>
<confirmation-dialog hidden></confirmation-dialog>
<remove-logins-dialog hidden></remove-logins-dialog>
<div id="master-password-required-overlay"></div>
<template id="confirmation-dialog-template">
@ -61,6 +63,31 @@
</div>
</template>
<template id="remove-logins-dialog-template">
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/remove-logins-dialog.css">
<div class="overlay">
<div class="container" role="dialog" aria-labelledby="title" aria-describedby="message">
<button class="dismiss-button" data-l10n-id="confirmation-dialog-dismiss-button">
<img class="dismiss-icon" src="chrome://global/skin/icons/close.svg"/>
</button>
<div class="content">
<img class="warning-icon" src="chrome://global/skin/icons/delete.svg"/>
<h1 class="title" id="title"></h1>
<p class="message" id="message"></p>
<div class="checkbox-wrapper">
<input id="confirmation-checkbox" type="checkbox" class="checkbox"></input>
<label for="confirmation-checkbox"></label>
</div>
</div>
<div class="buttons">
<button class="confirm-button danger-button"></button>
<button class="cancel-button" data-l10n-id="confirmation-dialog-cancel-button"></button>
</div>
</div>
</template>
<template id="fxaccounts-button-template">
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
<link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
@ -275,6 +302,7 @@
<button role="menuitem" class="menuitem-button menuitem-import-browser ghost-button" hidden data-supported-platforms="Win32,MacIntel" data-event-name="AboutLoginsImportFromBrowser" data-l10n-id="about-logins-menu-menuitem-import-from-another-browser"></button>
<button role="menuitem" class="menuitem-button menuitem-import-file ghost-button" data-event-name="AboutLoginsImportFromFile" data-l10n-id="about-logins-menu-menuitem-import-from-a-file"></button>
<button role="menuitem" class="menuitem-button menuitem-export ghost-button" data-event-name="AboutLoginsExportPasswordsDialog" data-l10n-id="about-logins-menu-menuitem-export-logins"></button>
<button role="menuitem" class="menuitem-button menuitem-remove-all-logins ghost-button" data-event-name="AboutLoginsRemoveAllLoginsDialog" data-l10n-id="about-logins-menu-menuitem-remove-all-logins"></button>
<hr role="separator" class="menuitem-separator"></hr>
<button role="menuitem" class="menuitem-button menuitem-preferences ghost-button" data-event-name="AboutLoginsOpenPreferences" data-l10n-id="menu-menuitem-preferences"></button>
<hr role="separator" class="menuitem-separator"></hr>

Просмотреть файл

@ -15,6 +15,13 @@ const gElements = {
loginIntro: document.querySelector("login-intro"),
loginItem: document.querySelector("login-item"),
loginFilter: document.querySelector("login-filter"),
menuButton: document.querySelector("menu-button"),
// removeAllLogins button is nested inside of menuButton
get removeAllButton() {
return this.menuButton.shadowRoot.querySelector(
".menuitem-remove-all-logins"
);
},
};
let numberOfLogins = 0;
@ -23,6 +30,7 @@ function updateNoLogins() {
document.documentElement.classList.toggle("no-logins", numberOfLogins == 0);
gElements.loginList.classList.toggle("no-logins", numberOfLogins == 0);
gElements.loginItem.classList.toggle("no-logins", numberOfLogins == 0);
gElements.removeAllButton.disabled = numberOfLogins == 0;
}
function handleAllLogins(logins) {
@ -31,9 +39,14 @@ function handleAllLogins(logins) {
updateNoLogins();
}
let fxaLoggedIn = null;
let passwordSyncEnabled = null;
function handleSyncState(syncState) {
gElements.fxAccountsButton.updateState(syncState);
gElements.loginIntro.updateState(syncState);
fxaLoggedIn = syncState.loggedIn;
passwordSyncEnabled = syncState.passwordSyncEnabled;
}
window.addEventListener("AboutLoginsChromeToContent", event => {
@ -115,6 +128,40 @@ window.addEventListener("AboutLoginsChromeToContent", event => {
}
});
window.addEventListener("AboutLoginsRemoveAllLoginsDialog", () => {
let options = {};
if (fxaLoggedIn && passwordSyncEnabled) {
options.title = "about-logins-confirm-remove-all-sync-dialog-title";
options.message = "about-logins-confirm-remove-all-sync-dialog-message";
} else {
options.title = "about-logins-confirm-remove-all-dialog-title";
options.message = "about-logins-confirm-remove-all-dialog-message";
}
options.confirmCheckboxLabel =
"about-logins-confirm-remove-all-dialog-checkbox-label";
options.confirmButtonLabel =
"about-logins-confirm-remove-all-dialog-confirm-button";
options.count = numberOfLogins;
let dialog = document.querySelector("remove-logins-dialog");
let dialogPromise = dialog.show(options);
try {
dialogPromise.then(
() => {
let removeAllEvt = new CustomEvent("AboutLoginsRemoveAllLogins", {
bubbles: true,
});
window.dispatchEvent(removeAllEvt);
},
() => {}
);
} catch (e) {
if (e != undefined) {
throw e;
}
}
});
window.addEventListener("AboutLoginsExportPasswordsDialog", async event => {
recordTelemetryEvent({
object: "export",

Просмотреть файл

@ -78,6 +78,10 @@
background-image: url("chrome://browser/skin/save.svg");
}
.menuitem-remove-all-logins {
background-image: url("chrome://global/skin/icons/delete.svg");
}
.menuitem-preferences {
background-image: url("chrome://global/skin/icons/settings.svg");
}

Просмотреть файл

@ -129,6 +129,13 @@ export default class MenuButton extends HTMLElement {
if (this._menu.hidden) {
this._showMenu();
}
if (successor.disabled) {
if (next) {
successor = items[activeItemIndex + 2];
} else {
successor = items[activeItemIndex - 2];
}
}
successor.focus();
}

Просмотреть файл

@ -0,0 +1,114 @@
/* 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/. */
.overlay {
position: fixed;
z-index: 1;
inset: 0;
/* TODO: this color is used in the about:preferences overlay, but
why isn't it declared as a variable? */
background-color: rgba(0,0,0,0.5);
display: flex;
}
.container {
z-index: 2;
position: relative;
display: flex;
flex-direction: column;
min-width: 300px;
max-width: 660px;
min-height: 200px;
align-self: center;
margin-inline-start: calc(var(--sidebar-width) - 48px);
background-color: var(--in-content-page-background);
color: var(--in-content-text-color);
box-shadow: var(--shadow-30);
/* show a border in high contrast mode */
outline: 1px solid transparent;
}
.title {
grid-area: 1 / 2 / 2 / 8;
}
.message {
font-weight: 600;
grid-area: 2 / 2 / 3 / 8;
font-size: 1.25em;
}
label[for="confirmation-checkbox"] {
font-size: 1.25em;
}
.dismiss-button {
position: absolute;
top: 0;
inset-inline-end: 0;
min-width: 20px;
min-height: 20px;
margin: 16px;
padding: 0;
line-height: 0;
}
.dismiss-icon {
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
fill-opacity: 0;
}
.warning-icon {
-moz-context-properties: fill;
fill: currentColor;
width: 32px;
height: 32px;
margin: 8px;
}
.content,
.buttons {
padding: 36px 48px;
padding-bottom: 24px;
}
.content {
display: grid;
grid-template-columns: 0.5fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 0.5fr 0.5fr 0.5fr;
}
.checkbox-wrapper {
display: flex;
grid-area: 3 / 2 / 4 / 8;
align-self: first baseline;
}
.warning-icon {
grid-area: 1 / 1 / 2 / 2;
}
.checkbox {
grid-area: 3 / 2 / 4 / 8;
font-size: 1.1em;
align-self: center;
}
.buttons {
display: flex;
justify-content: flex-end;
padding-block: 16px 32px;
padding-inline: 48px 0;
border-top: 1px solid var(--in-content-border-color);
margin-inline: 48px;
}
.buttons.macosx > .confirm-button {
order: 1;
}
.buttons > button {
min-width: 140px;
}

Просмотреть файл

@ -0,0 +1,119 @@
/* 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/. */
import { setKeyboardAccessForNonDialogElements } from "../aboutLoginsUtils.js";
export default class RemoveLoginsDialog extends HTMLElement {
constructor() {
super();
this._promise = null;
}
connectedCallback() {
if (this.shadowRoot) {
return;
}
let template = document.querySelector("#remove-logins-dialog-template");
let shadowRoot = this.attachShadow({ mode: "open" });
document.l10n.connectRoot(shadowRoot);
shadowRoot.appendChild(template.content.cloneNode(true));
this._buttons = this.shadowRoot.querySelector(".buttons");
this._cancelButton = this.shadowRoot.querySelector(".cancel-button");
this._confirmButton = this.shadowRoot.querySelector(".confirm-button");
this._dismissButton = this.shadowRoot.querySelector(".dismiss-button");
this._message = this.shadowRoot.querySelector(".message");
this._overlay = this.shadowRoot.querySelector(".overlay");
this._title = this.shadowRoot.querySelector(".title");
this._checkbox = this.shadowRoot.querySelector(".checkbox");
this._checkboxLabel = this.shadowRoot.querySelector(
"label[for='confirmation-checkbox']"
);
this._buttons.classList.toggle("macosx", navigator.platform == "MacIntel");
}
handleEvent(event) {
switch (event.type) {
case "keydown":
if (event.key === "Escape" && !event.defaultPrevented) {
this.onCancel();
}
break;
case "click":
if (
event.target.classList.contains("cancel-button") ||
event.currentTarget.classList.contains("dismiss-button") ||
event.target.classList.contains("overlay")
) {
this.onCancel();
} else if (event.target.classList.contains("confirm-button")) {
this.onConfirm();
} else if (event.target.classList.contains("checkbox")) {
this._confirmButton.disabled = !this._checkbox.checked;
}
}
}
hide() {
setKeyboardAccessForNonDialogElements(true);
this._cancelButton.removeEventListener("click", this);
this._confirmButton.removeEventListener("click", this);
this._dismissButton.removeEventListener("click", this);
this._overlay.removeEventListener("click", this);
this._checkbox.removeEventListener("click", this);
window.removeEventListener("keydown", this);
this._checkbox.checked = false;
this.hidden = true;
}
show({ title, message, confirmButtonLabel, confirmCheckboxLabel, count }) {
setKeyboardAccessForNonDialogElements(false);
this.hidden = false;
document.l10n.setAttributes(this._title, title, {
count,
});
document.l10n.setAttributes(this._message, message, {
count,
});
document.l10n.setAttributes(this._confirmButton, confirmButtonLabel);
document.l10n.setAttributes(this._checkboxLabel, confirmCheckboxLabel, {
count,
});
this._checkbox.addEventListener("click", this);
this._cancelButton.addEventListener("click", this);
this._confirmButton.addEventListener("click", this);
this._dismissButton.addEventListener("click", this);
this._overlay.addEventListener("click", this);
window.addEventListener("keydown", this);
this._confirmButton.disabled = true;
// For speed-of-use, focus the confirmation checkbox when the dialog loads.
// Introducing this checkbox provides enough of a buffer for accidental deletions.
this._checkbox.focus();
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
return this._promise;
}
onCancel() {
this._reject();
this.hide();
}
onConfirm() {
this._resolve();
this.hide();
}
}
customElements.define("remove-logins-dialog", RemoveLoginsDialog);

Просмотреть файл

@ -5,6 +5,8 @@
browser.jar:
content/browser/aboutlogins/components/confirmation-dialog.css (content/components/confirmation-dialog.css)
content/browser/aboutlogins/components/confirmation-dialog.js (content/components/confirmation-dialog.js)
content/browser/aboutlogins/components/remove-logins-dialog.css (content/components/remove-logins-dialog.css)
content/browser/aboutlogins/components/remove-logins-dialog.js (content/components/remove-logins-dialog.js)
content/browser/aboutlogins/components/fxaccounts-button.css (content/components/fxaccounts-button.css)
content/browser/aboutlogins/components/fxaccounts-button.js (content/components/fxaccounts-button.js)
content/browser/aboutlogins/components/login-filter.css (content/components/login-filter.css)

Просмотреть файл

@ -37,6 +37,7 @@ skip-if = (os != "win" && os != "mac") # import is only available on Windows and
[browser_openSite.js]
[browser_osAuthDialog.js]
skip-if = (os == 'linux') # bug 1527745
[browser_removeAllDialog.js]
[browser_sessionRestore.js]
skip-if = debug # Bug 1576876
[browser_tabKeyNav.js]

Просмотреть файл

@ -50,7 +50,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -36,7 +36,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -21,7 +21,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -10,7 +10,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(aboutLoginsTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -10,7 +10,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -124,7 +124,7 @@ add_task(async function test_all_logins_removed() {
);
});
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
await SpecialPowers.spawn(browser, [], async () => {
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));

Просмотреть файл

@ -25,7 +25,7 @@ add_task(async function setup() {
TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
info(`TEST_LOGIN3 added with guid=${TEST_LOGIN3.guid}`);
registerCleanupFunction(() => {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.prefs.clearUserPref(SORT_PREF_NAME);
});
});

Просмотреть файл

@ -43,7 +43,7 @@ add_task(async function test() {
await mpDialogShown;
registerCleanupFunction(async function() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

Просмотреть файл

@ -26,7 +26,7 @@ add_task(async function setup() {
await tabOpenedPromise;
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -11,7 +11,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -25,7 +25,7 @@ add_task(async function test() {
});
registerCleanupFunction(function() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

Просмотреть файл

@ -0,0 +1,326 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm", this);
async function openRemoveAllDialog(browser) {
await SimpleTest.promiseFocus(browser);
await BrowserTestUtils.synthesizeMouseAtCenter("menu-button", {}, browser);
function getRemoveAllMenuButton() {
let menuButton = window.document.querySelector("menu-button");
return menuButton.shadowRoot.querySelector(".menuitem-remove-all-logins");
}
await BrowserTestUtils.synthesizeMouseAtCenter(
getRemoveAllMenuButton,
{},
browser
);
info("remove all dialog should be opened");
}
async function waitForRemoveAllLogins() {
return new Promise(resolve => {
Services.obs.addObserver(function observer(subject, topic, changeType) {
if (changeType != "removeAllLogins") {
return;
}
Services.obs.removeObserver(observer, "passwordmgr-storage-changed");
resolve();
}, "passwordmgr-storage-changed");
});
}
add_task(async function setup() {
await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: "about:logins",
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllUserFacingLogins();
});
TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
});
add_task(async function test_remove_all_dialog_l10n() {
ok(TEST_LOGIN1, "test_login1");
let browser = gBrowser.selectedBrowser;
await openRemoveAllDialog(browser);
await SpecialPowers.spawn(browser, [], async () => {
const EventUtils = ContentTaskUtils.getEventUtils(content);
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
ok(!dialog.hidden);
let title = dialog.shadowRoot.querySelector(".title");
let message = dialog.shadowRoot.querySelector(".message");
let label = dialog.shadowRoot.querySelector(
"label[for='confirmation-checkbox']"
);
let cancelButton = dialog.shadowRoot.querySelector(".cancel-button");
let removeAllButton = dialog.shadowRoot.querySelector(".confirm-button");
await content.document.l10n.translateElements([
title,
message,
label,
cancelButton,
removeAllButton,
]);
is(
title.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-title",
"Title contents should match l10n-id attribute set on element"
);
is(
message.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-message",
"Message contents should match l10n-id attribute set on element"
);
is(
label.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-checkbox-label",
"Label contents should match l10n-id attribute set on outer element"
);
is(
cancelButton.dataset.l10nId,
"confirmation-dialog-cancel-button",
"Cancel button contents should match l10n-id attribute set on outer element"
);
is(
removeAllButton.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-confirm-button",
"Remove all button contents should match l10n-id attribute set on outer element"
);
is(
JSON.parse(title.dataset.l10nArgs).count,
1,
"Title contents should match l10n-args attribute set on element"
);
is(
JSON.parse(message.dataset.l10nArgs).count,
1,
"Message contents should match l10n-args attribute set on element"
);
is(
JSON.parse(label.dataset.l10nArgs).count,
1,
"Label contents should match l10n-id attribute set on outer element"
);
EventUtils.synthesizeMouseAtCenter(
dialog.shadowRoot.querySelector(".cancel-button"),
{},
content
);
await ContentTaskUtils.waitForCondition(
() => dialog.hidden,
"Waiting for the dialog to be hidden after clicking cancel button"
);
});
});
add_task(async function test_remove_all_dialog_keyboard_navigation() {
let browser = gBrowser.selectedBrowser;
await openRemoveAllDialog(browser);
await SpecialPowers.spawn(browser, [], async () => {
const EventUtils = ContentTaskUtils.getEventUtils(content);
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
let cancelButton = dialog.shadowRoot.querySelector(".cancel-button");
let removeAllButton = dialog.shadowRoot.querySelector(".confirm-button");
is(
removeAllButton.disabled,
true,
"Remove all should be disabled on dialog open"
);
await EventUtils.synthesizeKey(" ", {}, content);
is(
removeAllButton.disabled,
false,
"Remove all should be enabled when activating the checkbox"
);
await EventUtils.synthesizeKey(" ", {}, content);
is(
removeAllButton.disabled,
true,
"Remove all should be disabled after deactivating the checkbox"
);
await EventUtils.synthesizeKey("KEY_Tab", {}, content);
is(
dialog.shadowRoot.activeElement,
cancelButton,
"Cancel button should be the next element in tab order"
);
await EventUtils.synthesizeKey(" ", {}, content);
await ContentTaskUtils.waitForCondition(
() => dialog.hidden,
"Waiting for the dialog to be hidden after activating cancel button via Space key"
);
});
await openRemoveAllDialog(browser);
await SpecialPowers.spawn(browser, [], async () => {
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
await EventUtils.synthesizeKey("KEY_Escape", {}, content);
await ContentTaskUtils.waitForCondition(
() => dialog.hidden,
"Waiting for the dialog to be hidden after activating Escape key"
);
});
await openRemoveAllDialog(browser);
await SpecialPowers.spawn(browser, [], async () => {
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
let dismissButton = dialog.shadowRoot.querySelector(".dismiss-button");
await EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }, content);
is(
dialog.shadowRoot.activeElement,
dismissButton,
"dismiss button should be focused"
);
await EventUtils.synthesizeKey(" ", {}, content);
await ContentTaskUtils.waitForCondition(
() => dialog.hidden,
"Waiting for the dialog to be hidden after activating X button"
);
});
});
add_task(async function test_remove_all_dialog_remove_logins() {
TEST_LOGIN2 = await addLogin(TEST_LOGIN2);
let browser = gBrowser.selectedBrowser;
let removeAllPromise = waitForRemoveAllLogins();
await openRemoveAllDialog(browser);
await SpecialPowers.spawn(browser, [], async () => {
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
let title = dialog.shadowRoot.querySelector(".title");
let message = dialog.shadowRoot.querySelector(".message");
let label = dialog.shadowRoot.querySelector(
"label[for='confirmation-checkbox']"
);
let cancelButton = dialog.shadowRoot.querySelector(".cancel-button");
let removeAllButton = dialog.shadowRoot.querySelector(".confirm-button");
let checkbox = dialog.shadowRoot.querySelector(".checkbox");
await content.document.l10n.translateElements([
title,
message,
cancelButton,
removeAllButton,
label,
checkbox,
]);
is(
dialog.shadowRoot.activeElement,
checkbox,
"Checkbox should be the focused element on dialog open"
);
is(
title.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-title",
"Title contents should match l10n-id attribute set on element"
);
is(
JSON.parse(title.dataset.l10nArgs).count,
2,
"Title contents should match l10n-args attribute set on element"
);
is(
message.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-message",
"Message contents should match l10n-id attribute set on element"
);
is(
JSON.parse(message.dataset.l10nArgs).count,
2,
"Message contents should match l10n-args attribute set on element"
);
is(
label.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-checkbox-label",
"Label contents should match l10n-id attribute set on outer element"
);
is(
JSON.parse(label.dataset.l10nArgs).count,
2,
"Label contents should match l10n-id attribute set on outer element"
);
is(
cancelButton.dataset.l10nId,
"confirmation-dialog-cancel-button",
"Cancel button contents should match l10n-id attribute set on outer element"
);
is(
removeAllButton.dataset.l10nId,
"about-logins-confirm-remove-all-dialog-confirm-button",
"Remove all button contents should match l10n-id attribute set on outer element"
);
is(
removeAllButton.disabled,
true,
"Remove all button should be disabled on dialog open"
);
});
function activateConfirmCheckbox() {
let dialog = window.document.querySelector("remove-logins-dialog");
return dialog.shadowRoot.querySelector(".checkbox");
}
await BrowserTestUtils.synthesizeMouseAtCenter(
activateConfirmCheckbox,
{},
browser
);
await SpecialPowers.spawn(browser, [], async () => {
let dialog = Cu.waiveXrays(
content.document.querySelector("remove-logins-dialog")
);
let removeAllButton = dialog.shadowRoot.querySelector(".confirm-button");
is(
removeAllButton.disabled,
false,
"Remove all should be enabled after clicking the checkbox"
);
});
function getDialogRemoveAllButton() {
let dialog = window.document.querySelector("remove-logins-dialog");
return dialog.shadowRoot.querySelector(".confirm-button");
}
await BrowserTestUtils.synthesizeMouseAtCenter(
getDialogRemoveAllButton,
{},
browser
);
await removeAllPromise;
await SpecialPowers.spawn(browser, [], async () => {
let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
await ContentTaskUtils.waitForCondition(
() => content.document.documentElement.classList.contains("no-logins"),
"Waiting for no logins view since all logins should be deleted"
);
await ContentTaskUtils.waitForCondition(
() => loginList.classList.contains("no-logins"),
"Waiting for login-list to be in no logins view as all logins should be deleted"
);
});
await BrowserTestUtils.synthesizeMouseAtCenter("menu-button", {}, browser);
await SpecialPowers.spawn(browser, [], async () => {
let menuButton = content.document.querySelector("menu-button");
let removeAllMenuButton = menuButton.shadowRoot.querySelector(
".menuitem-remove-all-logins"
);
ok(
removeAllMenuButton.disabled,
"Remove all logins menu button is disabled if there are no logins"
);
});
});

Просмотреть файл

@ -23,7 +23,7 @@ async function checkLoginDisplayed(browser, testGuid) {
add_task(async function() {
TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
registerCleanupFunction(() => {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
const testGuid = TEST_LOGIN1.guid;

Просмотреть файл

@ -22,7 +22,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -11,7 +11,7 @@ add_task(async function setup() {
});
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -43,7 +43,7 @@ add_task(async function setup() {
registerCleanupFunction(async () => {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
await BrowserTestUtils.closeWindow(newWin);
});
});

Просмотреть файл

@ -95,6 +95,7 @@ add_task(async function test_menu_open_close() {
sendKey("TAB"); // Import from file
}
sendKey("TAB"); // Export
sendKey("TAB"); // Remove All Logins
if (navigator.platform == "Win32" || navigator.platform == "MacIntel") {
// The Import menuitem is only visible on Windows/macOS, where we will need another Tab
@ -145,6 +146,7 @@ add_task(async function test_menu_keyboard_cycling() {
let allItems = [
"menuitem-export",
"menuitem-remove-all-logins",
"menuitem-preferences",
"menuitem-help",
];

Просмотреть файл

@ -199,7 +199,7 @@ add_task(async function test_breachAlertHiddenAfterDismissal() {
);
info("Clear login storage");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
const breachesByLoginGUID2 = await LoginBreaches.getPotentialBreachesByLoginGUID(
[BREACHED_LOGIN, NOT_BREACHED_LOGIN],

Просмотреть файл

@ -38,7 +38,7 @@ function addLogin(host, timestamp) {
}
async function setupPasswords() {
loginManager.removeAllLogins();
loginManager.removeAllUserFacingLogins();
addLogin(FXA_HOST, REFERENCE_DATE);
addLogin(NEW_HOST, REFERENCE_DATE);
addLogin(OLD_HOST, REFERENCE_DATE - 10000);

Просмотреть файл

@ -272,7 +272,7 @@ add_task(async function setup() {
}
registerCleanupFunction(() => {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
if (loginCrypto.finalize) {
loginCrypto.finalize();
}
@ -327,7 +327,7 @@ add_task(async function test_importExistingLogins() {
"Sanity check the source exists"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let logins = Services.logins.getAllLogins();
Assert.equal(
logins.length,

Просмотреть файл

@ -50,7 +50,7 @@ add_task(async function setup() {
}
registerCleanupFunction(() => {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
});

Просмотреть файл

@ -1293,7 +1293,7 @@ add_task(async function test_passwordsAvailable() {
let hashes = []; // the hashes of all migrator websites, this is going to be used for the clean up
registerCleanupFunction(() => {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
logins = Services.logins.getAllLogins();
Assert.equal(logins.length, 0, "There are no logins after the cleanup");
// remove all the values created in this test from the registry

Просмотреть файл

@ -83,7 +83,7 @@ const mockGetMonitorData = data => {
};
registerCleanupFunction(function head_cleanup() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
// Used to replace AboutProtectionsParent.VPNSubStatus and Region.current

Просмотреть файл

@ -23,6 +23,7 @@ menu =
about-logins-menu-menuitem-import-from-another-browser = Import from Another Browser…
about-logins-menu-menuitem-import-from-a-file = Import from a File…
about-logins-menu-menuitem-export-logins = Export Logins…
about-logins-menu-menuitem-remove-all-logins = Remove All Logins…
menu-menuitem-preferences =
{ PLATFORM() ->
[windows] Options
@ -160,6 +161,35 @@ about-logins-confirm-remove-dialog-title = Remove this login?
confirm-delete-dialog-message = This action cannot be undone.
about-logins-confirm-remove-dialog-confirm-button = Remove
about-logins-confirm-remove-all-dialog-confirm-button = Remove All
about-logins-confirm-remove-all-dialog-checkbox-label =
{ $count ->
[1] Yes, remove this login
*[other] Yes, remove these logins
}
about-logins-confirm-remove-all-dialog-title =
{ $count ->
[one] Remove { $count } login?
*[other] Remove all { $count } logins?
}
about-logins-confirm-remove-all-dialog-message =
{ $count ->
[1] This will remove the login youve saved to { -brand-short-name } and any breach alerts that appear here. You wont be able to undo this action.
*[other] This will remove the logins youve saved to { -brand-short-name } and any breach alerts that appear here. You wont be able to undo this action.
}
about-logins-confirm-remove-all-sync-dialog-title =
{ $count ->
[one] Remove { $count } login from all devices?
*[other] Remove all { $count } logins from all devices?
}
about-logins-confirm-remove-all-sync-dialog-message=
{ $count ->
[1] This will remove the login youve saved to { -brand-short-name } on all devices synced to your { -fxaccount-brand-name }. This will also remove breach alerts that appear here. You wont be able to undo this action.
*[other] This will remove all logins youve saved to { -brand-short-name } on all devices synced to your { -fxaccount-brand-name }. This will also remove breach alerts that appear here. You wont be able to undo this action.
}
about-logins-confirm-export-dialog-title = Export logins and passwords
about-logins-confirm-export-dialog-message = Your passwords will be saved as readable text (e.g., BadP@ssw0rd) so anyone who can open the exported file can view them.
about-logins-confirm-export-dialog-confirm-button = Export…

Просмотреть файл

@ -14,7 +14,7 @@ function resetPassword() {
token.reset();
try {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
} catch (e) {}
let l10n = new Localization(["security/pippki/pippki.ftl"], true);

Просмотреть файл

@ -411,7 +411,7 @@ PasswordStore.prototype = {
},
async wipe() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
},
};
@ -463,8 +463,18 @@ PasswordTracker.prototype = {
}
break;
// Bug 1613620: We iterate through the removed logins and track them to ensure
// the logins are deleted across synced devices/accounts
case "removeAllLogins":
this._log.trace(data);
subject.QueryInterface(Ci.nsIArrayExtensions);
let count = subject.Count();
for (let i = 0; i < count; i++) {
let currentSubject = subject.GetElementAt(i);
let tracked = await this._trackLogin(currentSubject);
if (tracked) {
this._log.trace(data + ": " + currentSubject.guid);
}
}
this.score += SCORE_INCREMENT_XLARGE;
break;
}

Просмотреть файл

@ -106,3 +106,51 @@ add_task(async function test_onWipe() {
await tracker.stop();
}
});
add_task(async function test_removeAllLogins() {
let recordNum = 0;
_("Verify that all tracked logins are removed.");
async function createPassword() {
_("RECORD NUM: " + recordNum);
let record = {
id: "GUID" + recordNum,
hostname: "http://foo.bar.com",
formSubmitURL: "http://foo.bar.com",
username: "john" + recordNum,
password: "smith",
usernameField: "username",
passwordField: "password",
};
recordNum++;
let login = store._nsLoginInfoFromRecord(record);
Services.logins.addLogin(login);
await tracker.asyncObserver.promiseObserversComplete();
}
try {
_("Tell tracker to start tracking changes");
tracker.start();
await createPassword();
await createPassword();
let changes = await tracker.getChangedIDs();
do_check_attribute_count(changes, 2);
Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 2);
await tracker.clearChangedIDs();
changes = await tracker.getChangedIDs();
do_check_attribute_count(changes, 0);
_("Tell sync to remove all logins");
Services.logins.removeAllUserFacingLogins();
await tracker.asyncObserver.promiseObserversComplete();
changes = await tracker.getChangedIDs();
do_check_attribute_count(changes, 2);
Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 5);
} finally {
_("Clean up.");
await store.wipe();
await tracker.clearChangedIDs();
tracker.resetScore();
await tracker.stop();
}
});

Просмотреть файл

@ -30,7 +30,7 @@ function run_test() {
add_task(async function test_verifyLogin() {
// This test expects a clean slate -- no saved passphrase.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let johnHelper = track_collections_helper();
let johnU = johnHelper.with_updated_collection;

Просмотреть файл

@ -318,7 +318,7 @@ async function test_login_manager_logins_not_cleared_with_uri_contains_domain()
await ForgetAboutSite.removeDataFromDomain("mozilla.org");
check_login_exists(TEST_HOST, true);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
check_login_exists(TEST_HOST, false);
}

Просмотреть файл

@ -436,10 +436,24 @@ LoginManager.prototype = {
},
/**
* Remove all stored logins.
* Remove all user facing stored logins.
*
* This will not remove the FxA Sync key, which is stored with the rest of a user's logins.
*/
removeAllUserFacingLogins() {
log.debug("Removing all user facing logins");
this._storage.removeAllUserFacingLogins();
},
/**
* Remove all logins from data store, including the FxA Sync key.
*
* NOTE: You probably want `removeAllUserFacingLogins()` instead of this function.
* This function will remove the FxA Sync key, which will break syncing of saved user data
* e.g. bookmarks, history, open tabs, logins and passwords, add-ons, and options
*/
removeAllLogins() {
log.debug("Removing all logins");
log.debug("Removing all logins from local store, including FxA key");
this._storage.removeAllLogins();
},

Просмотреть файл

@ -108,7 +108,7 @@ LoginStore.prototype._save = async function() {
/**
* Delete logins backup file if the last saved login was removed using
* removeLogin() or if all logins were removed at once using removeAllLogins().
* removeLogin() or if all logins were removed at once using removeAllUserFacingLogins().
* Note that if the user has a fxa key stored as a login, we just update the
* backup to only store the key when the last saved user facing login is removed.
*/

Просмотреть файл

@ -101,11 +101,22 @@ interface nsILoginManager : nsISupports {
void recordPasswordUse(in nsILoginInfo aLogin, in boolean aPrivateContextWithoutExplicitConsent, in AString aLoginType, in boolean aFilled);
/**
* Remove all logins known to login manager.
* Remove all stored user facing logins.
*
* This will remove all the logins that a user can access through about:logins.
* This will not remove the FxA Sync key which is stored with the rest of a user's logins
* but is not accessible through about:logins
*
* The browser sanitization feature allows the user to clear any stored
* passwords. This interface allows that to be done without getting each
* login first (which might require knowing the master password).
* login first.
*
*/
void removeAllUserFacingLogins();
/**
* Completely remove all logins, including the user's FxA Sync key.
*
*/
void removeAllLogins();

Просмотреть файл

@ -106,11 +106,22 @@ interface nsILoginManagerStorage : nsISupports {
void recordPasswordUse(in nsILoginInfo aLogin);
/**
* Remove all stored logins.
* Remove all stored user facing logins.
*
* This will remove all the logins that a user can access through about:logins.
* This will not remove the FxA Sync key which is stored with the rest of a user's logins
* but is not accessible through about:logins
*
* The browser sanitization feature allows the user to clear any stored
* passwords. This interface allows that to be done without getting each
* login first (which might require knowing the master password).
* login first.
*
*/
void removeAllUserFacingLogins();
/**
* Completely remove all logins, including the user's FxA key.
*
*/
void removeAllLogins();

Просмотреть файл

@ -35,6 +35,11 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIUUIDGenerator"
);
XPCOMUtils.defineLazyModuleGetters(this, {
FXA_PWDMGR_HOST: "resource://gre/modules/FxAccountsCommon.js",
FXA_PWDMGR_REALM: "resource://gre/modules/FxAccountsCommon.js",
});
class LoginManagerStorage_json {
constructor() {
this.__crypto = null; // nsILoginManagerCrypto service
@ -639,19 +644,50 @@ class LoginManagerStorage_json {
}
/**
* Removes all logins from storage.
* Removes all logins from local storage, including FxA Sync key.
*
* NOTE: You probably want removeAllUserFacingLogins instead of this function.
*
*/
removeAllLogins() {
this._store.ensureDataReady();
this.log("Removing all logins");
this._store.data.logins = [];
this._store.data.potentiallyVulnerablePasswords = [];
this.__decryptedPotentiallyVulnerablePasswords = null;
this._store.data.dismissedBreachAlertsByLoginGUID = {};
this._store.saveSoon();
LoginHelper.notifyStorageChanged("removeAllLogins", null);
LoginHelper.notifyStorageChanged("removeAllLogins", []);
}
/**
* Removes all user facing logins from storage. e.g. all logins except the FxA Sync key
*
* If you need to remove the FxA key, use `removeAllLogins` instead
*/
removeAllUserFacingLogins() {
this._store.ensureDataReady();
this.log("Removing all logins");
let [allLogins, ids] = this._searchLogins({});
let fxaKey = this._store.data.logins.find(
login =>
login.hostname == FXA_PWDMGR_HOST && login.httpRealm == FXA_PWDMGR_REALM
);
if (fxaKey) {
this._store.data.logins = [fxaKey];
allLogins = allLogins.filter(item => item != fxaKey);
} else {
this._store.data.logins = [];
}
this._store.data.potentiallyVulnerablePasswords = [];
this.__decryptedPotentiallyVulnerablePasswords = null;
this._store.data.dismissedBreachAlertsByLoginGUID = {};
this._store.saveSoon();
LoginHelper.notifyStorageChanged("removeAllLogins", allLogins);
}
findLogins(origin, formActionOrigin, httpRealm) {

Просмотреть файл

@ -50,7 +50,7 @@ this.LoginTestUtils = {
* Erases all the data stored by the Login Manager service.
*/
clearData() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
for (let origin of Services.logins.getAllDisabledHosts()) {
Services.logins.setLoginSavingEnabled(origin, true);
}

Просмотреть файл

@ -47,7 +47,7 @@ function observeMasterPasswordDialog(window, result) {
}
add_task(async function setup() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let login = LoginTestUtils.testData.formLogin({
origin: "http://example.org",
formActionOrigin: "http://example.org",

Просмотреть файл

@ -98,6 +98,6 @@ add_task(async function test() {
BrowserTestUtils.removeTab(tab);
// Reset all passwords before next iteration.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
});

Просмотреть файл

@ -412,7 +412,7 @@ add_task(async function fill_generated_password_with_matching_logins() {
"Generated password shouldn't have changed to match the filled password"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
LoginTestUtils.resetGeneratedPasswordsCache();
});

Просмотреть файл

@ -83,14 +83,14 @@ async function loginBackupDeleted() {
});
}
// 1. Test that logins backup is deleted when Services.logins.removeAllLogins() is called.
// 1. Test that logins backup is deleted when Services.logins.removeAllUserFacingLogins() is called.
// 2. Test that logins backup is deleted when the last saved login is removed using
// Services.logins.removeLogin() when no fxa key is saved.
// 3. If a fxa key is stored as a login, test that logins backup is updated to only store
// the fxa key when the last user facing login is deleted.
add_task(async function test_deleteLoginsBackup_removeAll() {
// Remove logins.json and logins-backup.json before starting.
info("Testing the removeAllLogins() case");
info("Testing the removeAllUserFacingLogins() case");
await OS.File.remove(loginStorePath, { ignoreAbsent: true });
await OS.File.remove(loginBackupPath, { ignoreAbsent: true });
@ -113,7 +113,7 @@ add_task(async function test_deleteLoginsBackup_removeAll() {
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing all logins");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
await storageUpdatePromise;
info("Writes to storage are complete when removeAllLogins() is called");

Просмотреть файл

@ -177,7 +177,7 @@ add_task(async function test_edit_password() {
await LoginTestUtils.clearData();
await cleanupDoorhanger();
await cleanupPasswordNotifications();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
// Create the pre-existing logins when needed.
info("Adding any saved logins");
@ -269,7 +269,7 @@ add_task(async function test_edit_password() {
await cleanupDoorhanger();
await cleanupPasswordNotifications();
await clearMessageCache(browser);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
);
}

Просмотреть файл

@ -178,5 +178,5 @@ async function test_save_change({
);
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}

Просмотреть файл

@ -16,7 +16,7 @@ const usernameInputSelector = "#form-basic-username";
requestLongerTimeout(2);
async function task_setup() {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
LoginTestUtils.resetGeneratedPasswordsCache();
await cleanupPasswordNotifications();
}

Просмотреть файл

@ -96,7 +96,7 @@ add_task(async function test_httpsUpgradeCaptureFields_changePW() {
is(login.password, "pass2", "Check the password changed");
is(login.timesUsed, 2, "Check times used increased");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(
@ -165,7 +165,7 @@ add_task(
"timeLastUsed == timePasswordChanged"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
);

Просмотреть файл

@ -78,7 +78,7 @@ async function showChangePasswordDoorhanger(
}
async function setupLogins(...logins) {
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let savedLogins = {};
let timesCreated = new Set();
for (let login of logins) {

Просмотреть файл

@ -886,7 +886,7 @@ add_task(async function test_recipeCaptureFields_ExistingLogin() {
is(login.password, "notifyp1", "Check the password unchanged");
is(login.timesUsed, 2, "Check times used incremented");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_saveUsingEnter() {
@ -925,7 +925,7 @@ add_task(async function test_saveUsingEnter() {
is(login.password, "notifyp1", "Check the password used on the new entry");
is(login.timesUsed, 1, "Check times used on new entry");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
await testWithTextboxSelector("#password-notification-password");

Просмотреть файл

@ -160,5 +160,5 @@ async function test_save_change(testData) {
);
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}

Просмотреть файл

@ -362,5 +362,5 @@ async function test_submit_telemetry(tc) {
// Clean up the database before the next test case is executed.
await cleanupDoorhanger(notif);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}

Просмотреть файл

@ -115,7 +115,7 @@ add_task(async function test_setup() {
authPromptModalType = Services.prefs.getIntPref("prompts.modalType.httpAuth");
normalWin = await BrowserTestUtils.openNewBrowserWindow({ private: false });
privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_normal_popup_notification_1() {
@ -169,7 +169,7 @@ add_task(async function test_private_popup_notification_2() {
);
// clear existing logins for parity with the previous test
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
await focusWindow(privateWin);
await BrowserTestUtils.withNewTab(
{
@ -243,7 +243,7 @@ add_task(async function test_private_popup_notification_no_capture_pref_2b() {
Services.prefs.setBoolPref(PRIVATE_BROWSING_CAPTURE_PREF, false);
// clear existing logins for parity with the previous test
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
await focusWindow(privateWin);
await BrowserTestUtils.withNewTab(
@ -289,7 +289,7 @@ add_task(async function test_normal_popup_notification_3() {
"match existing username/password: no popup notification should appear"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.logins.addLogin(login);
let allLogins = Services.logins.getAllLogins();
// Sanity check the HTTP login exists.
@ -340,7 +340,7 @@ add_task(async function test_private_popup_notification_3b() {
" match existing username/password: no popup notification should appear"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.logins.addLogin(login);
let allLogins = Services.logins.getAllLogins();
// Sanity check the HTTP login exists.
@ -392,7 +392,7 @@ add_task(async function test_normal_new_password_4() {
"test 4: run with a login, outside of private mode," +
" add a new password: popup notification should appear"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.logins.addLogin(login);
let allLogins = Services.logins.getAllLogins();
// Sanity check the HTTP login exists.
@ -684,7 +684,7 @@ add_task(async function test_normal_http_basic_auth() {
info(
"test normal/basic-auth: verify that we get a doorhanger after basic-auth login"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
clearHttpAuths();
await focusWindow(normalWin);
@ -737,7 +737,7 @@ add_task(async function test_private_http_basic_auth() {
info(
"test private/basic-auth: verify that we don't get a doorhanger after basic-auth login"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
clearHttpAuths();
const capturePrefValue = Services.prefs.getBoolPref(
@ -792,7 +792,7 @@ add_task(async function test_private_http_basic_auth_no_capture_pref() {
);
Services.prefs.setBoolPref(PRIVATE_BROWSING_CAPTURE_PREF, false);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
clearHttpAuths();
await focusWindow(privateWin);

Просмотреть файл

@ -811,7 +811,7 @@ SimpleTest.registerCleanupFunction(() => {
);
// Remove all logins and disabled hosts
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let disabledHosts = Services.logins.getAllDisabledHosts();
disabledHosts.forEach(host =>

Просмотреть файл

@ -24,7 +24,7 @@ let FILE_PATH = "/tests/toolkit/components/passwordmgr/test/mochitest/slow_image
// part of a suite
runInParent(function removeAll() {
let {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
})
let readyPromise = registerRunTests();

Просмотреть файл

@ -16,7 +16,7 @@ Login Manager test: autofill with autocomplete=new-password fields
<script>
function initLogins() {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
const {LoginManagerParent} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
if (LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin()) {
@ -282,7 +282,7 @@ add_task(async function test_autofillAutocompletePassword_withGeneration() {
await promiseNoUnexpectedPopupShown();
info("Removing all logins to test auto-saving of generated passwords");
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
while (pword.value) {
synthesizeKey("KEY_Backspace");
@ -445,7 +445,7 @@ add_task(async function test_autofillAutocompletePassword_saveLoginDisabled() {
add_task(async function test_deleteAndReselectGeneratedPassword() {
info("Removing all logins to test auto-saving of generated passwords");
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
// form should not be filled
checkForm(2, "", "");

Просмотреть файл

@ -62,7 +62,7 @@ add_task(async function test_two_logins() {
checkLoginForm(uname, "", pword, "");
let removedPromise = promiseStorageChanged(["removeAllLogins"]);
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
await removedPromise;
});

Просмотреть файл

@ -32,7 +32,7 @@ let readyPromise = registerRunTests(1);
const { TestUtils } = SpecialPowers.Cu.import("resource://testing-common/TestUtils.jsm");
async function prepareLogins(logins = []) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
for (let login of logins) {
let storageAddPromise = promiseStorageChanged(["addLogin"]);

Просмотреть файл

@ -33,7 +33,7 @@ let win = window.open("about:blank");
SimpleTest.registerCleanupFunction(() => win.close());
async function prepareLoginsAndProcessForm(url, logins = []) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
let dates = Date.now();
for (let login of logins) {

Просмотреть файл

@ -45,7 +45,7 @@ async function checkWindowLoginForm(expectedUsername, expectedPassword) {
}
async function prepareLogins(logins = []) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
let dates = Date.now();
for (let login of logins) {

Просмотреть файл

@ -38,7 +38,7 @@ async function prepareAndProcessForm(url, login) {
}
async function checkFormsWithLogin(formUrls, login, expectedUsername, expectedPassword) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
await LoginManager.addLogin(login);
for (let url of formUrls) {

Просмотреть файл

@ -31,7 +31,7 @@ let win = window.open("about:blank");
SimpleTest.registerCleanupFunction(() => win.close());
async function prepareLoginsAndProcessForm(url, logins = []) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
let dates = Date.now();
for (let login of logins) {

Просмотреть файл

@ -34,7 +34,7 @@ let html = `
</form>`;
async function prepareLogins(logins = []) {
await LoginManager.removeAllLogins();
await LoginManager.removeAllUserFacingLogins();
for (let login of logins) {
let storageAddPromise = promiseStorageChanged(["addLogin"]);

Просмотреть файл

@ -14,20 +14,20 @@ let readyPromise = registerRunTests();
const DEFAULT_ORIGIN = window.location.origin;
function removeAllLoginsInParent() {
runInParent(function removeAllLogins() {
function removeAllUserFacingLoginsInParent() {
runInParent(function removeAllUserFacingLogins() {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
}
function add2logins() {
removeAllLoginsInParent();
removeAllUserFacingLoginsInParent();
addLoginsInParent([DEFAULT_ORIGIN, DEFAULT_ORIGIN, null, "real••••user", "pass1", "", ""], [DEFAULT_ORIGIN, DEFAULT_ORIGIN, null, "user2", "pass2", "", ""]);
}
function addSingleLogin() {
removeAllLoginsInParent();
removeAllUserFacingLoginsInParent();
addLoginsInParent([DEFAULT_ORIGIN, DEFAULT_ORIGIN, null, "real••••user", "pass1", "", ""])
}

Просмотреть файл

@ -54,7 +54,7 @@ async function setupWithOneLogin(pageUrl) {
function resetSavedLogins() {
let chromeScript = runInParent(function testTeardown() {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
chromeScript.destroy();
}

Просмотреть файл

@ -332,7 +332,7 @@ add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
@ -418,7 +418,7 @@ add_task(
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
@ -613,7 +613,7 @@ add_task(async function test_addUsernameBeforeAutoSaveEdit() {
LoginHelper.getBrowserForPrompt.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
@ -750,7 +750,7 @@ add_task(async function test_editUsernameOfFilledSavedLogin() {
LoginHelper.getBrowserForPrompt.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
@ -785,7 +785,7 @@ add_task(
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.setLoginSavingEnabled("https://www.example.com", true);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
);
@ -873,7 +873,7 @@ add_task(
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
@ -992,7 +992,7 @@ add_task(
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
@ -1057,7 +1057,7 @@ add_task(
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
);
@ -1119,6 +1119,6 @@ add_task(
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
);

Просмотреть файл

@ -190,6 +190,6 @@ add_task(async function test_searchAndDedupeLogins_acceptDifferentSubdomains() {
Assert.ok(actual[i].equals(login), `Check index ${i}`);
}
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
});

Просмотреть файл

@ -281,7 +281,7 @@ async function runTestcase({ formOrigin, savedLogins, expectedItems }) {
"All items correctly cleared."
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
/**

Просмотреть файл

@ -211,15 +211,15 @@ add_task(function test_removeLogin_nonexisting() {
/**
* Tests removing all logins at once.
*/
add_task(function test_removeAllLogins() {
add_task(function test_removeAllUserFacingLogins() {
for (let loginInfo of TestData.loginList()) {
Services.logins.addLogin(loginInfo);
}
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
LoginTestUtils.checkLogins([]);
// The function should also work when there are no logins to delete.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
/**
@ -460,7 +460,7 @@ add_task(function test_addLogin_badDates() {
!!Services.logins.addLogin(defaultsLogin),
"Sanity check adding defaults formLogin"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
// 0 is a valid date in this context - new nsLoginInfo timestamps init to 0
for (let pname of ["timeCreated", "timeLastUsed", "timePasswordChanged"]) {
@ -473,7 +473,7 @@ add_task(function test_addLogin_badDates() {
!!Services.logins.addLogin(loginInfo),
"Check 0 value for " + pname
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
// negative dates get clamped to 0 and are ok
@ -487,7 +487,7 @@ add_task(function test_addLogin_badDates() {
!!Services.logins.addLogin(loginInfo),
"Check -1 value for " + pname
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
}
// out-of-range dates will throw
@ -544,5 +544,5 @@ add_task(async function test_addLogins_badDates() {
let savedLogins = Services.logins.getAllLogins();
Assert.equal(savedLogins.length, 1);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});

Просмотреть файл

@ -84,7 +84,7 @@ add_task(async function test_logins_decrypt_failure() {
Assert.equal(Services.logins.countLogins("", "", ""), logins.length);
// Removing all logins removes the non-decryptable entries also.
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
Assert.equal(Services.logins.getAllLogins().length, 0);
Assert.equal(Services.logins.countLogins("", "", ""), 0);
});
@ -151,7 +151,7 @@ add_task(function test_add_logins_with_decrypt_failure() {
Services.logins.addLogin(login);
equal(Services.logins.searchLogins(searchProp).length, 1);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
// Test the "syncID" metadata works as expected on decryption failure.

Просмотреть файл

@ -46,7 +46,7 @@ add_task(async function test_invalid_logins() {
0,
`Should have no logins in storage: ${JSON.stringify(savedLogins, null, 2)}`
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_new_logins() {
@ -97,7 +97,7 @@ add_task(async function test_new_logins() {
2,
"There should be 2 logins in total"
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_duplicate_logins() {
@ -135,7 +135,7 @@ add_task(async function test_duplicate_logins() {
1,
`There should still be 1 login for ${HOST1}`
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_different_passwords() {
@ -208,7 +208,7 @@ add_task(async function test_different_passwords() {
"We should NOT have updated the password for this login."
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_different_usernames_without_guid() {
@ -247,7 +247,7 @@ add_task(async function test_different_usernames_without_guid() {
`There should now be 2 logins for ${HOST1}`
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_different_usernames_with_guid() {
@ -289,7 +289,7 @@ add_task(async function test_different_usernames_with_guid() {
Assert.equal(storageLogin.username, USER2, "Check username updated");
Assert.equal(storageLogin.origin, HOST2, "Check origin updated");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});
add_task(async function test_different_targets() {
@ -355,5 +355,5 @@ add_task(async function test_different_targets() {
`There should now be 2 logins for ${HOST1}`
);
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
});

Просмотреть файл

@ -37,7 +37,7 @@ async function setupCsv(csvLines) {
TTU.getAndClearKeyedHistogram("FX_MIGRATION_LOGINS_QUANTITY");
TTU.getAndClearKeyedHistogram("FX_MIGRATION_LOGINS_IMPORT_MS");
TTU.getAndClearKeyedHistogram("FX_MIGRATION_LOGINS_JANK_MS");
Services.logins.removeAllLogins();
Services.logins.removeAllUserFacingLogins();
let tmpFile = await LoginTestUtils.file.setupCsvFileWithLines(csvLines);
return tmpFile.path;

Просмотреть файл

@ -35,7 +35,7 @@ let TestObserver = {
Assert.ok(expectedData.equals(subject)); // nsILoginInfo.equals()
break;
case "removeAllLogins":
Assert.equal(subject, null);
Assert.ok(subject instanceof Ci.nsIArray);
break;
case "hostSavingEnabled":
case "hostSavingDisabled":
@ -126,6 +126,10 @@ add_task(function test_notifications() {
testnum++;
testdesc = "removeAllLogins (again)";
expectedNotification = "addLogin";
expectedData = testuser1;
Services.logins.addLogin(testuser1);
expectedNotification = "removeAllLogins";
expectedData = null;
Services.logins.removeAllLogins();