diff --git a/prefs/zen/gtk.yaml b/prefs/zen/gtk.yaml index 5f6b7eb62..5fb3a41ab 100644 --- a/prefs/zen/gtk.yaml +++ b/prefs/zen/gtk.yaml @@ -4,7 +4,7 @@ # GTK-specific preferences - name: widget.gtk.rounded-bottom-corners.enabled - # Disabled for https://github.com/zen-browser/desktop/issues/6302, + # 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)" diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 72a332238..174bc87cd 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,25 +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); } - console.log(this.#file.dataReady) - 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 @@ -238,6 +269,7 @@ export class nsZenSessionManager { this.log("Tab entry in session file:", tab); } } + delete this._dataFromFile; } get #shouldRestoreOnlyPinned() { @@ -414,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; @@ -475,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 b1235a912..b5913a5a4 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" },