diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index d2a3802f7..3bbdca913 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 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41963cdfed 100644 +index 2a055f0c5f34f0a2667f659185120c07d38f4e41..1562a49c47f934b3f4372ce8ca74d5c0559b8ae7 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -127,6 +127,9 @@ const TAB_EVENTS = [ @@ -12,15 +12,16 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 ]; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -@@ -196,6 +199,7 @@ ChromeUtils.defineESModuleGetters(lazy, { +@@ -196,6 +199,8 @@ ChromeUtils.defineESModuleGetters(lazy, { TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs", TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", + ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs", ++ ZenWindowSync: "resource:///modules/zen/ZenWindowSync.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "blankURI", () => { -@@ -1261,10 +1265,7 @@ var SessionStoreInternal = { +@@ -1261,10 +1266,7 @@ var SessionStoreInternal = { */ get willAutoRestore() { return ( @@ -32,7 +33,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 ); }, -@@ -1934,6 +1935,9 @@ var SessionStoreInternal = { +@@ -1934,6 +1936,9 @@ var SessionStoreInternal = { case "TabPinned": case "TabUnpinned": case "SwapDocShells": @@ -42,7 +43,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 this.saveStateDelayed(win); break; case "TabGroupCreate": -@@ -2044,6 +2048,10 @@ var SessionStoreInternal = { +@@ -2044,6 +2049,10 @@ var SessionStoreInternal = { this._windows[aWindow.__SSi].isTaskbarTab = true; } @@ -53,7 +54,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 let tabbrowser = aWindow.gBrowser; // add tab change listeners to all already existing tabs -@@ -2131,6 +2139,7 @@ var SessionStoreInternal = { +@@ -2131,6 +2140,7 @@ var SessionStoreInternal = { null, "sessionstore-one-or-no-tab-restored" ); @@ -61,7 +62,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 this._deferredAllWindowsRestored.resolve(); } // this window was opened by _openWindowWithState -@@ -2175,7 +2184,6 @@ var SessionStoreInternal = { +@@ -2175,7 +2185,6 @@ var SessionStoreInternal = { if (closedWindowState) { let newWindowState; if ( @@ -69,7 +70,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 !lazy.SessionStartup.willRestore() ) { // We want to split the window up into pinned tabs and unpinned tabs. -@@ -2239,6 +2247,15 @@ var SessionStoreInternal = { +@@ -2239,6 +2248,15 @@ var SessionStoreInternal = { }); this._shouldRestoreLastSession = false; } @@ -85,7 +86,16 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 if (this._restoreLastWindow && aWindow.toolbar.visible) { // always reset (if not a popup window) -@@ -2491,7 +2508,7 @@ var SessionStoreInternal = { +@@ -2383,7 +2401,7 @@ var SessionStoreInternal = { + + var tabbrowser = aWindow.gBrowser; + +- let browsers = Array.from(tabbrowser.browsers); ++ let browsers = aWindow.gZenWorkspaces.allUsedBrowsers; + + TAB_EVENTS.forEach(function (aEvent) { + tabbrowser.tabContainer.removeEventListener(aEvent, this, true); +@@ -2491,7 +2509,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. @@ -94,16 +104,17 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 this.maybeSaveClosedWindow(winData, isLastWindow); } -@@ -2512,7 +2529,7 @@ var SessionStoreInternal = { +@@ -2512,7 +2530,8 @@ var SessionStoreInternal = { // Save non-private windows if they have at // least one saveable tab or are the last window. - if (!winData.isPrivate && !winData.isTaskbarTab) { ++ lazy.ZenWindowSync.on_WindowCloseAndBrowserFlushed(browsers); + if (!winData.isPrivate && !winData.isTaskbarTab && !winData.isZenUnsynced) { this.maybeSaveClosedWindow(winData, isLastWindow); if (!isLastWindow && winData.closedId > -1) { -@@ -2608,6 +2625,7 @@ var SessionStoreInternal = { +@@ -2608,6 +2627,7 @@ var SessionStoreInternal = { let alreadyStored = winIndex != -1; // If sidebar command is truthy, i.e. sidebar is open, store sidebar settings let shouldStore = hasSaveableTabs || isLastWindow; @@ -111,7 +122,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 if (shouldStore && !alreadyStored) { let index = this._closedWindows.findIndex(win => { -@@ -3408,7 +3426,7 @@ var SessionStoreInternal = { +@@ -3408,7 +3428,7 @@ var SessionStoreInternal = { if (!isPrivateWindow && tabState.isPrivate) { return; } @@ -120,7 +131,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 return; } -@@ -4129,6 +4147,12 @@ var SessionStoreInternal = { +@@ -4129,6 +4149,12 @@ var SessionStoreInternal = { Math.min(tabState.index, tabState.entries.length) ); tabState.pinned = false; @@ -133,7 +144,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 if (inBackground === false) { aWindow.gBrowser.selectedTab = newTab; -@@ -4565,6 +4589,8 @@ var SessionStoreInternal = { +@@ -4565,6 +4591,8 @@ var SessionStoreInternal = { // Append the tab if we're opening into a different window, tabIndex: aSource == aTargetWindow ? pos : Infinity, pinned: state.pinned, @@ -142,7 +153,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 userContextId: state.userContextId, skipLoad: true, preferredRemoteType, -@@ -5414,7 +5440,7 @@ var SessionStoreInternal = { +@@ -5414,7 +5442,7 @@ var SessionStoreInternal = { for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) { let tab = tabbrowser.tabs[i]; @@ -151,7 +162,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 removableTabs.push(tab); } } -@@ -5525,7 +5551,7 @@ var SessionStoreInternal = { +@@ -5525,7 +5553,7 @@ var SessionStoreInternal = { // collect the data for all windows for (ix in this._windows) { @@ -160,7 +171,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 // window data is still in _statesToRestore continue; } -@@ -5668,11 +5694,12 @@ var SessionStoreInternal = { +@@ -5668,11 +5696,12 @@ var SessionStoreInternal = { } let tabbrowser = aWindow.gBrowser; @@ -174,7 +185,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 // update the internal state data for this window for (let tab of tabs) { if (tab == aWindow.FirefoxViewHandler.tab) { -@@ -5683,6 +5710,9 @@ var SessionStoreInternal = { +@@ -5683,6 +5712,9 @@ var SessionStoreInternal = { tabsData.push(tabData); } @@ -184,7 +195,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 // update tab group state for this window winData.groups = []; for (let tabGroup of aWindow.gBrowser.tabGroups) { -@@ -5695,7 +5725,7 @@ var SessionStoreInternal = { +@@ -5695,7 +5727,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) { @@ -193,7 +204,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 winData.title = tabbrowser.tabs[0].label; } winData.selected = selectedIndex; -@@ -5810,8 +5840,8 @@ var SessionStoreInternal = { +@@ -5810,8 +5842,8 @@ var SessionStoreInternal = { // selectTab represents. let selectTab = 0; if (overwriteTabs) { @@ -204,7 +215,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 selectTab = Math.min(selectTab, winData.tabs.length); } -@@ -5833,6 +5863,7 @@ var SessionStoreInternal = { +@@ -5833,6 +5865,7 @@ var SessionStoreInternal = { if (overwriteTabs) { for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) { if (!tabbrowser.tabs[i].selected) { @@ -212,7 +223,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 tabbrowser.removeTab(tabbrowser.tabs[i]); } } -@@ -5866,6 +5897,12 @@ var SessionStoreInternal = { +@@ -5866,6 +5899,12 @@ var SessionStoreInternal = { savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id) ); } @@ -225,7 +236,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 // Move the originally open tabs to the end. if (initialTabs) { -@@ -6419,6 +6456,25 @@ var SessionStoreInternal = { +@@ -6419,6 +6458,25 @@ var SessionStoreInternal = { // Most of tabData has been restored, now continue with restoring // attributes that may trigger external events. @@ -251,7 +262,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 if (tabData.pinned) { tabbrowser.pinTab(tab); -@@ -7343,7 +7399,7 @@ var SessionStoreInternal = { +@@ -7343,7 +7401,7 @@ var SessionStoreInternal = { let groupsToSave = new Map(); for (let tIndex = 0; tIndex < window.tabs.length; ) { @@ -260,7 +271,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..78108f7fa0250ba74dc0afdefb67cf41 // Adjust window.selected if (tIndex + 1 < window.selected) { window.selected -= 1; -@@ -7358,7 +7414,7 @@ var SessionStoreInternal = { +@@ -7358,7 +7416,7 @@ var SessionStoreInternal = { ); // We don't want to increment tIndex here. continue; diff --git a/src/browser/components/sessionstore/TabStateFlusher-sys-mjs.patch b/src/browser/components/sessionstore/TabStateFlusher-sys-mjs.patch new file mode 100644 index 000000000..a39d1b174 --- /dev/null +++ b/src/browser/components/sessionstore/TabStateFlusher-sys-mjs.patch @@ -0,0 +1,13 @@ +diff --git a/browser/components/sessionstore/TabStateFlusher.sys.mjs b/browser/components/sessionstore/TabStateFlusher.sys.mjs +index ed7953e41e8d61695c04cd9fc40f9a9d40d56d77..6d5730e428a5e9bf5a07ce329eae66b101c15df4 100644 +--- a/browser/components/sessionstore/TabStateFlusher.sys.mjs ++++ b/browser/components/sessionstore/TabStateFlusher.sys.mjs +@@ -101,7 +101,7 @@ var TabStateFlusherInternal = { + */ + flushWindow(window) { + let promises = []; +- for (let browser of window.gBrowser.browsers) { ++ for (let browser of window.gZenWorkspaces.allUsedBrowsers) { + if (window.gBrowser.getTabForBrowser(browser).linkedPanel) { + promises.push(this.flush(browser)); + } diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index 292c3c366..5d7afb08f 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -92,6 +92,19 @@ class nsZenWindowSync { */ #lastSelectedTab = null; + /** + * A list containing all swaped tabs with their respective browser permanent + * keys. This is used in between SSWindowClosing and WindowCloseAndBrowserFlushed. + * + * When we close windows, there's a small chance that browsers havent't been flushed + * yet when we try to move active tabs to other windows. This map allows us to + * retrieve the correct tab entries from the cache in order to avoid losing + * tab history. + * + * @type {Map} + */ + #swapedTabsTabEntriesForWC = new Map(); + /** * Iterator that yields all currently opened browser windows. * (Might miss the most recent one.) @@ -108,6 +121,23 @@ class nsZenWindowSync { }, }; + /** + * @returns {Array} A list of all currently opened browser windows. + */ + get #browserWindowsList() { + return Array.from(this.#browserWindows); + } + + /** + * @returns {Window|null} The first opened browser window, or null if none exist. + */ + get #firstSyncedWindow() { + for (let window of this.#browserWindows) { + return window; + } + return null; + } + init() { if (!lazy.gWindowSyncEnabled) { return; @@ -160,7 +190,7 @@ class nsZenWindowSync { (hasUnsyncedArg || (typeof aWindow.arguments[0] === "string" && aWindow.arguments.length > 1 && - !![...this.#browserWindows].length)) + !!this.#browserWindowsList.length)) ) { this.log("Not syncing new window due to unsynced argument or existing synced windows"); aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true"); @@ -543,7 +573,7 @@ class nsZenWindowSync { async #swapBrowserDocShellsAsync(aOurTab, aOtherTab) { this.#maybeFlushTabState(aOtherTab); await this.#styleSwapedBrowsers(aOurTab, aOtherTab, () => { - this.#swapBrowserDocSheellsInner(aOurTab, aOtherTab); + this.#swapBrowserDocShellsInner(aOurTab, aOtherTab); }); } @@ -594,7 +624,7 @@ class nsZenWindowSync { * @param {boolean} options.focus - Indicates if the tab should be focused after the swap. * @param {boolean} options.onClose - Indicates if the swap is done during a tab close operation. */ - #swapBrowserDocSheellsInner(aOurTab, aOtherTab, { focus = true, onClose = false } = {}) { + #swapBrowserDocShellsInner(aOurTab, aOtherTab, { focus = true, onClose = false } = {}) { // Can't swap between chrome and content processes. if (aOurTab.linkedBrowser.isRemoteBrowser != aOtherTab.linkedBrowser.isRemoteBrowser) { return false; @@ -768,12 +798,11 @@ class nsZenWindowSync { * * @param {Window} aWindow - The window to move active tabs from. */ - #moveAllActiveTabsToOtherWindows(aWindow) { - const mostRecentWindow = [...this.#browserWindows].find((win) => win !== aWindow); + #moveAllActiveTabsToOtherWindowsForClose(aWindow) { + const mostRecentWindow = this.#browserWindowsList.find((win) => win !== aWindow); if (!mostRecentWindow || !aWindow.gZenWorkspaces) { return; } - lazy.TabStateFlusher.flushWindow(aWindow); const activeTabsOnClosedWindow = aWindow.gZenWorkspaces.allStoredTabs.filter( (tab) => tab._zenContentsVisible ); @@ -781,11 +810,16 @@ class nsZenWindowSync { const targetTab = this.getItemFromWindow(mostRecentWindow, tab.id); if (targetTab) { this.log(`Moving active tab ${tab.id} to most recent window on close`); - this.#swapBrowserDocSheellsInner(targetTab, tab, { + targetTab._zenContentsVisible = true; + if (!tab.linkedBrowser) { + continue; + } + delete tab._zenContentsVisible; + this.#swapBrowserDocShellsInner(targetTab, tab, { focus: targetTab.selected, onClose: true, }); - targetTab._zenContentsVisible = true; + this.#swapedTabsTabEntriesForWC.set(tab.linkedBrowser.permanentKey, targetTab); // 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. @@ -930,7 +964,7 @@ class nsZenWindowSync { (tab) => !tab.hasAttribute("zen-empty-tab") ); const selectedTab = aWindow.gBrowser.selectedTab; - let win = [...this.#browserWindows][0]; + let win = this.#firstSyncedWindow; const moveAllTabsToWindow = async (allowSelected = false) => { const { gBrowser, gZenWorkspaces } = win; win.focus(); @@ -1091,7 +1125,35 @@ class nsZenWindowSync { window.removeEventListener(eventName, this); } delete window.gZenWindowSync; - this.#moveAllActiveTabsToOtherWindows(window); + this.#moveAllActiveTabsToOtherWindowsForClose(window); + } + + on_WindowCloseAndBrowserFlushed(aBrowsers) { + if (this.#swapedTabsTabEntriesForWC.size === 0) { + return; + } + for (let browser of aBrowsers) { + const tab = this.#swapedTabsTabEntriesForWC.get(browser.permanentKey); + if (tab) { + let win = tab.ownerGlobal; + this.log(`Finalizing swap for tab ${tab.id} on window close`); + lazy.TabStateCache.update( + tab.linkedBrowser.permanentKey, + lazy.TabStateCache.get(browser.permanentKey) + ); + let tabData = this.#getTabEntriesFromCache(tab); + let activePageData = tabData.entries[tabData.index - 1] || null; + + // If the page has a title, set it. When doing a swap and we still didn't + // flush the tab state, the title might not be correct. + if (activePageData) { + win.gBrowser.setInitialTabTitle(tab, activePageData.title, { + isContentTitle: activePageData.title && activePageData.title != activePageData.url, + }); + } + } + } + this.#swapedTabsTabEntriesForWC.clear(); } on_TabGroupCreate(aEvent) {