commit 9625ece3b94b184997f55f085d85290aed595e6d
parent 2ba5de45ca0e296051c085063633a30f018b014c
Author: Harsheet <hsohaney@mozilla.com>
Date: Fri, 7 Nov 2025 16:22:49 +0000
Bug 1998176 - Clean up any backup items and disable any backup operations if backup is turned off by pref flips. r=cdupuis
Differential Revision: https://phabricator.services.mozilla.com/D271603
Diffstat:
4 files changed, 103 insertions(+), 15 deletions(-)
diff --git a/browser/components/backup/BackupService.sys.mjs b/browser/components/backup/BackupService.sys.mjs
@@ -1195,7 +1195,10 @@ export class BackupService extends EventTarget {
this.#postRecoveryResolver = resolve;
this.#backupWriteAbortController = new AbortController();
this.#regenerationDebouncer = new lazy.DeferredTask(async () => {
- if (!this.#backupWriteAbortController.signal.aborted) {
+ if (
+ !this.#backupWriteAbortController.signal.aborted &&
+ this.archiveEnabledStatus.enabled
+ ) {
await this.deleteLastBackup();
if (lazy.scheduledBackupsPref) {
await this.createBackupOnIdleDispatch({
@@ -3938,14 +3941,14 @@ export class BackupService extends EventTarget {
// immediately reflect across any observers, instead of waiting on idle.
this.#statusPrefObserver = () => {
// Wrap in an arrow function so 'this' is preserved.
- this.#notifyStatusObservers();
+ this.#handleStatusChange();
};
for (let pref of BackupService.STATUS_OBSERVER_PREFS) {
Services.prefs.addObserver(pref, this.#statusPrefObserver);
}
lazy.NimbusFeatures.backupService.onUpdate(this.#statusPrefObserver);
- this.#notifyStatusObservers();
+ this.#handleStatusChange();
}
/**
@@ -3967,10 +3970,30 @@ export class BackupService extends EventTarget {
}
/**
+ * Performs tasks required whenever archive or restore change their status
+ *
+ * 1. Notifies any observers that a change has taken place
+ * 2. If archive is disabled, clean up any backup files
+ */
+ #handleStatusChange() {
+ this.#notifyStatusObservers();
+
+ if (!this.archiveEnabledStatus.enabled) {
+ // We won't wait for this promise to accept/reject since rejections are
+ // ignored anyways
+ this.cleanupBackupFiles();
+ }
+ }
+
+ /**
* Notify any listeners about the availability of the backup service, then
* update relevant telemetry metrics.
*/
#notifyStatusObservers() {
+ lazy.logConsole.log(
+ "Notifying observers about a BackupService state change"
+ );
+
Services.obs.notifyObservers(null, "backup-service-status-updated");
let status = this.archiveEnabledStatus;
@@ -3992,6 +4015,22 @@ export class BackupService extends EventTarget {
}
}
+ async cleanupBackupFiles() {
+ lazy.logConsole.debug("Cleaning up backup data");
+ try {
+ if (this.state.encryptionEnabled) {
+ await this.disableEncryption();
+ }
+ this.deleteLastBackup();
+ } catch (e) {
+ // Ignore any exceptions
+ lazy.logConsole.error(
+ "There was an error when cleaning up backup files: ",
+ e
+ );
+ }
+ }
+
/**
* Called when the last known backup should be deleted and a new one
* created. This uses the #regenerationDebouncer to debounce clusters of
@@ -4014,7 +4053,7 @@ export class BackupService extends EventTarget {
this.#takenMeasurements = true;
}
- if (lazy.scheduledBackupsPref) {
+ if (lazy.scheduledBackupsPref && this.archiveEnabledStatus.enabled) {
lazy.logConsole.debug("Scheduled backups enabled.");
let now = Math.floor(Date.now() / 1000);
let lastBackupDate = this.#_state.lastBackupDate;
diff --git a/browser/components/backup/actors/BackupUIParent.sys.mjs b/browser/components/backup/actors/BackupUIParent.sys.mjs
@@ -137,14 +137,7 @@ export class BackupUIParent extends JSWindowActorParent {
*/
return { success: true };
} else if (message.name == "DisableScheduledBackups") {
- try {
- if (this.#bs.state.encryptionEnabled) {
- await this.#bs.disableEncryption();
- }
- await this.#bs.deleteLastBackup();
- } catch (e) {
- // no-op so that scheduled backups can still be turned off
- }
+ await this.#bs.cleanupBackupFiles();
this.#bs.setScheduledBackups(false);
} else if (message.name == "ShowFilepicker") {
let { win, filter, existingBackupPath } = message.data;
diff --git a/browser/components/backup/tests/xpcshell/test_BackupService.js b/browser/components/backup/tests/xpcshell/test_BackupService.js
@@ -19,10 +19,16 @@ const { ERRORS } = ChromeUtils.importESModule(
"chrome://browser/content/backup/backup-constants.mjs"
);
+const { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
const LAST_BACKUP_TIMESTAMP_PREF_NAME =
"browser.backup.scheduled.last-backup-timestamp";
const LAST_BACKUP_FILE_NAME_PREF_NAME =
"browser.backup.scheduled.last-backup-file";
+const BACKUP_ARCHIVE_ENABLED_PREF_NAME = "browser.backup.archive.enabled";
+const BACKUP_RESTORE_ENABLED_PREF_NAME = "browser.backup.restore.enabled";
/** @type {nsIToolkitProfile} */
let currentProfile;
@@ -877,9 +883,6 @@ async function testSelectableProfilesPreventBackup(
const SELECTABLE_PROFILES_CREATED_PREF = "browser.profiles.created";
- // Make sure backup and restore are enabled.
- const BACKUP_ARCHIVE_ENABLED_PREF_NAME = "browser.backup.archive.enabled";
- const BACKUP_RESTORE_ENABLED_PREF_NAME = "browser.backup.restore.enabled";
Services.prefs.setBoolPref(BACKUP_ARCHIVE_ENABLED_PREF_NAME, true);
Services.prefs.setBoolPref(BACKUP_RESTORE_ENABLED_PREF_NAME, true);
@@ -1199,3 +1202,36 @@ add_task(async function test_getBackupFileInfo_error_handling() {
sandbox.restore();
}
});
+
+/**
+ * Tests changing the status prefs to ensure that backup is cleaned up if being disabled.
+ */
+add_task(async function test_changing_prefs_cleanup() {
+ let sandbox = sinon.createSandbox();
+ let bs = BackupService.init();
+
+ let cleanupStub = sandbox.stub(bs, "cleanupBackupFiles");
+ let statusUpdatePromise = TestUtils.topicObserved(
+ "backup-service-status-updated"
+ );
+
+ Services.prefs.setBoolPref(BACKUP_ARCHIVE_ENABLED_PREF_NAME, false);
+
+ await statusUpdatePromise;
+
+ Assert.equal(
+ cleanupStub.callCount,
+ 1,
+ "Cleanup backup files was called on pref change"
+ );
+
+ Services.prefs.setBoolPref(BACKUP_ARCHIVE_ENABLED_PREF_NAME, true);
+
+ Assert.equal(
+ cleanupStub.callCount,
+ 1,
+ "Cleanup backup files should not have been called when enabling backups"
+ );
+
+ Services.prefs.clearUserPref(BACKUP_ARCHIVE_ENABLED_PREF_NAME);
+});
diff --git a/browser/components/backup/tests/xpcshell/test_BackupService_regeneration.js b/browser/components/backup/tests/xpcshell/test_BackupService_regeneration.js
@@ -637,3 +637,23 @@ add_task(async function test_newtab_link_blocked() {
NewTabUtils.activityStreamLinks.blockURL("https://example.com");
}, "Saw regeneration on the blocking of a newtab link");
});
+
+/**
+ * Tests that setting up a regeneration without archiveEnabledStatus being true
+ * leads to NO regeneration upon data mutation
+ */
+add_task(async function test_enabledStatus_no_regeneration() {
+ let bookmark = await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ url: "data:text/plain,Content",
+ title: "Regeneration Test Bookmark",
+ });
+
+ Services.prefs.setBoolPref("browser.backup.archive.enabled", false);
+
+ await expectNoRegeneration(async () => {
+ await PlacesUtils.bookmarks.remove(bookmark);
+ }, "Saw no regeneration on bookmark removed.");
+
+ Services.prefs.clearUserPref("browser.backup.archive.enabled");
+});