tor-browser

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

commit f332ba95d3b0e36ea8ed118c22aae713be785c8d
parent b0768a1f51b4f61e102c24e2b695812e33f24457
Author: Hiroyuki Ikezoe <hikezoe.birchill@mozilla.com>
Date:   Tue,  2 Dec 2025 02:03:30 +0000

Bug 1999429 - Respect block and inline options for visual scrollIntoView. r=layout-reviewers,dholbert

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

Diffstat:
Agfx/layers/apz/test/mochitest/helper_scrollIntoView_block_center.html | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/layers/apz/test/mochitest/test_group_scrollIntoView.html | 4++++
Mlayout/base/PresShell.cpp | 40++++++++++++++++++++++++++++------------
Mlayout/base/PresShell.h | 3+++
4 files changed, 92 insertions(+), 12 deletions(-)

diff --git a/gfx/layers/apz/test/mochitest/helper_scrollIntoView_block_center.html b/gfx/layers/apz/test/mochitest/helper_scrollIntoView_block_center.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <title>Tests that Element.scrollIntoView respects block option for visual scroll</title> + <script src="apz_test_native_event_utils.js"></script> + <script src="apz_test_utils.js"></script> + <script src="/tests/SimpleTest/paint_listener.js"></script> +</head> +<style> +body { + margin: 0px; + padding: 0px; +} +#target { + position: fixed; + width: 100%; + height: 10px; + top: 50%; + bottom: 50%; + background-color: green; +} +</style> +<body> +<div id="target"></div> +<script> +async function test() { + is(window.scrollY, 0, "The initial scroll offset should be 0"); + is(visualViewport.scale, 2.0, "The document should get scaled by 2.0"); + is(visualViewport.pageTop, 0, "The initial visual viewport pageTop should be 0"); + + const target = document.querySelector("#target"); + const targetRect = target.getBoundingClientRect(); + + const scrollPromise = + new Promise(resolve => visualViewport.addEventListener("scroll", resolve)); + target.scrollIntoView({ block: "center" }); + await scrollPromise; + + await promiseApzFlushedRepaints(); + + // Calculate the expected position. + const centerOfTarget = targetRect.y + targetRect.height * 0.5; + const expected = centerOfTarget - visualViewport.height * 0.5; + + isfuzzy(visualViewport.offsetTop, expected, 1.0, + "The position:fixed element is aligned at the center of visual viewport"); +} + +SpecialPowers.getDOMWindowUtils(window).setResolutionAndScaleTo(2.0); +waitUntilApzStable() +.then(test) +.then(subtestDone, subtestFailed); +</script> +</body> +</html> diff --git a/gfx/layers/apz/test/mochitest/test_group_scrollIntoView.html b/gfx/layers/apz/test/mochitest/test_group_scrollIntoView.html @@ -31,6 +31,10 @@ var subtests = [ "prefs": [...prefs, ["layout.scroll_fixed_content_into_view_visually", true]] }, + {"file": "helper_scrollIntoView_block_center.html", + "prefs": [...prefs, + ["layout.scroll_fixed_content_into_view_visually", true]] + }, ]; if (isApzEnabled()) { diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -3741,9 +3741,10 @@ static bool NeedToVisuallyScroll(const nsSize& aLayoutViewportSize, return true; } -void PresShell::ScrollFrameIntoVisualViewport(Maybe<nsPoint>& aDestination, - const nsRect& aPositionFixedRect, - ScrollFlags aScrollFlags) { +void PresShell::ScrollFrameIntoVisualViewport( + Maybe<nsPoint>& aDestination, const nsRect& aPositionFixedRect, + const nsIFrame* aPositionFixedFrame, ScrollAxis aVertical, + ScrollAxis aHorizontal, ScrollFlags aScrollFlags) { PresShell* root = GetRootPresShell(); if (!root) { return; @@ -3760,6 +3761,7 @@ void PresShell::ScrollFrameIntoVisualViewport(Maybe<nsPoint>& aDestination, } if (!aDestination) { + MOZ_ASSERT(aPositionFixedFrame); // If we have in the top level content document but we didn't reach to // the root scroll container in the frame tree walking up loop in // ScrollFrameIntoView, it means the target element is inside a @@ -3802,10 +3804,23 @@ void PresShell::ScrollFrameIntoVisualViewport(Maybe<nsPoint>& aDestination, // position because the position:fixed origin (0, 0) is the layout scroll // position. Otherwise if we've already scrolled, this scrollIntoView // operation will jump back to near (0, 0) position. - // Bug 1947470: We need to calculate the destination with `WhereToScroll` - // options. - const nsPoint layoutOffset = rootScrollContainer->GetScrollPosition(); - aDestination = Some(aPositionFixedRect.TopLeft() + layoutOffset); + nsPoint layoutOffset = rootScrollContainer->GetScrollPosition(); + + const nsRect visibleRect(layoutOffset, visualViewportSize); + nscoord unusedRangeMinOutparam; + nscoord unusedRangeMaxOutparam; + nscoord x = ComputeWhereToScroll( + aHorizontal.mWhereToScroll, layoutOffset.x, aPositionFixedRect.x, + aPositionFixedRect.XMost(), visibleRect.x, visibleRect.XMost(), + &unusedRangeMinOutparam, &unusedRangeMaxOutparam); + nscoord y = ComputeWhereToScroll( + aVertical.mWhereToScroll, layoutOffset.y, aPositionFixedRect.y, + aPositionFixedRect.YMost(), visibleRect.y, visibleRect.YMost(), + &unusedRangeMinOutparam, &unusedRangeMaxOutparam); + + layoutOffset.x += x; + layoutOffset.y += y; + aDestination = Some(layoutOffset); } // NOTE: It seems chrome doesn't respect the root element's @@ -3888,7 +3903,7 @@ bool PresShell::ScrollFrameIntoView( nsIFrame* container = aTargetFrame; - bool inPositionFixedSubtree = false; + const nsIFrame* positionFixedFrame = nullptr; auto isPositionFixed = [&](const nsIFrame* aFrame) -> bool { return aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed && nsLayoutUtils::IsReallyFixedPos(aFrame); @@ -3901,7 +3916,7 @@ bool PresShell::ScrollFrameIntoView( MaybeSkipPaddingSides(aTargetFrame); while (nsIFrame* parent = container->GetParent()) { if (isPositionFixed(container)) { - inPositionFixedSubtree = true; + positionFixedFrame = container; } container = parent; if (container->IsScrollContainerOrSubclass()) { @@ -3947,7 +3962,7 @@ bool PresShell::ScrollFrameIntoView( // keeping rect relative to container do { if (isPositionFixed(container)) { - inPositionFixedSubtree = true; + positionFixedFrame = container; } if (ScrollContainerFrame* sf = do_QueryFrame(container)) { @@ -4029,11 +4044,12 @@ bool PresShell::ScrollFrameIntoView( // scroll the rect into view visually, and that may require scrolling // the visual viewport in scenarios where there is not enough layout // scroll range. - if (!rootScrollDestination && !inPositionFixedSubtree) { + if (!rootScrollDestination && !positionFixedFrame) { return didScroll; } - ScrollFrameIntoVisualViewport(rootScrollDestination, rect, aScrollFlags); + ScrollFrameIntoVisualViewport(rootScrollDestination, rect, positionFixedFrame, + aVertical, aHorizontal, aScrollFlags); return didScroll; } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h @@ -407,6 +407,9 @@ class PresShell final : public nsStubDocumentObserver, void ScrollFrameIntoVisualViewport(Maybe<nsPoint>& aDestination, const nsRect& aPositionFixedRect, + const nsIFrame* aPositionFixedFrame, + ScrollAxis aVertical, + ScrollAxis aHorizontal, ScrollFlags aScrollFlags); public: