From 47c03d03c8a55a9e91065954935f8bde2be2dc12 Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Sat, 27 Dec 2025 12:00:40 +0100 Subject: [PATCH 1/3] fix: Run a mouse tracker event on compact mode to check for closes, b=no-bug, c=compact-mode --- src/zen/compact-mode/ZenCompactMode.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/zen/compact-mode/ZenCompactMode.mjs b/src/zen/compact-mode/ZenCompactMode.mjs index 65469e7b7..90cdfc9a1 100644 --- a/src/zen/compact-mode/ZenCompactMode.mjs +++ b/src/zen/compact-mode/ZenCompactMode.mjs @@ -716,8 +716,14 @@ window.gZenCompactModeManager = { // If we want the toolbars to be draggable, we need to make sure to check the hover state after a short delay. // This is because the mouse is left to be handled natively so firefox thinks the mouse left the window for a split second. setTimeout(() => { + let isHovered = event.target.matches(':hover'); + MousePosTracker._callListener({ + onMouseEnter: () => (isHovered = true), + onMouseLeave: () => {}, + getMouseTargetRect: () => window.windowUtils.getBoundsWithoutFlushing(target), + }); // Let's double check if the mouse is still hovering over the element, see the bug above. - if (event.target.matches(':hover')) { + if (isHovered) { return; } From ec8b14e8f1257eb2661cba8228261808305e8fb1 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:26:34 +0100 Subject: [PATCH 2/3] fix: Fixed custom tab icons not persisting across sessions, p=#11744 * fix: Fixed custom tab icons not persisting across sessions, b=no-bug, c=common, tabs * feat: Allow rename tab context menu to appear on expanded mode, b=no-bug, c=tabs --- .../sessionstore/SessionStore-sys-mjs.patch | 37 ++--- .../sessionstore/TabState-sys-mjs.patch | 4 +- .../tabbrowser/content/tabbrowser-js.patch | 156 +++++++++--------- src/zen/common/modules/ZenSessionStore.mjs | 4 +- src/zen/sessionstore/ZenWindowSync.sys.mjs | 3 +- src/zen/tabs/ZenPinnedTabManager.mjs | 12 +- 6 files changed, 100 insertions(+), 116 deletions(-) diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index 9021801e8..dc6f7f71d 100644 --- a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch +++ b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs -index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd21451b6e8eb 100644 +index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4f8553b7b20c0456929b572ca1522627a689b482 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -127,6 +127,8 @@ const TAB_EVENTS = [ @@ -139,18 +139,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 userContextId: state.userContextId, skipLoad: true, preferredRemoteType, -@@ -5032,7 +5055,10 @@ var SessionStoreInternal = { - !activePageData || - (activePageData && activePageData.url != "about:blank") - ) { -+ let hadZenStaticFlag = tab.hasAttribute("zen-has-static-icon"); -+ tab.removeAttribute("zen-has-static-icon"); - win.gBrowser.setIcon(tab, tabData.image); -+ if (hadZenStaticFlag) tab.setAttribute("zen-has-static-icon", true); - } - lazy.TabStateCache.update(browser.permanentKey, { - image: null, -@@ -5374,7 +5400,7 @@ var SessionStoreInternal = { +@@ -5374,7 +5397,7 @@ var SessionStoreInternal = { for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) { let tab = tabbrowser.tabs[i]; @@ -159,7 +148,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 removableTabs.push(tab); } } -@@ -5483,7 +5509,7 @@ var SessionStoreInternal = { +@@ -5483,7 +5506,7 @@ var SessionStoreInternal = { // collect the data for all windows for (ix in this._windows) { @@ -168,7 +157,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 // window data is still in _statesToRestore continue; } -@@ -5625,11 +5651,12 @@ var SessionStoreInternal = { +@@ -5625,11 +5648,12 @@ var SessionStoreInternal = { } let tabbrowser = aWindow.gBrowser; @@ -182,7 +171,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 // update the internal state data for this window for (let tab of tabs) { if (tab == aWindow.FirefoxViewHandler.tab) { -@@ -5640,6 +5667,9 @@ var SessionStoreInternal = { +@@ -5640,6 +5664,9 @@ var SessionStoreInternal = { tabsData.push(tabData); } @@ -192,7 +181,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 // update tab group state for this window winData.groups = []; for (let tabGroup of aWindow.gBrowser.tabGroups) { -@@ -5652,7 +5682,7 @@ var SessionStoreInternal = { +@@ -5652,7 +5679,7 @@ var SessionStoreInternal = { // a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab, // since it's only inserted into the tab strip after it's selected). if (aWindow.FirefoxViewHandler.tab?.selected) { @@ -201,7 +190,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 winData.title = tabbrowser.tabs[0].label; } winData.selected = selectedIndex; -@@ -5765,8 +5795,8 @@ var SessionStoreInternal = { +@@ -5765,8 +5792,8 @@ var SessionStoreInternal = { // selectTab represents. let selectTab = 0; if (overwriteTabs) { @@ -212,7 +201,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 selectTab = Math.min(selectTab, winData.tabs.length); } -@@ -5788,6 +5818,7 @@ var SessionStoreInternal = { +@@ -5788,6 +5815,7 @@ var SessionStoreInternal = { if (overwriteTabs) { for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) { if (!tabbrowser.tabs[i].selected) { @@ -220,7 +209,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 tabbrowser.removeTab(tabbrowser.tabs[i]); } } -@@ -5821,6 +5852,9 @@ var SessionStoreInternal = { +@@ -5821,6 +5849,9 @@ var SessionStoreInternal = { savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id) ); } @@ -230,7 +219,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 // Move the originally open tabs to the end. if (initialTabs) { -@@ -6372,6 +6406,25 @@ var SessionStoreInternal = { +@@ -6372,6 +6403,25 @@ var SessionStoreInternal = { // Most of tabData has been restored, now continue with restoring // attributes that may trigger external events. @@ -245,7 +234,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 + tab.zenStaticLabel = tabData.zenStaticLabel; + } + if (tabData.zenHasStaticIcon) { -+ tab.setAttribute("zen-has-static-icon", "true"); ++ tab.zenStaticIcon = tabData.image; + } + if (tabData.zenDefaultUserContextId) { + tab.setAttribute("zenDefaultUserContextId", true); @@ -256,7 +245,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 if (tabData.pinned) { tabbrowser.pinTab(tab); -@@ -7290,7 +7343,7 @@ var SessionStoreInternal = { +@@ -7290,7 +7340,7 @@ var SessionStoreInternal = { let groupsToSave = new Map(); for (let tIndex = 0; tIndex < window.tabs.length; ) { @@ -265,7 +254,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..0deff2a0238fa3822387fddd44ddd214 // Adjust window.selected if (tIndex + 1 < window.selected) { window.selected -= 1; -@@ -7305,7 +7358,7 @@ var SessionStoreInternal = { +@@ -7305,7 +7355,7 @@ var SessionStoreInternal = { ); // We don't want to increment tIndex here. continue; diff --git a/src/browser/components/sessionstore/TabState-sys-mjs.patch b/src/browser/components/sessionstore/TabState-sys-mjs.patch index 2cbec42e4..092a80398 100644 --- a/src/browser/components/sessionstore/TabState-sys-mjs.patch +++ b/src/browser/components/sessionstore/TabState-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs -index 82721356d191055bec0d4b0ca49e481221988801..e0fde4fc475471d170ce46c1207e6e7b67fcaf1d 100644 +index 82721356d191055bec0d4b0ca49e481221988801..fc117fc8e0f5eb3d0a2fed70558773afb9afce9a 100644 --- a/browser/components/sessionstore/TabState.sys.mjs +++ b/browser/components/sessionstore/TabState.sys.mjs @@ -85,7 +85,25 @@ class _TabState { @@ -15,7 +15,7 @@ index 82721356d191055bec0d4b0ca49e481221988801..e0fde4fc475471d170ce46c1207e6e7b + tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon"); + tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab"); + tabData.zenStaticLabel = tab.zenStaticLabel; -+ tabData.zenHasStaticIcon = tab.hasAttribute("zen-has-static-icon"); ++ tabData.zenHasStaticIcon = !!tab.zenStaticIcon; + tabData.zenGlanceId = tab.getAttribute("glance-id"); + tabData.zenIsGlance = tab.hasAttribute("zen-glance-tab"); + tabData._zenPinnedInitialState = tab._zenPinnedInitialState; diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 32e12d994..76c8cfaf3 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 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca90aa52900 100644 +index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703ca0991da 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -386,6 +386,7 @@ @@ -123,19 +123,17 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 }); aTab.style.marginInlineStart = ""; -@@ -1098,6 +1159,11 @@ +@@ -1098,6 +1159,9 @@ let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"]; + try { -+ if (aTab.hasAttribute("zen-has-static-icon")) { -+ return; -+ } ++ aIconURL = aTab.zenStaticIcon || aIconURL; + gZenPinnedTabManager.onTabIconChanged(aTab, aIconURL); if ( aIconURL && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol)) -@@ -1107,6 +1173,9 @@ +@@ -1107,6 +1171,9 @@ ); return; } @@ -145,7 +143,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 let browser = this.getBrowserForTab(aTab); browser.mIconURL = aIconURL; -@@ -1379,7 +1448,6 @@ +@@ -1379,7 +1446,6 @@ // Preview mode should not reset the owner if (!this._previewMode && !oldTab.selected) { @@ -153,7 +151,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } let lastRelatedTab = this._lastRelatedTabMap.get(oldTab); -@@ -1470,6 +1538,7 @@ +@@ -1470,6 +1536,7 @@ if (!this._previewMode) { newTab.recordTimeFromUnloadToReload(); newTab.updateLastAccessed(); @@ -161,7 +159,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 oldTab.updateLastAccessed(); // if this is the foreground window, update the last-seen timestamps. if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) { -@@ -1622,6 +1691,9 @@ +@@ -1622,6 +1689,9 @@ } let activeEl = document.activeElement; @@ -171,7 +169,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // If focus is on the old tab, move it to the new tab. if (activeEl == oldTab) { newTab.focus(); -@@ -1945,6 +2017,11 @@ +@@ -1945,6 +2015,11 @@ } _setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) { @@ -183,7 +181,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (!aLabel || aLabel.includes("about:reader?")) { return false; } -@@ -2053,7 +2130,7 @@ +@@ -2053,7 +2128,7 @@ newIndex = this.selectedTab._tPos + 1; } @@ -192,7 +190,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (this.isTabGroupLabel(targetTab)) { throw new Error( "Replacing a tab group label with a tab is not supported" -@@ -2328,6 +2405,7 @@ +@@ -2328,6 +2403,7 @@ uriIsAboutBlank, userContextId, skipLoad, @@ -200,7 +198,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } = {}) { let b = document.createXULElement("browser"); // Use the JSM global to create the permanentKey, so that if the -@@ -2401,8 +2479,7 @@ +@@ -2401,8 +2477,7 @@ // we use a different attribute name for this? b.setAttribute("name", name); } @@ -210,7 +208,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 b.setAttribute("transparent", "true"); } -@@ -2567,7 +2644,7 @@ +@@ -2567,7 +2642,7 @@ let panel = this.getPanel(browser); let uniqueId = this._generateUniquePanelID(); @@ -219,7 +217,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 aTab.linkedPanel = uniqueId; // Inject the into the DOM if necessary. -@@ -2626,8 +2703,8 @@ +@@ -2626,8 +2701,8 @@ // If we transitioned from one browser to two browsers, we need to set // hasSiblings=false on both the existing browser and the new browser. if (this.tabs.length == 2) { @@ -230,7 +228,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else { aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1; } -@@ -2814,7 +2891,6 @@ +@@ -2814,7 +2889,6 @@ this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, { tabIndex: tab._tPos + 1, userContextId: tab.userContextId, @@ -238,7 +236,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 focusUrlBar: true, }); resolve(this.selectedBrowser); -@@ -2923,6 +2999,9 @@ +@@ -2923,6 +2997,9 @@ schemelessInput, hasValidUserGestureActivation = false, textDirectiveUserActivation = false, @@ -248,7 +246,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } = {} ) { // all callers of addTab that pass a params object need to pass -@@ -2933,10 +3012,17 @@ +@@ -2933,10 +3010,17 @@ ); } @@ -266,7 +264,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // If we're opening a foreground tab, set the owner by default. ownerTab ??= inBackground ? null : this.selectedTab; -@@ -2944,6 +3030,7 @@ +@@ -2944,6 +3028,7 @@ if (this.selectedTab.owner) { this.selectedTab.owner = null; } @@ -274,7 +272,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // Find the tab that opened this one, if any. This is used for // determining positioning, and inherited attributes such as the -@@ -2996,6 +3083,21 @@ +@@ -2996,6 +3081,21 @@ noInitialLabel, skipBackgroundNotify, }); @@ -296,7 +294,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (insertTab) { // Insert the tab into the tab container in the correct position. this.#insertTabAtIndex(t, { -@@ -3004,6 +3106,7 @@ +@@ -3004,6 +3104,7 @@ ownerTab, openerTab, pinned, @@ -304,7 +302,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 bulkOrderedOpen, tabGroup: tabGroup ?? openerTab?.group, }); -@@ -3022,6 +3125,7 @@ +@@ -3022,6 +3123,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -312,7 +310,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 })); if (focusUrlBar) { -@@ -3146,6 +3250,12 @@ +@@ -3146,6 +3248,12 @@ } } @@ -325,7 +323,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // Additionally send pinned tab events if (pinned) { this.#notifyPinnedStatus(t); -@@ -3349,10 +3459,10 @@ +@@ -3349,10 +3457,10 @@ isAdoptingGroup = false, isUserTriggered = false, telemetryUserCreateSource = "unknown", @@ -337,7 +335,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } if (!color) { -@@ -3373,9 +3483,14 @@ +@@ -3373,9 +3481,14 @@ label, isAdoptingGroup ); @@ -354,7 +352,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 ); group.addTabs(tabs); -@@ -3496,7 +3611,7 @@ +@@ -3496,7 +3609,7 @@ } this.#handleTabMove(tab, () => @@ -363,7 +361,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 ); } -@@ -3698,6 +3813,7 @@ +@@ -3698,6 +3811,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -371,7 +369,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } ) { // If we don't have a preferred remote type (or it is `NOT_REMOTE`), and -@@ -3767,6 +3883,7 @@ +@@ -3767,6 +3881,7 @@ openWindowInfo, name, skipLoad, @@ -379,7 +377,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 }); } -@@ -3955,7 +4072,7 @@ +@@ -3955,7 +4070,7 @@ // Add a new tab if needed. if (!tab) { let createLazyBrowser = @@ -388,7 +386,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 let url = "about:blank"; if (tabData.entries?.length) { -@@ -3992,8 +4109,10 @@ +@@ -3992,8 +4107,10 @@ insertTab: false, skipLoad: true, preferredRemoteType, @@ -400,7 +398,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (select) { tabToSelect = tab; } -@@ -4005,7 +4124,8 @@ +@@ -4005,7 +4122,8 @@ this.pinTab(tab); // Then ensure all the tab open/pinning information is sent. this._fireTabOpen(tab, {}); @@ -410,7 +408,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 let { groupId } = tabData; const tabGroup = tabGroupWorkingData.get(groupId); // if a tab refers to a tab group we don't know, skip any group -@@ -4019,7 +4139,10 @@ +@@ -4019,7 +4137,10 @@ tabGroup.stateData.id, tabGroup.stateData.color, tabGroup.stateData.collapsed, @@ -422,7 +420,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 ); tabsFragment.appendChild(tabGroup.node); } -@@ -4064,9 +4187,23 @@ +@@ -4064,9 +4185,23 @@ // to remove the old selected tab. if (tabToSelect) { let leftoverTab = this.selectedTab; @@ -438,15 +436,15 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 + gZenWorkspaces._initialTab._shouldRemove = true; + } + } -+ } + } + else { + gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab; - } ++ } + this._hasAlreadyInitializedZenSessionStore = true; if (tabs.length > 1 || !tabs[0].selected) { this._updateTabsAfterInsert(); -@@ -4257,11 +4394,14 @@ +@@ -4257,11 +4392,14 @@ if (ownerTab) { tab.owner = ownerTab; } @@ -462,7 +460,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if ( !bulkOrderedOpen && ((openerTab && -@@ -4273,7 +4413,7 @@ +@@ -4273,7 +4411,7 @@ let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab); let previousTab = lastRelatedTab || openerTab || this.selectedTab; @@ -471,7 +469,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 tabGroup = previousTab.group; } if ( -@@ -4284,7 +4424,7 @@ +@@ -4284,7 +4422,7 @@ ) { elementIndex = Infinity; } else if (previousTab.visible) { @@ -480,7 +478,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else if (previousTab == FirefoxViewHandler.tab) { elementIndex = 0; } -@@ -4312,14 +4452,14 @@ +@@ -4312,14 +4450,14 @@ } // Ensure index is within bounds. if (tab.pinned) { @@ -499,7 +497,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (pinned && !itemAfter?.pinned) { itemAfter = null; -@@ -4330,7 +4470,7 @@ +@@ -4330,7 +4468,7 @@ this.tabContainer._invalidateCachedTabs(); @@ -508,7 +506,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 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); -@@ -4358,7 +4498,11 @@ +@@ -4358,7 +4496,11 @@ const tabContainer = pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; @@ -520,7 +518,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } this._updateTabsAfterInsert(); -@@ -4366,6 +4510,7 @@ +@@ -4366,6 +4508,7 @@ if (pinned) { this._updateTabBarForPinnedTabs(); } @@ -528,7 +526,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 TabBarVisibility.update(); } -@@ -4916,6 +5061,7 @@ +@@ -4916,6 +5059,7 @@ telemetrySource, } = {} ) { @@ -536,7 +534,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // When 'closeWindowWithLastTab' pref is enabled, closing all tabs // can be considered equivalent to closing the window. if ( -@@ -5005,6 +5151,7 @@ +@@ -5005,6 +5149,7 @@ if (lastToClose) { this.removeTab(lastToClose, aParams); } @@ -544,7 +542,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } catch (e) { console.error(e); } -@@ -5043,6 +5190,12 @@ +@@ -5043,6 +5188,12 @@ aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start(); } @@ -557,7 +555,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && aTab.closing) { -@@ -5057,6 +5210,9 @@ +@@ -5057,6 +5208,9 @@ // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; let isLastTab = this.#isLastTabInWindow(aTab); @@ -567,7 +565,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -5105,7 +5261,13 @@ +@@ -5105,7 +5259,13 @@ // We're not animating, so we can cancel the animation stopwatch. Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId); aTab._closeTimeAnimTimerId = null; @@ -582,7 +580,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 return; } -@@ -5239,7 +5401,7 @@ +@@ -5239,7 +5399,7 @@ closeWindowWithLastTab != null ? closeWindowWithLastTab : !window.toolbar.visible || @@ -591,7 +589,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -5263,6 +5425,7 @@ +@@ -5263,6 +5423,7 @@ newTab = true; } @@ -599,7 +597,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -5303,13 +5466,7 @@ +@@ -5303,13 +5464,7 @@ aTab._mouseleave(); if (newTab) { @@ -614,7 +612,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else { TabBarVisibility.update(); } -@@ -5442,6 +5599,7 @@ +@@ -5442,6 +5597,7 @@ this.tabs[i]._tPos = i; } @@ -622,7 +620,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (!this._windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -5663,6 +5821,7 @@ +@@ -5663,6 +5819,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -630,7 +628,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -5675,13 +5834,13 @@ +@@ -5675,13 +5832,13 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -646,7 +644,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 ); let tab = this.tabContainer.findNextTab(aTab, { -@@ -5697,7 +5856,7 @@ +@@ -5697,7 +5854,7 @@ } if (tab) { @@ -655,7 +653,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } // If no qualifying visible tab was found, see if there is a tab in -@@ -5718,7 +5877,7 @@ +@@ -5718,7 +5875,7 @@ }); } @@ -664,7 +662,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } _blurTab(aTab) { -@@ -5729,7 +5888,7 @@ +@@ -5729,7 +5886,7 @@ * @returns {boolean} * False if swapping isn't permitted, true otherwise. */ @@ -673,7 +671,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // Do not allow transfering a private tab to a non-private window // and vice versa. if ( -@@ -5783,6 +5942,7 @@ +@@ -5783,6 +5940,7 @@ // fire the beforeunload event in the process. Close the other // window if this was its last tab. if ( @@ -681,7 +679,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 !remoteBrowser._beginRemoveTab(aOtherTab, { adoptedByTab: aOurTab, closeWindowWithLastTab: true, -@@ -5794,7 +5954,7 @@ +@@ -5794,7 +5952,7 @@ // If this is the last tab of the window, hide the window // immediately without animation before the docshell swap, to avoid // about:blank being painted. @@ -690,7 +688,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (closeWindow) { let win = aOtherTab.ownerGlobal; win.windowUtils.suppressAnimation(true); -@@ -5918,11 +6078,13 @@ +@@ -5918,11 +6076,13 @@ } // Finish tearing down the tab that's going away. @@ -704,7 +702,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 this.setTabTitle(aOurTab); -@@ -6124,10 +6286,10 @@ +@@ -6124,10 +6284,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -717,7 +715,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6185,7 +6347,8 @@ +@@ -6185,7 +6345,8 @@ * * @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab */ @@ -727,7 +725,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (this.tabs.length == 1) { return null; } -@@ -6209,12 +6372,14 @@ +@@ -6209,12 +6370,14 @@ } // tell a new window to take the "dropped" tab @@ -743,7 +741,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } /** -@@ -6319,7 +6484,7 @@ +@@ -6319,7 +6482,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -752,7 +750,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } /** -@@ -6404,8 +6569,8 @@ +@@ -6404,8 +6567,8 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -763,7 +761,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -6431,10 +6596,16 @@ +@@ -6431,10 +6594,16 @@ this.#handleTabMove( element, () => { @@ -782,7 +780,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 if (neighbor && this.isTab(element) && tabIndex > element._tPos) { neighbor.after(element); } else { -@@ -6492,23 +6663,28 @@ +@@ -6492,23 +6661,28 @@ #moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) { if (this.isTabGroupLabel(targetElement)) { targetElement = targetElement.group; @@ -817,7 +815,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else if (!element.pinned && targetElement && targetElement.pinned) { // If the caller asks to move an unpinned element next to a pinned // tab, move the unpinned element to be the first unpinned element -@@ -6521,14 +6697,34 @@ +@@ -6521,14 +6695,34 @@ // 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. @@ -853,7 +851,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -6537,7 +6733,7 @@ +@@ -6537,7 +6731,7 @@ element, () => { if (moveBefore) { @@ -862,7 +860,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 } else if (targetElement) { targetElement.after(element); } else { -@@ -6607,10 +6803,10 @@ +@@ -6607,10 +6801,10 @@ * @param {TabMetricsContext} [metricsContext] */ moveTabToGroup(aTab, aGroup, metricsContext) { @@ -875,7 +873,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -6656,6 +6852,7 @@ +@@ -6656,6 +6850,7 @@ let state = { tabIndex: tab._tPos, @@ -883,7 +881,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 }; if (tab.visible) { state.elementIndex = tab.elementIndex; -@@ -6682,7 +6879,7 @@ +@@ -6682,7 +6877,7 @@ let changedTabGroup = previousTabState.tabGroupId != currentTabState.tabGroupId; @@ -892,7 +890,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 tab.dispatchEvent( new CustomEvent("TabMove", { bubbles: true, -@@ -6723,6 +6920,10 @@ +@@ -6723,6 +6918,10 @@ moveActionCallback(); @@ -903,7 +901,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -6815,6 +7016,8 @@ +@@ -6815,6 +7014,8 @@ params.userContextId = aTab.getAttribute("usercontextid"); } let newTab = this.addWebTab("about:blank", params); @@ -912,7 +910,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 let newBrowser = this.getBrowserForTab(newTab); aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -7623,7 +7826,7 @@ +@@ -7623,7 +7824,7 @@ // preventDefault(). It will still raise the window if appropriate. break; } @@ -921,7 +919,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 window.focus(); aEvent.preventDefault(); break; -@@ -7640,7 +7843,6 @@ +@@ -7640,7 +7841,6 @@ } case "TabGroupCollapse": aEvent.target.tabs.forEach(tab => { @@ -929,7 +927,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 }); break; case "TabGroupCreateByUser": -@@ -8589,6 +8791,7 @@ +@@ -8589,6 +8789,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -937,15 +935,15 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a87bcaf4e9b92a9f63e025409efabca9 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -8670,6 +8873,7 @@ +@@ -8670,6 +8871,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = -+ !this.mTab.hasAttribute("zen-has-static-icon") && ++ !this.mTab.zenStaticIcon && !this.mBrowser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9623,7 +9827,7 @@ var TabContextMenu = { +@@ -9623,7 +9825,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; diff --git a/src/zen/common/modules/ZenSessionStore.mjs b/src/zen/common/modules/ZenSessionStore.mjs index dd53dbb83..a316e11ca 100644 --- a/src/zen/common/modules/ZenSessionStore.mjs +++ b/src/zen/common/modules/ZenSessionStore.mjs @@ -24,8 +24,8 @@ class ZenSessionStore extends nsZenPreloadedFeature { if (tabData.zenStaticLabel) { tab.zenStaticLabel = tabData.zenStaticLabel; } - if (tabData.zenHasStaticIcon) { - tab.setAttribute('zen-has-static-icon', 'true'); + if (tabData.zenHasStaticIcon && tabData.image) { + tab.zenStaticIcon = tabData.image; } if (tabData.zenEssential) { tab.setAttribute('zen-essential', 'true'); diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index b421bb89b..2d8878751 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -340,7 +340,7 @@ class nsZenWindowSync { } const { gBrowser, gZenFolders } = aWindow; if (flags & SYNC_FLAG_ICON) { - aTargetItem.removeAttribute('zen-has-static-icon'); + aTargetItem.zenStaticIcon = aOriginalItem.zenStaticIcon; if (gBrowser.isTab(aOriginalItem)) { gBrowser.setIcon( aTargetItem, @@ -350,7 +350,6 @@ class nsZenWindowSync { // Icons are a zen-only feature for tab groups. gZenFolders.setFolderUserIcon(aTargetItem, aOriginalItem.iconURL); } - this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, 'zen-has-static-icon'); } if (flags & SYNC_FLAG_LABEL) { if (gBrowser.isTab(aOriginalItem)) { diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index e67aca315..46822ebd1 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -326,9 +326,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { // Remove everything except the entry we want to keep state.entries = [initialState.entry]; - state.image = tab.hasAttribute('zen-has-static-icon') - ? tab.getAttribute('image') - : initialState.image; + state.image = initialState.image; state.index = 0; SessionStore.setTabState(tab, state); @@ -477,14 +475,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { }); document.getElementById('context_zen-edit-tab-icon').addEventListener('command', () => { const tab = TabContextMenu.contextTab; - tab.removeAttribute('zen-has-static-icon'); + delete tab.zenStaticIcon; gZenEmojiPicker .open(tab.iconImage, { emojiAsSVG: true }) .then((icon) => { - gBrowser.setIcon(tab, icon); if (icon) { - tab.setAttribute('zen-has-static-icon', 'true'); + tab.zenStaticIcon = icon; } + gBrowser.setIcon(tab, icon); lazy.TabStateCache.update(tab.permanentKey, { image: null, }); @@ -528,7 +526,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { document.getElementById('context_zen-edit-tab-title').hidden = isEssential || !Services.prefs.getBoolPref('zen.tabs.rename-tabs') || - gZenVerticalTabsManager._prefsSidebarExpanded; + !gZenVerticalTabsManager._prefsSidebarExpanded; } moveToAnotherTabContainerIfNecessary(event, movingTabs) { From 1338b43e10c3b45f3049058b8afd519037384bc1 Mon Sep 17 00:00:00 2001 From: "mr. m" Date: Sat, 27 Dec 2025 18:52:09 +0100 Subject: [PATCH 3/3] feat: Add migration logic for zen_pins database to window sync, b=no-bug, c=common, tabs --- .../sessionstore/SessionStore-sys-mjs.patch | 4 +- .../sessionstore/TabState-sys-mjs.patch | 7 +- .../tabbrowser/content/tabbrowser-js.patch | 15 ++-- src/zen/common/modules/ZenSessionStore.mjs | 3 - .../sessionstore/ZenSessionManager.sys.mjs | 77 +++++++++++++++++-- src/zen/tabs/ZenPinnedTabManager.mjs | 3 +- 6 files changed, 85 insertions(+), 24 deletions(-) diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index dc6f7f71d..fe61001fd 100644 --- a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch +++ b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs -index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4f8553b7b20c0456929b572ca1522627a689b482 100644 +index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4439fe5fb3c7002b173415b615892ef356b22959 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -127,6 +127,8 @@ const TAB_EVENTS = [ @@ -233,7 +233,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..4f8553b7b20c0456929b572ca1522627 + if (tabData.zenStaticLabel) { + tab.zenStaticLabel = tabData.zenStaticLabel; + } -+ if (tabData.zenHasStaticIcon) { ++ if (tabData.zenHasStaticIcon && tabData.image) { + tab.zenStaticIcon = tabData.image; + } + if (tabData.zenDefaultUserContextId) { diff --git a/src/browser/components/sessionstore/TabState-sys-mjs.patch b/src/browser/components/sessionstore/TabState-sys-mjs.patch index 092a80398..bc19f5a93 100644 --- a/src/browser/components/sessionstore/TabState-sys-mjs.patch +++ b/src/browser/components/sessionstore/TabState-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs -index 82721356d191055bec0d4b0ca49e481221988801..fc117fc8e0f5eb3d0a2fed70558773afb9afce9a 100644 +index 82721356d191055bec0d4b0ca49e481221988801..ffa95005b96ea384433f18dace63faa35d2d21bf 100644 --- a/browser/components/sessionstore/TabState.sys.mjs +++ b/browser/components/sessionstore/TabState.sys.mjs -@@ -85,7 +85,25 @@ class _TabState { +@@ -85,7 +85,24 @@ class _TabState { tabData.groupId = tab.group.id; } @@ -11,7 +11,6 @@ index 82721356d191055bec0d4b0ca49e481221988801..fc117fc8e0f5eb3d0a2fed70558773af + tabData.zenEssential = tab.getAttribute("zen-essential"); + tabData.pinned = tabData.pinned || tabData.zenEssential; + tabData.zenDefaultUserContextId = tab.getAttribute("zenDefaultUserContextId"); -+ tabData.zenPinnedEntry = tab.getAttribute("zen-pinned-entry"); + tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon"); + tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab"); + tabData.zenStaticLabel = tab.zenStaticLabel; @@ -28,7 +27,7 @@ index 82721356d191055bec0d4b0ca49e481221988801..fc117fc8e0f5eb3d0a2fed70558773af tabData.userContextId = tab.userContextId || 0; -@@ -98,7 +116,7 @@ class _TabState { +@@ -98,7 +115,7 @@ class _TabState { // Copy data from the tab state cache only if the tab has fully finished // restoring. We don't want to overwrite data contained in __SS_data. diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 76c8cfaf3..97eb477f4 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 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703ca0991da 100644 +index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..b01c89f2e0d3b44dddaa01a20b4da13d797de7be 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -386,6 +386,7 @@ @@ -901,16 +901,17 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -6815,6 +7014,8 @@ +@@ -6815,6 +7014,9 @@ params.userContextId = aTab.getAttribute("usercontextid"); } let newTab = this.addWebTab("about:blank", params); + newTab._zenContentsVisible = true; + newTab.zenStaticLabel = aTab.zenStaticLabel; ++ newTab.zenStaticIcon = aTab.zenStaticIcon; let newBrowser = this.getBrowserForTab(newTab); aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -7623,7 +7824,7 @@ +@@ -7623,7 +7825,7 @@ // preventDefault(). It will still raise the window if appropriate. break; } @@ -919,7 +920,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703 window.focus(); aEvent.preventDefault(); break; -@@ -7640,7 +7841,6 @@ +@@ -7640,7 +7842,6 @@ } case "TabGroupCollapse": aEvent.target.tabs.forEach(tab => { @@ -927,7 +928,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703 }); break; case "TabGroupCreateByUser": -@@ -8589,6 +8789,7 @@ +@@ -8589,6 +8790,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -935,7 +936,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -8670,6 +8871,7 @@ +@@ -8670,6 +8872,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = @@ -943,7 +944,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..28a42ceb86f2b0f0747062e4881ce703 !this.mBrowser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9623,7 +9825,7 @@ var TabContextMenu = { +@@ -9623,7 +9826,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; diff --git a/src/zen/common/modules/ZenSessionStore.mjs b/src/zen/common/modules/ZenSessionStore.mjs index a316e11ca..a01a8ef60 100644 --- a/src/zen/common/modules/ZenSessionStore.mjs +++ b/src/zen/common/modules/ZenSessionStore.mjs @@ -33,9 +33,6 @@ class ZenSessionStore extends nsZenPreloadedFeature { if (tabData.zenDefaultUserContextId) { tab.setAttribute('zenDefaultUserContextId', 'true'); } - if (tabData.zenPinnedEntry) { - tab.setAttribute('zen-pinned-entry', tabData.zenPinnedEntry); - } if (tabData._zenPinnedInitialState) { tab._zenPinnedInitialState = tabData._zenPinnedInitialState; } diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 07605d741..254d1e379 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -73,7 +73,7 @@ export class nsZenSessionManager { }); lazy.SessionStore.promiseAllWindowsRestored.then(() => { - delete this._migrationSpaceData; + delete this._migrationData; }); } @@ -88,14 +88,15 @@ export class nsZenSessionManager { * This is only called once during the first run after updating * to a version that uses the new session manager. */ - async #getSpacesFromDBForMigration() { + async #getDataFromDBForMigration() { try { const { PlacesUtils } = ChromeUtils.importESModule( 'resource://gre/modules/PlacesUtils.sys.mjs' ); const db = await PlacesUtils.promiseDBConnection(); - const rows = await db.executeCached('SELECT * FROM zen_workspaces ORDER BY created_at ASC'); - this._migrationSpaceData = rows.map((row) => ({ + let data = {}; + let rows = await db.execute('SELECT * FROM zen_workspaces ORDER BY created_at ASC'); + data.spaces = rows.map((row) => ({ uuid: row.getResultByName('uuid'), name: row.getResultByName('name'), icon: row.getResultByName('icon'), @@ -111,6 +112,22 @@ export class nsZenSessionManager { } : null, })); + rows = await db.execute('SELECT * FROM zen_pins ORDER BY position ASC'); + data.pins = rows.map((row) => ({ + uuid: row.getResultByName('uuid'), + title: row.getResultByName('title'), + url: row.getResultByName('url'), + containerTabId: row.getResultByName('container_id'), + workspaceUuid: row.getResultByName('workspace_uuid'), + position: row.getResultByName('position'), + isEssential: Boolean(row.getResultByName('is_essential')), + isGroup: Boolean(row.getResultByName('is_group')), + parentUuid: row.getResultByName('folder_parent_uuid'), + editedTitle: Boolean(row.getResultByName('edited_title')), + folderIcon: row.getResultByName('folder_icon'), + isFolderCollapsed: Boolean(row.getResultByName('is_folder_collapsed')), + })); + this._migrationData = data; } catch { /* ignore errors during migration */ } @@ -126,7 +143,7 @@ export class nsZenSessionManager { let promises = []; promises.push(this.#file.load()); if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) { - promises.push(this.#getSpacesFromDBForMigration()); + promises.push(this.#getDataFromDBForMigration()); } await Promise.all(promises); } catch (e) { @@ -149,12 +166,58 @@ export class nsZenSessionManager { // gotten the opportunity to save the session yet. if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) { Services.prefs.setBoolPref(MIGRATION_PREF, true); + const pins = this._migrationData?.pins || []; + const applyTabsToObject = (obj) => { + // Lets add pins that don't exist yet in the sidebar object. + obj.tabs = obj.tabs || []; + obj.folders = obj.folders || []; + for (const pin of pins) { + let isGroup = pin.isGroup; + let object = { + pinned: true, + ...(isGroup + ? { + workspaceId: pin.workspaceUuid, + userIcon: pin.folderIcon, + name: pin.title, + parentId: pin.parentUuid, + collapsed: pin.isFolderCollapsed, + id: pin.uuid, + emptyTabIds: [], + } + : { + zenSyncId: pin.uuid, + groupId: pin.parentUuid, + entries: [{ url: pin.url, title: pin.title }], + zenStaticLabel: pin.editedTitle, + zenEssential: pin.isEssential, + userContextId: pin.containerTabId || 0, + zenWorkspace: pin.workspaceUuid, + title: pin.title, + }), + }; + let items = isGroup ? obj.folders : obj.tabs; + let index = items.findIndex( + (tab) => + tab.zenSyncId === pin.uuid || tab.zenPinnedId === pin.uuid || tab.id === pin.uuid + ); + if (index === -1) { + // Add to the start of the tabs array to keep the order + items.unshift(object); + } else { + // Update existing tab data + items[index] = { ...object, ...items[index] }; + } + } + }; this.#sidebar = { ...this.#sidebar, - spaces: this._migrationSpaceData || [], + spaces: this._migrationData?.spaces || [], }; + applyTabsToObject(this.#sidebar); for (const winData of initialState?.windows || []) { - winData.spaces = this._migrationSpaceData || []; + winData.spaces = this._migrationData?.spaces || []; + applyTabsToObject(winData); } return; } diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 46822ebd1..fda9a0ebd 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -475,12 +475,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { }); document.getElementById('context_zen-edit-tab-icon').addEventListener('command', () => { const tab = TabContextMenu.contextTab; - delete tab.zenStaticIcon; gZenEmojiPicker .open(tab.iconImage, { emojiAsSVG: true }) .then((icon) => { if (icon) { tab.zenStaticIcon = icon; + } else { + delete tab.zenStaticIcon; } gBrowser.setIcon(tab, icon); lazy.TabStateCache.update(tab.permanentKey, {