tor-browser

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

commit cb0e9ab3be4e5d8310155a978463a59f8c49493b
parent 39952d088a8c1a636a93469db97c52d4cdb1eeda
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Wed,  1 Oct 2025 14:49:18 +0000

Bug 1991410 - Don't apply intersection root rect clip to targets inside popups. r=tnikkel,layout-reviewers

Since they don't get clipped by the viewport.

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

Diffstat:
Mdom/base/DOMIntersectionObserver.cpp | 7++++---
Mtoolkit/content/tests/chrome/window_largemenu.xhtml | 32+++++++++++++++++++++++++++++++-
2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp @@ -556,9 +556,10 @@ static Maybe<nsRect> ComputeTheIntersection( intersectionRect = intersectionRectRelativeToRoot.EdgeInclusiveIntersection( *aRemoteDocumentVisibleRect); - if (intersectionRect.isNothing()) { - return Nothing(); - } + } else if (aTarget->HasAnyStateBits(NS_FRAME_IN_POPUP)) { + // Popups don't get clipped to the viewport, so avoid applying the root + // intersection rect, see bug 1991410. + intersectionRect = Some(intersectionRectRelativeToRoot); } else { intersectionRect = intersectionRectRelativeToRoot.EdgeInclusiveIntersection(aRootBounds); diff --git a/toolkit/content/tests/chrome/window_largemenu.xhtml b/toolkit/content/tests/chrome/window_largemenu.xhtml @@ -93,13 +93,41 @@ async function nextTest() popup.openPopupAtScreen(100, y, false); } +async function assertItemsOcclusionState(popup) { + info("Checking intersection state"); + let items = popup.querySelectorAll("menuitem"); + isnot(items.length, 0, "Should have items"); + let {promise, resolve} = Promise.withResolvers(); + let io = new IntersectionObserver(function(entries) { + resolve(entries) + }); + for (let item of items) { + io.observe(item); + } + let entries = await promise; + io.disconnect(); + is(entries.length, items.length, "Should have an entry per element"); + + let scrollbox = popup.scrollBox.scrollbox; + let scrollBoxRect = scrollbox.getBoundingClientRect(); + let borderTop = parseFloat(getComputedStyle(scrollbox).borderTop); + let borderBottom = parseFloat(getComputedStyle(scrollbox).borderBottom); + + for (let entry of entries) { + let itemRect = entry.boundingClientRect; + // Importantly, we don't consider the page's viewport. + let shouldIntersect = itemRect.bottom >= scrollBoxRect.top + borderTop && itemRect.top <= scrollBoxRect.bottom - borderBottom; + is(entry.isIntersecting, shouldIntersect, `${entry.target.outerHTML} intersection state matches`); + } +} + async function popupShown() { // This is needed for overflow events to run. await nextFrame(); startTest(); } -function startTest() +async function startTest() { if (gTests[gTestIndex] == "menu movement") return testPopupMovement(); @@ -114,6 +142,8 @@ function startTest() var scrollbox = popup.scrollBox.scrollbox; var expectedScrollPos = 0; + await assertItemsOcclusionState(popup); + info(`${gTests[gTestIndex]}: ${JSON.stringify(rect)} | ${screen.width}x${screen.height} | ${gScreenY}`); if (gTestIndex == 0) { // the popup should be in the center of the screen