diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index aa83c63a5..f9f5f66e2 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..667b76e3065aafcc60ab146f40013ce791e59747 100644 +index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..fb0030061a3bd9df2c8a444970b56ce81600d3af 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -127,6 +127,8 @@ const TAB_EVENTS = [ @@ -63,19 +63,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 if (this._restoreLastWindow && aWindow.toolbar.visible) { // always reset (if not a popup window) -@@ -2384,11 +2401,9 @@ var SessionStoreInternal = { - tabbrowser.selectedTab.label; - } - -- if (AppConstants.platform != "macosx") { - // Until we decide otherwise elsewhere, this window is part of a series - // of closing windows to quit. - winData._shouldRestore = true; -- } - - // Store the window's close date to figure out when each individual tab - // was closed. This timestamp should allow re-arranging data based on how -@@ -2465,7 +2480,7 @@ var SessionStoreInternal = { +@@ -2465,7 +2482,7 @@ var SessionStoreInternal = { // 2) Flush the window. // 3) When the flush is complete, revisit our decision to store the window // in _closedWindows, and add/remove as necessary. @@ -84,7 +72,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 this.maybeSaveClosedWindow(winData, isLastWindow); } -@@ -2486,7 +2501,7 @@ var SessionStoreInternal = { +@@ -2486,7 +2503,7 @@ var SessionStoreInternal = { // Save non-private windows if they have at // least one saveable tab or are the last window. @@ -93,7 +81,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 this.maybeSaveClosedWindow(winData, isLastWindow); if (!isLastWindow && winData.closedId > -1) { -@@ -3373,7 +3388,7 @@ var SessionStoreInternal = { +@@ -3373,7 +3390,7 @@ var SessionStoreInternal = { if (!isPrivateWindow && tabState.isPrivate) { return; } @@ -102,7 +90,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 return; } -@@ -4089,6 +4104,12 @@ var SessionStoreInternal = { +@@ -4089,6 +4106,12 @@ var SessionStoreInternal = { Math.min(tabState.index, tabState.entries.length) ); tabState.pinned = false; @@ -115,7 +103,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 if (inBackground === false) { aWindow.gBrowser.selectedTab = newTab; -@@ -4525,6 +4546,7 @@ var SessionStoreInternal = { +@@ -4525,6 +4548,7 @@ var SessionStoreInternal = { // Append the tab if we're opening into a different window, tabIndex: aSource == aTargetWindow ? pos : Infinity, pinned: state.pinned, @@ -123,7 +111,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 userContextId: state.userContextId, skipLoad: true, preferredRemoteType, -@@ -5374,7 +5396,7 @@ var SessionStoreInternal = { +@@ -5374,7 +5398,7 @@ var SessionStoreInternal = { for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) { let tab = tabbrowser.tabs[i]; @@ -132,16 +120,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 removableTabs.push(tab); } } -@@ -5434,7 +5456,7 @@ var SessionStoreInternal = { - } - - let workspaceID = aWindow.getWorkspaceID(); -- if (workspaceID) { -+ if (workspaceID && !(this.isLastRestorableWindow() && AppConstants.platform == "macosx")) { - winData.workspaceID = workspaceID; - } - }, -@@ -5483,7 +5505,7 @@ var SessionStoreInternal = { +@@ -5483,7 +5507,7 @@ var SessionStoreInternal = { // collect the data for all windows for (ix in this._windows) { @@ -150,7 +129,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 // window data is still in _statesToRestore continue; } -@@ -5625,11 +5647,12 @@ var SessionStoreInternal = { +@@ -5625,11 +5649,12 @@ var SessionStoreInternal = { } let tabbrowser = aWindow.gBrowser; @@ -164,7 +143,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 // update the internal state data for this window for (let tab of tabs) { if (tab == aWindow.FirefoxViewHandler.tab) { -@@ -5640,6 +5663,7 @@ var SessionStoreInternal = { +@@ -5640,6 +5665,7 @@ var SessionStoreInternal = { tabsData.push(tabData); } @@ -172,7 +151,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 // update tab group state for this window winData.groups = []; for (let tabGroup of aWindow.gBrowser.tabGroups) { -@@ -5652,7 +5676,7 @@ var SessionStoreInternal = { +@@ -5652,7 +5678,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) { @@ -181,7 +160,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 winData.title = tabbrowser.tabs[0].label; } winData.selected = selectedIndex; -@@ -5765,8 +5789,8 @@ var SessionStoreInternal = { +@@ -5765,8 +5791,8 @@ var SessionStoreInternal = { // selectTab represents. let selectTab = 0; if (overwriteTabs) { @@ -192,7 +171,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 selectTab = Math.min(selectTab, winData.tabs.length); } -@@ -5809,6 +5833,8 @@ var SessionStoreInternal = { +@@ -5809,6 +5835,8 @@ var SessionStoreInternal = { winData.tabs, winData.groups ?? [] ); @@ -201,7 +180,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 this._log.debug( `restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs` ); -@@ -6372,6 +6398,25 @@ var SessionStoreInternal = { +@@ -6372,6 +6400,25 @@ var SessionStoreInternal = { // Most of tabData has been restored, now continue with restoring // attributes that may trigger external events. @@ -227,7 +206,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 if (tabData.pinned) { tabbrowser.pinTab(tab); -@@ -7290,7 +7335,7 @@ var SessionStoreInternal = { +@@ -7290,7 +7337,7 @@ var SessionStoreInternal = { let groupsToSave = new Map(); for (let tIndex = 0; tIndex < window.tabs.length; ) { @@ -236,7 +215,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..667b76e3065aafcc60ab146f40013ce7 // Adjust window.selected if (tIndex + 1 < window.selected) { window.selected -= 1; -@@ -7305,7 +7350,7 @@ var SessionStoreInternal = { +@@ -7305,7 +7352,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 0a7c56bc4..450c981c3 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..e1d2c5ca0cbe5431df01f8d6411f88e3325e0ee9 100644 +index 82721356d191055bec0d4b0ca49e481221988801..80547ec951f881bef134b637730954eb1525c623 100644 --- a/browser/components/sessionstore/TabState.sys.mjs +++ b/browser/components/sessionstore/TabState.sys.mjs -@@ -85,7 +85,23 @@ class _TabState { +@@ -85,7 +85,24 @@ class _TabState { tabData.groupId = tab.group.id; } @@ -17,6 +17,7 @@ index 82721356d191055bec0d4b0ca49e481221988801..e1d2c5ca0cbe5431df01f8d6411f88e3 + tabData.zenHasStaticLabel = tab.hasAttribute("zen-has-static-label"); + tabData.zenGlanceId = tab.getAttribute("glance-id"); + tabData.zenIsGlance = tab.hasAttribute("zen-glance-tab"); ++ tabData._zenPinnedInitialState = tab._zenPinnedInitialState; + tabData._zenIsActiveTab = tab._zenContentsVisible; + tabData.searchMode = tab.ownerGlobal.gURLBar.getSearchMode(browser, true); @@ -26,3 +27,12 @@ index 82721356d191055bec0d4b0ca49e481221988801..e1d2c5ca0cbe5431df01f8d6411f88e3 tabData.userContextId = tab.userContextId || 0; +@@ -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. +- this.copyFromCache(browser.permanentKey, tabData, options); ++ this.copyFromCache(tab.permanentKey, tabData, options); + + // After copyFromCache() was called we check for properties that are kept + // in the cache only while the tab is pending or restoring. Once that diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 6b6c0d70d..39ff4e0d3 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..577d459794a7259e95a0aff0d76400a39aa1a03b 100644 +index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..d39d295c786fd9e6da29a8fa4b3b2a00a1cfd380 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -386,6 +386,7 @@ @@ -177,7 +177,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..577d459794a7259e95a0aff0d76400a3 + return false; + } + gZenPinnedTabManager.onTabLabelChanged(aTab); -+ if (!aLabel || aLabel.includes("about:reader?") || aTab.hasAttribute("zen-has-static-label")) { ++ if (!aLabel || aLabel.includes("about:reader?") || (aTab.hasAttribute("zen-has-static-label") && !aTab._zenChangeLabelFlag)) { return false; } diff --git a/src/zen/common/modules/ZenSessionStore.mjs b/src/zen/common/modules/ZenSessionStore.mjs index 77aed2ebf..95b2aac20 100644 --- a/src/zen/common/modules/ZenSessionStore.mjs +++ b/src/zen/common/modules/ZenSessionStore.mjs @@ -33,6 +33,9 @@ class ZenSessionStore extends nsZenPreloadedFeature { if (tabData.zenPinnedEntry) { tab.setAttribute('zen-pinned-entry', tabData.zenPinnedEntry); } + if (tabData._zenPinnedInitialState) { + tab._zenPinnedInitialState = tabData._zenPinnedInitialState; + } } async #waitAndCleanup() { diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 0541894d3..2a12091bb 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -250,44 +250,42 @@ export class nsZenSessionManager { return; } this.log('Restoring new window with Zen session data'); - lazy.setTimeout(() => { - const state = lazy.SessionStore.getCurrentState(true); - const windows = (state.windows || []).filter( - (win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced - ); - let windowToClone = windows.length > 1 ? windows[windows.length - 2] : {}; - let newWindow = Cu.cloneInto(windowToClone, {}); - if (windows.length < 2) { - // We only want to restore the sidebar object if we found - // only one normal window to clone from (which is the one - // we are opening). - this.log('Restoring sidebar data into new window'); - this.#restoreWindowData(newWindow); - } - newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []); + const state = lazy.SessionStore.getCurrentState(true); + const windows = (state.windows || []).filter( + (win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced + ); + let windowToClone = windows[0] || {}; + let newWindow = Cu.cloneInto(windowToClone, {}); + if (windows.length < 2) { + // We only want to restore the sidebar object if we found + // only one normal window to clone from (which is the one + // we are opening). + this.log('Restoring sidebar data into new window'); + this.#restoreWindowData(newWindow); + } + newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []); - // These are window-specific from the previous window state that - // we don't want to restore into the new window. Otherwise, new - // windows would appear overlapping the previous one, or with - // the same size and position, which should be decided by the - // window manager. - delete newWindow.selected; - delete newWindow.screenX; - delete newWindow.screenY; - delete newWindow.width; - delete newWindow.height; - delete newWindow.sizemode; - delete newWindow.sizemodeBeforeMinimized; - delete newWindow.zIndex; + // These are window-specific from the previous window state that + // we don't want to restore into the new window. Otherwise, new + // windows would appear overlapping the previous one, or with + // the same size and position, which should be decided by the + // window manager. + delete newWindow.selected; + delete newWindow.screenX; + delete newWindow.screenY; + delete newWindow.width; + delete newWindow.height; + delete newWindow.sizemode; + delete newWindow.sizemodeBeforeMinimized; + delete newWindow.zIndex; - const newState = { windows: [newWindow] }; - this.log(`Cloning window with ${newWindow.tabs.length} tabs`); - SessionStoreInternal.restoreWindows(aWindow, newState, { - firstWindow: true, - }); - - lazy.setTimeout(resolvePromise); + const newState = { windows: [newWindow] }; + this.log(`Cloning window with ${newWindow.tabs.length} tabs`); + SessionStoreInternal.restoreWindows(aWindow, newState, { + firstWindow: true, }); + + lazy.setTimeout(resolvePromise); } } diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index 792478b5c..998c79b0d 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -160,8 +160,7 @@ class nsZenWindowSync { } } - /** * Generates a unique tab ID. - * + /** * @returns {string} A unique tab ID. */ get #newTabSyncId() { @@ -248,14 +247,12 @@ class nsZenWindowSync { }); // Wait for the last handler to finish before processing the next event. lastHandlerPromise.then(() => { - try { - this.#handleNextEvent(aEvent); - } finally { + this.#handleNextEvent(aEvent).finally(() => { if (--this.#eventHandlingContext.eventCount === 0) { this.#eventHandlingContext.window = null; } resolveNewPromise(); - } + }); }); } @@ -267,12 +264,28 @@ class nsZenWindowSync { #handleNextEvent(aEvent) { const handler = `on_${aEvent.type}`; if (typeof this[handler] === 'function') { - this[handler](aEvent); + return this[handler](aEvent) || Promise.resolve(); } else { console.warn(`ZenWindowSync: No handler for event type: ${aEvent.type}`); } } + /** + * Ensures that all synced tabs with a given ID has the same permanentKey. + * @param {Object} aTab - The tab to ensure sync for. + */ + #makeSureTabSyncsPermanentKey(aTab) { + if (!aTab.id) { + return; + } + this.#runOnAllWindows(null, (win) => { + const tab = this.#getItemFromWindow(win, aTab.id); + if (tab) { + tab.permanentKey = aTab.linkedBrowser.permanentKey; + } + }); + } + /** * Retrieves a item element from a window by its ID. * @@ -284,6 +297,20 @@ class nsZenWindowSync { return aWindow.document.getElementById(aItemId); } + /** + * Synchronizes a specific attribute from the original item to the target item. + * @param {MozTabbrowserTab|MozTabbrowserTabGroup} aOriginalItem - The original item to copy from. + * @param {MozTabbrowserTab|MozTabbrowserTabGroup} aTargetItem - The target item to copy to. + * @param {string} aAttributeName - The name of the attribute to synchronize. + */ + #maybeSyncAttributeChange(aOriginalItem, aTargetItem, aAttributeName) { + if (aOriginalItem.hasAttribute(aAttributeName)) { + aTargetItem.setAttribute(aAttributeName, aOriginalItem.getAttribute(aAttributeName)); + } else { + aTargetItem.removeAttribute(aAttributeName); + } + } + /** * Synchronizes the icon and label of the target tab with the original tab. * @@ -310,17 +337,13 @@ class nsZenWindowSync { aTargetItem._zenChangeLabelFlag = true; gBrowser._setTabLabel(aTargetItem, aOriginalItem.label); delete aTargetItem._zenChangeLabelFlag; + this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, 'zen-has-static-label'); } else if (gBrowser.isTabGroup(aOriginalItem)) { aTargetItem.label = aOriginalItem.label; } } if (flags & SYNC_FLAG_MOVE && !aTargetItem.hasAttribute('zen-empty-tab')) { - const workspaceId = aOriginalItem.getAttribute('zen-workspace-id'); - if (workspaceId) { - aTargetItem.setAttribute('zen-workspace-id', workspaceId); - } else { - aTargetItem.removeAttribute('zen-workspace-id'); - } + this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, 'zen-workspace-id'); this.#syncItemPosition(aOriginalItem, aTargetItem, aWindow); } if (gBrowser.isTab(aTargetItem)) { @@ -440,8 +463,9 @@ class nsZenWindowSync { * @param {Object} aOtherTab - The tab in the other window. */ async #swapBrowserDocShellsAsync(aOurTab, aOtherTab) { - await this.#styleSwapedBrowsers(aOurTab, aOtherTab); - this.#swapBrowserDocSheellsInner(aOurTab, aOtherTab); + await this.#styleSwapedBrowsers(aOurTab, aOtherTab, () => { + this.#swapBrowserDocSheellsInner(aOurTab, aOtherTab); + }); } /** @@ -512,6 +536,7 @@ class nsZenWindowSync { () => { this.log(`Swapping docshells between windows for tab ${aOurTab.id}`); aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false); + this.#makeSureTabSyncsPermanentKey(aOurTab); }, onClose ); @@ -540,13 +565,13 @@ class nsZenWindowSync { * * @param {Object} aOurTab - The tab in the current window. * @param {Object} aOtherTab - The tab in the other window. - * @param {boolean} onClose - Indicates if the styling is done during a tab close operation. + * @param {Function|undefined} callback - The callback function to execute after styling. */ - async #styleSwapedBrowsers(aOurTab, aOtherTab, onClose = false) { + async #styleSwapedBrowsers(aOurTab, aOtherTab, callback = undefined) { const ourBrowser = aOurTab.linkedBrowser; const otherBrowser = aOtherTab.linkedBrowser; - if (!onClose) { + if (callback) { const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob( aOtherTab.linkedBrowser, { @@ -572,6 +597,7 @@ class nsZenWindowSync { void img.getBoundingClientRect(); await loadPromise; otherBrowser.setAttribute('zen-pseudo-hidden', 'true'); + callback(); } this.#maybeRemovePseudoImageForBrowser(ourBrowser); @@ -649,7 +675,7 @@ class nsZenWindowSync { // We can animate later, whats important is to always stay on the same // process and avoid async operations here to avoid the closed window // being unloaded before the swap is done. - this.#styleSwapedBrowsers(targetTab, tab, /* onClose =*/ true); + this.#styleSwapedBrowsers(targetTab, tab); } } } @@ -660,7 +686,7 @@ class nsZenWindowSync { * @param {Window} aWindow - The window that triggered the event. * @param {Object} aPreviousTab - The previously selected tab. */ - #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) { + async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) { const selectedTab = aWindow.gBrowser.selectedTab; // On some occasions, such as when closing a window, this // function might be called multiple times for the same tab. @@ -676,7 +702,7 @@ class nsZenWindowSync { if (otherTabToShow) { otherTabToShow._zenContentsVisible = true; delete aPreviousTab._zenContentsVisible; - this.#swapBrowserDocShellsAsync(otherTabToShow, aPreviousTab); + await this.#swapBrowserDocShellsAsync(otherTabToShow, aPreviousTab); } } if (selectedTab._zenContentsVisible || selectedTab.hasAttribute('zen-empty-tab')) { @@ -686,7 +712,7 @@ class nsZenWindowSync { selectedTab._zenContentsVisible = true; if (otherSelectedTab) { delete otherSelectedTab._zenContentsVisible; - this.#swapBrowserDocShellsAsync(selectedTab, otherSelectedTab); + await this.#swapBrowserDocShellsAsync(selectedTab, otherSelectedTab); } } @@ -701,6 +727,16 @@ class nsZenWindowSync { this.#syncItemForAllWindows(item, flags); } + /** + * Retrieves the tab state for a given tab. + * + * @param {Object} tab - The tab to retrieve the state for. + * @returns {Object} The tab state. + */ + #getTabState(tab) { + return JSON.parse(lazy.SessionStore.getTabState(tab)); + } + /* Mark: Public API */ shouldLoadTab(aTab) { @@ -721,6 +757,20 @@ class nsZenWindowSync { ); } + setPinnedTabState(aTab) { + const state = this.#getTabState(aTab); + const initialState = { + entry: state.entries[state.index - 1], + image: state.image, + }; + this.#runOnAllWindows(null, (win) => { + const targetTab = this.#getItemFromWindow(win, aTab.id); + if (targetTab) { + targetTab._zenPinnedInitialState = initialState; + } + }); + } + moveTabsToSyncedWorkspace(aWindow, aWorkspaceId) { const tabsToMove = aWindow.gZenWorkspaces.allStoredTabs.filter( (tab) => !tab.hasAttribute('zen-empty-tab') @@ -788,6 +838,7 @@ class nsZenWindowSync { SYNC_FLAG_ICON | SYNC_FLAG_LABEL | SYNC_FLAG_MOVE ); }); + this.#makeSureTabSyncsPermanentKey(tab); } on_ZenTabIconChanged(aEvent) { @@ -811,10 +862,19 @@ class nsZenWindowSync { } on_TabPinned(aEvent) { + const tab = aEvent.target; + this.setPinnedTabState(tab); return this.on_TabMove(aEvent); } on_TabUnpinned(aEvent) { + const tab = aEvent.target; + this.#runOnAllWindows(null, (win) => { + const targetTab = this.#getItemFromWindow(win, tab.id); + if (targetTab) { + delete targetTab._zenPinnedInitialState; + } + }); return this.on_TabMove(aEvent); } @@ -849,7 +909,7 @@ class nsZenWindowSync { } this.#lastFocusedWindow = new WeakRef(window); this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab); - this.#onTabSwitchOrWindowFocus(window); + return this.#onTabSwitchOrWindowFocus(window); } on_TabSelect(aEvent) { @@ -859,7 +919,7 @@ class nsZenWindowSync { } this.#lastSelectedTab = new WeakRef(tab); const previousTab = aEvent.detail.previousTab; - this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab); + return this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab); } on_SSWindowClosing(aEvent) { diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 820dc161d..6cebde5fb 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -95,22 +95,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { _onTabResetPinButton(event, tab) { event.stopPropagation(); - const pin = this._pinsCache?.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id')); - if (!pin) { - return; - } - let userContextId; - if (tab.hasAttribute('usercontextid')) { - userContextId = tab.getAttribute('usercontextid'); - } - const pinnedUrl = Services.io.newURI(pin.url); - const browser = tab.linkedBrowser; - browser.loadURI(pinnedUrl, { - triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({ - userContextId, - }), - }); - this.resetPinChangedUrl(tab); + this._resetTabToStoredState(tab); } get enabled() { @@ -146,6 +131,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } + #getTabState(tab) { + return JSON.parse(SessionStore.getTabState(tab)); + } + async _onTabClick(e) { const tab = e.target?.closest('tab'); if (e.button === 1 && tab) { @@ -169,10 +158,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { async replacePinnedUrlWithCurrent(tab = undefined) { tab ??= TabContextMenu.contextTab; - if (!tab || !tab.pinned || !tab.getAttribute('zen-pin-id')) { + if (!tab || !tab.pinned) { return; } + window.gZenWindowSync.setPinnedTabState(tab); this.resetPinChangedUrl(tab); gZenUIManager.showToast('zen-pinned-tab-replaced'); } @@ -332,35 +322,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } _resetTabToStoredState(tab) { - const id = tab.getAttribute('zen-pin-id'); - if (!id) { - return; - } + const state = this.#getTabState(tab); - const pin = this._pinsCache.find((pin) => pin.uuid === id); - if (!pin) { - return; - } + const initialState = tab._zenPinnedInitialState; - const tabState = SessionStore.getTabState(tab); - const state = JSON.parse(tabState); + // Remove everything except the entry we want to keep + state.entries = [initialState.entry]; - const foundEntryIndex = state.entries?.findIndex((entry) => entry.url === pin.url); - if (foundEntryIndex === -1) { - state.entries = [ - { - url: pin.url, - title: pin.title, - triggeringPrincipal_base64: lazy.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL, - }, - ]; - } else { - // Remove everything except the entry we want to keep - const existingEntry = state.entries[foundEntryIndex]; - existingEntry.title = pin.title; - state.entries = [existingEntry]; - } - state.image = pin.iconUrl || state.image; + state.image = initialState.image; state.index = 0; SessionStore.setTabState(tab, state); @@ -405,16 +374,15 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { if (tab.hasAttribute('zen-workspace-id')) { tab.removeAttribute('zen-workspace-id'); } - if (tab.pinned && tab.hasAttribute('zen-pin-id')) { + if (tab.pinned) { gBrowser.zenHandleTabMove(tab, () => { if (tab.ownerGlobal !== window) { tab = gBrowser.adoptTab(tab, { selectTab: tab.selected, }); tab.setAttribute('zen-essential', 'true'); - } else { - section.appendChild(tab); } + section.appendChild(tab); }); } else { gBrowser.pinTab(tab); @@ -507,8 +475,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } const isVisible = contextTab.pinned && !contextTab.multiselected; const zenAddEssential = document.getElementById('context_zen-add-essential'); - document.getElementById('context_zen-reset-pinned-tab').hidden = - !isVisible || !contextTab.getAttribute('zen-pin-id'); + document.getElementById('context_zen-reset-pinned-tab').hidden = !isVisible; document.getElementById('context_zen-replace-pinned-url-with-current').hidden = !isVisible; zenAddEssential.hidden = contextTab.getAttribute('zen-essential') || !!contextTab.group; zenAddEssential.setAttribute( @@ -646,22 +613,23 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { async onLocationChange(browser) { const tab = gBrowser.getTabForBrowser(browser); - if (!tab || !tab.pinned || tab.hasAttribute('zen-essential') || !this._pinsCache) { - return; - } - const pin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id')); - if (!pin) { + if ( + !tab || + !tab.pinned || + tab.hasAttribute('zen-essential') || + !tab._zenPinnedInitialState?.entry + ) { return; } // Remove # and ? from the URL - const pinUrl = pin.url.split('#')[0]; + const pinUrl = tab._zenPinnedInitialState.entry.url.split('#')[0]; const currentUrl = browser.currentURI.spec.split('#')[0]; // Add an indicator that the pin has been changed if (pinUrl === currentUrl) { this.resetPinChangedUrl(tab); return; } - this.pinHasChangedUrl(tab, pin); + this.pinHasChangedUrl(tab); } resetPinChangedUrl(tab) { @@ -673,7 +641,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { tab.style.removeProperty('--zen-original-tab-icon'); } - pinHasChangedUrl(tab, pin) { + pinHasChangedUrl(tab) { if (tab.hasAttribute('zen-pinned-changed')) { return; } @@ -682,7 +650,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } else { tab.setAttribute('zen-pinned-changed', 'true'); } - tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl?.spec})`); + tab.style.setProperty('--zen-original-tab-icon', `url(${tab._zenPinnedInitialState.image})`); } removeTabContainersDragoverClass(hideIndicator = true) { @@ -799,35 +767,12 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } async updatePinTitle(tab, newTitle, isEdited = true) { - const uuid = tab.getAttribute('zen-pin-id'); - - const browsers = Services.wm.getEnumerator('navigator:browser'); - - // update the label for the same pin across all windows - for (const browser of browsers) { - const tabs = browser.gBrowser.tabs; - // Fix pinned cache for the browser - const browserCache = browser.gZenPinnedTabManager?._pinsCache; - if (browserCache) { - const pin = browserCache.find((pin) => pin.uuid === uuid); - if (pin) { - pin.title = newTitle; - pin.editedTitle = isEdited; - } - } - for (let i = 0; i < tabs.length; i++) { - const tabToEdit = tabs[i]; - if (tabToEdit.getAttribute('zen-pin-id') === uuid && tabToEdit !== tab) { - tabToEdit.removeAttribute('zen-has-static-label'); - if (isEdited) { - gBrowser._setTabLabel(tabToEdit, newTitle); - tabToEdit.setAttribute('zen-has-static-label', 'true'); - } else { - gBrowser.setTabTitle(tabToEdit); - } - break; - } - } + tab.removeAttribute('zen-has-static-label'); + if (isEdited) { + gBrowser._setTabLabel(tab, newTitle); + tab.setAttribute('zen-has-static-label', 'true'); + } else { + gBrowser.setTabTitle(tab); } } @@ -943,20 +888,8 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } - async onTabLabelChanged(tab) { + onTabLabelChanged(tab) { tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { bubbles: true, detail: { tab } })); - if (!this._pinsCache) { - return; - } - // If our current pin in the cache point to about:blank, we need to update the entry - const pin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id')); - if (!pin) { - return; - } - - if (pin.url === 'about:blank' && tab.linkedBrowser.currentURI.spec !== 'about:blank') { - await this.replacePinnedUrlWithCurrent(tab); - } } }