mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-12 17:55:19 +00:00
feat: Allow unload to run instantly and fix closing windows on mac, b=no-bug, c=no-component
This commit is contained in:
@@ -7,5 +7,4 @@ category browser-before-ui-startup resource:///modules/zen/ZenSessionManager.sys
|
||||
category browser-before-ui-startup resource:///modules/zen/ZenWindowSync.sys.mjs ZenWindowSync.init
|
||||
|
||||
# App shutdown consumers
|
||||
category browser-quit-application-granted resource:///modules/zen/ZenSessionManager.sys.mjs ZenSessionStore.uninit
|
||||
category browser-quit-application-granted resource:///modules/zen/ZenWindowSync.sys.mjs ZenWindowSync.uninit
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Note that changing this hidden pref will make the previous session file
|
||||
// unused, causing a new session file to be created on next write.
|
||||
const SHOULD_COMPRESS_FILE = Services.prefs.getBoolPref('zen.session-store.compress-file', true);
|
||||
|
||||
const FILE_NAME = SHOULD_COMPRESS_FILE ? 'zen-sessions.jsonlz4' : 'zen-sessions.json';
|
||||
|
||||
export class nsZenSessionFile {
|
||||
#path = PathUtils.join(PathUtils.profileDir, FILE_NAME);
|
||||
#sidebar = [];
|
||||
|
||||
async read() {
|
||||
try {
|
||||
const data = await IOUtils.readJSON(this.#path, { compress: SHOULD_COMPRESS_FILE });
|
||||
this.#sidebar = data.sidebar || [];
|
||||
} catch {
|
||||
// File doesn't exist yet, that's fine.
|
||||
}
|
||||
}
|
||||
|
||||
get sidebar() {
|
||||
return this.#sidebar;
|
||||
}
|
||||
|
||||
set sidebar(data) {
|
||||
this.#sidebar = data;
|
||||
}
|
||||
|
||||
async #write(data) {
|
||||
await IOUtils.writeJSON(this.#path, data, { compress: SHOULD_COMPRESS_FILE });
|
||||
}
|
||||
|
||||
async store() {
|
||||
const data = { sidebar: this.#sidebar };
|
||||
await this.#write(data);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { JSONFile } from 'resource://gre/modules/JSONFile.sys.mjs';
|
||||
import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
nsZenSessionFile: 'resource:///modules/zen/ZenSessionFile.sys.mjs',
|
||||
PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs',
|
||||
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
|
||||
TabGroupState: 'resource:///modules/sessionstore/TabGroupState.sys.mjs',
|
||||
@@ -14,31 +16,67 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
setTimeout: 'resource://gre/modules/Timer.sys.mjs',
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gShouldLog', 'zen.session-store.log', true);
|
||||
|
||||
// Note that changing this hidden pref will make the previous session file
|
||||
// unused, causing a new session file to be created on next write.
|
||||
const SHOULD_COMPRESS_FILE = Services.prefs.getBoolPref('zen.session-store.compress-file', true);
|
||||
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref('zen.session-store.backup-file', true);
|
||||
|
||||
const FILE_NAME = SHOULD_COMPRESS_FILE ? 'zen-sessions.jsonlz4' : 'zen-sessions.json';
|
||||
const MIGRATION_PREF = 'zen.ui.migration.session-manager-restore';
|
||||
const OBSERVING = ['browser-window-before-show'];
|
||||
|
||||
class nsZenSessionManager {
|
||||
#file;
|
||||
/**
|
||||
* Class representing the sidebar object stored in the session file.
|
||||
* This object holds all the data related to tabs, groups, folders
|
||||
* and split view state.
|
||||
*/
|
||||
class nsZenSidebarObject {
|
||||
#sidebar = {};
|
||||
|
||||
constructor() {
|
||||
this.#file = new lazy.nsZenSessionFile();
|
||||
get data() {
|
||||
return { ...this.#sidebar };
|
||||
}
|
||||
|
||||
set data(data) {
|
||||
console.log(data);
|
||||
this.#sidebar = data;
|
||||
}
|
||||
}
|
||||
|
||||
export class nsZenSessionManager {
|
||||
#file;
|
||||
#sidebarObject = new nsZenSidebarObject();
|
||||
|
||||
// Called from SessionComponents.manifest on app-startup
|
||||
init() {
|
||||
for (let topic of OBSERVING) {
|
||||
Services.obs.addObserver(this, topic);
|
||||
let profileDir = Services.dirsvc.get('ProfD', Ci.nsIFile).path;
|
||||
let backupFile = null;
|
||||
if (SHOULD_BACKUP_FILE) {
|
||||
backupFile = PathUtils.join(profileDir, 'zen-sessions-backup', FILE_NAME);
|
||||
}
|
||||
let filePath = PathUtils.join(profileDir, FILE_NAME);
|
||||
this.#file = new JSONFile({
|
||||
path: filePath,
|
||||
compression: SHOULD_COMPRESS_FILE ? 'lz4' : undefined,
|
||||
backupFile,
|
||||
});
|
||||
}
|
||||
|
||||
uninit() {
|
||||
for (let topic of OBSERVING) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
log(...args) {
|
||||
if (lazy.gShouldLog) {
|
||||
console.info('ZenSessionManager:', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
async readFile() {
|
||||
await this.#file.read();
|
||||
console.log(await this.#file.load());
|
||||
try {
|
||||
this.#sidebar = (await this.#file.load()) || {};
|
||||
} catch (e) {
|
||||
console.error('ZenSessionManager: Failed to read session file', e);
|
||||
this.#sidebar = {};
|
||||
}
|
||||
}
|
||||
|
||||
onFileRead(initialState) {
|
||||
@@ -53,33 +91,18 @@ class nsZenSessionManager {
|
||||
// Restore all windows with the same sidebar object, this will
|
||||
// guarantee that all tabs, groups, folders and split view data
|
||||
// are properly synced across all windows.
|
||||
this.log(`Restoring Zen session data into ${initialState.windows?.length || 0} windows`);
|
||||
for (const winData of initialState.windows || []) {
|
||||
this.restoreWindowData(winData);
|
||||
}
|
||||
}
|
||||
|
||||
get #sidebar() {
|
||||
return this.#file.sidebar;
|
||||
return { ...this.#sidebarObject.data };
|
||||
}
|
||||
|
||||
set #sidebar(data) {
|
||||
this.#file.sidebar = data;
|
||||
}
|
||||
|
||||
observe(aSubject, aTopic) {
|
||||
switch (aTopic) {
|
||||
case 'browser-window-before-show': // catch new windows
|
||||
this.#onBeforeBrowserWindowShown(aSubject);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the browser-window-before-show observer notification. */
|
||||
#onBeforeBrowserWindowShown(aWindow) {
|
||||
// TODO: Initialize new window
|
||||
void aWindow;
|
||||
this.#sidebarObject.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +118,10 @@ class nsZenSessionManager {
|
||||
return;
|
||||
}
|
||||
this.#collectWindowData(state);
|
||||
this.#file.store();
|
||||
// This would save the data to disk asynchronously.
|
||||
this.#file.data = this.#sidebar;
|
||||
this.#file.saveSoon();
|
||||
this.log(`Saving Zen session data with ${this.#sidebar.tabs?.length || 0} tabs`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +179,8 @@ class nsZenSessionManager {
|
||||
}
|
||||
|
||||
restoreWindowData(aWindowData) {
|
||||
const sidebar = this.#file.sidebar;
|
||||
const sidebar = this.#sidebar;
|
||||
console.log(sidebar);
|
||||
if (!sidebar) {
|
||||
return;
|
||||
}
|
||||
@@ -167,25 +194,30 @@ class nsZenSessionManager {
|
||||
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
|
||||
return;
|
||||
}
|
||||
this.log('Restoring new window with Zen session data');
|
||||
aWindow._zenPromiseNewWindowRestored = new Promise((resolve) => {
|
||||
lazy.SessionSaver.run().then(() => {
|
||||
lazy.setTimeout(() => {
|
||||
const state = lazy.SessionStore.getCurrentState(true);
|
||||
const windows = state.windows || [];
|
||||
let windowToClone =
|
||||
windows.find(
|
||||
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
|
||||
) || {};
|
||||
let newWindow = Cu.cloneInto(windowToClone, {});
|
||||
lazy.setTimeout(() => {
|
||||
const state = lazy.SessionStore.getCurrentState(true);
|
||||
const windows = (state.windows || []).find(
|
||||
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
|
||||
);
|
||||
let windowToClone = windows[0];
|
||||
let newWindow = Cu.cloneInto(windowToClone, {});
|
||||
if (windows.length < 2) {
|
||||
// We only want to restore the sidebar object if we found
|
||||
// only one normal window to clone from (which is the one
|
||||
// we are opening).
|
||||
this.log('Restoring sidebar data into new window');
|
||||
this.restoreWindowData(newWindow);
|
||||
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
|
||||
delete newWindow.selected;
|
||||
const newState = { windows: [newWindow] };
|
||||
SessionStoreInternal.restoreWindows(aWindow, newState, {
|
||||
firstWindow: true,
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
|
||||
delete newWindow.selected;
|
||||
const newState = { windows: [newWindow] };
|
||||
this.log(`Cloning window with ${newWindow.tabs.length} tabs`);
|
||||
SessionStoreInternal.restoreWindows(aWindow, newState, {
|
||||
firstWindow: true,
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
|
||||
|
||||
@@ -14,8 +14,10 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gWindowSyncEnabled', 'zen.window-sync.enabled');
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gShouldLog', 'zen.window-sync.log', true);
|
||||
|
||||
const OBSERVING = ['browser-window-before-show'];
|
||||
const INSTANT_EVENTS = ['unload'];
|
||||
const EVENTS = [
|
||||
'TabOpen',
|
||||
'TabClose',
|
||||
@@ -37,7 +39,7 @@ const EVENTS = [
|
||||
'TabSelect',
|
||||
|
||||
'focus',
|
||||
'unload',
|
||||
...INSTANT_EVENTS,
|
||||
];
|
||||
|
||||
// Flags acting as an enum for sync types.
|
||||
@@ -115,6 +117,12 @@ class nsZenWindowSync {
|
||||
}
|
||||
}
|
||||
|
||||
log(...args) {
|
||||
if (lazy.gShouldLog) {
|
||||
console.info('ZenWindowSync:', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a browser window is about to be shown.
|
||||
* Adds event listeners for the specified events.
|
||||
@@ -143,6 +151,7 @@ class nsZenWindowSync {
|
||||
aWindow.arguments.length > 1 &&
|
||||
[...this.#browserWindows].length > 0))
|
||||
) {
|
||||
this.log('Not syncing new window due to unsynced argument or existing synced windows');
|
||||
aWindow.document.documentElement.setAttribute('zen-unsynced-window', 'true');
|
||||
return;
|
||||
}
|
||||
@@ -219,6 +228,9 @@ class nsZenWindowSync {
|
||||
if (!window.gZenStartup.isReady || window.gZenWorkspaces?.privateWindowOrDisabled) {
|
||||
return;
|
||||
}
|
||||
if (INSTANT_EVENTS.includes(aEvent.type)) {
|
||||
return this.#handleNextEvent(aEvent);
|
||||
}
|
||||
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
|
||||
// We're already handling an event for another window.
|
||||
// To avoid re-entrancy issues, we skip this event.
|
||||
@@ -813,7 +825,12 @@ class nsZenWindowSync {
|
||||
|
||||
on_focus(aEvent) {
|
||||
const { ownerGlobal: window } = aEvent.target;
|
||||
if (!window.gBrowser || this.#lastFocusedWindow?.deref() === window) {
|
||||
if (
|
||||
!window.gBrowser ||
|
||||
this.#lastFocusedWindow?.deref() === window ||
|
||||
window.closing ||
|
||||
!window.toolbar.visible
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.#lastFocusedWindow = new WeakRef(window);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.zen += [
|
||||
"ZenSessionFile.sys.mjs",
|
||||
"ZenSessionManager.sys.mjs",
|
||||
"ZenWindowSync.sys.mjs",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user