tor-browser

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

commit 10657554bc2492b3a866be5c3883b33cd04d0964
parent d4aa022380da7e3c96a25035d4dd3182a209ad02
Author: Duncan McIntosh <dmcintosh@mozilla.com>
Date:   Wed, 29 Oct 2025 19:37:55 +0000

Bug 1992808 - Part 3: Differentiate between a backup scheduled before Firefox started and one scheduled after Firefox starts. r=cdupuis

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

Diffstat:
Mbrowser/components/backup/BackupService.sys.mjs | 22++++++++++++++++++++--
Mbrowser/components/backup/metrics.yaml | 5++---
Mbrowser/components/backup/tests/xpcshell/test_BackupService_scheduler.js | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/browser/components/backup/BackupService.sys.mjs b/browser/components/backup/BackupService.sys.mjs @@ -4059,16 +4059,24 @@ export class BackupService extends EventTarget { now - lastBackupDate > lazy.minimumTimeBetweenBackupsSeconds ) { lazy.logConsole.debug( - "Last backup exceeded minimum time between backups. Queing a " + + "Last backup exceeded minimum time between backups. Queueing a " + "backup via idleDispatch." ); + // Just because the user hasn't sent us events in a while doesn't mean // that the browser itself isn't busy. It might be, for example, playing // video or doing a complex calculation that the user is actively // waiting to complete, and we don't want to draw resources from that. // Instead, we'll use ChromeUtils.idleDispatch to wait until the event // loop in the parent process isn't so busy with higher priority things. - this.createBackupOnIdleDispatch(); + let expectedBackupTime = + lastBackupDate + lazy.minimumTimeBetweenBackupsSeconds; + this.createBackupOnIdleDispatch({ + reason: + expectedBackupTime < this._startupTimeUnixSeconds + ? "missed" + : "idle", + }); } else { lazy.logConsole.debug( "Last backup was too recent. Not creating one for now." @@ -4078,6 +4086,16 @@ export class BackupService extends EventTarget { } /** + * Gets the time that Firefox started as milliseconds since the Unix epoch. + * + * This is in a getter to make it easier for tests to stub it out. + */ + get _startupTimeUnixSeconds() { + let startupTimeMs = Services.startup.getStartupInfo().process.getTime(); + return Math.floor(startupTimeMs / 1000); + } + + /** * Calls BackupService.createBackup at the next moment when the event queue * is not busy with higher priority events. This is intentionally broken out * into its own method to make it easier to stub out in tests. diff --git a/browser/components/backup/metrics.yaml b/browser/components/backup/metrics.yaml @@ -650,10 +650,9 @@ browser.backup: The reason that the backup was started. Can be one of the following: - "manual" if the user selected "Backup Now"; - "idle" if the user was idle and sufficiently long passed since - the last backup (not implemented yet, will be in Part 3); + the last backup; - "missed" if the user was idle, sufficiently long has passed since - the last backup, and this backup was scheduled to happen before - Firefox opened (not implemented yet, will be in Part 3); + the last backup, and this backup was scheduled to happen before; - "user deleted some data" if cookies or other user data was deleted; - "encryption" if the password was added, changed, or removed; diff --git a/browser/components/backup/tests/xpcshell/test_BackupService_scheduler.js b/browser/components/backup/tests/xpcshell/test_BackupService_scheduler.js @@ -42,6 +42,11 @@ add_setup(() => { MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME ); }); + + // Set the startup time to zero so we only need to worry about it in its + // test. (Approximately, make sure Firefox starts 'long before' each test + // runs.) + sinon.stub(BackupService.prototype, "_startupTimeUnixSeconds").get(() => 0); }); /** @@ -183,6 +188,11 @@ add_task(async function test_BackupService_idle_no_backup_exists() { bs.createBackupOnIdleDispatch.calledOnce, "BackupService.createBackupOnIdleDispatch was called." ); + Assert.equal( + bs.createBackupOnIdleDispatch.firstCall.args[0].reason, + "idle", + "Recorded that the backup was caused by being idle." + ); sandbox.restore(); }); @@ -251,6 +261,11 @@ add_task(async function test_BackupService_idle_expired_backup() { bs.createBackupOnIdleDispatch.calledOnce, "BackupService.createBackupOnIdleDispatch was called." ); + Assert.equal( + bs.createBackupOnIdleDispatch.firstCall.args[0].reason, + "idle", + "Recorded that the backup was caused by being idle." + ); sandbox.restore(); }); @@ -287,6 +302,11 @@ add_task(async function test_BackupService_idle_time_travel() { "BackupService.createBackupOnIdleDispatch was called." ); Assert.equal( + bs.createBackupOnIdleDispatch.firstCall.args[0].reason, + "idle", + "Recorded that the backup was caused by being idle." + ); + Assert.equal( bs.state.lastBackupDate, null, "Should have cleared the last backup date." @@ -294,3 +314,50 @@ add_task(async function test_BackupService_idle_time_travel() { sandbox.restore(); }); + +/** + * Tests that calling onIdle when a backup has occurred after the threshold + * yet before Firefox started indicates to telemetry that the backup was + * missed. + */ +add_task(async function test_BackupService_idle_expired_backup() { + // Let's calculate a Date that's twenty five seconds ago. + let twentyFiveSecondsAgo = Date.now() - 25000; + let lastBackupPrefValue = Math.floor(twentyFiveSecondsAgo / 1000); + + Services.prefs.setIntPref( + LAST_BACKUP_TIMESTAMP_PREF_NAME, + lastBackupPrefValue + ); + + let bs = new BackupService(); + let sandbox = sinon.createSandbox(); + + // This needs to be greater than + // LAST_BACKUP_TIMESTAMP_PREF_NAME + MINIMUM_TIME_BETWEEN_BACKUPS_SECONDS_PREF_NAME + // and less than the current time. + let twentySecondsAgo = Math.floor(twentyFiveSecondsAgo + 21); + sandbox.stub(bs, "_startupTimeUnixSeconds").get(() => twentySecondsAgo); + + bs.initBackupScheduler(); + Assert.equal( + bs.state.lastBackupDate, + lastBackupPrefValue, + "State should have cached lastBackupDate" + ); + + sandbox.stub(bs, "createBackupOnIdleDispatch"); + + bs.onIdle(); + Assert.ok( + bs.createBackupOnIdleDispatch.calledOnce, + "BackupService.createBackupOnIdleDispatch was called." + ); + Assert.equal( + bs.createBackupOnIdleDispatch.firstCall.args[0].reason, + "missed", + "Recorded that the backup was caused by missing the deadline." + ); + + sandbox.restore(); +});