diff options
Diffstat (limited to 'testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html')
-rw-r--r-- | testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html b/testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html new file mode 100644 index 0000000000..9f55e2d2c9 --- /dev/null +++ b/testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html @@ -0,0 +1,662 @@ +<!DOCTYPE html> +<title>The animation-timeline: scroll-timeline-name</title> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timelines-named"> +<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/6674"> +<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 anim { + from { translate: 50px; } + to { translate: 150px; } + } + #target { + width: 100px; + height: 100px; + } + .square { + width: 100px; + height: 100px; + } + .square-container { + width: 300px; + height: 300px; + } + .scroller { + overflow: scroll; + } + .content { + inline-size: 100%; + block-size: 100%; + padding-inline-end: 100px; + padding-block-end: 100px; + } +</style> +<body> +<div id="log"></div> +<script> +"use strict"; + +setup(assert_implements_animation_timeline); + +function createScroller(t, scrollerSizeClass) { + let scroller = document.createElement('div'); + let className = scrollerSizeClass || 'square'; + scroller.className = `scroller ${className}`; + let content = document.createElement('div'); + content.className = 'content'; + + scroller.appendChild(content); + + t.add_cleanup(function() { + content.remove(); + scroller.remove(); + }); + + return scroller; +} + +function createTarget(t) { + let target = document.createElement('div'); + target.id = 'target'; + + t.add_cleanup(function() { + target.remove(); + }); + + return target; +} + +function createScrollerAndTarget(t, scrollerSizeClass) { + return [createScroller(t, scrollerSizeClass), createTarget(t)]; +} + +// ------------------------- +// Test scroll-timeline-name +// ------------------------- + +promise_test(async t => { + let target = document.createElement('div'); + target.id = 'target'; + target.className = 'scroller'; + let content = document.createElement('div'); + content.className = 'content'; + + // <div id='target' class='scroller'> + // <div id='content'></div> + // </div> + document.body.appendChild(target); + target.appendChild(content); + + target.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + target.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); + + content.remove(); + target.remove(); +}, 'scroll-timeline-name is referenceable in animation-timeline on the ' + + 'declaring element itself'); + +promise_test(async t => { + let [parent, target] = createScrollerAndTarget(t, 'square-container'); + + // <div id='parent' class='scroller'> + // <div id='target'></div> + // <div id='content'></div> + // </div> + document.body.appendChild(parent); + parent.insertBefore(target, parent.firstElementChild); + + parent.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + parent.scrollTop = 100; // 50%, in [0, 200]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, "scroll-timeline-name is referenceable in animation-timeline on that " + + "element's descendants"); + +promise_test(async t => { + let [sibling, target] = createScrollerAndTarget(t); + + // <div id='sibling' class='scroller'> ... </div> + // <div id='target'></div> + document.body.appendChild(sibling); + document.body.appendChild(target); + + sibling.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + sibling.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, "scroll-timeline-name is referenceable in animation-timeline on that " + + "element's following siblings"); + +promise_test(async t => { + let [sibling, target] = createScrollerAndTarget(t); + let parent = document.createElement('div'); + + // <div id='sibling' class='scroller'> ... </div> + // <div id='parent'> + // <div id='target'></div> + // </div> + document.body.appendChild(sibling); + document.body.appendChild(parent); + parent.appendChild(target); + + sibling.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + sibling.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); + + parent.remove(); +}, "scroll-timeline-name is referenceable in animation-timeline on that " + + "element's following siblings' descendants"); + +// FIXME: We may use global scope for scroll-timeline-name. +// See https://github.com/w3c/csswg-drafts/issues/7047 +promise_test(async t => { + let [sibling, target] = createScrollerAndTarget(t); + + // <div id='target'></div> + // <div id='sibling' class='scroller'> ... </div> + document.body.appendChild(target); + document.body.appendChild(sibling); + + sibling.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + sibling.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '50px', + 'Animation with unknown timeline name holds current time at zero'); +}, "scroll-timeline-name is not referenceable in animation-timeline on that " + + "element's previous siblings"); + +promise_test(async t => { + let [sibling, target] = createScrollerAndTarget(t); + let parent = document.createElement('div'); + parent.className = 'scroller square-container'; + let content = document.createElement('div'); + content.className = 'content'; + + // <div id='parent' class='scroller'> + // <div id='sibling' class='scroller'> ... </div> + // <div id='target'></div> + // <div id='content'></div> + // </div> + document.body.appendChild(parent); + parent.appendChild(sibling); + parent.appendChild(target); + parent.appendChild(content); + + parent.style.scrollTimelineName = 'timeline'; + parent.style.scrollTimelineAxis = 'inline'; + sibling.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + parent.scrollTop = 50; // 25%, in [0, 200]. + sibling.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); + + content.remove(); + parent.remove(); +}, 'scroll-timeline-name is matched based on tree order, which considers ' + + 'siblings closer than parents'); + +promise_test(async t => { + let sibling = document.createElement('div'); + sibling.className = 'square'; + sibling.style.overflowX = 'clip'; // This makes overflow-y be clip as well. + let target = document.createElement('div'); + target.id = 'target'; + + // <div id='sibling' style='overflow-x: clip'></div> + // <div id='target'></div> + document.body.appendChild(sibling); + document.body.appendChild(target); + + sibling.style.scrollTimelineName = 'timeline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + sibling.scrollTop = 50; // 50%, in [0, 100]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, 'none', + 'Animation with an unresolved current time'); + + target.remove(); + sibling.remove(); +}, 'scroll-timeline-name on an element which is not a scroll-container'); + +promise_test(async t => { + let [sibling, target] = createScrollerAndTarget(t); + let main = document.createElement('div'); + main.id = 'name'; + + // <div id='main'> + // <div id='sibling' class='scroller'> ... </div> + // <div id='target'></div> + // </div> + document.body.appendChild(main); + main.appendChild(sibling); + main.appendChild(target); + + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + sibling.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + + // Unknown animation-timeline, current time held at zero. + assert_equals(getComputedStyle(target).translate, '50px'); + + // Ensure that #main (an ancestor of the scroller) needs style recalc. + main.style.background = 'lightgray'; + sibling.style.scrollTimelineName = 'timeline'; + await waitForCSSScrollTimelineStyle(); + assert_equals(getComputedStyle(target).translate, '100px'); + + main.remove(); +}, 'scroll-timeline-name affects subsequent siblings when changed'); + +promise_test(async t => { + let target = createTarget(t); + + // <div id='target'></div> + document.body.appendChild(target); + + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + + // Unknown animation-timeline, current time held at zero. + assert_equals(getComputedStyle(target).translate, '50px'); + + let scroller = createScroller(t); + // <div class='scroller'> ... </div> + // <div id='target'></div> + document.body.insertBefore(scroller, target); + scroller.style.scrollTimelineName = 'timeline'; + + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '50px'); + + // Ensure that time is not just held at zero. + scroller.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-name on inserted element affects subsequent siblings'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div class='scroller'> ... </div> + // <div id='target'></div> + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + + scroller.style.scrollTimelineName = 'timeline'; + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + await waitForCSSScrollTimelineStyle(); + + assert_equals(getComputedStyle(target).translate, '100px'); + + // This effectively removes the CSS-created ScrollTimeline on this element, + // thus invoking "setting the timeline of an animation" [1] with a null- + // timeline on affected elements. This in turn causes the current time to + // become unresolved [2], ultimately resulting in no effect value. + // + // [1] https://drafts.csswg.org/web-animations-1/#setting-the-timeline + // [2] https://drafts.csswg.org/web-animations-1/#the-current-time-of-an-animation + scroller.remove(); + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, 'none'); +}, 'scroll-timeline-name on removed element affects subsequent siblings'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div class='scroller' style='display:none'> ... </div> + // <div id='target'></div> + scroller.style.display = 'none'; + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimelineName = 'timeline'; + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + + // Unknown animation-timeline, current time held at zero. + assert_equals(getComputedStyle(target).translate, '50px'); + + scroller.style.display = 'block'; + scroller.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-name on element leaving display:none affects subsequent siblings'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div class='scroller'> ... </div> + // <div id='target'></div> + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + + scroller.style.scrollTimelineName = 'timeline'; + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + await waitForCSSScrollTimelineStyle(); + + assert_equals(getComputedStyle(target).translate, '100px'); + + // See comment in the test "scroll-timeline-name on removed element ..." for + // an explantation of this result. (Setting display:none is similar to + // removing the element). + scroller.style.display = 'none'; + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, 'none'); +}, 'scroll-timeline-name on element becoming display:none affects subsequent siblings'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div id='scroller' class='scroller'> ... </div> + // <div id='target'></div> + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimelineName = 'timeline'; + scroller.style.display = 'none'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + await waitForNextFrame(); + + const anim = target.getAnimations()[0]; + assert_true(!!anim, 'Failed to create animation'); + assert_equals(anim.timeline, null); + // Hold time of animation is zero. + assert_equals(getComputedStyle(target).translate, '50px'); + + scroller.style.display = 'block'; + scroller.scrollTop = 50; + await waitForNextFrame(); + + assert_true(!!anim.timeline, 'Failed to create timeline'); + assert_equals(getComputedStyle(target).translate, '100px'); + +}, 'scroll-timeline-name on element not resolved until element becomes visible'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div id='scroller' class='scroller'> ... </div> + // <div id='target'></div> + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimelineName = 'timeline-A'; + scroller.scrollTop = 50; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline-B'; + + await waitForNextFrame(); + + const anim = target.getAnimations()[0]; + assert_true(!!anim, 'Failed to create animation'); + assert_equals(anim.timeline, null); + // Hold time of animation is zero. + assert_equals(getComputedStyle(target).translate, '50px'); + + scroller.style.scrollTimelineName = 'timeline-B'; + await waitForNextFrame(); + + assert_true(!!anim.timeline, 'Failed to create timeline'); + assert_equals(getComputedStyle(target).translate, '100px'); + +}, 'Change in scroll-timeline-name to match animation timeline updates animation.'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + + // <div id='scroller' class='scroller'> ... </div> + // <div id='target'></div> + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimelineName = 'timeline-A'; + scroller.scrollTop = 50; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline-A'; + + await waitForNextFrame(); + + const anim = target.getAnimations()[0]; + assert_true(!!anim, 'Failed to create animation'); + assert_true(!!anim.timeline, 'Failed to create timeline'); + assert_equals(getComputedStyle(target).translate, '100px'); + + scroller.style.scrollTimelineName = 'timeline-B'; + await waitForNextFrame(); + + assert_equals(anim.timeline, null, 'Failed to remove timeline'); + assert_equals(getComputedStyle(target).translate, 'none'); + +}, 'Change in scroll-timeline-name to no longer match animation timeline updates animation.'); + +promise_test(async t => { + let target = createTarget(t); + let scroller1 = createScroller(t); + let scroller2 = createScroller(t); + + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + scroller1.style.scrollTimelineName = 'timeline'; + scroller2.style.scrollTimelineName = 'timeline'; + scroller1.id = 'A'; + scroller2.id = 'B'; + + // <div class='scroller' id='A'> ... </div> (scroller1) + // <div class='scroller' id="B"> ... </div> (scroller2) + // <div id='target'></div> + document.body.appendChild(scroller1); + document.body.appendChild(scroller2); + document.body.append(target); + + scroller1.scrollTop = 10; // 10%, in [50, 150]. + scroller2.scrollTop = 50; // 50%, in [50, 150]. + await waitForNextFrame(); + + + // The named timeline lookup should select scroller2. + let anim = target.getAnimations()[0]; + assert_true(!!anim, 'Failed to fetch animation'); + assert_equals(anim.timeline.source.id, 'B'); + assert_equals(getComputedStyle(target).translate, '100px'); + + scroller2.remove(); + + // Now it should select scroller1. + anim = target.getAnimations()[0]; + assert_true(!!anim, 'Failed to fetch animation after update'); + assert_true(!!anim.timeline, 'Animation no longer has a timeline'); + assert_equals(anim.timeline.source.id, 'A', 'Timeline not updated'); + assert_equals(getComputedStyle(target).translate, '60px'); +}, 'Timeline lookup finds next candidate when element is removed'); + +promise_test(async t => { + let target = createTarget(t); + let scroller1 = createScroller(t); + + target.style.animation = 'anim 10s linear'; + target.style.animationTimeline = 'timeline'; + scroller1.style.scrollTimelineName = 'timeline'; + scroller1.id = 'A'; + + // <div class='scroller' id='A'> ... </div> (scroller1) + // <div id='target'></div> + document.body.appendChild(scroller1); + document.body.append(target); + + scroller1.scrollTop = 10; // 10%, in [50, 150]. + + await waitForNextFrame(); + + const anim = target.getAnimations()[0]; + + assert_true(!!anim.timeline, 'Failed to retrieve animation'); + assert_equals(anim.timeline.source.id, 'A'); + assert_equals(getComputedStyle(target).translate, '60px'); + + await waitForNextFrame(); + + let scroller2 = createScroller(t); + scroller2.style.scrollTimelineName = 'timeline'; + scroller2.id = 'B'; + + // <div class='scroller' id="A"> ... </div> (scroller1) + // <div class='scroller' id="B"> ... </div> (scroller2) + // <div id='target'></div> + document.body.insertBefore(scroller2, target); + + scroller2.scrollTop = 50; // 50%, in [50, 150]. + + await waitForNextFrame(); + + // The timeline should be updated to scroller2. + assert_true(!!anim.timeline, 'Animation no longer has a timeline'); + assert_equals(anim.timeline.source.id, 'B', 'Timeline not updated'); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'Timeline lookup updates candidate when closer match available.'); + +promise_test(async t => { + let target = createTarget(t); + + // <div id='target'></div> + document.body.append(target); + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + await waitForNextFrame(); + + // Timeline initially cannot be resolved, resulting in a null + // timeline. The animation's hold time is zero. + let anim = document.getAnimations()[0]; + assert_equals(getComputedStyle(target).translate, '50px'); + + await waitForNextFrame(); + + let scroller = createScroller(t); + scroller.style.scrollTimelineName = 'timeline'; + + // <div class='scroller'> ... </div> (scroller1) + // <div id='target'></div> + document.body.insertBefore(scroller, target); + + scroller.scrollTop = 50; // 50%, in [50, 150]. + + await waitForNextFrame(); + + // The timeline should be updated to scroller. + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'Timeline lookup updates candidate when match becomes available.'); + +// ------------------------- +// Test scroll-timeline-axis +// ------------------------- + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + scroller.style.writingMode = 'vertical-lr'; + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimeline = 'timeline block'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + scroller.scrollLeft = 50; + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-axis is block'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + scroller.style.writingMode = 'vertical-lr'; + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimeline = 'timeline inline'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + scroller.scrollTop = 50; + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-axis is inline'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + scroller.style.writingMode = 'vertical-lr'; + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimeline = 'timeline horizontal'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + scroller.scrollLeft = 50; + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-axis is horizontal'); + +promise_test(async t => { + let [scroller, target] = createScrollerAndTarget(t); + scroller.style.writingMode = 'vertical-lr'; + + document.body.appendChild(scroller); + document.body.appendChild(target); + + scroller.style.scrollTimeline = 'timeline vertical'; + target.style.animation = "anim 10s linear"; + target.style.animationTimeline = 'timeline'; + + scroller.scrollTop = 50; + await waitForNextFrame(); + assert_equals(getComputedStyle(target).translate, '100px'); +}, 'scroll-timeline-axis is vertical'); + +// TODO: Add more tests which change scroll-timeline-axis property. +// Those animations which use this timeline should be restyled properly. + +</script> +</body> |