diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index e72703430..b3350b1d1 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 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a0291b4d8d35 100644 +index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..87fb109037d13183e76184201de600a92b6a07d6 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -422,15 +422,49 @@ @@ -307,18 +307,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 }); } -@@ -3570,7 +3659,9 @@ - let hiddenTabs = new Map(); - /** @type {Map} */ - let tabGroupWorkingData = new Map(); -- -+ if (this._hasAlreadyInitializedZenSessionStore) { -+ selectTab += 1; // SessionStoreInternal.restoreTabs expects a 1-based index. -+ } - for (const tabGroupData of tabGroupDataList) { - tabGroupWorkingData.set(tabGroupData.id, { - stateData: tabGroupData, -@@ -3613,7 +3704,7 @@ +@@ -3613,7 +3702,7 @@ // Add a new tab if needed. if (!tab) { let createLazyBrowser = @@ -327,7 +316,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 let url = "about:blank"; if (tabData.entries?.length) { -@@ -3651,7 +3742,8 @@ +@@ -3651,7 +3740,8 @@ skipLoad: true, preferredRemoteType, }); @@ -337,7 +326,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if (select) { tabToSelect = tab; } -@@ -3663,7 +3755,8 @@ +@@ -3663,7 +3753,8 @@ this.pinTab(tab); // Then ensure all the tab open/pinning information is sent. this._fireTabOpen(tab, {}); @@ -347,7 +336,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 let { groupId } = tabData; const tabGroup = tabGroupWorkingData.get(groupId); // if a tab refers to a tab group we don't know, skip any group -@@ -3677,7 +3770,10 @@ +@@ -3677,7 +3768,10 @@ tabGroup.stateData.id, tabGroup.stateData.color, tabGroup.stateData.collapsed, @@ -359,7 +348,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 ); tabsFragment.appendChild(tabGroup.node); } -@@ -3722,9 +3818,23 @@ +@@ -3722,9 +3816,23 @@ // to remove the old selected tab. if (tabToSelect) { let leftoverTab = this.selectedTab; @@ -369,21 +358,21 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 + } else { + gZenWorkspaces._tabToRemoveForEmpty = leftoverTab; + if (Services.prefs.getBoolPref("zen.workspaces.continue-where-left-off")) { -+ gZenWorkspaces._tabToSelect = selectTab - 1; ++ gZenWorkspaces._tabToSelect = selectTab - 2; // -1 for the offset, and another -1 for the empty tab. + } + if (gZenWorkspaces._initialTab && !gZenVerticalTabsManager._canReplaceNewTab) { + gZenWorkspaces._initialTab._shouldRemove = true; + } + } -+ } + } + else { + gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab; - } ++ } + this._hasAlreadyInitializedZenSessionStore = true; if (tabs.length > 1 || !tabs[0].selected) { this._updateTabsAfterInsert(); -@@ -3919,7 +4029,7 @@ +@@ -3919,7 +4027,7 @@ // Ensure we have an index if one was not provided. if (typeof elementIndex != "number" && typeof tabIndex != "number") { // Move the new tab after another tab if needed, to the end otherwise. @@ -392,7 +381,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if ( !bulkOrderedOpen && ((openerTab && -@@ -3942,7 +4052,7 @@ +@@ -3942,7 +4050,7 @@ ) { elementIndex = Infinity; } else if (previousTab.visible) { @@ -401,7 +390,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } else if (previousTab == FirefoxViewHandler.tab) { elementIndex = 0; } -@@ -3970,14 +4080,14 @@ +@@ -3970,14 +4078,14 @@ } // Ensure index is within bounds. if (tab.pinned) { @@ -420,7 +409,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // Prevent a flash of unstyled content by setting up the tab content // and inherited attributes before appending it (see Bug 1592054): -@@ -3985,7 +4095,7 @@ +@@ -3985,7 +4093,7 @@ this.tabContainer._invalidateCachedTabs(); @@ -429,7 +418,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 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); -@@ -4018,6 +4128,7 @@ +@@ -4018,6 +4126,7 @@ if (pinned) { this._updateTabBarForPinnedTabs(); } @@ -437,7 +426,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 TabBarVisibility.update(); } -@@ -4307,6 +4418,9 @@ +@@ -4307,6 +4416,9 @@ return; } @@ -447,7 +436,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 this.removeTabs(selectedTabs, { isUserTriggered, telemetrySource }); } -@@ -4568,6 +4682,7 @@ +@@ -4568,6 +4680,7 @@ telemetrySource, } = {} ) { @@ -455,7 +444,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // When 'closeWindowWithLastTab' pref is enabled, closing all tabs // can be considered equivalent to closing the window. if ( -@@ -4657,6 +4772,7 @@ +@@ -4657,6 +4770,7 @@ if (lastToClose) { this.removeTab(lastToClose, aParams); } @@ -463,7 +452,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } catch (e) { console.error(e); } -@@ -4695,6 +4811,12 @@ +@@ -4695,6 +4809,12 @@ aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start(); } @@ -476,7 +465,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && aTab.closing) { -@@ -4709,6 +4831,9 @@ +@@ -4709,6 +4829,9 @@ // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; let isLastTab = this.#isLastTabInWindow(aTab); @@ -486,7 +475,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -4891,7 +5016,7 @@ +@@ -4891,7 +5014,7 @@ closeWindowWithLastTab != null ? closeWindowWithLastTab : !window.toolbar.visible || @@ -495,7 +484,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -4915,6 +5040,7 @@ +@@ -4915,6 +5038,7 @@ newTab = true; } @@ -503,7 +492,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -4955,9 +5081,7 @@ +@@ -4955,9 +5079,7 @@ aTab._mouseleave(); if (newTab) { @@ -514,7 +503,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } else { TabBarVisibility.update(); } -@@ -5090,6 +5214,7 @@ +@@ -5090,6 +5212,7 @@ this.tabs[i]._tPos = i; } @@ -522,7 +511,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if (!this._windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -5302,6 +5427,7 @@ +@@ -5302,6 +5425,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -530,7 +519,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -5314,13 +5440,13 @@ +@@ -5314,13 +5438,13 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -546,7 +535,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 ); let tab = this.tabContainer.findNextTab(aTab, { -@@ -5336,7 +5462,7 @@ +@@ -5336,7 +5460,7 @@ } if (tab) { @@ -555,7 +544,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } // If no qualifying visible tab was found, see if there is a tab in -@@ -5357,7 +5483,7 @@ +@@ -5357,7 +5481,7 @@ }); } @@ -564,7 +553,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } _blurTab(aTab) { -@@ -5759,10 +5885,10 @@ +@@ -5759,10 +5883,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -577,7 +566,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -5952,7 +6078,7 @@ +@@ -5952,7 +6076,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -586,7 +575,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } /** -@@ -6029,7 +6155,7 @@ +@@ -6029,7 +6153,7 @@ // Don't allow mixing pinned and unpinned tabs. if (this.isTab(element) && element.pinned) { @@ -595,7 +584,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -6055,10 +6181,16 @@ +@@ -6055,10 +6179,16 @@ this.#handleTabMove( element, () => { @@ -614,7 +603,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if (neighbor && this.isTab(element) && tabIndex > element._tPos) { neighbor.after(element); } else { -@@ -6122,7 +6254,7 @@ +@@ -6122,7 +6252,7 @@ moveBefore = true; } } @@ -623,7 +612,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 element = element.group; if (targetElement?.group) { targetElement = targetElement.group; -@@ -6130,8 +6262,12 @@ +@@ -6130,8 +6260,12 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -637,7 +626,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 moveBefore = false; } else if (!element.pinned && targetElement && targetElement.pinned) { // If the caller asks to move an unpinned element next to a pinned -@@ -6145,7 +6281,7 @@ +@@ -6145,7 +6279,7 @@ // move the tab group right before the first unpinned tab. // 4. Moving a tab group and the first unpinned tab is grouped: // move the tab group right before the first unpinned tab's tab group. @@ -646,7 +635,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 if (targetElement.group) { targetElement = targetElement.group; } -@@ -6153,6 +6289,7 @@ +@@ -6153,6 +6287,7 @@ } let getContainer = () => @@ -654,7 +643,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -6210,7 +6347,7 @@ +@@ -6210,7 +6345,7 @@ if (!this.isTab(aTab)) { throw new Error("Can only move a tab into a tab group"); } @@ -663,7 +652,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -6304,6 +6441,10 @@ +@@ -6304,6 +6439,10 @@ moveActionCallback(); @@ -674,7 +663,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7198,7 +7339,7 @@ +@@ -7198,7 +7337,7 @@ // preventDefault(). It will still raise the window if appropriate. break; } @@ -683,7 +672,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 window.focus(); aEvent.preventDefault(); break; -@@ -8143,6 +8284,7 @@ +@@ -8143,6 +8282,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -691,7 +680,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -9108,7 +9250,7 @@ var TabContextMenu = { +@@ -9108,7 +9248,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; @@ -700,7 +689,7 @@ index 96fd8acdc09cc4c9649d1ed7503c2a0bde536613..e6b46996d96706d3641b6fc5c435a029 // Move Tab items let contextMoveTabOptions = document.getElementById( "context_moveTabOptions" -@@ -9384,6 +9526,7 @@ var TabContextMenu = { +@@ -9384,6 +9524,7 @@ var TabContextMenu = { ) ); } else { diff --git a/src/zen/common/styles/zen-browser-container.css b/src/zen/common/styles/zen-browser-container.css index 9f82586c6..dca854b3a 100644 --- a/src/zen/common/styles/zen-browser-container.css +++ b/src/zen/common/styles/zen-browser-container.css @@ -8,7 +8,7 @@ #tabbrowser-tabpanels[dragging-split='true'] { width: -moz-available; position: relative; - overflow: hidden; + overflow: clip; &.browserSidebarContainer { :root:not([zen-no-padding='true']) & { diff --git a/src/zen/common/zenThemeModifier.js b/src/zen/common/zenThemeModifier.js index a28c00218..52aec35dc 100644 --- a/src/zen/common/zenThemeModifier.js +++ b/src/zen/common/zenThemeModifier.js @@ -46,11 +46,15 @@ var ZenThemeModifier = { Services.prefs.addObserver(pref, handleEvent); } - window.addEventListener('unload', () => { - for (let pref of kZenThemePrefsList) { - Services.prefs.removeObserver(pref, handleEvent); - } - }); + window.addEventListener( + 'unload', + () => { + for (let pref of kZenThemePrefsList) { + Services.prefs.removeObserver(pref, handleEvent); + } + }, + { once: true } + ); }, handleEvent(event) { diff --git a/src/zen/tests/workspaces/browser_issue_8699.js b/src/zen/tests/workspaces/browser_issue_8699.js index e6e4ec0ec..e76b08ebe 100644 --- a/src/zen/tests/workspaces/browser_issue_8699.js +++ b/src/zen/tests/workspaces/browser_issue_8699.js @@ -4,7 +4,10 @@ 'use strict'; add_task(async function test_Restore_Closed_Tabs() { - const currentTab = gBrowser.selectedTab; + const currentTab = BrowserTestUtils.addTab(window.gBrowser, 'https://example.com/current', { + skipAnimation: true, + }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); const tabsToClose = []; for (let i = 0; i < 3; i++) { const tab = await BrowserTestUtils.openNewForegroundTab( @@ -35,7 +38,7 @@ add_task(async function test_Restore_Closed_Tabs() { ok(!currentTab.selected, 'Current tab should not be selected after restore'); Assert.equal( gBrowser.tabs.length, - 5, // 1 initial tab + 3 restored tabs + 5, // 1 initial tab + 3 restored tabs + 1 for empty tab 'There should be four tabs after restoring closed tabs' ); gBrowser.selectedTab = currentTab; diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index 363d94e54..6fe257018 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -151,14 +151,20 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { } if (!this.privateWindowOrDisabled) { + const observerFunction = async function observe(subject) { + this._workspaceBookmarksCache = null; + await this.workspaceBookmarks(); + this._invalidateBookmarkContainers(); + }; Services.obs.addObserver(this, 'weave:engine:sync:finish'); - Services.obs.addObserver( - async function observe(subject) { - this._workspaceBookmarksCache = null; - await this.workspaceBookmarks(); - this._invalidateBookmarkContainers(); - }.bind(this), - 'workspace-bookmarks-updated' + Services.obs.addObserver(observerFunction, 'workspace-bookmarks-updated'); + window.addEventListener( + 'unload', + () => { + Services.obs.removeObserver(this, 'weave:engine:sync:finish'); + Services.obs.removeObserver(observerFunction, 'workspace-bookmarks-updated'); + }, + { once: true } ); } } @@ -962,7 +968,14 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { ) { this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`); setTimeout(() => { - gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(tabs[this._tabToSelect]); + let tabToUse = gZenGlanceManager.getTabOrGlanceParent(tabs[this._tabToSelect]); + if (tabToUse.pinned) { + // We are before the empty tab here, so we need to select the next tab + tabToUse = gZenGlanceManager.getTabOrGlanceParent( + tabs[this._tabToSelect + 1] || tabs[this._tabToSelect] + ); + } + gBrowser.selectedTab = tabToUse; this._removedByStartupPage = true; gBrowser.removeTab(this._tabToRemoveForEmpty, { skipSessionStore: true, @@ -1537,6 +1550,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { const container = this.workspaceElement(otherWorkspace.uuid); container.active = otherWorkspace.uuid === workspace.uuid; } + // note: We are calling this even though it is also called in `updateTabsContainers`. This is mostly + // due to a race condition where the workspace strip is not updated before the tabs are moved. + this.makeSureEmptyTabIsFirst(); gBrowser.pinnedTabsContainer = this.pinnedTabsContainer || gBrowser.pinnedTabsContainer; gBrowser.tabContainer.pinnedTabsContainer = this.pinnedTabsContainer || gBrowser.tabContainer.pinnedTabsContainer; diff --git a/surfer.json b/surfer.json index 30f022990..f038adcd0 100644 --- a/surfer.json +++ b/surfer.json @@ -19,7 +19,7 @@ "brandShortName": "Zen", "brandFullName": "Zen Browser", "release": { - "displayVersion": "1.14.6b", + "displayVersion": "1.14.7b", "github": { "repo": "zen-browser/desktop" },