Update web-platform-tests to revision b'b728032f59a396243864b0f8584e7211e3632005'

This commit is contained in:
WPT Sync Bot 2022-11-10 01:22:36 +00:00
parent ace9b32b1c
commit df68c4e5d1
15632 changed files with 514865 additions and 155000 deletions

View file

@ -19,11 +19,21 @@ test_valid_value('animation',
test_valid_value('animation',
'1s linear 1s 2 reverse forwards paused anim auto',
'1s linear 1s 2 reverse forwards paused anim');
test_valid_value('animation',
'1s linear 1s 2 reverse forwards paused anim scroll()');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim initial');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim 2000');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim scroll(abc block)');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim scroll(inline abc)');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim scroll(abc)');
test_invalid_value('animation',
'1s linear 1s 2 reverse forwards paused anim scroll("string")');
test_computed_value('animation',
'1s linear 1s 2 reverse forwards paused anim');

View file

@ -22,7 +22,7 @@ test_computed_value('animation-timeline', 'auto, auto');
test_computed_value('animation-timeline', 'none, none');
test_computed_value('animation-timeline', 'auto, none');
test_computed_value('animation-timeline', 'none, auto');
test_computed_value('animation-timeline', '"test"');
test_computed_value('animation-timeline', '"test"', ["test", '"test"']);
test_computed_value('animation-timeline', '"none"');
test_computed_value('animation-timeline', '"auto"');
test_computed_value('animation-timeline', '"initial"');
@ -31,7 +31,7 @@ test_computed_value('animation-timeline', '"unset"');
test_computed_value('animation-timeline', '"revert"');
test_computed_value('animation-timeline', 'test');
test_computed_value('animation-timeline', 'test1, test2');
test_computed_value('animation-timeline', 'test1, "test2", none, test3, auto');
test_computed_value('animation-timeline', 'test1, "test2", none, test3, auto', ["test1, test2, none, test3, auto", 'test1, "test2", none, test3, auto']);
test(() => {
let style = getComputedStyle(document.getElementById('target'));
@ -42,4 +42,19 @@ test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('animation-timeline'), -1);
}, 'The animation-timeline property shows up in CSSStyleDeclaration.cssText');
// https://drafts.csswg.org/scroll-animations-1/#scroll-notation
//
// animation-timeline: scroll(<axis>? <scroller>?);
// <axis> = block | inline | vertical | horizontal
// <scroller> = root | nearest
test_computed_value('animation-timeline', 'scroll()');
test_computed_value('animation-timeline', 'scroll(block)', 'scroll()');
test_computed_value('animation-timeline', 'scroll(inline)');
test_computed_value('animation-timeline', 'scroll(horizontal)');
test_computed_value('animation-timeline', 'scroll(vertical)');
test_computed_value('animation-timeline', 'scroll(root)');
test_computed_value('animation-timeline', 'scroll(nearest)', 'scroll()');
test_computed_value('animation-timeline', 'scroll(inline nearest)', 'scroll(inline)');
test_computed_value('animation-timeline', 'scroll(vertical root)');
</script>

View file

@ -4,39 +4,30 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scrollers {
main {
overflow: hidden;
height: 0px;
}
#scrollers > div {
overflow: scroll;
main > div {
overflow: hidden;
width: 100px;
height: 100px;
}
#scrollers > div > div {
main > div > div {
height: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline1 {
source: selector(#scroller1);
orientation: auto;
start: 0px;
end: 100px;
#scroller1 {
scroll-timeline: timeline1;
}
@scroll-timeline timeline2 {
source: selector(#scroller2);
orientation: auto;
start: 0px;
end: 100px;
#scroller2 {
scroll-timeline: timeline2;
}
@scroll-timeline timeline3 {
source: selector(#scroller3);
orientation: auto;
start: 0px;
end: 100px;
#scroller3 {
scroll-timeline: timeline3;
}
#element {
width: 0px;
@ -51,13 +42,13 @@
#element { animation-play-state: paused; }
}
</style>
<div id=scrollers>
<main>
<div id=scroller1><div></div></div>
<div id=scroller2><div></div></div>
<div id=scroller3><div></div></div>
<div id=scroller4><div></div></div>
</div>
<div id=container></div>
<div id=container></div>
</main>
<script>
// Force layout of scrollers.
scroller1.offsetTop;

View file

@ -5,17 +5,14 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scrollers {
main > div {
overflow: hidden;
height: 0px;
}
#scrollers > div {
overflow: scroll;
width: 100px;
height: 100px;
}
#scrollers > div > div {
main > div > div {
height: 200px;
width: 200px;
}
@keyframes top {
@ -35,25 +32,17 @@
to { right: 200px; }
}
@scroll-timeline top_timeline {
source: selector(#scroller1);
start: 0px;
end: 100px;
#top_scroller {
scroll-timeline: block top_timeline;
}
@scroll-timeline bottom_timeline {
source: selector(#scroller1);
start: 0px;
end: 80px;
#bottom_scroller {
scroll-timeline: inline bottom_timeline;
}
@scroll-timeline left_timeline {
source: selector(#scroller2);
start: 0px;
end: 100px;
#left_scroller {
scroll-timeline: block left_timeline;
}
@scroll-timeline right_timeline {
source: selector(#scroller2);
start: 20px;
end: 60px;
#right_scroller {
scroll-timeline: inline right_timeline;
}
#element {
@ -67,25 +56,35 @@
#element { animation-play-state: paused; }
}
</style>
<div id=scrollers>
<div id=scroller1><div></div></div>
<div id=scroller2><div></div></div>
</div>
<div id=element></div>
<main>
<div id=top_scroller><div></div></div>
<div id=bottom_scroller><div></div></div>
<div id=left_scroller><div></div></div>
<div id=right_scroller><div></div></div>
<div id=element></div>
</main>
<script>
// Force layout of scrollers.
scroller1.offsetTop;
scroller2.offsetTop;
top_scroller.offsetTop;
bottom_scroller.offsetTop;
left_scroller.offsetTop;
right_scroller.offsetTop;
scroller1.scrollTop = 20;
scroller2.scrollTop = 40;
top_scroller.scrollTop = 20;
top_scroller.scrollLeft = 40;
bottom_scroller.scrollTop = 20;
bottom_scroller.scrollLeft = 40;
left_scroller.scrollTop = 60;
left_scroller.scrollLeft = 80;
right_scroller.scrollTop = 60;
right_scroller.scrollLeft = 80;
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element).top, '120px');
assert_equals(getComputedStyle(element).bottom, '125px');
assert_equals(getComputedStyle(element).left, '140px');
assert_equals(getComputedStyle(element).right, '150px');
assert_equals(getComputedStyle(element).bottom, '140px');
assert_equals(getComputedStyle(element).left, '160px');
assert_equals(getComputedStyle(element).right, '180px');
}, 'animation-timeline works with multiple timelines');
</script>

View file

@ -0,0 +1,440 @@
<!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";
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 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 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 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 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 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 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 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 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 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 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 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 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');
// TODO: Add more tests which change scroll-timeline-name property.
// Those animations which use this timeline should be restyled propertly.
// -------------------------
// 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 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 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 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 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>

View file

@ -16,7 +16,7 @@ test_valid_value('animation-timeline', 'auto, auto');
test_valid_value('animation-timeline', 'none, none');
test_valid_value('animation-timeline', 'auto, none');
test_valid_value('animation-timeline', 'none, auto');
test_valid_value('animation-timeline', '"test"');
test_valid_value('animation-timeline', '"test"', ["test", '"test"']);
test_valid_value('animation-timeline', '"none"');
test_valid_value('animation-timeline', '"auto"');
test_valid_value('animation-timeline', '"initial"');
@ -25,7 +25,7 @@ test_valid_value('animation-timeline', '"unset"');
test_valid_value('animation-timeline', '"revert"');
test_valid_value('animation-timeline', 'test');
test_valid_value('animation-timeline', 'test1, test2');
test_valid_value('animation-timeline', 'test1, "test2", none, test3, auto');
test_valid_value('animation-timeline', 'test1, "test2", none, test3, auto', ["test1, test2, none, test3, auto", 'test1, "test2", none, test3, auto']);
test_invalid_value('animation-timeline', '10px');
test_invalid_value('animation-timeline', 'auto auto');
@ -34,4 +34,25 @@ test_invalid_value('animation-timeline', 'foo bar');
test_invalid_value('animation-timeline', '"foo" "bar"');
test_invalid_value('animation-timeline', 'rgb(1, 2, 3)');
test_invalid_value('animation-timeline', '#fefefe');
// https://drafts.csswg.org/scroll-animations-1/#scroll-notation
//
// animation-timeline: scroll(<axis>? <scroller>?);
// <axis> = block | inline | vertical | horizontal
// <scroller> = root | nearest
test_valid_value('animation-timeline', 'scroll()');
test_valid_value('animation-timeline', 'scroll(block)', 'scroll()');
test_valid_value('animation-timeline', 'scroll(inline)');
test_valid_value('animation-timeline', 'scroll(horizontal)');
test_valid_value('animation-timeline', 'scroll(vertical)');
test_valid_value('animation-timeline', 'scroll(root)');
test_valid_value('animation-timeline', 'scroll(nearest)', 'scroll()');
test_valid_value('animation-timeline', 'scroll(inline nearest)', 'scroll(inline)');
test_valid_value('animation-timeline', 'scroll(vertical root)');
test_invalid_value('animation-timeline', 'scroll(root block)');
test_invalid_value('animation-timeline', 'scroll(abc root)');
test_invalid_value('animation-timeline', 'scroll(abc)');
test_invalid_value('animation-timeline', 'scroll(vertical abc)');
test_invalid_value('animation-timeline', 'scroll("string")');
</script>

View file

@ -0,0 +1,140 @@
<!DOCTYPE html>
<title>The animation-timeline: scroll() notation</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-notation">
<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>
<style>
@keyframes anim {
from { translate: 50px; }
to { translate: 150px; }
}
html {
min-height: 100vh;
/* This makes the max scrollable ragne be 100px in root element */
padding-bottom: 100px;
}
#container {
width: 300px;
height: 300px;
overflow: scroll;
}
#target {
width: 100px;
/* This makes the max scrollable ragne be 100px in the block direction */
height: 100px;
}
/* large block content */
.block-content {
block-size: 100%;
}
/* large inline content */
.inline-content {
inline-size: 100%;
block-size: 5px;
/* This makes the max scrollable ragne be 100px in the inline direction */
padding-inline-end: 100px;
}
</style>
<body>
<div id="log"></div>
<script>
"use strict";
const root = document.scrollingElement;
const createTargetWithStuff = function(t, contentClass) {
let container = document.createElement('div');
container.id = 'container';
let target = document.createElement('div');
target.id = 'target';
let content = document.createElement('div');
content.className = contentClass;
// <div id='container'>
// <div id='target'></div>
// <div class=contentClass></div>
// </div>
document.body.appendChild(container);
container.appendChild(target);
container.appendChild(content);
if (t && typeof t.add_cleanup === 'function') {
t.add_cleanup(() => {
content.remove();
target.remove();
container.remove();
});
}
return [container, target];
};
async function scrollLeft(element, value) {
element.scrollLeft = value;
await waitForNextFrame();
}
async function scrollTop(element, value) {
element.scrollTop = value;
await waitForNextFrame();
}
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, 'block-content');
div.style.animation = "anim 10s linear scroll(nearest)";
await scrollTop(root, 50);
assert_equals(getComputedStyle(div).translate, '50px');
await scrollTop(container, 50);
assert_equals(getComputedStyle(div).translate, '100px');
await scrollTop(root, 0);
}, 'animation-timeline: scroll(nearest)');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, 'block-content');
div.style.animation = "anim 10s linear scroll(root)";
await scrollTop(container, 50);
assert_equals(getComputedStyle(div).translate, '50px');
await scrollTop(root, 50);
assert_equals(getComputedStyle(div).translate, '100px');
await scrollTop(root, 0);
}, 'animation-timeline: scroll(root)');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, 'inline-content');
div.style.animation = "anim 10s linear scroll(inline)";
await scrollLeft(container, 50);
assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(inline)');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, 'block-content');
container.style.writingMode = 'vertical-lr';
div.style.animation = "anim 10s linear scroll(horizontal)";
await scrollLeft(container, 50);
assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(horizontal)');
promise_test(async t => {
let [container, div] = createTargetWithStuff(t, 'inline-content');
container.style.writingMode = 'vertical-lr';
div.style.animation = "anim 10s linear scroll(vertical)";
await scrollTop(container, 50);
assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(vertical)');
// TODO: Add more tests which change the overflow property of the container for
// scroll(nearest)
</script>
</body>

View file

@ -1,61 +0,0 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#phase-algorithm">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#contents {
height: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
#element {
width: 0px;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#element { animation-play-state: paused; }
}
</style>
<div id=scroller>
<div id=contents></div>
</div>
<div id=container></div>
<script>
promise_test(async (t) => {
try {
// Make sure scroller has a layout box.
await waitForNextFrame();
container.innerHTML = `
<div id=element></div>
<style>
@scroll-timeline timeline {
source: selector(#scroller);
start: 50px;
end: 100px;
}
#element {
animation: expand 10s linear;
animation-timeline: timeline;
}
</style>
`;
// Animation should not apply in before phase.
assert_equals(getComputedStyle(element).width, '0px');
await waitForNextFrame();
// Animation should still not apply.
assert_equals(getComputedStyle(element).width, '0px');
} finally {
container.innerHTML = '';
}
}, 'Animation does not apply when timeline phase is before');
</script>

View file

@ -1,48 +0,0 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#contents {
height: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: selector(#scroller);
start: 0px;
end: 100px;
}
@scroll-timeline timeline {
source: selector(#scroller);
start: 0px;
end: 50px;
}
#element {
animation: expand 10s linear;
animation-timeline: timeline;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#element { animation-play-state: paused; }
}
</style>
<div id=scroller>
<div id=contents></div>
</div>
<div id=element></div>
<script>
promise_test(async (t) => {
scroller.scrollTop = 25;
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, '150px');
}, 'Latest @scroll-timeline rule wins');
</script>

View file

