From 030ed7abf4de17da150b0d1335ac87a57611f5da Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Sat, 21 Feb 2026 00:04:09 +0100 Subject: [PATCH] feat: Make sure to search from other backups files when restoring, b=no-bug, c=no-component --- prefs/zen/gtk.yaml | 4 +- .../sessionstore/ZenSessionManager.sys.mjs | 64 +++++++++++++++---- surfer.json | 2 +- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/prefs/zen/gtk.yaml b/prefs/zen/gtk.yaml index a2c88535b..5fb3a41ab 100644 --- a/prefs/zen/gtk.yaml +++ b/prefs/zen/gtk.yaml @@ -4,7 +4,9 @@ # GTK-specific preferences - name: widget.gtk.rounded-bottom-corners.enabled - value: true + # Disabled for https://github.com/zen-browser/desktop/issues/6302, + # also see https://bugzilla.mozilla.org/show_bug.cgi?id=1979083 + value: false condition: "defined(MOZ_WIDGET_GTK)" - name: zen.widget.linux.transparency diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 70b456703..a6cee0f74 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -110,6 +110,23 @@ export class nsZenSessionManager { return PathUtils.join(PathUtils.profileDir, "zen-sessions-backup"); } + async #getBackupRecoveryOrder() { + // Also add the most recent backup file to the recovery order + let backupFiles = [PathUtils.join(this.#backupFolderPath, "clean.jsonlz4")]; + let prefix = PathUtils.join(this.#backupFolderPath, "zen-sessions-"); + try { + let files = await IOUtils.getChildren(this.#backupFolderPath); + files = files + .filter((file) => file.startsWith(prefix)) + .sort() + .reverse(); + backupFiles.push(files[0]); + } catch { + /* ignore errors reading backup folder */ + } + return backupFiles; + } + /** * Gets the spaces data from the Places database for migration. * This is only called once during the first run after updating @@ -199,6 +216,31 @@ export class nsZenSessionManager { } } + async #readDataFromFile() { + try { + await this.#file.load(); + this._dataFromFile = this.#file.data; + if (!this._dataFromFile?.spaces) { + // Go to the catch block to try to recover from backup files + // if the file is empty or has invalid data, as it can happen if the app + // crashes while writing the session file. + throw new Error("No data in session file"); + } + } catch { + for (const backupFile of await this.#getBackupRecoveryOrder()) { + try { + let data = await IOUtils.readJSON(backupFile, { decompress: true }); + this.log(`Recovered data from backup file ${backupFile}`); + this._dataFromFile = data; + break; + } catch (e) { + /* ignore errors reading backup files */ + console.error(`Failed to read backup file ${backupFile}`, e); + } + } + } + } + /** * Reads the session file and populates the sidebar object. * This should be only called once at startup. @@ -206,24 +248,14 @@ export class nsZenSessionManager { * @see SessionFileInternal.read */ async readFile() { - let fileExists = await IOUtils.exists(this.#storeFilePath); - if (!fileExists) { - this.log("Session file does not exist, running migration", this.#storeFilePath); - this._shouldRunMigration = true; - } this.init(); try { this.log("Reading Zen session file from disk"); - let promises = []; - promises.push(this.#file.load()); - if (this._shouldRunMigration) { - promises.push(this.#getDataFromDBForMigration()); - } - await Promise.all(promises); + await this.#readDataFromFile(); } catch (e) { console.error("ZenSessionManager: Failed to read session file", e); } - this.#sidebar = this.#file.data || {}; + this.#sidebar = this._dataFromFile || {}; if (!this.#sidebar.spaces?.length && !this._shouldRunMigration) { this.log("No spaces data found in session file, running migration", this.#sidebar); // If we have no spaces data, we should run migration @@ -237,6 +269,7 @@ export class nsZenSessionManager { this.log("Tab entry in session file:", tab); } } + delete this._dataFromFile; } get #shouldRestoreOnlyPinned() { @@ -413,7 +446,7 @@ export class nsZenSessionManager { ]; } for (const winData of initialState?.windows || []) { - winData.spaces = this._migrationData?.spaces || []; + winData.spaces = winData.spaces || this._migrationData?.spaces || []; if (winData.tabs) { for (const tabData of winData.tabs) { let storeId = tabData.zenSyncId || tabData.zenPinnedId; @@ -474,6 +507,11 @@ export class nsZenSessionManager { // browsing mode. We also don't want to save if there are no windows. return; } + const cleanPath = PathUtils.join(this.#backupFolderPath, "clean.jsonlz4"); + IOUtils.copy(this.#storeFilePath, cleanPath, { recursive: true }).catch(() => { + /* ignore errors creating clean backup, as it is not critical and + * we want to save the session even if we fail to create it */ + }); this.#collectWindowData(windows); // This would save the data to disk asynchronously or when quitting the app. let sidebar = this.#sidebar; diff --git a/surfer.json b/surfer.json index b383649ec..ac037ec0e 100644 --- a/surfer.json +++ b/surfer.json @@ -20,7 +20,7 @@ "brandShortName": "Zen", "brandFullName": "Zen Browser", "release": { - "displayVersion": "1.18.9b", + "displayVersion": "1.18.10b", "github": { "repo": "zen-browser/desktop" },