summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
diff options
context:
space:
mode:
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.html662
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>