diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml
index a61105b51..4d9cf90b3 100644
--- a/src/browser/base/content/zen-assets.inc.xhtml
+++ b/src/browser/base/content/zen-assets.inc.xhtml
@@ -57,4 +57,3 @@
-
diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn
index efee1ce48..d42187d8a 100644
--- a/src/browser/base/content/zen-assets.jar.inc.mn
+++ b/src/browser/base/content/zen-assets.jar.inc.mn
@@ -41,7 +41,6 @@
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/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();
-}