tor-browser

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

commit 824e154bcf2b2198d86c863673eaf26ccbb10d54
parent 17b749e93fcf9ab5f85033d6ad3876c34dc9a8b7
Author: Johannes Schmidt <joschmidt@mozilla.com>
Date:   Thu, 16 Oct 2025 17:11:36 +0000

Bug 1993480 - Rust store not mirrored during import - r=dimi

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

Diffstat:
Mtoolkit/components/passwordmgr/LoginHelper.sys.mjs | 5++++-
Mtoolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs | 25+++++++++++++++----------
Mtoolkit/components/passwordmgr/test/browser/browser_rust_mirror.js | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 89 insertions(+), 11 deletions(-)

diff --git a/toolkit/components/passwordmgr/LoginHelper.sys.mjs b/toolkit/components/passwordmgr/LoginHelper.sys.mjs @@ -1413,9 +1413,11 @@ export const LoginHelper = { * @returns {Object[]} An entry for each processed row containing how the row was processed and the login data. */ async maybeImportLogins(loginDatas) { + // by setting this flag we ensure no events are submitted this.importing = true; + const processor = new ImportRowProcessor(); + try { - const processor = new ImportRowProcessor(); for (let rawLoginData of loginDatas) { // Do some sanitization on a clone of the loginData. let loginData = ChromeUtils.shallowClone(rawLoginData); @@ -1710,6 +1712,7 @@ export const LoginHelper = { * Send a notification when stored data is changed. */ notifyStorageChanged(changeType, data) { + // do not emit individual events during csv import if (this.importing) { return; } diff --git a/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs b/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs @@ -249,8 +249,10 @@ export class LoginManagerRustMirror { } break; + // re-migrate on importLogins event case "importLogins": - // ignoring importLogins event + this.#logger.log("re-migrating logins after import..."); + await this.#migrate(); break; default: @@ -260,11 +262,6 @@ export class LoginManagerRustMirror { } async #maybeRunMigration() { - if (this.#migrationInProgress) { - this.#logger.log("Migration already in progress."); - return; - } - if (!this.#isEnabled || lazy.LoginHelper.isPrimaryPasswordSet()) { this.#logger.log("Mirror is not active. Migration will not run."); return; @@ -281,16 +278,24 @@ export class LoginManagerRustMirror { return; } - this.#logger.log("Migration is needed, migrating..."); + this.#logger.log("Migration is needed"); + + await this.#migrate(); + } + + async #migrate() { + if (this.#migrationInProgress) { + this.#logger.log("Migration already in progress."); + return; + } + + this.#logger.log("Starting migration..."); // We ignore events during migration run. Once we switch the // stores over, we will run an initial migration again to ensure // consistancy. this.#migrationInProgress = true; - // wait until loaded - await this.#jsonStorage.initializationPromise; - const t0 = Date.now(); const runId = Services.uuid.generateUUID(); let numberOfLoginsToMigrate = 0; diff --git a/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js b/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js @@ -11,6 +11,9 @@ const { LoginManagerRustStorage } = ChromeUtils.importESModule( const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); +const { LoginCSVImport } = ChromeUtils.importESModule( + "resource://gre/modules/LoginCSVImport.sys.mjs" +); /** * Tests addLogin gets synced to Rust Storage @@ -109,6 +112,73 @@ add_task(async function test_mirror_removeLogin() { }); /** + * Tests CSV import: addition gets synced to Rust Storage + */ +add_task(async function test_mirror_csv_import_add() { + await SpecialPowers.pushPrefEnv({ + set: [["signon.rustMirror.enabled", true]], + }); + + let csvFile = await LoginTestUtils.file.setupCsvFileWithLines([ + "url,username,password,httpRealm,formActionOrigin,guid,timeCreated,timeLastUsed,timePasswordChanged", + `https://example.com,joe@example.com,qwerty,My realm,,{5ec0d12f-e194-4279-ae1b-d7d281bb46f0},1589617814635,1589710449871,1589617846802`, + ]); + await LoginCSVImport.importFromCSV(csvFile.path); + + // note LoginManagerRustStorage is a singleton and already initialized when + // Services.logins gets initialized. + const rustStorage = new LoginManagerRustStorage(); + + const storedLoginInfos = await Services.logins.getAllLogins(); + const rustStoredLoginInfos = await rustStorage.getAllLogins(); + LoginTestUtils.assertLoginListsEqual(storedLoginInfos, rustStoredLoginInfos); + + LoginTestUtils.clearData(); + rustStorage.removeAllLogins(); + await SpecialPowers.flushPrefEnv(); +}); + +/** + * Tests CSV import: modification gets synced to Rust Storage + */ +add_task(async function test_mirror_csv_import_modify() { + await SpecialPowers.pushPrefEnv({ + set: [["signon.rustMirror.enabled", true]], + }); + + // create a login + const loginInfo = LoginTestUtils.testData.formLogin({ + origin: "https://example.com", + username: "username", + password: "password", + }); + const login = await Services.logins.addLoginAsync(loginInfo); + // and import it, so we update + let csvFile = await LoginTestUtils.file.setupCsvFileWithLines([ + "url,username,password,httpRealm,formActionOrigin,guid,timeCreated,timeLastUsed,timePasswordChanged", + `https://example.com,username,qwerty,My realm,,${login.guid},1589617814635,1589710449871,1589617846802`, + ]); + await LoginCSVImport.importFromCSV(csvFile.path); + + // note LoginManagerRustStorage is a singleton and already initialized when + // Services.logins gets initialized. + const rustStorage = new LoginManagerRustStorage(); + + const [storedLoginInfo] = await Services.logins.getAllLogins(); + const [rustStoredLoginInfo] = await rustStorage.getAllLogins(); + + Assert.equal( + storedLoginInfo.password, + rustStoredLoginInfo.password, + "password has been updated via csv import" + ); + + LoginTestUtils.clearData(); + rustStorage.removeAllLogins(); + await SpecialPowers.flushPrefEnv(); +}); + +/** * Verifies that the migration is triggered by according pref change */ add_task(async function test_migration_is_triggered_by_pref_change() {