diff --git a/src/zen/sessionstore/SessionComponents.manifest b/src/zen/sessionstore/SessionComponents.manifest index f8f08d1d7..9da9dc105 100644 --- a/src/zen/sessionstore/SessionComponents.manifest +++ b/src/zen/sessionstore/SessionComponents.manifest @@ -4,3 +4,8 @@ # Browser global components initializing before UI startup category browser-before-ui-startup resource:///modules/zen/ZenSessionManager.sys.mjs ZenSessionStore.init +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 diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 2a229daca..06478075e 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -25,7 +25,15 @@ class nsZenSessionManager { // Called from SessionComponents.manifest on app-startup init() { - this.#initObservers(); + for (let topic of OBSERVING) { + Services.obs.addObserver(this, topic); + } + } + + uninit() { + for (let topic of OBSERVING) { + Services.obs.removeObserver(this, topic); + } } async readFile() { @@ -38,12 +46,6 @@ class nsZenSessionManager { } } - #initObservers() { - for (let topic of OBSERVING) { - Services.obs.addObserver(this, topic); - } - } - get #sidebar() { return this.#file.sidebar; } diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs new file mode 100644 index 000000000..a96e09687 --- /dev/null +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -0,0 +1,23 @@ +// 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 OBSERVING = ['browser-window-before-show']; + +class nsZenWindowSync { + constructor() {} + + init() { + for (let topic of OBSERVING) { + Services.obs.addObserver(this, topic); + } + } + + uninit() { + for (let topic of OBSERVING) { + Services.obs.removeObserver(this, topic); + } + } +} + +export const ZenWindowSync = new nsZenWindowSync(); diff --git a/src/zen/sessionstore/moz.build b/src/zen/sessionstore/moz.build index af5a7dd3b..902ae2a30 100644 --- a/src/zen/sessionstore/moz.build +++ b/src/zen/sessionstore/moz.build @@ -5,4 +5,5 @@ EXTRA_JS_MODULES.zen += [ "ZenSessionFile.sys.mjs", "ZenSessionManager.sys.mjs", + "ZenWindowSync.sys.mjs", ] diff --git a/src/zen/workspaces/ZenWindowSyncing.mjs b/src/zen/workspaces/ZenWindowSyncing.mjs deleted file mode 100644 index 4c06ae0e0..000000000 --- a/src/zen/workspaces/ZenWindowSyncing.mjs +++ /dev/null @@ -1,308 +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/. -{ - 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') && !tab.hasAttribute('zen-empty-tab')) { - const tabId = gZenUIManager.generateUuidv4(); - tab.setAttribute('zen-sync-id', tabId); - } - } - } - - #setUpEventListeners() { - const kEvents = [ - 'TabClose', - 'TabOpen', - 'TabMove', - - 'TabPinned', - 'TabUnpinned', - - 'TabAddedToEssentials', - 'TabRemovedFromEssentials', - - 'TabHide', - 'TabShow', - - 'ZenTabIconChanged', - 'ZenTabLabelChanged', - - 'TabGroupCreate', - 'TabGroupRemoved', - 'TabGrouped', - 'TabUngrouped', - 'TabGroupMoved', - ]; - 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((resolve) => { - this.foreachWindowAsActive(async (browser) => { - if (browser.gZenWorkspaceWindowSync && !this.windowIsActive(browser)) { - await browser.gZenWorkspaceWindowSync.onExternalTabEvent(event); - } - }).then(() => { - 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': - case 'TabGroupMoved': - this.#onTabMove(event); - break; - case 'ZenTabIconChanged': - this.#onTabIconChanged(event); - break; - 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; - } - 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); - } - } - - #onTabIconChanged(event) { - this.#updateTabIconAndLabel(event); - } - - #onTabLabelChanged(event) { - this.#updateTabIconAndLabel(event); - } - - #updateTabIconAndLabel(event) { - const targetTab = event.target; - 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); - } - } - - #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 tabToMove = this.#getTabWithId(tabId); - const workspaceId = targetTab.getAttribute('zen-workspace-id'); - const isEssential = targetTab.hasAttribute('zen-essential'); - if (tabToMove) { - 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); - } - } - }); - } - } - - async #onTabOpen(event) { - const targetTab = event.target; - const isPinned = targetTab.pinned; - const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); - 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, - }); - if (!isEssential) { - gZenWorkspaces.moveTabToWorkspace( - duplicatedTab, - targetTab.getAttribute('zen-workspace-id') - ); - } - duplicatedTab.setAttribute('zen-sync-id', targetTab.getAttribute('zen-sync-id')); - } - - #onTabGroupCreate(event) { - void event; - //const targetGroup = event.target; - //const isSplitView = targetGroup.classList.contains('zen-split-view'); - //const isFolder = targetGroup.isZenFolder; - } - } - - window.gZenWorkspaceWindowSync = new nsZenWorkspaceWindowSync(); -}