tor-browser

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

commit 96eccf5af235e2f592e45fda4e79e6194448fc74
parent fc6f1cb437ff6188b95f248cac91278db55cd0f4
Author: Kelly Cochrane <kcochrane@mozilla.com>
Date:   Fri, 12 Dec 2025 14:45:36 +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/sessionstore/test/marionette/manifest.toml | 2--
Dbrowser/components/sessionstore/test/marionette/test_restore_split_view.py | 97-------------------------------------------------------------------------------
Mbrowser/components/tabbrowser/content/tabbrowser.js | 13+++++++++++++
Mbrowser/components/tabbrowser/content/tabsplitview.js | 19+++++++++++++------
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+-
9 files changed, 54 insertions(+), 132 deletions(-)

diff --git a/browser/components/sessionstore/test/marionette/manifest.toml b/browser/components/sessionstore/test/marionette/manifest.toml @@ -20,8 +20,6 @@ skip-if = [ ["test_restore_sidebar_automatic.py"] -["test_restore_split_view.py"] - ["test_restore_windows_after_close_last_tabs.py"] skip-if = [ "os == 'mac' && os_version == '10.15' && arch == 'x86_64'", diff --git a/browser/components/sessionstore/test/marionette/test_restore_split_view.py b/browser/components/sessionstore/test/marionette/test_restore_split_view.py @@ -1,97 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 0.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/0.0/. - -import os -import sys - -# add this directory to the path -sys.path.append(os.path.dirname(__file__)) - -from session_store_test_case import SessionStoreTestCase - - -def inline(title): - return f"data:text/html;charset=utf-8,<html><head><title>{title}</title></head><body></body></html>" - - -class TestSessionRestoreSplitView(SessionStoreTestCase): - """ - Test the interactions between Session Restore and Split View. - """ - - def setUp(self): - super().setUp( - startup_page=1, - include_private=False, - restore_on_demand=True, - test_windows=set( - [ - ( - inline("Tab 1"), - inline("Tab 2"), - inline("Tab 3"), - ), - ] - ), - ) - - def test_add_inactive_tabs_to_split_view(self): - """ - When we restart with some tabs, we defer loading and setting up those - tabs until they become active. - - Ensure that adding these tabs to a split view triggers that. - """ - self.assertEqual( - len(self.marionette.chrome_window_handles), - 1, - msg="Should have 1 window open.", - ) - - # There are currently three tabs open. - # Switch to the first one, and then restart. - self.marionette.execute_script("gBrowser.selectTabAtIndex(0)") - self.marionette.restart() - self.marionette.set_context("chrome") - - # After restart: - # Tab 1 is active. - # Tab 2 & Tab 3 are inactive, and haven't been activated yet. - self.assertEqual( - self.marionette.execute_script("return gBrowser.tabs.length"), - 3, - msg="Should have 3 tabs open.", - ) - self.assertEqual( - self.marionette.execute_script( - "return gBrowser.tabContainer.selectedIndex" - ), - 0, - msg="First tab should be selected.", - ) - - # Create a split view with the inactive tabs (Tab 2 & Tab 3). - # Select Tab 2 to activate the split view. - # Wait for both tabs in the split view to finish restoring. - self.marionette.execute_async_script( - """ - let [resolve] = arguments; - gBrowser.addTabSplitView([gBrowser.tabs[1], gBrowser.tabs[2]]); - let promiseTabsRestored = new Promise(resolve => { - let tabsRemaining = 2; - function handleTabRestored() { - if (!--tabsRemaining) { - gBrowser.tabContainer.removeEventListener( - "SSTabRestored", - handleTabRestored - ); - resolve(); - } - } - gBrowser.tabContainer.addEventListener("SSTabRestored", handleTabRestored); - }); - gBrowser.selectTabAtIndex(1); - promiseTabsRestored.then(resolve); - """ - ) diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js @@ -3318,6 +3318,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 @@ -164,9 +164,11 @@ /** * Show all Split View tabs in the content area. */ - #activate() { + #activate(skipShowPanels = false) { updateUrlbarButton.arm(); - gBrowser.showSplitViewPanels(this.#tabs); + if (!skipShowPanels) { + gBrowser.showSplitViewPanels(this.#tabs); + } this.container.dispatchEvent( new CustomEvent("TabSplitViewActivate", { detail: { tabs: this.#tabs, splitview: this }, @@ -178,8 +180,10 @@ /** * 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", { @@ -242,6 +246,7 @@ } if (this.hasActiveTab) { this.#activate(); + gBrowser.setIsSplitViewActive(true, this.#tabs); } } @@ -250,6 +255,7 @@ */ unsplitTabs() { gBrowser.unsplitTabs(this); + gBrowser.setIsSplitViewActive(false, this.#tabs); } /** @@ -284,10 +290,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; const selectedPanel = this.selectedPanel; @@ -426,6 +422,11 @@ // offsets it. this.selectedPanel = selectedPanel; } + + 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;