tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 284de9eac57ac641b3a0fc8ce0b1d903a3458249
parent e992d5f0c52f8716f2e4ebef134d81d757333e9f
Author: Jonathan Sudiaman <jsudiaman@mozilla.com>
Date:   Mon,  6 Oct 2025 14:14:31 +0000

Bug 1986944 - Add splitters to resize each content area r=emilio,desktop-theme-reviewers,tabbrowser-reviewers,sclements

Differential Revision: https://phabricator.services.mozilla.com/D264887

Diffstat:
Mbrowser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js | 47+++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/themes/shared/tabbrowser/content-area.css | 37++++++++++++++++++++++++++++++++-----
Mtoolkit/content/widgets/tabbox.js | 42++++++++++++++++++++++++++++++++++--------
Mtoolkit/content/xul.css | 1+
4 files changed, 114 insertions(+), 13 deletions(-)

diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js @@ -43,6 +43,14 @@ async function checkSplitViewPanelVisible(tab, isVisible) { } } +function dragSplitter(deltaX, splitter) { + AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false }); + EventUtils.synthesizeMouseAtCenter(splitter, { type: "mousedown" }); + EventUtils.synthesizeMouse(splitter, deltaX, 0, { type: "mousemove" }); + EventUtils.synthesizeMouse(splitter, 0, 0, { type: "mouseup" }); + AccessibilityUtils.resetEnv(); +} + add_task(async function test_splitViewCreateAndAddTabs() { let tab1 = BrowserTestUtils.addTab(gBrowser, "about:blank"); let tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank"); @@ -177,3 +185,42 @@ add_task(async function test_split_view_preserves_multiple_pairings() { splitView1.close(); splitView2.close(); }); + +add_task(async function test_resize_split_view_panels() { + const tab1 = await addTabAndLoadBrowser(); + const tab2 = await addTabAndLoadBrowser(); + await BrowserTestUtils.switchTab(gBrowser, tab1); + + info("Activate split view."); + const splitView = gBrowser.addTabSplitView([tab1, tab2]); + const { tabpanels } = gBrowser; + await BrowserTestUtils.waitForMutationCondition( + tabpanels, + { childList: true }, + () => tabpanels.querySelector(".split-view-splitter") + ); + await BrowserTestUtils.waitForMutationCondition( + tabpanels.splitViewSplitter, + { attributes: true }, + () => BrowserTestUtils.isVisible(tabpanels.splitViewSplitter) + ); + + info("Resize split view panels."); + const leftPanel = document.getElementById(tab1.linkedPanel); + const rightPanel = document.getElementById(tab2.linkedPanel); + const originalLeftWidth = leftPanel.getBoundingClientRect().width; + const originalRightWidth = rightPanel.getBoundingClientRect().width; + dragSplitter(-100, tabpanels.splitViewSplitter); + Assert.less( + leftPanel.getBoundingClientRect().width, + originalLeftWidth, + "Left panel is smaller." + ); + Assert.greater( + rightPanel.getBoundingClientRect().width, + originalRightWidth, + "Right panel is larger." + ); + + splitView.close(); +}); diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css @@ -109,6 +109,11 @@ min-height: 0; } +.browserSidebarContainer { + position: absolute; + inset: 0; +} + .sidebar-browser-stack { flex: 1; } @@ -125,6 +130,7 @@ padding: 0; color-scheme: unset; background: var(--tabpanel-background-color); + display: flex; &[pendingpaint] { background-image: url("chrome://global/skin/icons/pendingpaint.png"); @@ -145,12 +151,33 @@ -moz-user-focus: none !important; } - .split-view-panel[column="0"] { - grid-column: 1; - } + &[splitview] { + --panel-min-width: 140px; + + .split-view-panel { + position: static; + flex: 1; + min-width: var(--panel-min-width); + max-width: calc(100% - var(--panel-min-width)); + } + + .split-view-panel[column="0"] { + order: 0; + } + + .split-view-splitter { + order: 1; + flex: none; + } + + .split-view-panel[column="1"] { + order: 2; + } - .split-view-panel[column="1"] { - grid-column: 2; + /* Panels with a custom width shouldn't grow. */ + .split-view-panel[width] { + flex: none; + } } } diff --git a/toolkit/content/widgets/tabbox.js b/toolkit/content/widgets/tabbox.js @@ -263,6 +263,13 @@ */ #splitViewPanels = []; + /** + * The splitter placed in between Split View panels. + * + * @type {XULElement} + */ + #splitViewSplitter = null; + constructor() { super(); this._tabbox = null; @@ -278,6 +285,17 @@ return (this._tabbox = this.closest("tabbox")); } + get splitViewSplitter() { + if (!this.#splitViewSplitter) { + const splitter = document.createXULElement("splitter"); + splitter.className = "split-view-splitter"; + splitter.setAttribute("resizebefore", "sibling"); + splitter.setAttribute("resizeafter", "none"); + this.#splitViewSplitter = splitter; + } + return this.#splitViewSplitter; + } + /** * nsIDOMXULRelatedElement */ @@ -329,12 +347,11 @@ } for (const [i, panel] of newPanels.entries()) { const panelEl = document.getElementById(panel); - if (panelEl) { - panelEl.classList.add("split-view-panel"); - panelEl.setAttribute("column", i); - } + panelEl?.classList.add("split-view-panel"); + panelEl?.setAttribute("column", i); } this.#splitViewPanels = newPanels; + this.#isSplitViewActive = !!newPanels.length; } get splitViewPanels() { @@ -350,16 +367,25 @@ */ removePanelFromSplitView(panel, updateArray = true) { const panelEl = document.getElementById(panel); - if (panelEl) { - panelEl.classList.remove("split-view-panel"); - panelEl.removeAttribute("column"); - } + panelEl?.classList.remove("split-view-panel"); + panelEl?.removeAttribute("column"); if (updateArray) { const index = this.#splitViewPanels.indexOf(panel); if (index !== -1) { this.#splitViewPanels.splice(index, 1); } } + this.#isSplitViewActive = !!this.#splitViewPanels.length; + } + + set #isSplitViewActive(isActive) { + this.toggleAttribute("splitview", isActive); + this.splitViewSplitter.hidden = !isActive; + if (isActive) { + // Place splitter after first panel, so that it can be resized. + const firstPanel = document.getElementById(this.splitViewPanels[0]); + firstPanel?.after(this.#splitViewSplitter); + } } } diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css @@ -462,6 +462,7 @@ deck > *|*:not(:-moz-native-anonymous) { tabpanels > .deck-selected, tabpanels > .split-view-panel, +tabpanels > .split-view-splitter, deck > .deck-selected { -moz-subtree-hidden-only-visually: 0; visibility: inherit;