From 81e854a89f57862623c65c1fe4c70dc6507de6bd Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Wed, 27 Aug 2025 23:25:07 +0200 Subject: [PATCH 1/5] feat: Full cross-window workspace syncing, b=no-bug, c=workspaces --- src/browser/base/content/zen-assets.inc.xhtml | 1 + .../base/content/zen-assets.jar.inc.mn | 1 + src/zen/workspaces/ZenWindowSyncing.mjs | 226 ++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 src/zen/workspaces/ZenWindowSyncing.mjs diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml index 0756714ea6..d94a632dda 100644 --- a/src/browser/base/content/zen-assets.inc.xhtml +++ b/src/browser/base/content/zen-assets.inc.xhtml @@ -58,3 +58,4 @@ + diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn index 4c341e4e13..78d3f4bbd9 100644 --- a/src/browser/base/content/zen-assets.jar.inc.mn +++ b/src/browser/base/content/zen-assets.jar.inc.mn @@ -44,6 +44,7 @@ content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs) content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs) content/browser/zen-components/ZenWorkspaces.mjs (../../zen/workspaces/ZenWorkspaces.mjs) + content/browser/zen-components/ZenWindowSyncing.mjs (../../zen/workspaces/ZenWindowSyncing.mjs) content/browser/zen-components/ZenWorkspaceCreation.mjs (../../zen/workspaces/ZenWorkspaceCreation.mjs) content/browser/zen-components/ZenWorkspacesStorage.mjs (../../zen/workspaces/ZenWorkspacesStorage.mjs) content/browser/zen-components/ZenWorkspacesSync.mjs (../../zen/workspaces/ZenWorkspacesSync.mjs) diff --git a/src/zen/workspaces/ZenWindowSyncing.mjs b/src/zen/workspaces/ZenWindowSyncing.mjs new file mode 100644 index 0000000000..19fbf09f42 --- /dev/null +++ b/src/zen/workspaces/ZenWindowSyncing.mjs @@ -0,0 +1,226 @@ +// 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/. +{ + class nsZenWorkspaceWindowSync extends nsZenMultiWindowFeature { + #ignoreNextEvents = false; + #waitForPromise = null; + + constructor() { + super(); + if (!window.closed) { + this.init(); + } + } + + async init() { + await gZenWorkspaces.promiseInitialized; + this.#makeSureAllTabsHaveIds(); + this.#setUpEventListeners(); + } + + #makeSureAllTabsHaveIds() { + const allTabs = gZenWorkspaces.allStoredTabs; + for (const tab of allTabs) { + if (!tab.hasAttribute('zen-sync-id')) { + const tabId = gZenUIManager.generateUuidv4(); + tab.setAttribute('zen-sync-id', tabId); + } + } + } + + #setUpEventListeners() { + const kEvents = [ + 'TabClose', + 'TabOpen', + 'TabPinned', + 'TabUnpinned', + 'TabAddedToEssentials', + 'TabRemovedFromEssentials', + 'TabHide', + 'TabShow', + 'TabMove', + ]; + const eventListener = this.#handleEvent.bind(this); + for (const event of kEvents) { + window.addEventListener(event, eventListener); + } + + window.addEventListener('unload', () => { + for (const event of kEvents) { + window.removeEventListener(event, eventListener); + } + }); + } + + #handleEvent(event) { + this.#propagateToOtherWindows(event); + } + + async #propagateToOtherWindows(event) { + if (this.#ignoreNextEvents) { + return; + } + if (this.#waitForPromise) { + await this.#waitForPromise; + } + this.#waitForPromise = new Promise(async (resolve) => { + await this.foreachWindowAsActive(async (browser) => { + if (browser.gZenWorkspaceWindowSync && !this.windowIsActive(browser)) { + await browser.gZenWorkspaceWindowSync.onExternalTabEvent(event); + } + }); + resolve(); + }); + } + + async onExternalTabEvent(event) { + this.#ignoreNextEvents = true; + switch (event.type) { + case 'TabClose': + this.#onTabClose(event); + break; + case 'TabOpen': + await this.#onTabOpen(event); + break; + case 'TabPinned': + this.#onTabPinned(event); + break; + case 'TabUnpinned': + this.#onTabUnpinned(event); + break; + case 'TabAddedToEssentials': + this.#onTabAddedToEssentials(event); + break; + case 'TabRemovedFromEssentials': + this.#onTabRemovedFromEssentials(event); + break; + case 'TabHide': + this.#onTabHide(event); + break; + case 'TabShow': + this.#onTabShow(event); + break; + case 'TabMove': + this.#onTabMove(event); + break; + default: + console.warn(`Unhandled event type: ${event.type}`); + break; + } + this.#ignoreNextEvents = false; + } + + #getTabId(tab) { + return tab.getAttribute('zen-sync-id'); + } + + #getTabWithId(tabId) { + for (const tab of gZenWorkspaces.allStoredTabs) { + if (this.#getTabId(tab) === tabId) { + return tab; + } + } + return null; + } + + #onTabClose(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToClose = this.#getTabWithId(tabId); + if (tabToClose) { + gBrowser.removeTab(tabToClose); + } + } + + #onTabPinned(event) { + const targetTab = event.target; + if (targetTab.hasAttribute('zen-essential')) { + return this.#onTabAddedToEssentials(event); + } + const tabId = this.#getTabId(targetTab); + const elementIndex = targetTab.elementIndex; + const tabToPin = this.#getTabWithId(tabId); + if (tabToPin) { + gBrowser.pinTab(tabToPin); + gBrowser.moveTabTo(tabToPin, { elementIndex, forceUngrouped: !!targetTab.group }); + } + } + + #onTabUnpinned(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToUnpin = this.#getTabWithId(tabId); + if (tabToUnpin) { + gBrowser.unpinTab(tabToUnpin); + } + } + + #onTabAddedToEssentials(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToAdd = this.#getTabWithId(tabId); + if (tabToAdd) { + gZenPinnedTabManager.addToEssentials(tabToAdd); + } + } + + #onTabRemovedFromEssentials(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToRemove = this.#getTabWithId(tabId); + if (tabToRemove) { + gZenPinnedTabManager.removeFromEssentials(tabToRemove); + } + } + + #onTabHide(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToHide = this.#getTabWithId(tabId); + if (tabToHide) { + gBrowser.hideTab(tabToHide); + } + } + + #onTabShow(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const tabToShow = this.#getTabWithId(tabId); + if (tabToShow) { + gBrowser.showTab(tabToShow); + } + } + + #onTabMove(event) { + const targetTab = event.target; + const tabId = this.#getTabId(targetTab); + const elementIndex = targetTab.elementIndex; + const tabToMove = this.#getTabWithId(tabId); + if (tabToMove) { + gBrowser.moveTabTo(tabToMove, { elementIndex, forceUngrouped: !!targetTab.group }); + } + } + + async #onTabOpen(event) { + await new Promise((resolve) => { + const targetTab = event.target; + const isPinned = targetTab.pinned; + const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); + const elementIndex = targetTab.elementIndex; + + const duplicatedTab = SessionStore.duplicateTab(window, targetTab, 0, true); + if (isEssential) { + gZenPinnedTabManager.addToEssentials(duplicatedTab); + } else if (isPinned) { + gBrowser.pinTab(duplicatedTab); + } + + gBrowser.moveTabTo(duplicatedTab, { elementIndex, forceUngrouped: !!targetTab.group }); + resolve(); + }); + } + } + + window.gZenWorkspaceWindowSync = new nsZenWorkspaceWindowSync(); +} From 7a4cdaa45cbb98b4420eec8232787a803bc1b943 Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Mon, 1 Sep 2025 16:09:59 +0200 Subject: [PATCH 2/5] feat: Also change icons and labels if the tab is pending, b=no-bug, c=tabs, workspaces --- src/zen/tabs/ZenPinnedTabManager.mjs | 2 ++ src/zen/workspaces/ZenWindowSyncing.mjs | 30 ++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 408481622d..3f3ca47369 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -92,6 +92,7 @@ } onTabIconChanged(tab, url = null) { + tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } })); const iconUrl = url ?? tab.iconImage.src; if (!iconUrl && tab.hasAttribute('zen-pin-id')) { try { @@ -1436,6 +1437,7 @@ } async onTabLabelChanged(tab) { + tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { detail: { tab } })); if (!this._pinsCache) { return; } diff --git a/src/zen/workspaces/ZenWindowSyncing.mjs b/src/zen/workspaces/ZenWindowSyncing.mjs index 19fbf09f42..bcb63d8f42 100644 --- a/src/zen/workspaces/ZenWindowSyncing.mjs +++ b/src/zen/workspaces/ZenWindowSyncing.mjs @@ -40,6 +40,8 @@ 'TabHide', 'TabShow', 'TabMove', + 'ZenTabIconChanged', + 'ZenTabLabelChanged', ]; const eventListener = this.#handleEvent.bind(this); for (const event of kEvents) { @@ -104,6 +106,12 @@ case 'TabMove': this.#onTabMove(event); break; + case 'ZenTabIconChanged': + this.#onTabIconChanged(event); + break; + case 'ZenTabLabelChanged': + this.#onTabLabelChanged(event); + break; default: console.warn(`Unhandled event type: ${event.type}`); break; @@ -156,6 +164,26 @@ } } + #onTabIconChanged(event) { + this.#updateTabIconAndLabel(event); + } + + #onTabLabelChanged(event) { + this.#updateTabIconAndLabel(event); + } + + #updateTabIconAndLabel(event) { + const targetTab = event.target; + if (targetTab.hasAttribute("pending")) { + const tabId = this.#getTabId(targetTab); + const tabToChange = this.#getTabWithId(tabId); + if (tabToChange) { + gBrowser.setIcon(tabToChange, gBrowser.getIcon(targetTab)); + gBrowser._setTabLabel(tabToChange, targetTab.label); + } + } + } + #onTabAddedToEssentials(event) { const targetTab = event.target; const tabId = this.#getTabId(targetTab); @@ -209,7 +237,7 @@ const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); const elementIndex = targetTab.elementIndex; - const duplicatedTab = SessionStore.duplicateTab(window, targetTab, 0, true); + const duplicatedTab = SessionStore.duplicateTab(window, targetTab, 0); if (isEssential) { gZenPinnedTabManager.addToEssentials(duplicatedTab); } else if (isPinned) { From 91f5d58fbc1c62f415ea6ee4e5e7f69afe2705fa Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Tue, 2 Sep 2025 16:21:04 +0200 Subject: [PATCH 3/5] feat: Dont session duplicate the tabs, b=no-bug, c=workspaces --- src/zen/workspaces/ZenWindowSyncing.mjs | 55 +++++++++++++------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/zen/workspaces/ZenWindowSyncing.mjs b/src/zen/workspaces/ZenWindowSyncing.mjs index bcb63d8f42..da5de683bd 100644 --- a/src/zen/workspaces/ZenWindowSyncing.mjs +++ b/src/zen/workspaces/ZenWindowSyncing.mjs @@ -66,13 +66,14 @@ if (this.#waitForPromise) { await this.#waitForPromise; } - this.#waitForPromise = new Promise(async (resolve) => { - await this.foreachWindowAsActive(async (browser) => { + this.#waitForPromise = new Promise((resolve) => { + this.foreachWindowAsActive(async (browser) => { if (browser.gZenWorkspaceWindowSync && !this.windowIsActive(browser)) { await browser.gZenWorkspaceWindowSync.onExternalTabEvent(event); } + }).then(() => { + resolve(); }); - resolve(); }); } @@ -174,13 +175,11 @@ #updateTabIconAndLabel(event) { const targetTab = event.target; - if (targetTab.hasAttribute("pending")) { - const tabId = this.#getTabId(targetTab); - const tabToChange = this.#getTabWithId(tabId); - if (tabToChange) { - gBrowser.setIcon(tabToChange, gBrowser.getIcon(targetTab)); - gBrowser._setTabLabel(tabToChange, targetTab.label); - } + const tabId = this.#getTabId(targetTab); + const tabToChange = this.#getTabWithId(tabId); + if (tabToChange && tabToChange.hasAttribute('pending')) { + gBrowser.setIcon(tabToChange, gBrowser.getIcon(targetTab)); + gBrowser._setTabLabel(tabToChange, targetTab.label); } } @@ -223,30 +222,34 @@ #onTabMove(event) { const targetTab = event.target; const tabId = this.#getTabId(targetTab); - const elementIndex = targetTab.elementIndex; + const tabIndex = targetTab._pPos; const tabToMove = this.#getTabWithId(tabId); if (tabToMove) { - gBrowser.moveTabTo(tabToMove, { elementIndex, forceUngrouped: !!targetTab.group }); + gBrowser.moveTabTo(tabToMove, { tabIndex, forceUngrouped: !!targetTab.group }); } } async #onTabOpen(event) { - await new Promise((resolve) => { - const targetTab = event.target; - const isPinned = targetTab.pinned; - const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); - const elementIndex = targetTab.elementIndex; - - const duplicatedTab = SessionStore.duplicateTab(window, targetTab, 0); - if (isEssential) { - gZenPinnedTabManager.addToEssentials(duplicatedTab); - } else if (isPinned) { - gBrowser.pinTab(duplicatedTab); - } + const targetTab = event.target; + const isPinned = targetTab.pinned; + const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); + const elementIndex = targetTab.elementIndex; - gBrowser.moveTabTo(duplicatedTab, { elementIndex, forceUngrouped: !!targetTab.group }); - resolve(); + const duplicatedTab = gBrowser.addTrustedTab(targetTab.linkedBrowser.currentURI.spec, { + createLazyBrowser: true, }); + + duplicatedTab.setAttribute('zen-pin-id', targetTab.getAttribute('zen-pin-id')); + duplicatedTab.setAttribute('zen-tab-id', targetTab.getAttribute('zen-tab-id')); + duplicatedTab.setAttribute('zen-workspace-id', targetTab.getAttribute('zen-workspace-id')); + + if (isEssential) { + gZenPinnedTabManager.addToEssentials(duplicatedTab); + } else if (isPinned) { + gBrowser.pinTab(duplicatedTab); + } + + gBrowser.moveTabTo(duplicatedTab, { elementIndex, forceUngrouped: !!targetTab.group }); } } From a55b1c7495cf1e1daa3bea602ef5a7f71cc7daeb Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Sat, 27 Sep 2025 17:54:28 +0200 Subject: [PATCH 4/5] feat: Properly handle tab moves, b=no-bug, c=workspaces --- src/zen/workspaces/ZenWindowSyncing.mjs | 83 ++++++++++++++++++++----- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/src/zen/workspaces/ZenWindowSyncing.mjs b/src/zen/workspaces/ZenWindowSyncing.mjs index da5de683bd..857f9fc9cf 100644 --- a/src/zen/workspaces/ZenWindowSyncing.mjs +++ b/src/zen/workspaces/ZenWindowSyncing.mjs @@ -22,7 +22,7 @@ #makeSureAllTabsHaveIds() { const allTabs = gZenWorkspaces.allStoredTabs; for (const tab of allTabs) { - if (!tab.hasAttribute('zen-sync-id')) { + if (!tab.hasAttribute('zen-sync-id') && !tab.hasAttribute('zen-empty-tab')) { const tabId = gZenUIManager.generateUuidv4(); tab.setAttribute('zen-sync-id', tabId); } @@ -33,15 +33,25 @@ const kEvents = [ 'TabClose', 'TabOpen', + 'TabMove', + 'TabPinned', 'TabUnpinned', + 'TabAddedToEssentials', 'TabRemovedFromEssentials', + 'TabHide', 'TabShow', - 'TabMove', + 'ZenTabIconChanged', 'ZenTabLabelChanged', + + 'TabGroupCreate', + 'TabGroupRemoved', + 'TabGrouped', + 'TabUngrouped', + 'TabGroupMoved', ]; const eventListener = this.#handleEvent.bind(this); for (const event of kEvents) { @@ -105,6 +115,7 @@ this.#onTabShow(event); break; case 'TabMove': + case 'TabGroupMoved': this.#onTabMove(event); break; case 'ZenTabIconChanged': @@ -113,6 +124,14 @@ case 'ZenTabLabelChanged': this.#onTabLabelChanged(event); break; + case 'TabGroupCreate': + this.#onTabGroupCreate(event); + break; + case 'TabGroupRemoved': + case 'TabGrouped': + case 'TabUngrouped': + // Tab grouping changes are automatically synced by Firefox + break; default: console.warn(`Unhandled event type: ${event.type}`); break; @@ -222,10 +241,36 @@ #onTabMove(event) { const targetTab = event.target; const tabId = this.#getTabId(targetTab); - const tabIndex = targetTab._pPos; const tabToMove = this.#getTabWithId(tabId); + const workspaceId = targetTab.getAttribute('zen-workspace-id'); + const isEssential = targetTab.hasAttribute('zen-essential'); if (tabToMove) { - gBrowser.moveTabTo(tabToMove, { tabIndex, forceUngrouped: !!targetTab.group }); + let tabSibling = targetTab.previousElementSibling; + let isFirst = false; + if (!tabSibling?.hasAttribute('zen-sync-id')) { + isFirst = true; + } + gBrowser.zenHandleTabMove(tabToMove, () => { + if (isFirst) { + let container; + if (isEssential) { + container = gZenWorkspaces.getEssentialsSection(tabToMove); + } else { + const workspaceElement = gZenWorkspaces.workspaceElement(workspaceId); + container = tabToMove.pinned + ? workspaceElement.pinnedTabsContainer + : workspaceElement.tabsContainer; + } + container.insertBefore(tabToMove, container.firstChild); + } else { + let relativeTab = gZenWorkspaces.allStoredTabs.find((tab) => { + return this.#getTabId(tab) === this.#getTabId(tabSibling); + }); + if (relativeTab) { + relativeTab.after(tabToMove); + } + } + }); } } @@ -233,23 +278,29 @@ const targetTab = event.target; const isPinned = targetTab.pinned; const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); - const elementIndex = targetTab.elementIndex; - + if (!this.#getTabId(targetTab) && !targetTab.hasAttribute('zen-empty-tab')) { + const tabId = gZenUIManager.generateUuidv4(); + targetTab.setAttribute('zen-sync-id', tabId); + } const duplicatedTab = gBrowser.addTrustedTab(targetTab.linkedBrowser.currentURI.spec, { createLazyBrowser: true, + essential: isEssential, + pinned: isPinned, }); - - duplicatedTab.setAttribute('zen-pin-id', targetTab.getAttribute('zen-pin-id')); - duplicatedTab.setAttribute('zen-tab-id', targetTab.getAttribute('zen-tab-id')); - duplicatedTab.setAttribute('zen-workspace-id', targetTab.getAttribute('zen-workspace-id')); - - if (isEssential) { - gZenPinnedTabManager.addToEssentials(duplicatedTab); - } else if (isPinned) { - gBrowser.pinTab(duplicatedTab); + if (!isEssential) { + gZenWorkspaces.moveTabToWorkspace( + duplicatedTab, + targetTab.getAttribute('zen-workspace-id') + ); } + duplicatedTab.setAttribute('zen-pin-id', targetTab.getAttribute('zen-pin-id')); + duplicatedTab.setAttribute('zen-sync-id', targetTab.getAttribute('zen-sync-id')); + } - gBrowser.moveTabTo(duplicatedTab, { elementIndex, forceUngrouped: !!targetTab.group }); + #onTabGroupCreate(event) { + const targetGroup = event.target; + const isSplitView = targetGroup.classList.contains('zen-split-view'); + const isFolder = targetGroup.isZenFolder; } } From 86006c889149bf4d44ea166637b859242f56194f Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Sun, 28 Sep 2025 23:45:13 +0200 Subject: [PATCH 5/5] feat: Start on new session restore, b=no-bug, c=no-component --- prefs/browser.yaml | 3 +- src/zen/sessionstore/ZenSessionFile.sys.mjs | 27 ++++++++++ .../sessionstore/ZenSessionManager.sys.mjs | 50 +++++++++++++++++++ src/zen/sessionstore/ZenSessionWindow.sys.mjs | 35 +++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/zen/sessionstore/ZenSessionFile.sys.mjs create mode 100644 src/zen/sessionstore/ZenSessionManager.sys.mjs create mode 100644 src/zen/sessionstore/ZenSessionWindow.sys.mjs diff --git a/prefs/browser.yaml b/prefs/browser.yaml index 30aa603e9c..f76c33f5c8 100644 --- a/prefs/browser.yaml +++ b/prefs/browser.yaml @@ -3,7 +3,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. - name: browser.startup.page - value: 3 + value: 0 + locked: true - name: browser.sessionstore.restore_pinned_tabs_on_demand value: true diff --git a/src/zen/sessionstore/ZenSessionFile.sys.mjs b/src/zen/sessionstore/ZenSessionFile.sys.mjs new file mode 100644 index 0000000000..c50960ae10 --- /dev/null +++ b/src/zen/sessionstore/ZenSessionFile.sys.mjs @@ -0,0 +1,27 @@ +// 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/. + +const FILE_NAME = 'zen-sessions.jsonlz4'; + +export class nsZenSessionFile { + #path; + + #windows; + + constructor() { + this.#path = PathUtils.join(profileDir, FILE_NAME); + } + + async read() { + try { + return await IOUtils.readJSON(this.#path, { compress: true }); + } catch (e) { + return {}; + } + } + + async write(data) { + await IOUtils.writeJSON(this.#path, data, { compress: true }); + } +} diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs new file mode 100644 index 0000000000..68aa829e40 --- /dev/null +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -0,0 +1,50 @@ +// 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 { + cancelIdleCallback, + clearTimeout, + requestIdleCallback, + setTimeout, +} from 'resource://gre/modules/Timer.sys.mjs'; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + ZenSessionFile: 'resource://gre/modules/ZenSessionFile.sys.mjs', + PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs', + RunState: 'resource:///modules/sessionstore/RunState.sys.mjs', +}); + +class nsZenSessionManager { + #file; + + constructor() { + this.#file = null; + } + + get file() { + if (!this.#file) { + this.#file = lazy.ZenSessionFile; + } + return this.#file; + } + + /** + * Saves the current session state. Collects data and writes to disk. + * + * @param forceUpdateAllWindows (optional) + * Forces us to recollect data for all windows and will bypass and + * update the corresponding caches. + */ + saveState(forceUpdateAllWindows = false) { + if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) { + // Don't save (or even collect) anything in permanent private + // browsing mode + return Promise.resolve(); + } + } +} + +export const ZenSessionStore = new nsZenSessionManager(); diff --git a/src/zen/sessionstore/ZenSessionWindow.sys.mjs b/src/zen/sessionstore/ZenSessionWindow.sys.mjs new file mode 100644 index 0000000000..4600702349 --- /dev/null +++ b/src/zen/sessionstore/ZenSessionWindow.sys.mjs @@ -0,0 +1,35 @@ +// 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/. + +export class ZenSessionWindow { + #id; + #selectedWorkspace; + #selectedTab; + + constructor(id) { + this.#id = id; + this.#selectedWorkspace = null; + this.#selectedTab = null; + } + + get id() { + return this.#id; + } + + get selectedWorkspace() { + return this.#selectedWorkspace; + } + + set selectedWorkspace(workspace) { + this.#selectedWorkspace = workspace; + } + + get selectedTab() { + return this.#selectedTab; + } + + set selectedTab(tab) { + this.#selectedTab = tab; + } +}