@ -1,60 +0,0 @@
<html class="reftest-wait">
<title>The default scroll-timeline at rule in quirks mode</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-ref.html">
<style>
@keyframes update {
from { transform: translateY(0px); }
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
}
#box {
width: 100px;
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
}
#covered {
width: 100px;
height: 100px;
background-color: red;
}
* {
margin-top: 0px;
margin-bottom: 0px;
}
</style>
<div id="box"></div>
<div id="covered"></div>
<script>
window.addEventListener('load', function() {
const scroller = document.scrollingElement;
// Move the scroller to the halfway point.
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = 0.5 * maxScroll;
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
</script>

View file

@ -1,247 +0,0 @@
<!DOCTYPE html>
<title>@scroll-timeline: Element-based offsets</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#element-based-offset-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
.filler {
height: 150px;
background-color: darkgray;
}
.offset {
height: 50px;
background-color: green;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline_start_start {
source: selector(#scroller);
start: selector(#offset1);
end: selector(#offset2);
}
@scroll-timeline timeline_end_end {
source: selector(#scroller);
start: selector(#offset1) end;
end: selector(#offset2) end;
}
@scroll-timeline timeline_end_start {
source: selector(#scroller);
start: selector(#offset1) end;
end: selector(#offset2) start;
}
@scroll-timeline timeline_end_1_end {
source: selector(#scroller);
start: selector(#offset1) end 1;
end: selector(#offset2) end;
}
@scroll-timeline timeline_end_start_05 {
source: selector(#scroller);
start: selector(#offset1) end;
end: selector(#offset2) 0.5;
}
@scroll-timeline timeline_start_400px {
source: selector(#scroller);
start: selector(#offset1);
end: 400px;
}
@scroll-timeline timeline_50px_end {
source: selector(#scroller);
start: 50px;
end: selector(#offset2) end;
}
@scroll-timeline timeline_outside {
source: selector(#scroller);
start: selector(#offset_outside);
end: auto;
}
@scroll-timeline timeline_display_none {
source: selector(#scroller);
start: selector(#offset_display_none);
end: auto;
}
@scroll-timeline timeline_null_target {
source: selector(#scroller);
start: selector(#no_such_id);
end: selector(#no_such_id);
}
#container > div {
width: 0px;
animation-name: expand;
animation-duration: 10s;
animation-timing-function: linear;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#container > div { animation-play-state: paused; }
}
#element_start_start { animation-timeline: timeline_start_start; }
#element_end_end { animation-timeline: timeline_end_end; }
#element_end_start { animation-timeline: timeline_end_start; }
#element_end_1_end { animation-timeline: timeline_end_1_end; }
#element_end_start_05 { animation-timeline: timeline_end_start_05; }
#element_start_400px { animation-timeline: timeline_start_400px; }
#element_50px_end { animation-timeline: timeline_50px_end; }
#element_outside { animation-timeline: timeline_outside; }
#element_display_none { animation-timeline: timeline_display_none; }
#element_null_target { animation-timeline: timeline_null_target; }
</style>
<div id=scroller>
<div id=contents>
<div class=filler></div>
<div class=offset id=offset1></div>
<div class=filler></div>
<div class=offset id=offset2></div>
<div class=filler></div>
</div>
</div>
<div id=offset_outside></div>
<div id=offset_display_none style="display:none"></div>
<div id=container>
<div id=element_start_start></div>
<div id=element_end_end></div>
<div id=element_end_start></div>
<div id=element_end_1_end></div>
<div id=element_end_start_05></div>
<div id=element_start_400px></div>
<div id=element_50px_end></div>
<div id=element_outside></div>
<div id=element_display_none></div>
<div id=element_null_target></div>
</div>
<script>
// The contents of the scroller looks approximately like this:
//
// +-------+
// | |
// | 150px | filler
// | |
// +-------+
// +-------+
// | 50px | #offset1
// +-------+
// +-------+
// | |
// | 150px | filler
// | |
// +-------+
// +-------+
// | 50px | #offset2
// +-------+
// +-------+
// | |
// | 150px | filler
// | |
// +-------+
//
// The height of the scrollport is 100px.
// Scrolls top to 'offset', waits for a frame, then call the provided
// assertions function.
function test_scroll(element, offset, assertions, description) {
promise_test(async (t) => {
scroller.scrollTop = offset;
await waitForNextFrame();
assertions();
}, `${description} [${element.id}]`);
}
// Tests that the computed value of 'width' on element is the expected value
// after scrolling top to the specifed offset.
function test_width_at_scroll_top(element, offset, expected) {
test_scroll(element, offset, () => {
assert_equals(getComputedStyle(element).width, expected);
}, `Scroll at offset ${offset} updates animation correctly`);
}
// [200, 400]
test_width_at_scroll_top(element_start_start, 0, '0px');
test_width_at_scroll_top(element_start_start, 199, '0px');
test_width_at_scroll_top(element_start_start, 200, '100px');
test_width_at_scroll_top(element_start_start, 300, '150px');
test_width_at_scroll_top(element_start_start, 398, '199px');
test_width_at_scroll_top(element_start_start, 400, '0px');
// [50, 250]
test_width_at_scroll_top(element_end_end, 0, '0px');
test_width_at_scroll_top(element_end_end, 49, '0px');
test_width_at_scroll_top(element_end_end, 50, '100px');
test_width_at_scroll_top(element_end_end, 150, '150px');
test_width_at_scroll_top(element_end_end, 248, '199px');
test_width_at_scroll_top(element_end_end, 250, '0px');
// [50, 400]
test_width_at_scroll_top(element_end_start, 0, '0px');
test_width_at_scroll_top(element_end_start, 49, '0px');
test_width_at_scroll_top(element_end_start, 50, '100px');
test_width_at_scroll_top(element_end_start, 225, '150px');
test_width_at_scroll_top(element_end_start, 393, '198px');
test_width_at_scroll_top(element_end_start, 400, '0px');
// [100, 250]
test_width_at_scroll_top(element_end_1_end, 0, '0px');
test_width_at_scroll_top(element_end_1_end, 99, '0px');
test_width_at_scroll_top(element_end_1_end, 100, '100px');
test_width_at_scroll_top(element_end_1_end, 175, '150px');
test_width_at_scroll_top(element_end_1_end, 247, '198px');
test_width_at_scroll_top(element_end_1_end, 250, '0px');
// [50, 375]
test_width_at_scroll_top(element_end_start_05, 0, '0px');
test_width_at_scroll_top(element_end_start_05, 49, '0px');
test_width_at_scroll_top(element_end_start_05, 50, '100px');
test_width_at_scroll_top(element_end_start_05, 206, '148px');
test_width_at_scroll_top(element_end_start_05, 362, '196px');
test_width_at_scroll_top(element_end_start_05, 375, '0px');
// [200, 300]
test_width_at_scroll_top(element_start_400px, 0, '0px');
test_width_at_scroll_top(element_start_400px, 199, '0px');
test_width_at_scroll_top(element_start_400px, 200, '100px');
test_width_at_scroll_top(element_start_400px, 300, '150px');
test_width_at_scroll_top(element_start_400px, 398, '199px');
test_width_at_scroll_top(element_start_400px, 400, '0px');
// [50, 250]
test_width_at_scroll_top(element_50px_end, 0, '0px');
test_width_at_scroll_top(element_50px_end, 49, '0px');
test_width_at_scroll_top(element_50px_end, 50, '100px');
test_width_at_scroll_top(element_50px_end, 150, '150px');
test_width_at_scroll_top(element_50px_end, 248, '199px');
test_width_at_scroll_top(element_50px_end, 250, '0px');
// Offset not a decendant of scroller (=> no effect value)
test_width_at_scroll_top(element_outside, 0, '0px');
test_width_at_scroll_top(element_outside, 100, '0px');
test_width_at_scroll_top(element_outside, 200, '0px');
test_width_at_scroll_top(element_outside, 300, '0px');
test_width_at_scroll_top(element_outside, 400, '0px');
test_width_at_scroll_top(element_outside, 450, '0px');
// Target of element-based offset has no layout box (=> no effect value)
test_width_at_scroll_top(element_display_none, 0, '0px');
test_width_at_scroll_top(element_display_none, 100, '0px');
test_width_at_scroll_top(element_display_none, 200, '0px');
test_width_at_scroll_top(element_display_none, 300, '0px');
test_width_at_scroll_top(element_display_none, 400, '0px');
test_width_at_scroll_top(element_display_none, 450, '0px');
// Target of element-based offset is null (=> no effect value)
test_width_at_scroll_top(element_null_target, 0, '0px');
test_width_at_scroll_top(element_null_target, 100, '0px');
test_width_at_scroll_top(element_null_target, 200, '0px');
test_width_at_scroll_top(element_null_target, 300, '0px');
test_width_at_scroll_top(element_null_target, 400, '0px');
test_width_at_scroll_top(element_null_target, 450, '0px');
</script>

View file

@ -1,207 +0,0 @@
<!DOCTYPE html>
<title>@scroll-timeline element offset invalidation</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#typedef-element-offset">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#scroller > div {
height: 50px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: selector(#scroller);
start: selector(#offset1) end;
end: selector(#offset2) end;
}
#element {
width: 0px;
height: 20px;
animation: expand 1000s linear;
animation-timeline: timeline;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#element { animation-play-state: paused; }
}
</style>
<div id=scroller></div>
<div id=element></div>
<p class=sibling1></p>
<p class=sibling2></p>
<script>
function setup() {
while (scroller.firstChild)
scroller.firstChild.remove();
for (let i = 0; i < 10; i++)
scroller.append(document.createElement('div'));
}
// The contents of the scroller look like this:
//
// +-------+
// | 50px | div (0)
// +-------+
// +-------+
// | 50px | div (1)
// +-------+
// +-------+
// | 50px | div (2)
// +-------+
// +-------+
// | 50px | div (3)
// +-------+
// +-------+
// | 50px | div (4)
// +-------+
// +-------+
// | 50px | div (5)
// +-------+
// +-------+
// | 50px | div (6)
// +-------+
// +-------+
// | 50px | div (7)
// +-------+
// +-------+
// | 50px | div (8)
// +-------+
// +-------+
// | 50px | div (9)
// +-------+
//
// The height of the scrollport is 100px.
function invalidation_test(func, description) {
promise_test(async (t) => {
setup();
await func();
}, description);
}
function remove(id) {
let old_element = document.getElementById(id);
if (old_element)
old_element.removeAttribute('id');
}
function reassign(id, element) {
remove(id);
element.setAttribute('id', id);
}
async function assert_element_width_at_scroll(expected_width, scroll) {
scroller.scrollTop = scroll;
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, expected_width);
}
invalidation_test(async () => {
await assert_element_width_at_scroll('0px', 0);
}, 'Offsets missing');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
// [100, 150]
reassign('offset1', scroller.children[4]);
await assert_element_width_at_scroll('100px', 100);
}, 'Change first offset');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
// [50, 250]
reassign('offset2', scroller.children[7]);
await assert_element_width_at_scroll('125px', 100);
}, 'Change second offset');
invalidation_test(async () => {
// [50, 250]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[7]);
await assert_element_width_at_scroll('125px', 100);
// [0, 200]
reassign('offset1', scroller.children[2]);
reassign('offset2', scroller.children[4]);
await assert_element_width_at_scroll('150px', 50);
}, 'Change both offsets');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
remove('offset1');
await assert_element_width_at_scroll('0px', 0);
}, 'Remove first offset');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
remove('offset2');
await assert_element_width_at_scroll('0px', 0);
}, 'Remove second offset');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
remove('offset1');
remove('offset2');
await assert_element_width_at_scroll('0px', 0);
}, 'Remove both offsets');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
reassign('offset1', document.querySelector('.sibling1'));
await assert_element_width_at_scroll('0px', 0);
}, 'Reassign first offset to sibling of scroller');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
reassign('offset2', document.querySelector('.sibling2'));
await assert_element_width_at_scroll('0px', 0);
}, 'Reassign second offset to sibling of scroller');
invalidation_test(async () => {
// [50, 150]
reassign('offset1', scroller.children[3]);
reassign('offset2', scroller.children[5]);
await assert_element_width_at_scroll('150px', 100);
reassign('offset1', document.querySelector('.sibling1'));
reassign('offset2', document.querySelector('.sibling2'));
await assert_element_width_at_scroll('0px', 0);
}, 'Reassign both offsets to sibling of scroller');
</script>

View file

@ -1,186 +0,0 @@
<!DOCTYPE html>
<title>@scroll-timeline source invalidation</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scrollers {
overflow: hidden;
height: 0;
}
.scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
.contents {
height: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: selector(#scroller);
start: 0px;
end: 100px;
}
#element {
width: 0px;
height: 20px;
animation: expand 1000s linear;
animation-timeline: timeline;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#element { animation-play-state: paused; }
}
</style>
<div id=scrollers></div>
<div id=element></div>
<script>
function createScroller() {
let scroller = document.createElement('div');
let contents = document.createElement('div');
scroller.classList.add('scroller');
contents.classList.add('contents');
scroller.append(contents);
return scroller;
}
function wrapInDiv(element) {
let div = document.createElement('div');
div.append(element);
return div;
}
function scrollerAt(n) {
return document.querySelectorAll('.scroller')[n];
}
// Resets #scrollers to a state where it has three .scroller children with
// scrollTop offsets 10, 20 and 30.
function cleanup() {
while (scrollers.firstChild)
scrollers.firstChild.remove();
for (let i = 0; i < 3; i++)
scrollers.append(createScroller());
scrollerAt(0).scrollTop = 10;
scrollerAt(1).scrollTop = 20;
scrollerAt(2).scrollTop = 30;
}
// Do an initial "cleanup" to set up the first test.
cleanup();
function invalidation_test(func, description) {
promise_test(async (t) => {
t.add_cleanup(cleanup);
await func();
}, description);
}
invalidation_test(() => {
assert_equals(getComputedStyle(element).width, '0px');
}, 'Nonexistent source');
invalidation_test(() => {
assert_equals(getComputedStyle(element).width, '0px');
scrollerAt(0).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '110px');
scrollerAt(1).setAttribute('id', 'scroller'); // No effect
assert_equals(getComputedStyle(element).width, '110px');
scrollerAt(2).setAttribute('id', 'scroller'); // No effect
assert_equals(getComputedStyle(element).width, '110px');
}, 'Setting id attribute');
invalidation_test(() => {
assert_equals(getComputedStyle(element).width, '0px');
scrollerAt(0).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '110px');
scrollerAt(0).removeAttribute('id');
assert_equals(getComputedStyle(element).width, '0px');
}, 'Removing id attribute');
invalidation_test(() => {
assert_equals(getComputedStyle(element).width, '0px');
scrollerAt(2).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '130px');
scrollerAt(1).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '120px');
scrollerAt(0).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '110px');
}, 'Setting id attribute earlier in the tree');
invalidation_test(async () => {
assert_equals(getComputedStyle(element).width, '0px');
// Appending a new element with id 'scroller' already set before
// insertion into the tree.
let scroller = createScroller();
scroller.setAttribute('id', 'scroller');
scrollers.append(scroller);
// Make sure |scroller| has a layout box.
//
// https://drafts.csswg.org/scroll-animations-1/#avoiding-cycles
//
// TODO: Depending on the outcome of Issue 5261, the call to offsetTop
// might be unnecessary.
// https://github.com/w3c/csswg-drafts/issues/5261
scroller.offsetTop;
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, '100px');
}, 'Appending a new element');
invalidation_test(async () => {
assert_equals(getComputedStyle(element).width, '0px');
let scroller = createScroller();
scroller.setAttribute('id', 'scroller');
scrollers.append(wrapInDiv(wrapInDiv(scroller)));
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, '100px');
}, 'Inserting a subtree with #scroller descendant');
invalidation_test(() => {
assert_equals(getComputedStyle(element).width, '0px');
scrollerAt(0).setAttribute('id', 'scroller');
scrollerAt(1).setAttribute('id', 'scroller');
scrollerAt(2).setAttribute('id', 'scroller');
assert_equals(getComputedStyle(element).width, '110px');
scrollerAt(0).remove();
assert_equals(getComputedStyle(element).width, '120px');
scrollerAt(0).remove();
assert_equals(getComputedStyle(element).width, '130px');
scrollerAt(0).remove();
assert_equals(getComputedStyle(element).width, '0px');
}, 'Removing source element');
invalidation_test(async () => {
assert_equals(getComputedStyle(element).width, '0px');
// Create a chain: #scrollers -> div -> div -> #scroller
let scroller = createScroller();
let div = wrapInDiv(wrapInDiv(scroller));
scrollers.append(div);
scroller.setAttribute('id', 'scroller');
scroller.scrollTop = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, '150px');
div.remove();
assert_equals(getComputedStyle(element).width, '0px');
}, 'Removing ancestor of source element');
</script>

View file

@ -1,131 +0,0 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#descdef-scroll-timeline-source">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
:root {
height: 100vh;
overflow: scroll;
}
body {
height: 200vh;
}
.scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#boxless {
display: none;
}
.contents {
height: 300px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline_source_none {
source: none;
start: 0px;
end: 100px;
}
@scroll-timeline timeline_source_auto {
source: auto;
start: 0px;
end: 100px;
}
@scroll-timeline timeline_source_selector {
source: selector(#scroller);
start: 0px;
end: 100px;
}
@scroll-timeline timeline_source_unspecified {
start: 0px;
end: 100px;
}
@scroll-timeline timeline_source_nonexistent_id {
source: selector(#doesnotexist);
start: 0px;
end: 100px;
}
@scroll-timeline timeline_source_no_layout_box {
source: selector(#boxless);
start: 0px;
end: 100px;
}
#container > div {
width: 0px;
animation-name: expand;
animation-duration: 10s;
animation-timing-function: linear;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#container > div { animation-play-state: paused; }
}
#element_source_none { animation-timeline: timeline_source_none; }
#element_source_auto { animation-timeline: timeline_source_auto; }
#element_source_unspecified { animation-timeline: timeline_source_unspecified; }
#element_source_selector { animation-timeline: timeline_source_selector; }
#element_source_nonexistent_id { animation-timeline: timeline_source_nonexistent_id; }
#element_source_no_layout_box { animation-timeline: timeline_source_no_layout_box; }
</style>
<body>
<div class=scroller id=scroller>
<div class=contents></div>
</div>
<div class=scroller id=boxless>
<div class=contents></div>
</div>
<div id=container>
<div id=element_source_none></div>
<div id=element_source_auto></div>
<div id=element_source_unspecified></div>
<div id=element_source_selector></div>
<div id=element_source_nonexistent_id></div>
<div id=element_source_no_layout_box></div>
</div>
<script>
// Set progress of animations linked to #scroller to 75%.
scroller.scrollTop = 75;
// Set progress of animations linked to document.scrollingElement to 25%.
document.scrollingElement.scrollTop = 25;
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_none).width, '0px');
}, 'Source none causes inactive timeline');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_auto).width, '125px');
}, 'Source auto selects scrollingElement of the document');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_unspecified).width, '125px');
}, 'Unspecified source behaves like auto');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_selector).width, '175px');
}, 'Source selector(<id-selector>) selects an element');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_nonexistent_id).width, '0px');
}, 'Unknown source causes inactive timeline');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_source_no_layout_box).width, '0px');
}, 'Source with no layout box causes inactive timeline');
// TODO(https://github.com/w3c/csswg-drafts/issues/5289): Add tests for
// sources that change when behavior is clarified.
</script>
</body>

View file

@ -1,191 +0,0 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-offset-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#contents {
height: 300px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline_0px_100px {
source: selector(#scroller);
start: 0px;
end: 100px;
}
@scroll-timeline timeline_50px_100px {
source: selector(#scroller);
start: 50px;
end: 100px;
}
@scroll-timeline timeline_auto_auto {
source: selector(#scroller);
start: auto;
end: auto;
}
@scroll-timeline timeline_auto_auto_implicit {
source: selector(#scroller);
}
@scroll-timeline timeline_50px_auto {
source: selector(#scroller);
start: 50px;
end: auto;
}
@scroll-timeline timeline_auto_100px {
source: selector(#scroller);
start: auto;
end: 100px;
}
@scroll-timeline timeline_25p_75p {
source: selector(#scroller);
start: 25%;
end: 75%;
}
@scroll-timeline timeline_calc_calc {
source: selector(#scroller);
start: calc(25% + 10px);
end: calc(75% + 10px);
}
#container > div {
width: 0px;
animation-name: expand;
animation-duration: 10s;
animation-timing-function: linear;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#container > div { animation-play-state: paused; }
}
#element_0px_100px { animation-timeline: timeline_0px_100px; }
#element_50px_100px { animation-timeline: timeline_50px_100px; }
#element_auto_auto { animation-timeline: timeline_auto_auto; }
#element_auto_auto_implicit { animation-timeline: timeline_auto_auto_implicit; }
#element_50px_auto { animation-timeline: timeline_50px_auto; }
#element_auto_100px { animation-timeline: timeline_auto_100px; }
#element_25p_75p { animation-timeline: timeline_25p_75p; }
#element_calc_calc { animation-timeline: timeline_calc_calc; }
</style>
<div id=scroller>
<div id=contents></div>
</div>
<div id=container>
<div id=element_0px_100px></div>
<div id=element_50px_100px></div>
<div id=element_auto_auto></div>
<div id=element_auto_auto_implicit></div>
<div id=element_50px_auto></div>
<div id=element_auto_100px></div>
<div id=element_25p_75p></div>
<div id=element_calc_calc></div>
</div>
<script>
// Scrolls top to 'offset', waits for a frame, then call the provided
// assertions function.
function test_scroll(element, offset, assertions, description) {
promise_test(async (t) => {
scroller.scrollTop = offset;
await waitForNextFrame();
assertions();
}, `${description} [${element.id}]`);
}
// Tests that the computed value of 'width' on element is the expected value
// after scrolling top to the specifed offset.
function test_width_at_scroll_top(element, offset, expected) {
test_scroll(element, offset, () => {
assert_equals(getComputedStyle(element).width, expected);
}, `Scroll at offset ${offset} updates animation correctly`);
}
// Tests that the computed value of 'width' on element is (approximately)
// the expected value after scrolling top to the offset specified by
// 'fraction'. The 'fraction' parameter is either a number which maps [0, 1]
// to the full scroll range, or a function which accepts the maximum scroll,
// and returns a specific offset.
function test_approximate_width_at_fraction(element, fraction, expected) {
const max = scroller.scrollHeight - scroller.clientHeight;
const offsetFunction = (typeof fraction == 'function') ? fraction : () => max * fraction;
const resolvedOffset = Math.floor(offsetFunction(max));
test_scroll(element, resolvedOffset, () => {
assert_approx_equals(parseInt(getComputedStyle(element).width), parseInt(expected), 5);
}, `Scroll at offset ${resolvedOffset} updates animation correctly`);
}
// [0px, 100px]
test_width_at_scroll_top(element_0px_100px, 0, '100px');
test_width_at_scroll_top(element_0px_100px, 1, '101px');
test_width_at_scroll_top(element_0px_100px, 50, '150px');
test_width_at_scroll_top(element_0px_100px, 99, '199px');
test_width_at_scroll_top(element_0px_100px, 100, '0px');
test_width_at_scroll_top(element_0px_100px, 101, '0px');
// [50px, 100px]
test_width_at_scroll_top(element_50px_100px, 0, '0px');
test_width_at_scroll_top(element_50px_100px, 1, '0px');
test_width_at_scroll_top(element_50px_100px, 49, '0px');
test_width_at_scroll_top(element_50px_100px, 50, '100px');
test_width_at_scroll_top(element_50px_100px, 51, '102px');
test_width_at_scroll_top(element_50px_100px, 99, '198px');
test_width_at_scroll_top(element_50px_100px, 100, '0px');
test_width_at_scroll_top(element_50px_100px, 101, '0px');
// [auto, auto]
test_approximate_width_at_fraction(element_auto_auto, 0, '100px');
test_approximate_width_at_fraction(element_auto_auto, 0.1, '110px');
test_approximate_width_at_fraction(element_auto_auto, 0.5, '150px');
test_approximate_width_at_fraction(element_auto_auto, 0.9, '190px');
test_approximate_width_at_fraction(element_auto_auto, 1, '200px');
// [auto, auto] (implicit)
test_approximate_width_at_fraction(element_auto_auto_implicit, 0, '100px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.1, '110px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.5, '150px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.9, '190px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 1, '200px');
// [50px, auto]
{
let offset = (t) => (max => 50 * (1 - t) + max * t);
test_width_at_scroll_top(element_50px_auto, 0, '0px');
test_width_at_scroll_top(element_50px_auto, 49, '0px');
test_width_at_scroll_top(element_50px_auto, 50, '100px');
test_approximate_width_at_fraction(element_50px_auto, offset(0.5), '150px');
test_approximate_width_at_fraction(element_50px_auto, offset(0.9), '190px');
test_approximate_width_at_fraction(element_50px_auto, 1, '200px');
}
// [auto, 100px]
test_width_at_scroll_top(element_auto_100px, 0, '100px');
test_width_at_scroll_top(element_auto_100px, 1, '101px');
test_width_at_scroll_top(element_auto_100px, 50, '150px');
test_width_at_scroll_top(element_auto_100px, 99, '199px');
test_width_at_scroll_top(element_auto_100px, 100, '0px');
// [25%, 75%]
test_approximate_width_at_fraction(element_25p_75p, 0, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.1, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.2, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.35, '120px');
test_approximate_width_at_fraction(element_25p_75p, 0.55, '160px');
test_approximate_width_at_fraction(element_25p_75p, 0.8, '0px');
test_approximate_width_at_fraction(element_25p_75p, 1, '0px');
// [calc(25% + 10px), calc(75% + 10px)]
{
let offset = (t) => (max => (max * 0.25 + 10) * (1 - t) + (max * 0.75 + 10) * t);
test_approximate_width_at_fraction(element_calc_calc, offset(0), '0px');
test_approximate_width_at_fraction(element_calc_calc, offset(0.5), '150px');
test_approximate_width_at_fraction(element_calc_calc, offset(1.1), '0px');
}
</script>

View file

@ -1,44 +0,0 @@
<!DOCTYPE html>
<title>@scroll-timeline: Unknown Descriptors</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/5109">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#contents {
height: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
unknown: 100px;
source: selector(#scroller);
start: 0px;
end: 100px;
time-range: 10s;
foo: bar;
}
#element {
width: 0px;
animation: expand 10s linear timeline;
}
</style>
<div id=scroller>
<div id=contents></div>
</div>
<div id=element></div>
<script>
promise_test(async (t) => {
scroller.scrollTop = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(element).width, '150px');
}, 'Unknown descriptors do not invalidate the @scroll-timeline rule');
</script>

View file

@ -0,0 +1,241 @@
<!DOCTYPE html>
<title>The various animation longhands with progress based animations</title>
<link rel="help" src="https://drafts.csswg.org/css-animations-2">
<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/4862">
<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: 0px; }
to { translate: 100px; }
}
#container {
width: 300px;
height: 300px;
overflow: scroll;
}
#target {
width: 100px;
height: 100px;
translate: none;
}
</style>
<body>
<div id="log"></div>
<script>
"use strict";
const createTargetAndScroller = function(t) {
let container = document.createElement('div');
container.id = 'container';
let target = document.createElement('div');
target.id = 'target';
let content = document.createElement('div');
content.style.blockSize = '100%';
// The height of target is 100px and the content is 100%, so the scroll range
// is [0, 100].
// <div id='container'>
// <div id='target'></div>
// <div style='block-size: 100%;'></div>
// </div>
document.body.appendChild(container);
container.appendChild(target);
container.appendChild(content);
if (t && typeof t.add_cleanup === 'function') {
t.add_cleanup(() => {
content.remove();
target.remove();
container.remove();
});
}
return [target, container];
};
async function scrollTop(element, value) {
element.scrollTop = value;
await waitForNextFrame();
}
// ------------------------------
// Test animation-duration
// ------------------------------
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '25px');
}, 'animation-duration');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '0s linear anim scroll(nearest)';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '100px');
}, 'animation-duration: 0s');
// ------------------------------
// Test animation-iteration-count
// ------------------------------
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '25px');
// Let animation become 50% in the 1st iteration.
target.style.animationIterationCount = '2';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).translate, '50px');
// Let animation become 0% in the 2nd iteration.
target.style.animationIterationCount = '4';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).translate, '0px');
}, 'animation-iteration-count');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationIterationCount = '0';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '0px');
}, 'animation-iteration-count: 0');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationIterationCount = 'infinite';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '100px');
}, 'animation-iteration-count: infinite');
// ------------------------------
// Test animation-direction
// ------------------------------
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
await scrollTop(scroller, 25) // [0, 100].
assert_equals(getComputedStyle(target).translate, '25px');
}, 'animation-direction: normal');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationDirection = 'reverse';
await scrollTop(scroller, 25); // 25% in the reversing direction.
assert_equals(getComputedStyle(target).translate, '75px');
}, 'animation-direction: reverse');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationIterationCount = '2';
target.style.animationDirection = 'alternate';
await scrollTop(scroller, 10); // 20% in the 1st iteration.
assert_equals(getComputedStyle(target).translate, '20px');
await scrollTop(scroller, 60); // 20% in the 2nd iteration (reversing direction).
assert_equals(getComputedStyle(target).translate, '80px');
}, 'animation-direction: alternate');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationIterationCount = '2';
target.style.animationDirection = 'alternate-reverse';
await scrollTop(scroller, 10); // 20% in the 1st iteration (reversing direction).
assert_equals(getComputedStyle(target).translate, '80px');
await scrollTop(scroller, 60); // 20% in the 2nd iteration.
assert_equals(getComputedStyle(target).translate, '20px');
}, 'animation-direction: alternate-reverse');
// ------------------------------
// Test animation-delay
// ------------------------------
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
await scrollTop(scroller, 25); // [0, 100].
assert_equals(getComputedStyle(target).translate, '25px');
// (start delay: 10s) (duration: 10s)
// before active
// |--------------------|--------------------|
// 0px 50px 100px (The scroller)
// 0% 100% (The iteration progress)
// Let animation be in before phase.
target.style.animationDelay = '10s';
target.style.animationDelayStart = '10s'; // crbug.com/1375994
assert_equals(getComputedStyle(target).translate, 'none');
await scrollTop(scroller, 50); // The animation enters active phase.
assert_equals(getComputedStyle(target).translate, '0px');
await scrollTop(scroller, 75); // The ieration progress is 50%.
assert_equals(getComputedStyle(target).translate, '50px');
}, 'animation-delay with a positive value');
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
// active
// |--------------------|
// 0px 100px (The scroller)
// 50% 100% (The iteration progress)
await scrollTop(scroller, 20); // [0, 100].
target.style.animationDelay = '-5s';
target.style.animationDelayStart = '-5s'; // crbug.com/1375994
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).translate, '60px');
}, 'animation-delay with a negative value');
// ------------------------------
// Test animation-fill-mode
// ------------------------------
promise_test(async t => {
let [target, scroller] = createTargetAndScroller(t);
target.style.animation = '10s linear anim scroll(nearest)';
target.style.animationDelay = '10s';
target.style.animationDelayStart = '10s'; // crbug.com/1375994
await scrollTop(scroller, 25);
assert_equals(getComputedStyle(target).translate, 'none');
target.style.animationFillMode = 'backwards';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).translate, '0px');
}, 'animation-fill-mode');
</script>
</body>

View file

@ -5,16 +5,12 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scrollers {
main > div {
overflow: hidden;
height: 0px;
}
#scrollers > div {
overflow: scroll;
width: 100px;
height: 100px;
}
#scrollers > div > div {
main > div > div {
height: 200px;
}
@ -23,10 +19,8 @@
to { top: 200px; }
}
@scroll-timeline top_timeline {
source: selector(#scroller1);
start: 0px;
end: 100px;
#scroller1 {
scroll-timeline: top_timeline;
}
#element {
@ -40,19 +34,19 @@
#element { animation-play-state: paused; }
}
</style>
<div id=scrollers>
<main>
<div id=scroller1><div></div></div>
</div>
<div id=element></div>
<div id=element></div>
</main>
<script>
// Force layout of scrollers.
scroller1.offsetTop;
scroller1.scrollTop = 20;
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element).top, '120px');
}, 'progress based animation timeline works');
window.onload = async () => {
promise_test(async (t) => {
await waitForNextFrame();
const anim = document.getAnimations()[0];
await anim.ready;
scroller1.scrollTop = 20;
await waitForNextFrame();
assert_equals(getComputedStyle(element).top, '120px');
}, 'progress based animation timeline works');
};
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
</head>
<style>
#outer { scroll-timeline-axis: inline; }
#target { scroll-timeline-axis: vertical; }
</style>
<div id="outer">
<div id="target"></div>
</div>
<script>
test_computed_value('scroll-timeline-axis', 'initial', 'block');
test_computed_value('scroll-timeline-axis', 'inherit', 'inline');
test_computed_value('scroll-timeline-axis', 'unset', 'block');
test_computed_value('scroll-timeline-axis', 'revert', 'block');
test_computed_value('scroll-timeline-axis', 'block');
test_computed_value('scroll-timeline-axis', 'inline');
test_computed_value('scroll-timeline-axis', 'vertical');
test_computed_value('scroll-timeline-axis', 'horizontal');
test(() => {
let style = getComputedStyle(document.getElementById('target'));
assert_not_equals(Array.from(style).indexOf('scroll-timeline-axis'), -1);
}, 'The scroll-timeline-axis property shows up in CSSStyleDeclaration enumeration');
test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('scroll-timeline-axis'), -1);
}, 'The scroll-timeline-axis property shows up in CSSStyleDeclaration.cssText');
</script>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<div id="target"></div>
<script>
test_valid_value('scroll-timeline-axis', 'initial');
test_valid_value('scroll-timeline-axis', 'inherit');
test_valid_value('scroll-timeline-axis', 'unset');
test_valid_value('scroll-timeline-axis', 'revert');
test_valid_value('scroll-timeline-axis', 'block');
test_valid_value('scroll-timeline-axis', 'inline');
test_valid_value('scroll-timeline-axis', 'vertical');
test_valid_value('scroll-timeline-axis', 'horizontal');
test_invalid_value('scroll-timeline-axis', 'abc');
test_invalid_value('scroll-timeline-axis', '10px');
test_invalid_value('scroll-timeline-axis', 'auto');
test_invalid_value('scroll-timeline-axis', 'none');
</script>

View file

