commit d0e1b223c7f795e58fe39ba0ba1d5ec5fb0c222c
parent dff1d8a32d02b00585cac1ac9dced99abf6d5057
Author: David P. <daparks@mozilla.com>
Date: Wed, 29 Oct 2025 23:42:12 +0000
Bug 1990634: Part 2 - Block manual and scheduled backups if managed profiles exist r=profiles-reviewers,mconley,kpatenio,mossop,cdupuis
There is are known issues (bug 1990980) with backups of managed profiles. We
need to prevent the user from requesting backups until that issue is fixed.
This is done by making sure that browser.backup.archive.enabled and
browser.backup.restore.enabled are false if browser.profiles.created is true.
Differential Revision: https://phabricator.services.mozilla.com/D267709
Diffstat:
3 files changed, 133 insertions(+), 0 deletions(-)
diff --git a/browser/components/backup/BackupService.sys.mjs b/browser/components/backup/BackupService.sys.mjs
@@ -638,6 +638,14 @@ export class BackupService extends EventTarget {
};
}
+ if (lazy.SelectableProfileService.hasCreatedSelectableProfiles()) {
+ return {
+ enabled: false,
+ reason:
+ "Archiving a profile is disabled because the user has created selectable profiles.",
+ };
+ }
+
return { enabled: true };
}
@@ -671,6 +679,14 @@ export class BackupService extends EventTarget {
};
}
+ if (lazy.SelectableProfileService.hasCreatedSelectableProfiles()) {
+ return {
+ enabled: false,
+ reason:
+ "Restoring a profile is disabled because the user has created selectable profiles.",
+ };
+ }
+
return { enabled: true };
}
diff --git a/browser/components/backup/tests/xpcshell/test_BackupService.js b/browser/components/backup/tests/xpcshell/test_BackupService.js
@@ -865,6 +865,111 @@ add_task(
);
/**
+ * Tests that the existence of selectable profiles prevent backups (see bug
+ * 1990980).
+ *
+ * @param {boolean} aSetCreatedSelectableProfilesBeforeSchedulingBackups
+ * If true (respectively, false), set browser.profiles.created before
+ * (respectively, after) attempting to setScheduledBackups.
+ */
+async function testSelectableProfilesPreventBackup(
+ aSetCreatedSelectableProfilesBeforeSchedulingBackups
+) {
+ let sandbox = sinon.createSandbox();
+ Services.fog.testResetFOG();
+ const TEST_UID = "ThisIsMyTestUID";
+ const TEST_EMAIL = "foxy@mozilla.org";
+ sandbox.stub(UIState, "get").returns({
+ status: UIState.STATUS_SIGNED_IN,
+ uid: TEST_UID,
+ email: TEST_EMAIL,
+ });
+
+ 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);
+
+ // Make sure created profiles pref is not set until we want it to be.
+ Services.prefs.setBoolPref(SELECTABLE_PROFILES_CREATED_PREF, false);
+
+ const setHasSelectableProfiles = () => {
+ // "Enable" selectable profiles by pref.
+ Services.prefs.setBoolPref(SELECTABLE_PROFILES_CREATED_PREF, true);
+ Assert.ok(
+ Services.prefs.getBoolPref(SELECTABLE_PROFILES_CREATED_PREF),
+ "set has selectable profiles | browser.profiles.created = true"
+ );
+ };
+
+ if (aSetCreatedSelectableProfilesBeforeSchedulingBackups) {
+ setHasSelectableProfiles();
+ }
+
+ let bs = new BackupService({});
+ bs.initBackupScheduler();
+ bs.setScheduledBackups(true);
+
+ const SCHEDULED_BACKUP_ENABLED_PREF = "browser.backup.scheduled.enabled";
+ if (!aSetCreatedSelectableProfilesBeforeSchedulingBackups) {
+ Assert.ok(
+ Services.prefs.getBoolPref(SCHEDULED_BACKUP_ENABLED_PREF, true),
+ "enabled scheduled backups | browser.backup.scheduled.enabled = true"
+ );
+ registerCleanupFunction(() => {
+ // Just in case the test fails.
+ bs.setScheduledBackups(false);
+ info("cleared scheduled backups");
+ });
+
+ setHasSelectableProfiles();
+ }
+
+ // Backups attempts should be rejected because of selectable profiles.
+ let fakeProfilePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "testSelectableProfilesPreventBackup"
+ );
+ registerCleanupFunction(async () => {
+ await maybeRemovePath(fakeProfilePath);
+ });
+ let failedBackup = await bs.createBackup({
+ profilePath: fakeProfilePath,
+ });
+ Assert.equal(failedBackup, null, "Backup returned null");
+
+ // Test cleanup
+ if (!aSetCreatedSelectableProfilesBeforeSchedulingBackups) {
+ bs.uninitBackupScheduler();
+ }
+
+ Services.prefs.clearUserPref(SELECTABLE_PROFILES_CREATED_PREF);
+ // These tests assume that backups and restores have been enabled.
+ Services.prefs.setBoolPref(BACKUP_ARCHIVE_ENABLED_PREF_NAME, true);
+ Services.prefs.setBoolPref(BACKUP_RESTORE_ENABLED_PREF_NAME, true);
+ sandbox.restore();
+}
+
+add_task(
+ async function test_managing_profiles_before_scheduling_prevents_backup() {
+ await testSelectableProfilesPreventBackup(
+ true /* aSetCreatedSelectableProfilesBeforeSchedulingBackups */
+ );
+ }
+);
+
+add_task(
+ async function test_managing_profiles_after_scheduling_prevents_backup() {
+ await testSelectableProfilesPreventBackup(
+ false /* aSetCreatedSelectableProfilesBeforeSchedulingBackups */
+ );
+ }
+);
+
+/**
* Tests that if there's a post-recovery.json file in the profile directory
* when checkForPostRecovery() is called, that it is processed, and the
* postRecovery methods on the associated BackupResources are called with the
diff --git a/browser/components/profiles/SelectableProfileService.sys.mjs b/browser/components/profiles/SelectableProfileService.sys.mjs
@@ -231,6 +231,14 @@ class SelectableProfileServiceClass extends EventEmitter {
() => this.updateEnabledState(),
"profile-after-change"
);
+
+ Services.prefs.addObserver(PROFILES_CREATED_PREF_NAME, () =>
+ Services.obs.notifyObservers(
+ null,
+ "sps-profile-created",
+ lazy.PROFILES_CREATED ? "true" : "false"
+ )
+ );
}
// Migrate any early users who created profiles before the datastore service
@@ -242,6 +250,10 @@ class SelectableProfileServiceClass extends EventEmitter {
}
}
+ hasCreatedSelectableProfiles() {
+ return Services.prefs.getBoolPref(PROFILES_CREATED_PREF_NAME, false);
+ }
+
#getEnabledState() {
if (!Services.policies.isAllowed("profileManagement")) {
return false;