diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index aa71a21a0..01d9f1062 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -1131,7 +1131,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature { const dropElementGroup = dropElement?.isZenFolder ? dropElement : dropElement?.group; const isSplitGroup = dropElement?.group?.hasAttribute('split-view-group'); - let firstGroupElem = dropElementGroup.querySelector('.zen-tab-group-start').nextElementSibling; + let firstGroupElem = + dropElementGroup?.querySelector('.zen-tab-group-start')?.nextElementSibling; if (gBrowser.isTabGroup(firstGroupElem)) firstGroupElem = firstGroupElem.labelElement; const isInMiddleZone = diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index fedf747fe..1010002dd 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -247,7 +247,7 @@ export class nsZenSessionManager { */ restoreNewWindow(aWindow, SessionStoreInternal, resolvePromise) { if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) { - return; + return resolvePromise(); } this.log('Restoring new window with Zen session data'); const state = lazy.SessionStore.getCurrentState(true); diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index 555ca93bd..72944ad0e 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -36,8 +36,8 @@ const EVENTS = [ 'TabGroupRemoved', 'TabGroupMoved', - //'ZenTabRemovedFromSplit', - //'ZenSplitViewTabsSplit', + 'ZenTabRemovedFromSplit', + 'ZenSplitViewTabsSplit', 'TabSelect', @@ -242,10 +242,14 @@ class nsZenWindowSync { */ #handleNextEvent(aEvent) { const handler = `on_${aEvent.type}`; - if (typeof this[handler] === 'function') { - return this[handler](aEvent) || Promise.resolve(); - } else { - console.warn(`ZenWindowSync: No handler for event type: ${aEvent.type}`); + try { + if (typeof this[handler] === 'function') { + return this[handler](aEvent) || Promise.resolve(); + } else { + throw new Error(`No handler for event type: ${aEvent.type}`); + } + } catch (e) { + return Promise.reject(e); } } @@ -483,7 +487,7 @@ class nsZenWindowSync { // Restore the listeners for the swapped in tab. if (!onClose) { - tabListener = new otherTabBrowser.zenTabProgressListener(aTab, otherBrowser, false, false); + tabListener = new otherTabBrowser.zenTabProgressListener(aTab, otherBrowser, true, false); otherTabBrowser._tabListeners.set(aTab, tabListener); const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL; @@ -527,10 +531,13 @@ class nsZenWindowSync { this.log(`Swapping docshells between windows for tab ${aOurTab.id}`); aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false); this.#makeSureTabSyncsPermanentKey(aOurTab); + if (!aOtherTab.hasAttribute('busy')) { + aOurTab.removeAttribute('busy'); + } }, onClose ); - const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture']; + const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture', 'busy']; // swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted', // but we need to manually remove some attributes from the other tab. for (let attr of kAttributesToRemove) { @@ -676,11 +683,12 @@ class nsZenWindowSync { * * @param {Window} aWindow - The window that triggered the event. * @param {Object} aPreviousTab - The previously selected tab. + * @param {boolean} ignoreSameTab - Indicates if the same tab should be ignored. */ - async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) { + async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null, ignoreSameTab = false) { // On some occasions, such as when closing a window, this // function might be called multiple times for the same tab. - if (aWindow.gBrowser.selectedTab === this.#lastSelectedTab) { + if (aWindow.gBrowser.selectedTab === this.#lastSelectedTab && !ignoreSameTab) { return; } if (aPreviousTab?._zenContentsVisible) { @@ -786,11 +794,13 @@ class nsZenWindowSync { continue; } newTab._zenContentsVisible = true; + gBrowser.setTabTitle(newTab); gZenWorkspaces.moveTabToWorkspace(newTab, aWorkspaceId); } } if (success) { aWindow.close(); + win.gBrowser.selectedTab.linkedBrowser.focus(); } }; if (!win) { @@ -809,13 +819,11 @@ class nsZenWindowSync { on_TabOpen(aEvent) { const tab = aEvent.target; const window = tab.ownerGlobal; - if (tab.selected) { - tab._zenContentsVisible = true; - } if (tab.id) { // This tab was opened as part of a sync operation. return; } + tab._zenContentsVisible = true; tab.id = this.#newTabSyncId; this.#runOnAllWindows(window, (win) => { const newTab = win.gBrowser.addTrustedTab('about:blank', { @@ -1002,12 +1010,16 @@ class nsZenWindowSync { .map((tab) => this.#getItemFromWindow(win, tab.id)) .filter(Boolean); if (otherWindowTabs.length > 0 && win.gZenViewSplitter) { - const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid'); + const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid', -1); if (group) { - group.id = tabGroup.id; + let otherTabGroup = group.tabs[0].group; + otherTabGroup.id = tabGroup.id; + this.#syncItemWithOriginal(aEvent.target, otherTabGroup, win, SYNC_FLAG_MOVE); } } }); + + return this.#onTabSwitchOrWindowFocus(window, null, /* ignoreSameTab = */ true); } } diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index 4fc621f49..8fd03aa9a 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -1084,8 +1084,12 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { * * @param {Tab[]} tabs - The tabs to split. * @param {string|undefined} gridType - The type of grid layout. + * @param {number} initialIndex - The index of the initially active tab. + * use -1 to avoid selecting any tab. + * @return {object|undefined} The split view data or undefined if the split was not performed. */ splitTabs(tabs, gridType, initialIndex = 0) { + const tabIndexToUse = Math.max(0, initialIndex); return this.#withoutSplitViewTransition(() => { // TODO: Add support for splitting essential tabs tabs = tabs.filter((t) => !t.hidden && !t.hasAttribute('zen-empty-tab')); @@ -1095,7 +1099,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { const existingSplitTab = tabs.find((tab) => tab.splitView); if (existingSplitTab) { - this._moveTabsToContainer(tabs, tabs[initialIndex]); + this._moveTabsToContainer(tabs, tabs[tabIndexToUse]); const groupIndex = this._data.findIndex((group) => group.tabs.includes(existingSplitTab)); const group = this._data[groupIndex]; const gridTypeChange = gridType && group.gridType !== gridType; @@ -1150,8 +1154,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { layoutTree: this.calculateLayoutTree(tabs, gridType), }; this._data.push(splitData); - if (!this._sessionRestoring) { - window.gBrowser.selectedTab = tabs[initialIndex] ?? tabs[0]; + if (!this._sessionRestoring && initialIndex >= 0) { + window.gBrowser.selectedTab = tabs[tabIndexToUse] ?? tabs[0]; } // Add tabs to the split view group