@ -1,70 +1,45 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#descdef-scroll-timeline-orientation">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller_horizontal_tb, #scroller_vertical_lr{
overflow: scroll;
.scroller {
overflow: hidden;
width: 100px;
height: 100px;
}
#scroller_horizontal_tb {
writing-mode: horizontal-tb;
}
#scroller_vertical_lr {
writing-mode: vertical-lr;
}
.contents {
height: 300px;
width: 300px;
height: 200px;
width: 200px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline_auto {
source: selector(#scroller_horizontal_tb);
orientation: auto;
start: 0px;
end: 100px;
#timeline_initial_axis {
scroll-timeline: timeline_initial_axis;
}
@scroll-timeline timeline_vertical {
source: selector(#scroller_horizontal_tb);
orientation: vertical;
start: 0px;
end: 100px;
#timeline_vertical {
scroll-timeline: vertical timeline_vertical;
}
@scroll-timeline timeline_horizontal {
source: selector(#scroller_horizontal_tb);
orientation: horizontal;
start: 0px;
end: 100px;
#timeline_horizontal {
scroll-timeline: horizontal timeline_horizontal;
}
@scroll-timeline timeline_block_in_horizontal {
source: selector(#scroller_horizontal_tb);
orientation: block;
start: 0px;
end: 100px;
#timeline_block_in_horizontal {
scroll-timeline: block timeline_block_in_horizontal;
}
@scroll-timeline timeline_inline_in_horizontal {
source: selector(#scroller_horizontal_tb);
orientation: inline;
start: 0px;
end: 100px;
#timeline_inline_in_horizontal {
scroll-timeline: inline timeline_inline_in_horizontal;
}
@scroll-timeline timeline_block_in_vertical {
source: selector(#scroller_vertical_lr);
orientation: block;
start: 0px;
end: 100px;
#timeline_block_in_vertical {
scroll-timeline: block timeline_block_in_vertical;
writing-mode: vertical-lr;
}
@scroll-timeline timeline_inline_in_vertical {
source: selector(#scroller_vertical_lr);
orientation: inline;
start: 0px;
end: 100px;
#timeline_inline_in_vertical {
scroll-timeline: inline timeline_inline_in_vertical;
writing-mode: vertical-lr;
}
#container > div {
width: 0px;
@ -76,7 +51,7 @@
@supports not (animation-timeline:foo) {
#container > div { animation-play-state: paused; }
}
#element_auto { animation-timeline: timeline_auto; }
#element_initial_axis { animation-timeline: timeline_initial_axis; }
#element_vertical { animation-timeline: timeline_vertical; }
#element_horizontal { animation-timeline: timeline_horizontal; }
#element_block_in_horizontal { animation-timeline: timeline_block_in_horizontal; }
@ -84,14 +59,15 @@
#element_block_in_vertical { animation-timeline: timeline_block_in_vertical; }
#element_inline_in_vertical { animation-timeline: timeline_inline_in_vertical; }
</style>
<div id=scroller_horizontal_tb>
<div class=contents></div>
</div>
<div id=scroller_vertical_lr>
<div class=contents></div>
</div>
<div class=scroller id=timeline_initial_axis><div class=contents></div></div>
<div class=scroller id=timeline_vertical><div class=contents></div></div>
<div class=scroller id=timeline_horizontal><div class=contents></div></div>
<div class=scroller id=timeline_block_in_horizontal><div class=contents></div></div>
<div class=scroller id=timeline_inline_in_horizontal><div class=contents></div></div>
<div class=scroller id=timeline_block_in_vertical><div class=contents></div></div>
<div class=scroller id=timeline_inline_in_vertical><div class=contents></div></div>
<div id=container>
<div id=element_auto></div>
<div id=element_initial_axis></div>
<div id=element_vertical></div>
<div id=element_horizontal></div>
<div id=element_block_in_horizontal></div>
@ -101,47 +77,50 @@
</div>
<script>
// Animations linked to vertical scroll-timelines are at 75% progress.
scroller_horizontal_tb.scrollTop = 75;
scroller_vertical_lr.scrollTop = 75;
timeline_initial_axis.scrollTop = 75;
timeline_vertical.scrollTop = 75;
timeline_block_in_horizontal.scrollTop = 75;
timeline_inline_in_vertical.scrollTop = 75;
// Animations linked to horizontal scroll-timelines are at 25% progress.
scroller_horizontal_tb.scrollLeft = 25;
scroller_vertical_lr.scrollLeft = 25;
timeline_horizontal.scrollLeft = 25;
timeline_block_in_vertical.scrollLeft = 25;
timeline_inline_in_horizontal.scrollLeft = 25;
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_auto).width, '175px');
}, 'Orientation auto behaves as expected');
assert_equals(getComputedStyle(element_initial_axis).width, '175px');
}, 'Initial axis');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_vertical).width, '175px');
}, 'Orientation vertical behaves as expected');
}, 'Vertical axis');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_horizontal).width, '125px');
}, 'Orientation horizontal behaves as expected');
}, 'Horizontal axis');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_block_in_horizontal).width, '175px');
}, 'Orientation block behaves as expected in horizontal writing-mode');
}, 'Block axis in horizontal writing-mode');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(element_inline_in_horizontal).width, '125px');
}, 'Orientation inline behaves as expected in horizontal writing-mode');
}, 'Inline axis in horizontal writing-mode');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(scroller_vertical_lr).writingMode, 'vertical-lr');
assert_equals(getComputedStyle(timeline_block_in_vertical).writingMode, 'vertical-lr');
assert_equals(getComputedStyle(element_block_in_vertical).width, '125px');
}, 'Orientation block behaves as expected in vertical writing-mode');
}, 'Block axis in vertical writing-mode');
promise_test(async (t) => {
await waitForNextFrame();
assert_equals(getComputedStyle(scroller_vertical_lr).writingMode, 'vertical-lr');
assert_equals(getComputedStyle(timeline_inline_in_vertical).writingMode, 'vertical-lr');
assert_equals(getComputedStyle(element_inline_in_vertical).width, '175px');
}, 'Orientation inline behaves as expected in vertical writing-mode');
}, 'Inline axis in vertical writing-mode');
</script>

View file

