commit 046ec69f081a6b24948dd8c48bc1030bb1194e23
parent 92a4d5250a0fa8978d244313c3ba14a43da742c8
Author: Tessa Heidkamp <theidkamp@g79vdt4jmw.speedport.ip>
Date: Thu, 20 Nov 2025 16:03:00 +0000
Bug 2001305 - Set poisoned pref to true after failed migration. r=joschmidt,credential-management-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D273403
Diffstat:
2 files changed, 37 insertions(+), 10 deletions(-)
diff --git a/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs b/toolkit/components/passwordmgr/LoginManagerRustMirror.sys.mjs
@@ -82,13 +82,19 @@ function recordMigrationStatus(
numberOfLoginsToMigrate,
numberOfLoginsMigrated
) {
+ const had_errors = numberOfLoginsMigrated < numberOfLoginsToMigrate;
+
Glean.pwmgr.rustMigrationStatus.record({
run_id: runId,
duration_ms: duration,
number_of_logins_to_migrate: numberOfLoginsToMigrate,
number_of_logins_migrated: numberOfLoginsMigrated,
- had_errors: numberOfLoginsMigrated < numberOfLoginsToMigrate,
+ had_errors,
});
+
+ if (had_errors) {
+ Services.prefs.setBoolPref("signon.rustMirror.poisoned", true);
+ }
}
function recordMigrationFailure(runId, error) {
@@ -334,6 +340,7 @@ export class LoginManagerRustMirror {
this.#logger.log("Migration complete.");
} catch (e) {
+ Services.prefs.setBoolPref("signon.rustMirror.poisoned", true);
this.#logger.error("migration error:", e);
} finally {
const duration = Date.now() - t0;
diff --git a/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js b/toolkit/components/passwordmgr/test/browser/browser_rust_mirror.js
@@ -291,18 +291,23 @@ add_task(async function test_migration_is_idempotent() {
add_task(async function test_migration_partial_failure() {
// ensure mirror is off
await SpecialPowers.pushPrefEnv({
- set: [["signon.rustMirror.enabled", false]],
+ set: [
+ ["signon.rustMirror.enabled", false],
+ ["signon.rustMirror.poisoned", false],
+ ],
});
const rustStorage = new LoginManagerRustStorage();
// Save the first (valid) login into Rust for real, then simulate results
- sinon.stub(rustStorage, "addLoginsAsync").callsFake(async (logins, _cont) => {
- await rustStorage.addWithMeta(logins[0]);
- return [
- { login: {}, error: null }, // row 0 success
- { login: null, error: { message: "row failed" } }, // row 1 failure
- ];
- });
+ sinon
+ .stub(LoginManagerRustStorage.prototype, "addLoginsAsync")
+ .callsFake(async (logins, _cont) => {
+ await rustStorage.addWithMeta(logins[0]);
+ return [
+ { login: {}, error: null }, // row 0 success
+ { login: null, error: { message: "row failed" } }, // row 1 failure
+ ];
+ });
const login_ok = LoginTestUtils.testData.formLogin({
username: "test-user-ok",
@@ -327,6 +332,12 @@ add_task(async function test_migration_partial_failure() {
const rustLogins = await rustStorage.getAllLogins();
Assert.equal(rustLogins.length, 1, "only valid login migrated");
+ Assert.equal(
+ Services.prefs.getBoolPref("signon.rustMirror.poisoned", false),
+ true,
+ "poisoned pref is set to true on partial migration failure"
+ );
+
sinon.restore();
LoginTestUtils.clearData();
rustStorage.removeAllLogins();
@@ -340,7 +351,10 @@ add_task(async function test_migration_partial_failure() {
add_task(async function test_migration_rejects_when_bulk_add_rejects() {
// turn mirror off
await SpecialPowers.pushPrefEnv({
- set: [["signon.rustMirror.enabled", false]],
+ set: [
+ ["signon.rustMirror.enabled", false],
+ ["signon.rustMirror.poisoned", false],
+ ],
});
const rustStorage = new LoginManagerRustStorage();
@@ -371,6 +385,12 @@ add_task(async function test_migration_rejects_when_bulk_add_rejects() {
);
Assert.equal(newPrefValue, true, "pref has not been reset");
+ Assert.equal(
+ Services.prefs.getBoolPref("signon.rustMirror.poisoned", false),
+ true,
+ "poisoned pref is set to true on hard migration failure"
+ );
+
sinon.restore();
LoginTestUtils.clearData();
rustStorage.removeAllLogins();