tor-browser

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

commit e0722384b722bd5d13103eb93ccd0ccfbad6e49b
parent d7a6ef996dd69a3c32a6f95d24bd8fae8df64355
Author: Jeremy Swinarton <jswinarton@mozilla.com>
Date:   Tue, 28 Oct 2025 19:12:07 +0000

Bug 1980036: Stop THP/TGHP from suppressing panel open requests from the other panel element r=sthompson,tabbrowser-reviewers

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

Diffstat:
Mbrowser/components/tabbrowser/content/tab-hover-preview.mjs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mbrowser/components/tabbrowser/test/browser/tabs/browser.toml | 4----
2 files changed, 69 insertions(+), 16 deletions(-)

diff --git a/browser/components/tabbrowser/content/tab-hover-preview.mjs b/browser/components/tabbrowser/content/tab-hover-preview.mjs @@ -186,6 +186,9 @@ class TabPanel extends Panel { /** @type {DOMElement|null} */ #thumbnailElement; + /** @type {TabHoverPanelSet} */ + #panelSet; + constructor(panel, panelSet) { super(); @@ -202,7 +205,7 @@ class TabPanel extends Panel { ); this.panelElement = panel; - this.panelSet = panelSet; + this.#panelSet = panelSet; this.win = this.panelElement.ownerGlobal; @@ -244,12 +247,12 @@ class TabPanel extends Panel { ) { this.#updatePreview(); } - this.panelSet.panelOpener.execute(() => { - if (!this.panelSet.shouldActivate()) { + this.#panelSet.panelOpener.execute(() => { + if (!this.#panelSet.shouldActivate()) { return; } this.panelElement.openPopup(this.#tab, this.popupOptions); - }); + }, this); this.win.addEventListener("TabSelect", this); this.panelElement.addEventListener("popupshowing", this); this.#tab.addEventListener("TabAttrModified", this); @@ -273,7 +276,8 @@ class TabPanel extends Panel { this.panelElement.removeEventListener("popupshowing", this); this.win.removeEventListener("TabSelect", this); this.panelElement.hidePopup(); - this.panelSet.panelOpener.setZeroDelay(); + this.#panelSet.panelOpener.clear(this); + this.#panelSet.panelOpener.setZeroDelay(); } getPrettyURI(uri) { @@ -503,7 +507,7 @@ class TabGroupPanel extends Panel { return; } this.#doOpenPanel(); - }); + }, this); } /** @@ -550,6 +554,7 @@ class TabGroupPanel extends Panel { } this.panelElement.hidePopup(); + this.#panelSet.panelOpener.clear(this); this.#panelSet.panelOpener.setZeroDelay(); } @@ -696,6 +701,12 @@ class TabPreviewPanelTimedFunction { /** @type {boolean} */ #useZeroDelay; + /** @type {function(): void | null} */ + #target; + + /** @type {TabPanel} */ + #from; + constructor(zeroDelayTime, win) { XPCOMUtils.defineLazyPreferenceGetter( this, @@ -708,9 +719,38 @@ class TabPreviewPanelTimedFunction { this.#timer = null; this.#useZeroDelay = false; + + this.#target = null; + this.#from = null; } - execute(target) { + /** + * Execute a function after a delay, according to the following rules: + * - By default, execute the function after the time specified by `ui.tooltip.delay_ms`. + * - If a timer is already active, the timer will not be restarted, but the + * function to be executed will be set to the one from the most recent + * call (see notes below) + * - If the zero delay has been set with `setZeroDelay`, the function will + * invoke immediately + * + * Multiple calls to `execute` within the delay will not invoke the function + * each time. The original delay will be preserved (i.e. the function will + * execute after `ui.tooltip.delay_ms` from the first call) but the function + * that is executed may be updated by subsequent calls to execute. This + * ensures that if the panel we want to open changes (e.g. if a user hovers + * over a tab, then quickly switches to a tab group before the delay + * expires), the delay is not restarted, which would cause a longer than + * usual time to open. + * + * @param {function(): void | null} target + * The function to execute + * @param {TabPanel} from + * The calling panel + */ + execute(target, from) { + this.#target = target; + this.#from = from; + if (this.delayActive) { return; } @@ -721,22 +761,39 @@ class TabPreviewPanelTimedFunction { this.#timer = this.#win.setTimeout( () => { this.#timer = null; - target(); + this.#target(); }, this.#useZeroDelay ? 0 : this._prefPreviewDelay ); } - clear() { - if (this.#timer) { + /** + * Clear the timer, if it is active, for example when a user moves off a panel. + * This has the effect of suppressing the delayed function execution. + * + * @param {TabPanel} from + * The calling panel. This must be the same as the panel that most recently + * called `execute`. If it is not, the call will be ignored. This is + * necessary to prevent, e.g., the tab hover panel from inadvertently + * cancelling the opening of the tab group hover panel in cases where the + * user quickly hovers between tabs and tab groups before the panel fully + * opens. + */ + clear(from) { + if (from == this.#from && this.#timer) { this.#win.clearTimeout(this.#timer); this.#timer = null; + this.#from = null; } } + /** + * Temporarily suppress the delay mechanism. + * + * The delay will automatically reactivate after a set interval, which is + * configured by the constructor. + */ setZeroDelay() { - this.clear(); - if (this.#useZeroDelay) { return; } diff --git a/browser/components/tabbrowser/test/browser/tabs/browser.toml b/browser/components/tabbrowser/test/browser/tabs/browser.toml @@ -580,10 +580,6 @@ tags = "vertical-tabs" ["browser_tab_preview.js"] tags = "vertical-tabs" -skip-if = [ - "os == 'linux' && os_version == '24.04' && processor == 'x86_64' && swgl", # Bug 1980036 - "os == 'mac' && os_version == '10.15' && processor == 'x86_64'", # Bug 1980036 -] ["browser_tab_splitview.js"]