@ -1,273 +0,0 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<div id="target"></div>
<script>
// Runs a function while a stylesheet is temporarily inserted into the
// document.
function with_stylesheet(text, func) {
let s = document.createElement('style');
try {
s.textContent = text;
document.documentElement.append(s);
func(s.sheet.rules);
} finally {
s.remove();
}
}
// Runs a test while a stylesheet is temporarily inserted into the
// document.
function test_stylesheet(text, func, description) {
test(() => {
with_stylesheet(text, func);
}, description);
}
function test_valid_rule(text, description) {
test_stylesheet(text, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
}, description);
}
function test_invalid_rule(text, description) {
test_stylesheet(text, (rules) => {
assert_equals(rules.length, 0);
}, description);
}
// Verify that for the _specifed_ value for a given _descriptor_, the _expected_
// string can be observed via the equivalent attribute on CSSScrollTimelineRule.
function test_descriptor(descriptor, specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
let attribute = descriptor.replaceAll(/\-./g, x => x[1].toUpperCase());
test_stylesheet(`@scroll-timeline test { ${descriptor}:${specified}; }`, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
if (Array.isArray(expected)) {
assert_in_array(rules[0][attribute], expected, "serialization should be sound");
} else {
assert_equals(rules[0][attribute], expected, "serialization should be canonical");
}
}, `CSSScrollTimelineRule.${attribute} ${specified}`);
}
test_valid_rule('@scroll-timeline foo {}', 'Empty block');
test_valid_rule('@scroll-timeline foo {', 'EOF ends block');
test_valid_rule('@scroll-timeline "foo" {}', 'Timeline name can be a <string>');
test_invalid_rule('@scroll-timeline', 'Missing prelude');
test_invalid_rule('@scroll-timeline foo', 'Missing block');
test_invalid_rule('@scroll-timeline {}', 'Missing timeline name');
test_invalid_rule('@scroll-timeline 123 {}', 'Timeline name must be an identifier');
test_invalid_rule('@scroll-timeline none {}', 'Timeline name must match <custom-ident>');
test_invalid_rule('@scroll-timeline NONE {}', 'Timeline name must match <custom-ident> (caps)');
test_invalid_rule('@scroll-timeline NoNe {}', 'Timeline name must match <custom-ident> (mixed)');
test_invalid_rule('@scroll-timeline initial {}', 'Timeline name may not be initial');
test_invalid_rule('@scroll-timeline inherit {}', 'Timeline name may not be inherit');
test_invalid_rule('@scroll-timeline unset {}', 'Timeline name may not be unset');
test_invalid_rule('@scroll-timeline revert {}', 'Timeline name may not be revert');
test_invalid_rule('@scroll-timeline default {}', 'Timeline name may not be default');
test_invalid_rule('@scroll-timeline foo bar {}', 'Extra timeline name');
// CSSRule.type
test(() => {
with_stylesheet(`@scroll-timeline valid { }`, (rules) => {
assert_equals(rules.length, 1);
let rule = rules[0];
assert_equals(rule.constructor.name, 'CSSScrollTimelineRule');
assert_equals(rule.type, 0);
});
}, 'CSSRule.type returns 0');
// CSSScrollTimelineRule.name
function test_name(specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
test_stylesheet(`@scroll-timeline ${specified} { }`, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
assert_equals(rules[0].name, expected);
}, `CSSScrollTimelineRule.name ${specified}`);
}
test_name('foo');
test_name('Foo');
test_name('f___123');
test_name('a\\9 b', 'a\tb'); // U+0009 CHARACTER TABULATION
test_name('"foo"', 'foo');
test_name('"none"', 'none');
// CSSScrollTimelineRule.cssText
function test_csstext(description, specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
test_stylesheet(specified, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
assert_equals(rules[0].cssText, expected);
}, `CSSScrollTimelineRule.cssText: ${description}`);
}
test_csstext(
'empty rule',
`@scroll-timeline timeline {}`,
`@scroll-timeline timeline { }`);
// U+0009 CHARACTER TABULATION
test_csstext(
'escaped name',
`@scroll-timeline tab\\9 tab {}`,
`@scroll-timeline tab\\9 tab { }`);
test_csstext(
'source descriptor',
`@scroll-timeline timeline { source: selector(#foo); }`);
test_csstext(
'orientation descriptor',
`@scroll-timeline timeline { orientation: inline; }`);
// https://github.com/w3c/csswg-drafts/issues/6617
test_csstext(
'scroll-offsets descriptor (none)',
`@scroll-timeline timeline { scroll-offsets: none; }`,
`@scroll-timeline timeline { scroll-offsets: none; }`,);
test_csstext(
'scroll-offsets descriptor (auto)',
`@scroll-timeline timeline { scroll-offsets: auto; }`);
test_csstext(
'scroll-offsets descriptor (container-based offset, px)',
`@scroll-timeline timeline { scroll-offsets: 100px; }`);
test_csstext(
'scroll-offsets descriptor (container-based offset, percentage)',
`@scroll-timeline timeline { scroll-offsets: 100%; }`);
test_csstext(
'scroll-offsets descriptor (element offset)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar); }`);
test_csstext(
'scroll-offsets descriptor (element offset with edge)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start; }`);
test_csstext(
'scroll-offsets descriptor (element offset with threshold)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) 1; }`);
test_csstext(
'scroll-offsets descriptor (element offset with edge and threshold)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start 1; }`);
test_csstext(
'scroll-offsets descriptor (element offset with threshold and edge)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) 1 start; }`,
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start 1; }`);
test_csstext(
'scroll-offsets descriptor (multiple offsets)',
`@scroll-timeline timeline { scroll-offsets: auto, selector(#bar) start 1; }`);
test_csstext(
'defaults',
`@scroll-timeline timeline { source: auto; orientation: auto; scroll-offsets: none; }`);
test_csstext(
'order',
`@scroll-timeline timeline { orientation: auto; source: auto; scroll-offsets: none; }`,
`@scroll-timeline timeline { source: auto; orientation: auto; scroll-offsets: none; }`);
// CSSScrollTimelineRule.source
function test_source(specified, expected) {
test_descriptor('source', specified, expected);
}
test_source('selector(#foo)');
test_source('selector( #foo )', 'selector(#foo)');
test_source(' selector(#foo) ', 'selector(#foo)');
test_source('none');
test_source(' none ', 'none');
test_source('selector(#a\\9 b)');
test_source('auto');
test_source('#foo', 'auto');
test_source('', 'auto');
test_source('element(#foo)', 'auto');
test_source('selector(#foo more)', 'auto');
test_source('selector(html)', 'auto');
test_source('selector(foo)', 'auto');
test_source('selector(:before)', 'auto');
test_source('selector(*)', 'auto');
test_source('selector(.a)', 'auto');
test_source('selector(.a, .b)', 'auto');
// CSSScrollTimelineRule.orientation
function test_orientation(specified, expected) {
test_descriptor('orientation', specified, expected);
}
test_orientation('auto');
test_orientation('block');
test_orientation('inline');
test_orientation('horizontal');
test_orientation('vertical');
test_orientation(' vertical ', 'vertical');
test_orientation('', 'auto');
test_orientation('foo', 'auto');
test_orientation('10px', 'auto');
test_orientation('red', 'auto');
// CSSScrollTimelineRule.scrollOffsets
function test_offsets(specified, expected) {
test_descriptor('scroll-offsets', specified, expected);
}
test_offsets('none');
test_offsets(' none ', 'none');
test_offsets('', 'none');
test_offsets('red', 'none');
test_offsets('#fff', 'none');
test_offsets('unset', 'none');
test_offsets('unknown(#foo)', 'none');
test_offsets('start', 'none');
test_offsets('end', 'none');
test_offsets('3', 'none');
test_offsets('selector(#foo) 3 start 10', 'none');
test_offsets('0%, 100%, selector(.a)', 'none');
test_offsets('selector(#foo) 3 end, start', 'none');
test_offsets('auto');
test_offsets('10em');
test_offsets('10%');
test_offsets('calc(1px + 1%)', ['calc(1px + 1%)', 'calc(1% + 1px)']);
test_offsets('selector(#foo)');
test_offsets(' selector(#foo)', 'selector(#foo)');
test_offsets('selector(#foo) start');
test_offsets('selector(#foo) start 3');
test_offsets('selector(#foo) 3');
test_offsets('selector(#foo) 3.5');
test_offsets('selector(#foo) end');
test_offsets('selector(#foo) end 3');
test_offsets('selector(#foo) 3 end', 'selector(#foo) end 3');
test_offsets(' auto , auto ', 'auto, auto');
test_offsets('10%, 100px');
test_offsets('auto, 100%');
test_offsets('0%, selector( #foo) 3 end ', "0%, selector(#foo) end 3");
test_offsets('selector(#foo) end 3, selector(#bar)');
test_offsets('0%, auto, selector(#foo) start, auto, selector(#bar) 12.3');
</script>

View file

@ -1,10 +1,11 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The default scroll-timeline at rule in the iframe for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>The default scroll() timeline in the iframe for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-iframe-ref.html">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline">
<link rel="match" href="scroll-timeline-default-iframe-ref.html">
<meta name="fuzzy" content="25;100">
<iframe id="target" width="400" height="400" srcdoc='
<html>
@ -13,11 +14,6 @@
from { transform: translateY(0px); }
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
@ -27,7 +23,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
width: 100px;

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>Reference for default @scroll-timeline</title>
<title>Reference for default scroll() timeline</title>
<iframe width="400" height="400" srcdoc='
<html>
<style>

View file

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The default scroll-timeline at rule in the iframe</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>The default scroll() timeline in the iframe</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-iframe-ref.html">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline">
<link rel="match" href="scroll-timeline-default-iframe-ref.html">
<iframe id="target" width="400" height="400" srcdoc='
<html>
@ -13,11 +13,6 @@
from { transform: translateY(0px); }
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
@ -27,7 +22,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
width: 100px;

View file

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The default scroll-timeline at rule</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>The default scroll() timeline for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-ref.html">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline">
<link rel="match" href="scroll-timeline-default-ref.html">
<style>
@keyframes update {
@ -12,12 +12,6 @@
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
@ -28,7 +22,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
@ -45,17 +39,24 @@
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
window.addEventListener('load', function() {
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the halfway point.
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = 0.5 * maxScroll;
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
await waitForNextFrame();
await waitForNextFrame();
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -0,0 +1,63 @@
<html class="reftest-wait">
<title>The default scroll() timeline in quirks mode</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline">
<link rel="match" href="scroll-timeline-default-ref.html">
<style>
@keyframes update {
from { transform: translateY(0px); }
to { transform: translateY(200px); }
}
html {
min-height: 100%;
padding-bottom: 100px;
}
#box {
width: 100px;
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: scroll();
}
#covered {
width: 100px;
height: 100px;
background-color: red;
}
* {
margin-top: 0px;
margin-bottom: 0px;
}
</style>
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the halfway point. Then advance to the next frame
// to pick up the new timeline time.
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = 0.5 * maxScroll;
await waitForNextFrame();
await waitForNextFrame();
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>Reference for default @scroll-timeline</title>
<title>Reference for default scroll() timeline</title>
<style>
html {
min-height: 100%;

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>Reference for default @scroll-timeline with vertical-rl</title>
<title>Reference for default scroll() timeline with vertical-rl</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<style>
html {

View file

@ -1,12 +1,12 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The default scroll-timeline at rule with writing-mode:vertical-rl</title>
<title>The default scroll() timeline with writing-mode:vertical-rl</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using
the default scroll-timeline at rule with writing-mode:vertical-rl">
<link rel="match" href="at-scroll-timeline-default-descriptors-writing-mode-rl-ref.html">
the default scroll() timeline with writing-mode:vertical-rl">
<link rel="match" href="scroll-timeline-default-writing-mode-rl-ref.html">
<style>
@keyframes update {
@ -14,12 +14,6 @@
to { transform: translateX(-200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-block-size: 100%;
padding-block-end: 100px;
@ -31,7 +25,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
@ -48,16 +42,24 @@
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
window.addEventListener('load', function() {
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the halfway point.
const maxScroll = scroller.scrollWidth - scroller.clientWidth;
scroller.scrollLeft = -0.5 * maxScroll;
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
await waitForNextFrame();
await waitForNextFrame();
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The default scroll-timeline at rule for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>The default scroll() timeline</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-ref.html">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline">
<link rel="match" href="scroll-timeline-default-ref.html">
<style>
@keyframes update {
@ -12,12 +12,6 @@
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
@ -28,7 +22,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
@ -46,16 +40,24 @@
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
window.addEventListener('load', function() {
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the halfway point.
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = 0.5 * maxScroll;
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
await waitForNextFrame();
await waitForNextFrame();
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -0,0 +1,29 @@
<!-- Quirks mode -->
<title>Tests the document scroller in quirks mode</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1180575">
<link rel="author" href="mailto:andruud@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/scroll-animations/scroll-timelines/testcommon.js"></script>
<script src="/css/css-animations/support/testcommon.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 100; }
to { z-index: 100; }
}
#element {
animation: anim forwards scroll(root);
}
</style>
<div id=element></div>
<script>
'use strict';
promise_test(async () => {
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(element).zIndex, "100");
});
</script>

View file

@ -1,15 +1,17 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
<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>
#scrollers > div {
overflow: scroll;
main > div {
overflow: hidden;
width: 100px;
height: 100px;
}
#scrollers > div > div {
main > div > div {
height: 200px;
}
@keyframes expand {
@ -27,11 +29,11 @@
animation-timing-function: steps(10, end);
}
</style>
<div id=scrollers>
<main>
<div id=scroller1><div></div></div>
<div id=scroller2><div></div></div>
</div>
<div id=container></div>
<div id=container></div>
</main>
<script>
// Force layout of scrollers.
scroller1.offsetTop;
@ -49,35 +51,6 @@
return element;
}
function insertSheet(text) {
let style = document.createElement('style');
style.textContent = text;
container.append(style);
return style;
}
// Insert an @scroll-timeline rule given 'options', where each option
// has a reasonable default.
function insertScrollTimeline(options) {
if (typeof(options) == 'undefined')
options = {};
if (typeof(options.name) == 'undefined')
options.name = 'timeline';
if (typeof(options.source) == 'undefined')
options.source = 'selector(#scroller1)';
if (typeof(options.start) == 'undefined')
options.start = '0px';
if (typeof(options.end) == 'undefined')
options.end = '100px';
return insertSheet(`
@scroll-timeline ${options.name} {
source: ${options.source};
start: ${options.start};
end: ${options.end};
}
`);
}
// Runs a test with dynamically added/removed elements or CSS rules.
// Each test is instantiated twice: once for the initial style resolve where
// the DOM change was effectuated, and once after scrolling.
@ -91,13 +64,16 @@
} finally {
while (container.firstChild)
container.firstChild.remove();
scroller1.style = '';
scroller2.style = '';
}
}, description);
};
// Verify that the computed style is as expected immediately after the
// rule change took place.
// Verify that the computed style is as expected after a full frame update
// following the rule change took place.
instantiate(async (element, expected) => {
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(element).width, expected);
}, description + ' [immediate]');
@ -120,43 +96,57 @@
await assert_width(element, '100px');
// Switch to scroll timeline.
let sheet1 = insertScrollTimeline();
let sheet2 = insertSheet('#element { animation-timeline: timeline; }');
scroller1.style.scrollTimelineName = 'timeline';
element.style.animationTimeline = 'timeline';
await assert_width(element, '120px');
// Switching from ScrollTimeline -> DocumentTimeline should preserve
// current time.
sheet1.remove();
sheet2.remove();
scroller1.style = '';
element.style = '';
await assert_width(element, '120px');
}, 'Switching between document and scroll timelines');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
// Note: #scroller1 is at 20%, and #scroller2 is at 40%.
insertScrollTimeline({name: 'timeline1', source: 'selector(#scroller1)'});
insertScrollTimeline({name: 'timeline2', source: 'selector(#scroller2)'});
// Flush style and create the animation with play pending.
getComputedStyle(element).animation;
insertSheet(`
.tl1 { animation-timeline: timeline1; }
.tl2 { animation-timeline: timeline2; }
`);
let anim = element.getAnimations()[0];
assert_true(anim.pending, "The animation is in play pending");
// Switch to scroll timeline for a pending animation.
scroller1.style.scrollTimelineName = 'timeline';
element.style.animationTimeline = 'timeline';
await anim.ready;
assert_false(anim.pending, "The animation is not pending");
await assert_width(element, '120px');
}, 'Switching pending animation from document to scroll timelines');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
// Note: #scroller1 is at 20%, and #scroller2 is at 40%.
scroller1.style.scrollTimelineName = 'timeline1';
scroller2.style.scrollTimelineName = 'timeline2';
await assert_width(element, '100px');
element.classList.add('tl1');
element.style.animationTimeline = 'timeline1';
await assert_width(element, '120px');
element.classList.add('tl2');
element.style.animationTimeline = 'timeline2';
await assert_width(element, '140px');
element.classList.remove('tl2');
element.style.animationTimeline = 'timeline1';
await assert_width(element, '120px');
// Switching from ScrollTimeline -> DocumentTimeline should preserve
// current time.
element.classList.remove('tl1');
element.style.animationTimeline = '';
await assert_width(element, '120px');
}, 'Changing computed value of animation-timeline changes effective timeline');
@ -164,124 +154,95 @@
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
insertScrollTimeline({source: 'selector(#scroller1)'});
insertSheet(`
.scroll { animation-timeline: timeline; }
.none { animation-timeline: none; }
`);
scroller1.style.scrollTimelineName = 'timeline';
// DocumentTimeline applies by default.
await assert_width(element, '100px');
// DocumentTimeline -> none
element.classList.add('none');
element.style.animationTimeline = 'none';
await assert_width(element, '0px');
// none -> DocumentTimeline
element.classList.remove('none');
element.style.animationTimeline = '';
await assert_width(element, '100px');
// DocumentTimeline -> ScrollTimeline
element.classList.add('scroll');
element.style.animationTimeline = 'timeline';
await assert_width(element, '120px');
// ScrollTimeline -> none
element.classList.add('none');
await assert_width(element, '0px');
element.style.animationTimeline = 'none';
await assert_width(element, '120px');
// none -> ScrollTimeline
element.classList.remove('none');
element.style.animationTimeline = 'timeline';
await assert_width(element, '120px');
}, 'Changing to/from animation-timeline:none');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
insertSheet('#element { animation-timeline: timeline; }');
element.style.animationTimeline = 'timeline';
// Unknown animation-timeline, current time held at zero.
await assert_width(element, '100px');
insertScrollTimeline({source: 'selector(#scroller1)'});
scroller1.style.scrollTimelineName = 'timeline';
await assert_width(element, '120px');
insertScrollTimeline({source: 'selector(#scroller2)'});
scroller2.style.scrollTimelineName = 'timeline';
await assert_width(element, '140px');
}, 'Changing the source descriptor switches effective timeline');
}, 'Changing scroll-timeline on preceding elements affects target element');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
insertSheet('#element { animation-timeline: timeline; }');
// Unknown animation-timeline, current time held at zero.
await assert_width(element, '100px');
insertScrollTimeline({start: '0px'});
await assert_width(element, '120px');
insertScrollTimeline({start: '50px'});
await assert_width(element, '0px');
}, 'Changing the start descriptor switches effective timeline');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
insertSheet('#element { animation-timeline: timeline; }');
// Unknown animation-timeline, current time held at zero.
await assert_width(element, '100px');
insertScrollTimeline({end: '100px'});
await assert_width(element, '120px');
insertScrollTimeline({end: '10px'});
await assert_width(element, '0px');
}, 'Changing the end descriptor switches effective timeline');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
let reverse = insertSheet('#element { animation-direction: reverse; }');
insertSheet('#element { animation-timeline: timeline; }');
element.style.animationDirection = 'reverse';
element.style.animationTimeline = 'timeline';
// Unknown animation-timeline, current time held at zero.
await assert_width(element, '200px');
// Note: #scroller1 is at 20%.
insertScrollTimeline({source: 'selector(#scroller1)'});
scroller1.style.scrollTimelineName = 'timeline';
await assert_width(element, '180px');
// Note: #scroller1 is at 40%.
insertScrollTimeline({source: 'selector(#scroller2)'});
// Note: #scroller2 is at 40%.
scroller2.style.scrollTimelineName = 'timeline';
await assert_width(element, '160px');
reverse.remove();
element.style.animationDirection = '';
await assert_width(element, '140px');
}, 'Reverse animation direction');
dynamic_rule_test(async (t, assert_width) => {
let element = insertElement();
insertSheet('#element { animation-timeline: timeline; }');
element.style.animationTimeline = 'timeline';
// Unknown animation-timeline, current time held at zero.
await assert_width(element, '100px');
// Note: #scroller1 is at 20%.
insertScrollTimeline({source: 'selector(#scroller1)'});
scroller1.style.scrollTimelineName = 'timeline';
await assert_width(element, '120px');
let paused = insertSheet('#element { animation-play-state: paused; }');
element.style.animationPlayState = 'paused';
// We should still be at the same position after pausing.
await assert_width(element, '120px');
// Note: #scroller1 is at 40%.
insertScrollTimeline({source: 'selector(#scroller2)'});
// Note: #scroller2 is at 40%.
scroller2.style.scrollTimelineName = 'timeline';
// Even when switching timelines, we should be at the same position until
// we unpause.
await assert_width(element, '120px');
// Unpausing should synchronize to the scroll position.
paused.remove();
element.style.animationPlayState = '';
await assert_width(element, '140px');
}, 'Switching timelines while paused');
@ -289,20 +250,18 @@
let element = insertElement();
// Note: #scroller1 is at 20%.
insertScrollTimeline({source: 'selector(#scroller1)'});
scroller1.style.scrollTimelineName = 'timeline';
await assert_width(element, '100px');
insertSheet(`#element {
animation-timeline: timeline;
animation-play-state: paused;
}`);
element.style.animationTimeline = 'timeline';
element.style.animationPlayState = 'paused';
// Pausing should happen before the timeline is modified. (Tentative).
// https://github.com/w3c/csswg-drafts/issues/5653
await assert_width(element, '100px');
insertSheet('#element { animation-play-state: running; }');
element.style.animationPlayState = 'running';
await assert_width(element, '120px');
}, 'Switching timelines and pausing at the same time');
</script>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>Reference for default @scroll-timeline</title>
<title>Reference for the default scroll() timeline</title>
<style>
html {
min-height: 100%;

View file

@ -1,12 +1,12 @@
<!DOCTYPE html>
<html class="reftest-wait">
<title>The default scroll-timeline at rule when the frame size changed</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>The default scroll() timeline when the frame size changed</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using
the default scroll-timeline at rule and update the
the default scroll() timeline and update the
frame size">
<link rel="match" href="at-scroll-timeline-frame-size-changed-ref.html">
<link rel="match" href="scroll-timeline-frame-size-changed-ref.html">
<style>
@keyframes update {
@ -14,12 +14,6 @@
to { transform: translateY(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
html {
min-height: 100%;
padding-bottom: 100px;
@ -30,7 +24,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll();
}
#covered {
@ -48,21 +42,26 @@
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
window.addEventListener('load', function() {
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the 25% point.
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = 0.25 * maxScroll;
await waitForNextFrame();
window.requestAnimationFrame(() => {
// Update scroll range to make the current position become 50% point.
scroller.style.paddingBottom = "50px";
// Update scroll range to make the current position become 50% point.
scroller.style.paddingBottom = "50px";
await waitForNextFrame();
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
});
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<title>scroll-timeline and container queries</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand">
<link rel="help" src="https://drafts.csswg.org/css-contain-3/#container-queries">
<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>
#outer {
height: 100px;
width: 150px;
}
#container {
container-type: size;
}
#scroller {
overflow: auto;
width: auto;
height: 100px;
}
#scroller > div {
height: 200px;
}
/* This does not apply initially. */
@container (width > 200px) {
#scroller {
scroll-timeline: timeline;
}
}
@keyframes expand {
from { background-color: rgb(100, 100, 100); }
to { background-color: rgb(200, 200, 200); }
}
#element {
height: 10px;
width: 10px;
animation: expand 10s linear timeline;
background-color: rgb(0, 0, 0);
}
</style>
<div id=outer>
<div id=container>
<div id=scroller>
<div></div>
</div>
<div id=element>
</div>
</div>
</div>
<script>
promise_test(async (t) => {
element.offsetTop;
scroller.scrollTop = 50;
await waitForNextFrame();
// Unknown timeline, time held at zero.
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(100, 100, 100)');
// This causes the timeline to be created.
outer.style.width = '250px';
// Check value with getComputedStyle immediately, which is the unanimated
// value since the scroll timeline is inactive before the next frame.
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(0, 0, 0)');
// Also check value after one frame.
await waitForNextFrame();
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(150, 150, 150)');
}, 'Timeline appearing via container queries');
</script>

View file

@ -1,6 +1,5 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#phase-algorithm">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
<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>
@ -24,24 +23,18 @@
animation-timeline: timeline;
}
</style>
<div id="container"></div>
<div id=element></div>
<div id="container">
<div id=element></div>
</div>
<script>
promise_test(async (t) => {
let div = document.createElement('div');
div.setAttribute('id', 'scroller');
div.style.scrollTimeline = 'timeline';
div.innerHTML = '<div id=contents></div>';
try {
container.innerHTML = `
<div id=scroller>
<div id=contents></div>
</div>
<style>
@scroll-timeline timeline {
source: selector(#scroller);
start: 0px;
end: 100px;
}
</style>
`;
container.insertBefore(div, element);
// The source has no layout box at the time the scroll timeline is created.
assert_equals(getComputedStyle(element).width, '0px');
@ -51,7 +44,7 @@ promise_test(async (t) => {
// The timeline should now be active, and the animation should apply:
assert_equals(getComputedStyle(element).width, '100px');
} finally {
container.innerHTML = '';
div.remove();
}
}, 'Animation does not apply when timeline is initially inactive');

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>Reference for @scroll-timeline with inline orientation</title>
<title>Reference for scroll timeline with inline orientation and root scroller</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<style>
html {

View file

@ -1,12 +1,12 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The scroll-timeline at rule with inline orientation and default source</title>
<title>Scroll timeline with inline orientation and root scroller</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#descdef-scroll-timeline-orientation">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the inline orientation">
<link rel="match" href="at-scroll-timeline-inline-orientation-ref.html">
<link rel="match" href="scroll-timeline-inline-orientation-ref.html">
<style>
@keyframes update {
@ -14,12 +14,6 @@
to { transform: translateX(200px); }
}
@scroll-timeline test-timeline {
source: auto;
orientation: inline;
scroll-offsets: none;
}
html {
min-width: 100%;
padding-right: 100px;
@ -31,7 +25,7 @@
height: 100px;
background-color: green;
animation: update 1s linear;
animation-timeline: test-timeline;
animation-timeline: scroll(inline root);
display: inline-block;
}
@ -51,16 +45,24 @@
<div id="box"></div>
<div id="covered"></div>
<script src="/web-animations/testcommon.js"></script>
<script>
window.addEventListener('load', function() {
document.documentElement.addEventListener('TestRendered', async () => {
runTest();
}, { once: true });
async function runTest() {
const scroller = document.scrollingElement;
await waitForCompositorReady();
// Move the scroller to the halfway point.
const maxScroll = scroller.scrollWidth - scroller.clientWidth;
scroller.scrollLeft = 0.5 * maxScroll;
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("reftest-wait");
});
});
await waitForNextFrame();
await waitForNextFrame();
document.documentElement.classList.remove("reftest-wait");
}
</script>

View file

@ -14,12 +14,6 @@
from { height: 100px; }
to { height: 100px; }
}
@scroll-timeline timeline1 {
source: selector(#scroller1);
}
@scroll-timeline timeline2 {
source: selector(#scroller2);
}
main {
height: 0px;
overflow: hidden;
@ -40,18 +34,19 @@
animation: expand_height 10s timeline2;
}
</style>
<main id=main></main>
<div id=element1></div>
<div>
<div id=element2></div>
</div>
<main id=main>
<div id=element1></div>
<div>
<div id=element2></div>
</div>
</main>
<script>
function insertScroller(id) {
function insertScroller(timeline_name) {
let scroller = document.createElement('div');
scroller.setAttribute('id', id);
scroller.setAttribute('class', 'scroller');
scroller.classList.add('scroller');
scroller.style.scrollTimeline = timeline_name;
scroller.append(document.createElement('div'));
main.append(scroller);
main.insertBefore(scroller, element1);
}
promise_test(async () => {
@ -60,15 +55,15 @@
let events1 = [];
let events2 = [];
insertScroller('scroller1');
// Even though #scroller1 was just inserted into the DOM, |timeline1|
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('scroller2');
insertScroller('timeline2');
assert_equals(getComputedStyle(element2).height, '1px');
})).observe(element1);

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
</head>
<style>
#outer { scroll-timeline-name: foo; }
#target { scroll-timeline-name: bar; }
</style>
<div id="outer">
<div id="target"></div>
</div>
<script>
test_computed_value('scroll-timeline-name', 'initial', 'none');
test_computed_value('scroll-timeline-name', 'inherit', 'foo');
test_computed_value('scroll-timeline-name', 'unset', 'none');
test_computed_value('scroll-timeline-name', 'revert', 'none');
test_computed_value('scroll-timeline-name', 'none');
test_computed_value('scroll-timeline-name', 'test');
test_computed_value('scroll-timeline-name', 'tEst');
test(() => {
let style = getComputedStyle(document.getElementById('target'));
assert_not_equals(Array.from(style).indexOf('scroll-timeline-name'), -1);
}, 'The scroll-timeline-name property shows up in CSSStyleDeclaration enumeration');
test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('scroll-timeline-name'), -1);
}, 'The scroll-timeline-name property shows up in CSSStyleDeclaration.cssText');
</script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<div id="target"></div>
<script>
test_valid_value('scroll-timeline-name', 'initial');
test_valid_value('scroll-timeline-name', 'inherit');
test_valid_value('scroll-timeline-name', 'unset');
test_valid_value('scroll-timeline-name', 'revert');
test_valid_value('scroll-timeline-name', 'none');
test_valid_value('scroll-timeline-name', 'abc');
test_valid_value('scroll-timeline-name', ' abc', 'abc');
test_valid_value('scroll-timeline-name', 'aBc');
test_valid_value('scroll-timeline-name', 'auto');
test_invalid_value('scroll-timeline-name', 'default');
test_invalid_value('scroll-timeline-name', '10px');
test_invalid_value('scroll-timeline-name', 'foo bar');
test_invalid_value('scroll-timeline-name', '"foo" "bar"');
test_invalid_value('scroll-timeline-name', 'rgb(1, 2, 3)');
test_invalid_value('scroll-timeline-name', '#fefefe');
</script>

