110 lines
3.6 KiB
HTML
110 lines
3.6 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://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 () => {
|
|
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 => {
|
|
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);
|
|
|
|
// 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>
|