tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 509c13d6867e64dced5a7583d428dc5d277e6f5d
parent 63df885248efaaa6b2ed7e3c51d7e831c045b17a
Author: Tessa Heidkamp <theidkamp@mozilla.com>
Date:   Tue, 16 Dec 2025 10:58:31 +0000

Bug 2003467 - Common Sync MS4 - change modifyLogin to async. r=joschmidt,credential-management-reviewers,mtigley

Differential Revision: https://phabricator.services.mozilla.com/D274732

Diffstat:
Mbrowser/components/aboutlogins/AboutLoginsParent.sys.mjs | 2+-
Mbrowser/components/aboutlogins/tests/browser/browser_openSite.js | 2+-
Mservices/fxaccounts/FxAccountsStorage.sys.mjs | 2+-
Mservices/sync/modules/engines/passwords.sys.mjs | 4++--
Mservices/sync/tests/unit/test_password_engine.js | 18+++++++++---------
Mservices/sync/tps/extensions/tps/resource/modules/passwords.sys.mjs | 4++--
Mservices/sync/tps/extensions/tps/resource/tps.sys.mjs | 2+-
Mtoolkit/components/passwordmgr/LoginHelper.sys.mjs | 5++++-
Mtoolkit/components/passwordmgr/LoginManager.sys.mjs | 12++++++++++++
Mtoolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs | 6+++---
Mtoolkit/components/passwordmgr/LoginManagerParent.sys.mjs | 4++--
Mtoolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs | 14+++++++++-----
Mtoolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs | 2+-
Mtoolkit/components/passwordmgr/nsILoginManager.idl | 24++++++++++++++++++++++++
Mtoolkit/components/passwordmgr/storage-geckoview.sys.mjs | 4++++
Mtoolkit/components/passwordmgr/storage-json.sys.mjs | 6++++++
Mtoolkit/components/passwordmgr/storage-rust.sys.mjs | 7+++++++
Mtoolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs | 2+-
Mtoolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js | 2+-
Mtoolkit/components/passwordmgr/test/browser/browser_rust_mirror.js | 2+-
Mtoolkit/components/passwordmgr/test/unit/test_logins_change.js | 83++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtoolkit/components/passwordmgr/test/unit/test_logins_decrypt_failure.js | 4++--
Mtoolkit/components/passwordmgr/test/unit/test_logins_metainfo.js | 25++++++++++++-------------
Mtoolkit/components/passwordmgr/test/unit/test_notifications.js | 2+-
Mtoolkit/components/passwordmgr/test/unit/test_reencrypt.js | 7+++++--
Mtoolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs | 2+-
Mtools/@types/generated/lib.gecko.xpcom.d.ts | 1+
27 files changed, 156 insertions(+), 92 deletions(-)