View file

@ -1,23 +1,19 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>The default scroll-timeline at rule with paused animations</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>Scroll timeline with paused animations</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/css-animations/#animation-play-state">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/scroll-animations/scroll-timelines/testcommon.js"></script>
<script src="/css/css-animations/support/testcommon.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
.fill-vh {
width: 100px;
height: 100vh;
@ -28,40 +24,66 @@
<script>
'use strict';
test(t => {
const div = addDiv(t, { style: 'width: 50px; height: 100px;' });
const filling = addDiv(t, { class: 'fill-vh' });
const scroller = document.scrollingElement;
getComputedStyle(document.scrollingElement).height
div.style.animation = 'anim 100s linear timeline paused';
assert_equals(getComputedStyle(div).width, '100px');
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = maxScroll;
assert_equals(getComputedStyle(div).width, '100px');
async function resetScrollPosition() {
// Reset to 0 so we don't affect following tests.
document.scrollingElement.scrollTop = 0;
}, 'Test that the scroll animation is paused');
return waitForNextFrame();
}
test(t => {
promise_test(async t => {
const div = addDiv(t, { style: 'width: 50px; height: 100px;' });
const filling = addDiv(t, { class: 'fill-vh' });
const scroller = document.scrollingElement;
getComputedStyle(document.scrollingElement).height
t.add_cleanup(resetScrollPosition);
div.style.animation = 'anim 100s linear timeline';
div.style.animation = 'anim 100s linear paused scroll(root)';
await waitForCSSScrollTimelineStyle();
const anim = div.getAnimations()[0];
await anim.ready;
assert_percents_equal(anim.currentTime, 0, 'timeline time reset');
assert_equals(getComputedStyle(div).width, '100px');
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = maxScroll;
await waitForNextFrame();
assert_equals(getComputedStyle(div).width, '100px');
}, 'Test that the scroll animation is paused');
promise_test(async t => {
const div = addDiv(t, { style: 'width: 50px; height: 100px;' });
const filling = addDiv(t, { class: 'fill-vh' });
const scroller = document.scrollingElement;
await waitForNextFrame();
div.style.animation = 'anim 100s linear forwards scroll(root)';
await waitForCSSScrollTimelineStyle();
const anim = div.getAnimations()[0];
await anim.ready;
assert_percents_equal(anim.currentTime, 0, 'timeline time reset');
assert_equals(getComputedStyle(div).width, '100px');
await waitForNextFrame();
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = maxScroll;
await waitForNextFrame();
assert_equals(getComputedStyle(div).width, '200px');
div.style.animationPlayState = "paused";
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'paused';
assert_equals(anim.playState, 'paused');
assert_equals(getComputedStyle(div).width, '200px',
'Current time preserved when pause-pending.');
assert_true(anim.pending,
'Pending state after changing animationPlayState');
await anim.ready;
assert_equals(getComputedStyle(div).width, '200px',
'Current time preserved when paused.');
assert_percents_equal(anim.timeline.currentTime, 100);
document.scrollingElement.scrollTop = 0;
await waitForNextFrame();
assert_percents_equal(anim.timeline.currentTime, 0);
assert_equals(getComputedStyle(div).width, '200px');
}, 'Test that the scroll animation is paused by updating animation-play-state');

View file

@ -1,23 +1,20 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>The default scroll-timeline at rule with animation moving from end point</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>Root-scrolling timeline with animation moving from end point</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation">
<link rel="help" href="https://drafts.csswg.org/web-animations/#update-an-animations-finished-state">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-animations/support/testcommon.js"></script>
<script src="/scroll-animations/scroll-timelines/testcommon.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: auto;
orientation: auto;
scroll-offsets: none;
}
.fill-vh {
width: 100px;
height: 100vh;
@ -28,21 +25,34 @@
<script>
'use strict';
test(t => {
promise_test(async t => {
const div = addDiv(t, { style: 'width: 50px; height: 100px;' });
const filling = addDiv(t, { class: 'fill-vh' });
const scroller = document.scrollingElement;
getComputedStyle(document.scrollingElement).height;
scroller.scrollTop = 0;
await waitForNextFrame();
div.style.animation = 'anim 100s linear timeline';
assert_equals(getComputedStyle(div).width, '100px');
div.style.animation = 'anim 100s linear scroll(root)';
await waitForCSSScrollTimelineStyle();
const anim = div.getAnimations()[0];
await anim.ready;
assert_percents_equal(anim.timeline.currentTime, 0,
'Timeline time when animation is ready');
assert_equals(getComputedStyle(div).width, '100px',
'Width at animation start');
await waitForNextFrame();
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
scroller.scrollTop = maxScroll;
assert_equals(getComputedStyle(div).width, '200px');
await waitForNextFrame();
assert_equals(getComputedStyle(div).width, '200px',
'Width at scroll limit');
document.scrollingElement.scrollTop = 0;
assert_equals(getComputedStyle(div).width, '100px');
await waitForNextFrame();
assert_equals(getComputedStyle(div).width, '100px',
'Width after reset to scroll top');
}, 'Test that the scroll animation is still responsive after moving from 100%');
</script>

View file

@ -1,14 +1,14 @@
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<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>
<style>
#scroller {
overflow: scroll;
overflow: hidden;
width: 100px;
height: 100px;
scroll-timeline: timeline;
}
#contents {
height: 200px;
@ -17,11 +17,6 @@
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline {
source: selector(#scroller);
start: 0px;
end: 100px;
}
#element {
width: 0px;
animation: expand 10s linear;

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6674">
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<script src="/css/support/shorthand-testcommon.js"></script>
<div id="target"></div>
<script>
test_valid_value('scroll-timeline', 'block none', 'block');
test_valid_value('scroll-timeline', 'none inline', 'inline');
test_valid_value('scroll-timeline', 'horizontal abc');
test_valid_value('scroll-timeline', 'inline abc');
test_valid_value('scroll-timeline', 'inline aBc');
test_valid_value('scroll-timeline', 'vertical none', 'vertical');
test_valid_value('scroll-timeline', 'inline inline');
test_valid_value('scroll-timeline', 'abc');
test_valid_value('scroll-timeline', 'inline');
test_invalid_value('scroll-timeline', '');
test_invalid_value('scroll-timeline', 'abc abc');
test_computed_value('scroll-timeline', 'block none', 'block');
test_computed_value('scroll-timeline', 'abc inline', 'inline abc');
test_computed_value('scroll-timeline', 'none vertical', 'vertical');
test_computed_value('scroll-timeline', 'horizontal abc');
test_computed_value('scroll-timeline', 'vertical vertical');
test_computed_value('scroll-timeline', 'abc');
test_shorthand_value('scroll-timeline', 'vertical abc',
{
'scroll-timeline-axis': 'vertical',
'scroll-timeline-name': 'abc',
});
test_shorthand_value('scroll-timeline', 'horizontal inline',
{
'scroll-timeline-axis': 'horizontal',
'scroll-timeline-name': 'inline',
});
</script>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<title>scroll-timeline and container queries</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand">
<link rel="help" src="https://drafts.csswg.org/css-contain-3/#container-queries">
<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>
#scroller {
overflow: auto;
width: auto;
height: 100px;
}
#scroller > div {
height: 200px;
}
@keyframes anim {
from { background-color: rgb(100, 100, 100); }
to { background-color: rgb(200, 200, 200); }
}
#element {
height: 10px;
width: 10px;
animation: anim 10s linear timeline;
background-color: rgb(0, 0, 0);
}
</style>
<div>
<div id=scroller>
<div></div>
</div>
<div>
<div id=element></div>
</div>
</div>
<script>
promise_test(async (t) => {
element.offsetTop;
scroller.scrollTop = 50;
await waitForNextFrame();
// Unknown timeline, time held at zero.
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(100, 100, 100)');
scroller.style.scrollTimeline = 'timeline';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(150, 150, 150)');
}, 'Timelines appearing on preceding siblings are visible to getComputedStyle');
</script>

View file

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html class="reftest-wait">
<title>Reference for @scroll-timeline with a specified scroller</title>
<title>Reference for scroll timeline with a specified scroller</title>
<style>
#scroller {
overflow: scroll;

View file

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<title>The scroll-timeline at rule with a specified scroller for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<title>A scroll timeline with a specified scroller for print</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the a specified scroller">
<link rel="match" href="at-scroll-timeline-specified-scroller-print-ref.html">
<meta name="assert" content="CSS animation correctly updates values when using a specified scroller">
<link rel="match" href="scroll-timeline-specified-scroller-print-ref.html">
<style>
@keyframes anim {
@ -12,13 +12,8 @@
to { transform: translateX(100px); }
}
@scroll-timeline timeline {
source: selector(#scroller);
orientation: auto;
scroll-offsets: none;
}
#scroller {
scroll-timeline: timeline;
overflow: scroll;
width: 100px;
height: 100px;

View file

@ -0,0 +1,14 @@
'use strict';
/**
* Returns a Promise that is resolved after a CSS scroll timeline is created (as
* the result of a style change) and a snapshot has been taken, so that the
* animation style is correctly reflected by getComputedStyle().
* Technically, this only takes a full frame update. We implement this as two
* requestAnimationFrame callbacks because the result will be available at the
* beginning of the second frame.
*/
async function waitForCSSScrollTimelineStyle() {
await waitForNextFrame();
await waitForNextFrame();
}

View file

@ -0,0 +1,208 @@
<!DOCTYPE html>
<title>Animations using view-timeline</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timelines-named">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 0; }
to { z-index: 100; }
}
.vertical-scroller {
overflow: auto;
width: 100px;
height: 100px;
}
.vertical-scroller > div {
height: 50px;
z-index: -1;
}
.horizontal-scroller {
overflow: auto;
width: 100px;
height: 100px;
writing-mode: vertical-lr;
}
.horizontal-scroller > div {
width: 50px;
z-index: -1;
}
</style>
<main id=main></main>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
}
async function scrollTop(e, value) {
e.scrollTop = value;
await waitForNextFrame();
}
async function scrollLeft(e, value) {
e.scrollLeft = value;
await waitForNextFrame();
}
</script>
<template id=default_view_timeline>
<style>
#target {
view-timeline: t1;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical-scroller>
<div></div> <!-- [0px, 50px] -->
<div></div> <!-- [50px, 100px] -->
<div></div> <!-- [100px, 150px] -->
<div id=target></div> <!-- [150px, 200px] -->
<div></div> <!-- [200px, 250px] -->
<div></div> <!-- [250px, 300px] -->
<div></div> <!-- [300px, 350px] -->
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, default_view_timeline);
assert_equals(getComputedStyle(target).zIndex, '-1');
await scrollTop(scroller, 25);
assert_equals(getComputedStyle(target).zIndex, '-1');
await scrollTop(scroller, 50); // 0% (enter 0%)
assert_equals(getComputedStyle(target).zIndex, '0');
await scrollTop(scroller, 125); // 50%
assert_equals(getComputedStyle(target).zIndex, '50');
await scrollTop(scroller, 200); // 100% (exit 100%)
assert_equals(getComputedStyle(target).zIndex, '100');
await scrollTop(scroller, 225);
assert_equals(getComputedStyle(target).zIndex, '-1');
}, 'Default view-timeline');
</script>
<template id=horizontal_timeline>
<style>
#target {
view-timeline: t1 horizontal;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=horizontal-scroller>
<div></div> <!-- [0px, 50px] -->
<div></div> <!-- [50px, 100px] -->
<div></div> <!-- [100px, 150px] -->
<div id=target></div> <!-- [150px, 200px] -->
<div></div> <!-- [200px, 250px] -->
<div></div> <!-- [250px, 300px] -->
<div></div> <!-- [300px, 350px] -->
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, horizontal_timeline);
assert_equals(getComputedStyle(target).zIndex, '-1');
await scrollLeft(scroller, 25);
assert_equals(getComputedStyle(target).zIndex, '-1');
await scrollLeft(scroller, 50); // 0% (enter 0%)
assert_equals(getComputedStyle(target).zIndex, '0');
await scrollLeft(scroller, 125); // 50%
assert_equals(getComputedStyle(target).zIndex, '50');
await scrollLeft(scroller, 200); // 100% (exit 100%)
assert_equals(getComputedStyle(target).zIndex, '100');
await scrollLeft(scroller, 225);
assert_equals(getComputedStyle(target).zIndex, '-1');
}, 'Horizontal view-timeline');
</script>
<template id=multiple_timelines>
<style>
#timelines {
view-timeline: tv vertical, th horizontal;
background-color: red;
}
#scroller {
width: 100px;
height: 100px;
overflow: hidden;
display: grid;
grid-template-columns: 50px 50px 50px 50px 50px 50px 50px;
grid-template-row: 50px 50px 50px 50px 50px 50px 50px;
}
#scroller > div {
z-index: -1;
width: 50px;
height: 50px;
}
#target_v { animation: anim 1s linear tv; }
#target_h { animation: anim 1s linear th; }
</style>
<div id=scroller>
<!-- Created dynamically -->
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, multiple_timelines);
// Create a 350px x 350px grid (7x7 items of 50x50px each), with the
// timelines at item [3,3], an element attached to the horizontal timeline
// at [4,3], and an element attached to the vertical timeline at [3,4].
// x x x x x x x
// x x x x x x x
// x x x x x x x
// x x x T H x x
// x x x V x x x
// x x x x x x x
// x x x x x x x
// x x x x x x x
let grid_size = 7;
for (let i = 0; i < (grid_size*grid_size); ++i) {
let div = document.createElement('div');
if (i == (3 * grid_size + 3))
div.id = 'timelines';
if (i == (3 * grid_size + 4))
div.id = 'target_h';
if (i == (4 * grid_size + 3))
div.id = 'target_v';
scroller.append(div);
}
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
// First scroll vertically.
await scrollTop(scroller, 25);
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
await scrollTop(scroller, 50); // 0% (enter 0%)
assert_equals(getComputedStyle(target_v).zIndex, '0');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
await scrollTop(scroller, 125); // 50%
assert_equals(getComputedStyle(target_v).zIndex, '50');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
await scrollTop(scroller, 200); // 100% (exit 100%)
assert_equals(getComputedStyle(target_v).zIndex, '100');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
await scrollTop(scroller, 225);
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
// Then horizontally.
await scrollLeft(scroller, 25);
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
await scrollLeft(scroller, 50); // 0% (enter 0%)
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '0');
await scrollLeft(scroller, 125); // 50%
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '50');
await scrollLeft(scroller, 200); // 100% (exit 100%)
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '100');
await scrollLeft(scroller, 225);
assert_equals(getComputedStyle(target_v).zIndex, '-1');
assert_equals(getComputedStyle(target_h).zIndex, '-1');
}, 'Multiple view-timelines on the same element');
</script>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
</head>
<style>
#outer { view-timeline-axis: block, inline; }
#target { view-timeline-axis: vertical; }
</style>
<div id=outer>
<div id=target></div>
</div>
<script>
test_computed_value('view-timeline-axis', 'initial', 'block');
test_computed_value('view-timeline-axis', 'inherit', 'block, inline');
test_computed_value('view-timeline-axis', 'unset', 'block');
test_computed_value('view-timeline-axis', 'revert', 'block');
test_computed_value('view-timeline-axis', 'block');
test_computed_value('view-timeline-axis', 'inline');
test_computed_value('view-timeline-axis', 'vertical');
test_computed_value('view-timeline-axis', 'horizontal');
test_computed_value('view-timeline-axis', 'block, inline');
test_computed_value('view-timeline-axis', 'inline, block');
test_computed_value('view-timeline-axis', 'block, vertical, horizontal, inline');
test_computed_value('view-timeline-axis', 'inline, inline, inline, inline');
test(() => {
let style = getComputedStyle(document.getElementById('target'));
assert_not_equals(Array.from(style).indexOf('view-timeline-axis'), -1);
}, 'The view-timeline-axis property shows up in CSSStyleDeclaration enumeration');
test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('view-timeline-axis'), -1);
}, 'The view-timeline-axis property shows up in CSSStyleDeclaration.cssText');
</script>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<div id="target"></div>
<script>
test_valid_value('view-timeline-axis', 'initial');
test_valid_value('view-timeline-axis', 'inherit');
test_valid_value('view-timeline-axis', 'unset');
test_valid_value('view-timeline-axis', 'revert');
test_valid_value('view-timeline-axis', 'block');
test_valid_value('view-timeline-axis', 'inline');
test_valid_value('view-timeline-axis', 'vertical');
test_valid_value('view-timeline-axis', 'horizontal');
test_valid_value('view-timeline-axis', 'block, inline');
test_valid_value('view-timeline-axis', 'inline, block');
test_valid_value('view-timeline-axis', 'block, vertical, horizontal, inline');
test_valid_value('view-timeline-axis', 'inline, inline, inline, inline');
test_invalid_value('view-timeline-axis', 'abc');
test_invalid_value('view-timeline-axis', '10px');
test_invalid_value('view-timeline-axis', 'auto');
test_invalid_value('view-timeline-axis', 'none');
test_invalid_value('view-timeline-axis', 'block inline');
test_invalid_value('view-timeline-axis', 'block / inline');
</script>

