From b074bf069086a5e95f4656f3637ad25db04e0748 Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Wed, 30 Apr 2025 18:15:15 +0200 Subject: [PATCH] fix: Fixed pinned tabs not restoring correctly, b=(no-bug), c=workspaces --- .../tabbrowser/content/tabbrowser-js.patch | 78 +++++++++++-------- src/zen/workspaces/ZenWorkspaces.mjs | 27 +++++-- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 1c8fc2219..a91f4a787 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66f0137ad2 100644 +index 6dece2b9d0462d90a28e75350ce983d87816ef73..a285c5cbc23eaeebc8248b950c0275645f8a5cd3 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -415,11 +415,58 @@ @@ -389,17 +389,23 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 ); tabsFragment.appendChild(tabGroup.node); } -@@ -3726,6 +3866,9 @@ +@@ -3723,8 +3863,15 @@ + // to remove the old selected tab. + if (tabToSelect) { + let leftoverTab = this.selectedTab; ++ if (tabToSelect.pinned) { ++ ZenWorkspaces._tabToRemoveForEmpty = leftoverTab; ++ } else { this.selectedTab = tabToSelect; this.removeTab(leftoverTab); - } ++ } ++ } + else { + this.selectedTab._possiblyEmpty = this.selectedTab.isEmpty; // Not needed, but just in case. -+ } + } if (tabs.length > 1 || !tabs[0].selected) { - this._updateTabsAfterInsert(); -@@ -3912,7 +4055,7 @@ +@@ -3912,7 +4059,7 @@ // Ensure we have an index if one was not provided. if (typeof index != "number") { // Move the new tab after another tab if needed, to the end otherwise. @@ -408,7 +414,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if ( !bulkOrderedOpen && ((openerTab && -@@ -3958,18 +4101,18 @@ +@@ -3958,18 +4105,18 @@ // Ensure index is within bounds. if (tab.pinned) { @@ -431,7 +437,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (this.isTab(itemAfter) && itemAfter.group == tabGroup) { // Place at the front of, or between tabs in, the same tab group this.tabContainer.insertBefore(tab, itemAfter); -@@ -4290,6 +4433,9 @@ +@@ -4290,6 +4437,9 @@ return; } @@ -441,7 +447,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 this.removeTabs(selectedTabs, { telemetrySource }); } -@@ -4542,6 +4688,7 @@ +@@ -4542,6 +4692,7 @@ telemetrySource, } = {} ) { @@ -449,7 +455,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 // When 'closeWindowWithLastTab' pref is enabled, closing all tabs // can be considered equivalent to closing the window. if ( -@@ -4626,6 +4773,7 @@ +@@ -4626,6 +4777,7 @@ if (lastToClose) { this.removeTab(lastToClose, aParams); } @@ -457,7 +463,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 } catch (e) { console.error(e); } -@@ -4650,6 +4798,7 @@ +@@ -4650,6 +4802,7 @@ telemetrySource, } = {} ) { @@ -465,7 +471,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (UserInteraction.running("browser.tabs.opening", window)) { UserInteraction.finish("browser.tabs.opening", window); } -@@ -4663,6 +4812,12 @@ +@@ -4663,6 +4816,12 @@ aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start(); } @@ -478,7 +484,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && aTab.closing) { -@@ -4677,7 +4832,9 @@ +@@ -4677,7 +4836,9 @@ // frame created for it (for example, by updating the visually selected // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; @@ -489,7 +495,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -4840,7 +4997,7 @@ +@@ -4840,7 +5001,7 @@ closeWindowWithLastTab != null ? closeWindowWithLastTab : !window.toolbar.visible || @@ -498,7 +504,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -4864,6 +5021,7 @@ +@@ -4864,6 +5025,7 @@ newTab = true; } @@ -506,7 +512,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -4903,9 +5061,7 @@ +@@ -4903,9 +5065,7 @@ aTab._mouseleave(); if (newTab) { @@ -517,7 +523,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 } else { TabBarVisibility.update(); } -@@ -5034,6 +5190,8 @@ +@@ -5034,6 +5194,8 @@ this.tabs[i]._tPos = i; } @@ -526,7 +532,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (!this._windowIsClosing) { if (wasPinned) { this.tabContainer._positionPinnedTabs(); -@@ -5159,8 +5317,8 @@ +@@ -5159,8 +5321,8 @@ return closedCount; } @@ -537,7 +543,15 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (unloadBlocked) { return; } -@@ -5260,13 +5418,13 @@ +@@ -5247,6 +5409,7 @@ + aExcludeTabs.push(FirefoxViewHandler.tab); + } + ++ aExcludeTabs.push(...ZenWorkspaces.getTabsToExclude(aTab)); + let excludeTabs = new Set(aExcludeTabs); + + // If this tab has a successor, it should be selectable, since +@@ -5260,13 +5423,13 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -553,7 +567,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 ); let tab = this.tabContainer.findNextTab(aTab, { -@@ -5282,7 +5440,7 @@ +@@ -5282,7 +5445,7 @@ } if (tab) { @@ -562,7 +576,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 } // If no qualifying visible tab was found, see if there is a tab in -@@ -5303,7 +5461,7 @@ +@@ -5303,7 +5466,7 @@ }); } @@ -571,7 +585,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 } _blurTab(aTab) { -@@ -5704,10 +5862,10 @@ +@@ -5704,10 +5867,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -584,7 +598,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6001,7 +6159,7 @@ +@@ -6001,7 +6164,7 @@ // Don't allow mixing pinned and unpinned tabs. if (this.isTab(element) && element.pinned) { @@ -593,7 +607,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -6028,9 +6186,16 @@ +@@ -6028,9 +6191,16 @@ element, () => { let neighbor = this.tabs[tabIndex]; @@ -611,7 +625,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (neighbor && this.isTab(element) && tabIndex > element._tPos) { neighbor.after(element); } else { -@@ -6099,7 +6264,9 @@ +@@ -6099,7 +6269,9 @@ targetElement = targetElement.group; } } @@ -622,7 +636,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 // Don't allow mixing pinned and unpinned tabs. if (element.pinned && !targetElement?.pinned) { targetElement = this.tabs[this.pinnedTabCount - 1]; -@@ -6109,7 +6276,13 @@ +@@ -6109,7 +6281,13 @@ moveBefore = true; } @@ -636,7 +650,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 if (element.pinned && this.tabContainer.verticalMode) { return this.tabContainer.verticalPinnedTabsContainer; } -@@ -6169,7 +6342,7 @@ +@@ -6169,7 +6347,7 @@ if (!this.isTab(aTab)) { throw new Error("Can only move a tab into a tab group"); } @@ -645,7 +659,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -6263,6 +6436,10 @@ +@@ -6263,6 +6441,10 @@ moveActionCallback(); @@ -656,7 +670,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7080,7 +7257,7 @@ +@@ -7080,7 +7262,7 @@ // preventDefault(). It will still raise the window if appropriate. break; } @@ -665,7 +679,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 window.focus(); aEvent.preventDefault(); break; -@@ -7981,6 +8158,7 @@ +@@ -7981,6 +8163,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -673,7 +687,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -8954,7 +9132,7 @@ var TabContextMenu = { +@@ -8954,7 +9137,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; @@ -682,7 +696,7 @@ index 6dece2b9d0462d90a28e75350ce983d87816ef73..e80730ed2db404c0d47f2e29c3235c66 // Move Tab items let contextMoveTabOptions = document.getElementById( "context_moveTabOptions" -@@ -9223,6 +9401,7 @@ var TabContextMenu = { +@@ -9223,6 +9406,7 @@ var TabContextMenu = { telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP, }); } else { diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index 7662489ec..ed09082e1 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -784,13 +784,10 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { } const currentTab = gBrowser.selectedTab; let showed = false; - if (currentTab.pinned) { + if (this._tabToRemoveForEmpty) { this.selectEmptyTab(); - try { - gZenTabUnloader.explicitUnloadTabs([currentTab]); - } catch (e) { - console.error('ZenWorkspaces: Error unloading tab', e); - } + gBrowser.removeTab(this._tabToRemoveForEmpty); + delete this._tabToRemoveForEmpty; showed = true; } else { const currentTabURL = currentTab.linkedBrowser?.currentURI?.spec; @@ -2660,7 +2657,12 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { return [userContextId, false, undefined]; } - if (this.shouldForceContainerTabsToWorkspace && typeof userContextId !== 'undefined' && this._workspaceCache?.workspaces) { + if ( + this.shouldForceContainerTabsToWorkspace && + typeof userContextId !== 'undefined' && + this._workspaceCache?.workspaces && + !fromExternal + ) { // Find all workspaces that match the given userContextId const matchingWorkspaces = this._workspaceCache.workspaces.filter( (workspace) => workspace.containerTabId === userContextId @@ -2684,6 +2686,17 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { return [activeWorkspaceUserContextId, true, undefined]; } + getTabsToExclude(aTab) { + const tabWorkspaceId = aTab.getAttribute('zen-workspace-id'); + // Return all tabs that are not on the same workspace + return this.allStoredTabs.filter( + (tab) => + tab.getAttribute('zen-workspace-id') !== tabWorkspaceId && + !tab.hasAttribute('zen-essential') && + !(this.containerSpecificEssentials && tab.getAttribute('container') !== aTab.getAttribute('container')) + ); + } + async shortcutSwitchTo(index) { const workspaces = await this._workspaces(); // The index may be out of bounds, if it doesnt exist, don't do anything