tor-browser

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

commit c6f501b010f11a05adb4a066daf279d719d8eda6
parent 5d0d15f127cf986001b3336b826ba5765ea0ed58
Author: Harsheet <hsohaney@mozilla.com>
Date:   Mon, 24 Nov 2025 20:21:28 +0000

Bug 2001943 - filter out private windows when deciding what to backup. r=cdupuis

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

Diffstat:
Mbrowser/components/backup/resources/SessionStoreBackupResource.sys.mjs | 58+++++++++++++++++++++++++++++++++-------------------------
Mbrowser/components/backup/tests/xpcshell/test_SessionStoreBackupResource_mockSessionStore.js | 52++++++++++++++++++++++++++++++++++++----------------
2 files changed, 69 insertions(+), 41 deletions(-)

diff --git a/browser/components/backup/resources/SessionStoreBackupResource.sys.mjs b/browser/components/backup/resources/SessionStoreBackupResource.sys.mjs @@ -60,6 +60,38 @@ export class SessionStoreBackupResource extends BackupResource { return this._sessionStore || lazy.SessionStore; } + get filteredSessionStoreState() { + let sessionStoreState = this.#sessionStore.getCurrentState(true); + // Preserving session cookies in a backup used on a different machine + // may break behavior for websites. So we leave them out of the backup. + sessionStoreState.cookies = []; + + // Remove session storage. + if (sessionStoreState.windows) { + // We don't want to backup private windows + sessionStoreState.windows = sessionStoreState.windows.filter( + w => !w?.isPrivate + ); + sessionStoreState.windows.forEach(win => { + if (win.tabs) { + win.tabs.forEach(tab => delete tab.storage); + } + if (win._closedTabs) { + win._closedTabs.forEach(closedTab => delete closedTab.state.storage); + } + }); + } + if (sessionStoreState.savedGroups) { + sessionStoreState.savedGroups.forEach(group => { + if (group.tabs) { + group.tabs.forEach(tab => delete tab.state.storage); + } + }); + } + + return sessionStoreState; + } + async backup( stagingPath, profilePath = PathUtils.profileDir, @@ -86,33 +118,9 @@ export class SessionStoreBackupResource extends BackupResource { } }); - let sessionStoreState = this.#sessionStore.getCurrentState(true); let sessionStorePath = PathUtils.join(stagingPath, "sessionstore.jsonlz4"); - // Preserving session cookies in a backup used on a different machine - // may break behavior for websites. So we leave them out of the backup. - sessionStoreState.cookies = []; - - // Remove session storage. - if (sessionStoreState.windows) { - sessionStoreState.windows.forEach(win => { - if (win.tabs) { - win.tabs.forEach(tab => delete tab.storage); - } - if (win._closedTabs) { - win._closedTabs.forEach(closedTab => delete closedTab.state.storage); - } - }); - } - if (sessionStoreState.savedGroups) { - sessionStoreState.savedGroups.forEach(group => { - if (group.tabs) { - group.tabs.forEach(tab => delete tab.state.storage); - } - }); - } - - await IOUtils.writeJSON(sessionStorePath, sessionStoreState, { + await IOUtils.writeJSON(sessionStorePath, this.filteredSessionStoreState, { compress: true, }); await BackupResource.copyFiles(profilePath, stagingPath, [ diff --git a/browser/components/backup/tests/xpcshell/test_SessionStoreBackupResource_mockSessionStore.js b/browser/components/backup/tests/xpcshell/test_SessionStoreBackupResource_mockSessionStore.js @@ -24,7 +24,7 @@ const mockSessionStore = { { tabs: [ { - someData: "hi I am data", + someData: "hi I am data, I will get serialized", moreData: -3.7, storage: { message: "I don't get serialized!", @@ -62,7 +62,7 @@ const mockSessionStore = { { tabs: [ { - someData: "hi I am window #2's data", + someData: "hi I am window #2's data, I will get serialized", moreData: -3.7, storage: { message: "I don't get serialized!", @@ -82,6 +82,30 @@ const mockSessionStore = { }, ], }, + { + tabs: [ + { + someData: "hi I am the private window's data", + storage: { + message: "I don't get serialized!", + }, + }, + ], + isPrivate: true, + _closedTabs: [], + }, + { + tabs: [ + { + someData: "hi I am the private window #2's data", + storage: { + message: "I don't get serialized!", + }, + }, + ], + isPrivate: true, + _closedTabs: [], + }, ], savedGroups: [ { @@ -110,13 +134,15 @@ const mockSessionStore = { }; // This is mockSessionStore but with the data that should not be saved removed. -const filteredMockSessionData = mockSessionStore.getCurrentState(true); -filteredMockSessionData.windows.forEach(win => { - win.tabs.forEach(tab => delete tab.storage); - win._closedTabs.forEach(closedTab => delete closedTab.state.storage); -}); -filteredMockSessionData.savedGroups.forEach(group => { - group.tabs.forEach(tab => delete tab.state.storage); +let filteredMockSessionData; +let sessionStoreBackupResource; + +add_setup(() => { + // let's use the SessionStoreBackupResource's filtering + sessionStoreBackupResource = new SessionStoreBackupResource(mockSessionStore); + + filteredMockSessionData = + sessionStoreBackupResource.filteredSessionStoreState; }); /** @@ -126,9 +152,6 @@ filteredMockSessionData.savedGroups.forEach(group => { add_task(async function test_backups_have_correct_window_state() { let sandbox = sinon.createSandbox(); - let sessionStoreBackupResource = new SessionStoreBackupResource( - mockSessionStore - ); let sourcePath = await IOUtils.createUniqueDirectory( PathUtils.tempDir, "SessionStoreBackupResource-src" @@ -145,7 +168,7 @@ add_task(async function test_backups_have_correct_window_state() { Assert.equal( filteredMockSessionData.windows.length, 2, - "will serialize 2 windows" + "will serialize only 2 windows, since we don't backup private window sessions" ); Assert.equal( filteredMockSessionData.windows[0].tabs.length, @@ -217,9 +240,6 @@ add_task(async function test_backups_have_correct_window_state() { * the recovery directory into the destination profile directory. */ add_task(async function test_recover() { - let sessionStoreBackupResource = new SessionStoreBackupResource( - mockSessionStore - ); let recoveryPath = await IOUtils.createUniqueDirectory( PathUtils.tempDir, "SessionStoreBackupResource-recover"