View file

@ -0,0 +1,140 @@
<!DOCTYPE html>
<title>Animations using named timeline ranges</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 0; background-color: skyblue;}
to { z-index: 100; background-color: coral; }
}
#scroller {
border: 10px solid lightgray;
overflow-y: scroll;
width: 200px;
height: 200px;
}
#target {
margin: 800px 0px;
width: 100px;
height: 100px;
z-index: -1;
background-color: green;
}
</style>
<main id=main>
</main>
<template>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
}
async function scrollTop(e, value) {
e.scrollTop = value;
await waitForNextFrame();
}
async function waitForAnimationReady(target) {
await waitForNextFrame();
await Promise.all(target.getAnimations().map(x => x.promise));
}
async function assertValueAt(scroller, target, args) {
await waitForAnimationReady(target);
await scrollTop(scroller, args.scrollTop);
assert_equals(getComputedStyle(target).zIndex, args.expected.toString());
}
function test_animation_delay(options) {
promise_test(async (t) => {
inflate(t, document.querySelector('template'));
let scroller = main.querySelector('#scroller');
let target = main.querySelector('#target');
target.style.viewTimeline = 't1 block';
// TODO(crbug.com/1375998): Create the timeline in a separate frame to
// work around a bug.
await waitForNextFrame();
target.style.animation = 'anim 10s linear t1';
target.style.animationDelayStart = options.startDelay;
target.style.animationDelayEnd = options.endDelay;
// Accommodates floating point precision errors at the endpoints.
target.style.animationFillMode = 'both';
// 0%
await assertValueAt(scroller, target,
{ scrollTop: options.rangeStart, expected: 0 });
// 50%
await assertValueAt(scroller, target,
{ scrollTop: (options.rangeStart + options.rangeEnd) / 2, expected: 50 });
// 100%
await assertValueAt(scroller, target,
{ scrollTop: options.rangeEnd, expected: 100 });
// Test before/after phases (need to clear the fill mode for that).
target.style.animationFillMode = 'initial';
await assertValueAt(scroller, target,
{ scrollTop: options.rangeStart - 10, expected: -1 });
await assertValueAt(scroller, target,
{ scrollTop: options.rangeEnd + 10, expected: -1 });
// Check 50% again without fill mode.
await assertValueAt(scroller, target,
{ scrollTop: (options.rangeStart + options.rangeEnd) / 2, expected: 50 });
}, `Animation with delays [${options.startDelay}, ${options.endDelay}]`);
}
test_animation_delay({
startDelay: 'initial',
endDelay: 'initial',
rangeStart: 600,
rangeEnd: 900
});
test_animation_delay({
startDelay: 'cover 0%',
endDelay: 'cover 100%',
rangeStart: 600,
rangeEnd: 900
});
test_animation_delay({
startDelay: 'contain 0%',
endDelay: 'contain 100%',
rangeStart: 700,
rangeEnd: 800
});
test_animation_delay({
startDelay: 'enter 0%',
endDelay: 'enter 100%',
rangeStart: 600,
rangeEnd: 700
});
test_animation_delay({
startDelay: 'exit 0%',
endDelay: 'exit 100%',
rangeStart: 800,
rangeEnd: 900
});
test_animation_delay({
startDelay: 'contain -50%',
endDelay: 'enter 200%',
rangeStart: 650,
rangeEnd: 800
});
test_animation_delay({
startDelay: 'enter 0%',
endDelay: 'exit 100%',
rangeStart: 600,
rangeEnd: 900
});
</script>

View file

@ -0,0 +1,177 @@
<!DOCTYPE html>
<title>Changes to view-timeline are reflected in dependent elements</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand">
<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 { z-index: 0; }
to { z-index: 100; }
}
.scroller {
overflow: hidden;
width: 100px;
height: 100px;
}
.scroller > div {
height: 100px;
}
#target {
height: 0px;
z-index: -1;
}
</style>
<main id=main></main>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
main.offsetTop;
}
async function scrollTop(e, value) {
e.scrollTop = value;
await waitForNextFrame();
}
async function scrollLeft(e, value) {
e.scrollLeft = value;
await waitForNextFrame();
}
</script>
<template id=dynamic_view_timeline_name>
<style>
.timeline {
view-timeline-name: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div id=div75></div>
<div id=div25></div>
<div id=div_before></div>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, dynamic_view_timeline_name);
await scrollTop(scroller, 50);
// scrollTop=50 is 75% for div75.
div75.classList.add('timeline');
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '75');
// scrollTop=50 is 25% for div25.
div25.classList.add('timeline');
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '25');
// scrollTop=50 is before the timeline start for div_before.
div_before.classList.add('timeline');
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '-1');
// Scroll to 25% (for div_before) to verify that we're linked to that
// timeline.
await scrollTop(scroller, 150);
assert_equals(getComputedStyle(target).zIndex, '25');
// Now we should be back to div25's timeline, although with the new
// scrollTop=150, it's actually at 75%.
div_before.classList.remove('timeline');
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '75');
}, 'Dynamically changing view-timeline-name');
</script>
<template id=dynamic_view_timeline_axis>
<style>
#timeline {
width: 100px;
height: 100px;
margin: 100px;
view-timeline-name: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div id=timeline style="background: red;"></div>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, dynamic_view_timeline_axis);
await scrollTop(scroller, 50); // 25% (vertical)
await scrollLeft(scroller, 20); // 10% (horizontal)
assert_equals(getComputedStyle(target).zIndex, '25');
timeline.style.viewTimelineAxis = 'horizontal';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '10');
}, 'Dynamically changing view-timeline-axis');
</script>
<template id=dynamic_view_timeline_inset>
<style>
#timeline {
width: 100px;
height: 100px;
margin: 100px;
view-timeline-name: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div id=timeline style="background: red;"></div>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, dynamic_view_timeline_inset);
await scrollTop(scroller, 50); // 25% (without inset).
assert_equals(getComputedStyle(target).zIndex, '25');
timeline.style.viewTimelineInset = '0px 50px';
await waitForCSSScrollTimelineStyle();
assert_equals(getComputedStyle(target).zIndex, '0');
}, 'Dynamically changing view-timeline-inset');
</script>
<template id=timeline_display_none>
<style>
#timeline {
view-timeline-name: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline></div>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_display_none);
await scrollTop(scroller, 50);
assert_equals(getComputedStyle(target).zIndex, '25');
timeline.style.display = 'none';
assert_equals(getComputedStyle(target).zIndex, '-1');
}, 'Element with view-timeline becoming display:none');
</script>

View file

@ -0,0 +1,740 @@
<!DOCTYPE html>
<title>Animations using view-timeline-inset</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline-inset">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 0; }
to { z-index: 100; }
}
#scroller {
overflow: hidden;
width: 80px;
height: 100px;
}
#target {
margin: 150px;
width: 50px;
height: 50px;
z-index: -1;
}
</style>
<main id=main></main>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
}
async function scrollTop(e, value) {
e.scrollTop = value;
await waitForNextFrame();
}
async function scrollLeft(e, value) {
e.scrollLeft = value;
await waitForNextFrame();
}
async function assertValueAt(scroller, target, args) {
if (args.scrollTop !== undefined)
await scrollTop(scroller, args.scrollTop);
if (args.scrollLeft !== undefined)
await scrollLeft(scroller, args.scrollLeft);
assert_equals(getComputedStyle(target).zIndex, args.expected.toString());
}
</script>
<!--
Explanation of scroll positions
===============================
Please note the following:
- The scroller has a width x height of 80x100px.
- The content is 50x50px with a 150px margin on all sides.
In other words, the size of the scroller content is 200x200px.
This means that, for vertical direction scrolling, assuming no insets:
- The start offset is 50px (scroller height + 50px is 150px, which consumes
exactly the margin of the content).
- The end offset is 200px (this is where the bottom edge of the scroller has
just cleared the content).
- The halfway point is (50px + 200px) / 2 = 125px.
For horizontal direction scrolling, assuming no insets:
- The start offset is 70px (scroller width + 70px is 150px, which consumes
exactly the margin of the content).
- The end offset is 200px (this is where the left edge of the scroller has
just cleared the content).
- The halfway point is (70px + 200px) / 2 = 135px.
The start and end insets will adjust the start and end offsets accordingly,
and the expectations in this file explicitly write out those adjustments.
For example, if the start offset is normally 50px, but there's an inset of
10px, we'll expect 50px + 10px rather than 60px.
Halfway-point expectations write out the adjustment from the "normal"
halfway-point, e.g. for start-inset:10px and end-inset:20px, we expect
"125px + 5px" since (20-10)/2 == 5.
Finally, note that for right-to-left and bottom-to-top scrolling directions
scroll offsets go the in the negative direction. This is why some expectations
negate all the offsets.
-->
<template id=test_one_value>
<style>
#target {
view-timeline: t1;
view-timeline-inset: 10px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_one_value);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 10, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 0, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset with one value');
</script>
<template id=test_two_values>
<style>
#target {
view-timeline: t1;
view-timeline-inset: 10px 20px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_two_values);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset with two values');
</script>
<template id=test_em_values>
<style>
#target {
font-size: 10px;
view-timeline: t1;
view-timeline-inset: 10px 2em;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_em_values);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset with em values');
</script>
<template id=test_percentage_values>
<style>
#target {
font-size: 10px;
view-timeline: t1;
view-timeline-inset: calc(5px + max(1%, 5%)) 20%;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_percentage_values);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset with percentage values');
</script>
<template id=test_outset>
<style>
#target {
view-timeline: t1;
view-timeline-inset: -10px -20px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=vertical>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_outset);
await assertValueAt(scroller, target, { scrollTop:20, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 - 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 - 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 + 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:220, expected:-1 });
}, 'view-timeline-inset with negative values');
</script>
<template id=test_horizontal>
<style>
#target {
view-timeline: t1 horizontal;
view-timeline-inset: 10px 20px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_horizontal);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset with horizontal scroller');
</script>
<template id=test_block>
<style>
#target {
view-timeline: t1 block;
view-timeline-inset: 10px 20px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_block);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset with block scroller');
</script>
<template id=test_inline>
<style>
#target {
view-timeline: t1 inline;
view-timeline-inset: 10px 20px;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_inline);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset with inline scroller');
</script>
<template id=test_auto_block>
<style>
#scroller {
scroll-padding-block: 10px 20px;
}
#target {
view-timeline: t1 block;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_block);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, block');
</script>
<template id=test_auto_block_vertical_lr>
<style>
#scroller {
scroll-padding-block: 10px 20px;
writing-mode: vertical-lr;
}
#target {
view-timeline: t1 block;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_block_vertical_lr);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset:auto, block, vertical-lr');
</script>
<template id=test_auto_block_vertical_rl>
<style>
#scroller {
scroll-padding-block: 10px 20px;
writing-mode: vertical-rl;
}
#target {
view-timeline: t1 block;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_block_vertical_rl);
// Note: this represents horizontal scrolling from right to left.
await assertValueAt(scroller, target, { scrollLeft:-20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:-(70 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:-(135 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:-200, expected:-1 });
}, 'view-timeline-inset:auto, block, vertical-rl');
</script>
<template id=test_auto_inline>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset:auto, inline');
</script>
<template id=test_auto_inline_vertical_rl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-rl;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline_vertical_rl);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, inline, vertical-rl');
</script>
<template id=test_auto_inline_vertical_lr>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-lr;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline_vertical_lr);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, inline, vertical-lr');
</script>
<template id=test_auto_inline_rtl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
direction: rtl;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline_rtl);
await assertValueAt(scroller, target, { scrollLeft:-20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:-(70 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:-(135 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:-200, expected:-1 });
}, 'view-timeline-inset:auto, inline, rtl');
</script>
<template id=test_auto_inline_vertical_rl_rtl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-rl;
direction: rtl;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline_vertical_rl_rtl);
await assertValueAt(scroller, target, { scrollTop:-50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:-(50 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:-(125 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:-200, expected:-1 });
}, 'view-timeline-inset:auto, inline, vertical-rl, rtl');
</script>
<template id=test_auto_inline_vertical_lr_rtl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-lr;
direction: rtl;
}
#target {
view-timeline: t1 inline;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_inline_vertical_lr_rtl);
await assertValueAt(scroller, target, { scrollTop:-50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:-(50 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:-(125 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:-200, expected:-1 });
}, 'view-timeline-inset:auto, inline, vertical-lr, rtl');
</script>
<template id=test_auto_vertical>
<style>
#scroller {
scroll-padding-block: 10px 20px;
}
#target {
view-timeline: t1 vertical;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_vertical);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, vertical');
</script>
<template id=test_auto_vertical_vertical_rl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-rl;
}
#target {
view-timeline: t1 vertical;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_vertical_vertical_rl);
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, vertical, vertical-rl');
</script>
<template id=test_auto_vertical_vertical_rl_rtl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
writing-mode: vertical-rl;
direction: rtl;
}
#target {
view-timeline: t1 vertical;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_vertical_vertical_rl_rtl);
await assertValueAt(scroller, target, { scrollTop:-50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:-(50 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:-(125 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:-200, expected:-1 });
}, 'view-timeline-inset:auto, vertical, vertical-rl, rtl');
</script>
<template id=test_auto_horizontal>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
}
#target {
view-timeline: t1 horizontal;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_horizontal);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset:auto, horizontal');
</script>
<template id=test_auto_horizontal_rtl>
<style>
#scroller {
scroll-padding-inline: 10px 20px;
direction: rtl;
}
#target {
view-timeline: t1 horizontal;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_horizontal_rtl);
await assertValueAt(scroller, target, { scrollLeft:-20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:-(70 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:-(135 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:-200, expected:-1 });
}, 'view-timeline-inset:auto, horizontal, rtl');
</script>
<template id=test_auto_horizontal_vertical_lr>
<style>
#scroller {
scroll-padding-block: 10px 20px;
writing-mode: vertical-lr;
}
#target {
view-timeline: t1 horizontal;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_horizontal_vertical_lr);
await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:135 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
}, 'view-timeline-inset:auto, horizontal, vertical-lr');
</script>
<template id=test_auto_horizontal_vertical_rl>
<style>
#scroller {
scroll-padding-block: 10px 20px;
writing-mode: vertical-rl;
}
#target {
view-timeline: t1 horizontal;
view-timeline-inset: auto auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_horizontal_vertical_rl);
await assertValueAt(scroller, target, { scrollLeft:-20, expected:-1 });
await assertValueAt(scroller, target, { scrollLeft:-(70 + 20), expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollLeft:-(135 + 5), expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollLeft:-(200 - 10), expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollLeft:-200, expected:-1 });
}, 'view-timeline-inset:auto, horizontal, vertical-rl');
</script>
<template id=test_auto_mix>
<style>
#scroller {
font-size: 10px;
scroll-padding-block: 50px calc(10% + 1em);
}
#target {
view-timeline: t1;
view-timeline-inset: 10% auto;
animation: anim 1s linear t1;
}
</style>
<div id=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, test_auto_mix);
// Note: 10% of scroller height 100px is 10px, and 1em with font-size:10px
// is also 10px. Hence we expect the end inset specified as calc(10% + 1em)
// to be 20px.
await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
await assertValueAt(scroller, target, { scrollTop:125 + 5, expected:50 }); // 50%
await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
}, 'view-timeline-inset:auto, mix');
</script>
<!--
TODO: How to test view-timeline:auto + scroll-padding:auto? The UA may
in theory use any value in that case.
https://drafts.csswg.org/css-scroll-snap-1/#valdef-scroll-padding-auto
-->

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7243">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
<style>
#outer { font-size:10px; }
#outer { view-timeline-inset: 1px 2px, auto 3px; }
#target { view-timeline-inset: 42px; }
</style>
<div id=outer>
<div id=target></div>
</div>
<script>
test_computed_value('view-timeline-inset', 'initial', '0px');
test_computed_value('view-timeline-inset', 'inherit', '1px 2px, auto 3px');
test_computed_value('view-timeline-inset', 'unset', '0px');
test_computed_value('view-timeline-inset', 'revert', '0px');
test_computed_value('view-timeline-inset', '1px');
test_computed_value('view-timeline-inset', '1%');
test_computed_value('view-timeline-inset', 'calc(1% + 1px)');
test_computed_value('view-timeline-inset', '1px 2px');
test_computed_value('view-timeline-inset', '1px 2em', '1px 20px');
test_computed_value('view-timeline-inset', 'calc(1px + 1em) 2px', '11px 2px');
test_computed_value('view-timeline-inset', '1px 2px, 3px 4px');
test_computed_value('view-timeline-inset', '1px auto, auto 4px');
test_computed_value('view-timeline-inset', '1px, 2px, 3px');
test_computed_value('view-timeline-inset', '1px 1px, 2px 3px', '1px, 2px 3px');
test_computed_value('view-timeline-inset', 'auto auto, auto auto', 'auto, auto');
test(() => {
let style = getComputedStyle(document.getElementById('target'));
assert_not_equals(Array.from(style).indexOf('view-timeline-inset'), -1);
}, 'The view-timeline-inset property shows up in CSSStyleDeclaration enumeration');
test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('view-timeline-inset'), -1);
}, 'The view-timeline-inset property shows up in CSSStyleDeclaration.cssText');
</script>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7243">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<div id="target"></div>
<script>
test_valid_value('view-timeline-inset', 'initial');
test_valid_value('view-timeline-inset', 'inherit');
test_valid_value('view-timeline-inset', 'unset');
test_valid_value('view-timeline-inset', 'revert');
test_valid_value('view-timeline-inset', '1px');
test_valid_value('view-timeline-inset', '1px 2px');
test_valid_value('view-timeline-inset', '1px 2em');
test_valid_value('view-timeline-inset', 'calc(1em + 1px) 2px');
test_valid_value('view-timeline-inset', '1px 2px, 3px 4px');
test_valid_value('view-timeline-inset', '1px auto, auto 4px');
test_valid_value('view-timeline-inset', '1px, 2px, 3px');
test_valid_value('view-timeline-inset', '1px 1px, 2px 3px', '1px, 2px 3px');
test_valid_value('view-timeline-inset', 'auto auto, auto auto', 'auto, auto');
test_invalid_value('view-timeline-inset', 'none');
test_invalid_value('view-timeline-inset', 'foo bar');
test_invalid_value('view-timeline-inset', '"foo" "bar"');
test_invalid_value('view-timeline-inset', 'rgb(1, 2, 3)');
test_invalid_value('view-timeline-inset', '#fefefe');
test_invalid_value('view-timeline-inset', '1px 2px 3px');
test_invalid_value('view-timeline-inset', '1px 2px auto');
test_invalid_value('view-timeline-inset', 'auto 2px 3px');
test_invalid_value('view-timeline-inset', 'auto auto auto');
test_invalid_value('view-timeline-inset', '1px / 2px');
</script>

