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:
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"