commit 2c941048716c99e7f7f01c0815815a800d265026
parent e1becde5a12a97840e2d0f673b88c1c64e018a16
Author: Narcis Beleuzu <nbeleuzu@mozilla.com>
Date: Tue, 14 Oct 2025 18:04:51 +0300
Revert "Bug 1986946 - Add context menu entry point for split view, and handle the single tab and multi-selected cases r=tabbrowser-reviewers,fluent-reviewers,jsudiaman,flod,sthompson,sclements,desktop-theme-reviewers,sfoster" for bc failure on browser_tab_splitview.js
This reverts commit 0f7b9bd615a8bda5995a0e58a71728ab76c27d58.
Diffstat:
9 files changed, 37 insertions(+), 453 deletions(-)
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
@@ -2633,9 +2633,6 @@ pref("browser.tabs.fadeOutExplicitlyUnloadedTabs", true);
// they are explicitly unloaded) are faded out in the tab bar.
pref("browser.tabs.fadeOutUnloadedTabs", false);
-// Whether tabs can be "split" or displayed side by side at once.
-pref("browser.tabs.splitView.enabled", false);
-
// If true, unprivileged extensions may use experimental APIs on
// nightly and developer edition.
pref("extensions.experiments.enabled", false);
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
@@ -32,13 +32,6 @@
data-lazy-l10n-id="tab-context-ungroup-tab"
data-l10n-args='{"groupCount": 1}'
hidden="true"/>
- <menuitem id="context_moveTabToSplitView"
- class="badge-new"
- hidden="true"/>
- <menuitem id="context_separateSplitView"
- class="badge-new"
- data-lazy-l10n-id="tab-context-separate-split-view"
- hidden="true"/>
<menuseparator/>
<menuitem id="context_reloadTab" data-lazy-l10n-id="reload-tab"/>
<menuitem id="context_reloadSelectedTabs" data-lazy-l10n-id="reload-tabs" hidden="true"/>
diff --git a/browser/base/content/main-popupset.js b/browser/base/content/main-popupset.js
@@ -24,12 +24,6 @@ document.addEventListener(
case "context_ungroupTab":
TabContextMenu.ungroupTabs();
break;
- case "context_moveTabToSplitView":
- TabContextMenu.moveTabsToSplitView();
- break;
- case "context_separateSplitView":
- TabContextMenu.unsplitTabs();
- break;
case "context_reloadTab":
gBrowser.reloadTab(TabContextMenu.contextTab);
break;
@@ -527,9 +521,6 @@ document.addEventListener(
case "bhTooltip":
BookmarksEventHandler.fillInBHTooltip(event.target, event);
break;
- case "tabContextMenu":
- TabContextMenu.addNewBadge();
- break;
}
});
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
@@ -374,7 +374,10 @@
}
get group() {
- return this.closest("tab-group");
+ if (this.parentElement?.tagName == "tab-group") {
+ return this.parentElement;
+ }
+ return null;
}
get splitview() {
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
@@ -3225,12 +3225,9 @@
return;
}
- for (let i = splitview.tabs.length - 1; i >= 0; i--) {
- this.#handleTabMove(splitview.tabs[i], () =>
- gBrowser.tabContainer.insertBefore(
- splitview.tabs[i],
- splitview.nextElementSibling
- )
+ for (const tab of splitview.tabs) {
+ this.#handleTabMove(tab, () =>
+ gBrowser.tabContainer.insertBefore(tab, splitview.nextElementSibling)
);
}
splitview.remove();
@@ -7411,7 +7408,7 @@
}
getTabPids(tab) {
- if (!tab?.linkedBrowser) {
+ if (!tab.linkedBrowser) {
return [];
}
@@ -9493,34 +9490,6 @@ var TabContextMenu = {
contextUngroupTab.hidden = true;
}
- // Split View
- let splitViewEnabled = Services.prefs.getBoolPref(
- "browser.tabs.splitView.enabled",
- false
- );
- let contextMoveTabToNewSplitView = document.getElementById(
- "context_moveTabToSplitView"
- );
- let contextSeparateSplitView = document.getElementById(
- "context_separateSplitView"
- );
- let hasSplitViewTab = this.contextTabs.some(tab => tab.splitview);
- contextMoveTabToNewSplitView.hidden = !splitViewEnabled || hasSplitViewTab;
- contextSeparateSplitView.hidden = !splitViewEnabled || !hasSplitViewTab;
- if (splitViewEnabled) {
- contextMoveTabToNewSplitView.removeAttribute("data-l10n-id");
- contextMoveTabToNewSplitView.setAttribute(
- "data-l10n-id",
- this.contextTabs.length < 2
- ? "tab-context-add-split-view"
- : "tab-context-open-in-split-view"
- );
-
- let pinnedTabs = this.contextTabs.filter(t => t.pinned);
- contextMoveTabToNewSplitView.disabled =
- this.contextTabs.length > 2 || pinnedTabs.length;
- }
-
// Only one of Reload_Tab/Reload_Selected_Tabs should be visible.
document.getElementById("context_reloadTab").hidden = this.multiselected;
document.getElementById("context_reloadSelectedTabs").hidden =
@@ -9944,55 +9913,6 @@ var TabContextMenu = {
gBrowser.ungroupTab(this.contextTabs[i]);
}
},
-
- moveTabsToSplitView() {
- let insertBefore = this.contextTabs.includes(gBrowser.selectedTab)
- ? gBrowser.selectedTab
- : this.contextTabs[0];
- let tabsToAdd = this.contextTabs;
-
- // Ensure selected tab is always first in split view
- const selectedTabIndex = tabsToAdd.indexOf(gBrowser.selectedTab);
- if (selectedTabIndex > -1 && selectedTabIndex != 0) {
- const [removed] = tabsToAdd.splice(selectedTabIndex, 1);
- tabsToAdd.unshift(removed);
- }
-
- let newTab = null;
- if (this.contextTabs.length < 2) {
- // Open new tab to split with context tab
- newTab = gBrowser.addTrustedTab(BROWSER_NEW_TAB_URL);
- tabsToAdd = [this.contextTabs[0], newTab];
- }
-
- gBrowser.addTabSplitView(tabsToAdd, {
- insertBefore,
- });
-
- if (newTab) {
- gBrowser.selectedTab = newTab;
- }
- },
-
- unsplitTabs() {
- const splitviews = new Set(
- this.contextTabs.map(tab => tab.splitview).filter(Boolean)
- );
- splitviews.forEach(splitview => gBrowser.unsplitTabs(splitview));
- },
-
- addNewBadge() {
- let badgeNewMenuItems = document.querySelectorAll(
- "#tabContextMenu menuitem.badge-new"
- );
-
- badgeNewMenuItems.forEach(badgedMenuItem => {
- badgedMenuItem.setAttribute(
- "badge",
- gBrowser.tabLocalization.formatValueSync("tab-context-badge-new")
- );
- });
- },
};
ChromeUtils.defineESModuleGetters(TabContextMenu, {
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
@@ -186,7 +186,32 @@
tab.setAttribute("aria-setsize", tabCount);
});
this.hasActiveTab = hasActiveTab;
- this.#updateOverflowLabel();
+
+ // When a group containing the active tab is collapsed,
+ // the overflow count displays the number of additional tabs
+ // in the group adjacent to the active tab.
+ let overflowCountLabel = this.overflowContainer.querySelector(
+ ".tab-group-overflow-count"
+ );
+ if (tabCount > 1) {
+ gBrowser.tabLocalization
+ .formatValue("tab-group-overflow-count", {
+ tabCount: tabCount - 1,
+ })
+ .then(result => (overflowCountLabel.textContent = result));
+ gBrowser.tabLocalization
+ .formatValue("tab-group-overflow-count-tooltip", {
+ tabCount: tabCount - 1,
+ })
+ .then(result => {
+ overflowCountLabel.setAttribute("tooltiptext", result);
+ overflowCountLabel.setAttribute("aria-description", result);
+ });
+ this.toggleAttribute("hasmultipletabs", true);
+ } else {
+ overflowCountLabel.textContent = "";
+ this.toggleAttribute("hasmultipletabs", false);
+ }
}
for (const mutation of mutations) {
for (const addedNode of mutation.addedNodes) {
@@ -396,51 +421,11 @@
}
}
- #updateOverflowLabel() {
- // When a group containing the active tab is collapsed,
- // the overflow count displays the number of additional tabs
- // in the group adjacent to the active tab.
- let overflowCountLabel = this.overflowContainer.querySelector(
- ".tab-group-overflow-count"
- );
- let tabs = this.tabs;
- let tabCount = tabs.length;
- const overflowOffset =
- this.hasActiveTab && gBrowser.selectedTab.splitview ? 2 : 1;
-
- if (tabCount > 1) {
- this.toggleAttribute("hasmultipletabs", true);
- } else {
- overflowCountLabel.textContent = "";
- this.toggleAttribute("hasmultipletabs", false);
- }
-
- gBrowser.tabLocalization
- .formatValue("tab-group-overflow-count", {
- tabCount: tabCount - overflowOffset,
- })
- .then(result => (overflowCountLabel.textContent = result));
- gBrowser.tabLocalization
- .formatValue("tab-group-overflow-count-tooltip", {
- tabCount: tabCount - overflowOffset,
- })
- .then(result => {
- overflowCountLabel.setAttribute("tooltiptext", result);
- overflowCountLabel.setAttribute("aria-description", result);
- });
- }
-
/**
* @returns {MozTabbrowserTab[]}
*/
get tabs() {
- let childrenArray = Array.from(this.children);
- for (let i = childrenArray.length - 1; i >= 0; i--) {
- if (childrenArray[i].tagName == "tab-split-view-wrapper") {
- childrenArray.splice(i, 1, ...childrenArray[i].tabs);
- }
- }
- return childrenArray.filter(node => node.matches("tab"));
+ return Array.from(this.children).filter(node => node.matches("tab"));
}
/**
@@ -637,8 +622,6 @@
if (previousTab.group === this) {
this.#updateTabAriaHidden(previousTab);
}
-
- this.#updateOverflowLabel();
}
/**
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js
@@ -4,7 +4,7 @@
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
- set: [["sidebar.verticalTabs", true]],
+ set: [["sidebar.verticalTabs", false]],
});
});
@@ -13,7 +13,6 @@ registerCleanupFunction(async function () {
set: [
["sidebar.verticalTabs", false],
["sidebar.revamp", false],
- ["browser.tabs.splitView.enabled", false],
],
});
});
@@ -52,43 +51,6 @@ function dragSplitter(deltaX, splitter) {
AccessibilityUtils.resetEnv();
}
-/**
- * @param {MozTabbrowserTab} tab
- * @param {function(splitViewMenuItem: Element, unsplitMenuItem: Element) => Promise<void>} callback
- */
-const withTabMenu = async function (tab, callback) {
- const tabContextMenu = document.getElementById("tabContextMenu");
- Assert.equal(
- tabContextMenu.state,
- "closed",
- "context menu is initially closed"
- );
- const contextMenuShown = BrowserTestUtils.waitForPopupEvent(
- tabContextMenu,
- "shown"
- );
-
- EventUtils.synthesizeMouseAtCenter(
- tab,
- { type: "contextmenu", button: 2 },
- window
- );
- await contextMenuShown;
-
- const moveTabToNewSplitViewItem = document.getElementById(
- "context_moveTabToSplitView"
- );
- const unsplitTabItem = document.getElementById("context_separateSplitView");
-
- let contextMenuHidden = BrowserTestUtils.waitForPopupEvent(
- tabContextMenu,
- "hidden"
- );
- await callback(moveTabToNewSplitViewItem, unsplitTabItem);
- tabContextMenu.hidePopup();
- return contextMenuHidden;
-};
-
add_task(async function test_splitViewCreateAndAddTabs() {
let tab1 = BrowserTestUtils.addTab(gBrowser, "about:blank");
let tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank");
@@ -277,222 +239,3 @@ add_task(async function test_resize_split_view_panels() {
splitView.close();
});
-
-add_task(async function test_tabGroupContextMenuMoveTabsToNewGroup() {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.tabs.splitView.enabled", true]],
- });
- const tab1 = await addTabAndLoadBrowser();
- const tab2 = await addTabAndLoadBrowser();
- const tab3 = await addTabAndLoadBrowser();
- let tabs = [tab1, tab2, tab3];
-
- // Click the first tab in our test split view to make sure the default tab at the
- // start of the tab strip is deselected
- EventUtils.synthesizeMouseAtCenter(tab1, {});
-
- tabs.forEach(t => {
- EventUtils.synthesizeMouseAtCenter(
- t,
- { ctrlKey: true, metaKey: true },
- window
- );
- });
-
- let tabToClick = tab3;
- await withTabMenu(
- tabToClick,
- async (moveTabToNewSplitViewItem, unsplitTabItem) => {
- await BrowserTestUtils.waitForMutationCondition(
- moveTabToNewSplitViewItem,
- { attributes: true },
- async () => {
- return (
- !moveTabToNewSplitViewItem.hidden &&
- moveTabToNewSplitViewItem.disabled
- );
- },
- "moveTabToNewSplitViewItem is visible and disabled"
- );
- await BrowserTestUtils.waitForMutationCondition(
- unsplitTabItem,
- { attributes: true },
- async () => {
- return unsplitTabItem.hidden;
- },
- "unsplitTabItem is hidden"
- );
- }
- );
-
- // Test opening split view from 2 non-consecutive tabs
- let tabContainer = document.getElementById("tabbrowser-arrowscrollbox");
- let tab3Index = Array.from(tabContainer.children).indexOf(tab3);
- EventUtils.synthesizeMouseAtCenter(tab3, {});
- tabToClick = tab3;
-
- [tabs[0], tabs[2]].forEach(t => {
- gBrowser.addToMultiSelectedTabs(t);
- ok(t.multiselected, "added tab to mutliselection");
- });
-
- await withTabMenu(
- tabToClick,
- async (moveTabToNewSplitViewItem, unsplitTabItem) => {
- await BrowserTestUtils.waitForMutationCondition(
- moveTabToNewSplitViewItem,
- { attributes: true },
- async () => {
- return (
- !moveTabToNewSplitViewItem.hidden &&
- !moveTabToNewSplitViewItem.disabled
- );
- },
- "moveTabToNewSplitViewItem is visible and not disabled"
- );
- await BrowserTestUtils.waitForMutationCondition(
- unsplitTabItem,
- { attributes: true },
- async () => {
- return unsplitTabItem.hidden;
- },
- "unsplitTabItem is hidden"
- );
-
- moveTabToNewSplitViewItem.click();
- }
- );
-
- let splitview = tab1.splitview;
- [tab1, tab3].forEach((t, idx) => {
- Assert.equal(t.splitview, splitview, `tabs[${idx}] is in split view`);
- });
- Assert.strictEqual(
- Array.from(tabContainer.children).indexOf(splitview),
- tab3Index - 1,
- "Non-concecutive tabs have been added to split view and moved to active tab location"
- );
-
- splitview.unsplitTabs();
-
- // Test adding consecutive tabs to a new split view
-
- EventUtils.synthesizeMouseAtCenter(tab1, {});
-
- [tab1, tab2].forEach(t => {
- EventUtils.synthesizeMouseAtCenter(
- t,
- { ctrlKey: true, metaKey: true },
- window
- );
- });
-
- tabToClick = tab2;
- await withTabMenu(
- tabToClick,
- async (moveTabToNewSplitViewItem, unsplitTabItem) => {
- await BrowserTestUtils.waitForMutationCondition(
- moveTabToNewSplitViewItem,
- { attributes: true },
- async () => {
- return (
- !moveTabToNewSplitViewItem.hidden &&
- !moveTabToNewSplitViewItem.disabled
- );
- },
- "moveTabToNewSplitViewItem is visible and not disabled"
- );
- await BrowserTestUtils.waitForMutationCondition(
- unsplitTabItem,
- { attributes: true },
- async () => {
- return unsplitTabItem.hidden;
- },
- "unsplitTabItem is hidden"
- );
-
- moveTabToNewSplitViewItem.click();
- }
- );
-
- splitview = tab1.splitview;
-
- Assert.ok(tab1.splitview, "tab is in split view");
- [tab1, tab2].forEach((t, idx) => {
- Assert.equal(t.splitview, splitview, `tabs[${idx}] is in split view`);
- });
-
- // Test unsplitting tabs using context menu
-
- await withTabMenu(
- tabToClick,
- async (moveTabToNewSplitViewItem, unsplitTabItem) => {
- await BrowserTestUtils.waitForMutationCondition(
- moveTabToNewSplitViewItem,
- { attributes: true },
- async () => {
- return moveTabToNewSplitViewItem.hidden;
- },
- "moveTabToNewSplitViewItem is hidden"
- );
- await BrowserTestUtils.waitForMutationCondition(
- unsplitTabItem,
- { attributes: true },
- async () => {
- return !unsplitTabItem.hidden;
- },
- "unsplitTabItem is visible"
- );
-
- unsplitTabItem.click();
- }
- );
-
- // Test adding split view with one tab and new tab
-
- tabToClick = tab1;
- EventUtils.synthesizeMouseAtCenter(tab1, {});
-
- await withTabMenu(
- tabToClick,
- async (moveTabToNewSplitViewItem, unsplitTabItem) => {
- await BrowserTestUtils.waitForMutationCondition(
- moveTabToNewSplitViewItem,
- { attributes: true },
- async () => {
- return (
- !moveTabToNewSplitViewItem.hidden &&
- !moveTabToNewSplitViewItem.disabled
- );
- },
- "moveTabToNewSplitViewItem is visible and not disabled"
- );
- await BrowserTestUtils.waitForMutationCondition(
- unsplitTabItem,
- { attributes: true },
- async () => {
- return unsplitTabItem.hidden;
- },
- "unsplitTabItem is hidden"
- );
-
- moveTabToNewSplitViewItem.click();
- }
- );
-
- splitview = tab1.splitview;
-
- Assert.equal(tab1.splitview, splitview, `tab1 is in split view`);
- Assert.equal(
- splitview.tabs[1],
- gBrowser.selectedTab,
- "New tab is active in split view"
- );
- Assert.ok(!tab2.splitview, "tab2 is not in split view");
- Assert.ok(!tab3.splitview, "tab3 is not in split view");
-
- splitview.close();
- while (gBrowser.tabs.length > 1) {
- BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
- }
-});
diff --git a/browser/locales/en-US/browser/tabbrowser.ftl b/browser/locales/en-US/browser/tabbrowser.ftl
@@ -372,18 +372,4 @@ tab-group-context-open-saved-group-in-new-window =
# Displayed within the tooltip on tabs inside of a tab split view
tabbrowser-tab-label-tab-split-view = Split view
-# Open a new tab next to the current tab and display their contents side by side
-tab-context-add-split-view =
- .label = Add Split View
- .accesskey = t
-# Display the two selected tabs' contents side by side
-tab-context-open-in-split-view =
- .label = Open in Split View
- .accesskey = t
-# Separate the two split view tabs and display the tabs and their contents as normal
-tab-context-separate-split-view =
- .label = Separate Split View
- .accesskey = t
-tab-context-badge-new = New
-
##
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
@@ -1045,37 +1045,6 @@
/* Split View */
-tab-group > tab-split-view-wrapper {
- #tabbrowser-tabs[orient="vertical"] & {
- margin-inline-start: var(--space-xxsmall);
- margin-block: calc(var(--space-xsmall) * -1);
-
- .tabbrowser-tab:last-child {
- .tab-group-line {
- display: none;
- }
- }
- }
-
- @container vertical-tabs-container (max-width: 210px) {
- #tabbrowser-tabs[orient="vertical"] & {
- margin-inline-start: 0;
-
- .tabbrowser-tab:last-child {
- .tab-group-line {
- display: flex;
- }
- }
- }
- }
-
- #tabbrowser-tabs[orient="horizontal"] & > .tab-split-view-container {
- margin-inline: 0;
- padding-block-end: 0;
- padding-inline: 0;
- }
-}
-
#tabbrowser-tabs .tab-split-view-container {
display: grid;
grid-template-columns: 50% 50%;
@@ -1264,8 +1233,7 @@ tab-group {
}
#tabbrowser-tabs[orient="vertical"] &[movingtabgroup][collapsed] > .tabbrowser-tab[visuallyselected],
- #tabbrowser-tabs[orient="vertical"] &[collapsed] > .tabbrowser-tab:not([visuallyselected]),
- #tabbrowser-tabs[orient="vertical"] &[collapsed] > tab-split-view-wrapper:not([hasactivetab]) {
+ #tabbrowser-tabs[orient="vertical"] &[collapsed] > .tabbrowser-tab:not([visuallyselected]) {
display: none;
}