View file

@ -0,0 +1,292 @@
<!DOCTYPE html>
<title>Named view-timeline lookup</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timelines-named">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 0; }
to { z-index: 100; }
}
.scroller {
overflow: auto;
width: 100px;
height: 100px;
}
.scroller > div {
height: 25px;
z-index: -1;
}
</style>
<main id=main></main>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
main.offsetTop;
}
</script>
<template id=timeline_self>
<style>
#target {
height: 0px;
view-timeline: t1;
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div id=target></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_self);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '100');
}, 'view-timeline on self');
</script>
<template id=timeline_preceding_sibling>
<style>
#timeline {
height: 0px;
view-timeline: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline></div>
<div></div>
<div></div>
<div id=target></div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_preceding_sibling);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '75');
}, 'view-timeline on preceding sibling');
</script>
<template id=timeline_ancestor>
<style>
#timeline {
height: 0px;
view-timeline: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div></div>
<div></div>
<div id=timeline>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '25');
}, 'view-timeline on ancestor');
</script>
<template id=timeline_ancestor_sibling>
<style>
#timeline {
height: 0px;
view-timeline: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline></div>
<div></div>
<div>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor_sibling);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '75');
}, 'view-timeline on ancestor sibling');
</script>
<template id=timeline_ancestor_sibling_closest>
<style>
#timeline1, #timeline2 {
height: 0px;
view-timeline: t1;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline1></div>
<div></div>
<div id=timeline2></div>
<div>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor_sibling_closest);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '50');
}, 'view-timeline on ancestor sibling, closest wins');
</script>
<template id=timeline_ancestor_sibling_skips_nonmatching>
<style>
#timeline1 {
height: 0px;
view-timeline: t1;
}
#timeline2 {
height: 0px;
view-timeline: t2;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline1></div>
<div></div>
<div id=timeline2></div>
<div>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor_sibling_skips_nonmatching);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '75');
}, 'view-timeline on ancestor sibling, skips nonmatching names');
</script>
<template id=timeline_ancestor_closer_scroll_timeline_wins>
<style>
#timeline {
height: 0px;
view-timeline: t1;
}
#scroll {
overflow: auto;
scroll-timeline: t1;
}
#scroll > div {
height: 50px;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timeline></div>
<div></div>
<div id=scroll>
<div></div>
</div>
<div>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor_closer_scroll_timeline_wins);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '0');
}, 'view-timeline on ancestor sibling, closer scroll-timeline wins');
</script>
<template id=timeline_ancestor_view_timeline_wins_on_same_element>
<style>
#timelines {
height: 0px;
view-timeline: t1;
scroll-timeline: t1;
overflow: auto;
}
#timelines > div {
height: 50px;
}
#target {
animation: anim 1s linear t1;
}
</style>
<div id=scroller class=scroller>
<div></div>
<div id=timelines>
<div></div>
</div>
<div></div>
<div>
<div>
<div id=target></div>
</div>
</div>
<div></div>
<div></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, timeline_ancestor_view_timeline_wins_on_same_element);
await waitForNextFrame();
assert_equals(getComputedStyle(target).zIndex, '75');
}, 'view-timeline on ancestor sibling, view-timeline wins on same element');
</script>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
</head>
<style>
#outer { view-timeline-name: foo, bar; }
#target { view-timeline-name: faz; }
</style>
<div id=outer>
<div id=target></div>
</div>
<script>
test_computed_value('view-timeline-name', 'initial', 'none');
test_computed_value('view-timeline-name', 'inherit', 'foo, bar');
test_computed_value('view-timeline-name', 'unset', 'none');
test_computed_value('view-timeline-name', 'revert', 'none');
test_computed_value('view-timeline-name', 'none');
test_computed_value('view-timeline-name', 'foo');
test_computed_value('view-timeline-name', 'foo, bar');
test_computed_value('view-timeline-name', 'bar, foo');
test_computed_value('view-timeline-name', 'a, b, c, D, e');
test_computed_value('view-timeline-name', 'none, none');
test_computed_value('view-timeline-name', 'a, b, c, none, d, e');
test(() => {
let style = getComputedStyle(document.getElementById('target'));
assert_not_equals(Array.from(style).indexOf('view-timeline-name'), -1);
}, 'The view-timeline-name property shows up in CSSStyleDeclaration enumeration');
test(() => {
let style = document.getElementById('target').style;
assert_not_equals(style.cssText.indexOf('view-timeline-name'), -1);
}, 'The view-timeline-name property shows up in CSSStyleDeclaration.cssText');
</script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<div id="target"></div>
<script>
test_valid_value('view-timeline-name', 'initial');
test_valid_value('view-timeline-name', 'inherit');
test_valid_value('view-timeline-name', 'unset');
test_valid_value('view-timeline-name', 'revert');
test_valid_value('view-timeline-name', 'none');
test_valid_value('view-timeline-name', 'abc');
test_valid_value('view-timeline-name', ' abc', 'abc');
test_valid_value('view-timeline-name', 'abc ', 'abc');
test_valid_value('view-timeline-name', 'aBc');
test_valid_value('view-timeline-name', 'foo, bar');
test_valid_value('view-timeline-name', 'bar, foo');
test_valid_value('view-timeline-name', 'none, none');
test_valid_value('view-timeline-name', 'a, none, b');
test_valid_value('view-timeline-name', 'auto');
test_invalid_value('view-timeline-name', 'default');
test_invalid_value('view-timeline-name', '10px');
test_invalid_value('view-timeline-name', 'foo bar');
test_invalid_value('view-timeline-name', '"foo" "bar"');
test_invalid_value('view-timeline-name', 'rgb(1, 2, 3)');
test_invalid_value('view-timeline-name', '#fefefe');
</script>

View file

@ -0,0 +1,104 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7627">
<link rel="help" href="https://github.com/w3c/csswg-drafts/pull/7694">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<script src="/css/support/shorthand-testcommon.js"></script>
<div id="target"></div>
<script>
test_valid_value('view-timeline', 'abcd');
test_valid_value('view-timeline', 'none block', 'none');
test_valid_value('view-timeline', 'none inline');
// view-timeline-name: inline/block/horizontal/vertical.
test_valid_value('view-timeline', 'inline block', 'inline');
test_valid_value('view-timeline', 'block block', 'block');
test_valid_value('view-timeline', 'vertical block', 'vertical');
test_valid_value('view-timeline', 'horizontal block', 'horizontal');
test_valid_value('view-timeline', 'a, b, c');
test_valid_value('view-timeline', 'a inline, b block, c vertical', 'a inline, b, c vertical');
test_valid_value('view-timeline', 'auto');
test_invalid_value('view-timeline', 'abc abc');
test_invalid_value('view-timeline', 'block none');
test_invalid_value('view-timeline', 'none none');
test_invalid_value('view-timeline', 'default');
test_invalid_value('view-timeline', ',');
test_invalid_value('view-timeline', ',,block,,');
test_computed_value('view-timeline', 'abcd');
test_computed_value('view-timeline', 'none block', 'none');
test_computed_value('view-timeline', 'none inline');
test_computed_value('view-timeline', 'inline block', 'inline');
test_computed_value('view-timeline', 'block block', 'block');
test_computed_value('view-timeline', 'vertical block', 'vertical');
test_computed_value('view-timeline', 'horizontal block', 'horizontal');
test_computed_value('view-timeline', 'a, b, c');
test_computed_value('view-timeline', 'a inline, b block, c vertical', 'a inline, b, c vertical');
test_shorthand_value('view-timeline', 'abc vertical',
{
'view-timeline-name': 'abc',
'view-timeline-axis': 'vertical',
});
test_shorthand_value('view-timeline', 'abc vertical, def',
{
'view-timeline-name': 'abc, def',
'view-timeline-axis': 'vertical, block',
});
test_shorthand_value('view-timeline', 'abc, def',
{
'view-timeline-name': 'abc, def',
'view-timeline-axis': 'block, block',
});
test_shorthand_value('view-timeline', 'inline horizontal',
{
'view-timeline-name': 'inline',
'view-timeline-axis': 'horizontal',
});
function test_shorthand_contraction(shorthand, longhands, expected) {
let longhands_fmt = Object.entries(longhands).map((e) => `${e[0]}:${e[1]}`).join(';');
test((t) => {
t.add_cleanup(() => {
for (let shorthand of Object.keys(longhands))
target.style.removeProperty(shorthand);
});
for (let [shorthand, value] of Object.entries(longhands))
target.style.setProperty(shorthand, value);
assert_equals(target.style.getPropertyValue(shorthand), expected, 'Declared value');
assert_equals(getComputedStyle(target).getPropertyValue(shorthand), expected, 'Computed value');
}, `Shorthand contraction of ${longhands_fmt}`);
}
test_shorthand_contraction('view-timeline', {
'view-timeline-name': 'abc',
'view-timeline-axis': 'inline',
}, 'abc inline');
test_shorthand_contraction('view-timeline', {
'view-timeline-name': 'a, b',
'view-timeline-axis': 'inline, block',
}, 'a inline, b');
test_shorthand_contraction('view-timeline', {
'view-timeline-name': 'none, none',
'view-timeline-axis': 'block, block',
}, 'none, none');
// Longhands with different lengths:
test_shorthand_contraction('view-timeline', {
'view-timeline-name': 'a, b, c',
'view-timeline-axis': 'inline, inline',
}, '');
test_shorthand_contraction('view-timeline', {
'view-timeline-name': 'a, b',
'view-timeline-axis': 'inline, inline, inline',
}, '');
</script>

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<title>Used values of view-timeline properties</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timeline-name">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { z-index: 0; }
to { z-index: 100; }
}
.scroller {
overflow: hidden;
width: 100px;
height: 100px;
}
.scroller > div {
width: 300px;
height: 300px;
z-index: -1;
}
</style>
<main id=main></main>
<script>
function inflate(t, template) {
t.add_cleanup(() => main.replaceChildren());
main.append(template.content.cloneNode(true));
}
async function scrollTop(e, value) {
e.scrollTop = value;
await waitForNextFrame();
}
async function scrollLeft(e, value) {
e.scrollLeft = value;
await waitForNextFrame();
}
</script>
<template id=omitted_axis>
<style>
#target {
view-timeline-name: t1, t2; /* Two items */
view-timeline-axis: inline; /* One item */
animation: anim 1s linear t2;
}
</style>
<div id=scroller class=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, omitted_axis);
assert_equals(getComputedStyle(target).zIndex, '-1');
// enter 0% is at scrollTop/Left = -100
// exit 100% is at scrollTop/Left = 300
// This means that at scrollTop/Left=0, the animation is at 25%.
await scrollTop(scroller, 0);
await scrollLeft(scroller, 0);
assert_equals(getComputedStyle(target).zIndex, '25');
// The timeline should be inline-axis:
await scrollTop(scroller, 100); // 50%
await scrollLeft(scroller, 40); // 35%
assert_equals(getComputedStyle(target).zIndex, '35');
}, 'Use the last value from view-timeline-axis if omitted');
</script>
<template id=omitted_inset>
<style>
#target {
view-timeline-name: t1, t2; /* Two items */
view-timeline-inset: 100px; /* One item */
animation: anim 1s linear t2;
}
</style>
<div id=scroller class=scroller>
<div id=target></div>
</div>
</template>
<script>
promise_test(async (t) => {
inflate(t, omitted_inset);
assert_equals(getComputedStyle(target).zIndex, '-1');
// 0% is normally at at scrollTop = -100
// 100% is normally at scrollTop/Left = 300
// However, we have a 100px inset in both ends, which makes the
// range [0, 200].
await scrollTop(scroller, 0);
assert_equals(getComputedStyle(target).zIndex, '0');
await scrollTop(scroller, 100); // 50%
assert_equals(getComputedStyle(target).zIndex, '50');
}, 'Use the last value from view-timeline-inset if omitted');
</script>