tor-browser

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

commit 5d796ce5231b43eb6c06778a3e6073c7d67c53aa
parent d6720705613bfbd71ecbe41201619e93782a7015
Author: Kelly Cochrane <kcochrane@mozilla.com>
Date:   Thu, 11 Dec 2025 13:57:23 +0000

Bug 1997878 - Preserve width of backgrounded split view panels to prevent issues with PiP when switching tabs r=desktop-theme-reviewers,tabbrowser-reviewers,jules,jsudiaman,emilio

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

Diffstat:
Mbrowser/components/tabbrowser/content/tabbrowser.js | 13+++++++++++++
Mbrowser/components/tabbrowser/content/tabsplitview.js | 25++++++++++++-------------
Mbrowser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js | 13+------------
Mbrowser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_footer.js | 8+++++++-
Mbrowser/themes/shared/tabbrowser/content-area.css | 17+++++++++++------
Mtoolkit/content/widgets/tabbox.js | 15++++++++-------
Mtoolkit/content/xul.css | 2+-
7 files changed, 53 insertions(+), 40 deletions(-)

diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js @@ -3297,6 +3297,19 @@ } /** + * Toggle split view active attribute + * + * @param {boolean} isActive + * @param {MozTabbrowserTab[]} tabs + */ + setIsSplitViewActive(isActive, tabs) { + for (const tab of tabs) { + this.tabpanels.setSplitViewPanelActive(isActive, tab.linkedPanel); + } + this.tabpanels.isSplitViewActive = gBrowser.selectedTab.splitview; + } + + /** * Ensures the split view footer exists for the given tab. * * @param {MozTabbrowserTab} tab diff --git a/browser/components/tabbrowser/content/tabsplitview.js b/browser/components/tabbrowser/content/tabsplitview.js @@ -39,9 +39,6 @@ /** @type {MozTabbrowserTab[]} */ #tabs = []; - /** @type {boolean} */ - #activated = false; - /** * @returns {boolean} */ @@ -147,26 +144,26 @@ /** * Show all Split View tabs in the content area. */ - #activate() { + #activate(skipShowPanels = false) { updateUrlbarButton.arm(); - if (this.#activated) { - return; + if (!skipShowPanels) { + gBrowser.showSplitViewPanels(this.#tabs); } - gBrowser.showSplitViewPanels(this.#tabs); this.container.dispatchEvent( new CustomEvent("TabSplitViewActivate", { detail: { tabs: this.#tabs, splitview: this }, bubbles: true, }) ); - this.#activated = true; } /** * Remove Split View tabs from the content area. */ - #deactivate() { - gBrowser.hideSplitViewPanels(this.#tabs); + #deactivate(skipHidePanels = false) { + if (!skipHidePanels) { + gBrowser.hideSplitViewPanels(this.#tabs); + } updateUrlbarButton.arm(); this.container.dispatchEvent( new CustomEvent("TabSplitViewDeactivate", { @@ -174,7 +171,6 @@ bubbles: true, }) ); - this.#activated = false; } /** @@ -202,6 +198,7 @@ } if (this.hasActiveTab) { this.#activate(); + gBrowser.setIsSplitViewActive(true, this.#tabs); } } @@ -210,6 +207,7 @@ */ unsplitTabs() { gBrowser.unsplitTabs(this); + gBrowser.setIsSplitViewActive(false, this.#tabs); } /** @@ -244,10 +242,11 @@ */ on_TabSelect(event) { this.hasActiveTab = event.target.splitview === this; + gBrowser.setIsSplitViewActive(this.hasActiveTab, this.#tabs); if (this.hasActiveTab) { - this.#activate(); + this.#activate(true); } else { - this.#deactivate(); + this.#deactivate(true); } } } diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview.js @@ -28,19 +28,8 @@ async function checkSplitViewPanelVisible(tab, isVisible) { await BrowserTestUtils.waitForMutationCondition( panel, { attributes: true }, - () => panel.classList.contains("split-view-panel") == isVisible + () => panel.classList.contains("split-view-panel-active") == isVisible ); - if (isVisible) { - Assert.ok( - gBrowser.splitViewBrowsers.includes(tab.linkedBrowser), - "Split view panel is active." - ); - } else { - Assert.ok( - !gBrowser.splitViewBrowsers.includes(tab.linkedBrowser), - "Split view panel is inactive." - ); - } } function dragSplitter(deltaX, splitter) { diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_footer.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_splitview_footer.js @@ -29,12 +29,18 @@ async function setupSplitView() { info("Add tabs into an active split view."); await BrowserTestUtils.switchTab(gBrowser, tabs[0]); const splitView = gBrowser.addTabSplitView(tabs); + const tabpanels = document.getElementById("tabbrowser-tabpanels"); + await BrowserTestUtils.waitForMutationCondition( + tabpanels, + { attributes: true }, + () => tabpanels.hasAttribute("splitview") + ); for (const tab of tabs) { const tabPanel = document.getElementById(tab.linkedPanel); await BrowserTestUtils.waitForMutationCondition( tabPanel, { attributes: true }, - () => tabPanel.classList.contains("split-view-panel") + () => tabPanel.classList.contains("split-view-panel-active") ); } diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css @@ -156,16 +156,21 @@ -moz-user-focus: none !important; } - &[splitview] { + .split-view-panel { --panel-min-width: 140px; + min-width: var(--panel-min-width); + max-width: calc(100% - var(--panel-min-width)); + width: 49.4%; + } - .split-view-panel { - position: relative; - flex: 1; - min-width: var(--panel-min-width); - max-width: calc(100% - var(--panel-min-width)); + &[splitview] { + .split-view-panel.split-view-panel-active { margin: var(--space-xsmall); + flex: 1; + position: relative; + width: unset; } + /* Ensure any dialogs are clipped in an inactive/not-selected panel. */ :root:not([inDOMFullscreen]) & > .split-view-panel:not(.deck-selected) { overflow: clip; diff --git a/toolkit/content/widgets/tabbox.js b/toolkit/content/widgets/tabbox.js @@ -367,10 +367,6 @@ } set splitViewPanels(newPanels) { - const oldPanels = this.#splitViewPanels; - for (const panel of oldPanels) { - this.removePanelFromSplitView(panel, false); - } for (const [i, panel] of newPanels.entries()) { const panelEl = document.getElementById(panel); panelEl?.classList.add("split-view-panel"); @@ -381,7 +377,7 @@ } } this.#splitViewPanels = newPanels; - this.#isSplitViewActive = !!newPanels.length; + this.isSplitViewActive = !!newPanels.length; } get splitViewPanels() { @@ -409,10 +405,10 @@ this.#splitViewPanels.splice(index, 1); } } - this.#isSplitViewActive = !!this.#splitViewPanels.length; + this.isSplitViewActive = !!this.#splitViewPanels.length; } - set #isSplitViewActive(isActive) { + set isSplitViewActive(isActive) { this.toggleAttribute("splitview", isActive); this.splitViewSplitter.hidden = !isActive; if (isActive) { @@ -421,6 +417,11 @@ firstPanel?.after(this.#splitViewSplitter); } } + + setSplitViewPanelActive(isActive, panel) { + const panelEl = document.getElementById(panel); + panelEl?.classList.toggle("split-view-panel-active", isActive); + } } MozXULElement.implementCustomInterface(MozTabpanels, [ diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css @@ -461,7 +461,7 @@ deck > *|*:not(:-moz-native-anonymous) { } tabpanels > .deck-selected, -tabpanels > .split-view-panel, +tabpanels > .split-view-panel-active, tabpanels > .split-view-splitter, deck > .deck-selected { -moz-subtree-hidden-only-visually: 0;