Files
sousa-gecko/testing/web-platform/tests/scroll-animations/css/scroll-timeline-multi-pass.tentative.html
T
Rune Lillesveen 73ac648427 Bug 2029367 [wpt PR 58613] - Snapshot post-layout for all but scroll-timelines, a=testonly
Automatic update from web-platform-tests
Snapshot post-layout for all but scroll-timelines

'Run snapshot post-layout state steps' was introduced in the CSSOM View
spec[1] to formalize when state for scroll-state() queries, anchor
position effects based on scrolling, and scroll-timeline progress is
updated.

There is a pull request[2] to tell when to invoke snapshotting from the
HTML event loop. This CL aligns snapshotting with that CL for all
snapshotting except for scroll-timelines.

scroll-timeline-multi-pass.tentative.html is updated to align with the
one-snapshot-per-resizeObserver-loop-iteration in the HTML spec PR. The
difference between scroll-timelines and other snapshotting is that the
scroll-timelines are still snapshotted before the first style/layout
update in the resizeObserver loop.

The reason we keep the pre-layout snapshotting of scroll-timelines for
now is that timleines are only progressed before the first style/layout
update, which means the post-layout snapshotting will always cause
scroll-timelines to be a frame late (with the exception of the first
frame) unless we start allowing scroll-timelines to progress in the
resizeObserver loop.

[1] https://drafts.csswg.org/cssom-view/#run-snapshot-post-layout-state-steps
[2] https://github.com/whatwg/html/pull/11613

Bug: 384523570
Change-Id: Iabb35fd06e5a2b9a95a09a25d27d7baad545653f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7673480
Reviewed-by: Robert Flack <flackr@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1601971}

--
Capture uncaught error causing harness flakiness

--

wpt-commits: 7427991d737d3a1704194ad8673ccc57d1ac78f4, a7f2794f9d1382ede06e2cf284b2eca5d9e07944
wpt-pr: 58613
2026-04-17 08:38:32 +00:00

118 lines
4.0 KiB
HTML

<!DOCTYPE html>
<title>ScrollTimelines may trigger multiple style/layout passes</title>
<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/5261">
<link rel="help" src="https://github.com/whatwg/html/pull/11613">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#avoiding-cycles">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes expand_width {
from { width: 100px; }
to { width: 100px; }
}
@keyframes expand_height {
from { height: 100px; }
to { height: 100px; }
}
main {
height: 0px;
overflow: hidden;
timeline-scope: --timeline1, --timeline2;
}
.scroller {
height: 100px;
overflow: scroll;
}
.scroller > div {
height: 200px;
}
#element1 {
width: 1px;
animation: expand_width 10s;
animation-timeline: --timeline1;
}
#element2 {
height: 1px;
animation: expand_height 10s;
animation-timeline: --timeline2;
}
</style>
<main id=main>
<div id=element1></div>
<div>
<div id=element2></div>
</div>
</main>
<script>
setup(assert_implements_animation_timeline);
function insertScroller(timeline_name) {
let scroller = document.createElement('div');
scroller.classList.add('scroller');
scroller.style.scrollTimeline = timeline_name;
scroller.append(document.createElement('div'));
main.insertBefore(scroller, element1);
}
promise_test(async (t) => {
await waitForNextFrame();
let events1 = [];
let events2 = [];
insertScroller('--timeline1');
// Even though the scroller was just inserted into the DOM, |timeline1|
// remains inactive until the next frame.
//
// https://drafts.csswg.org/scroll-animations-1/#avoiding-cycles
assert_equals(getComputedStyle(element1).width, '1px');
(new ResizeObserver(entries => t.step_func(() => {
events1.push(entries);
insertScroller('--timeline2');
assert_equals(getComputedStyle(element2).height, '1px');
}))).observe(element1);
(new ResizeObserver(entries => {
events2.push(entries);
})).observe(element2);
await waitForNextFrame();
// According to the basic rules of the spec [1], the timeline is
// inactive at the time the resize observer event was delivered, because
// #scroller1 did not have a layout box at the time style recalc for
// #element1 happened.
//
// However, an additional style/layout pass should take place
// (before resize observer deliveries) if we detect new ScrollTimelines
// in this situation, hence we ultimately do expect the animation to
// apply [2].
//
// [1] https://drafts.csswg.org/scroll-animations-1/#avoiding-cycles
// [2] https://github.com/w3c/csswg-drafts/issues/5261
assert_equals(events1.length, 1);
assert_equals(events1[0].length, 1);
assert_equals(events1[0][0].contentBoxSize.length, 1);
assert_equals(events1[0][0].contentBoxSize[0].inlineSize, 100);
// The HTML PR[3] that uses the 'run snapshot post-layout state steps',
// would try to create new scroll-timelines for each iteration of the
// resize observer loop, so the ResizeObserver event should reflect the
// animated value for the second observation.
//
// [3] https://github.com/whatwg/html/pull/11613/
assert_equals(events2.length, 2);
assert_equals(events2[0].length, 1);
assert_equals(events2[0][0].contentBoxSize.length, 1);
assert_equals(events2[0][0].contentBoxSize[0].blockSize, 1);
assert_equals(events2[1].length, 1);
assert_equals(events2[1][0].contentBoxSize.length, 1);
assert_equals(events2[1][0].contentBoxSize[0].blockSize, 100);
assert_equals(getComputedStyle(element1).width, '100px');
assert_equals(getComputedStyle(element2).height, '100px');
}, 'Multiple style/layout passes occur when necessary');
</script>