Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 1 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /scroll-animations/css/scroll-timeline-multi-pass.tentative.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<title>ScrollTimelines may trigger multiple style/layout passes</title>
<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 () => {
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.
//
assert_equals(getComputedStyle(element1).width, '1px');
(new ResizeObserver(entries => {
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].
//
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);
// ScrollTimelines created during the ResizeObserver should remain
// inactive during the frame they're created, so the ResizeObserver
// event should not reflect the animated value.
assert_equals(events2.length, 1);
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(getComputedStyle(element1).width, '100px');
assert_equals(getComputedStyle(element2).height, '100px');
}, 'Multiple style/layout passes occur when necessary');
</script>