tor-browser

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

commit 79cf28a5d8e33529610c6b2e0aad985203c9334b
parent 8f3ad1c4b1d2f67128e3a27de98d1fa52c4aaea1
Author: Harsheet <hsohaney@mozilla.com>
Date:   Thu,  2 Oct 2025 21:40:48 +0000

Bug 1990517 - Show restore errors if any restore resource's fail. r=sthompson

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

Diffstat:
Mbrowser/components/backup/BackupService.sys.mjs | 2++
Abrowser/components/backup/tests/xpcshell/test_ResourceFailures.js | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/backup/tests/xpcshell/xpcshell.toml | 2++
3 files changed, 112 insertions(+), 0 deletions(-)

diff --git a/browser/components/backup/BackupService.sys.mjs b/browser/components/backup/BackupService.sys.mjs @@ -2598,6 +2598,7 @@ export class BackupService extends EventTarget { } catch (_) { lazy.logConsole.warn("Could not remove ", RECOVERY_FILE_DEST_PATH); } + try { // We're using a try/finally here to clean up the temporary OSKeyStore. // We need to make sure that cleanup occurs _after_ the recovery has @@ -2791,6 +2792,7 @@ export class BackupService extends EventTarget { `Failed to recover resource: ${resourceKey}`, e ); + throw e; } } diff --git a/browser/components/backup/tests/xpcshell/test_ResourceFailures.js b/browser/components/backup/tests/xpcshell/test_ResourceFailures.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. +https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + BackupError: "resource:///modules/backup/BackupError.mjs", + ERRORS: "chrome://browser/content/backup/backup-constants.mjs", + AppConstants: "resource://gre/modules/AppConstants.sys.mjs", +}); + +/** + * Test's the case where a resource fails to recover successfully. We expect + * the complete recover process to error out. + */ +add_task(async function testResourceFailure() { + let sandbox = sinon.createSandbox(); + registerCleanupFunction(() => { + sandbox.restore(); + }); + + let testBackupPath = await IOUtils.createUniqueDirectory( + PathUtils.tempDir, + "checkForErrorsTestBackup" + ); + + let fake1ManifestEntry = { fake1: "hello from 1" }; + sandbox + .stub(FakeBackupResource1.prototype, "backup") + .resolves(fake1ManifestEntry); + sandbox.stub(FakeBackupResource1.prototype, "recover").resolves(); + + let fake2ManifestEntry = { fake1: "hello from 2" }; + sandbox + .stub(FakeBackupResource2.prototype, "backup") + .resolves(fake2ManifestEntry); + sandbox + .stub(FakeBackupResource2.prototype, "recover") + .rejects(new BackupError("recovery failed", ERRORS.RECOVERY_FAILED)); + + let fake3ManifestEntry = { fake1: "hello from 3" }; + sandbox + .stub(FakeBackupResource3.prototype, "backup") + .resolves(fake3ManifestEntry); + sandbox.stub(FakeBackupResource3.prototype, "recover").resolves(); + + let bs = new BackupService({ + FakeBackupResource1, + FakeBackupResource2, + FakeBackupResource3, + }); + + let { manifest, archivePath: backupFilePath } = await bs.createBackup({ + profilePath: testBackupPath, + }); + Assert.ok(await IOUtils.exists(backupFilePath), "The backup file exists"); + + let archiveDateSuffix = bs.generateArchiveDateSuffix( + new Date(manifest.meta.date) + ); + + // We also expect the HTML file to have been written to the folder pointed + // at by browser.backups.location, within backupDirPath folder. + const EXPECTED_ARCHIVE_PATH = PathUtils.join( + bs.state.backupDirPath, + `${BackupService.BACKUP_FILE_NAME}_${manifest.meta.profileName}_${archiveDateSuffix}.html` + ); + Assert.ok( + await IOUtils.exists(EXPECTED_ARCHIVE_PATH), + "Single-file backup archive was written." + ); + Assert.equal( + backupFilePath, + EXPECTED_ARCHIVE_PATH, + "Backup was written to the configured destination folder" + ); + Assert.deepEqual( + Object.keys(manifest.resources).sort(), + ["fake1", "fake2", "fake3"], + "Manifest contains all expected BackupResource keys" + ); + + let recoveredProfilePath = await IOUtils.createUniqueDirectory( + PathUtils.tempDir, + "createBackupTestRecoveredProfile" + ); + + await Assert.rejects( + bs.recoverFromBackupArchive( + backupFilePath, + null, + false, + testBackupPath, + recoveredProfilePath + ), + err => err.cause == ERRORS.RECOVERY_FAILED + ); + + // Since we fail on backupResource2, backupResource1 should never be called + sinon.assert.notCalled(FakeBackupResource1.prototype.recover); + + // Ideally, we should be removing any left over data when we fail. + // The current behavior does not do this, so let's clear the paths + // manually in the test. + await maybeRemovePath(backupFilePath); + await maybeRemovePath(testBackupPath); + await maybeRemovePath(recoveredProfilePath); +}); diff --git a/browser/components/backup/tests/xpcshell/xpcshell.toml b/browser/components/backup/tests/xpcshell/xpcshell.toml @@ -74,6 +74,8 @@ run-sequentially = ["true"] # Mock Windows registry interferes with normal opera ["test_PreferencesBackupResource_searchEngines.js"] +["test_ResourceFailures.js"] + ["test_SessionStoreBackupResource.js"] ["test_backupService_findBackupsInWellKnownLocations.js"]