From 2ea7024eea2f19aac4e230a6a370a2f16a414348 Mon Sep 17 00:00:00 2001 From: mauro-balades Date: Tue, 1 Oct 2024 23:21:38 +0200 Subject: [PATCH] Synced workspaces across windows and also implemented `MultiWindowFeatures` --- src/ZenCommonUtils.mjs | 42 ++++ src/ZenKeyboardShortcuts.mjs | 2 +- src/ZenThemesCommon.mjs | 17 +- src/ZenThemesImporter.mjs | 2 +- src/ZenWorkspaces.mjs | 454 +++++++++++++++++------------------ 5 files changed, 273 insertions(+), 244 deletions(-) create mode 100644 src/ZenCommonUtils.mjs diff --git a/src/ZenCommonUtils.mjs b/src/ZenCommonUtils.mjs new file mode 100644 index 0000000..484a3db --- /dev/null +++ b/src/ZenCommonUtils.mjs @@ -0,0 +1,42 @@ + +var gZenOperatingSystemCommonUtils = { + kZenOSToSmallName: { + WINNT: 'windows', + Darwin: 'macos', + Linux: 'linux', + }, + + get currentOperatingSystem() { + let os = Services.appinfo.OS; + return this.kZenOSToSmallName[os]; + }, +}; + +class ZenMultiWindowFeature { + constructor() {} + + static get browsers() { + return Services.wm.getEnumerator('navigator:browser'); + } + + static get currentBrowser() { + return Services.wm.getMostRecentWindow('navigator:browser'); + } + + isActiveWindow() { + return ZenMultiWindowFeature.currentBrowser === window; + } + + async foreachWindowAsActive(callback) { + if (!this.isActiveWindow()) { + return; + } + for (const browser of ZenMultiWindowFeature.browsers) { + try { + await callback(browser); + } catch (e) { + console.error(e); + } + } + } +} diff --git a/src/ZenKeyboardShortcuts.mjs b/src/ZenKeyboardShortcuts.mjs index 19a94b5..efabf72 100644 --- a/src/ZenKeyboardShortcuts.mjs +++ b/src/ZenKeyboardShortcuts.mjs @@ -788,7 +788,7 @@ var gZenKeyboardShortcutsManager = { }, _applyShortcuts() { - for (const browser of ZenThemesCommon.browsers) { + for (const browser of ZenMultiWindowFeature.browsers) { let mainKeyset = browser.document.getElementById('mainKeyset'); if (!mainKeyset) { throw new Error('Main keyset not found'); diff --git a/src/ZenThemesCommon.mjs b/src/ZenThemesCommon.mjs index b59ef92..487d0f3 100644 --- a/src/ZenThemesCommon.mjs +++ b/src/ZenThemesCommon.mjs @@ -1,17 +1,6 @@ var ZenThemesCommon = { - kZenOSToSmallName: { - WINNT: 'windows', - Darwin: 'macos', - Linux: 'linux', - }, - kZenColors: ['#aac7ff', '#74d7cb', '#a0d490', '#dec663', '#ffb787', '#dec1b1', '#ffb1c0', '#ddbfc3', '#f6b0ea', '#d4bbff'], - get currentOperatingSystem() { - let os = Services.appinfo.OS; - return this.kZenOSToSmallName[os]; - }, - get browsers() { return Services.wm.getEnumerator('navigator:browser'); }, @@ -63,8 +52,8 @@ var ZenThemesCommon = { const isNegation = negation === '!'; if ( - (isNegation && os === this.currentOperatingSystem) || - (os !== '' && os !== this.currentOperatingSystem && !isNegation) + (isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) || + (os !== '' && os !== gZenOperatingSystemCommonUtils.currentOperatingSystem && !isNegation) ) { continue; } @@ -80,7 +69,7 @@ var ZenThemesCommon = { return newThemePreferences; } - return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem)); + return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)); }, throttle(mainFunction, delay) { diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index d62fcd2..c14c2f9 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -177,7 +177,7 @@ var gZenThemeImporter = new (class { } writeToDom(themesWithPreferences) { - for (const browser of ZenThemesCommon.browsers) { + for (const browser of ZenMultiWindowFeature.browsers) { for (const { enabled, preferences, name } of themesWithPreferences) { const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`; diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index f507163..e43699f 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -1,9 +1,9 @@ -var ZenWorkspaces = { +var ZenWorkspaces = new class extends ZenMultiWindowFeature { /** * Stores workspace IDs and their last selected tabs. */ - _lastSelectedWorkspaceTabs: {}, + _lastSelectedWorkspaceTabs = {} async init() { if (!this.shouldHaveWorkspaces) { @@ -11,30 +11,36 @@ var ZenWorkspaces = { return; // We are in a hidden window, don't initialize ZenWorkspaces } console.info('ZenWorkspaces: Initializing ZenWorkspaces...'); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "shouldShowIconStrip", + "zen.workspaces.show-icon-strip", + true, + this._expandWorkspacesStrip.bind(this) + ); + ChromeUtils.defineLazyGetter(this, 'tabContainer', () => document.getElementById('tabbrowser-tabs')); await this.initializeWorkspaces(); console.info('ZenWorkspaces: ZenWorkspaces initialized'); - }, - - get shouldShowIconStrip() { - delete this.shouldShowIconStrip; - this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true); - return this.shouldShowIconStrip; - }, + } get shouldHaveWorkspaces() { - delete this.shouldHaveWorkspaces; - let docElement = document.documentElement; - this.shouldHaveWorkspaces = !(docElement.hasAttribute('privatebrowsingmode') - || docElement.getAttribute('chromehidden').includes('toolbar') - || docElement.getAttribute('chromehidden').includes('menubar')); - return this.shouldHaveWorkspaces; - }, + if (typeof this._shouldHaveWorkspaces === 'undefined') { + let docElement = document.documentElement; + this._shouldHaveWorkspaces = !(docElement.hasAttribute('privatebrowsingmode') + || docElement.getAttribute('chromehidden').includes('toolbar') + || docElement.getAttribute('chromehidden').includes('menubar')); + return this._shouldHaveWorkspaces; + } + return this._shouldHaveWorkspaces; + } get workspaceEnabled() { - delete this.workspaceEnabled; - this.workspaceEnabled = Services.prefs.getBoolPref('zen.workspaces.enabled', false) && this.shouldHaveWorkspaces; - return this.workspaceEnabled; - }, + if (typeof this._workspaceEnabled === 'undefined') { + this._workspaceEnabled = Services.prefs.getBoolPref('zen.workspaces.enabled', false) && this.shouldHaveWorkspaces; + return this._workspaceEnabled; + } + return this._workspaceEnabled; + } getActiveWorkspaceFromCache() { try { @@ -42,12 +48,12 @@ var ZenWorkspaces = { } catch (e) { return null; } - }, + } // Wrorkspaces saving/loading get _storeFile() { return PathUtils.join(PathUtils.profileDir, 'zen-workspaces', 'Workspaces.json'); - }, + } async _workspaces() { if (!this._workspaceCache) { @@ -57,7 +63,7 @@ var ZenWorkspaces = { } } return this._workspaceCache; - }, + } async onWorkspacesEnabledChanged() { if (this.workspaceEnabled) { @@ -69,16 +75,10 @@ var ZenWorkspaces = { gBrowser.showTab(tab); } } - }, - - async onWorkspacesIconStripChanged() { - this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true); - await this._expandWorkspacesStrip(); - }, + } async initializeWorkspaces() { Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this)); - Services.prefs.addObserver('zen.workspaces.show-icon-strip', this.onWorkspacesIconStripChanged.bind(this)); let file = new FileUtils.File(this._storeFile); if (!file.exists()) { await IOUtils.writeJSON(this._storeFile, {}); @@ -108,7 +108,7 @@ var ZenWorkspaces = { this.changeWorkspace(activeWorkspace, true); } } - }, + } handleTabClose(event) { if (this.__contextIsDelete) { @@ -125,11 +125,11 @@ var ZenWorkspaces = { this.changeWorkspace({ uuid: workspaceID }, true); } } - }, + } - _kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => ( + _kIcons = JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => ( (typeof Intl.Segmenter !== 'undefined') ? new Intl.Segmenter().segment(icon).containing().segment : Array.from(icon)[0] - )), + )) _initializeWorkspaceCreationIcons() { let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container'); @@ -148,7 +148,7 @@ var ZenWorkspaces = { }).bind(this, button); container.appendChild(button); } - }, + } _initializeWorkspaceEditIcons() { let container = this._workspaceEditIconsContainer; @@ -168,7 +168,7 @@ var ZenWorkspaces = { }).bind(this, button); container.appendChild(button); } - }, + } async saveWorkspace(workspaceData) { let json = await IOUtils.readJSON(this._storeFile); @@ -186,7 +186,7 @@ var ZenWorkspaces = { this._workspaceCache = null; await this._updateWorkspacesChangeContextMenu(); - }, + } async removeWorkspace(windowID) { let json = await this._workspaces(); @@ -198,24 +198,24 @@ var ZenWorkspaces = { await this.unsafeSaveWorkspaces(json); await this._propagateWorkspaceData(); await this._updateWorkspacesChangeContextMenu(); - }, + } async saveWorkspaces() { await IOUtils.writeJSON(this._storeFile, await this._workspaces()); this._workspaceCache = null; - }, + } async unsafeSaveWorkspaces(workspaces) { await IOUtils.writeJSON(this._storeFile, workspaces); this._workspaceCache = workspaces; - }, + } // Workspaces dialog UI management openSaveDialog() { let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview'); PanelUI.showSubView('PanelUI-zen-workspaces-create', parentPanel); - }, + } async openEditDialog(workspaceUuid) { this._workspaceEditDialog.setAttribute('data-workspace-uuid', workspaceUuid); @@ -235,16 +235,16 @@ var ZenWorkspaces = { }); let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview'); PanelUI.showSubView('PanelUI-zen-workspaces-edit', parentPanel); - }, + } closeWorkspacesSubView() { let parentPanel = document.getElementById('PanelUI-zen-workspaces-multiview'); parentPanel.goBack(); - }, + } workspaceHasIcon(workspace) { return typeof workspace.icon !== 'undefined' && workspace.icon !== ''; - }, + } getWorkspaceIcon(workspace) { if (this.workspaceHasIcon(workspace)) { @@ -254,109 +254,112 @@ var ZenWorkspaces = { return new Intl.Segmenter().segment(workspace.name).containing().segment.toUpperCase(); } return Array.from(workspace.name)[0].toUpperCase(); - }, + } get shouldShowContainers() { return Services.prefs.getBoolPref('privacy.userContext.ui.enabled') && ContextualIdentityService.getPublicIdentities().length > 0; - }, + } async _propagateWorkspaceData({ ignoreStrip = false } = {}) { - let currentContainer = document.getElementById('PanelUI-zen-workspaces-current-info'); - let workspaceList = document.getElementById('PanelUI-zen-workspaces-list'); - if (!ignoreStrip) { - await this._expandWorkspacesStrip(); - } - const createWorkspaceElement = (workspace) => { - let element = document.createXULElement('toolbarbutton'); - element.className = 'subviewbutton'; - element.setAttribute('tooltiptext', workspace.name); - element.setAttribute('zen-workspace-id', workspace.uuid); - if (workspace.used) { - element.setAttribute('active', 'true'); - } - if (workspace.default) { - element.setAttribute('default', 'true'); - } - const containerGroup = ContextualIdentityService.getPublicIdentities().find( - (container) => container.userContextId === workspace.containerTabId - ); - if (containerGroup) { - element.classList.add('identity-color-' + containerGroup.color); - element.setAttribute('data-usercontextid', containerGroup.userContextId); - } - let childs = window.MozXULElement.parseXULToFragment(` -
-
- -
-
-
-
-
- - - - `); - - // use text content instead of innerHTML to avoid XSS - childs.querySelector('.zen-workspace-icon').textContent = this.getWorkspaceIcon(workspace); - childs.querySelector('.zen-workspace-name').textContent = workspace.name; - if (containerGroup) { - childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel( - containerGroup.userContextId - ); - } - - childs.querySelector('.zen-workspace-actions').addEventListener('command', (event) => { - let button = event.target; - this._contextMenuId = button.closest('toolbarbutton[zen-workspace-id]').getAttribute('zen-workspace-id'); - const popup = button.ownerDocument.getElementById('zenWorkspaceActionsMenu'); - popup.openPopup(button, 'after_end'); - }); - element.appendChild(childs); - element.onclick = (async () => { - if (event.target.closest('.zen-workspace-actions')) { - return; // Ignore clicks on the actions button + await this.foreachWindowAsActive(async (browser) => { + let currentContainer = browser.document.getElementById('PanelUI-zen-workspaces-current-info'); + let workspaceList = browser.document.getElementById('PanelUI-zen-workspaces-list'); + const createWorkspaceElement = (workspace) => { + let element = browser.document.createXULElement('toolbarbutton'); + element.className = 'subviewbutton'; + element.setAttribute('tooltiptext', workspace.name); + element.setAttribute('zen-workspace-id', workspace.uuid); + if (workspace.used) { + element.setAttribute('active', 'true'); } - await this.changeWorkspace(workspace); - let panel = document.getElementById('PanelUI-zen-workspaces'); - PanelMultiView.hidePopup(panel); - document.getElementById('zen-workspaces-button').removeAttribute('open'); - }).bind(this, workspace); - return element; - }; - let workspaces = await this._workspaces(); - let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used); - currentContainer.innerHTML = ''; - workspaceList.innerHTML = ''; - workspaceList.parentNode.style.display = 'flex'; - if (workspaces.workspaces.length - 1 <= 0) { - workspaceList.innerHTML = 'No workspaces available'; - workspaceList.setAttribute('empty', 'true'); - } else { - workspaceList.removeAttribute('empty'); - } - if (activeWorkspace) { - let currentWorkspace = createWorkspaceElement(activeWorkspace); - currentContainer.appendChild(currentWorkspace); - } - for (let workspace of workspaces.workspaces) { - if (workspace.used) { - continue; + if (workspace.default) { + element.setAttribute('default', 'true'); + } + const containerGroup = browser.ContextualIdentityService.getPublicIdentities().find( + (container) => container.userContextId === workspace.containerTabId + ); + if (containerGroup) { + element.classList.add('identity-color-' + containerGroup.color); + element.setAttribute('data-usercontextid', containerGroup.userContextId); + } + let childs = browser.MozXULElement.parseXULToFragment(` +
+
+ +
+
+
+
+
+ + + + `); + + // use text content instead of innerHTML to avoid XSS + childs.querySelector('.zen-workspace-icon').textContent = browser.ZenWorkspaces.getWorkspaceIcon(workspace); + childs.querySelector('.zen-workspace-name').textContent = workspace.name; + if (containerGroup) { + childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel( + containerGroup.userContextId + ); + } + + childs.querySelector('.zen-workspace-actions').addEventListener('command', (event) => { + let button = event.target; + browser.ZenWorkspaces._contextMenuId = button.closest('toolbarbutton[zen-workspace-id]').getAttribute('zen-workspace-id'); + const popup = button.ownerDocument.getElementById('zenWorkspaceActionsMenu'); + popup.openPopup(button, 'after_end'); + }); + element.appendChild(childs); + element.onclick = (async () => { + if (event.target.closest('.zen-workspace-actions')) { + return; // Ignore clicks on the actions button + } + await browser.ZenWorkspaces.changeWorkspace(workspace); + let panel = browser.document.getElementById('PanelUI-zen-workspaces'); + PanelMultiView.hidePopup(panel); + browser.document.getElementById('zen-workspaces-button').removeAttribute('open'); + }).bind(browser.ZenWorkspaces, workspace, browser); + return element; + }; + browser.ZenWorkspaces._workspaceCache = null; + let workspaces = await browser.ZenWorkspaces._workspaces(); + let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used); + currentContainer.innerHTML = ''; + workspaceList.innerHTML = ''; + workspaceList.parentNode.style.display = 'flex'; + if (workspaces.workspaces.length - 1 <= 0) { + workspaceList.innerHTML = 'No workspaces available'; + workspaceList.setAttribute('empty', 'true'); + } else { + workspaceList.removeAttribute('empty'); } - let workspaceElement = createWorkspaceElement(workspace); - workspaceList.appendChild(workspaceElement); - } - }, + if (activeWorkspace) { + let currentWorkspace = createWorkspaceElement(activeWorkspace); + currentContainer.appendChild(currentWorkspace); + } + for (let workspace of workspaces.workspaces) { + if (workspace.used) { + continue; + } + let workspaceElement = createWorkspaceElement(workspace); + workspaceList.appendChild(workspaceElement); + } + if (!ignoreStrip) { + await browser.ZenWorkspaces._expandWorkspacesStrip(browser); + } + }); + } async openWorkspacesDialog(event) { if (!this.workspaceEnabled) { return; } - let target = event.target; + let target = document.getElementById('zen-workspaces-button'); let panel = document.getElementById('PanelUI-zen-workspaces'); await this._propagateWorkspaceData({ ignoreStrip: true @@ -365,7 +368,7 @@ var ZenWorkspaces = { position: 'bottomright topright', triggerEvent: event, }).catch(console.error); - }, + } async initializeWorkspacesButton() { if (!this.workspaceEnabled) { @@ -376,12 +379,12 @@ var ZenWorkspaces = { return; } await this._expandWorkspacesStrip(); - }, + } - async _expandWorkspacesStrip() { - let workspaces = await this._workspaces(); - let workspaceList = document.getElementById('zen-workspaces-button'); - const newWorkspacesButton = document.createXULElement('toolbarbutton'); + async _expandWorkspacesStrip(browser = window) { + let workspaces = await browser.ZenWorkspaces._workspaces(); + let workspaceList = browser.document.getElementById('zen-workspaces-button'); + const newWorkspacesButton = browser.document.createXULElement('toolbarbutton'); newWorkspacesButton.id = 'zen-workspaces-button'; newWorkspacesButton.setAttribute('removable', 'true'); newWorkspacesButton.setAttribute('showInPrivateBrowsing', 'false'); @@ -389,7 +392,7 @@ var ZenWorkspaces = { if (this.shouldShowIconStrip) { for (let workspace of workspaces.workspaces) { - let button = document.createXULElement('toolbarbutton'); + let button = browser.document.createXULElement('toolbarbutton'); button.className = 'subviewbutton'; button.setAttribute('tooltiptext', workspace.name); button.setAttribute('zen-workspace-id', workspace.uuid); @@ -405,50 +408,50 @@ var ZenWorkspaces = { return; } await this.changeWorkspace(workspace); - }).bind(this, workspace); - let icon = document.createXULElement('div'); + }).bind(browser.ZenWorkspaces, workspace); + let icon = browser.document.createXULElement('div'); icon.className = 'zen-workspace-icon'; icon.textContent = this.getWorkspaceIcon(workspace); button.appendChild(icon); newWorkspacesButton.appendChild(button); } // Listen for context menu events and open the all workspaces dialog - newWorkspacesButton.addEventListener('contextmenu', (event) => { + newWorkspacesButton.addEventListener('contextmenu', ((event) => { event.preventDefault(); - this.openWorkspacesDialog(event); - }); + browser.ZenWorkspaces.openWorkspacesDialog(event); + }).bind(this)); } workspaceList.after(newWorkspacesButton); workspaceList.remove(); if (!this.shouldShowIconStrip) { - await this._updateWorkspacesButton(); + await this._updateWorkspacesButton(browser); } - }, + } - async _updateWorkspacesButton() { - let button = document.getElementById('zen-workspaces-button'); + async _updateWorkspacesButton(browser = window) { + let button = browser.document.getElementById('zen-workspaces-button'); if (!button) { return; } - let activeWorkspace = (await this._workspaces()).workspaces.find((workspace) => workspace.used); + let activeWorkspace = (await browser.ZenWorkspaces._workspaces()).workspaces.find((workspace) => workspace.used); if (activeWorkspace) { button.setAttribute('as-button', 'true'); button.classList.add('toolbarbutton-1', 'zen-sidebar-action-button'); - button.addEventListener('click', this.openWorkspacesDialog.bind(this)); + button.addEventListener('click', browser.ZenWorkspaces.openWorkspacesDialog.bind(browser.ZenWorkspaces)); - const wrapper = document.createXULElement('hbox'); + const wrapper = browser.document.createXULElement('hbox'); wrapper.className = 'zen-workspace-sidebar-wrapper'; - const icon = document.createElement('div'); + const icon = browser.document.createElement('div'); icon.className = 'zen-workspace-sidebar-icon'; icon.textContent = this.getWorkspaceIcon(activeWorkspace); // use text content instead of innerHTML to avoid XSS - const name = document.createElement('div'); + const name = browser.document.createElement('div'); name.className = 'zen-workspace-sidebar-name'; name.textContent = activeWorkspace.name; @@ -462,25 +465,25 @@ var ZenWorkspaces = { button.innerHTML = ''; button.appendChild(wrapper); } - }, + } // Workspaces management get _workspaceCreateInput() { return document.getElementById('PanelUI-zen-workspaces-create-input'); - }, + } get _workspaceEditDialog() { return document.getElementById('PanelUI-zen-workspaces-edit'); - }, + } get _workspaceEditInput() { return document.getElementById('PanelUI-zen-workspaces-edit-input'); - }, + } get _workspaceEditIconsContainer() { return document.getElementById('PanelUI-zen-workspaces-edit-icons-container'); - }, + } _deleteAllTabsInWorkspace(workspaceID) { for (let tab of gBrowser.tabs) { @@ -492,7 +495,7 @@ var ZenWorkspaces = { }); } } - }, + } _prepareNewWorkspace(window) { document.documentElement.setAttribute('zen-workspace-id', window.uuid); @@ -506,12 +509,12 @@ var ZenWorkspaces = { if (tabCount === 0) { this._createNewTabForWorkspace(window); } - }, + } _createNewTabForWorkspace(window) { let tab = gZenUIManager.openAndChangeToTab(Services.prefs.getStringPref('browser.startup.homepage')); tab.setAttribute('zen-workspace-id', window.uuid); - }, + } async saveWorkspaceFromCreate() { let workspaceName = this._workspaceCreateInput.value; @@ -525,7 +528,7 @@ var ZenWorkspaces = { document.getElementById('PanelUI-zen-workspaces').hidePopup(true); await this._updateWorkspacesButton(); await this._propagateWorkspaceData(); - }, + } async saveWorkspaceFromEdit() { let workspaceUuid = this._workspaceEditDialog.getAttribute('data-workspace-uuid'); @@ -541,10 +544,9 @@ var ZenWorkspaces = { workspaceData.name = workspaceName; workspaceData.icon = icon?.label; await this.saveWorkspace(workspaceData); - await this._updateWorkspacesButton(); await this._propagateWorkspaceData(); this.closeWorkspacesSubView(); - }, + } onWorkspaceCreationNameChange(event) { let button = document.getElementById('PanelUI-zen-workspaces-create-save'); @@ -553,7 +555,7 @@ var ZenWorkspaces = { return; } button.removeAttribute('disabled'); - }, + } onWorkspaceEditChange() { let button = document.getElementById('PanelUI-zen-workspaces-edit-save'); @@ -567,68 +569,64 @@ var ZenWorkspaces = { return; } button.removeAttribute('disabled'); - }, + } get _shouldAllowPinTab() { return Services.prefs.getBoolPref('zen.workspaces.individual-pinned-tabs'); - }, - - get tabContainer() { - delete this.tabContainer; - return (this.tabContainer = document.getElementById("tabbrowser-tabs")); - }, + } async changeWorkspace(window, onInit = false) { if (!this.workspaceEnabled) { return; } - this.tabContainer._invalidateCachedTabs(); - const shouldAllowPinnedTabs = this._shouldAllowPinTab; - let firstTab = undefined; let workspaces = await this._workspaces(); for (let workspace of workspaces.workspaces) { workspace.used = workspace.uuid === window.uuid; } await this.unsafeSaveWorkspaces(workspaces); - console.info('ZenWorkspaces: Changing workspace to', window.uuid); - for (let tab of gBrowser.tabs) { - if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) { - if (!firstTab) { - firstTab = tab; - } else if (gBrowser.selectedTab === tab) { - // If the selected tab is already in the workspace, we don't want to change it - firstTab = null; // note: Do not add "undefined" here, a new tab would be created - } - gBrowser.showTab(tab); - if (!tab.hasAttribute('zen-workspace-id')) { - // We add the id to those tabs that got inserted before we initialize the workspaces - // example use case: opening a link from an external app - tab.setAttribute('zen-workspace-id', window.uuid); + const shouldAllowPinnedTabs = this._shouldAllowPinTab; + await this.foreachWindowAsActive(async (browser) => { + browser.ZenWorkspaces.tabContainer._invalidateCachedTabs(); + let firstTab = undefined; + console.info('ZenWorkspaces: Changing workspace to', window.uuid); + for (let tab of browser.gBrowser.tabs) { + if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) { + if (!firstTab) { + firstTab = tab; + } else if (browser.gBrowser.selectedTab === tab) { + // If the selected tab is already in the workspace, we don't want to change it + firstTab = null; // note: Do not add "undefined" here, a new tab would be created + } + browser.gBrowser.showTab(tab); + if (!tab.hasAttribute('zen-workspace-id')) { + // We add the id to those tabs that got inserted before we initialize the workspaces + // example use case: opening a link from an external app + tab.setAttribute('zen-workspace-id', window.uuid); + } } } - } - if (firstTab) { - gBrowser.selectedTab = this._lastSelectedWorkspaceTabs[window.uuid] ?? firstTab; - } - if (typeof firstTab === 'undefined' && !onInit) { - this._createNewTabForWorkspace(window); - } - for (let tab of gBrowser.tabs) { - if (tab.getAttribute('zen-workspace-id') !== window.uuid) { - // FOR UNLOADING TABS: - // gBrowser.discardBrowser(tab, true); - gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs); + if (firstTab) { + browser.gBrowser.selectedTab = browser.ZenWorkspaces._lastSelectedWorkspaceTabs[window.uuid] ?? firstTab; } - } - this.tabContainer._invalidateCachedTabs(); - document.documentElement.setAttribute('zen-workspace-id', window.uuid); - await this.saveWorkspaces(); - await this._updateWorkspacesButton(); - await this._propagateWorkspaceData(); - await this._updateWorkspacesChangeContextMenu(); + if (typeof firstTab === 'undefined' && !onInit) { + browser.ZenWorkspaces._createNewTabForWorkspace(window); + } + for (let tab of browser.gBrowser.tabs) { + if (tab.getAttribute('zen-workspace-id') !== window.uuid) { + // FOR UNLOADING TABS: + // gBrowser.discardBrowser(tab, true); + browser.gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs); + } + } + browser.ZenWorkspaces.tabContainer._invalidateCachedTabs(); + browser.document.documentElement.setAttribute('zen-workspace-id', window.uuid); + await browser.ZenWorkspaces._updateWorkspacesChangeContextMenu(); - document.getElementById('tabbrowser-tabs')._positionPinnedTabs(); - }, + browser.document.getElementById('tabbrowser-tabs')._positionPinnedTabs(); + }); + await this.saveWorkspaces(); + await this._propagateWorkspaceData(); + } async _updateWorkspacesChangeContextMenu() { const workspaces = await this._workspaces(); @@ -650,7 +648,7 @@ var ZenWorkspaces = { menuPopup.appendChild(menuItem); } - }, + } _createWorkspaceData(name, isDefault, icon) { let window = { @@ -662,7 +660,7 @@ var ZenWorkspaces = { }; this._prepareNewWorkspace(window); return window; - }, + } async createAndSaveWorkspace(name = 'New Workspace', isDefault = false, icon = undefined) { if (!this.workspaceEnabled) { @@ -671,7 +669,7 @@ var ZenWorkspaces = { let workspaceData = this._createWorkspaceData(name, isDefault, icon); await this.saveWorkspace(workspaceData); await this.changeWorkspace(workspaceData); - }, + } async onTabBrowserInserted(event) { let tab = event.originalTarget; @@ -684,7 +682,7 @@ var ZenWorkspaces = { return; } tab.setAttribute('zen-workspace-id', activeWorkspace.uuid); - }, + } async onLocationChange(browser) { let tab = gBrowser.getTabForBrowser(browser); @@ -699,11 +697,11 @@ var ZenWorkspaces = { workspaceID = activeWorkspace.uuid; } this._lastSelectedWorkspaceTabs[workspaceID] = tab; - }, + } // Context menu management - _contextMenuId: null, + _contextMenuId = null async updateContextMenu(_) { console.assert(this._contextMenuId, 'No context menu ID set'); document @@ -737,7 +735,7 @@ var ZenWorkspaces = { } else { openInContainerMenuItem.setAttribute('hidden', 'true'); } - }, + } async contextChangeContainerTab(event) { let workspaces = await this._workspaces(); @@ -746,7 +744,7 @@ var ZenWorkspaces = { workspace.containerTabId = userContextId; await this.saveWorkspace(workspace); await this._propagateWorkspaceData(); - }, + } onContextMenuClose() { let target = document.querySelector( @@ -756,7 +754,7 @@ var ZenWorkspaces = { target.removeAttribute('active'); } this._contextMenuId = null; - }, + } async setDefaultWorkspace() { let workspaces = await this._workspaces(); @@ -765,25 +763,25 @@ var ZenWorkspaces = { } await this.unsafeSaveWorkspaces(workspaces); await this._propagateWorkspaceData(); - }, + } async openWorkspace() { let workspaces = await this._workspaces(); let workspace = workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId); await this.changeWorkspace(workspace); - }, + } async contextDelete(event) { this.__contextIsDelete = true; event.stopPropagation(); await this.removeWorkspace(this._contextMenuId); this.__contextIsDelete = false; - }, + } async contextEdit(event) { event.stopPropagation(); await this.openEditDialog(this._contextMenuId); - }, + } async changeWorkspaceShortcut(offset = 1) { // Cycle through workspaces @@ -793,7 +791,7 @@ var ZenWorkspaces = { // note: offset can be negative let nextWorkspace = workspaces.workspaces[(workspaceIndex + offset + workspaces.workspaces.length) % workspaces.workspaces.length]; await this.changeWorkspace(nextWorkspace); - }, + } _initializeWorkspaceTabContextMenus() { const menu = document.createXULElement('menu'); @@ -807,7 +805,7 @@ var ZenWorkspaces = { menu.appendChild(menuPopup); document.getElementById('context_closeDuplicateTabs').after(menu); - }, + } async changeTabWorkspace(workspaceID) { const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab]; @@ -822,7 +820,7 @@ var ZenWorkspaces = { } const workspaces = await this._workspaces(); await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID)); - }, + } // Tab browser utilities createContainerTabMenu(event) { @@ -834,7 +832,7 @@ var ZenWorkspaces = { excludeUserContextId: containerTabId, showDefaultTab: true, }); - }, + } getContextIdIfNeeded(userContextId) { const activeWorkspace = this.getActiveWorkspaceFromCache(); @@ -844,7 +842,7 @@ var ZenWorkspaces = { return [userContextId, false]; } return [activeWorkspaceUserContextId, true]; - }, + } async shortcutSwitchTo(index) { const workspaces = await this._workspaces();