Bug 1947470 - Do MoveInsideAndClamp for the given position:fixed rectangle to avoid unexpected scrolling. r=dlrobertson

For safety, both of helper_scrollIntoView_bug1950744.html and
helper_scrollIntoView_bug1950744-2.html run with
layout.scroll_fixed_content_into_view_visually=true and
layout.scroll_fixed_content_into_view_visually=false because these tests
should NOT cause any scrolling regardless of the pref value.

Differential Revision: https://phabricator.services.mozilla.com/D243735
This commit is contained in:
Hiroyuki Ikezoe
2025-04-08 00:17:48 +00:00
parent 39b3f6141e
commit 66f31cb50d
4 changed files with 41 additions and 25 deletions

View File

@@ -33,17 +33,16 @@
<input type="text" id="name" /> <input type="text" id="name" />
</div> </div>
<script> <script>
const isAndroid = getPlatform() == "android";
async function test() { async function test() {
is(window.scrollY, 0, "The initial scroll offset should be 0"); is(window.scrollY, 0, "The initial scroll offset should be 0");
is(visualViewport.scale, 1.0, "The document should not get scaled"); is(visualViewport.scale, 1.0, "The document should not get scaled");
is(visualViewport.pageTop, 0, "The initial visual viewport pageTop should be 0"); is(visualViewport.pageTop, 0, "The initial visual viewport pageTop should be 0");
visualViewport.addEventListener("scroll", () => { visualViewport.addEventListener("scroll", () => {
ok(isAndroid, "Any VisualViewport scroll event should not be observed"); ok(false, "Any VisualViewport scroll event should not be observed");
}); });
window.addEventListener("scroll", () => { window.addEventListener("scroll", () => {
ok(isAndroid, "Any scroll event should not be observed"); ok(false, "Any scroll event should not be observed");
}); });
// Scroll to the input element inside a position:fixed element. // Scroll to the input element inside a position:fixed element.
@@ -55,13 +54,8 @@ async function test() {
await promiseFrame(); await promiseFrame();
await promiseFrame(); await promiseFrame();
if (isAndroid) { is(visualViewport.pageTop, 0, "The visual viewport pageTop should be zero");
todo_is(visualViewport.pageTop, 0, "The visual viewport pageTop should be zero"); is(window.scrollY, 0, "The scroll offset should be zero");
todo_is(window.scrollY, 0, "The scroll offset should be zero");
} else {
is(visualViewport.pageTop, 0, "The visual viewport pageTop should be zero");
is(window.scrollY, 0, "The scroll offset should be zero");
}
} }
waitUntilApzStable() waitUntilApzStable()

View File

@@ -30,7 +30,6 @@
<div id="target"></div> <div id="target"></div>
</div> </div>
<script> <script>
const isAndroid = getPlatform() == "android";
async function test() { async function test() {
is(window.scrollY, 0, "The initial scroll offset should be 0"); is(window.scrollY, 0, "The initial scroll offset should be 0");
is(visualViewport.scale, 1.0, "The document should not get scaled"); is(visualViewport.scale, 1.0, "The document should not get scaled");
@@ -43,10 +42,10 @@ async function test() {
await scrollendPromise; await scrollendPromise;
visualViewport.addEventListener("scroll", () => { visualViewport.addEventListener("scroll", () => {
ok(isAndroid, "Any VisualViewport scroll event should not be observed"); ok(false, "Any VisualViewport scroll event should not be observed");
}); });
window.addEventListener("scroll", () => { window.addEventListener("scroll", () => {
ok(isAndroid, "Any scroll event should not be observed"); ok(false, "Any scroll event should not be observed");
}); });
document.querySelector("#target").scrollIntoView(); document.querySelector("#target").scrollIntoView();
@@ -57,11 +56,7 @@ async function test() {
await promiseFrame(); await promiseFrame();
await promiseFrame(); await promiseFrame();
if (isAndroid) { is(window.scrollY, 2000, "The scroll offset should stay at 2000px");
todo_is(window.scrollY, 2000, "The scroll offset should stay at 2000px");
} else {
is(window.scrollY, 2000, "The scroll offset should stay at 2000px");
}
} }
waitUntilApzStable() waitUntilApzStable()

View File

@@ -15,8 +15,22 @@ var subtests = [
"prefs": [...prefs, "prefs": [...prefs,
["layout.scroll_fixed_content_into_view_visually", true]] ["layout.scroll_fixed_content_into_view_visually", true]]
}, },
{"file": "helper_scrollIntoView_bug1950744.html", prefs}, {"file": "helper_scrollIntoView_bug1950744.html",
{"file": "helper_scrollIntoView_bug1950744-2.html", prefs}, "prefs": [...prefs,
["layout.scroll_fixed_content_into_view_visually", false]]
},
{"file": "helper_scrollIntoView_bug1950744.html",
"prefs": [...prefs,
["layout.scroll_fixed_content_into_view_visually", true]]
},
{"file": "helper_scrollIntoView_bug1950744-2.html",
"prefs": [...prefs,
["layout.scroll_fixed_content_into_view_visually", false]]
},
{"file": "helper_scrollIntoView_bug1950744-2.html",
"prefs": [...prefs,
["layout.scroll_fixed_content_into_view_visually", true]]
},
]; ];
if (isApzEnabled()) { if (isApzEnabled()) {

View File

@@ -3856,18 +3856,31 @@ void PresShell::ScrollFrameIntoVisualViewport(Maybe<nsPoint>& aDestination,
const nsSize visualViewportSize = const nsSize visualViewportSize =
rootScrollContainer->GetVisualViewportSize(); rootScrollContainer->GetVisualViewportSize();
const nsSize layoutViewportSize = root->GetLayoutViewportSize();
const nsRect layoutViewport = nsRect(nsPoint(), layoutViewportSize);
// `positon:fixed` element are attached/fixed to the ViewportFrame, which is
// the parent of the root scroll container frame, thus what we need here is
// the visible area of the position:fixed element inside the root scroll
// container frame.
// For example, if the top left position of the fixed element is (-100,
// -100), it's outside of the scrollable range either in the layout viewport
// or the visual viewport. Likewise, if the right bottom position of the
// fixed element is (110vw, 110vh), it's also outside of the scrollable
// range.
const nsRect clampedPositionFixedRect =
aPositionFixedRect.MoveInsideAndClamp(layoutViewport);
// If the position:fixed element is already inside the visual viewport, we // If the position:fixed element is already inside the visual viewport, we
// don't need to scroll visually. // don't need to scroll visually.
if (aPositionFixedRect.y >= 0 && if (clampedPositionFixedRect.y >= 0 &&
aPositionFixedRect.YMost() <= visualViewportSize.height && clampedPositionFixedRect.YMost() <= visualViewportSize.height &&
aPositionFixedRect.x >= 0 && clampedPositionFixedRect.x >= 0 &&
aPositionFixedRect.XMost() <= visualViewportSize.width) { clampedPositionFixedRect.XMost() <= visualViewportSize.width) {
return; return;
} }
// If the position:fixed element is totally outside of the the layout // If the position:fixed element is totally outside of the the layout
// viewport, it will never be in the viewport. // viewport, it will never be in the viewport.
const nsSize layoutViewportSize = root->GetLayoutViewportSize();
if (!NeedToVisuallyScroll(layoutViewportSize, aPositionFixedRect)) { if (!NeedToVisuallyScroll(layoutViewportSize, aPositionFixedRect)) {
return; return;
} }