fix: Fixed restoring tabs after restoring windows, b=bug #8699, c=workspaces, tests

This commit is contained in:
Mr. M
2025-06-07 20:35:08 +02:00
parent 10592ed243
commit ebd1910bb8
5 changed files with 119 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..16b171c56081759e81d3efa6c0c7840fbd7902ff 100644
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..d3472a36fc99c488f0fd0fa8cb9c6927c24bdc6d 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
@@ -51,6 +51,15 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..16b171c56081759e81d3efa6c0c7840f
userContextId: state.userContextId,
skipLoad: true,
preferredRemoteType,
@@ -5179,7 +5182,7 @@ var SessionStoreInternal = {
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
let tab = tabbrowser.tabs[i];
- if (homePages.includes(tab.linkedBrowser.currentURI.spec)) {
+ if (homePages.includes(tab.linkedBrowser.currentURI.spec) && !tab.hasAttribute("zen-empty-tab")) {
removableTabs.push(tab);
}
}
@@ -5239,7 +5242,7 @@ var SessionStoreInternal = {
}

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91bf07fdb93 100644
index d5aa64842a35c6697263c63fd3a0571b64b01344..478050fd5202b70ee3191471a017e091a3a87e92 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -413,11 +413,41 @@
@@ -301,7 +301,18 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
});
}
@@ -3560,7 +3642,7 @@
@@ -3517,7 +3599,9 @@
let shouldUpdateForPinnedTabs = false;
/** @type {Map<TabGroupStateData['id'], TabGroupWorkingData>} */
let tabGroupWorkingData = new Map();
-
+ if (this._hasAlreadyInitializedZenSessionStore) {
+ selectTab += 1; // SessionStoreInternal.restoreTabs expects a 1-based index.
+ }
for (const tabGroupData of tabGroupDataList) {
tabGroupWorkingData.set(tabGroupData.id, {
stateData: tabGroupData,
@@ -3560,7 +3644,7 @@
// Add a new tab if needed.
if (!tab) {
let createLazyBrowser =
@@ -310,7 +321,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
let url = "about:blank";
if (tabData.entries?.length) {
@@ -3598,7 +3680,8 @@
@@ -3598,7 +3682,8 @@
skipLoad: true,
preferredRemoteType,
});
@@ -320,7 +331,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (select) {
tabToSelect = tab;
}
@@ -3622,7 +3705,8 @@
@@ -3622,7 +3707,8 @@
// needs calling:
shouldUpdateForPinnedTabs = true;
}
@@ -330,7 +341,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group
@@ -3636,7 +3720,10 @@
@@ -3636,7 +3722,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -342,26 +353,31 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -3684,8 +3771,16 @@
@@ -3684,9 +3773,23 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
- this.selectedTab = tabToSelect;
- this.removeTab(leftoverTab);
+ gZenWorkspaces._tabToRemoveForEmpty = leftoverTab;
+ if (Services.prefs.getBoolPref("zen.workspaces.continue-where-left-off")) {
+ gZenWorkspaces._tabToSelect = selectTab - 1;
+ }
+ if (gZenWorkspaces._initialTab && !gZenVerticalTabsManager._canReplaceNewTab) {
+ gZenWorkspaces._initialTab._shouldRemove = true;
+ if (this._hasAlreadyInitializedZenSessionStore) {
this.selectedTab = tabToSelect;
this.removeTab(leftoverTab);
+ } else {
+ gZenWorkspaces._tabToRemoveForEmpty = leftoverTab;
+ if (Services.prefs.getBoolPref("zen.workspaces.continue-where-left-off")) {
+ gZenWorkspaces._tabToSelect = selectTab - 1;
+ }
+ if (gZenWorkspaces._initialTab && !gZenVerticalTabsManager._canReplaceNewTab) {
+ gZenWorkspaces._initialTab._shouldRemove = true;
+ }
+ }
+ }
+ else {
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
}
+ this._hasAlreadyInitializedZenSessionStore = true;
if (tabs.length > 1 || !tabs[0].selected) {
@@ -3881,7 +3976,7 @@
this._updateTabsAfterInsert();
@@ -3881,7 +3984,7 @@
// Ensure we have an index if one was not provided.
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
// Move the new tab after another tab if needed, to the end otherwise.
@@ -370,7 +386,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -3904,7 +3999,7 @@
@@ -3904,7 +4007,7 @@
) {
elementIndex = Infinity;
} else if (previousTab.visible) {
@@ -379,7 +395,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -3932,14 +4027,14 @@
@@ -3932,14 +4035,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -398,7 +414,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// Prevent a flash of unstyled content by setting up the tab content
// and inherited attributes before appending it (see Bug 1592054):
@@ -3947,7 +4042,7 @@
@@ -3947,7 +4050,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -407,7 +423,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
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);
@@ -3980,6 +4075,7 @@
@@ -3980,6 +4083,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
@@ -415,7 +431,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
TabBarVisibility.update();
}
@@ -4268,6 +4364,9 @@
@@ -4268,6 +4372,9 @@
return;
}
@@ -425,7 +441,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
this.removeTabs(selectedTabs, { telemetrySource });
}
@@ -4520,6 +4619,7 @@
@@ -4520,6 +4627,7 @@
telemetrySource,
} = {}
) {
@@ -433,7 +449,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window.
if (
@@ -4604,6 +4704,7 @@
@@ -4604,6 +4712,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -441,7 +457,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
} catch (e) {
console.error(e);
}
@@ -4641,6 +4742,12 @@
@@ -4641,6 +4750,12 @@
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
}
@@ -454,7 +470,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -4655,7 +4762,9 @@
@@ -4655,7 +4770,9 @@
// frame created for it (for example, by updating the visually selected
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
@@ -465,7 +481,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -4821,7 +4930,7 @@
@@ -4821,7 +4938,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -474,7 +490,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -4845,6 +4954,7 @@
@@ -4845,6 +4962,7 @@
newTab = true;
}
@@ -482,7 +498,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -4885,9 +4995,7 @@
@@ -4885,9 +5003,7 @@
aTab._mouseleave();
if (newTab) {
@@ -493,7 +509,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
} else {
TabBarVisibility.update();
}
@@ -5016,6 +5124,7 @@
@@ -5016,6 +5132,7 @@
this.tabs[i]._tPos = i;
}
@@ -501,7 +517,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (!this._windowIsClosing) {
if (wasPinned) {
this.tabContainer._positionPinnedTabs();
@@ -5230,6 +5339,7 @@
@@ -5230,6 +5347,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -509,7 +525,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
@@ -5242,13 +5352,13 @@
@@ -5242,13 +5360,13 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -525,7 +541,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
);
let tab = this.tabContainer.findNextTab(aTab, {
@@ -5264,7 +5374,7 @@
@@ -5264,7 +5382,7 @@
}
if (tab) {
@@ -534,7 +550,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -5285,7 +5395,7 @@
@@ -5285,7 +5403,7 @@
});
}
@@ -543,7 +559,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
}
_blurTab(aTab) {
@@ -5686,10 +5796,10 @@
@@ -5686,10 +5804,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -556,7 +572,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -5909,7 +6019,7 @@
@@ -5909,7 +6027,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -565,7 +581,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
}
/**
@@ -5986,7 +6096,7 @@
@@ -5986,7 +6104,7 @@
// Don't allow mixing pinned and unpinned tabs.
if (this.isTab(element) && element.pinned) {
@@ -574,7 +590,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -6012,10 +6122,16 @@
@@ -6012,10 +6130,16 @@
this.#handleTabMove(
element,
() => {
@@ -593,7 +609,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
neighbor.after(element);
} else {
@@ -6084,17 +6200,29 @@
@@ -6084,17 +6208,29 @@
targetElement = targetElement.group;
}
}
@@ -627,7 +643,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
if (element.pinned && this.tabContainer.verticalMode) {
return this.tabContainer.verticalPinnedTabsContainer;
}
@@ -6154,7 +6282,7 @@
@@ -6154,7 +6290,7 @@
if (!this.isTab(aTab)) {
throw new Error("Can only move a tab into a tab group");
}
@@ -636,7 +652,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -6248,6 +6376,10 @@
@@ -6248,6 +6384,10 @@
moveActionCallback();
@@ -647,7 +663,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -7145,7 +7277,7 @@
@@ -7145,7 +7285,7 @@
// preventDefault(). It will still raise the window if appropriate.
break;
}
@@ -656,7 +672,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
window.focus();
aEvent.preventDefault();
break;
@@ -8044,6 +8176,7 @@
@@ -8044,6 +8184,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -664,7 +680,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9009,7 +9142,7 @@ var TabContextMenu = {
@@ -9009,7 +9150,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;
@@ -673,7 +689,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..adf3d7c318196a8a02158dfaf9d4d91b
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
@@ -9278,6 +9411,7 @@ var TabContextMenu = {
@@ -9278,6 +9419,7 @@ var TabContextMenu = {
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
});
} else {

View File

@@ -8,5 +8,7 @@ support-files = [
["browser_double_click_newtab.js"]
["browser_overflow_scrollbox.js"]
["browser_issue_8699.js"]
["browser_private_mode.js"]
["browser_private_mode_startup.js"]

View File

@@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Restore_Closed_Tabs() {
const currentTab = gBrowser.selectedTab;
const tabsToClose = [];
for (let i = 0; i < 3; i++) {
const tab = await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
`https://example.com/${i}`,
true,
{ skipAnimation: true }
);
tabsToClose.push(tab);
}
gBrowser.selectedTab = tabsToClose[0];
await TabStateFlusher.flushWindow(window);
Assert.equal(
gBrowser.tabs.length,
5, // 1 initial tab + 3 new tabs
'There should be four tabs after opening three new tabs'
);
gBrowser.removeTabs(tabsToClose);
await TabStateFlusher.flushWindow(window);
await new Promise((resolve) => {
Assert.equal(
gBrowser.selectedTab,
currentTab,
'Current tab should still be selected after closing tabs'
);
Assert.equal(gBrowser.tabs.length, 2, 'There should be one tab left after closing all tabs');
restoreLastClosedTabOrWindowOrSession();
ok(!currentTab.selected, 'Current tab should not be selected after restore');
Assert.equal(
gBrowser.tabs.length,
5, // 1 initial tab + 3 restored tabs
'There should be four tabs after restoring closed tabs'
);
gBrowser.selectedTab = currentTab;
resolve();
});
for (const tab of gBrowser.tabs.filter((t) => t !== currentTab)) {
await BrowserTestUtils.removeTab(tab);
}
});

View File

@@ -12,7 +12,7 @@ add_task(async function test_Private_Mode_Startup() {
setTimeout(() => {
Assert.equal(
privateWindow.gBrowser.tabs.length,
1,
2,
'Private window should start with one tab'
);
resolve();