diff --git a/browser/components/aboutlogins/AboutLoginsParent.sys.mjs b/browser/components/aboutlogins/AboutLoginsParent.sys.mjs @@ -360,7 +360,7 @@ export class AboutLoginsParent extends JSWindowActorParent { modifiedLogin.password = loginUpdates.password; } try { - Services.logins.modifyLogin(logins[0], modifiedLogin); + await Services.logins.modifyLoginAsync(logins[0], modifiedLogin); } catch (error) { this.#handleLoginStorageErrors(modifiedLogin, error); } diff --git a/browser/components/aboutlogins/tests/browser/browser_openSite.js b/browser/components/aboutlogins/tests/browser/browser_openSite.js @@ -83,7 +83,7 @@ add_task(async function test_launch_login_item() { "passwordmgr-storage-changed", (_, data) => data == "modifyLogin" ); - Services.logins.modifyLogin(TEST_LOGIN1, modifiedLogin); + await Services.logins.modifyLoginAsync(TEST_LOGIN1, modifiedLogin); await storageChangedPromised; BrowserTestUtils.removeTab(newTab); diff --git a/services/fxaccounts/FxAccountsStorage.sys.mjs b/services/fxaccounts/FxAccountsStorage.sys.mjs @@ -558,7 +558,7 @@ LoginManagerStorage.prototype = { httpRealm: FXA_PWDMGR_REALM, }); if (existingLogins.length) { - Services.logins.modifyLogin(existingLogins[0], login); + await Services.logins.modifyLoginAsync(existingLogins[0], login); } else { await Services.logins.addLoginAsync(login); } diff --git a/services/sync/modules/engines/passwords.sys.mjs b/services/sync/modules/engines/passwords.sys.mjs @@ -333,7 +333,7 @@ PasswordStore.prototype = { prop.setPropertyAsAUTF8String("guid", newID); let oldLogin = await this._getLoginFromGUID(oldID); - this.storage.modifyLogin(oldLogin, prop, true); + await this.storage.modifyLoginAsync(oldLogin, prop, true); }, async itemExists(id) { @@ -425,7 +425,7 @@ PasswordStore.prototype = { loginItem.everSynced = true; - this.storage.modifyLogin(loginItem, newinfo, true); + await this.storage.modifyLoginAsync(loginItem, newinfo, true); }, async wipe() { diff --git a/services/sync/tests/unit/test_password_engine.js b/services/sync/tests/unit/test_password_engine.js @@ -77,14 +77,14 @@ add_task(async function test_ignored_fields() { let nonSyncableProps = new PropertyBag(); nonSyncableProps.setProperty("timeLastUsed", Date.now()); nonSyncableProps.setProperty("timesUsed", 3); - Services.logins.modifyLogin(login, nonSyncableProps); + await Services.logins.modifyLoginAsync(login, nonSyncableProps); let noChanges = await engine.pullNewChanges(); deepEqual(noChanges, {}, "Should not track non-syncable fields"); let syncableProps = new PropertyBag(); syncableProps.setProperty("username", "newuser"); - Services.logins.modifyLogin(login, syncableProps); + await Services.logins.modifyLoginAsync(login, syncableProps); let changes = await engine.pullNewChanges(); deepEqual( @@ -127,7 +127,7 @@ add_task(async function test_ignored_sync_credentials() { let props = new PropertyBag(); props.setProperty("password", "newcreds"); - Services.logins.modifyLogin(login, props); + await Services.logins.modifyLoginAsync(login, props); noChanges = await engine.pullNewChanges(); deepEqual(noChanges, {}, "Should not track changes to FxA credentials"); @@ -208,7 +208,7 @@ add_task(async function test_password_engine() { let props = new PropertyBag(); let localPasswordChangeTime = Date.now() - 1 * 60 * 60 * 24 * 1000; props.setProperty("timePasswordChanged", localPasswordChangeTime); - Services.logins.modifyLogin(login, props); + await Services.logins.modifyLoginAsync(login, props); let logins = await Services.logins.searchLoginsAsync({ origin: "https://mozilla.com", @@ -304,7 +304,7 @@ add_task(async function test_sync_outgoing() { for (let i = 1; i <= 2; i++) { _("Modify the password iteration " + i); foundLogins[0].password = "newpassword" + i; - Services.logins.modifyLogin(login, foundLogins[0]); + await Services.logins.modifyLoginAsync(login, foundLogins[0]); foundLogins = await Services.logins.searchLoginsAsync({ origin: "http://mozilla.com", }); @@ -348,7 +348,7 @@ add_task(async function test_sync_outgoing() { // Next, modify the username and sync. _("Modify the username"); foundLogins[0].username = "newuser"; - Services.logins.modifyLogin(login, foundLogins[0]); + await Services.logins.modifyLoginAsync(login, foundLogins[0]); foundLogins = await Services.logins.searchLoginsAsync({ origin: "http://mozilla.com", }); @@ -657,7 +657,7 @@ add_task(async function test_sync_incoming_deleted_localchanged_remotenewer() { origin: "http://mozilla.com", }); foundLogins[0].password = "wallaby"; - Services.logins.modifyLogin(login, foundLogins[0]); + await Services.logins.modifyLoginAsync(login, foundLogins[0]); // Use a time in the future to ensure that the remote record is newer. collection.updateRecord( @@ -715,7 +715,7 @@ add_task(async function test_sync_incoming_deleted_localchanged_localnewer() { origin: "http://www.mozilla.com", }); foundLogins[0].password = "cheetah"; - Services.logins.modifyLogin(login, foundLogins[0]); + await Services.logins.modifyLoginAsync(login, foundLogins[0]); // Use a time in the past to ensure that the local record is newer. collection.updateRecord( @@ -1109,7 +1109,7 @@ add_task(async function test_roundtrip_unknown_fields() { Date.now() - 1 * 60 * 60 * 24 * 1000 ); props.setProperty("timePasswordChanged", localPasswordChangeTime); - Services.logins.modifyLogin(login, props); + await Services.logins.modifyLoginAsync(login, props); let logins = await Services.logins.searchLoginsAsync({ origin: "https://mozilla.com", diff --git a/services/sync/tps/extensions/tps/resource/modules/passwords.sys.mjs b/services/sync/tps/extensions/tps/resource/modules/passwords.sys.mjs @@ -142,7 +142,7 @@ Password.prototype = { * * @return nothing */ - Update() { + async Update() { let oldlogin = new nsLoginInfo( this.props.hostname, this.props.submitURL, @@ -161,7 +161,7 @@ Password.prototype = { this.updateProps.usernameField, this.updateProps.passwordField ); - Services.logins.modifyLogin(oldlogin, newlogin); + await Services.logins.modifyLoginAsync(oldlogin, newlogin); }, /** diff --git a/services/sync/tps/extensions/tps/resource/tps.sys.mjs b/services/sync/tps/extensions/tps/resource/tps.sys.mjs @@ -485,7 +485,7 @@ export var TPS = { (await passwordOb.Find()) != -1, "password not found" ); - passwordOb.Update(); + await passwordOb.Update(); } break; default: diff --git a/toolkit/components/passwordmgr/LoginHelper.sys.mjs b/toolkit/components/passwordmgr/LoginHelper.sys.mjs @@ -368,7 +368,10 @@ class ImportRowProcessor { summaryRow.login ); } else if (summaryRow.result === "modified") { - Services.logins.modifyLogin(summaryRow.login, summaryRow.propBag); + await Services.logins.modifyLoginAsync( + summaryRow.login, + summaryRow.propBag + ); } } catch (e) { console.error(e); diff --git a/toolkit/components/passwordmgr/LoginManager.sys.mjs b/toolkit/components/passwordmgr/LoginManager.sys.mjs @@ -278,6 +278,7 @@ LoginManager.prototype = { /** * Change the specified login to match the new login or new properties. + * Deprecated: use modifyLoginAsync instead. */ modifyLogin(oldLogin, newLogin) { lazy.log.debug( @@ -288,6 +289,17 @@ LoginManager.prototype = { }, /** + * Async: Change the specified login to match the new login or new properties. + */ + async modifyLoginAsync(oldLogin, newLogin) { + lazy.log.debug( + "Modifying login", + oldLogin.QueryInterface(Ci.nsILoginMetaInfo).guid + ); + await this._storage.modifyLoginAsync(oldLogin, newLogin); + }, + + /** * Record that the password of a saved login was used (e.g. submitted or copied). */ recordPasswordUse( diff --git a/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs b/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs @@ -489,7 +489,7 @@ LoginManagerAuthPrompter.prototype = { } else if (aPassword.value != selectedLogin.password) { // update password this.log(`Updating password for ${realm}.`); - this._updateLogin(selectedLogin, newLogin); + await this._updateLogin(selectedLogin, newLogin); } else { this.log("Login unchanged, no further action needed."); Services.logins.recordPasswordUse( @@ -896,7 +896,7 @@ LoginManagerAuthPrompter.prototype = { /* ---------- Internal Methods ---------- */ - _updateLogin(login, aNewLogin) { + async _updateLogin(login, aNewLogin) { var now = Date.now(); var propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( Ci.nsIWritablePropertyBag @@ -913,7 +913,7 @@ LoginManagerAuthPrompter.prototype = { propBag.setProperty("timesUsedIncrement", 1); // Note that we don't call `recordPasswordUse` so we won't potentially record // both a use and a save/update. See bug 1640096. - Services.logins.modifyLogin(login, propBag); + await Services.logins.modifyLoginAsync(login, propBag); }, /** diff --git a/toolkit/components/passwordmgr/LoginManagerParent.sys.mjs b/toolkit/components/passwordmgr/LoginManagerParent.sys.mjs @@ -1084,7 +1084,7 @@ export class LoginManagerParent extends JSWindowActorParent { const prompter = this._getPrompter(browser); if (!canMatchExistingLogin) { - prompter.promptToChangePasswordWithUsernames( + await prompter.promptToChangePasswordWithUsernames( promptBrowser, logins, formLogin @@ -1383,7 +1383,7 @@ export class LoginManagerParent extends JSWindowActorParent { ) { lazy.log("Updating auto-saved login."); - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( existingLogin, lazy.LoginHelper.newPropertyBag({ password: formLogin.password, diff --git a/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs b/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs @@ -428,7 +428,7 @@ export class LoginManagerPrompter { ); } else { lazy.log.debug(`Update matched login: ${loginToUpdate.guid}.`); - this._updateLogin(loginToUpdate, login); + await this._updateLogin(loginToUpdate, login); // notify that this auto-saved login has been merged if (loginToRemove && loginToRemove.guid == autoSavedLoginGuid) { Services.obs.notifyObservers( @@ -824,7 +824,7 @@ export class LoginManagerPrompter { * function fills in .username and .usernameField with the values * from the login selected by the user. */ - promptToChangePasswordWithUsernames(browser, logins, aNewLogin) { + async promptToChangePasswordWithUsernames(browser, logins, aNewLogin) { lazy.log.debug( `Prompting user to change passowrd for username with count: ${logins.length}.` ); @@ -866,7 +866,10 @@ export class LoginManagerPrompter { selectedLogin.usernameField, aNewLogin.passwordField ); - LoginManagerPrompter._updateLogin(selectedLogin, newLoginWithUsername); + await LoginManagerPrompter._updateLogin( + selectedLogin, + newLoginWithUsername + ); } } @@ -875,7 +878,7 @@ export class LoginManagerPrompter { /** * Helper method to update and persist an existing nsILoginInfo object with new property values. */ - static _updateLogin(login, aNewLogin) { + static async _updateLogin(login, aNewLogin) { const now = Date.now(); const propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( Ci.nsIWritablePropertyBag @@ -894,7 +897,8 @@ export class LoginManagerPrompter { // use in this case though that is normally correct since we would instead // record the save/update in a separate probe and recording it in both would // be wrong. - Services.logins.modifyLogin(login, propBag); + + await Services.logins.modifyLoginAsync(login, propBag); } /** diff --git a/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs b/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs @@ -353,7 +353,7 @@ export class LoginManagerRustMirror { newLoginData = subject.queryElementAt(1, Ci.nsILoginInfo); this.#logger.log(`modifying login ${loginToModify.guid}...`); try { - this.#rustStorage.modifyLogin(loginToModify, newLoginData); + await this.#rustStorage.modifyLoginAsync(loginToModify, newLoginData); this.#logger.log(`modified login ${loginToModify.guid}.`); } catch (e) { status = "failure"; diff --git a/toolkit/components/passwordmgr/nsILoginManager.idl b/toolkit/components/passwordmgr/nsILoginManager.idl @@ -71,6 +71,7 @@ interface nsILoginManager : nsISupports { /** * Modify an existing login in the login manager. + * Deprecated: use ModifyLoginAsync instead. * * @param oldLogin * The login to be modified. @@ -91,6 +92,29 @@ interface nsILoginManager : nsISupports { void modifyLogin(in nsILoginInfo oldLogin, in nsISupports newLoginData); /** + *Like modifyLogin, but asynchronous. + * + * Modify an existing login in the login manager. + * + * @param oldLogin + * The login to be modified. + * @param newLoginData + * The new login values (either a nsILoginInfo or nsIProperyBag) + * + * If newLoginData is a nsILoginInfo, all of the old login's nsILoginInfo + * properties are changed to the values from newLoginData (but the old + * login's nsILoginMetaInfo properties are unmodified). + * + * If newLoginData is a nsIPropertyBag, only the specified properties + * will be changed. The nsILoginMetaInfo properties of oldLogin can be + * changed in this manner. + * + * If the propertybag contains an item named "timesUsedIncrement", the + * login's timesUsed property will be incremented by the item's value. + */ + Promise modifyLoginAsync(in nsILoginInfo oldLogin, in nsISupports newLoginData); + + /** * Record that the password of a saved login was used (e.g. submitted or copied). * * @param {nsILoginInfo} aLogin diff --git a/toolkit/components/passwordmgr/storage-geckoview.sys.mjs b/toolkit/components/passwordmgr/storage-geckoview.sys.mjs @@ -61,6 +61,10 @@ export class LoginManagerStorage extends LoginManagerStorage_json { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } + async modifyLoginAsync(_oldLogin, _newLoginData) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + } + recordPasswordUse(login) { lazy.GeckoViewAutocomplete.onLoginPasswordUsed( lazy.LoginEntry.fromLoginInfo(login) diff --git a/toolkit/components/passwordmgr/storage-json.sys.mjs b/toolkit/components/passwordmgr/storage-json.sys.mjs @@ -447,6 +447,12 @@ export class LoginManagerStorage_json { ]); } + async modifyLoginAsync(oldLogin, newLoginData, fromSync) { + let result = this.modifyLogin(oldLogin, newLoginData, fromSync); + // Emulate being async: + return Promise.resolve(result); + } + // Replace the login with a tombstone. It has a guid and sync-related properties, // but does not contain the login or password information. #replaceLoginWithTombstone(login) { diff --git a/toolkit/components/passwordmgr/storage-rust.sys.mjs b/toolkit/components/passwordmgr/storage-rust.sys.mjs @@ -385,6 +385,13 @@ export class LoginManagerRustStorage { this.#storageAdapter.update(idToModify, newLogin); } + async modifyLoginAsync(oldLogin, newLoginData, _fromSync) { + let result = this.modifyLogin(oldLogin, newLoginData, _fromSync); + + // Emulate being async: + return Promise.resolve(result); + } + /** * Checks to see if the specified GUID already exists. */ diff --git a/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs b/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs @@ -79,7 +79,7 @@ export const LoginTestUtils = { "passwordmgr-storage-changed", (_, data) => data == "modifyLogin" ); - Services.logins.modifyLogin(oldLogin, newLogin); + await Services.logins.modifyLoginAsync(oldLogin, newLogin); await storageChangedPromise; }, diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js @@ -489,7 +489,7 @@ add_task(async function test_pwOnlyOldLoginMatchesUPForm() { // Change the timePasswordChanged to be old so that the password won't be // revealed in the doorhanger. let oldTimeMS = new Date("2009-11-15").getTime(); - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( login2, LoginHelper.newPropertyBag({ timeCreated: oldTimeMS, diff --git a/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js b/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js @@ -74,7 +74,7 @@ add_task(async function test_mirror_modifyLogin() { const modifyLoginFinishedPromise = TestUtils.topicObserved( "rust-mirror.event.modifyLogin.finished" ); - Services.logins.modifyLogin(storedLoginInfo, modifiedLoginInfo); + await Services.logins.modifyLoginAsync(storedLoginInfo, modifiedLoginInfo); await modifyLoginFinishedPromise; const rustStorage = new LoginManagerRustStorage(); diff --git a/toolkit/components/passwordmgr/test/unit/test_logins_change.js b/toolkit/components/passwordmgr/test/unit/test_logins_change.js @@ -36,24 +36,23 @@ async function checkLoginInvalid(aLoginInfo, aExpectedError) { await Services.logins.addLoginAsync(testLogin); // Try to modify the existing login using nsILoginInfo and nsIPropertyBag. - Assert.throws( - () => Services.logins.modifyLogin(testLogin, aLoginInfo), + await Assert.rejects( + Services.logins.modifyLoginAsync(testLogin, aLoginInfo), aExpectedError ); - Assert.throws( - () => - Services.logins.modifyLogin( - testLogin, - newPropertyBag({ - origin: aLoginInfo.origin, - formActionOrigin: aLoginInfo.formActionOrigin, - httpRealm: aLoginInfo.httpRealm, - username: aLoginInfo.username, - password: aLoginInfo.password, - usernameField: aLoginInfo.usernameField, - passwordField: aLoginInfo.passwordField, - }) - ), + await Assert.rejects( + Services.logins.modifyLoginAsync( + testLogin, + newPropertyBag({ + origin: aLoginInfo.origin, + formActionOrigin: aLoginInfo.formActionOrigin, + httpRealm: aLoginInfo.httpRealm, + username: aLoginInfo.username, + password: aLoginInfo.password, + usernameField: aLoginInfo.usernameField, + passwordField: aLoginInfo.passwordField, + }) + ), aExpectedError ); @@ -306,24 +305,24 @@ add_task(async function test_modifyLogin_nsILoginInfo() { let differentLoginInfo = TestData.authLogin(); // Trying to modify a login that does not exist should throw. - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, updatedLoginInfo), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, updatedLoginInfo), /No matching logins/ ); // Add the first form login, then modify it to match the second. await Services.logins.addLoginAsync(loginInfo); - Services.logins.modifyLogin(loginInfo, updatedLoginInfo); + await Services.logins.modifyLoginAsync(loginInfo, updatedLoginInfo); // The data should now match the second login. await LoginTestUtils.checkLogins([updatedLoginInfo]); - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, updatedLoginInfo), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, updatedLoginInfo), /No matching logins/ ); // The login can be changed to have a different type and origin. - Services.logins.modifyLogin(updatedLoginInfo, differentLoginInfo); + await Services.logins.modifyLoginAsync(updatedLoginInfo, differentLoginInfo); await LoginTestUtils.checkLogins([differentLoginInfo]); // It is now possible to add a login with the old type and origin. @@ -331,8 +330,8 @@ add_task(async function test_modifyLogin_nsILoginInfo() { await LoginTestUtils.checkLogins([loginInfo, differentLoginInfo]); // Modifying a login to match an existing one should not be possible. - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, differentLoginInfo), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, differentLoginInfo), /already exists/ ); await LoginTestUtils.checkLogins([loginInfo, differentLoginInfo]); @@ -363,15 +362,15 @@ add_task(async function test_modifyLogin_nsIProperyBag() { }); // Trying to modify a login that does not exist should throw. - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, newPropertyBag()), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, newPropertyBag()), /No matching logins/ ); // Add the first form login, then modify it to match the second, changing // only some of its properties and checking the behavior with an empty string. await Services.logins.addLoginAsync(loginInfo); - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( loginInfo, newPropertyBag({ username: "new username", @@ -383,28 +382,30 @@ add_task(async function test_modifyLogin_nsIProperyBag() { // The data should now match the second login. await LoginTestUtils.checkLogins([updatedLoginInfo]); - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, newPropertyBag()), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, newPropertyBag()), /No matching logins/ ); // It is also possible to provide no properties to be modified. - Services.logins.modifyLogin(updatedLoginInfo, newPropertyBag()); + await Services.logins.modifyLoginAsync(updatedLoginInfo, newPropertyBag()); // Specifying a null property for a required value should throw. - Assert.throws( - () => - Services.logins.modifyLogin( - loginInfo, - newPropertyBag({ - usernameField: null, - }) - ), + await Assert.rejects( + Services.logins.modifyLoginAsync( + loginInfo, + newPropertyBag({ + usernameField: null, + }) + ), /No matching logins/ ); // The login can be changed to have a different type and origin. - Services.logins.modifyLogin(updatedLoginInfo, differentLoginProperties); + await Services.logins.modifyLoginAsync( + updatedLoginInfo, + differentLoginProperties + ); await LoginTestUtils.checkLogins([differentLoginInfo]); // It is now possible to add a login with the old type and origin. @@ -412,8 +413,8 @@ add_task(async function test_modifyLogin_nsIProperyBag() { await LoginTestUtils.checkLogins([loginInfo, differentLoginInfo]); // Modifying a login to match an existing one should not be possible. - Assert.throws( - () => Services.logins.modifyLogin(loginInfo, differentLoginProperties), + await Assert.rejects( + Services.logins.modifyLoginAsync(loginInfo, differentLoginProperties), /already exists/ ); await LoginTestUtils.checkLogins([loginInfo, differentLoginInfo]); diff --git a/toolkit/components/passwordmgr/test/unit/test_logins_decrypt_failure.js b/toolkit/components/passwordmgr/test/unit/test_logins_decrypt_failure.js @@ -38,8 +38,8 @@ add_task(async function test_logins_decrypt_failure() { Assert.equal(savedLogins.length, 0, "getAllLogins length"); const result = await Services.logins.searchLoginsAsync({}); Assert.equal(result.length, 0); - Assert.throws( - () => Services.logins.modifyLogin(logins[0], newPropertyBag()), + await Assert.rejects( + Services.logins.modifyLoginAsync(logins[0], newPropertyBag()), /No matching logins/ ); Assert.throws( diff --git a/toolkit/components/passwordmgr/test/unit/test_logins_metainfo.js b/toolkit/components/passwordmgr/test/unit/test_logins_metainfo.js @@ -147,7 +147,7 @@ add_task(async function test_modifyLogin_nsILoginInfo_metainfo_ignored() { newLoginInfo.timeLastUsed = Date.now(); newLoginInfo.timePasswordChanged = Date.now(); newLoginInfo.timesUsed = 12; - Services.logins.modifyLogin(gLoginInfo1, newLoginInfo); + await Services.logins.modifyLoginAsync(gLoginInfo1, newLoginInfo); newLoginInfo = await retrieveOriginMatching(gLoginInfo1.origin); assertMetaInfoEqual(newLoginInfo, gLoginMetaInfo1); @@ -162,7 +162,7 @@ add_task(async function test_modifyLogin_nsIProperyBag_metainfo() { let newUUIDValue = Services.uuid.generateUUID().toString(); // Check that properties are changed as requested. - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( gLoginInfo1, newPropertyBag({ guid: newUUIDValue, @@ -182,7 +182,7 @@ add_task(async function test_modifyLogin_nsIProperyBag_metainfo() { // Check that timePasswordChanged is updated when changing the password. let originalLogin = gLoginInfo2.clone().QueryInterface(Ci.nsILoginMetaInfo); - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( gLoginInfo2, newPropertyBag({ password: "new password", @@ -198,7 +198,7 @@ add_task(async function test_modifyLogin_nsIProperyBag_metainfo() { // Check that timePasswordChanged is not set to the current time when changing // the password and specifying a new value for the property at the same time. - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( gLoginInfo2, newPropertyBag({ password: "other password", @@ -214,7 +214,7 @@ add_task(async function test_modifyLogin_nsIProperyBag_metainfo() { Assert.equal(gLoginMetaInfo2.timePasswordChanged, newTimeMs); // Check the special timesUsedIncrement property. - Services.logins.modifyLogin( + await Services.logins.modifyLoginAsync( gLoginInfo2, newPropertyBag({ timesUsedIncrement: 2, @@ -232,14 +232,13 @@ add_task(async function test_modifyLogin_nsIProperyBag_metainfo() { * Tests that modifying a login to a duplicate GUID throws an exception. */ add_task(async function test_modifyLogin_nsIProperyBag_metainfo_duplicate() { - Assert.throws( - () => - Services.logins.modifyLogin( - gLoginInfo1, - newPropertyBag({ - guid: gLoginInfo2.guid, - }) - ), + await Assert.rejects( + Services.logins.modifyLoginAsync( + gLoginInfo1, + newPropertyBag({ + guid: gLoginInfo2.guid, + }) + ), /specified GUID already exists/ ); await LoginTestUtils.checkLogins([gLoginInfo1, gLoginInfo2, gLoginInfo3]); diff --git a/toolkit/components/passwordmgr/test/unit/test_notifications.js b/toolkit/components/passwordmgr/test/unit/test_notifications.js @@ -99,7 +99,7 @@ add_task(async function test_notifications() { expectedNotification = "modifyLogin"; expectedData = [testuser1, testuser2]; - Services.logins.modifyLogin(testuser1, testuser2); + await Services.logins.modifyLoginAsync(testuser1, testuser2); Assert.equal(expectedNotification, null); await LoginTestUtils.checkLogins([testuser2]); diff --git a/toolkit/components/passwordmgr/test/unit/test_reencrypt.js b/toolkit/components/passwordmgr/test/unit/test_reencrypt.js @@ -87,7 +87,10 @@ add_task(async function test_reencrypt_mixed_mechanism() { Services.prefs.setIntPref("security.sdr.mechanism", 0); // Reencrypt single login with different mechanism - Services.logins.modifyLogin(EXPECTED_LOGINS[0], EXPECTED_LOGINS[0].clone()); + await Services.logins.modifyLoginAsync( + EXPECTED_LOGINS[0], + EXPECTED_LOGINS[0].clone() + ); Services.prefs.setIntPref("security.sdr.mechanism", 1); @@ -111,7 +114,7 @@ add_task(async function test_reencrypt_race() { newLogins[0] = EXPECTED_LOGINS[1].clone(); newLogins[0].password = "different password"; - await Services.logins.modifyLogin(EXPECTED_LOGINS[1], newLogins[0]); + await Services.logins.modifyLoginAsync(EXPECTED_LOGINS[1], newLogins[0]); await reencryptionPromise; diff --git a/toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs b/toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs @@ -742,7 +742,7 @@ export class LoginDataSource extends DataSourceBase { } try { - Services.logins.modifyLogin(logins[0], modifiedLogin); + await Services.logins.modifyLoginAsync(logins[0], modifiedLogin); this.setNotification({ id: notificationId, viewMode: VIEW_MODES.LIST, diff --git a/tools/@types/generated/lib.gecko.xpcom.d.ts b/tools/@types/generated/lib.gecko.xpcom.d.ts @@ -5440,6 +5440,7 @@ interface nsILoginManager extends nsISupports { addLogins(aLogins: any): Promise<any>; removeLogin(aLogin: nsILoginInfo): void; modifyLogin(oldLogin: nsILoginInfo, newLoginData: nsISupports): void; + modifyLoginAsync(oldLogin: nsILoginInfo, newLoginData: nsISupports): Promise<any>; recordPasswordUse(aLogin: nsILoginInfo, aPrivateContextWithoutExplicitConsent: boolean, aLoginType: string, aFilled: boolean): void; removeAllUserFacingLogins(): void; removeAllLogins(): void;