Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android'
- Manifest: toolkit/components/passwordmgr/test/browser/browser.toml
/**
* Test that logins backup is deleted as expected when logins are deleted.
*/
ChromeUtils.defineESModuleGetters(this, {
FXA_PWDMGR_HOST: "resource://gre/modules/FxAccountsCommon.sys.mjs",
FXA_PWDMGR_REALM: "resource://gre/modules/FxAccountsCommon.sys.mjs",
});
const nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1",
Ci.nsILoginInfo,
"init"
);
const login1 = new nsLoginInfo(
null,
"notifyu1",
"notifyp1",
"user",
"pass"
);
const login2 = new nsLoginInfo(
null,
"",
"notifyp1",
"",
"pass"
);
const fxaKey = new nsLoginInfo(
FXA_PWDMGR_HOST,
null,
FXA_PWDMGR_REALM,
"foo@bar.com",
"pass2",
"",
""
);
const loginStorePath = PathUtils.join(PathUtils.profileDir, "logins.json");
const loginBackupPath = PathUtils.join(
PathUtils.profileDir,
"logins-backup.json"
);
async function waitForBackupUpdate() {
return new Promise(resolve => {
Services.obs.addObserver(function observer(_subject, _topic, _data) {
Services.obs.removeObserver(observer, "logins-backup-updated");
resolve();
}, "logins-backup-updated");
});
}
async function loginStoreExists() {
return TestUtils.waitForCondition(() => IOUtils.exists(loginStorePath));
}
async function loginBackupExists() {
return TestUtils.waitForCondition(() => IOUtils.exists(loginBackupPath));
}
async function loginBackupDeleted() {
return TestUtils.waitForCondition(
async () => !(await IOUtils.exists(loginBackupPath))
);
}
// 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_removeAllUserFacingLogins_fxaKey() {
info(
"Testing removeAllUserFacingLogins() case when there is a saved fxa key"
);
info("Adding two logins: fxa key and one user facing login");
let storageUpdatePromise = TestUtils.topicObserved(
"password-storage-updated"
);
await Services.logins.addLoginAsync(login1);
Assert.ok(true, "added login1");
await loginStoreExists();
await Services.logins.addLoginAsync(fxaKey);
Assert.ok(true, "added fxaKey");
await loginBackupExists();
Assert.ok(true, "logins-backup.json now exists");
await storageUpdatePromise;
info("Writes to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing all user facing logins");
Services.logins.removeAllUserFacingLogins();
await storageUpdatePromise;
info("Writes to storage are complete after removeAllUserFacingLogins call");
await waitForBackupUpdate();
Assert.ok(
true,
"logins-backup.json was updated to only store the fxa key, as expected"
);
// Clean up.
// Since there is a fxa key left, we need to call removeAllLogins() or removeLogin(fxaKey)
// to remove the fxa key. Otherwise the test will fail in verify mode when trying to add login1
Services.logins.removeAllLogins();
await IOUtils.remove(loginStorePath);
}
);
// Test that logins backup is deleted when Services.logins.removeAllUserFacingLogins() is called.
add_task(async function test_deleteLoginsBackup_removeAllUserFacingLogins() {
// Remove logins.json and logins-backup.json before starting.
info("Testing the removeAllUserFacingLogins() case");
await IOUtils.remove(loginStorePath, { ignoreAbsent: true });
await IOUtils.remove(loginBackupPath, { ignoreAbsent: true });
let storageUpdatePromise = TestUtils.topicObserved(
"password-storage-updated"
);
info("Add a login to create logins.json");
await Services.logins.addLoginAsync(login1);
await loginStoreExists();
Assert.ok(true, "logins.json now exists");
info("Add a second login to create logins-backup.json");
await Services.logins.addLoginAsync(login2);
await loginBackupExists();
info("logins-backup.json now exists");
await storageUpdatePromise;
info("Writes to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing all user facing logins");
Services.logins.removeAllUserFacingLogins();
await storageUpdatePromise;
info(
"Writes to storage are complete when removeAllUserFacingLogins() is called"
);
await loginBackupDeleted();
info(
"logins-backup.json was deleted as expected when all logins were removed"
);
// Clean up.
await IOUtils.remove(loginStorePath);
});
// 1. Test that logins backup is deleted when Services.logins.removeAllLogins() is called
// 2. If a FxA key is stored as a login, test that logins backup is deleted when
// Services.logins.removeAllLogins() is called
add_task(async function test_deleteLoginsBackup_removeAllLogins() {
// Remove logins.json and logins-backup.json before starting.
info("Testing the removeAllLogins() case");
await IOUtils.remove(loginStorePath, { ignoreAbsent: true });
await IOUtils.remove(loginBackupPath, { ignoreAbsent: true });
let storageUpdatePromise = TestUtils.topicObserved(
"password-storage-updated"
);
info("Add a login to create logins.json");
await Services.logins.addLoginAsync(login1);
Assert.ok(true, "added login1");
await loginStoreExists();
Assert.ok(true, "logins.json now exists");
await Services.logins.addLoginAsync(login2);
Assert.ok(true, "added login2");
await loginBackupExists();
info("logins-backup.json now exists");
await storageUpdatePromise;
info("Writes to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing all logins");
Services.logins.removeAllLogins();
await storageUpdatePromise;
info("Writes to storage are complete when removeAllLogins() is called");
await loginBackupDeleted();
info(
"logins-backup.json was deleted as expected when all logins were removed"
);
await IOUtils.remove(loginStorePath);
info("Testing the removeAllLogins() case when FxA key is present");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
await Services.logins.addLoginAsync(login1);
await loginStoreExists();
await Services.logins.addLoginAsync(fxaKey);
await loginBackupExists();
info("logins-backup.json now exists");
await storageUpdatePromise;
info("Write to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing all logins, including FxA key");
Services.logins.removeAllLogins();
await storageUpdatePromise;
info("Writes to storage are complete after the last removeAllLogins call");
await loginBackupDeleted();
info(
"logins-backup.json was deleted when the last logins were removed, as expected"
);
// Clean up.
await IOUtils.remove(loginStorePath);
});
// 1. Test that logins backup is deleted when the last saved login is removed using
// Services.logins.removeLogin() when no fxa key is saved.
// 2. Test that logins backup is updated when the last saved login is removed using
// Services.logins.removeLogin() when a fxa key is present.
add_task(async function test_deleteLoginsBackup_removeLogin() {
info("Testing the removeLogin() case when there is no saved fxa key");
info("Adding two logins");
let storageUpdatePromise = TestUtils.topicObserved(
"password-storage-updated"
);
await Services.logins.addLoginAsync(login1);
await loginStoreExists();
await Services.logins.addLoginAsync(login2);
await loginBackupExists();
info("logins-backup.json now exists");
await storageUpdatePromise;
info("Writes to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing one login");
Services.logins.removeLogin(login1);
await storageUpdatePromise;
info("Writes to storage are complete after one removeLogin call");
await loginBackupExists();
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
info("Removing the last login");
Services.logins.removeLogin(login2);
await storageUpdatePromise;
info("Writes to storage are complete after the last removeLogin call");
await loginBackupDeleted();
info(
"logins-backup.json was deleted as expected when the last saved login was removed"
);
await IOUtils.remove(loginStorePath);
info("Testing the removeLogin() case when there is a saved fxa key");
info("Adding two logins: one user facing and the fxa key");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
await Services.logins.addLoginAsync(login1);
await loginStoreExists();
await Services.logins.addLoginAsync(fxaKey);
await loginBackupExists();
info("logins-backup.json now exists");
await storageUpdatePromise;
info("Writes to storage are complete for addLogin calls");
storageUpdatePromise = TestUtils.topicObserved("password-storage-updated");
let backupUpdate = waitForBackupUpdate();
Services.logins.removeLogin(login1);
await storageUpdatePromise;
info("Writes to storage are complete after one removeLogin call");
await backupUpdate;
await loginBackupExists();
info("logins-backup.json was updated to contain only the fxa key");
// Clean up.
// Since there is a fxa key left, we need to call removeAllLogins() or removeLogin(fxaKey)
// to remove the fxa key. Otherwise the test will fail in verify mode when trying to add login1
Services.logins.removeAllLogins();
await IOUtils.remove(loginStorePath);
});