diff options
Diffstat (limited to 'testing/web-platform/tests/css/css-animations')
195 files changed, 11674 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-animations/AnimationEffect-getComputedTiming.tentative.html b/testing/web-platform/tests/css/css-animations/AnimationEffect-getComputedTiming.tentative.html new file mode 100644 index 0000000000..46c26fcb99 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/AnimationEffect-getComputedTiming.tentative.html @@ -0,0 +1,612 @@ +<!doctype html> +<meta charset=utf-8> +<title>AnimationEffect.getComputedTiming() for CSS animations</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes moveAnimation { + from { margin-left: 100px } + to { margin-left: 200px } +} +</style> +<body> +<div id="log"></div> +<script> + +'use strict'; + +// -------------------- +// delay +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().delay, 0, 'Initial value of delay'); +}, 'delay of a new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s -10s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().delay, -10 * MS_PER_SEC, + 'Initial value of delay'); +}, 'Negative delay of a new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 10s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().delay, 10 * MS_PER_SEC, + 'Initial value of delay'); +}, 'Positive delay of a new animation'); + + +// -------------------- +// endDelay +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().endDelay, 0, + 'Initial value of endDelay'); +}, 'endDelay of a new animation'); + + +// -------------------- +// fill +// -------------------- + +test(t => { + const getEffectWithFill = fill => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + fill }); + return div.getAnimations()[0].effect; + }; + + let effect = getEffectWithFill(''); + assert_equals(effect.getComputedTiming().fill, 'none', + 'Initial value of fill'); + effect = getEffectWithFill('forwards'); + assert_equals(effect.getComputedTiming().fill, 'forwards', + 'Fill forwards'); + effect = getEffectWithFill('backwards'); + assert_equals(effect.getComputedTiming().fill, 'backwards', + 'Fill backwards'); + effect = getEffectWithFill('both'); + assert_equals(effect.getComputedTiming().fill, 'both', + 'Fill forwards and backwards'); +}, 'fill of a new animation'); + + +// -------------------- +// iterationStart +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().iterationStart, 0, + 'Initial value of iterationStart'); +}, 'iterationStart of a new animation'); + + +// -------------------- +// iterations +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().iterations, 1, + 'Initial value of iterations'); +}, 'iterations of a new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 2016.5' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().iterations, 2016.5, + 'Initial value of iterations'); +}, 'iterations of a finitely repeating animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().iterations, Infinity, + 'Initial value of iterations'); +}, 'iterations of an infinitely repeating animation'); + + +// -------------------- +// duration +// -------------------- + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 100s -10s infinite' + }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().duration, 100 * MS_PER_SEC, + 'Initial value of duration'); +}, 'duration of a new animation'); + + +// -------------------- +// direction +// -------------------- + +test(t => { + const getEffectWithDir = dir => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + dir }); + return div.getAnimations()[0].effect; + }; + + let effect = getEffectWithDir(''); + assert_equals(effect.getComputedTiming().direction, 'normal', + 'Initial value of normal direction'); + effect = getEffectWithDir('reverse'); + assert_equals(effect.getComputedTiming().direction, 'reverse', + 'Initial value of reverse direction'); + effect = getEffectWithDir('alternate'); + assert_equals(effect.getComputedTiming().direction, 'alternate', + 'Initial value of alternate direction'); + effect = getEffectWithDir('alternate-reverse'); + assert_equals(effect.getComputedTiming().direction, 'alternate-reverse', + 'Initial value of alternate-reverse direction'); +}, 'direction of a new animation'); + + +// -------------------- +// easing +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().easing, 'linear', + 'Initial value of easing'); +}, 'easing of a new animation'); + + +// ------------------------------ +// endTime +// = max(start delay + active duration + end delay, 0) +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC, + 'Initial value of endTime'); +}, 'endTime of an new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s -5s' }); + const effect = div.getAnimations()[0].effect; + const answer = (100 - 5) * MS_PER_SEC; + assert_equals(effect.getComputedTiming().endTime, answer, + 'Initial value of endTime'); +}, 'endTime of an animation with a negative delay'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 10s -100s infinite' + }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().endTime, Infinity, + 'Initial value of endTime'); +}, 'endTime of an infinitely repeating animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 0s 100s infinite' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC, + 'Initial value of endTime'); +}, 'endTime of an infinitely repeating zero-duration animation'); + +test(t => { + // Fill forwards so div.getAnimations()[0] won't return an + // undefined value. + const div = addDiv(t, { + style: 'animation: moveAnimation 10s -100s forwards' + }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().endTime, 0, + 'Initial value of endTime'); +}, 'endTime of an animation that finishes before its startTime'); + + +// -------------------- +// activeDuration +// = iteration duration * iteration count +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 5' }); + const effect = div.getAnimations()[0].effect; + const answer = 100 * MS_PER_SEC * 5; + assert_equals(effect.getComputedTiming().activeDuration, answer, + 'Initial value of activeDuration'); +}, 'activeDuration of a new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().activeDuration, Infinity, + 'Initial value of activeDuration'); +}, 'activeDuration of an infinitely repeating animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 0s 1s infinite' }); + const effect = div.getAnimations()[0].effect; + // If either the iteration duration or iteration count are zero, + // the active duration is zero. + assert_equals(effect.getComputedTiming().activeDuration, 0, + 'Initial value of activeDuration'); +}, 'activeDuration of an infinitely repeating zero-duration animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 1s 0' }); + const effect = div.getAnimations()[0].effect; + // If either the iteration duration or iteration count are zero, + // the active duration is zero. + assert_equals(effect.getComputedTiming().activeDuration, 0, + 'Initial value of activeDuration'); +}, 'activeDuration of an animation with zero iterations'); + + +// -------------------- +// localTime +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().localTime, 0, + 'Initial value of localTime'); +}, 'localTime of a new animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const anim = div.getAnimations()[0]; + anim.currentTime = 5 * MS_PER_SEC; + assert_times_equal(anim.effect.getComputedTiming().localTime, + anim.currentTime, + 'current localTime after setting currentTime'); +}, 'localTime of an animation is always equal to currentTime'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + + const anim = div.getAnimations()[0]; + anim.playbackRate = 2; // 2 times faster + + await anim.ready; + + assert_times_equal(anim.effect.getComputedTiming().localTime, + anim.currentTime, + 'localTime is equal to currentTime'); + + await waitForFrame(); + + assert_times_equal(anim.effect.getComputedTiming().localTime, + anim.currentTime, + 'localTime is equal to currentTime'); +}, 'localTime reflects playbackRate immediately'); + +test(t => { + const div = addDiv(t); + const effect = new KeyframeEffect(div, {left: ["0px", "100px"]}); + + assert_equals(effect.getComputedTiming().localTime, null, + 'localTime for orphaned effect'); +}, 'localTime of an AnimationEffect without an Animation'); + + +// -------------------- +// progress +// +// Note: Even though CSS animations have a default animation-timing-function of +// "ease", this only applies between keyframes (often referred to as the +// keyframe-level easing). The progress value returned by getComputedTiming(), +// however, only reflects effect-level easing and this defaults to "linear", +// even for CSS animations. +// -------------------- + +test(t => { + const tests = [ + { fill: '', progress: [null, null] }, + { fill: 'none', progress: [null, null] }, + { fill: 'forwards', progress: [null, 1.0] }, + { fill: 'backwards', progress: [0.0, null] }, + { fill: 'both', progress: [0.0, 1.0] }, + ]; + for (const test of tests) { + const div = addDiv(t, { + style: 'animation: moveAnimation 100s 10s ' + test.fill + }); + const anim = div.getAnimations()[0]; + assert_true(anim.effect.getComputedTiming().progress === test.progress[0], + `Initial progress with "${test.fill}" fill`); + anim.finish(); + assert_true(anim.effect.getComputedTiming().progress === test.progress[1], + `Initial progress with "${test.fill}" fill`); + } +}, 'progress of an animation with different fill modes'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 10s 10 both' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Initial value of progress'); + anim.currentTime += 2.5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.finish() + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Value of progress'); +}, 'progress of an integral repeating animation with normal direction'); + +test(t => { + // Note: FillMode here is "both" because + // 1. Since this a zero-duration animation, it will already have finished + // so it won't be returned by getAnimations() unless it fills forwards. + // 2. Fill backwards, so the progress before phase wouldn't be + // unresolved (null value). + const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Initial value of progress in after phase'); + + // Seek backwards + anim.currentTime -= 1 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Value of progress before phase'); +}, 'progress of an infinitely repeating zero-duration animation'); + +test(t => { + // Default iterations = 1 + const div = addDiv(t, { style: 'animation: moveAnimation 0s both' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Initial value of progress in after phase'); + + // Seek backwards + anim.currentTime -= 1 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Value of progress before phase'); +}, 'progress of a finitely repeating zero-duration animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 0s 5s 10.25 both' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Initial value of progress (before phase)'); + + // Using iteration duration of 1 now. + // currentIteration now is floor(10.25) = 10, so progress should be 25%. + anim.finish(); + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress in after phase'); +}, 'progress of a non-integral repeating zero-duration animation'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 0s 5s 10.25 both reverse', + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Initial value of progress (before phase)'); + + // Seek forwards + anim.finish(); + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress in after phase'); +}, 'Progress of a non-integral repeating zero-duration animation ' + + 'with reversing direction'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 10s 10.25 both alternate', + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Initial value of progress'); + anim.currentTime += 2.5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); + anim.finish() + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); +}, 'progress of a non-integral repeating animation ' + + 'with alternate direction'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 10s 10.25 both alternate-reverse', + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Initial value of progress'); + anim.currentTime += 2.5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.currentTime += 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.finish() + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); +}, 'progress of a non-integral repeating animation ' + + 'with alternate-reversing direction'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 0s 10.25 both alternate', + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Initial value of progress'); + anim.currentTime += 2.5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); + anim.currentTime -= 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.0, + 'Value of progress'); + anim.finish() + assert_equals(anim.effect.getComputedTiming().progress, 0.25, + 'Value of progress'); +}, 'progress of a non-integral repeating zero-duration animation ' + + 'with alternate direction'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 0s 10.25 both alternate-reverse', + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Initial value of progress'); + anim.currentTime += 2.5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); + anim.currentTime -= 5 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().progress, 1.0, + 'Value of progress'); + anim.finish() + assert_equals(anim.effect.getComputedTiming().progress, 0.75, + 'Value of progress'); +}, 'progress of a non-integral repeating zero-duration animation ' + + 'with alternate-reverse direction'); + + +// -------------------- +// currentIteration +// -------------------- + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 2s' }); + const effect = div.getAnimations()[0].effect; + assert_equals(effect.getComputedTiming().currentIteration, null, + 'Initial value of currentIteration before phase'); +}, 'currentIteration of a new animation with no backwards fill is unresolved ' + + 'in before phase'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); + const anim = div.getAnimations()[0]; + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Initial value of currentIteration'); +}, 'currentIteration of a new animation is zero'); + +test(t => { + // Note: FillMode here is "both" because + // 1. Since this a zero-duration animation, it will already have finished + // so it won't be returned by getAnimations() unless it fills forwards. + // 2. Fill backwards, so the currentIteration (before phase) wouldn't be + // unresolved (null value). + const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity, + 'Initial value of currentIteration in after phase'); + + // Seek backwards + anim.currentTime -= 2 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Value of currentIteration count during before phase'); +}, 'currentIteration of an infinitely repeating zero-duration animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 0s 10.5 both' }); + const anim = div.getAnimations()[0]; + + // Note: currentIteration = ceil(iteration start + iteration count) - 1 + assert_equals(anim.effect.getComputedTiming().currentIteration, 10, + 'Initial value of currentIteration'); + + // Seek backwards + anim.currentTime -= 2 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Value of currentIteration count during before phase'); +}, 'currentIteration of a finitely repeating zero-duration animation'); + +test(t => { + const div = addDiv(t, { + style: 'animation: moveAnimation 100s 5.5 forwards' + }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Initial value of currentIteration'); + // The 3rd iteration + // Note: currentIteration = floor(scaled active time / iteration duration) + anim.currentTime = 250 * MS_PER_SEC; + assert_equals(anim.effect.getComputedTiming().currentIteration, 2, + 'Value of currentIteration during the 3rd iteration'); + // Finish + anim.finish(); + assert_equals(anim.effect.getComputedTiming().currentIteration, 5, + 'Value of currentIteration in after phase'); +}, 'currentIteration of an animation with a non-integral iteration count'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s 2 forwards' }); + const anim = div.getAnimations()[0]; + + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Initial value of currentIteration'); + // Finish + anim.finish(); + assert_equals(anim.effect.getComputedTiming().currentIteration, 1, + 'Value of currentIteration in after phase'); +}, 'currentIteration of an animation with an integral iteration count'); + +test(t => { + const div = addDiv(t, { style: 'animation: moveAnimation 100s forwards' }); + const anim = div.getAnimations()[0]; + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Initial value of currentIteration'); + // Finish + anim.finish(); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'Value of currentIteration in after phase'); +}, 'currentIteration of an animation with a default iteration count'); + +test(t => { + const div = addDiv(t); + const effect = new KeyframeEffect(div, {left: ["0px", "100px"]}); + + assert_equals(effect.getComputedTiming().currentIteration, null, + 'currentIteration for orphaned effect'); +}, 'currentIteration of an AnimationEffect without an Animation'); + +// TODO: If iteration duration is Infinity, currentIteration is 0. +// However, we cannot set iteration duration to Infinity in CSS Animation now. + +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/AnimationEffect-updateTiming.tentative.html b/testing/web-platform/tests/css/css-animations/AnimationEffect-updateTiming.tentative.html new file mode 100644 index 0000000000..e6556dac4b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/AnimationEffect-updateTiming.tentative.html @@ -0,0 +1,174 @@ +<!doctype html> +<meta charset=utf-8> +<title>AnimationEffect.updateTiming() for CSS animations</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim-empty { } +</style> +<body> +<div id="log"></div> +<script> +"use strict"; + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-empty 100s'; + + const animation = div.getAnimations()[0]; + animation.effect.updateTiming({ duration: 2 * MS_PER_SEC }); + + div.style.animationDuration = '4s'; + div.style.animationDelay = '6s'; + + assert_equals( + animation.effect.getTiming().duration, + 2 * MS_PER_SEC, + 'Duration should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().delay, + 6 * MS_PER_SEC, + 'Delay should be the value set by style' + ); +}, 'AnimationEffect.updateTiming({ duration }) causes changes to the' + + ' animation-duration to be ignored'); + +// The above test covers duration (with delay as a control). The remaining +// properties are: +// +// iterations - animation-iteration-count +// direction - animation-direction +// delay - animation-delay +// fill - animation-fill-mode +// +// Which we test in two batches, overriding two properties each time and using +// the remaining two properties as control values to check they are NOT +// overridden. + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-empty 100s'; + + const animation = div.getAnimations()[0]; + animation.effect.updateTiming({ iterations: 2, direction: 'reverse' }); + + div.style.animationIterationCount = '4'; + div.style.animationDirection = 'alternate'; + div.style.animationDelay = '6s'; + div.style.animationFillMode = 'both'; + + assert_equals( + animation.effect.getTiming().iterations, + 2, + 'Iterations should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().direction, + 'reverse', + 'Direction should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().delay, + 6 * MS_PER_SEC, + 'Delay should be the value set by style' + ); + assert_equals( + animation.effect.getTiming().fill, + 'both', + 'Fill should be the value set by style' + ); +}, 'AnimationEffect.updateTiming({ iterations, direction }) causes changes to' + + ' the animation-iteration-count and animation-direction to be ignored'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-empty 100s'; + + const animation = div.getAnimations()[0]; + animation.effect.updateTiming({ delay: 2 * MS_PER_SEC, fill: 'both' }); + + div.style.animationDelay = '4s'; + div.style.animationFillMode = 'forwards'; + div.style.animationIterationCount = '6'; + div.style.animationDirection = 'reverse'; + + assert_equals( + animation.effect.getTiming().delay, + 2 * MS_PER_SEC, + 'Delay should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().fill, + 'both', + 'Fill should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().iterations, + 6, + 'Iterations should be the value set by style' + ); + assert_equals( + animation.effect.getTiming().direction, + 'reverse', + 'Direction should be the value set by style' + ); +}, 'AnimationEffect.updateTiming({ delay, fill }) causes changes to' + + ' the animation-delay and animation-fill-mode to be ignored'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-empty 100s'; + + const animation = div.getAnimations()[0]; + assert_throws_js(TypeError, () => { + animation.effect.updateTiming({ duration: 2 * MS_PER_SEC, iterations: -1 }); + }, 'Negative iteration count should cause an error to be thrown'); + + div.style.animationDuration = '4s'; + + assert_equals( + animation.effect.getTiming().duration, + 4 * MS_PER_SEC, + 'Duration should be the value set by style' + ); +}, 'AnimationEffect.updateTiming() does override to changes from animation-*' + + ' properties if there is an error'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-empty 100s'; + + const animation = div.getAnimations()[0]; + animation.effect.updateTiming({ + easing: 'steps(4)', + endDelay: 2 * MS_PER_SEC, + iterationStart: 4, + }); + + div.style.animationDuration = '6s'; + div.style.animationTimingFunction = 'ease-in'; + + assert_equals( + animation.effect.getTiming().easing, + 'steps(4)', + 'endDelay should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().endDelay, + 2 * MS_PER_SEC, + 'endDelay should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().iterationStart, + 4, + 'iterationStart should be the value set by style' + ); +}, 'AnimationEffect properties that do not map to animation-* properties' + + ' should not be changed when animation-* style is updated'); + +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html new file mode 100644 index 0000000000..370d5ef85e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html @@ -0,0 +1,40 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.animationName</title> +<link rel="help" + href="https://drafts.csswg.org/css-animations-2/#dom-cssanimation-animationname"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes xyz { + to { left: 100px } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + div.style.animation = 'xyz 100s'; + assert_equals(div.getAnimations()[0].animationName, 'xyz', + 'Animation name matches keyframes rule name'); +}, 'Animation name makes keyframe rule'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'x\\yz 100s'; + assert_equals(div.getAnimations()[0].animationName, 'xyz', + 'Escaped animation name matches keyframes rule name'); +}, 'Escaped animation name'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'x\\79 z 100s'; + assert_equals(div.getAnimations()[0].animationName, 'xyz', + 'Hex-escaped animation name matches keyframes rule' + + ' name'); +}, 'Animation name with hex-escape'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html new file mode 100644 index 0000000000..b76af2c24a --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html @@ -0,0 +1,196 @@ +<!doctype html> +<meta charset=utf-8> +<title>Canceling a CSS animation</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes translateAnim { + to { transform: translate(100px) } +} +@keyframes marginLeftAnim { + to { margin-left: 100px } +} +@keyframes marginLeftAnim100To200 { + from { margin-left: 100px } + to { margin-left: 200px } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: translateAnim 100s' }); + const animation = div.getAnimations()[0]; + + await animation.ready; + + assert_not_equals(getComputedStyle(div).transform, 'none', + 'transform style is animated before canceling'); + animation.cancel(); + assert_equals(getComputedStyle(div).transform, 'none', + 'transform style is no longer animated after canceling'); +}, 'Animated style is cleared after canceling a running CSS animation'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: translateAnim 100s forwards' }); + const animation = div.getAnimations()[0]; + animation.finish(); + + await animation.ready; + + assert_not_equals(getComputedStyle(div).transform, 'none', + 'transform style is filling before canceling'); + animation.cancel(); + assert_equals(getComputedStyle(div).transform, 'none', + 'fill style is cleared after canceling'); +}, 'Animated style is cleared after canceling a filling CSS animation'); + +test(t => { + const div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' }); + const animation = div.getAnimations()[0]; + animation.cancel(); + + assert_equals(getComputedStyle(div).marginLeft, '0px', + 'margin-left style is not animated after canceling'); + + animation.currentTime = 50 * 1000; + assert_equals(getComputedStyle(div).marginLeft, '50px', + 'margin-left style is updated when canceled animation is' + + ' seeked'); +}, 'After canceling an animation, it can still be seeked'); + +promise_test(async t => { + const div = + addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' }); + const animation = div.getAnimations()[0]; + + await animation.ready; + + animation.cancel(); + assert_equals(getComputedStyle(div).marginLeft, '0px', + 'margin-left style is not animated after canceling'); + animation.play(); + assert_equals(getComputedStyle(div).marginLeft, '100px', + 'margin-left style is animated after re-starting animation'); + + await animation.ready; + + assert_equals(animation.playState, 'running', + 'Animation succeeds in running after being re-started'); +}, 'After canceling an animation, it can still be re-used'); + +test(t => { + const div = + addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' }); + const animation = div.getAnimations()[0]; + animation.cancel(); + assert_equals(getComputedStyle(div).marginLeft, '0px', + 'margin-left style is not animated after canceling'); + + // Trigger a change to some animation properties and check that this + // doesn't cause the animation to become live again + div.style.animationDuration = '200s'; + assert_equals(getComputedStyle(div).marginLeft, '0px', + 'margin-left style is still not animated after updating' + + ' animation-duration'); + assert_equals(animation.playState, 'idle', + 'Animation is still idle after updating animation-duration'); +}, 'After canceling an animation, updating animation properties doesn\'t make' + + ' it live again'); + +test(t => { + const div = + addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' }); + const animation = div.getAnimations()[0]; + animation.cancel(); + assert_equals(getComputedStyle(div).marginLeft, '0px', + 'margin-left style is not animated after canceling'); + + // Make some changes to animation-play-state and check that the + // animation doesn't become live again. This is because it should be + // possible to cancel an animation from script such that all future + // changes to style are ignored. + + // Redundant change + div.style.animationPlayState = 'running'; + assert_equals(animation.playState, 'idle', + 'Animation is still idle after a redundant change to' + + ' animation-play-state'); + + // Pause + div.style.animationPlayState = 'paused'; + assert_equals(animation.playState, 'idle', + 'Animation is still idle after setting' + + ' animation-play-state: paused'); + + // Play + div.style.animationPlayState = 'running'; + assert_equals(animation.playState, 'idle', + 'Animation is still idle after re-setting' + + ' animation-play-state: running'); + +}, 'After canceling an animation, updating animation-play-state doesn\'t' + + ' make it live again'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: translateAnim 10s both' }); + div.style.marginLeft = '0px'; + + const animation = div.getAnimations()[0]; + + await animation.ready; + + assert_equals(animation.playState, 'running'); + + div.style.animationName = 'none'; + flushComputedStyle(div); + + await waitForFrame(); + + assert_equals(animation.playState, 'idle'); + assert_equals(getComputedStyle(div).marginLeft, '0px'); +}, 'Setting animation-name to \'none\' cancels the animation'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: translateAnim 10s both' }); + const animation = div.getAnimations()[0]; + + await animation.ready; + + assert_equals(animation.playState, 'running'); + + div.style.display = 'none'; + + await waitForFrame(); + + assert_equals(animation.playState, 'idle'); + assert_equals(getComputedStyle(div).marginLeft, '0px'); +}, 'Setting display:none on an element cancel its animations'); + +promise_test(async t => { + const parentDiv = addDiv(t); + const childDiv = document.createElement('div'); + parentDiv.appendChild(childDiv); + + childDiv.setAttribute('style', 'animation: translateAnim 10s both'); + flushComputedStyle(childDiv); + + const animation = childDiv.getAnimations()[0]; + + await animation.ready; + + assert_equals(animation.playState, 'running'); + + parentDiv.style.display = 'none'; + await waitForFrame(); + + assert_equals(animation.playState, 'idle'); + assert_equals(getComputedStyle(childDiv).marginLeft, '0px'); +}, 'Setting display:none on an ancestor element cancels animations on ' + + 'descendants'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-compositeOrder.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-compositeOrder.tentative.html new file mode 100644 index 0000000000..d55db9a2d1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-compositeOrder.tentative.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Animation composite order</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-composite-order"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes margin50 { + from { + margin-left: 50px; + } + to { + margin-left: 50px; + } +} +@keyframes margin100 { + from { + margin-left: 100px; + } + to { + margin-left: 100px; + } +} +</style> +<div id="log"</div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'margin100 100s'; + assert_equals(getComputedStyle(div).marginLeft, '100px'); + div.style.animation = 'margin50 100s, margin100 100s'; + // The margin should be unaffected by margin50 since it is named earlier + // in the animation list. + assert_equals(getComputedStyle(div).marginLeft, '100px'); +}, "Animations are composited by their order in the animation-name property."); + +promise_test(async t => { + const div = addDiv(t); + const animA = div.animate({margin: ["100px","100px"]}, 100000); + assert_equals(getComputedStyle(div).marginLeft, '100px'); + div.style.animation = 'margin50 100s'; + // Wait for animation starts + await animA.ready; + await waitForAnimationFrames(1); + assert_equals(getComputedStyle(div).marginLeft, '100px', + "A higher-priority animation is not overriden by a more recent" + + "one."); +}, 'Web-animation replaces CSS animation'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html new file mode 100644 index 0000000000..04812d24e5 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html @@ -0,0 +1,225 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.effect</title> +<meta name="timeout" content="long"> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { + from { + margin-left: 0px; + } + to { + margin-left: 100px; + } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const watcher = new EventWatcher(t, div, [ 'animationend', + 'animationcancel' ], + fastEventsTimeout); + const animation = div.getAnimations()[0]; + await animation.ready; + + animation.currentTime = 50 * MS_PER_SEC; + animation.effect = null; + assert_equals(animation.playState, 'finished'); + assert_equals(getComputedStyle(div).marginLeft, '0px'); + await watcher.wait_for('animationend'); +}, 'Setting a null effect on a running animation fires an animationend event'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + await animation.ready; + + animation.currentTime = 50 * MS_PER_SEC; + animation.effect = new KeyframeEffect(div, + { left: [ '0px' , '100px'] }, + 100 * MS_PER_SEC); + assert_equals(getComputedStyle(div).marginLeft, '0px'); + assert_equals(getComputedStyle(div).left, '50px'); +}, 'Replacing an animation\'s effect with an effect that targets a different ' + + 'property should update both properties'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + await animation.ready; + + animation.currentTime = 50 * MS_PER_SEC; + animation.effect = new KeyframeEffect(div, + { left: [ '0px' , '100px'] }, + 20 * MS_PER_SEC); + assert_equals(animation.playState, 'finished'); +}, 'Replacing an animation\'s effect with a shorter one that should have ' + + 'already finished, the animation finishes immediately'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + assert_true(animation.pending); + + animation.effect = new KeyframeEffect(div, + { left: [ '0px' , '100px'] }, + 100 * MS_PER_SEC); + assert_true(animation.pending); + + await animation.ready; + + assert_false(animation.pending); +}, 'A play-pending animation\'s effect whose effect is replaced still exits ' + + 'the pending state'); + +promise_test(async t => { + const div1 = addDiv(t); + const div2 = addDiv(t); + + const watcher1 = new EventWatcher(t, div1, 'animationstart', + fastEventsTimeout); + // Watch |div2| as well to ensure it does *not* get events. + const watcher2 = new EventWatcher(t, div2, 'animationstart'); + + div1.style.animation = 'anim 100s'; + + const animation = div1.getAnimations()[0]; + animation.effect = new KeyframeEffect(div2, + { left: [ '0px', '100px' ] }, + 100 * MS_PER_SEC); + + await watcher1.wait_for('animationstart'); + + assert_equals(animation.effect.target, div2); + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'CSS animation events are dispatched at the original element even after' + + ' setting an effect with a different target element'); + +promise_test(async t => { + const div = addDiv(t); + const watcher = new EventWatcher(t, div, [ 'animationstart', + 'animationend', + 'animationcancel' ], + fastEventsTimeout); + div.style.animation = 'anim 100s'; + const animation = div.getAnimations()[0]; + animation.finish(); + + await watcher.wait_for([ 'animationstart', 'animationend' ]); + // Set a longer effect + animation.effect = new KeyframeEffect(div, + { left: [ '0px', '100px' ] }, + 200 * MS_PER_SEC); + await watcher.wait_for('animationstart'); +}, 'After replacing a finished animation\'s effect with a longer one ' + + 'it fires an animationstart event'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + div.style.animationComposition = 'add'; + const animation = div.getAnimations()[0]; + assert_equals(animation.effect.composite, 'add'); +}, 'Setting animation-composition sets the composite property on the effect'); + +test(t => { + const div = addDiv(t); + + // Create custom keyframes so we can tweak them + const stylesheet = document.styleSheets[0]; + const keyframes = '@keyframes anim-custom { to { left: 100px } }'; + const ruleIndex = stylesheet.insertRule(keyframes, 0); + const keyframesRule = stylesheet.cssRules[ruleIndex]; + + t.add_cleanup(function() { + stylesheet.deleteRule(ruleIndex); + }); + + div.style.animation = 'anim-custom 100s'; + + // Replace the effect + const animation = div.getAnimations()[0]; + animation.effect = new KeyframeEffect( + div, + { left: '200px' }, + 200 * MS_PER_SEC + ); + + // Update the timing properties + div.style.animationDuration = '4s'; + div.style.animationIterationCount = '6'; + div.style.animationDirection = 'reverse'; + div.style.animationDelay = '8s'; + div.style.animationFillMode = 'both'; + div.style.animationPlayState = 'paused'; + div.style.animationComposition = 'add'; + + // Update the keyframes + keyframesRule.deleteRule(0); + keyframesRule.appendRule('to { left: 300px }'); + + // Check nothing (except the play state) changed + assert_equals( + animation.effect.getTiming().duration, + 200 * MS_PER_SEC, + 'duration should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().iterations, + 1, + 'iterations should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().direction, + 'normal', + 'direction should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().delay, + 0, + 'delay should be the value set by the API' + ); + assert_equals( + animation.effect.getTiming().fill, + 'auto', + 'fill should be the value set by the API' + ); + assert_equals( + animation.effect.getKeyframes()[0].left, + '200px', + 'keyframes should be the value set by the API' + ); + assert_equals( + animation.effect.composite, + 'replace', + 'composite should be the value set by the API' + ); + + // Unlike the other properties animation-play-state maps to the Animation + // not the KeyframeEffect so it should be overridden. + assert_equals( + animation.playState, + 'paused', + 'play state should be the value set by style' + ); +}, 'Replacing the effect of a CSSAnimation causes subsequent changes to' + + ' corresponding animation-* properties to be ignored'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html new file mode 100644 index 0000000000..da087b93e0 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html @@ -0,0 +1,85 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.finished</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes abc { + to { transform: translate(10px) } +} +@keyframes def {} +</style> +<div id="log"></div> +<script> +'use strict'; + +const ANIM_PROP_VAL = 'abc 100s'; +const ANIM_DURATION = 100 * MS_PER_SEC; + +promise_test(async t => { + const div = addDiv(t); + + // Set up pending animation + div.style.animation = ANIM_PROP_VAL; + const animation = div.getAnimations()[0]; + const originalFinishedPromise = animation.finished; + + // Cancel the animation and flush styles + div.style.animation = ''; + getComputedStyle(div).animation; + + await promise_rejects_dom(t, 'AbortError', originalFinishedPromise, + 'finished promise is rejected with AbortError'); + + assert_not_equals(animation.finished, originalFinishedPromise, + 'Finished promise should change after the original is ' + + 'rejected'); +}, 'finished promise is rejected when an animation is canceled by resetting ' + + 'the animation property'); + +promise_test(async t => { + const div = addDiv(t); + // As before, but this time instead of removing all animations, simply update + // the list of animations. At least for Firefox, updating is a different + // code path. + + // Set up pending animation + div.style.animation = ANIM_PROP_VAL; + const animation = div.getAnimations()[0]; + const originalFinishedPromise = animation.finished; + + // Update the animation and flush styles + div.style.animation = 'def 100s'; + getComputedStyle(div).animation; + + await promise_rejects_dom(t, 'AbortError', originalFinishedPromise, + 'finished promise is rejected with AbortError'); + + assert_not_equals(animation.finished, originalFinishedPromise, + 'Finished promise should change after the original is ' + + 'rejected'); +}, 'finished promise is rejected when an animation is canceled by changing ' + + 'the animation property'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = ANIM_PROP_VAL; + const animation = div.getAnimations()[0]; + const originalFinishedPromise = animation.finished; + animation.currentTime = ANIM_DURATION; + + await animation.finished; + + div.style.animationPlayState = 'running'; + await waitForAnimationFrames(2); + + assert_equals(animation.finished, originalFinishedPromise, + 'The finished promise should NOT be reset'); + assert_equals(animation.currentTime, ANIM_DURATION, + 'Sanity check: the current time should not change'); +}, 'finished promise is not reset when animationPlayState is set to running'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html new file mode 100644 index 0000000000..e4d1f859fd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html @@ -0,0 +1,68 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.currentTime</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +.animated-div { + margin-left: 10px; + /* Make it easier to calculate expected values: */ + animation-timing-function: linear ! important; +} + +@keyframes anim { + from { margin-left: 100px; } + to { margin-left: 200px; } +} + +</style> +<div id="log"></div> +<script type="text/javascript"> + +'use strict'; + +promise_test(async t => { + const div = addDiv(t, { class: 'animated-div' }); + div.style.animation = 'anim 100s'; + const animation = div.getAnimations()[0]; + + assert_equals( + animation.currentTime, + 0, + 'Animation.currentTime should be zero when an animation ' + + 'is initially created' + ); + + await animation.ready; + + animation.currentTime = 50 * MS_PER_SEC; + + assert_time_equals_literal( + animation.currentTime, + 50 * MS_PER_SEC, + 'Check setting of currentTime actually works' + ); + assert_equals(getComputedStyle(div).marginLeft, '150px'); +}, 'currentTime can be used to seek a CSS animation'); + +promise_test(async t => { + const div = addDiv(t, { class: 'animated-div' }); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + await animation.ready; + + assert_throws_js( + TypeError, + () => { + animation.currentTime = null; + }, + 'Expect TypeError exception on trying to set Animation.currentTime to null' + ); +}, 'Setting currentTime to null on a CSS animation throws'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-getKeyframes-crash.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-getKeyframes-crash.html new file mode 100644 index 0000000000..72eb5e8819 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-getKeyframes-crash.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="help" href="https://crbug.com/1326225"> + <title>Crash test calling getKeyframes on an orphaned element</title> +</head> +<style type="text/css"> + @keyframes anim { + from { left: 0; } + } +</style> +<body> + <div id="container"> + <div id="target"> + </div> + </div> +</body> +<script type="text/javascript"> + target.style.animation = "anim 0.01s"; + var animation = target.getAnimations()[0]; + container.innerHTML = 1; + animation.effect.getKeyframes()[0].hasOwnProperty(); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html new file mode 100644 index 0000000000..623e01d235 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html @@ -0,0 +1,27 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.id</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes abc { } +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + div.style.animation = 'abc 100s'; + const animation = div.getAnimations()[0]; + assert_equals(animation.id, '', 'id for CSS Animation is initially empty'); + + animation.id = 'anim' + assert_equals(animation.id, 'anim', 'animation.id reflects the value set'); +}, 'Animation.id for CSS Animations'); + +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html new file mode 100644 index 0000000000..156a1afa96 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html @@ -0,0 +1,259 @@ +<!doctype html> +<meta charset=utf-8> +<title>Pausing a CSSAnimation</title> +<link rel="help" + href="https://drafts.csswg.org/css-animations-2/#animation-play-state"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { + 0% { margin-left: 0px } + 100% { margin-left: 10000px } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 1000s paused'; + + const animation = div.getAnimations()[0]; + animation.play(); + + await animation.ready; + await waitForNextFrame(); + + assert_equals( + animation.playState, + 'running', + 'Play state is running after calling play()' + ); + + // Flip the animation-play-state back and forth to check it has no effect + + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'running', + 'Should still be running even after flipping the animation-play-state' + ); +}, 'play() overrides animation-play-state'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s infinite paused'; + + const animation = div.getAnimations()[0]; + animation.playbackRate = -1; + animation.currentTime = -1; + + assert_throws_dom('InvalidStateError', () => { + animation.play(); + }, 'Trying to play a reversed infinite animation should throw'); + + assert_equals( + animation.playState, + 'paused', + 'Animation should still be paused' + ); + + animation.playbackRate = 1; + div.style.animationPlayState = 'running'; + + assert_equals( + animation.playState, + 'running', + 'Changing the animation-play-state should play the animation' + ); +}, 'play() does NOT override the animation-play-state if there was an error'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 1000s paused'; + + const animation = div.getAnimations()[0]; + animation.pause(); + + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + + await animation.ready; + await waitForNextFrame(); + + assert_equals(animation.playState, 'paused', 'playState is paused '); + + // Flip the animation-play-state back and forth to check it has no effect + + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'paused', + 'Should still be paused even after flipping the animation-play-state' + ); +}, 'pause() overrides animation-play-state'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s paused'; + + const animation = div.getAnimations()[0]; + animation.reverse(); + + assert_equals( + animation.playState, + 'running', + 'Play state is running after calling reverse()' + ); + + // Flip the animation-play-state back and forth to check it has no effect + + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'running', + 'Should still be running even after flipping the animation-play-state' + ); +}, 'reverse() overrides animation-play-state when it starts playing the' + + ' animation'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + animation.reverse(); + + assert_equals( + animation.playState, + 'running', + 'Play state is running after calling reverse()' + ); + + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'paused', + 'Should be paused after changing the animation-play-state' + ); +}, 'reverse() does NOT override animation-play-state if the animation is' + + ' already running'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + animation.startTime = null; + + assert_equals( + animation.playState, + 'paused', + 'Play state is paused after setting the start time to null' + ); + + // Flip the animation-play-state back and forth to check it has no effect + + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'paused', + 'Should still be paused even after flipping the animation-play-state' + ); +}, 'Setting the startTime to null overrides animation-play-state if the' + + ' animation is already running'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s paused'; + + const animation = div.getAnimations()[0]; + animation.startTime = document.timeline.currentTime; + + assert_equals( + animation.playState, + 'running', + 'Play state is running after setting the start time to non-null' + ); + + // Flip the animation-play-state back and forth to check it has no effect + + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'running', + 'Should still be running even after flipping the animation-play-state' + ); +}, 'Setting the startTime to non-null overrides animation-play-state if the' + + ' animation is paused'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + + const animation = div.getAnimations()[0]; + animation.startTime = document.timeline.currentTime; + + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + + assert_equals( + animation.playState, + 'paused', + 'Should be paused after changing the animation-play-state' + ); +}, 'Setting the startTime to non-null does NOT override the' + + ' animation-play-state if the animation is already running'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: anim 1000s' }); + const animation = div.getAnimations()[0]; + let readyPromiseRun = false; + + await animation.ready; + + div.style.animationPlayState = 'paused'; + assert_true(animation.pending && animation.playState === 'paused', + 'Animation is pause-pending'); + + // Set current time + animation.currentTime = 5 * MS_PER_SEC; + assert_equals(animation.playState, 'paused', + 'Animation is paused immediately after setting currentTime'); + assert_equals(animation.startTime, null, + 'Animation startTime is unresolved immediately after ' + + 'setting currentTime'); + assert_equals(animation.currentTime, 5 * MS_PER_SEC, + 'Animation currentTime does not change when forcing a ' + + 'pause operation to complete'); + + // The ready promise should now be resolved. If it's not then test will + // probably time out before anything else happens that causes it to resolve. + await animation.ready; +}, 'Setting the current time completes a pending pause'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html new file mode 100644 index 0000000000..acfdc1348f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.playState</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { } +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t, { 'style': 'animation: anim 100s' }); + const animation = div.getAnimations()[0]; + assert_true(animation.pending); + assert_equals(animation.playState, 'running'); + assert_equals(animation.startTime, null); +}, 'A new CSS animation is initially play-pending'); + +test(t => { + const div = addDiv(t, { 'style': 'animation: anim 1000s paused' }); + const animation = div.getAnimations()[0]; + assert_equals(animation.playState, 'paused'); +}, 'Animation returns correct playState when paused'); + +test(t => { + const div = addDiv(t, { 'style': 'animation: anim 1000s' }); + const animation = div.getAnimations()[0]; + animation.pause(); + assert_equals(animation.playState, 'paused'); +}, 'Animation.playState updates when paused by script'); + +test(t => { + const div = addDiv(t, { 'style': 'animation: anim 1000s paused' }); + const animation = div.getAnimations()[0]; + div.style.animationPlayState = 'running'; + + // This test also checks that calling playState flushes style + assert_equals(animation.playState, 'running', + 'Animation.playState reports running after updating' + + ' animation-play-state (got: ' + animation.playState + ')'); +}, 'Animation.playState updates when resumed by setting style'); + +test(t => { + const div = addDiv(t, { 'style': 'animation: anim 1000s' }); + const animation = div.getAnimations()[0]; + animation.cancel(); + assert_equals(animation.playState, 'idle'); +}, 'Animation returns correct playState when canceled'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html new file mode 100644 index 0000000000..2a22760693 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html @@ -0,0 +1,98 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.ready</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes abc { + to { transform: translate(10px) } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'abc 100s paused'; + const animation = div.getAnimations()[0]; + const originalReadyPromise = animation.ready; + + await animation.ready; + + div.style.animationPlayState = 'running'; + assert_not_equals(animation.ready, originalReadyPromise, + 'After updating animation-play-state a new ready promise' + + ' object is created'); +}, 'A new ready promise is created when setting animation-play-state: running'); + +promise_test(async t => { + const div = addDiv(t); + + // Set up pending animation + div.style.animation = 'abc 100s'; + const animation = div.getAnimations()[0]; + assert_true(animation.pending, 'Animation is initially pending'); + const readyPromise = animation.ready; + + // Cancel the animation and flush styles + div.style.animation = ''; + getComputedStyle(div).animation; + + await promise_rejects_dom(t, 'AbortError', readyPromise, + 'ready promise is rejected with AbortError'); +}, 'ready promise is rejected when an animation is canceled by resetting' + + ' the animation property'); + +promise_test(async t => { + const div = addDiv(t); + + // As before, but this time instead of removing all animations, simply update + // the list of animations. At least for Firefox, updating is a different + // code path. + + // Set up pending animation + div.style.animation = 'abc 100s'; + const animation = div.getAnimations()[0]; + assert_true(animation.pending, 'Animation is initially pending'); + const readyPromise = animation.ready; + + // Update the animation and flush styles + div.style.animation = 'def 100s'; + getComputedStyle(div).animation; + + await promise_rejects_dom(t, 'AbortError', readyPromise, + 'ready promise is rejected with AbortError'); +}, 'ready promise is rejected when an animation is canceled by updating' + + ' the animation property'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: abc 100s' }); + const animation = div.getAnimations()[0]; + const originalReadyPromise = animation.ready; + + await animation.ready; + + div.style.animationPlayState = 'paused'; + assert_not_equals(animation.ready, originalReadyPromise, + 'A new Promise object is generated when setting' + + ' animation-play-state: paused'); +}, 'A new ready promise is created when setting animation-play-state: paused'); + +promise_test(async t => { + const div = addDiv(t, { style: 'animation: abc 100s' }); + const animation = div.getAnimations()[0]; + + await animation.ready; + + div.style.animationPlayState = 'paused'; + const firstReadyPromise = animation.ready; + animation.pause(); + assert_equals(animation.ready, firstReadyPromise, + 'Ready promise objects are identical after redundant pause'); +}, 'Pausing twice re-uses the same Promise'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html b/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html new file mode 100644 index 0000000000..edef3b239e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html @@ -0,0 +1,69 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.startTime</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +.animated-div { + margin-left: 10px; + /* Make it easier to calculate expected values: */ + animation-timing-function: linear ! important; +} + +@keyframes anim { + from { margin-left: 100px; } + to { margin-left: 200px; } +} + +</style> +<div id="log"></div> +<script type="text/javascript"> + +'use strict'; + +test(t => { + const div = addDiv(t, { 'class': 'animated-div' }); + div.style.animation = 'anim 100s 100s'; + const animation = div.getAnimations()[0]; + + const timelineTime = animation.timeline.currentTime; + animation.startTime = timelineTime; + + assert_times_equal(animation.startTime, timelineTime, + 'Check setting of startTime actually works'); +}, 'The start time of a CSS animation can be set'); + +promise_test(async t => { + const div = addDiv(t, { 'class': 'animated-div' }); + div.style.animation = 'anim 100s 100s'; + const animation = div.getAnimations()[0]; + + // Seek to the half-way point + animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC; + + assert_equals(getComputedStyle(div).marginLeft, '150px'); +}, 'The start time can be set to seek a CSS animation'); + +promise_test(async t => { + const div = addDiv(t, { class: 'animated-div' }); + const eventWatcher = new EventWatcher(t, div, [ + 'animationstart', + 'animationend', + ]); + div.style.animation = 'anim 100s 100s'; + const animation = div.getAnimations()[0]; + + await animation.ready; + + animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC; + await eventWatcher.wait_for('animationstart'); + + animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC; + await eventWatcher.wait_for('animationend'); +}, 'Seeking a CSS animation using the start time dispatches animation events'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html b/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html new file mode 100644 index 0000000000..758f680949 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html @@ -0,0 +1,430 @@ +<!doctype html> +<meta charset=utf-8> +<title>Document.getAnimations() for CSS animations</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-composite-order"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes animLeft { + to { left: 100px } +} +@keyframes animTop { + to { top: 100px } +} +@keyframes animBottom { + to { bottom: 100px } +} +@keyframes animRight { + to { right: 100px } +} + +@keyframes anim1 { + to { left: 100px } +} +@keyframes anim2 { + to { top: 100px } +} +@keyframes anim3 { + to { bottom: 100px } +} +@keyframes anim4 { + to { right: 100px } +} + +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + assert_equals(document.getAnimations().length, 0, + 'getAnimations returns an empty sequence for a document' + + ' with no animations'); +}, 'getAnimations for non-animated content'); + +test(t => { + const div = addDiv(t); + + // Add an animation + div.style.animation = 'animLeft 100s'; + assert_equals(document.getAnimations().length, 1, + 'getAnimations returns a running CSS Animation'); + + // Add another animation + div.style.animation = 'animLeft 100s, animTop 100s'; + assert_equals(document.getAnimations().length, 2, + 'getAnimations returns two running CSS Animations'); + + // Remove both + div.style.animation = ''; + assert_equals(document.getAnimations().length, 0, + 'getAnimations returns no running CSS Animations'); +}, 'getAnimations for CSS Animations'); + +test(t => { + const div = addDiv(t); + const animation1 = 'animLeft 100s' + const animation2 = 'animBottom 100s' + div.style.animation = animation1; + const animations1 = document.getAnimations(); + assert_equals(animations1.length, 1, + 'getAnimations returns all running CSS Animations'); + div.style.animation = animation2 + ', ' + animation1; + const animations = document.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns all running CSS Animations'); + assert_equals(animations[0].animationName, 'animBottom', + 'Order of first animation returned'); + assert_equals(animations[1].animationName, 'animLeft', + 'Order of second animation returned'); +}, 'Order of CSS Animations - within an element unaffected by start time'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' + + 'animBottom 100s'; + + const animations = document.getAnimations(); + assert_equals(animations.length, 4, + 'getAnimations returns all running CSS Animations'); + assert_equals(animations[0].animationName, 'animLeft', + 'Order of first animation returned'); + assert_equals(animations[1].animationName, 'animTop', + 'Order of second animation returned'); + assert_equals(animations[2].animationName, 'animRight', + 'Order of third animation returned'); + assert_equals(animations[3].animationName, 'animBottom', + 'Order of fourth animation returned'); +}, 'Order of CSS Animations - within an element'); + +test(t => { + const div1 = addDiv(t, { style: 'animation: animLeft 100s' }); + const div2 = addDiv(t, { style: 'animation: animLeft 100s' }); + const div3 = addDiv(t, { style: 'animation: animLeft 100s' }); + const div4 = addDiv(t, { style: 'animation: animLeft 100s' }); + + let animations = document.getAnimations(); + assert_equals(animations.length, 4, + 'getAnimations returns all running CSS Animations'); + assert_equals(animations[0].effect.target, div1, + 'Order of first animation returned'); + assert_equals(animations[1].effect.target, div2, + 'Order of second animation returned'); + assert_equals(animations[2].effect.target, div3, + 'Order of third animation returned'); + assert_equals(animations[3].effect.target, div4, + 'Order of fourth animation returned'); + + // Order should be depth-first pre-order so add some depth as follows: + // + // <parent> + // / | + // 2 3 + // / \ + // 1 4 + // + // Which should give: 2, 1, 4, 3 + div2.appendChild(div1); + div2.appendChild(div4); + animations = document.getAnimations(); + assert_equals(animations[0].effect.target, div2, + 'Order of first animation returned after tree surgery'); + assert_equals(animations[1].effect.target, div1, + 'Order of second animation returned after tree surgery'); + assert_equals(animations[2].effect.target, div4, + 'Order of third animation returned after tree surgery'); + assert_equals(animations[3].effect.target, div3, + 'Order of fourth animation returned after tree surgery'); + +}, 'Order of CSS Animations - across elements'); + +test(t => { + const div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' }); + const div2 = addDiv(t, { style: 'animation: animBottom 100s' }); + + let expectedResults = [ [ div1, 'animLeft' ], + [ div1, 'animTop' ], + [ div2, 'animBottom' ] ]; + let animations = document.getAnimations(); + assert_equals(animations.length, expectedResults.length, + 'getAnimations returns all running CSS Animations'); + animations.forEach((anim, i) => { + assert_equals(anim.effect.target, expectedResults[i][0], + 'Target of animation in position ' + i); + assert_equals(anim.animationName, expectedResults[i][1], + 'Name of animation in position ' + i); + }); + + // Modify tree structure and animation list + div2.appendChild(div1); + div1.style.animation = 'animLeft 100s, animRight 100s, animTop 100s'; + + expectedResults = [ [ div2, 'animBottom' ], + [ div1, 'animLeft' ], + [ div1, 'animRight' ], + [ div1, 'animTop' ] ]; + animations = document.getAnimations(); + assert_equals(animations.length, expectedResults.length, + 'getAnimations returns all running CSS Animations after ' + + 'making changes'); + animations.forEach((anim, i) => { + assert_equals(anim.effect.target, expectedResults[i][0], + 'Target of animation in position ' + i + ' after changes'); + assert_equals(anim.animationName, expectedResults[i][1], + 'Name of animation in position ' + i + ' after changes'); + }); +}, 'Order of CSS Animations - across and within elements'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' }); + const animLeft = document.getAnimations()[0]; + assert_equals(animLeft.animationName, 'animLeft', + 'Originally, animLeft animation comes first'); + + // Disassociate animLeft from markup and restart + div.style.animation = 'animTop 100s'; + animLeft.play(); + + const animations = document.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns markup-bound and free animations'); + assert_equals(animations[0].animationName, 'animTop', + 'Markup-bound animations come first'); + assert_equals(animations[1], animLeft, 'Free animations come last'); +}, 'Order of CSS Animations - markup-bound vs free animations'); + +test(t => { + // Add an animation first + const div = addDiv(t, { style: 'animation: animLeft 100s' }); + const animLeft = document.getAnimations()[0]; + // Disassociate animLeft from markup and restart + div.style.animation = ''; + animLeft.play(); + + div.style.top = '0px'; + div.style.transition = 'all 100s'; + flushComputedStyle(div); + // *Then* add a transition + div.style.top = '100px'; + flushComputedStyle(div); + + // Although the transition was added later, it should come first in the list + const animations = document.getAnimations(); + assert_equals(animations.length, 2, + 'Both CSS animations and transitions are returned'); + assert_class_string(animations[0], 'CSSTransition', 'Transition comes first'); + assert_equals(animations[1], animLeft, 'Free animations come last'); +}, 'Order of CSS Animations - free animation vs CSS Transitions'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' }); + const animLeft = document.getAnimations()[0]; + const animTop = document.getAnimations()[1]; + + // Disassociate both animations from markup and restart in opposite order + div.style.animation = ''; + animTop.play(); + animLeft.play(); + + const animations = document.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns free animations'); + assert_equals(animations[0], animTop, + 'Free animations are returned in the order they are started'); + assert_equals(animations[1], animLeft, + 'Animations started later are returned later'); + + // Restarting an animation should have no effect + animTop.cancel(); + animTop.play(); + assert_equals(document.getAnimations()[0], animTop, + 'After restarting, the ordering of free animations' + + ' does not change'); +}, 'Order of CSS Animations - free animations'); + +test(t => { + // Add an animation first + const div = addDiv(t, { style: 'animation: animLeft 100s' }); + div.style.top = '0px'; + div.style.transition = 'all 100s'; + flushComputedStyle(div); + + // *Then* add a transition + div.style.top = '100px'; + flushComputedStyle(div); + + // Although the transition was added later, it should come first in the list + const animations = document.getAnimations(); + assert_equals(animations.length, 2, + 'Both CSS animations and transitions are returned'); + assert_class_string(animations[0], 'CSSTransition', 'Transition comes first'); + assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second'); +}, 'Order of CSS Animations and CSS Transitions'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s forwards' }); + div.getAnimations()[0].finish(); + assert_equals(document.getAnimations().length, 1, + 'Forwards-filling CSS animations are returned'); +}, 'Finished but filling CSS Animations are returned'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s' }); + div.getAnimations()[0].finish(); + assert_equals(document.getAnimations().length, 0, + 'Non-filling finished CSS animations are not returned'); +}, 'Finished but not filling CSS Animations are not returned'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s 100s' }); + assert_equals(document.getAnimations().length, 1, + 'Yet-to-start CSS animations are returned'); +}, 'Yet-to-start CSS Animations are returned'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s' }); + div.getAnimations()[0].cancel(); + assert_equals(document.getAnimations().length, 0, + 'CSS animations canceled by the API are not returned'); +}, 'CSS Animations canceled via the API are not returned'); + +test(t => { + const div = addDiv(t, { style: 'animation: animLeft 100s' }); + const anim = div.getAnimations()[0]; + anim.cancel(); + anim.play(); + assert_equals(document.getAnimations().length, 1, + 'CSS animations canceled and restarted by the API are ' + + 'returned'); +}, 'CSS Animations canceled and restarted via the API are returned'); + +test(t => { + addStyle(t, { + '#parent::after': "content: ''; animation: anim1 100s ; ", + '#parent::before': "content: ''; animation: anim2 100s;", + '#child::after': "content: ''; animation: anim3 100s;", + '#child::before': "content: ''; animation: anim4 100s;", + }); + const parent = addDiv(t, { id: 'parent' }); + const child = addDiv(t, { id: 'child'}); + parent.appendChild(child); + var animations = document.getAnimations(); + var expectedAnimations = [ + [parent, '::before', 'anim2'], + [parent, '::after', 'anim1'], + [child, '::before', 'anim4'], + [child, '::after', 'anim3'], + ]; + pseudoAnimCompare(animations, expectedAnimations) + + // Swap animation1 and aimation3's effect + var anim1 = animations[0]; + var anim3 = animations[2]; + const temp = anim1.effect; + anim1.effect = anim3.effect; + anim3.effect = temp; + + animations = document.getAnimations(); + expectedAnimations = [ + [child, '::before', 'anim2'], + [parent, '::after', 'anim1'], + [parent, '::before', 'anim4'], + [child, '::after', 'anim3'], + ]; + pseudoAnimCompare(animations, expectedAnimations) +}, 'pseudo element with replaced target does not affect animation ordering'); + +function pseudoAnimCompare(animations, expectedAnimations) { + assert_equals( + animations.length, + expectedAnimations.length, + 'CSS animations on both pseudo-elements and elements are returned' + ); + for (const [index, expected] of expectedAnimations.entries()) { + const [element, pseudo, name] = expected; + const actual = animations[index]; + if (pseudo) { + assert_equals( + actual.effect.target, + element, + `Animation #${index + 1} has the expected target` + ); + assert_equals( + actual.effect.pseudoElement, + pseudo, + `Animation #${index + 1} has the expected pseudo type` + ); + if (name) { + assert_equals( + actual.animationName, + name, + `Animation #${index + 1} has the expected name` + ); + } + } else { + assert_equals( + actual.effect.target, + element, + `Animation #${index + 1} has the expected target` + ); + assert_equals( + actual.effect.pseudoElement, + null, + `Animation #${index + 1} has a null pseudo type` + ); + } + } +} + +function pseudoTest(description, testMarkerPseudos) { + test(t => { + // Create two divs with the following arrangement: + // + // parent + // (::marker,) // Optionally + // ::before, + // ::after + // | + // child + + addStyle(t, { + '#parent::after': "content: ''; animation: animLeft 100s;", + '#parent::before': "content: ''; animation: animRight 100s;", + }); + + if (testMarkerPseudos) { + addStyle(t, { + '#parent': 'display: list-item;', + '#parent::marker': "content: ''; animation: animLeft 100s;", + }); + } + + const parent = addDiv(t, { id: 'parent' }); + const child = addDiv(t); + parent.appendChild(child); + for (const div of [parent, child]) { + div.setAttribute('style', 'animation: animBottom 100s'); + } + + const expectedAnimations = [ + [parent, undefined], + [parent, '::marker'], + [parent, '::before'], + [parent, '::after'], + [child, undefined], + ]; + if (!testMarkerPseudos) { + expectedAnimations.splice(1, 1); + } + + const animations = document.getAnimations(); + pseudoAnimCompare(animations, expectedAnimations) + }, description); +} + +pseudoTest('CSS Animations targetting (pseudo-)elements should have correct ' + + 'order after sorting', false); +pseudoTest('CSS Animations targetting (pseudo-)elements should have correct ' + + 'order after sorting (::marker)', true); +</script> diff --git a/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html b/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html new file mode 100644 index 0000000000..a5e2288427 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html @@ -0,0 +1,163 @@ +<!doctype html> +<meta charset=utf-8> +<title> +Element.getAnimations() - Dynamic changes to the list of CSS animations +</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim1 { + to { left: 100px } +} +@keyframes anim2 { } +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s'; + const originalAnimation = div.getAnimations()[0]; + + // Wait a moment so we can confirm the startTime doesn't change (and doesn't + // simply reflect the current time). + await originalAnimation.ready; + + const originalStartTime = originalAnimation.startTime; + const originalCurrentTime = originalAnimation.currentTime; + + // Wait a moment so we can confirm the startTime doesn't change (and + // doesn't simply reflect the current time). + await waitForNextFrame(); + + div.style.animationDuration = '200s'; + const animation = div.getAnimations()[0]; + assert_equals(animation, originalAnimation, + 'The same Animation is returned after updating' + + ' animation duration'); + assert_equals(animation.startTime, originalStartTime, + 'Animations returned by getAnimations preserve' + + ' their startTime even when they are updated'); + // Sanity check + assert_not_equals(animation.currentTime, originalCurrentTime, + 'Animation.currentTime has updated in next' + + ' requestAnimationFrame callback'); +}, 'Animations preserve their startTime when changed'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s, anim1 100s'; + + // Store original state + let animations = div.getAnimations(); + const animation1 = animations[0]; + const animation2 = animations[1]; + + // Update first in list + div.style.animationDuration = '200s, 100s'; + animations = div.getAnimations(); + assert_equals(animations[0], animation1, + 'First Animation is in same position after update'); + assert_equals(animations[1], animation2, + 'Second Animation is in same position after update'); +}, 'Updated Animations maintain their order in the list'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 200s, anim1 100s'; + + // Store original state + let animations = div.getAnimations(); + const animation1 = animations[0]; + const animation2 = animations[1]; + + // Wait before continuing so we can compare start times (otherwise the + // new Animation objects and existing Animation objects will all have the same + // start time). + await waitForAllAnimations(animations); + await waitForFrame(); + + // Swap duration of first and second in list and prepend animation at the + // same time + div.style.animation = 'anim1 100s, anim1 100s, anim1 200s'; + animations = div.getAnimations(); + assert_true(animations[0] !== animation1 && animations[0] !== animation2, + 'New Animation is prepended to start of list'); + assert_equals(animations[1], animation1, + 'First animation is in second position after update'); + assert_equals(animations[2], animation2, + 'Second animation is in third position after update'); + assert_equals(animations[1].startTime, animations[2].startTime, + 'Old animations have the same start time'); + assert_equals(animations[0].startTime, null, + 'New animation has a null start time'); + + await animations[0].ready; + + assert_greater_than(animations[0].startTime, animations[1].startTime, + 'New animation has later start time'); +}, 'Only the startTimes of existing animations are preserved'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s, anim1 100s'; + const secondAnimation = div.getAnimations()[1]; + + // Wait before continuing so we can compare start times + await secondAnimation.ready; + await waitForNextFrame(); + + // Trim list of animations + div.style.animationName = 'anim1'; + const animations = div.getAnimations(); + assert_equals(animations.length, 1, 'List of Animations was trimmed'); + assert_equals(animations[0], secondAnimation, + 'Remaining Animation is the second one in the list'); + assert_equals(typeof(animations[0].startTime), 'number', + 'Remaining Animation has resolved startTime'); + assert_less_than(animations[0].startTime, + animations[0].timeline.currentTime, + 'Remaining Animation preserves startTime'); +}, 'Animations are removed from the start of the list while preserving' + + ' the state of existing Animations'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s'; + const firstAddedAnimation = div.getAnimations()[0]; + + // Wait and add second Animation + await firstAddedAnimation.ready; + await waitForFrame(); + + div.style.animation = 'anim1 100s, anim1 100s'; + const secondAddedAnimation = div.getAnimations()[0]; + + // Wait again and add another Animation + await secondAddedAnimation.ready; + await waitForFrame(); + + div.style.animation = 'anim1 100s, anim2 100s, anim1 100s'; + const animations = div.getAnimations(); + assert_not_equals(firstAddedAnimation, secondAddedAnimation, + 'New Animations are added to start of the list'); + assert_equals(animations[0], secondAddedAnimation, + 'Second Animation remains in same position after' + + ' interleaving'); + assert_equals(animations[2], firstAddedAnimation, + 'First Animation remains in same position after' + + ' interleaving'); + await animations[1].ready; + + assert_greater_than(animations[1].startTime, animations[0].startTime, + 'Interleaved animation starts later than existing ' + + 'animations'); + assert_greater_than(animations[0].startTime, animations[2].startTime, + 'Original animations retain their start time'); +}, 'Animation state is preserved when interleaving animations in list'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html b/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html new file mode 100644 index 0000000000..651c9d89ed --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html @@ -0,0 +1,453 @@ +<!doctype html> +<meta charset=utf-8> +<title>Element.getAnimations() for CSS animations</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim1 { + to { left: 100px } +} +@keyframes anim2 { + to { top: 100px } +} +@keyframes multiPropAnim { + to { background: green, opacity: 0.5, left: 100px, top: 100px } +} +::before { + content: '' +} +::after { + content: '' +} +@keyframes empty { } +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + assert_equals(div.getAnimations().length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with no animations'); +}, 'getAnimations for non-animated content'); + +promise_test(async t => { + const div = addDiv(t); + + // Add an animation + div.style.animation = 'anim1 100s'; + let animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns an Animation running CSS Animations'); + await animations[0].ready; + + // Add a second animation + div.style.animation = 'anim1 100s, anim2 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns one CSSAnimation for each value of animation-name'); + // (We don't check the order of the Animations since that is covered by tests + // later in this file.) +}, 'getAnimations for CSS Animations'); + +test(t => { + const div = addDiv(t, { style: 'animation: anim1 100s' }); + assert_class_string(div.getAnimations()[0], 'CSSAnimation', + 'Interface of returned animation is CSSAnimation'); +}, 'getAnimations returns CSSAnimation objects for CSS Animations'); + +test(t => { + const div = addDiv(t); + + // Add an animation that targets multiple properties + div.style.animation = 'multiPropAnim 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns only one Animation for a CSS Animation' + + ' that targets multiple properties'); +}, 'getAnimations for multi-property animations'); + +promise_test(async t => { + const div = addDiv(t); + + // Add an animation + div.style.backgroundColor = 'red'; + div.style.animation = 'anim1 100s'; + getComputedStyle(div).backgroundColor; + + // Wait until a frame after the animation starts, then add a transition + let animations = div.getAnimations(); + await animations[0].ready; + await waitForFrame(); + + div.style.transition = 'all 100s'; + div.style.backgroundColor = 'green'; + + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations returns Animations for both animations and' + + ' transitions that run simultaneously'); + assert_class_string(animations[0], 'CSSTransition', + 'First-returned animation is the CSS Transition'); + assert_class_string(animations[1], 'CSSAnimation', + 'Second-returned animation is the CSS Animation'); +}, 'getAnimations for both CSS Animations and CSS Transitions at once'); + +async_test(t => { + const div = addDiv(t); + + // Set up event listener + div.addEventListener('animationend', t.step_func(() => { + assert_equals(div.getAnimations().length, 0, + 'getAnimations does not return Animations for finished ' + + ' (and non-forwards-filling) CSS Animations'); + t.done(); + })); + + // Add a very short animation + div.style.animation = 'anim1 0.01s'; +}, 'getAnimations for CSS Animations that have finished'); + +async_test(t => { + const div = addDiv(t); + + // Set up event listener + div.addEventListener('animationend', t.step_func(() => { + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns Animations for CSS Animations that have' + + ' finished but are filling forwards'); + t.done(); + })); + + // Add a very short animation + div.style.animation = 'anim1 0.01s forwards'; +}, 'getAnimations for CSS Animations that have finished but are' + + ' forwards filling'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'none 100s'; + + let animations = div.getAnimations(); + assert_equals(animations.length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with animation-name: none'); + + div.style.animation = 'none 100s, anim1 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns Animations only for those CSS Animations whose' + + ' animation-name is not none'); +}, 'getAnimations for CSS Animations with animation-name: none'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'missing 100s'; + let animations = div.getAnimations(); + assert_equals(animations.length, 0, + 'getAnimations returns an empty sequence for an element' + + ' with animation-name: missing'); + + div.style.animation = 'anim1 100s, missing 100s'; + animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns Animations only for those CSS Animations whose' + + ' animation-name is found'); +}, 'getAnimations for CSS Animations with animation-name: missing'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s, notyet 100s'; + let animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations initally only returns Animations for CSS Animations whose' + + ' animation-name is found'); + + await animations[0].ready; + await waitForFrame(); + + const keyframes = '@keyframes notyet { to { left: 100px; } }'; + document.styleSheets[0].insertRule(keyframes, 0); + animations = div.getAnimations(); + assert_equals(animations.length, 2, + 'getAnimations includes Animation when @keyframes rule is added' + + ' later'); + await waitForAllAnimations(animations); + + assert_true(animations[0].startTime < animations[1].startTime, + 'Newly added animation has a later start time'); + document.styleSheets[0].deleteRule(0); +}, 'getAnimations for CSS Animations where the @keyframes rule is added' + + ' later'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s, anim1 100s'; + assert_equals(div.getAnimations().length, 2, + 'getAnimations returns one Animation for each CSS animation-name' + + ' even if the names are duplicated'); +}, 'getAnimations for CSS Animations with duplicated animation-name'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'empty 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns Animations for CSS animations with an' + + ' empty keyframes rule'); +}, 'getAnimations for CSS Animations with empty keyframes rule'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s 100s'; + const animations = div.getAnimations(); + assert_equals(animations.length, 1, + 'getAnimations returns animations for CSS animations whose' + + ' delay makes them start later'); + await animations[0].ready; + await waitForFrame(); + + assert_true(animations[0].startTime <= document.timeline.currentTime, + 'For CSS Animations in delay phase, the start time of the Animation is' + + ' not in the future'); +}, 'getAnimations for CSS animations in delay phase'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim1 0s 100s'; + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns animations for CSS animations whose' + + ' duration is zero'); + div.remove(); +}, 'getAnimations for zero-duration CSS Animations'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s'; + const originalAnimation = div.getAnimations()[0]; + + // Update pause state (an Animation change) + div.style.animationPlayState = 'paused'; + const pendingAnimation = div.getAnimations()[0]; + assert_equals(pendingAnimation.playState, 'paused', + 'animation\'s play state is updated'); + assert_equals(originalAnimation, pendingAnimation, + 'getAnimations returns the same objects even when their' + + ' play state changes'); + + // Update duration (an Animation change) + div.style.animationDuration = '200s'; + const extendedAnimation = div.getAnimations()[0]; + assert_equals( + extendedAnimation.effect.getTiming().duration, + 200 * MS_PER_SEC, + 'animation\'s duration has been updated' + ); + assert_equals(originalAnimation, extendedAnimation, + 'getAnimations returns the same objects even when their' + + ' duration changes'); +}, 'getAnimations returns objects with the same identity'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim1 100s'; + + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns an animation before canceling'); + + const animation = div.getAnimations()[0]; + + animation.cancel(); + assert_equals(div.getAnimations().length, 0, + 'getAnimations does not return canceled animations'); + + animation.play(); + assert_equals(div.getAnimations().length, 1, + 'getAnimations returns canceled animations that have been re-started'); + +}, 'getAnimations for CSS Animations that are canceled'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'anim2 100s'; + + await div.getAnimations()[0].ready; + + // Prepend to the list and test that even though anim1 was triggered + // *after* anim2, it should come first because it appears first + // in the animation-name property. + div.style.animation = 'anim1 100s, anim2 100s'; + let anims = div.getAnimations(); + assert_equals(anims[0].animationName, 'anim1', + 'animation order after prepending to list'); + assert_equals(anims[1].animationName, 'anim2', + 'animation order after prepending to list'); + + // Normally calling cancel and play would this push anim1 to the top of + // the stack but it shouldn't for CSS animations that map an the + // animation-name property. + const anim1 = anims[0]; + anim1.cancel(); + anim1.play(); + anims = div.getAnimations(); + assert_equals(anims[0].animationName, 'anim1', + 'animation order after canceling and restarting'); + assert_equals(anims[1].animationName, 'anim2', + 'animation order after canceling and restarting'); +}, 'getAnimations for CSS Animations follows animation-name order'); + +test(t => { + addStyle(t, { '#target::after': 'animation: anim1 10s;', + '#target::before': 'animation: anim1 10s;' }); + const target = addDiv(t, { 'id': 'target' }); + target.style.animation = 'anim1 100s'; + const animations = target.getAnimations({ subtree: false }); + + assert_equals(animations.length, 1, + 'Should find only the element'); + assert_equals(animations[0].effect.target, target, + 'Effect target should be the element'); +}, '{ subtree: false } on a leaf element returns the element\'s animations' + + ' and ignore pseudo-elements'); + +test(t => { + addStyle(t, { '#target::after': 'animation: anim1 10s;', + '#target::before': 'animation: anim1 10s;' }); + const target = addDiv(t, { 'id': 'target' }); + target.style.animation = 'anim1 100s'; + const animations = target.getAnimations({ subtree: true }); + + assert_equals(animations.length, 3, + 'getAnimations({ subtree: true }) ' + + 'should return animations on pseudo-elements'); + assert_equals(animations[0].effect.target, target, + 'The animation targeting the parent element ' + + 'should be returned first'); + assert_equals(animations[0].effect.pseudoElement, null, + 'The animation targeting the parent element ' + + 'should be returned first') + assert_equals(animations[1].effect.pseudoElement, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned second'); + assert_equals(animations[2].effect.pseudoElement, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned last'); +}, '{ subtree: true } on a leaf element returns the element\'s animations' + + ' and its pseudo-elements\' animations'); + +test(t => { + addStyle(t, { '#parent::after': 'animation: anim1 10s;', + '#parent::before': 'animation: anim1 10s;', + '#child::after': 'animation: anim1 10s;', + '#child::before': 'animation: anim1 10s;' }); + const parent = addDiv(t, { 'id': 'parent' }); + parent.style.animation = 'anim1 100s'; + const child = addDiv(t, { 'id': 'child' }); + child.style.animation = 'anim1 100s'; + parent.appendChild(child); + + const animations = parent.getAnimations({ subtree: false }); + assert_equals(animations.length, 1, + 'Should find only the element even if it has a child'); + assert_equals(animations[0].effect.target, parent, + 'Effect target should be the element'); +}, '{ subtree: false } on an element with a child returns only the element\'s' + + ' animations'); + +test(t => { + addStyle(t, { '#parent::after': 'animation: anim1 10s;', + '#parent::before': 'animation: anim1 10s;', + '#child::after': 'animation: anim1 10s;', + '#child::before': 'animation: anim1 10s;' }); + const parent = addDiv(t, { 'id': 'parent' }); + const child = addDiv(t, { 'id': 'child' }); + parent.style.animation = 'anim1 100s'; + child.style.animation = 'anim1 100s'; + parent.appendChild(child); + + const animations = parent.getAnimations({ subtree: true }); + assert_equals(animations.length, 6, + 'Should find all elements, pseudo-elements that parent has'); + + assert_equals(animations[0].effect.target, parent, + 'The animation targeting the parent element ' + + 'should be returned first'); + assert_equals(animations[0].effect.pseudoElement, null, + 'The animation targeting the parent element ' + + 'should be returned first'); + assert_equals(animations[1].effect.pseudoElement, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned second'); + assert_equals(animations[1].effect.target, parent, + 'This ::before element should be child of parent element'); + assert_equals(animations[2].effect.pseudoElement, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned third'); + assert_equals(animations[2].effect.target, parent, + 'This ::after element should be child of parent element'); + + assert_equals(animations[3].effect.target, child, + 'The animation targeting the child element ' + + 'should be returned fourth'); + assert_equals(animations[4].effect.pseudoElement, '::before', + 'The animation targeting the ::before pseudo-element ' + + 'should be returned fifth'); + assert_equals(animations[4].effect.target, child, + 'This ::before element should be child of child element'); + assert_equals(animations[5].effect.pseudoElement, '::after', + 'The animation targeting the ::after pesudo-element ' + + 'should be returned last'); + assert_equals(animations[5].effect.target, child, + 'This ::after element should be child of child element'); +}, '{ subtree: true } on an element with a child returns animations from the' + + ' element, its pseudo-elements, its child and its child pseudo-elements'); + +test(t => { + const parent = addDiv(t, { 'id': 'parent' }); + const child1 = addDiv(t, { 'id': 'child1' }); + const grandchild1 = addDiv(t, { 'id': 'grandchild1' }); + const grandchild2 = addDiv(t, { 'id': 'grandchild2' }); + const child2 = addDiv(t, { 'id': 'child2' }); + + parent.style.animation = 'anim1 100s'; + child1.style.animation = 'anim1 100s'; + grandchild1.style.animation = 'anim1 100s'; + grandchild2.style.animation = 'anim1 100s'; + child2.style.animation = 'anim1 100s'; + + parent.appendChild(child1); + child1.appendChild(grandchild1); + child1.appendChild(grandchild2); + parent.appendChild(child2); + + const animations = parent.getAnimations({ subtree: true }); + assert_equals( + parent.getAnimations({ subtree: true }).length, 5, + 'Should find all descendants of the element'); + + assert_equals(animations[0].effect.target, parent, + 'The animation targeting the parent element ' + + 'should be returned first'); + + assert_equals(animations[1].effect.target, child1, + 'The animation targeting the child1 element ' + + 'should be returned second'); + + assert_equals(animations[2].effect.target, grandchild1, + 'The animation targeting the grandchild1 element ' + + 'should be returned third'); + + assert_equals(animations[3].effect.target, grandchild2, + 'The animation targeting the grandchild2 element ' + + 'should be returned fourth'); + + assert_equals(animations[4].effect.target, child2, + 'The animation targeting the child2 element ' + + 'should be returned last'); + +}, '{ subtree: true } on an element with many descendants returns animations' + + ' from all the descendants'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html b/testing/web-platform/tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html new file mode 100644 index 0000000000..a716745c84 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html @@ -0,0 +1,858 @@ +<!doctype html> +<meta charset=utf-8> +<title>KeyframeEffect.getKeyframes() for CSS animations</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim-empty { } + +@keyframes anim-empty-frames { + from { } + to { } +} + +@keyframes anim-only-timing { + from { animation-timing-function: linear; } + to { } +} + +@keyframes anim-only-non-animatable { + from { animation-duration: 3s; } + to { animation-duration: 5s; } +} + +@keyframes anim-simple { + from { color: rgb(0, 0, 0); } + to { color: rgb(255, 255, 255); } +} + +@keyframes anim-simple-three { + from { color: rgb(0, 0, 0); } + 50% { color: rgb(0, 0, 255); } + to { color: rgb(255, 255, 255); } +} + +@keyframes anim-simple-timing { + from { color: rgb(0, 0, 0); animation-timing-function: linear; } + 50% { color: rgb(0, 0, 255); animation-timing-function: ease-in-out; } + to { color: rgb(255, 255, 255); animation-timing-function: step-end; } +} + +@keyframes anim-simple-timing-some { + from { color: rgb(0, 0, 0); animation-timing-function: linear; } + 50% { color: rgb(0, 0, 255); } + to { color: rgb(255, 255, 255); } +} + +@keyframes anim-simple-composite { + from { color: rgb(0, 0, 0); animation-composition: replace; } + 50% { color: rgb(0, 0, 255); animation-composition: add; } + to { color: rgb(255, 255, 255); animation-composition: accumulate; } +} + +@keyframes anim-simple-composite-some { + from { color: rgb(0, 0, 0); animation-composition: add; } + 50% { color: rgb(0, 0, 255); } + to { color: rgb(255, 255, 255); } +} + +@keyframes anim-simple-shorthand { + from { margin: 8px; } + to { margin: 16px; } +} + +@keyframes anim-omit-to { + from { color: rgb(0, 0, 255); } +} + +@keyframes anim-omit-from { + to { color: rgb(0, 0, 255); } +} + +@keyframes anim-omit-from-to { + 50% { color: rgb(0, 0, 255); } +} + +@keyframes anim-partially-omit-to { + from { margin-top: 50px; + margin-bottom: 100px; } + to { margin-top: 150px !important; /* ignored */ + margin-bottom: 200px; } +} + +@keyframes anim-different-props { + from { color: rgb(0, 0, 0); margin-top: 8px; } + 25% { color: rgb(0, 0, 255); } + 75% { margin-top: 12px; } + to { color: rgb(255, 255, 255); margin-top: 16px } +} + +@keyframes anim-different-props-and-easing { + from { color: rgb(0, 0, 0); margin-top: 8px; animation-timing-function: linear; } + 25% { color: rgb(0, 0, 255); animation-timing-function: step-end; } + 75% { margin-top: 12px; animation-timing-function: ease-in; } + to { color: rgb(255, 255, 255); margin-top: 16px } +} + +@keyframes anim-merge-offset { + from { color: rgb(0, 0, 0); } + to { color: rgb(255, 255, 255); } + from { margin-top: 8px; } + to { margin-top: 16px; } +} + +@keyframes anim-merge-offset-and-easing { + from { color: rgb(0, 0, 0); animation-timing-function: step-end; } + to { color: rgb(255, 255, 255); } + from { margin-top: 8px; animation-timing-function: linear; } + to { margin-top: 16px; } + from { font-size: 16px; animation-timing-function: step-end; } + to { font-size: 32px; } + from { padding-left: 2px; animation-timing-function: linear; } + to { padding-left: 4px; } +} + +@keyframes anim-no-merge-equiv-easing { + from { margin-top: 0px; animation-timing-function: steps(1, end); } + from { margin-right: 0px; animation-timing-function: step-end; } + from { margin-bottom: 0px; animation-timing-function: steps(1); } + 50% { margin-top: 10px; animation-timing-function: step-end; } + 50% { margin-right: 10px; animation-timing-function: step-end; } + 50% { margin-bottom: 10px; animation-timing-function: step-end; } + to { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; } +} + +@keyframes anim-merge-offset-and-composite { + from { color: rgb(0, 0, 0); animation-composition: add; } + to { color: rgb(255, 255, 255); } + from { margin-top: 8px; animation-composition: accumulate; } + to { margin-top: 16px; } + from { font-size: 16px; animation-composition: add; } + to { font-size: 32px; } + from { padding-left: 2px; animation-composition: accumulate; } + to { padding-left: 4px; } +} + +@keyframes anim-merge-offset-easing-and-composite { + from { color: rgb(0, 0, 0); animation-composition: add; } + to { color: rgb(255, 255, 255); } + from { margin-top: 8px; animation-composition: accumulate; } + to { margin-top: 16px; } + from { font-size: 16px; animation-composition: add; animation-timing-function: linear; } + to { font-size: 32px; } + from { padding-left: 2px; animation-composition: accumulate; } + to { padding-left: 4px; } +} + +@keyframes anim-overriding { + from { padding-top: 50px } + 50%, from { padding-top: 30px } /* wins: 0% */ + 75%, 85%, 50% { padding-top: 20px } /* wins: 75%, 50% */ + 100%, 85% { padding-top: 70px } /* wins: 100% */ + 85.1% { padding-top: 60px } /* wins: 85.1% */ + 85% { padding-top: 30px } /* wins: 85% */ +} + +@keyframes anim-filter { + to { filter: blur(5px) sepia(60%) saturate(30%); } +} + +@keyframes anim-filter-drop-shadow { + from { filter: drop-shadow(10px 10px 10px rgb(0, 255, 0)); } + to { filter: drop-shadow(50px 30px 10px rgb(255, 0, 0)); } +} + +@keyframes anim-text-shadow { + to { text-shadow: none; } +} + +@keyframes anim-background-size { + to { background-size: 50%, 6px, contain } +} + +:root { + --var-100px: 100px; + --end-color: rgb(255, 0, 0); +} +@keyframes anim-variables { + to { transform: translate(var(--var-100px), 0) } +} +@keyframes anim-variables-shorthand { + to { margin: var(--var-100px) } +} +@keyframes anim-custom-property-in-keyframe { + to { --end-color: rgb(0, 255, 0); color: var(--end-color) } +} +@keyframes anim-only-custom-property-in-keyframe { + from { transform: translate(100px, 0) } + to { --not-used: 200px } +} + +@keyframes anim-only-timing-function-for-from-and-to { + from, to { animation-timing-function: linear } + 50% { left: 10px } +} + +</style> +<body> +<div id="log"></div> +<script> +"use strict"; + +const getKeyframes = elem => elem.getAnimations()[0].effect.getKeyframes(); + +// animation-timing-function values to test with, where the value +// is exactly the same as its serialization, sorted by the order +// getKeyframes() will group frames with the same easing function +// together (by nsTimingFunction::Compare). +const kTimingFunctionValues = [ + "ease", + "linear", + "ease-in", + "ease-out", + "ease-in-out", + "steps(1, start)", + "steps(2, start)", + "steps(1)", + "steps(2)", + "cubic-bezier(0, 0, 1, 1)", + "cubic-bezier(0, 0.25, 0.75, 1)" +]; + +const kCompositeValues = [ + "replace", + "add", + "accumulate" +]; + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-empty 100s'; + assert_equals(getKeyframes(div).length, 0, + "number of frames with empty @keyframes"); + + div.style.animation = 'anim-empty-frames 100s'; + assert_equals(getKeyframes(div).length, 0, + "number of frames when @keyframes has empty keyframes"); + + div.style.animation = 'anim-only-timing 100s'; + assert_equals(getKeyframes(div).length, 0, + "number of frames when @keyframes only has keyframes with " + + "animation-timing-function"); + + div.style.animation = 'anim-only-non-animatable 100s'; + assert_equals(getKeyframes(div).length, 0, + "number of frames when @keyframes only has frames with " + + "non-animatable properties"); +}, 'KeyframeEffect.getKeyframes() returns no frames for various kinds' + + ' of empty enimations'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-simple 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", + color: "rgb(0, 0, 0)", composite: "auto" }, + { offset: 1, computedOffset: 1, easing: "ease", + color: "rgb(255, 255, 255)", composite: "auto" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple' + + ' animation'); + +test(t => { + for (const easing of kTimingFunctionValues) { + const div = addDiv(t); + + div.style.animation = 'anim-simple-three 100s ' + easing; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + + for (let i = 0; i < frames.length; i++) { + assert_equals(frames[i].easing, easing, + "value for 'easing' on ComputedKeyframe #" + i); + } + } +}, 'KeyframeEffect.getKeyframes() returns frames with expected easing' + + ' values, when the easing comes from animation-timing-function on the' + + ' element'); + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-simple-timing 100s'; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + assert_equals(frames[0].easing, "linear", + "value of 'easing' on ComputedKeyframe #0"); + assert_equals(frames[1].easing, "ease-in-out", + "value of 'easing' on ComputedKeyframe #1"); + assert_equals(frames[2].easing, "steps(1)", + "value of 'easing' on ComputedKeyframe #2"); +}, 'KeyframeEffect.getKeyframes() returns frames with expected easing' + + ' values, when the easing is specified on each keyframe'); + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-simple-timing-some 100s step-start'; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + assert_equals(frames[0].easing, "linear", + "value of 'easing' on ComputedKeyframe #0"); + assert_equals(frames[1].easing, "steps(1, start)", + "value of 'easing' on ComputedKeyframe #1"); + assert_equals(frames[2].easing, "steps(1, start)", + "value of 'easing' on ComputedKeyframe #2"); +}, 'KeyframeEffect.getKeyframes() returns frames with expected easing' + + ' values, when the easing is specified on some keyframes'); + +test(t => { + for (const composite of kCompositeValues) { + const div = addDiv(t); + + div.style.animation = 'anim-simple-three 100s'; + div.style.animationComposition = composite; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + + for (let i = 0; i < frames.length; i++) { + assert_equals(frames[i].composite, "auto", + "value for 'composite' on ComputedKeyframe #" + i); + } + } +}, 'KeyframeEffect.getKeyframes() returns frames with expected composite' + + ' values, when the composite is set on the effect using animation-composition on the' + + ' element'); + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-simple-composite 100s'; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + assert_equals(frames[0].composite, "replace", + "value of 'composite' on ComputedKeyframe #0"); + assert_equals(frames[1].composite, "add", + "value of 'composite' on ComputedKeyframe #1"); + assert_equals(frames[2].composite, "accumulate", + "value of 'composite' on ComputedKeyframe #2"); +}, 'KeyframeEffect.getKeyframes() returns frames with expected composite' + + ' values, when the composite is specified on each keyframe'); + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-simple-composite-some 100s'; + div.style.animationComposition = 'accumulate'; + const frames = getKeyframes(div); + + assert_equals(frames.length, 3, "number of frames"); + assert_equals(frames[0].composite, "add", + "value of 'composite' on ComputedKeyframe #0"); + assert_equals(frames[1].composite, "auto", + "value of 'composite' on ComputedKeyframe #1"); + assert_equals(frames[2].composite, "auto", + "value of 'composite' on ComputedKeyframe #2"); +}, 'KeyframeEffect.getKeyframes() returns frames with expected composite' + + ' values, when the composite is specified on some keyframes'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-simple-shorthand 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + marginBottom: "8px", marginLeft: "8px", + marginRight: "8px", marginTop: "8px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + marginBottom: "16px", marginLeft: "16px", + marginRight: "16px", marginTop: "16px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple' + + ' animation that specifies a single shorthand property'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-omit-to 100s'; + div.style.color = 'rgb(255, 255, 255)'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + color: "rgb(0, 0, 255)" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with a 0% keyframe and no 100% keyframe'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-omit-from 100s'; + div.style.color = 'rgb(255, 255, 255)'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(0, 0, 255)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with a 100% keyframe and no 0% keyframe'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-omit-from-to 100s'; + div.style.color = 'rgb(255, 255, 255)'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)" }, + { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", + color: "rgb(0, 0, 255)" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with no 0% or 100% keyframe but with a 50% keyframe'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-partially-omit-to 100s'; + div.style.marginTop = '250px'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + marginTop: '50px', marginBottom: '100px' }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + marginTop: '250px', marginBottom: '200px' }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with a partially complete 100% keyframe (because the ' + + '!important rule is ignored)'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-different-props 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + color: "rgb(0, 0, 0)", marginTop: "8px" }, + { offset: 0.25, computedOffset: 0.25, easing: "ease", composite: "auto", + color: "rgb(0, 0, 255)" }, + { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: "auto", + marginTop: "12px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", marginTop: "16px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with different properties on different keyframes, all ' + + 'with the same easing function'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-different-props-and-easing 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "linear", composite: "auto", + color: "rgb(0, 0, 0)", marginTop: "8px" }, + { offset: 0.25, computedOffset: 0.25, easing: "steps(1)", composite: "auto", + color: "rgb(0, 0, 255)" }, + { offset: 0.75, computedOffset: 0.75, easing: "ease-in", composite: "auto", + marginTop: "12px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", marginTop: "16px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with different properties on different keyframes, with ' + + 'a different easing function on each'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-merge-offset 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + color: "rgb(0, 0, 0)", marginTop: "8px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", marginTop: "16px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with multiple keyframes for the same time, and all with ' + + 'the same easing function'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-merge-offset-and-easing 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto", + color: "rgb(0, 0, 0)", fontSize: "16px" }, + { offset: 0, computedOffset: 0, easing: "linear", composite: "auto", + marginTop: "8px", paddingLeft: "2px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", + paddingLeft: "4px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with multiple keyframes for the same time and with ' + + 'different easing functions'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-no-merge-equiv-easing 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto", + marginTop: "0px", marginRight: "0px", marginBottom: "0px" }, + { offset: 0.5, computedOffset: 0.5, easing: "steps(1)", composite: "auto", + marginTop: "10px", marginRight: "10px", marginBottom: "10px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + marginTop: "20px", marginRight: "20px", marginBottom: "20px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with multiple keyframes for the same time and with ' + + 'different but equivalent easing functions'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-merge-offset-and-composite 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "add", + color: "rgb(0, 0, 0)", fontSize: "16px" }, + { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate", + marginTop: "8px", paddingLeft: "2px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", + paddingLeft: "4px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with multiple keyframes for the same time and with ' + + 'different composite operations'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-merge-offset-easing-and-composite 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "add", + color: "rgb(0, 0, 0)" }, + { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate", + marginTop: "8px", paddingLeft: "2px" }, + { offset: 0, computedOffset: 0, easing: "linear", composite: "add", + fontSize: "16px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", + paddingLeft: "4px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + + 'animation with multiple keyframes for the same time and with ' + + 'different easing functions and composite operations'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-overriding 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + paddingTop: "30px" }, + { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", + paddingTop: "20px" }, + { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: "auto", + paddingTop: "20px" }, + { offset: 0.85, computedOffset: 0.85, easing: "ease", composite: "auto", + paddingTop: "30px" }, + { offset: 0.851, computedOffset: 0.851, easing: "ease", composite: "auto", + paddingTop: "60px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + paddingTop: "70px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected frames for ' + + 'overlapping keyframes'); + +// Gecko-specific test case: We are specifically concerned here that the +// computed value for filter, "none", is correctly represented. + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-filter 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + filter: "none" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + filter: "blur(5px) sepia(60%) saturate(30%)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with filter properties and missing keyframes'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-filter-drop-shadow 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + filter: "drop-shadow(rgb(0, 255, 0) 10px 10px 10px)" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + filter: "drop-shadow(rgb(255, 0, 0) 50px 30px 10px)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animation with drop-shadow of filter property'); + +// Gecko-specific test case: We are specifically concerned here that the +// computed value for text-shadow and a "none" specified on a keyframe +// are correctly represented. + +test(t => { + const div = addDiv(t); + div.style.textShadow = '1px 1px 2px rgb(0, 0, 0), ' + + '0 0 16px rgb(0, 0, 255), ' + + '0 0 3.2px rgb(0, 0, 255)'; + div.style.animation = 'anim-text-shadow 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + textShadow: "rgb(0, 0, 0) 1px 1px 2px," + + " rgb(0, 0, 255) 0px 0px 16px," + + " rgb(0, 0, 255) 0px 0px 3.2px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + textShadow: "none" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with text-shadow properties and missing keyframes'); + +// Gecko-specific test case: We are specifically concerned here that the +// initial value for background-size and the specified list are correctly +// represented. + +test(t => { + const div = addDiv(t); + + div.style.animation = 'anim-background-size 100s'; + let frames = getKeyframes(div); + + assert_equals(frames.length, 2, "number of frames"); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + backgroundSize: "auto" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + backgroundSize: "50% auto, 6px auto, contain" }, + ]; + + for (let i = 0; i < frames.length; i++) { + assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i); + } + + // Test inheriting a background-size value + + expected[0].backgroundSize = div.style.backgroundSize = + "30px auto, 40% auto, auto"; + frames = getKeyframes(div); + + for (let i = 0; i < frames.length; i++) { + assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i + + " after updating current style"); + } +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with background-size properties and missing keyframes'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-variables 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + transform: "none" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + transform: "translate(100px)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with CSS variables as keyframe values'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-variables-shorthand 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + marginBottom: "0px", + marginLeft: "0px", + marginRight: "0px", + marginTop: "0px" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + marginBottom: "100px", + marginLeft: "100px", + marginRight: "100px", + marginTop: "100px" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with CSS variables as keyframe values in a shorthand property'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-custom-property-in-keyframe 100s steps(2, start)'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "steps(2, start)", composite: "auto", + color: "rgb(0, 0, 0)" }, + { offset: 1, computedOffset: 1, easing: "steps(2, start)", composite: "auto", + color: "rgb(0, 255, 0)" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with a CSS variable which is overriden by the value in keyframe'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-only-custom-property-in-keyframe 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", + transform: "translate(100px)" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", + transform: "none" }, + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with only custom property in a keyframe'); + +test(t => { + const div = addDiv(t); + + // Add custom @keyframes rule + const stylesheet = document.styleSheets[0]; + const keyframes = '@keyframes anim-custom { to { left: 100px } }'; + const ruleIndex = stylesheet.insertRule(keyframes, 0); + const keyframesRule = stylesheet.cssRules[ruleIndex]; + + t.add_cleanup(function() { + stylesheet.deleteRule(ruleIndex); + }); + + div.style.animation = 'anim-custom 100s'; + + // Sanity check the initial result + let frames = getKeyframes(div); + assert_frames_equal( + frames[frames.length - 1], + { + offset: 1, + computedOffset: 1, + easing: 'ease', + composite: 'auto', + left: '100px', + }, + 'Keyframes reflect the initial @keyframes rule' + ); + + // Update the @keyframes rule + keyframesRule.deleteRule(0); + keyframesRule.appendRule('to { left: 200px }'); + + // Check the result from getKeyframes() is updated + frames = getKeyframes(div); + assert_frames_equal( + frames[frames.length - 1], + { + offset: 1, + computedOffset: 1, + easing: 'ease', + composite: 'auto', + left: '200px', + }, + 'Keyframes reflects the updated @keyframes rule' + ); +}, 'KeyframeEffect.getKeyframes() reflects changes to @keyframes rules'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-only-timing-function-for-from-and-to 100s'; + + const frames = getKeyframes(div); + + const expected = [ + { offset: 0, computedOffset: 0, easing: "linear", composite: "auto" }, + { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", left: "auto" }, + { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", left: "10px" }, + { offset: 1, computedOffset: 1, easing: "linear", composite: "auto" }, + { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", left: "auto" } + ]; + assert_frame_lists_equal(frames, expected); +}, 'KeyframeEffect.getKeyframes() returns expected values for ' + + 'animations with implicit values and a non-default timing' + + 'function specified for 0% and 100%'); + +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/KeyframeEffect-setKeyframes.tentative.html b/testing/web-platform/tests/css/css-animations/KeyframeEffect-setKeyframes.tentative.html new file mode 100644 index 0000000000..7d8f845413 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-setKeyframes.tentative.html @@ -0,0 +1,122 @@ +<!doctype html> +<meta charset=utf-8> +<title>KeyframeEffect.setKeyframes() for CSS animations</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim-simple { + from { left: 0px } + to { left: 100px } +} +</style> +<body> +<div id="log"></div> +<script> +"use strict"; + +// Note that the sanity check that getKeyframes() normally DOES return the +// updated keyframes is contained in KeyframeEffect-getKeyframes.html. +test(t => { + const div = addDiv(t); + + // Add custom @keyframes rule + const stylesheet = document.styleSheets[0]; + const keyframes = '@keyframes anim-custom { to { left: 100px } }'; + const ruleIndex = stylesheet.insertRule(keyframes, 0); + const keyframesRule = stylesheet.cssRules[ruleIndex]; + + t.add_cleanup(function() { + stylesheet.deleteRule(ruleIndex); + }); + + div.style.animation = 'anim-custom 100s'; + + // Update the keyframes via the API + const animation = div.getAnimations()[0]; + animation.effect.setKeyframes({ left: '200px' }); + + // Then update them via style + keyframesRule.deleteRule(0); + keyframesRule.appendRule('to { left: 300px }'); + + // The result should be the keyframes as set by the API, not via style. + const frames = animation.effect.getKeyframes(); + assert_frames_equal( + frames[frames.length - 1], + { + offset: null, + computedOffset: 1, + easing: 'linear', + composite: 'auto', + left: '200px', + }, + 'Keyframes reflect the value set via setKeyframes' + ); +}, 'KeyframeEffect.setKeyframes() causes subsequent changes to @keyframes' + + ' rules to be ignored'); + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim-simple 100s'; + + const animation = div.getAnimations()[0]; + assert_equals(animation.effect.getKeyframes()[0].easing, 'ease'); + + animation.effect.setKeyframes({ left: ['200px', '300px'] }); + assert_equals(animation.effect.getKeyframes()[0].easing, 'linear'); + + div.style.animationTimingFunction = 'ease-in'; + getComputedStyle(div).animationTimingFunction; + + assert_equals( + animation.effect.getKeyframes()[0].easing, + 'linear', + 'Easing should be the easing set by the API' + ); +}, 'KeyframeEffect.setKeyframes() causes subsequent changes to' + + ' animation-timing-function to be ignored'); + +test(t => { + const div = addDiv(t); + + const stylesheet = document.styleSheets[0]; + const keyframes = '@keyframes anim-custom { to { left: 100px } }'; + const ruleIndex = stylesheet.insertRule(keyframes, 0); + const keyframesRule = stylesheet.cssRules[ruleIndex]; + + t.add_cleanup(function() { + stylesheet.deleteRule(ruleIndex); + }); + + div.style.animation = 'anim-custom 100s'; + + // Try updating in a way that throws an error + const animation = div.getAnimations()[0]; + assert_throws_js(TypeError, () => { + animation.effect.setKeyframes({ left: '200px', offset: 'yer' }); + }); + + keyframesRule.deleteRule(0); + keyframesRule.appendRule('to { left: 300px }'); + + // The result should be the keyframes as set via style. + const frames = animation.effect.getKeyframes(); + assert_frames_equal( + frames[frames.length - 1], + { + offset: 1, + computedOffset: 1, + easing: 'ease', + composite: 'auto', + left: '300px', + }, + 'Keyframes reflect the value set via style' + ); +}, 'KeyframeEffect.setKeyframes() should NOT cause subsequent changes to' + + ' @keyframes rules to be ignored if it threw'); + +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html b/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html new file mode 100644 index 0000000000..4991762099 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html @@ -0,0 +1,63 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSSAnimation.effect.target</title> +<!-- TODO: Add a more specific link for this once it is specified. --> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { } +::before { + content: '' +} +::after { + content: '' +} +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + div.style.animation = 'anim 100s'; + const animation = div.getAnimations()[0]; + assert_equals(animation.effect.target, div, + 'Animation.target is the animatable div'); +}, 'Returned CSS animations have the correct effect target'); + +test(t => { + addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' }); + const div = addDiv(t, { class: 'after' }); + const anims = document.getAnimations(); + assert_equals(anims.length, 2, + 'Got animations running on ::after pseudo element'); + assert_equals(anims[0].effect.target, anims[1].effect.target, + 'Both animations return the same target object'); +}, 'effect.target should return the same CSSPseudoElement object each time'); + +test(t => { + addStyle(t, { '.after::after': 'animation: anim 10s;' }); + const div = addDiv(t, { class: 'after' }); + const pseudoTarget = document.getAnimations()[0].effect.target; + const effect = new KeyframeEffect(pseudoTarget, + { background: ["blue", "red"] }, + 3 * MS_PER_SEC); + const newAnim = new Animation(effect, document.timeline); + newAnim.play(); + const anims = document.getAnimations(); + assert_equals(anims.length, 2, + 'Got animations running on ::after pseudo element'); + assert_not_equals(anims[0], newAnim, + 'The scriped-generated animation appears last'); + assert_equals(newAnim.effect.target, pseudoTarget, + 'The effect.target of the scripted-generated animation is ' + + 'the same as the one from the argument of ' + + 'KeyframeEffect constructor'); + assert_equals(anims[0].effect.target, newAnim.effect.target, + 'Both animations return the same target object'); +}, 'effect.target from the script-generated animation should return the same ' + + 'CSSPseudoElement object as that from the CSS generated animation'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/META.yml b/testing/web-platform/tests/css/css-animations/META.yml new file mode 100644 index 0000000000..3ef1997000 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/META.yml @@ -0,0 +1,7 @@ +spec: https://drafts.csswg.org/css-animations/ +suggested_reviewers: + - plinss + - grorg + - dbaron + - tabatkins + - birtles diff --git a/testing/web-platform/tests/css/css-animations/animation-base-response-001.html b/testing/web-platform/tests/css/css-animations/animation-base-response-001.html new file mode 100644 index 0000000000..c6731e1798 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-base-response-001.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<title>Test that non-animated style is responsive to animated properties</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @keyframes font_size_animation { + from { font-size: 10px; } + to { font-size: 20px; } + } + @keyframes var_animation { + from { --x: 10px; } + to { --x: 20px; } + } + #targets > div { + animation-duration: 1000s; + animation-delay: -500s; + animation-timing-function: steps(2, end); + } + + #target1 { + animation-name: font_size_animation; + font-size: 1px; + width: 1em; + } + #ref1 { + width: 15px; + } + + #target2 { + animation-name: font_size_animation; + font-size: 1px; + width: 1ex; + } + #ref2 { + font-size: 15px; + width: 1ex; + } + + #target3 { + animation-name: var_animation; + --x: 0px; + width: var(--x); + } + #ref3 { + width: 20px; + } +</style> +<div id="targets"> + <div id="target1"></div> + <div id="target2"></div> + <div id="target3"></div> +</div> +<div id="refs"> + <div id="ref1"></div> + <div id="ref2"></div> + <div id="ref3"></div> +</div> +<script> + +// Test that the computed value of the given property is equal on +// 'target' and 'ref'. +function test_ref(target, ref, property, description) { + test(() => { + let actual = getComputedStyle(target).getPropertyValue(property); + let expected = getComputedStyle(ref).getPropertyValue(property); + assert_equals(actual, expected); + }, description); +} + +test_ref(target1, ref1, 'width', 'em units respond to font-size animation'); +test_ref(target2, ref2, 'width', 'ex units respond to font-size animation'); +test_ref(target3, ref3, 'width', 'var() references respond to custom property animation'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-base-response-002.html b/testing/web-platform/tests/css/css-animations/animation-base-response-002.html new file mode 100644 index 0000000000..e9ea964f75 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-base-response-002.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<title>Test that rem units are responsive to animated font-size on root</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @keyframes font_size_animation { + from { font-size: 10px; } + to { font-size: 20px; } + } + :root { + font-size: 1px; + animation: font_size_animation steps(2, end) 1000s -500s; + } + + #target1 { + width: 1rem; + } +</style> +<div id="target1"></div> +<script> +test(() => { + assert_equals(getComputedStyle(target1).getPropertyValue('width'), '15px'); +}, 'Animated font-size on root affects rem units'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-base-response-003.html b/testing/web-platform/tests/css/css-animations/animation-base-response-003.html new file mode 100644 index 0000000000..d6e6da7309 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-base-response-003.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<title>Tests that identical elements in the base style responds to font-size animation</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @keyframes font_size_animation { + from { font-size: 10px; } + to { font-size: 20px; } + } + div { + font-size: 1px; + min-width: 1em; + animation: font_size_animation steps(2, end) 1000s -500s; + } +</style> +<div></div> +<div></div> +<div></div> +<script> +test(() => { + let divs = document.querySelectorAll('div'); + for (let div of divs) + assert_equals(getComputedStyle(div).getPropertyValue('min-width'), '15px'); +}, 'Identical elements are all responsive to font-size animation'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-base-response-004.html b/testing/web-platform/tests/css/css-animations/animation-base-response-004.html new file mode 100644 index 0000000000..5638a572e9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-base-response-004.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title>Tests that base responds to font-affecting properties appearing via setKeyframes</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #target1 { + font-size: 10px; + height: 1em; + } +</style> +<div id=target1></div> +<script> + test(function() { + getComputedStyle(target1).height; + + let animation = target1.animate([ + { height: '50px' }, + { height: '100px' }, + ], { + duration: 1000000, + delay: -500000, + easing: 'steps(2, end)' + }); + + assert_equals(getComputedStyle(target1).height, '75px'); + + animation.effect.setKeyframes([ + { fontSize: '10px' }, + { fontSize: '20px' }, + ]); + + assert_equals(getComputedStyle(target1).height, '15px'); + }, 'Base is responsive to font-affecting appearing via setKeyframes'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-before-initial-box-construction-001.html b/testing/web-platform/tests/css/css-animations/animation-before-initial-box-construction-001.html new file mode 100644 index 0000000000..558c5ea186 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-before-initial-box-construction-001.html @@ -0,0 +1,53 @@ +<!doctype html> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="mailto:https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1605610"> +<link rel="help" href="https://drafts.csswg.org/web-animations/#extensions-to-the-element-interface"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +let t = async_test("animations started before initial-containing-block creation properly function"); +let loaded = false; +let finished = false; +let span; + +function check() { + if (!loaded || !finished) + return; + + assert_equals(getComputedStyle(span).opacity, "1", "Animation should be properly finished"); + t.done(); +} + +document.addEventListener("DOMContentLoaded", function() { + span = document.createElement("span"); + span.id = "myspan"; + span.innerHTML = `Some content`; + document.body.appendChild(span); + + let anim = span.animate([ + { + opacity: "0", + }, + { + opacity: "1", + }, + ], { + duration: 300, + }); + + anim.finished.then(t.step_func(function() { + finished = true; + check(); + })); +}); + +window.addEventListener("load", t.step_func(function() { + loaded = true; + check(); +})); +</script> +<!-- It is important that there are no more script after this stylesheet --> +<link rel="stylesheet" href="empty-sheet.css?pipe=trickle(d2)"> +<body> +</body> diff --git a/testing/web-platform/tests/css/css-animations/animation-change-underlying-value-changed-in-flight.html b/testing/web-platform/tests/css/css-animations/animation-change-underlying-value-changed-in-flight.html new file mode 100644 index 0000000000..444ea73785 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-change-underlying-value-changed-in-flight.html @@ -0,0 +1,42 @@ +<!doctype html> +<meta charset=utf-8> +<title>Changing the underlying value of an animated property with implicit keyframes</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +@keyframes implicit-from { + to { margin-left: 100px } +} + +@keyframes implicit-to { + from { margin-left: 100px } +} + +</style> +<div id="log"></div> +<script> +'use strict'; + +const implicit_keyframe_test = (animationName, offset) => { + test(t => { + const div = addDiv(t); + + // Set up animation to be paused and be at its mid-way point through easing. + div.style.animation = `${animationName} 10s paused steps(2, start)`; + const animation = div.getAnimations()[0]; + + assert_equals(getComputedStyle(div).marginLeft, "50px", "Computed style before changing the underlying style"); + + // Change the underlying value. + div.style.marginLeft = "200px"; + assert_equals(getComputedStyle(div).marginLeft, "150px", "Computed style after changing the underlying style"); + }, `Changing the underlying value of an animated property with an implicit ${offset}% keyframe`); +}; + +implicit_keyframe_test("implicit-from", "0"); +implicit_keyframe_test("implicit-to", "100"); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-common-ref.html b/testing/web-platform/tests/css/css-animations/animation-common-ref.html new file mode 100644 index 0000000000..ddc7da67dd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-common-ref.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<link rel="author" title="Brian Birtles" href="mailto:bbirtles@mozilla.com"> +<style> +div { + width: 100px; + height: 100px; + background-color: green; +} +</style> +<div></div> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animation-css-variable-in-keyframe-adjusted.html b/testing/web-platform/tests/css/css-animations/animation-css-variable-in-keyframe-adjusted.html new file mode 100644 index 0000000000..f8e760c8ff --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-css-variable-in-keyframe-adjusted.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations: adjust value of CSS variable used in keyframes</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +@keyframes anim { + from { margin-left: var(--margin-left) } + to { margin-left: calc(var(--margin-left) * 2) } +} + +</style> +<div id="log"></div> +<script> + +test(t => { + const div = addDiv(t); + div.style.setProperty('--margin-left', '100px'); + + div.style.animation = 'anim 1s linear'; + const animation = div.getAnimations()[0]; + animation.currentTime = 500; + + assert_equals( + getComputedStyle(div).marginLeft, + '150px', + 'Animated value before updating variable' + ); + + div.style.setProperty('--margin-left', '200px'); + + assert_equals( + getComputedStyle(div).marginLeft, + '300px', + 'Animated value after updating variable' + ); +}, 'Animations reflect changes to variables on element'); + +test(t => { + const parentDiv = addDiv(t); + const div = addDiv(t); + parentDiv.appendChild(div); + parentDiv.style.setProperty('--margin-left', '100px'); + + div.style.animation = 'anim 1s linear'; + const animation = div.getAnimations()[0]; + animation.currentTime = 500; + + assert_equals( + getComputedStyle(div).marginLeft, + '150px', + 'Animated value before updating variable' + ); + + parentDiv.style.setProperty('--margin-left', '200px'); + + assert_equals( + getComputedStyle(div).marginLeft, + '300px', + 'Animated value after updating variable' + ); +}, 'Animations reflect changes to variables on parent element'); + + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-001-manual.html new file mode 100644 index 0000000000..af64b9e722 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-001-manual.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - negative value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-18 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-delay is set to a negative time offset, + animation will execute as soon as it is applied + but act as if the animation had started the specified + time in the past."> +<style> + div { + animation-timing-function: linear; + + height: 100px; + width: 100px; + position: relative; + } + + #test-negative-delay { + animation-name: test-negative-delay; + animation-duration: 10s; + animation-delay: -5s; + + background-color: blue; + } + + #ref-no-delay { + animation-name: ref-no-delay; + animation-duration: 5s; + + background-color: yellow; + } + + @keyframes test-negative-delay { + from { + left: 150px; + } + to { + left: 0px; + } + } + + @keyframes ref-no-delay { + from { + left: 75px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there are a filled blue square with 'Filler Text' + and a filled yellow square with 'Filler Text', and if the two squares + start moving together from right to left as soon as the page loads. + </p> + <div id="test-negative-delay">Filler Text</div> + <div id="ref-no-delay">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-002-manual.html new file mode 100644 index 0000000000..32b3ae48cf --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-002-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - positive value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-18 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-delay is set to a positive time offset, + animation will delay execution by the specified offset value."> +<style> + div { + animation-name: sample; + animation-duration: 5s; + animation-delay: 5s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left after about 5 seconds + from the time the page is loaded. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-003-manual.html new file mode 100644 index 0000000000..7b90c04bbc --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-003-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - 0s</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-18 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-delay is set to 0s (zero seconds), + animation will execute as soon as it is applied."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-delay: 0s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left as soon as the page loads. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-004-manual.html new file mode 100644 index 0000000000..9eaab1448c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-004-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-18 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-delay applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 5s; + animation-delay: 5s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left after about 5 seconds + from the time the page is loaded. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-005-manual.html new file mode 100644 index 0000000000..7c41bdf677 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-005-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-18 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-delay applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 5s; + animation-delay: 5s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left after about 5 seconds + from the time the page is loaded. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-006-manual.html new file mode 100644 index 0000000000..1be2e380e9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-006-manual.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - initial keyword</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#values"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-delay property accepts 'initial' keyword."> +<style> + #contatiner { + animation-delay: 5s; + } + + #test { + animation-name: sample; + animation-duration: 10s; + animation-delay: initial; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left upon page load and lasts for a span of 10 seconds. + </p> + <div id="container"> + <div id="test">Filler Text</div> + </div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-007-manual.html b/testing/web-platform/tests/css/css-animations/animation-delay-007-manual.html new file mode 100644 index 0000000000..a5549d5ff5 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-007-manual.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - inherit keyword</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#values"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-delay property accepts 'inherit' keyword."> +<style> + #container { + animation-delay: 5s; + } + + #test { + animation-name: sample; + animation-duration: 5s; + animation-delay: inherit; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left for about 5 seconds, and then moves + from right to left and lasts for a span of 5 seconds. + </p> + <div id="container"> + <div id="test">Filler Text</div> + </div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-008.html b/testing/web-platform/tests/css/css-animations/animation-delay-008.html new file mode 100644 index 0000000000..52883fa72f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-008.html @@ -0,0 +1,37 @@ +<!doctype html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - liveness</title> +<link rel="author" title="Brian Birtles" href="mailto:bbirtles@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animations"> +<meta name="assert" content="Check that changes to animation-delay on a running +animation are reflected in output"> +<meta name="flags" content="dom"> +<link rel="match" href="animation-common-ref.html"> +<style> +@keyframes two-step { + from { background-color: red } + 50% { background-color: green } + to { background-color: green } +} +div { + width: 100px; + height: 100px; + background-color: orange; +} +</style> +<div></div> +<script> +// Set up animation with no delay +var div = document.querySelector('div'); +div.style.animation = 'two-step 200s steps(1)'; +window.getComputedStyle(div).animation; + +// Wait until animation has started and change delay +window.requestAnimationFrame(function() { + // Fast-forward to mid-point + div.style.animationDelay = '-100s'; + document.documentElement.removeAttribute('class'); +}); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-009.html b/testing/web-platform/tests/css/css-animations/animation-delay-009.html new file mode 100644 index 0000000000..6d25b8b1bc --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-009.html @@ -0,0 +1,46 @@ +<!doctype html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - liveness with animationend</title> +<link rel="author" title="Brian Birtles" href="mailto:bbirtles@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animations"> +<meta name="assert" content="Check that shortening the animation-delay triggers +an animationend event"> +<meta name="flags" content="dom"> +<link rel="match" href="animation-common-ref.html"> +<style> +@keyframes all-red { + from { background-color: red } + to { background-color: red } +} +div { + width: 100px; + height: 100px; + background-color: orange; +} +</style> +<div></div> +<script> +// Set up animation with no delay +var div = document.querySelector('div'); +div.style.animation = 'all-red 1000s'; +window.getComputedStyle(div).animation; + +// Set up an animationend event handler to change the background color +div.addEventListener('animationend', function() { + div.style.animation = 'none'; + div.style.backgroundColor = 'green'; +}); + +// Wait until animation has started and change delay +window.requestAnimationFrame(function() { + // Fast-forward to end + div.style.animationDelay = '-1000s'; + + // Wait a frame to allow the event handler to run + window.requestAnimationFrame(function() { + document.documentElement.removeAttribute('class'); + }); +}); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-010.html b/testing/web-platform/tests/css/css-animations/animation-delay-010.html new file mode 100644 index 0000000000..d18064dd21 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-010.html @@ -0,0 +1,46 @@ +<!doctype html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-delay - liveness with + animationstart</title> +<link rel="author" title="Brian Birtles" href="mailto:bbirtles@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animations"> +<meta name="assert" content="Check that extending the animation-delay triggers +an animationstart event"> +<meta name="flags" content="dom"> +<link rel="match" href="animation-common-ref.html"> +<style> +@keyframes all-orange { + from { background-color: orange } + to { background-color: orange } +} +div { + width: 100px; + height: 100px; + background-color: red; +} +</style> +<div></div> +<script> +// Set up animation with a negative delay such that it finishes very soon +var div = document.querySelector('div'); +div.style.animation = 'all-orange 1000s -999.99s'; + +// Wait for the animation to finish +div.addEventListener('animationend', function() { + // Set up an animationstart event handler to change the background color + div.addEventListener('animationstart', function() { + div.style.animation = 'none'; + div.style.backgroundColor = 'green'; + }); + + // Then extend the delay so that the animation restarts + div.style.animationDelay = '0s'; + + // Wait a frame to allow the event handler to run + window.requestAnimationFrame(function() { + document.documentElement.removeAttribute('class'); + }); +}); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animation-delay-011.html b/testing/web-platform/tests/css/css-animations/animation-delay-011.html new file mode 100644 index 0000000000..415a574755 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-delay-011.html @@ -0,0 +1,24 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Animations Test: inherited animation-delay with mismatched animation-name length</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animations"> +<link rel="match" href="animation-common-ref.html"> +<style> +div:after { + content: ''; + display: block; + width: 100px; + height: 100px; + background: red; + animation: doesntmatter 50s linear infinite, + bg 100s step-end infinite; + animation-play-state: paused; + animation-delay: inherit; +} + +@keyframes bg { + 50% { background: green; } +} +</style> +<div style="animation-delay: -50s"></div> diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-001-manual.html new file mode 100644 index 0000000000..5e1ebde7b1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-001-manual.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - alternate</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-direction is set to alternate, + animation cycle will iteration that are + odd counts are played in the normal direction, + and the animation cycle iterations that are + even counts are played in a reverse direction."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-direction: alternate; + animation-iteration-count: infinite; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load, + and then moves from left to right. This cycle gets repeated. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-002-manual.html new file mode 100644 index 0000000000..8ed910be30 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-002-manual.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - normal</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-direction is set to normal, + all iterations of animation are played as specified."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-direction: normal; + animation-iteration-count: infinite; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + which starts moving from right to left, then back to right and moves from + right to left again. This cycle gets repeated. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-003-manual.html new file mode 100644 index 0000000000..12a5bd5fa1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-003-manual.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - alternate-reverse</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-direction is set to alternate-reverse, + the animation cycle iterations that are + odd counts are played in the normal direction, + and the animation cycle iterations that are + even counts are played in a reverse direction."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-direction: alternate-reverse; + animation-iteration-count: infinite; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 0px; + } + to { + left: 150px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load, + and then moves from left to right. This cycle gets repeated. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-004-manual.html new file mode 100644 index 0000000000..83c6664d78 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-004-manual.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - reverse</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-direction is set to reverse, + all iterations of the animation are played in the reverse direction + from the way they were specified."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-direction: reverse; + animation-iteration-count: infinite; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + which starts moving from left to right, then back to left again and moves from + left to right. This cycle gets repeated. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-005-manual.html new file mode 100644 index 0000000000..b6ec869fc2 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-005-manual.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-direction applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + animation-direction: alternate; + animation-iteration-count: infinite; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load, + and then moves from left to right. This cycle gets repeated. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-direction-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-direction-006-manual.html new file mode 100644 index 0000000000..c1173409a8 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-direction-006-manual.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-direction - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-03-24 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-direction"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-direction applies to the ::before pseudo element"> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + animation-direction: alternate; + animation-iteration-count: infinite; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load, + and then moves from left to right. This cycle gets repeated. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-display-manual.html b/testing/web-platform/tests/css/css-animations/animation-display-manual.html new file mode 100644 index 0000000000..e62b44dfa1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-display-manual.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation - display</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animations"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that setting 'display' property to 'none' terminates + running animation applied to the element, and updating 'display' + property to a value other than 'none' will start the animation."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which moves from right to left twice. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + + setTimeout(function() { + div[0].style.display = "none"; + }, 5000); + + setTimeout(function() { + div[0].style.display = "block"; + }, 8000); + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-001-manual.html new file mode 100644 index 0000000000..d012b907cb --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-001-manual.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - blank value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration value is set blank, there will be no animation seen."> +<style> + div { + animation-name: sample; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left for about 2 seconds upon page load, + then moves from right to left. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + div[0].style.animationDuration = ""; + setTimeout(setAnimationDuration, 2000); + + function setAnimationDuration() { + div[0].style.animationDuration = "10s"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-002-manual.html new file mode 100644 index 0000000000..d0e7de1018 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-002-manual.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - finite value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration is set a finite time offset, + animation takes the specifies time to complete one cycle."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left upon page load and lasts for a span of 10 seconds. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-003-manual.html new file mode 100644 index 0000000000..72e1070f1f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-003-manual.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - negative value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration is set to a negative value, + it is treated as 0s (zero seconds) and no animation is seen."> +<style> + div { + animation-name: sample; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left for about 2 seconds upon page load, + then moves from right to left. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + div[0].style.animationDuration = "-2s"; + setTimeout(setAnimationDuration, 2000); + + function setAnimationDuration() { + div[0].style.animationDuration = "10s"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-004-manual.html new file mode 100644 index 0000000000..eda2a2c738 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-004-manual.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - 0s</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration is set to 0s (zero seconds), + animation occurs instantaneously, there will be no animation seen."> +<style> + div { + animation-name: sample; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left for about 2 seconds upon page load, + then moves from right to left. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + div[0].style.animationDuration = "0s"; + setTimeout(setAnimationDuration, 2000); + + function setAnimationDuration() { + div[0].style.animationDuration = "10s"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-005-manual.html new file mode 100644 index 0000000000..ea6a331c8e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-005-manual.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - 0s, animation-fill-mode - forwards</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration is set to 0s (zero seconds), + and 'animation-fill-mode' is set to 'forwards', + the last frame of the animation will be displayed."> +<style> + div { + height: 100px; + width: 100px; + position: absolute; + } + + #test { + animation-name: sample; + animation-duration: 0s; + animation-fill-mode: forwards; + + background-color: blue; + } + + #ref { + background-color: red; + left: 150px; + } + + @keyframes sample { + from { + left: 0px; + } + to { + left: 150px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue with 'Filler Text', + and without animation, and if there is no red. + </p> + <div id="ref"></div> + <div id="test">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-006-manual.html new file mode 100644 index 0000000000..2ad017b1fd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-006-manual.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - 0s, animation-fill-mode - both</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-duration is set to 0s (zero seconds), + and 'animation-fill-mode' is set to 'both', + the last frame of the animation will be displayed."> +<style> + div { + height: 100px; + width: 100px; + position: absolute; + } + + #test { + animation-name: sample; + animation-duration: 0s; + animation-fill-mode: both; + + background-color: blue; + } + + #ref { + background-color: red; + left: 150px; + } + + @keyframes sample { + from { + left: 0px; + } + to { + left: 150px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue with 'Filler Text', + and without animation, and if there is no red. + </p> + <div id="ref"></div> + <div id="test">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-007-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-007-manual.html new file mode 100644 index 0000000000..94d71796dc --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-007-manual.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-duration applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left upon page load and lasts for a span of 10 seconds. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-duration-008-manual.html b/testing/web-platform/tests/css/css-animations/animation-duration-008-manual.html new file mode 100644 index 0000000000..b5a8dba02a --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-duration-008-manual.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-duration - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-05 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-duration applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left upon page load and lasts for a span of 10 seconds. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-001-manual.html new file mode 100644 index 0000000000..2176672367 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-001-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - none</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-fill-mode is set to none, + animation has no effect when it is applied but not executing."> +<style> + div { + animation-name: sample; + animation-duration: 5s; + animation-fill-mode: none; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: YELLOW to GREEN. + After the animation is finished, the color goes back to BLUE. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-002-manual.html new file mode 100644 index 0000000000..753d93750a --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-002-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - forwards</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-fill-mode is set to forwards, + animation will apply the property values for the time the amination ended + after the animation ends."> +<style> + div { + animation-name: sample; + animation-duration: 5s; + animation-fill-mode: forwards; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: + YELLOW to GREEN. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-003-manual.html new file mode 100644 index 0000000000..561237d267 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-003-manual.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - backwards</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-fill-mode is set to backwards, + animation-delay is set a positive time offset, + and animation-direction is 'normal' or 'alternate-reverse', + animation will apply the from or 0% keyframe + that will start the first iteration."> +<style> + div { + animation-name: sample; + animation-duration: 5s; + animation-delay: 5s; + animation-fill-mode: backwards; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: YELLOW to GREEN. + After the animation is finished, the color goes back to BLUE. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-004-manual.html new file mode 100644 index 0000000000..77f8f49b70 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-004-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - both</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-delay"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check if animation-fill-mode is set to both, + the effects of both 'forwards' and 'backwards' will apply."> +<style> + div { + animation-name: sample; + animation-duration: 5s; + animation-delay: 5s; + animation-fill-mode: both; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: + YELLOW to GREEN. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-005-manual.html new file mode 100644 index 0000000000..c5e2172b31 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-005-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-fill-mode applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 5s; + animation-fill-mode: forwards; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: + YELLOW to GREEN. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-fill-mode-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-fill-mode-006-manual.html new file mode 100644 index 0000000000..4bf4b0b0ec --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-fill-mode-006-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-fill-mode - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-fill-mode applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 5s; + animation-fill-mode: forwards; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + @keyframes sample { + from { + background-color: yellow; + } + to { + background-color: green; + } + } +</style> +<body> + <p> + Test passes if there is a filled color square with 'Filler Text', + whose color gradually changes in the order: + YELLOW to GREEN. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-important-001.html b/testing/web-platform/tests/css/css-animations/animation-important-001.html new file mode 100644 index 0000000000..6b1fcfd76b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-important-001.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<title>Test !important declarations vs. animation effects</title> +<link rel="help" href="https://drafts.csswg.org/css-cascade/#cascade-origin"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + CSS.registerProperty({ + name: "--length-1", + syntax: "<length>", + initialValue: "0px", + inherits: false + }); + CSS.registerProperty({ + name: "--length-2", + syntax: "<length>", + initialValue: "0px", + inherits: false + }); +</script> +<style> + @keyframes bgcolor_animation { + from { background-color: rgb(10, 10, 10); } + to { background-color: rgb(20, 20, 20); } + } + @keyframes color_and_bg_animation { + from { background-color: rgb(10, 10, 10); color: rgb(10, 10, 10); } + to { background-color: rgb(20, 20, 20); color: rgb(20, 20, 20); } + } + a, div, ::before{ + animation-duration: 1000s; + animation-delay: -500s; + animation-timing-function: steps(2, end); + } + #target1 { + animation-name: bgcolor_animation; + } + #target2 { + background-color: rgb(0, 128, 0) !important; + animation-name: bgcolor_animation; + } + #target3 { + color: rgb(0, 128, 0) !important; + animation-name: color_and_bg_animation; + } + #target4::before { + color: rgb(0, 128, 0) !important; + animation-name: color_and_bg_animation; + content: " "; + } + #target5 { + animation-name: color_and_bg_animation; + } + #target5:visited { + color: rgb(0, 128, 0) !important; + } + #target6 { + background-color: white; + color: white !important; + } + #target7 { + --length-1: 10px; + --length-2: 10px !important; + } +</style> +<div id="target1"></div> +<div id="target2"></div> +<div id="target3"></div> +<div id="target4"></div> +<a href="" id="target5"></a> +<span id="target6"></span> +<span id="target7"></span> +<script> +test(() => { + assert_equals(getComputedStyle(target1).backgroundColor, 'rgb(15, 15, 15)'); +}, 'Interpolated value is observable'); + +test(() => { + assert_equals(getComputedStyle(target2).backgroundColor, 'rgb(0, 128, 0)'); +}, 'Important rules override animations'); + +test(() => { + assert_equals(getComputedStyle(target3).color, 'rgb(0, 128, 0)'); + assert_equals(getComputedStyle(target3).backgroundColor, 'rgb(15, 15, 15)'); +}, 'Non-overriden interpolations are observable'); + +test(() => { + assert_equals(getComputedStyle(target4, '::before').color, 'rgb(0, 128, 0)'); + assert_equals(getComputedStyle(target4, '::before').backgroundColor, 'rgb(15, 15, 15)'); +}, 'Important rules override animations (::before)'); + +test(() => { + assert_equals(getComputedStyle(target5).color, 'rgb(15, 15, 15)'); + assert_equals(getComputedStyle(target5).backgroundColor, 'rgb(15, 15, 15)'); +}, 'Important rules do not override animations on :visited as seen from JS'); + +test(() => { + getComputedStyle(target6).backgroundColor; + + let animation = target6.animate([ + { backgroundColor: 'rgb(10, 10, 10)' }, + { backgroundColor: 'rgb(20, 20, 20)' }, + ], { + duration: 1000000, + delay: -500000, + easing: 'steps(2, end)' + }); + + assert_equals(getComputedStyle(target6).backgroundColor, 'rgb(15, 15, 15)'); + assert_equals(getComputedStyle(target6).color, 'rgb(255, 255, 255)'); + + animation.effect.setKeyframes([ + { color: 'rgb(10, 10, 10)' }, + { color: 'rgb(20, 20, 20)' }, + ]); + + assert_equals(getComputedStyle(target6).backgroundColor, 'rgb(255, 255, 255)'); + assert_equals(getComputedStyle(target6).color, 'rgb(255, 255, 255)'); +}, 'Standard property animations appearing via setKeyframes do not override important declarations'); + +test(() => { + getComputedStyle(target7).getPropertyValue('--length-1'); + + let animation = target7.animate([ + { '--length-1': '100px' }, + { '--length-1': '200px' }, + ], { + duration: 1000000, + delay: -500000, + easing: 'steps(2, end)' + }); + + assert_equals(getComputedStyle(target7).getPropertyValue('--length-1'), '150px'); + assert_equals(getComputedStyle(target7).getPropertyValue('--length-2'), '10px'); + + animation.effect.setKeyframes([ + { '--length-2': '100px' }, + { '--length-2': '200px' }, + ]); + + assert_equals(getComputedStyle(target7).getPropertyValue('--length-1'), '10px'); + assert_equals(getComputedStyle(target7).getPropertyValue('--length-2'), '10px'); +}, 'Custom property animations appearing via setKeyframes do not override important declarations'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-important-002-ref.html b/testing/web-platform/tests/css/css-animations/animation-important-002-ref.html new file mode 100644 index 0000000000..52855cb91e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-important-002-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<style> + a { + text-decoration: underline; + background-color: rgb(0, 150, 0); + } +</style> +<a style="color: rgb(150, 0, 0)">Unvisited</a> +<a style="color: white">Visited</a> diff --git a/testing/web-platform/tests/css/css-animations/animation-important-002.html b/testing/web-platform/tests/css/css-animations/animation-important-002.html new file mode 100644 index 0000000000..2a68f8e0ca --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-important-002.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<title>Test that animated properties on :visited are overridden by !important</title> +<link rel="help" href="https://drafts.csswg.org/css-cascade/#cascade-origin"> +<link rel="match" href="animation-important-002-ref.html"> +<style> + @keyframes color_and_bg_animation { + from { background-color: rgb(0, 100, 0); color: rgb(100, 0, 0); } + to { background-color: rgb(0, 200, 0); color: rgb(200, 0, 0); } + } + a { + animation-name: color_and_bg_animation; + animation-duration: 1000s; + animation-delay: -500s; + animation-timing-function: steps(2, end); + } + a:visited { + color: white !important; + } +</style> +<a href="#unvisited">Unvisited</a> +<a href="">Visited</a> diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-001-manual.html new file mode 100644 index 0000000000..bcbc9eb122 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-001-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - default value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is not set, 1 is taken by default and + animation will play from beginning to end once."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left once. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-002-manual.html new file mode 100644 index 0000000000..1d62e77c31 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-002-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - infinite value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is set to infinite, + animation will repeat forever."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: infinite; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled square with 'Filler Text', + which repeatedly moves from right to left. Every time a cycle is finished, + the square gets re-positioned to right and continues to move left. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-003-manual.html new file mode 100644 index 0000000000..75b07b93a0 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-003-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - negative value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is set to negative count, + it is invalid and animation will play from beginning to end once."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: -2; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left once. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-004-manual.html new file mode 100644 index 0000000000..e1edfc7a2a --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-004-manual.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - non-integer value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is set to non-integer, + animation will end partway through its last cycle."> +<style> + #test-iteration-count { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: 2.1; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + #ref-path { + background-color: yellow; + height: 10px; + width: 250px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left along the yellow bar twice, + and for the third time it ends partway and then immediately positions + itself to the left. + </p> + <div id="test-iteration-count">Filler Text</div> + <div id="ref-path"></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-005-manual.html new file mode 100644 index 0000000000..4bd79cfc0d --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-005-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - integer value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is set to integer count, + animation will repeat the specified number of times."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: 2; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left twice. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-006-manual.html new file mode 100644 index 0000000000..0c0c403333 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-006-manual.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - 0 (zero)</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-iteration-count is set to 0 (zero), no animation is seen."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left for about 2 seconds upon page load, + then moves from right to left once. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + div[0].style.animationIterationCount = 0; + setTimeout(setAnimationDuration, 2000); + + function setAnimationDuration() { + div[0].style.animationIterationCount = 1; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-007-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-007-manual.html new file mode 100644 index 0000000000..bb748d84ad --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-007-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-iteration-count applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: 2; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left twice. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-008-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-008-manual.html new file mode 100644 index 0000000000..645ac557ca --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-008-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-iteration-count - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-iteration-count applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: 2; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left twice. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-009.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-009.html new file mode 100644 index 0000000000..da86c81b93 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-009.html @@ -0,0 +1,46 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSS Animation Test: fractional animation-iteration-count</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#animation-iteration-count"> +<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes margin-animation { + from { + margin-left: 0px; + } + to { + margin-left: 100px; + } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'margin-animation 1s -10s linear 1.5 normal forwards paused'; + assert_equals(getComputedStyle(div).marginLeft, '50px'); +}, 'Basic floating point iteration count'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'margin-animation 1s -10s linear 3.25 normal forwards paused'; + assert_equals(getComputedStyle(div).marginLeft, '25px'); +}, 'Floating point iteration count after multiple iterations'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'margin-animation 1s -10s linear 0.75 normal forwards paused'; + assert_equals(getComputedStyle(div).marginLeft, '75px'); +}, 'Floating point iteration count during first iteration'); + +promise_test(async t => { + const div = addDiv(t); + div.style.animation = 'margin-animation 1s -10s linear 1.75 alternate forwards paused'; + assert_equals(getComputedStyle(div).marginLeft, '25px'); +}, 'Floating point iteration count with alternating directions'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-count-calc.html b/testing/web-platform/tests/css/css-animations/animation-iteration-count-calc.html new file mode 100644 index 0000000000..44e1e96a58 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-count-calc.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test: Animation count accepts calc()</title> +<link rel="author" name="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://drafts.csswg.org/css-values-3/#number-value"> +<link rel="help" href="https://drafts.csswg.org/css-animations/#animation-iteration-count"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="target" style="animation-iteration-count: calc(1 + 3)"> +<script> +test(function() { + let target = document.getElementById("target"); + assert_equals(getComputedStyle(target).animationIterationCount, "4") +}, "calc() should be accepted in animation-iteration-count."); +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-iteration-event-manual.html b/testing/web-platform/tests/css/css-animations/animation-iteration-event-manual.html new file mode 100644 index 0000000000..e7845ea2e2 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-iteration-event-manual.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation events - animationiteration</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-iteration-count"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#events"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animationiteration event occurs at the end of + each iteration of an animation for which animation-iteration-count + is greater than one."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-iteration-count: 3; + + color: yellow; + font-weight: bolder; + font-size: xx-large; + text-align: center; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square, which moves from right to left + three times; and if the square contains digit 1 for the first animation, + digit 2 for the second, and 3 for the third animation. + </p> + <div></div> + <script> + var count = 1; + var div = document.getElementsByTagName("div"); + div[0].innerHTML = count; + div[0].addEventListener("animationiteration", animationIteration, true); + + function animationIteration() { + count += 1; + div[0].innerHTML = count; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-keyframes-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-keyframes-001-manual.html new file mode 100644 index 0000000000..eee2d42946 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-keyframes-001-manual.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: @keyframes - from, to, percentage</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#keyframes"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that @keyframes rule accepts 'from' and 'to' + keywords, and percentage values."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + } + + @keyframes sample { + from { + background-color: blue; + } + 30% { + background-color: green; + } + 65% { + background-color: yellow; + } + to { + background-color: blue; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text' + initially on page load, and if the color of the square gradully changes + in order: BLUE to GREEN to YELLOW and back to BLUE in 10 seconds. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-keyframes-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-keyframes-002-manual.html new file mode 100644 index 0000000000..544031304b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-keyframes-002-manual.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: @keyframes - negative percentage and higher than 100%</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#keyframes"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that a keyframe selector specifies negative percentage values + or values higher than 100%, the keyframe will be ignored."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + } + + @keyframes sample { + -100% { + background-color: red; + } + 0% { + background-color: blue; + } + 30% { + background-color: green; + } + 65% { + background-color: yellow; + } + 100% { + background-color: blue; + } + 200% { + background-color: red; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text' + initially on page load, if the color of the square gradully changes + in order: BLUE to GREEN to YELLOW and back to BLUE in 10 seconds, + and if there is no red. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-keyframes-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-keyframes-003-manual.html new file mode 100644 index 0000000000..b0c26ab4a1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-keyframes-003-manual.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: @keyframes - animation-timing-function</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#keyframes"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that @keyframes rule accpets 'animation-timing-function' + property used as the animation moves to the next keyframe."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + animation-timing-function: linear; + } + 50% { + left: 75px; + animation-timing-function: linear; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left with constant speed. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html b/testing/web-platform/tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html new file mode 100644 index 0000000000..d389b2b962 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html @@ -0,0 +1,27 @@ +<!doctype html> +<meta charset=utf-8> +<title>Multiple from and to keyframes</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +@keyframes multiple-from-to-keyframes { + from, to { animation-timing-function: cubic-bezier(0, 0, 1, 1) } + from { left: 50px } + to { left: 150px } +} + +</style> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + div.style.animation = 'multiple-from-to-keyframes 100s -50s linear'; + assert_equals(getComputedStyle(div).left, "100px"); +}, 'Animations should not use an implicit value when the first rule within a @keyframes rule specifies "from" and "to" but only specifies a timing function.'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-name-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-001-manual.html new file mode 100644 index 0000000000..f2cf502aa8 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-001-manual.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name - blank value</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-name is set without any value, + there will be no animation."> +<style> + div { + animation-name: ; + animation-duration: 8s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, then moves from right to left. + </p> + <div>Filler Text</div> + <script> + setTimeout(setAnimationName, 2000); + + function setAnimationName () { + var div = document.getElementsByTagName("div"); + div[0].style.animationName = "sample"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-name-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-002-manual.html new file mode 100644 index 0000000000..0315bcc136 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-002-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name matchs keyframes at-rule</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-name is set to refer to a keyframe at-rule + that provides the property values for the animation, + animation will execute."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-name-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-003-manual.html new file mode 100644 index 0000000000..df89724a93 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-003-manual.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name - none</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-name is set to none, there will be no animation."> +<style> + div { + animation-name: none; + animation-duration: 8s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, then moves from right to left. + </p> + <div>Filler Text</div> + <script> + setTimeout(setAnimationName, 2000); + + function setAnimationName () { + var div = document.getElementsByTagName("div"); + div[0].style.animationName = "sample"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-name-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-004-manual.html new file mode 100644 index 0000000000..aab4974a0b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-004-manual.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name mismatches keyframe at-rule</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-name does not match any keyframe at-rule, + there are no properties to be animated and animation will not execute."> +<style> + div { + animation-name: sample1; + animation-duration: 8s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, then moves from right to left. + </p> + <div>Filler Text</div> + <script> + setTimeout(setAnimationName, 2000); + + function setAnimationName () { + var div = document.getElementsByTagName("div"); + div[0].style.animationName = "sample"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-name-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-005-manual.html new file mode 100644 index 0000000000..f72cf10f4c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-005-manual.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-name applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-name-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-name-006-manual.html new file mode 100644 index 0000000000..b1d2258f4d --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-name-006-manual.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-name - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-name applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left on the page load. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time-ref.html b/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time-ref.html new file mode 100644 index 0000000000..ab9f614ed8 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time-ref.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<style> +#target { + width: 50px; + height: 50px; + background-color: green; + opacity: 0.4; +} +</style> +<body> + <div id="target"></div> +</body> +</html> + diff --git a/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time.html b/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time.html new file mode 100644 index 0000000000..c63c2f3b9f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-opacity-pause-and-set-time.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Animations Test: pauses a opacity animation and sets the current time</title> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<link rel="match" href="animation-opacity-pause-and-set-time-ref.html"> +<style> +#target { + width: 50px; + height: 50px; + background-color: green; +} +</style> +<body> + <div id="target"></div> +</body> +<script> +window.onload = () => { + requestAnimationFrame(() => { + let animation = document.getElementById("target").animate([ + {opacity: '0.8'}, + {opacity: '0.0'} + ], 1000); + requestAnimationFrame(() => { + animation.pause(); + animation.currentTime = 500; + document.documentElement.removeAttribute('class'); + }); + }); +}; +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animation-play-state-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-play-state-001-manual.html new file mode 100644 index 0000000000..779f2fe310 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-play-state-001-manual.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-play-state - paused</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-play-state"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-play-state is set to paused, + animation is paused where the progress it had made + before being paused."> +<style> + #test-animation-paused { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + #ref-path { + background-color: yellow; + height: 10px; + width: 250px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving right to left along the yellow bar, + and pauses in the middle after about 2 seconds' animation. + </p> + <div id="test-animation-paused">Filler Text</div> + <div id="ref-path"></div> + <script> + setTimeout(setAnimationRunning, 2000); + + function setAnimationRunning() { + var div = document.getElementsByTagName("div"); + div[0].style.animationPlayState = "paused"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-play-state-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-play-state-002-manual.html new file mode 100644 index 0000000000..05837727c9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-play-state-002-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-play-state - running</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-play-state"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-play-state is set to running, + animation will proceed as normal."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-play-state: running; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-play-state-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-play-state-003-manual.html new file mode 100644 index 0000000000..8a1f719479 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-play-state-003-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-play-state - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-play-state"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-play-state applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + animation-play-state: running; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-play-state-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-play-state-004-manual.html new file mode 100644 index 0000000000..27ca389dec --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-play-state-004-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-play-state - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-play-state"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-play-state applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + animation-play-state: running; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-play-state-005.tentative.html b/testing/web-platform/tests/css/css-animations/animation-play-state-005.tentative.html new file mode 100644 index 0000000000..bb65ce3a41 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-play-state-005.tentative.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CSS animations shouldn't restart after resetting its play state</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-play-state"> +<link rel="help" href="https://drafts.csswg.org/web-animations-1/#playing-an-animation-section"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7145"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + @keyframes anim { + from { transform: translateX(100px); } + to { transform: translateX(200px); } + } +</style> +<body> +<div id="log"></div> +<script> +'use strict'; + +// Check that the auto-rewind flag is set to false when playing an animation in +// response to a change in animation-play-state. +promise_test(async t => { + const div = addDiv(t, { style: 'animation: anim 100ms' }); + + await new Promise(function (resolve) { + div.addEventListener('animationend', () => { + // After the animation ends, change animation-play-state to paused, flush, + // then change it back to running to trigger the re-start. + div.style.animationPlayState = 'paused'; + getComputedStyle(div).animationPlayState; + div.style.animationPlayState = 'running'; + getComputedStyle(div).animationPlayState; + resolve(); + }); + }); + + // Check the animation has not restarted. + assert_equals(div.getAnimations().length, 0); + assert_equals(getComputedStyle(div).transform, 'none'); +}, 'CSS animation should not restart after resetting its animation-play-state'); + +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001-ref.html b/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001-ref.html new file mode 100644 index 0000000000..30d70aadc1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001-ref.html @@ -0,0 +1,16 @@ +<!doctype html> +<title>CSS test reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<style> +.test { + display: flex; +} +.test::before { + content: ""; + display: block; + width: 100px; + height: 100px; + background-color: green; +} +</style> +<div class="test"></div> diff --git a/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001.html b/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001.html new file mode 100644 index 0000000000..e141b62149 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-pseudo-dynamic-001.html @@ -0,0 +1,37 @@ +<!doctype html> +<meta charset=utf-8> +<title>Animation of pseudo-element is stopped properly in presence of dynamic DOM change that reconstructs the layout tree</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1564366"> +<link rel="match" href="animation-pseudo-dynamic-001-ref.html"> +<style> +@keyframes anim { + from { background-color: red } + to { background-color: red } +} +.test { + display: flex; +} +.test::before { + content: ""; + display: block; + width: 100px; + height: 100px; + background-color: green; +} +.tweak::before { + animation: anim 2s linear infinite; +} +</style> +<div class="test tweak">foo</div> +<script> +onload = function() { + const div = document.querySelector(".test"); + const pseudoStyle = getComputedStyle(div, "::before"); + div.getBoundingClientRect(); // update layout + div.classList.remove("tweak"); + div.innerHTML = ""; // This is necessary to trigger the bug. +} +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-shorthand-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-shorthand-001-manual.html new file mode 100644 index 0000000000..0a947efe84 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-shorthand-001-manual.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation shorthand</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation shorthand property accepts time + values for animation-duration and animation-delay in order."> +<style> + div { + animation: 10s 2s sample; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, and then moves from right to left + and lasts for a span of 10 seconds. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-shorthand-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-shorthand-002-manual.html new file mode 100644 index 0000000000..55824f9fc1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-shorthand-002-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation shorthand - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation shorthand applies to the ::after pseudo element + and accepts time values for animation-duration and animation-delay + in order."> +<style> + div::after { + animation: 10s 2s sample; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, and then moves from right to left + and lasts for a span of 10 seconds. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-shorthand-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-shorthand-003-manual.html new file mode 100644 index 0000000000..651e5325bd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-shorthand-003-manual.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation shorthand - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation shorthand applies to the ::before pseudo element + and accepts time values for animation-duration and animation-delay + in order."> +<style> + div::before { + animation: 10s 2s sample; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts staying left about 2 seconds, and then moves from right to left + and lasts for a span of 10 seconds. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-style-element-replaced-with-keyframes-rule-of-same-name.html b/testing/web-platform/tests/css/css-animations/animation-style-element-replaced-with-keyframes-rule-of-same-name.html new file mode 100644 index 0000000000..285442c95b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-style-element-replaced-with-keyframes-rule-of-same-name.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset=utf-8> +<title>Changes to @keyframes rules</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<div id="log"></div> +<script> +'use strict'; + +test(t => { + const div = addDiv(t); + + const originalStyleElement = document.createElement("style"); + originalStyleElement.textContent = '@keyframes anim-custom { from, to { left: 100px } }'; + document.head.appendChild(originalStyleElement); + + t.add_cleanup(() => originalStyleElement.remove()); + + div.style.animation = 'anim-custom 100s'; + + const computedStyle = getComputedStyle(div); + assert_equals(computedStyle.left, "100px", "The initial @keyframes rule is applied"); + + // Remove the original style element and add a new one with an animation with the same name. + const newStyleElement = document.createElement("style"); + newStyleElement.textContent = '@keyframes anim-custom { from, to { left: 200px } }'; + originalStyleElement.replaceWith(newStyleElement); + + t.add_cleanup(() => newStyleElement.remove()); + + assert_equals(computedStyle.left, "200px", "The new @keyframes rule is applied"); +}, 'Replacing a <style> element with a new <style> element while both containing the different @keyframes rule with the same name dynamically updates running animations.'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-001-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-001-manual.html new file mode 100644 index 0000000000..c9987250dd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-001-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - cubic-bezier with parameters (0,0,1,1)</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to cubic-bezier + with parameters (0,0,1,1), animation will progress over one cycle + of its duration with constant speed."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: cubic-bezier(0,0,1,1); + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left with constant speed. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-002-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-002-manual.html new file mode 100644 index 0000000000..d62bd7186e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-002-manual.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ease</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function is set to ease, + animation will starts slow, gain acceleration in the middle + and again slow down at the end."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: ease; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left; initially the animation starts slow, + gains acceleration in the middle and again slows down at the end. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-003-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-003-manual.html new file mode 100644 index 0000000000..40eec103db --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-003-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ease-in</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function is set to ease-in, + animation will start slow, gain acceleration as time progresses."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: ease-in; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left; animation starts slow + and gains acceleration as time progresses. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-004-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-004-manual.html new file mode 100644 index 0000000000..833b902252 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-004-manual.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ease-in-out</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function is set to ease-in-out, + animation will start slow, gain acceleration in the middel + and again slow down at the end."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: ease-in-out; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left; initially the animation starts slow, + gains acceleration in the middle and again slows down at the end. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-005-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-005-manual.html new file mode 100644 index 0000000000..4ae04758cb --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-005-manual.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ease-out</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function is set to ease-out, + animation will start with higher (than the normal) speed + and relatively slow down as time progresses."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: ease-out; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left; animation starts and relatively + slows down as time progresses. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-006-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-006-manual.html new file mode 100644 index 0000000000..f746e89bdd --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-006-manual.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - linear</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to linear, + animation will progress over one cycle of its duration + with constant speed."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: linear; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which starts moving from right to left with constant speed. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-007-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-007-manual.html new file mode 100644 index 0000000000..dee9328741 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-007-manual.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - step-start</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to step-start, + animation will play the end effect at the start of the interval."> +<style> + div { + height: 100px; + width: 100px; + position: absolute; + } + + #test-step-start { + animation-name: sample; + animation-duration: 10s; + animation-fill-mode: forwards; + animation-timing-function: step-start; + + background-color: blue; + } + + #ref-no-animation { + background-color: red; + left: 150px; + } + + @keyframes sample { + from { + left: 0px; + } + to { + left: 150px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which stays right, and if there is NO red. + </p> + <div id="ref-no-animation"></div> + <div id="test-step-start">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-008-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-008-manual.html new file mode 100644 index 0000000000..77a4e11be8 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-008-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - step-end</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to step-end, + animation will play the end effect at the end time point, + and keep the start effect at the start of the interval."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: step-end; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled square with 'Filler Text', + which stays right for about 10 seconds, and then jumps to left. + </p> + <div>Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-009-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-009-manual.html new file mode 100644 index 0000000000..138e6f61aa --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-009-manual.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - steps with parameters (<number>, start)</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to steps with paramenters + (<number>, start), animation will be defined by the number that divides + the domain of operation into equally size intervals, evey time the changes + happens at the start of the interval."> +<style> + div { + float: left; + height: 100px; + width: 100px; + position: absolute; + } + + #test-step-start { + animation-name: sample; + animation-duration: 10s; + animation-fill-mode: forwards; + animation-timing-function: steps(4, start); + + background-color: blue; + } + + #ref-1 { + background-color: yellow; + left: 200px; + } + + #ref-2 { + background-color: green; + left: 100px; + } + + #ref-3 { + background-color: red; + left: 0px; + } + + @keyframes sample { + from { + left: 400px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there are a filled square with 'Filler Text', + which jumps three times from right to left and orderly covers + the filled YELLOW, GREEN and RED squares. + </p> + <div id="ref-1"></div> + <div id="ref-2"></div> + <div id="ref-3"></div> + <div id="test-step-start">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-010-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-010-manual.html new file mode 100644 index 0000000000..eed5b76adb --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-010-manual.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - steps with parameters (<number>, end)</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="When animation-timing-function is set to steps with paramenters + (<number>, end), animation will be defined by the number that divides + the domain of operation into equally size intervals, evey time the changes + happens at the end of the interval."> +<style> + div { + float: left; + height: 100px; + width: 100px; + position: absolute; + } + + #test-step-end { + animation-name: sample; + animation-duration: 10s; + animation-fill-mode: forwards; + animation-timing-function: steps(3, end); + + background-color: blue; + } + + #ref-1 { + background-color: yellow; + left: 200px; + } + + #ref-2 { + background-color: green; + left: 100px; + } + + #ref-3 { + background-color: red; + left: 0px; + } + + @keyframes sample { + from { + left: 300px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there are a filled square with 'Filler Text', + which jumps three times from right to left and orderly covers + the filled YELLOW, GREEN and RED squares. + </p> + <div id="ref-1"></div> + <div id="ref-2"></div> + <div id="ref-3"></div> + <div id="test-step-end">Filler Text</div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-011-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-011-manual.html new file mode 100644 index 0000000000..dfca3921a2 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-011-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ::after</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function applies to the ::after pseudo element."> +<style> + div::after { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: linear; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which moves from right to left with constant speed. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-timing-function-012-manual.html b/testing/web-platform/tests/css/css-animations/animation-timing-function-012-manual.html new file mode 100644 index 0000000000..24ffa39e93 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-timing-function-012-manual.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation-timing-function - ::before</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-07 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animation-timing-function applies to the ::before pseudo element."> +<style> + div::before { + animation-name: sample; + animation-duration: 10s; + animation-timing-function: linear; + + background-color: blue; + content: "Filler Text"; + display: block; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square with 'Filler Text', + which moves from right to left with constant speed. + </p> + <div></div> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time-ref.html b/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time-ref.html new file mode 100644 index 0000000000..3cadfd8518 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time-ref.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<style> +#target { + width: 50px; + height: 50px; + background-color: green; + transform: translate(500px); +} +</style> +<body> + <div id="target"></div> +</body> +</html> + diff --git a/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time.html b/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time.html new file mode 100644 index 0000000000..e8a9bd9706 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animation-transform-pause-and-set-time.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Animations Test: pauses a transform animation and sets the current time</title> +<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-timing-function"> +<link rel="match" href="animation-transform-pause-and-set-time-ref.html"> +<style> +#target { + width: 50px; + height: 50px; + background-color: green; +} +</style> +<body> + <div id="target"></div> +</body> +<script> +window.onload = () => { + requestAnimationFrame(() => { + let animation = document.getElementById("target").animate([ + {transform: 'translateX(0px)'}, + {transform: 'translateX(1000px)'} + ], 1000); + requestAnimationFrame(() => { + animation.pause(); + animation.currentTime = 500; + document.documentElement.removeAttribute('class'); + }); + }); +}; +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/animationevent-interface.html b/testing/web-platform/tests/css/css-animations/animationevent-interface.html new file mode 100644 index 0000000000..5b1bc4265f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationevent-interface.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: AnimationEvent interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#interface-dom"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="animationevent-interface.js"></script> +<div id="log"></div> + diff --git a/testing/web-platform/tests/css/css-animations/animationevent-interface.js b/testing/web-platform/tests/css/css-animations/animationevent-interface.js new file mode 100644 index 0000000000..fcb7ceba20 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationevent-interface.js @@ -0,0 +1,220 @@ +(function() { + test(function() { + var event = new AnimationEvent(""); + assert_true(event instanceof window.AnimationEvent); + }, "the event is an instance of AnimationEvent"); + + test(function() { + var event = new AnimationEvent(""); + assert_true(event instanceof window.Event); + }, "the event inherts from Event"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent(); + }, 'First argument is required, so was expecting a TypeError.'); + }, 'Missing type argument'); + + test(function() { + var event = new AnimationEvent("test"); + assert_equals(event.type, "test"); + }, "type argument is string"); + + test(function() { + var event = new AnimationEvent(null); + assert_equals(event.type, "null"); + }, "type argument is null"); + + test(function() { + var event = new AnimationEvent(undefined); + assert_equals(event.type, "undefined"); + }, "event type set to undefined"); + + test(function() { + var event = new AnimationEvent("test"); + assert_equals(event.animationName, ""); + }, "animationName has default value of empty string"); + + test(function() { + var event = new AnimationEvent("test"); + assert_equals(event.elapsedTime, 0.0); + }, "elapsedTime has default value of 0.0"); + + test(function() { + var event = new AnimationEvent("test"); + assert_readonly(event, "animationName", "readonly attribute value"); + }, "animationName is readonly"); + + test(function() { + var event = new AnimationEvent("test"); + assert_readonly(event, "elapsedTime", "readonly attribute value"); + }, "elapsedTime is readonly"); + + test(function() { + var event = new AnimationEvent("test", null); + assert_equals(event.animationName, ""); + assert_equals(event.elapsedTime, 0.0); + }, "animationEventInit argument is null"); + + test(function() { + var event = new AnimationEvent("test", undefined); + assert_equals(event.animationName, ""); + assert_equals(event.elapsedTime, 0.0); + }, "animationEventInit argument is undefined"); + + test(function() { + var event = new AnimationEvent("test", {}); + assert_equals(event.animationName, ""); + assert_equals(event.elapsedTime, 0.0); + }, "animationEventInit argument is empty dictionary"); + + test(function() { + var event = new AnimationEvent("test", {pseudoElement: "::testPseudo"}); + assert_equals(event.pseudoElement, "::testPseudo"); + }, "AnimationEvent.pseudoElement initialized from the dictionary"); + + test(function() { + var event = new AnimationEvent("test", {animationName: "sample"}); + assert_equals(event.animationName, "sample"); + }, "animationName set to 'sample'"); + + test(function() { + var event = new AnimationEvent("test", {animationName: undefined}); + assert_equals(event.animationName, ""); + }, "animationName set to undefined"); + + test(function() { + var event = new AnimationEvent("test", {animationName: null}); + assert_equals(event.animationName, "null"); + }, "animationName set to null"); + + test(function() { + var event = new AnimationEvent("test", {animationName: false}); + assert_equals(event.animationName, "false"); + }, "animationName set to false"); + + test(function() { + var event = new AnimationEvent("test", {animationName: true}); + assert_equals(event.animationName, "true"); + }, "animationName set to true"); + + test(function() { + var event = new AnimationEvent("test", {animationName: 0.5}); + assert_equals(event.animationName, "0.5"); + }, "animationName set to a number"); + + test(function() { + var event = new AnimationEvent("test", {animationName: []}); + assert_equals(event.animationName, ""); + }, "animationName set to []"); + + test(function() { + var event = new AnimationEvent("test", {animationName: [1, 2, 3]}); + assert_equals(event.animationName, "1,2,3"); + }, "animationName set to [1, 2, 3]"); + + test(function() { + var event = new AnimationEvent("test", {animationName: {sample: 0.5}}); + assert_equals(event.animationName, "[object Object]"); + }, "animationName set to an object"); + + test(function() { + var event = new AnimationEvent("test", + {animationName: {valueOf: function () { return 'sample'; }}}); + assert_equals(event.animationName, "[object Object]"); + }, "animationName set to an object with a valueOf function"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: 0.5}); + assert_equals(event.elapsedTime, 0.5); + }, "elapsedTime set to 0.5"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: -0.5}); + assert_equals(event.elapsedTime, -0.5); + }, "elapsedTime set to -0.5"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: undefined}); + assert_equals(event.elapsedTime, 0); + }, "elapsedTime set to undefined"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: null}); + assert_equals(event.elapsedTime, 0); + }, "elapsedTime set to null"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: false}); + assert_equals(event.elapsedTime, 0); + }, "elapsedTime set to false"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: true}); + assert_equals(event.elapsedTime, 1); + }, "elapsedTime set to true"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: ""}); + assert_equals(event.elapsedTime, 0); + }, "elapsedTime set to ''"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: []}); + assert_equals(event.elapsedTime, 0); + }, "elapsedTime set to []"); + + test(function() { + var event = new AnimationEvent("test", {elapsedTime: [0.5]}); + assert_equals(event.elapsedTime, 0.5); + }, "elapsedTime set to [0.5]"); + + test(function() { + var event = new AnimationEvent( + "test", {elapsedTime: { valueOf: function() { return 0.5; }}}); + assert_equals(event.elapsedTime, 0.5); + }, "elapsedTime set to an object with a valueOf function"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: NaN}); + }, 'elapsedTime cannot be NaN so was expecting a TypeError'); + }, "elapsedTime cannot be set to NaN"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: Infinity}); + }, 'elapsedTime cannot be Infinity so was expecting a TypeError'); + }, "elapsedTime cannot be set to Infinity"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: -Infinity}); + }, 'elapsedTime cannot be -Infinity so was expecting a TypeError'); + }, "elapsedTime cannot be set to -Infinity"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: "sample"}); + }, 'elapsedTime cannot be a string so was expecting a TypeError'); + }, "elapsedTime cannot be set to 'sample'"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: [0.5, 1.0]}); + }, 'elapsedTime cannot be a multi-element array so was expecting a TypeError'); + }, "elapsedTime cannot be set to [0.5, 1.0]"); + + test(function() { + assert_throws_js(TypeError, function() { + new AnimationEvent("test", {elapsedTime: { sample: 0.5}}); + }, 'elapsedTime cannot be an object so was expecting a TypeError'); + }, "elapsedTime cannot be set to an object"); + + test(function() { + var eventInit = {animationName: "sample", elapsedTime: 0.5}; + var event = new AnimationEvent("test", eventInit); + assert_equals(event.animationName, "sample"); + assert_equals(event.elapsedTime, 0.5); + }, "AnimationEventInit properties set value"); +})(); diff --git a/testing/web-platform/tests/css/css-animations/animationevent-marker-pseudoelement.html b/testing/web-platform/tests/css/css-animations/animationevent-marker-pseudoelement.html new file mode 100644 index 0000000000..90c7b86ab9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationevent-marker-pseudoelement.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: AnimationEvent pseudoElement</title> +<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations/#interface-animationevent"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #target::marker { + content: ""; + animation: move 1s; + } + + @keyframes move { + to { transform: translate(100px); } + } + + #target { + display: list-item; + list-style-position: inside; + } +</style> +<div id='target'></div> +<script> + async_test(function(t) { + var target = document.getElementById('target'); + target.addEventListener("animationstart", t.step_func(function(evt) { + assert_true(evt instanceof window.AnimationEvent); + assert_equals(evt.pseudoElement, "::marker"); + + t.done(); + }), true); + }, "AnimationEvent should have the correct pseudoElement memeber"); +</script> + diff --git a/testing/web-platform/tests/css/css-animations/animationevent-pseudoelement.html b/testing/web-platform/tests/css/css-animations/animationevent-pseudoelement.html new file mode 100644 index 0000000000..8de41cc613 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationevent-pseudoelement.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: AnimationEvent pseudoElement</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#interface-animationevent"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #target::before { + content: ""; + animation: move 1s; + } + + @keyframes move { + to { transform: translate(100px); } + } +</style> +<div id='target'></div> +<script> + async_test(function(t) { + var target = document.getElementById('target'); + target.addEventListener("animationstart", t.step_func(function(evt) { + assert_true(evt instanceof window.AnimationEvent); + assert_equals(evt.pseudoElement, "::before"); + + t.done(); + }), true); + }, "AnimationEvent should have the correct pseudoElement memeber"); +</script> + diff --git a/testing/web-platform/tests/css/css-animations/animationevent-types.html b/testing/web-platform/tests/css/css-animations/animationevent-types.html new file mode 100644 index 0000000000..77f514a297 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationevent-types.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<title>CSS Animations Test: AnimationEvnt types - animationstart, animationend,animationiteration</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#event-animationevent"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #test { + animation-name: sample; + animation-duration: 2s; + animation-delay: -1s; + animation-iteration-count: 2; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<div id="test">Filler Text</div> +<div id="log"></div> +<script> + var testDiv = document.getElementById("test"); + + async_test(function(t) { + testDiv.addEventListener("animationstart", t.step_func(function(evt) { + assert_true(evt instanceof window.AnimationEvent); + + assert_idl_attribute(evt, "animationName", "animationstart has animationName property"); + assert_idl_attribute(evt, "elapsedTime", "animationstart has elapsedTime property"); + assert_idl_attribute(evt, "pseudoElement", "animationstart has pseudoElement property"); + + assert_equals(evt.animationName, "sample", "animationstart has animationName value"); + assert_equals(evt.elapsedTime, 1, "animationstart has elapsedTime value"); + assert_equals(evt.pseudoElement, "", "animaitonstart has correct pseudoElement value"); + + t.done(); + }), true); + }, "animationstart event is instanceof AnimationEvent"); + + async_test(function(t) { + testDiv.addEventListener("animationend", t.step_func(function(evt) { + assert_true(evt instanceof window.AnimationEvent); + + assert_idl_attribute(evt, "animationName", "animationend has animationName property"); + assert_idl_attribute(evt, "elapsedTime", "animationend has elapsedTime property"); + assert_idl_attribute(evt, "pseudoElement", "animationstart has pseudoElement property"); + + assert_equals(evt.animationName, "sample", "animationend has animationName value"); + assert_equals(evt.elapsedTime, 4, "animationend has elapsedTime value"); + assert_equals(evt.pseudoElement, "", "animaitonstart has correct pseudoElement value"); + + t.done(); + }), true); + }, "animationend event is instanceof AnimationEvent"); + + async_test(function(t) { + testDiv.addEventListener("animationiteration", t.step_func(function(evt) { + assert_true(evt instanceof window.AnimationEvent); + + assert_idl_attribute(evt, "animationName", "animationiteration has animationName property"); + assert_idl_attribute(evt, "elapsedTime", "animationiteration has elapsedTime property"); + assert_idl_attribute(evt, "pseudoElement", "animationstart has pseudoElement property"); + + assert_equals(evt.animationName, "sample", "animationiteration has animationName value"); + assert_equals(evt.elapsedTime, 2, "animationiteration has elapsedTime value"); + assert_equals(evt.pseudoElement, "", "animaitonstart has correct pseudoElement value"); + + t.done(); + }), true); + }, "animationiteration event is instanceof AnimationEvent"); +</script> + diff --git a/testing/web-platform/tests/css/css-animations/animationstart-and-animationend-events-manual.html b/testing/web-platform/tests/css/css-animations/animationstart-and-animationend-events-manual.html new file mode 100644 index 0000000000..4ca5eb736e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/animationstart-and-animationend-events-manual.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations Test: animation events - animationstart and animationend</title> +<link rel="author" title="Nokia Inc." href="http://www.nokia.com"> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="reviewer" title="Zhiqiang Zhang" href="mailto:zhiqiang.zhang@intel.com"> <!-- 2015-05-06 --> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-name"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-duration"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#events"> +<meta name="flags" content="animated"> +<meta name="assert" content="Check that animationstart event occurs at the start of an animation, + animationend event occurs when animation finishes."> +<style> + div { + animation-name: sample; + animation-duration: 10s; + + background-color: blue; + height: 100px; + width: 100px; + position: relative; + } + + @keyframes sample { + from { + left: 150px; + } + to { + left: 0px; + } + } +</style> +<body> + <p> + Test passes if there is a filled blue square, + which moves from right to left with text 'START' + and changes to 'END' when the animation is finished. + </p> + <div>Filler Text</div> + <script> + var div = document.getElementsByTagName("div"); + div[0].addEventListener("animationstart", animationStart, true); + div[0].addEventListener("animationend", animationEnd, true); + + function animationStart() { + div[0].innerHTML = "START"; + } + + function animationEnd() { + div[0].innerHTML = "END"; + } + </script> +</body> + diff --git a/testing/web-platform/tests/css/css-animations/cancel-animation-shadow-slot-invalidation.html b/testing/web-platform/tests/css/css-animations/cancel-animation-shadow-slot-invalidation.html new file mode 100644 index 0000000000..d67a22443b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/cancel-animation-shadow-slot-invalidation.html @@ -0,0 +1,27 @@ +<!doctype html> +<html class="reftest-wait"> +<title>CSS Animations Test: Cancel animation for target moving out of flat tree and style invalidation</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#eventdef-animationevent-animationcancel"> +<link rel="help" href="https://crbug.com/1093747"> +<link rel="match" href="../reference/pass_if_pass_below.html"> +<script src="/common/reftest-wait.js"></script> +<style> + @keyframes anim { + from { color: red; } + to { color: green; } + } + #anim { animation: anim 100s; } + .none { display: none; } +</style> +<p>Test passes if there is the word "PASS" below.</p> +<div id="host"><span id="anim">FAIL</span></div> +<div id="dirty" class="none">PASS</div> +<script> + const root = host.attachShadow({mode:"open"}); + root.innerHTML = "<slot />"; + requestAnimationFrame(() => { + root.firstChild.name = "skip-slot"; + dirty.className = ""; + takeScreenshot(); + }); +</script> diff --git a/testing/web-platform/tests/css/css-animations/computed-style-animation-parsing.html b/testing/web-platform/tests/css/css-animations/computed-style-animation-parsing.html new file mode 100644 index 0000000000..15a1665be5 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/computed-style-animation-parsing.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations: parsing computedStyle.animation</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#animation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="test1"></div> +<div id="test2"></div> +<script> +function testComputedStyle(computedStyle1, computedStyle2, name) { + assert_equals(computedStyle1.animationName, name, "computedStyle1.animationName"); + assert_equals(computedStyle2.animationName, name, "computedStyle2.animationName"); + assert_equals(computedStyle1.animation, computedStyle2.animation, + "computedStyle1 and computedStyle2 should have the same animation"); +} + +function testAnimation(input, name) { + var style1 = test1.style; + var style2 = test2.style; + var computedStyle1 = getComputedStyle(test1); + var computedStyle2 = getComputedStyle(test2); + + style1.animation = input; + style2.animation = style1.animation; + testComputedStyle(computedStyle1, computedStyle2, name); + style2.animation = computedStyle1.animation; + testComputedStyle(computedStyle1, computedStyle2, name); +} + +test(() => { + // We are duplicating the logic of testAnimation because the animationName of + // the getComputedStyle is "none" when there is no animation. + var style1 = test1.style; + var style2 = test2.style; + var computedStyle1 = getComputedStyle(test1); + var computedStyle2 = getComputedStyle(test2); + + style1.animation = ""; + style2.animation = style1.animation; + testComputedStyle(computedStyle1, computedStyle2, "none"); + style2.animation = computedStyle1.animation; + assert_equals(computedStyle2.animationName, "none"); +}, "Test animation name being empty."); + +test(() => { + testAnimation("myShorthandAnim", "myShorthandAnim"); +}, "Test a non-conflicting animation name."); + +test(() => { + testAnimation("none", "none"); + testAnimation("forwards", "none"); + testAnimation("none forwards", "forwards"); +}, "Test an animation name that is the same as a possible animation fill-mode."); + +test(() => { + testAnimation("normal", "none"); + testAnimation("reverse", "none"); + testAnimation("normal normal", "normal"); + testAnimation("normal reverse", "reverse"); +}, "Test an animation name that is the same as a possible animation direction."); + +test(() => { + testAnimation("running", "none"); + testAnimation("paused", "none"); + testAnimation("running running", "running"); + testAnimation("running paused", "paused"); +}, "Test an animation name that is the same as a possible running state."); +</script> diff --git a/testing/web-platform/tests/css/css-animations/crashtests/replace-keyframes-animating-filter-001.html b/testing/web-platform/tests/css/css-animations/crashtests/replace-keyframes-animating-filter-001.html new file mode 100644 index 0000000000..e6355376f7 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/crashtests/replace-keyframes-animating-filter-001.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html class="test-wait"> +<title>CSS Test (Animations): Changing an @keyframes that animates filter</title> +<link rel="author" title="L. David Baron" href="https://dbaron.org/"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1301937"> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/#keyframes"> +<link rel="help" href="https://drafts.fxtf.org/filter-effects/#supported-filter-functions"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#the-cssstylesheet-interface"> +<meta name="assert" content="This should not crash."> + +<style> +#el1 { + height: 100px; + width: 100px; + animation: kf1 2s linear; +} +@keyframes kf1 { + from { filter: grayscale(0.9) } + to { filter: grayscale(0.8) } +} +</style> +<div id="el1"></div> +<script> + +document.documentElement.addEventListener("TestRendered", step1); + +function step1() { + requestAnimationFrame(function() { requestAnimationFrame(step2); }); +} + +function step2() { + let css = document.styleSheets[0]; + css.insertRule("@keyframes kf1 { from { color: blue } to { color: blue } } ", css.rules.length); + requestAnimationFrame(step3); +} + +function step3() { + document.documentElement.classList.remove("test-wait"); +} + +</script> diff --git a/testing/web-platform/tests/css/css-animations/dialog-animation.html b/testing/web-platform/tests/css/css-animations/dialog-animation.html new file mode 100644 index 0000000000..f6c8f6f0fa --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/dialog-animation.html @@ -0,0 +1,44 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSS Animations on a <dialog></title> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +dialog[open] { + animation: dialog-open-animation 1ms; +} + +@keyframes dialog-open-animation { + from { opacity: 0 } +} + +</style> +<div id="log"></div> +<script> + +"use strict"; + +promise_test(async t => { + const dialog = addElement(t, "dialog"); + + // Open the dialog a first time, this should trigger a CSS Animation. + dialog.open = true; + const animations = dialog.getAnimations(); + assert_equals(animations.length, 1, "As the <dialog> is shown intially an animation is started"); + + await animations[0].finished; + + await waitForNextFrame(); + + dialog.open = false; + assert_equals(dialog.getAnimations().length, 0, "As the <dialog> is closed the animation is removed"); + + await waitForNextFrame(); + + dialog.open = true; + assert_equals(dialog.getAnimations().length, 1, "As the <dialog> is shown again an animation is started again"); +}, "CSS Animations tied to <dialog open> are canceled and restarted as the dialog is hidden and shown"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/dialog-backdrop-animation.html b/testing/web-platform/tests/css/css-animations/dialog-backdrop-animation.html new file mode 100644 index 0000000000..cc61c2e380 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/dialog-backdrop-animation.html @@ -0,0 +1,44 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSS Animations on a <dialog> ::backdrop</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> + +dialog[open]::backdrop { + animation: dialog-backdrop-animation 1ms; +} + +@keyframes dialog-backdrop-animation { + from { opacity: 0 } +} + +</style> +<div id="log"></div> +<script> + +"use strict"; + +promise_test(async t => { + const dialog = addElement(t, "dialog"); + + // Open the dialog a first time, this should trigger a CSS Animation. + dialog.showModal(); + const animations = dialog.getAnimations({ subtree: true }); + assert_equals(animations.length, 1, "As the <dialog> is shown intially an animation is started on its ::backdrop"); + + await animations[0].finished; + + await waitForNextFrame(); + + dialog.close(); + assert_equals(dialog.getAnimations({ subtree: true }).length, 0, "As the <dialog> is closed the animation is removed from its ::backdrop"); + + await waitForNextFrame(); + + dialog.showModal(); + assert_equals(dialog.getAnimations({ subtree: true }).length, 1, "As the <dialog> is shown again an animation is started again on its ::backdrop"); +}, "CSS Animations on a <dialog> ::backdrop are canceled and restarted as the dialog is hidden and shown"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html b/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html new file mode 100644 index 0000000000..3e577d6ea6 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html @@ -0,0 +1,437 @@ +<!doctype html> +<meta charset=utf-8> +<title>Tests for CSS animation event dispatch</title> +<meta name="timeout" content="long"> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { + from { margin-left: 0px; } + to { margin-left: 100px; } +} +</style> +<div id="log"></div> +<script> +'use strict'; + +const setupAnimation = (t, animationStyle) => { + const div = addDiv(t, { style: 'animation: ' + animationStyle }); + const animation = div.getAnimations()[0]; + const timeoutPromise = armTimeoutWhenReady(animation, fastEventsTimeout); + + const watcher = new EventWatcher(t, div, [ 'animationstart', + 'animationiteration', + 'animationend', + 'animationcancel' ], + timeoutPromise); + + return { animation, watcher, div }; +}; + +promise_test(async t => { + // Add 1ms delay to ensure that the delay is not included in the elapsedTime. + const { animation, watcher } = setupAnimation(t, 'anim 100s 1ms'); + + const evt = await watcher.wait_for('animationstart'); + assert_equals(evt.elapsedTime, 0.0); +}, 'Idle -> Active'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + // Seek to After phase. + animation.finish(); + + const events = await watcher.wait_for(['animationstart', 'animationend'], { + record: 'all', + }); + assert_equals(events[0].elapsedTime, 0.0); + assert_equals(events[1].elapsedTime, 100); +}, 'Idle -> After'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused'); + + await animation.ready; + + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + + const evt = await watcher.wait_for('animationstart'); + assert_equals(evt.elapsedTime, 0.0); +}, 'Before -> Active'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused'); + const allEvents = watcher.wait_for(['animationstart', 'animationend'], { + record: 'all', + }); + + await animation.ready; + + // Seek to After phase. + animation.finish(); + + const events = await allEvents; + assert_equals(events[0].elapsedTime, 0.0); + assert_equals(events[1].elapsedTime, 100.0); +}, 'Before -> After'); + +promise_test(async t => { + const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused'); + + await watcher.wait_for('animationstart'); + + // Make idle + div.style.display = 'none'; + + const evt = await watcher.wait_for('animationcancel'); + assert_equals(evt.elapsedTime, 0.0); +}, 'Active -> Idle, display: none'); + +promise_test(async t => { + const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused'); + + // Seek to After phase. + animation.finish(); + await watcher.wait_for(['animationstart', 'animationend']); + + div.style.display = 'none'; + + // Wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'After -> Idle, display: none'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + animation.currentTime = 100.0; + + // Make idle + animation.timeline = null; + + const evt = await watcher.wait_for('animationcancel'); + assert_time_equals_literal(evt.elapsedTime, 0.1); +}, 'Active -> Idle, setting Animation.timeline = null'); + +promise_test(async t => { + // We should NOT pause animation since calling cancel synchronously. + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + animation.currentTime = 50.0; + animation.cancel(); + + const evt = await watcher.wait_for('animationcancel'); + assert_time_equals_literal(evt.elapsedTime, 0.05); +}, 'Active -> Idle, calling Animation.cancel()'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + await watcher.wait_for('animationstart'); + + // Seek to Before phase. + animation.currentTime = 0; + + const evt = await watcher.wait_for('animationend'); + assert_equals(evt.elapsedTime, 0.0); +}, 'Active -> Before'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s paused'); + + await watcher.wait_for('animationstart'); + + // Seek to After phase. + animation.finish(); + + const evt = await watcher.wait_for('animationend'); + assert_equals(evt.elapsedTime, 100.0); +}, 'Active -> After'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to After phase. + animation.finish(); + await watcher.wait_for([ 'animationstart', 'animationend' ]); + + // Seek to Before phase. + animation.currentTime = 0; + + const events = await watcher.wait_for(['animationstart', 'animationend'], { + record: 'all', + }); + assert_equals(events[0].elapsedTime, 100.0); + assert_equals(events[1].elapsedTime, 0.0); +}, 'After -> Before'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to After phase. + animation.finish(); + await watcher.wait_for([ 'animationstart', 'animationend' ]); + + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + + const evt = await watcher.wait_for('animationstart'); + assert_equals(evt.elapsedTime, 100.0); +}, 'After -> Active'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3 paused'); + + await animation.ready; + + // Seek to iteration 0 (no animationiteration event should be dispatched) + animation.currentTime = 100 * MS_PER_SEC; + await watcher.wait_for('animationstart'); + + // Seek to iteration 2 + animation.currentTime = 300 * MS_PER_SEC; + let evt = await watcher.wait_for('animationiteration'); + assert_equals(evt.elapsedTime, 200); + + // Seek to After phase (no animationiteration event should be dispatched) + animation.currentTime = 400 * MS_PER_SEC; + evt = await watcher.wait_for('animationend'); + assert_equals(evt.elapsedTime, 300); +}, 'Active -> Active (forwards)'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3'); + + // Seek to After phase. + animation.finish(); + await watcher.wait_for([ 'animationstart', 'animationend' ]); + + // Seek to iteration 2 (no animationiteration event should be dispatched) + animation.pause(); + animation.currentTime = 300 * MS_PER_SEC; + await watcher.wait_for('animationstart'); + + // Seek to mid of iteration 0 phase. + animation.currentTime = 200 * MS_PER_SEC; + + const evt = await watcher.wait_for('animationiteration'); + assert_equals(evt.elapsedTime, 200.0); + + // Seek to before phase (no animationiteration event should be dispatched) + animation.currentTime = 0; + await watcher.wait_for('animationend'); +}, 'Active -> Active (backwards)'); + +promise_test(async t => { + const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused'); + + await watcher.wait_for('animationstart'); + + // Seek to Idle phase. + div.style.display = 'none'; + flushComputedStyle(div); + + await watcher.wait_for('animationcancel'); + + // Restart this animation. + div.style.display = ''; + await watcher.wait_for('animationstart'); +}, 'Active -> Idle -> Active: animationstart is fired by restarting animation'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 2 paused'); + + // Make After. + animation.finish(); + + await watcher.wait_for([ 'animationstart', 'animationend' ]); + animation.playbackRate = -1; + + let evt = await watcher.wait_for('animationstart'); + assert_equals(evt.elapsedTime, 200); + + // Seek to 1st iteration + animation.currentTime = 200 * MS_PER_SEC - 1; + + evt = await watcher.wait_for('animationiteration'); + assert_equals(evt.elapsedTime, 100); + + // Seek to before + animation.currentTime = 100 * MS_PER_SEC - 1; + + evt = await watcher.wait_for('animationend'); + assert_equals(evt.elapsedTime, 0); + assert_equals(animation.playState, 'running'); // delay +}, 'Negative playbackRate sanity test(Before -> Active -> Before)'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + animation.currentTime = 150 * MS_PER_SEC; + animation.currentTime = 50 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, before -> active, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + animation.currentTime = 250 * MS_PER_SEC; + animation.currentTime = 50 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, before -> after, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + // Get us into the initial state: + animation.currentTime = 150 * MS_PER_SEC; + + await watcher.wait_for('animationstart'); + + animation.currentTime = 50 * MS_PER_SEC; + animation.currentTime = 150 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, active -> before, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + // Get us into the initial state: + animation.currentTime = 150 * MS_PER_SEC; + + await watcher.wait_for('animationstart'); + + animation.currentTime = 250 * MS_PER_SEC; + animation.currentTime = 150 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, active -> after, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + // Get us into the initial state: + animation.currentTime = 250 * MS_PER_SEC; + + await watcher.wait_for(['animationstart', 'animationend']); + + animation.currentTime = 50 * MS_PER_SEC; + animation.currentTime = 250 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, after -> before, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s 100s'); + + // Get us into the initial state: + animation.currentTime = 250 * MS_PER_SEC; + + await watcher.wait_for(['animationstart', 'animationend']); + + animation.currentTime = 150 * MS_PER_SEC; + animation.currentTime = 250 * MS_PER_SEC; + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Redundant change, after -> active, then back'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + // Make idle + animation.cancel(); + await watcher.wait_for('animationcancel'); + + animation.cancel(); + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Call Animation.cancel after canceling animation.'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + // Make idle + animation.cancel(); + animation.play(); + await watcher.wait_for([ 'animationcancel', 'animationstart' ]); +}, 'Restart animation after canceling animation immediately.'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + // Make idle + animation.cancel(); + animation.play(); + animation.cancel(); + await watcher.wait_for('animationcancel'); + + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Call Animation.cancel after restarting animation immediately.'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + // Make idle + animation.timeline = null; + await watcher.wait_for('animationcancel'); + + animation.timeline = document.timeline; + animation.play(); + await watcher.wait_for('animationstart'); +}, 'Set timeline and play transition after clearing the timeline.'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + // Make idle + animation.cancel(); + await watcher.wait_for('animationcancel'); + + animation.effect = null; + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Set null target effect after canceling the animation.'); + +promise_test(async t => { + const { animation, watcher } = setupAnimation(t, 'anim 100s'); + + await watcher.wait_for('animationstart'); + + animation.effect = null; + await watcher.wait_for('animationend'); + + animation.cancel(); + // Then wait a couple of frames and check that no event was dispatched. + await waitForAnimationFrames(2); +}, 'Cancel the animation after clearing the target effect.'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/event-order.tentative.html b/testing/web-platform/tests/css/css-animations/event-order.tentative.html new file mode 100644 index 0000000000..58f0bb67ca --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/event-order.tentative.html @@ -0,0 +1,259 @@ +<!doctype html> +<meta charset=utf-8> +<title>Tests for CSS animation event order</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/testcommon.js"></script> +<style> +@keyframes anim { + from { margin-left: 0px; } + to { margin-left: 100px; } +} +@keyframes color-anim { + from { color: red; } + to { color: green; } +} +</style> +<div id="log"></div> +<script type='text/javascript'> +'use strict'; + +/** + * Asserts that the set of actual and received events match. + * @param actualEvents An array of the received AnimationEvent objects. + * @param expectedEvents A series of array objects representing the expected + * events, each having the form: + * [ event type, target element, [pseudo type], elapsed time ] + */ +const checkEvents = (actualEvents, ...expectedEvents) => { + const actualTypeSummary = actualEvents.map(event => event.type).join(', '); + const expectedTypeSummary = expectedEvents.map(event => event[0]).join(', '); + + assert_equals( + actualEvents.length, + expectedEvents.length, + `Number of events received (${actualEvents.length}) \ +should match expected number (${expectedEvents.length}) \ +(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})` + ); + + for (const [index, actualEvent] of actualEvents.entries()) { + const expectedEvent = expectedEvents[index]; + const [type, target] = expectedEvent; + const pseudoElement = expectedEvent.length === 4 ? expectedEvent[2] : ''; + const elapsedTime = expectedEvent[expectedEvent.length - 1]; + + assert_equals( + actualEvent.type, + type, + `Event #${index + 1} types should match \ +(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})` + ); + assert_equals( + actualEvent.target, + target, + `Event #${index + 1} targets should match` + ); + assert_equals( + actualEvent.pseudoElement, + pseudoElement, + `Event #${index + 1} pseudoElements should match` + ); + assert_equals( + actualEvent.elapsedTime, + elapsedTime, + `Event #${index + 1} elapsedTimes should match` + ); + } +}; + +const setupAnimation = (t, animationStyle, receiveEvents) => { + const div = addDiv(t, { style: 'animation: ' + animationStyle }); + + for (const name of ['start', 'iteration', 'end']) { + div['onanimation' + name] = evt => { + receiveEvents.push({ + type: evt.type, + target: evt.target, + pseudoElement: evt.pseudoElement, + elapsedTime: evt.elapsedTime, + }); + }; + } + + const watcher = new EventWatcher(t, div, [ + 'animationstart', + 'animationiteration', + 'animationend', + ]); + + const animation = div.getAnimations()[0]; + + return [animation, watcher, div]; +}; + +promise_test(async t => { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 100s 2 paused', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2 paused', events); + + await Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationstart') ]); + + checkEvents(events, ['animationstart', div1, 0], + ['animationstart', div2, 0]); + + events.length = 0; // Clear received event array + + animation1.currentTime = 100 * MS_PER_SEC; + animation2.currentTime = 100 * MS_PER_SEC; + + await Promise.all([ watcher1.wait_for('animationiteration'), + watcher2.wait_for('animationiteration') ]); + + checkEvents(events, ['animationiteration', div1, 100], + ['animationiteration', div2, 100]); + + events.length = 0; // Clear received event array + + animation1.finish(); + animation2.finish(); + + await Promise.all([ watcher1.wait_for('animationend'), + watcher2.wait_for('animationend') ]); + + checkEvents(events, ['animationend', div1, 200], + ['animationend', div2, 200]); +}, 'Same events are ordered by elements'); + +function pseudoTest(description, testMarkerPseudos) { + promise_test(async t => { + // Setup a hierarchy as follows: + // + // parent + // | + // (::marker, ::before, ::after) // ::marker optional + // | + // child + const parentDiv = addDiv(t, { style: 'animation: anim 100s' }); + + parentDiv.id = 'parent-div'; + addStyle(t, { + '#parent-div::after': "content: ''; animation: anim 100s", + '#parent-div::before': "content: ''; animation: anim 100s", + }); + + if (testMarkerPseudos) { + parentDiv.style.display = 'list-item'; + addStyle(t, { + '#parent-div::marker': "content: ''; animation: color-anim 100s", + }); + } + + const childDiv = addDiv(t, { style: 'animation: anim 100s' }); + parentDiv.append(childDiv); + + // Setup event handlers + let events = []; + for (const name of ['start', 'iteration', 'end', 'cancel']) { + parentDiv['onanimation' + name] = evt => { + events.push({ + type: evt.type, + target: evt.target, + pseudoElement: evt.pseudoElement, + elapsedTime: evt.elapsedTime, + }); + }; + } + + // Wait a couple of frames for the events to be dispatched + await waitForFrame(); + await waitForFrame(); + + const expectedEvents = [ + ['animationstart', parentDiv, 0], + ['animationstart', parentDiv, '::marker', 0], + ['animationstart', parentDiv, '::before', 0], + ['animationstart', parentDiv, '::after', 0], + ['animationstart', childDiv, 0], + ]; + if (!testMarkerPseudos) { + expectedEvents.splice(1, 1); + } + + checkEvents(events, ...expectedEvents); + }, description); +} + +pseudoTest('Same events on pseudo-elements follow the prescribed order', false); +pseudoTest('Same events on pseudo-elements follow the prescribed order ' + + '(::marker)', true); + +promise_test(async t => { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 200s 400s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 300s 2', events); + + await watcher2.wait_for('animationstart'); + + animation1.currentTime = 400 * MS_PER_SEC; + animation2.currentTime = 400 * MS_PER_SEC; + + events.length = 0; // Clear received event array + + await Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationiteration') ]); + + checkEvents(events, ['animationiteration', div2, 300], + ['animationstart', div1, 0]); +}, 'Start and iteration events are ordered by time'); + +promise_test(async t => { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 150s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2', events); + + await Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationstart') ]); + + animation1.currentTime = 150 * MS_PER_SEC; + animation2.currentTime = 150 * MS_PER_SEC; + + events.length = 0; // Clear received event array + + await Promise.all([ watcher1.wait_for('animationend'), + watcher2.wait_for('animationiteration') ]); + + checkEvents(events, ['animationiteration', div2, 100], + ['animationend', div1, 150]); +}, 'Iteration and end events are ordered by time'); + +promise_test(async t => { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 100s 100s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2', events); + + animation1.finish(); + animation2.finish(); + + await Promise.all([ watcher1.wait_for([ 'animationstart', + 'animationend' ]), + watcher2.wait_for([ 'animationstart', + 'animationend' ]) ]); + + checkEvents(events, ['animationstart', div2, 0], + ['animationstart', div1, 0], + ['animationend', div1, 100], + ['animationend', div2, 200]); +}, 'Start and end events are sorted correctly when fired simultaneously'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable-ref.html b/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable-ref.html new file mode 100644 index 0000000000..411d09d433 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable-ref.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title>Transform animation under large scale</title> +<link rel="author" title="Kevin Ellis" href="mailto:kevers@chromium.org"> +<link rel="help" href="https://crbug.com/1221622"> +<style> +#container { + display: inline-block; + margin-left: 150px; + padding: 0; + transform: scaleX(-1) rotate(90deg); +} +#block-1 { + background: blue; + height: 200px; + width: 100px; +} +#block-2 { + background: green; + height: 100px; + width: 100px; +} +</style> + +<div id="container"> + <div id="block-1"></div> + <div id="block-2"></div> +</div> diff --git a/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable.html b/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable.html new file mode 100644 index 0000000000..22d23c1f72 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/flip-running-animation-via-variable.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Transform animation under large scale</title> +<link rel="author" title="Kevin Ellis" href="mailto:kevers@chromium.org"> +<link rel="help" href="https://crbug.com/1221622"> +<link rel="match" href="flip-running-animation-via-variable-ref.html"> +<script src="/common/reftest-wait.js"></script> +<style> +@keyframes spin { + 0% { transform: scaleX(var(--scale)) rotate(0deg); } + 100% { transform: scaleX(var(--scale)) rotate(180deg); } +} + +html { + --scale: 1; +} + +html.tweaked { + --scale: -1; +} + +#container { + display: inline-block; + margin-left: 150px; + padding: 0; + /* Force animation to be effectively frozen at 50% progress. */ + animation: spin 1000000s cubic-bezier(0, 1, 1, 0) -500000s; +} + +#block-1 { + background: blue; + height: 200px; + width: 100px; +} + +#block-2 { + background: green; + height: 100px; + width: 100px; +} +</style> + +<body> + <div id="container"> + <div id="block-1"></div> + <div id="block-2"></div> + </div> +</body> + +<script> +window.onload = () => { + const anim = document.getAnimations()[0]; + anim.ready.then(() => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.querySelector('html').classList.add('tweaked'); + requestAnimationFrame(() => { + requestAnimationFrame(() => { + takeScreenshot(); + }); + }); + }); + }); + }); +}; +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/historical.html b/testing/web-platform/tests/css/css-animations/historical.html new file mode 100644 index 0000000000..f9b13fa016 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/historical.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> + +<title>Historical CSS Animation features must be removed</title> +<link rel="help" href="https://www.w3.org/TR/css-animations-1/#animations"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function isInterfaceNuked(name) { + test(function() { + assert_equals(window[name], undefined) + }, "Historical CSS features must be removed: " + name) +} +var nukedInterfaces = [ + "WebKitAnimationEvent", // Replaced by unprefixed AnimationEvent +]; +nukedInterfaces.forEach(isInterfaceNuked); +</script> diff --git a/testing/web-platform/tests/css/css-animations/idlharness.html b/testing/web-platform/tests/css/css-animations/idlharness.html new file mode 100644 index 0000000000..8ddf2e6a94 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/idlharness.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>css-animations IDL tests</title> + <meta name="timeout" content="long"> + <link rel="help" href="https://drafts.csswg.org/css-animations/"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> + +<body> + <div id="log"></div> + + <style> + @keyframes test { + from { top: 0px; } + to { top: 1px; } + } + </style> + + <script> + 'use strict'; + + idl_test( + ['css-animations'], + ['html', 'dom', 'cssom'], + idl_array => { + try { + window.keyframes = document.styleSheets[0].cssRules[0]; + } catch (e) { + // Surfaced in idlharness.js's test_object below. + } + + idl_array.add_objects({ + AnimationEvent: ['new AnimationEvent("animationstart")'], + CSSKeyframesRule: ['keyframes'], + CSSKeyframeRule: ['keyframes.cssRules[0]'], + }); + } + ); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element-ref.html b/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element-ref.html new file mode 100644 index 0000000000..a25f465fdf --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element-ref.html @@ -0,0 +1,26 @@ +<!doctype html> +<title>CSS Test Reference</title> +<style> +body { + font-size: 30px; +} + +.container { + font-size: 5px; + height: 40px; +} + +.container::after { + content: ""; + display: block; + border: 2px solid blue; + width: 1em; + height: 1em; + font-size: 1em; +} +</style> +<div class="container"></div> +<div class="container"></div> +<div class="container"></div> +<div class="container"></div> +<div class="container"></div> diff --git a/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element.html b/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element.html new file mode 100644 index 0000000000..4ebcbc95de --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/inheritance-pseudo-element.html @@ -0,0 +1,49 @@ +<!doctype html> +<title>@keyframes + pseudo-element inherits from the right style.</title> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1757017"> +<link rel="help" href="https://drafts.csswg.org/css-animations/#property-index"> +<link rel="match" href="inheritance-pseudo-element-ref.html"> +<style> +body { + font-size: 30px; +} + +.container { + font-size: 5px; + height: 40px; +} + +.container::after { + content: ""; + display: block; + border: 2px solid blue; + width: 1em; + height: 1em; +} + +@keyframes kf-fs5px { from, to { font-size: 5px; } } +.fs5px::after { + animation: kf-fs5px 1s infinite; +} + +@keyframes kf-fs1em { from, to { font-size: 1em; } } +.fs1em::after { + animation: kf-fs1em 1s infinite; +} + +@keyframes kf-fs100p { from, to { font-size: 100%; } } +.fs100p::after { + animation: kf-fs100p 1s infinite; +} + +@keyframes kf-fsinherit { from, to { font-size: inherit; } } +.fsinherit::after { + animation: kf-fsinherit 1s infinite; +} +</style> +<div class="container"></div> +<div class="container fs5px"></div> +<div class="container fs1em"></div> +<div class="container fs100p"></div> +<div class="container fsinherit"></div> diff --git a/testing/web-platform/tests/css/css-animations/inheritance.html b/testing/web-platform/tests/css/css-animations/inheritance.html new file mode 100644 index 0000000000..6e7697b9d8 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/inheritance.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Inheritance of CSS Animations properties</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#property-index"> +<meta name="assert" content="Properties inherit or not according to the spec."> +<meta name="assert" content="Properties have initial values according to the spec."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +</head> +<body> +<div id="container"> +<div id="target"></div> +</div> +<script> +assert_not_inherited('animation-delay', '0s', '2s'); +assert_not_inherited('animation-direction', 'normal', 'reverse'); +assert_not_inherited('animation-duration', '0s', '3s'); +assert_not_inherited('animation-fill-mode', 'none', 'forwards'); +assert_not_inherited('animation-iteration-count', '1', '4'); +assert_not_inherited('animation-name', 'none', 'spinner'); +assert_not_inherited('animation-play-state', 'running', 'paused'); +assert_not_inherited('animation-timing-function', 'ease', 'linear'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/keyframes-remove-documentElement-crash.html b/testing/web-platform/tests/css/css-animations/keyframes-remove-documentElement-crash.html new file mode 100644 index 0000000000..9573ce1b0f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/keyframes-remove-documentElement-crash.html @@ -0,0 +1,28 @@ +<!doctype html> +<html class="test-wait"> +<title>CSS Animations Test: Chrome crash when removing documentElement and @keyframes stylesheet</title> +<link rel="help" href="https://crbug.com/999522"> +<style> + @keyframes anim { + from { color: pink } + to { color: purple } + } + div { + animation: notfound 1s; + } +</style> +<div></div> +<script> +window.addEventListener('load', () => { + document.body.offsetTop; + document.querySelector("style").remove(); + // We need the root later to remove the test-wait class. + const root = document.documentElement; + document.documentElement.remove(); + + // rAF to make sure that style runs. + requestAnimationFrame(() => { + root.classList.remove('test-wait'); + }); +}); +</script> diff --git a/testing/web-platform/tests/css/css-animations/keyframes-unrelated-custom-property.html b/testing/web-platform/tests/css/css-animations/keyframes-unrelated-custom-property.html new file mode 100644 index 0000000000..c9ba7688a9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/keyframes-unrelated-custom-property.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Unrelated custom properties do not conflict with each other</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/"> +<link rel="help" href="https://crbug.com/1236043"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @keyframes test { + 0% { --x: green; } + 100% { --x: green; } + + /* This should not affect the background-color of #div: */ + 0% { --unused: yellow; } + } + #div { + animation: test 10s linear paused; + background-color: var(--x, red); + width: 100px; + height: 100px; + } +</style> +<div id=div></div> +<script> + +test(() => { + assert_equals(getComputedStyle(div).backgroundColor, 'rgb(0, 128, 0)'); +}, 'Unrelated custom properties do not conflict with each other'); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/keyframes-zero-angle-crash.html b/testing/web-platform/tests/css/css-animations/keyframes-zero-angle-crash.html new file mode 100644 index 0000000000..593e4a6a1c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/keyframes-zero-angle-crash.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> +<title>CSS Animations Test: Chrome crash when using zero angles in keyframes</title> +<link rel="help" href="https://crbug.com/1307230"> +<body> +<div id="test_div"> + Output does not matter, only check that we do not crash with zero angles. +</div> +<script> +let anim = new KeyframeEffect(test_div, [{ "transform": "rotate(0) scale3D(1,1,1)" }]); +new Animation(anim, document.timeline); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-animations/missing-values-first-keyframe.html b/testing/web-platform/tests/css/css-animations/missing-values-first-keyframe.html new file mode 100644 index 0000000000..311e26259c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/missing-values-first-keyframe.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html> +<title>Missing properties in first keyframe</title> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect"> +<meta name="assert" + content="CSS animation correctly interpolates from neutral keyframe"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<style type="text/css" media="screen"> + body { + margin: 0; + } + + .box { + position: relative; + width: 100px; + height: 100px; + left: 0; + background-color: green; + } + + #box1 { + left: 200px; + animation: move-left 2s paused linear; + } + + #box2 { + transform: translateX(200px); + animation: move-transform 2s paused linear; + } + + @keyframes move-left { + 0% { + opacity: 1; + } + 25% { + opacity: 1; + } + 50% { + left: 0; + opacity: 1; + } + 100% { + left: 0; + opacity: 0; + } + } + + @keyframes move-transform { + 0% { + opacity: 1; + } + 25% { + opacity: 1; + } + 50% { + transform: translateX(0); + opacity: 1; + } + 100% { + transform: translateX(0); + opacity: 0; + } + } +</style> +<body> + <div class="box" id="box1"></div> + <div class="box" id="box2"></div> +</body> +<script> + promise_test(async t => { + document.getAnimations().forEach(anim => { + anim.currentTime = 500; + }); + assert_equals(getComputedStyle(box1).left, "100px"); + assert_matrix_equals( + getComputedStyle(box2).transform, + 'matrix(1, 0, 0, 1, 100, 0)'); + }, 'Missing property values in the first keyframe are correctly ' + + 'interpolated from a neutral keyframe value'); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/missing-values-last-keyframe.html b/testing/web-platform/tests/css/css-animations/missing-values-last-keyframe.html new file mode 100644 index 0000000000..04c4eba45e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/missing-values-last-keyframe.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html> +<title>Missing properties in last keyframe</title> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect"> +<meta name="assert" + content="CSS animation correctly interpolates from neutral keyframe"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<style type="text/css" media="screen"> + body { + margin: 0; + } + + .box { + position: relative; + width: 100px; + height: 100px; + left: 0; + background-color: green; + } + + #box1 { + left: 200px; + animation: move-left 10s linear; + } + + #box2 { + transform: translateX(200px); + animation: move-transform 10s linear; + } + + @keyframes move-left { + 0% { + left: 0; + opacity: 0; + } + 50% { + left: 0; + opacity: 1; + } + 75% { + opacity: 1; + } + 100% { + opacity: 1; + } + } + + @keyframes move-transform { + 0% { + transform: translateX(0); + opacity: 0; + } + 50% { + transform: translateX(0); + opacity: 1; + } + 75% { + opacity: 1; + } + 100% { + opacity: 1; + } + } +</style> +<body> + <div class="box" id="box1"></div> + <div class="box" id="box2"></div> +</body> +<script> + promise_test(async t => { + document.getAnimations().forEach(anim => { + anim.currentTime = 7500; + }); + assert_equals(getComputedStyle(box1).left, "100px"); + assert_matrix_equals( + getComputedStyle(box2).transform, + 'matrix(1, 0, 0, 1, 100, 0)'); + }, 'Missing property values in the last keyframe are correctly ' + + 'interpolated from a neutral keyframe value'); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/nested-scale-animations-ref.html b/testing/web-platform/tests/css/css-animations/nested-scale-animations-ref.html new file mode 100644 index 0000000000..6dd3cde311 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/nested-scale-animations-ref.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<div style="width: 100px; height: 45px; background: blue"></div> +<div style="height: 10px"></div> +<div style="width: 100px; height: 45px; background: green"></div> diff --git a/testing/web-platform/tests/css/css-animations/nested-scale-animations.html b/testing/web-platform/tests/css/css-animations/nested-scale-animations.html new file mode 100644 index 0000000000..8793e044b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/nested-scale-animations.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Nested scale animations</title> +<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org"> +<link rel="help" href="https://crbug.com/1165408"> +<link rel="match" href="nested-scale-animations-ref.html"> +<meta name="assert" content="Contents under nested scale animations should not be too blurry"> +<script src="/common/reftest-wait.js"></script> +<style> +@keyframes scale { + 0% {transform: scale(1);} + 1% {transform: scale(10);} + 100% {transform: scale(10);} +} +.animate { + animation: scale 1s forwards; + position: relative; + top: 40%; + left: 40%; + width: 20%; + height: 20%; +} +</style> +<div style="width: 100px; height: 100px; overflow: hidden; position: relative"> + <div class="animate" onanimationstart="animationStarted()"> + <div class="animate" onanimationstart="animationStarted()"> + <div style="width: 4px; height: 2px; background: blue"></div> + <div style="width: 4px; height: 2px; background: green"></div> + </div> + </div> + <!-- To mask off the pixels that may be blurry/antialiased while the rendering + quality is acceptable. --> + <div style="position: absolute; top: 45px; left: 0; width: 100px; height: 10px; background: white"></div> +</div> +<script> +var startedCount = 0; +function animationStarted() { + startedCount++; + if (startedCount == 2) + takeScreenshotDelayed(900); +} +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-composition-computed.tentative.html b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-computed.tentative.html new file mode 100644 index 0000000000..535a40c9ca --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-computed.tentative.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationComposition</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#propdef-animation-composition"> +<meta name="assert" content="animation-composition computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-composition", "replace, add, accumulate"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-composition-invalid.tentative.html b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-invalid.tentative.html new file mode 100644 index 0000000000..a08b33c84d --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-invalid.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-composition with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#propdef-animation-composition"> +<meta name="assert" content="animation-composition supports only the grammar '<single-animation-composition> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-composition", "auto"); +test_invalid_value("animation-composition", "add replace"); + +test_invalid_value("animation-composition", "add, initial"); +test_invalid_value("animation-composition", "initial, add"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-composition-valid.tentative.html b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-valid.tentative.html new file mode 100644 index 0000000000..8dca8dbabe --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-composition-valid.tentative.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-composition with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#propdef-animation-composition"> +<meta name="assert" content="animation-composition supports the full grammar '<single-animation-composition> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-composition", "replace"); +test_valid_value("animation-composition", "add"); +test_valid_value("animation-composition", "accumulate"); +test_valid_value("animation-composition", "replace, add, accumulate"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-computed.html new file mode 100644 index 0000000000..f8d34b889b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-computed.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animation</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation"> +<meta name="assert" content="animation computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +// <single-animation> = <time> || <easing-function> || <time> || +// <single-animation-iteration-count> || <single-animation-direction> || +// <single-animation-fill-mode> || <single-animation-play-state> || +// [ none | <keyframes-name> ] + +test(() => { + assert_equals(getComputedStyle(document.getElementById('target')).animation, "0s ease 0s 1 normal none running none"); +}, "Default animation value"); + +test_computed_value("animation", "1s", "1s ease 0s 1 normal none running none"); +test_computed_value("animation", "cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none"); +test_computed_value("animation", "1s -3s", "1s ease -3s 1 normal none running none"); +test_computed_value("animation", "4", "0s ease 0s 4 normal none running none"); +test_computed_value("animation", "reverse", "0s ease 0s 1 reverse none running none"); +test_computed_value("animation", "both", "0s ease 0s 1 normal both running none"); +test_computed_value("animation", "paused", "0s ease 0s 1 normal none paused none"); +test_computed_value("animation", "none", "0s ease 0s 1 normal none running none"); +test_computed_value("animation", "anim", "0s ease 0s 1 normal none running anim"); + +test_computed_value("animation", "anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)", + "1s cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim"); + +test_computed_value("animation", "anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)", + "0s ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none"); + +// TODO: Add test with a single timing-function keyword. +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-computed.html new file mode 100644 index 0000000000..832466ac06 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationDelay</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-delay"> +<meta name="assert" content="animation-delay converts to seconds."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-delay", "-500ms", "-0.5s"); +test_computed_value("animation-delay", "calc(2 * 3s)", "6s"); +test_computed_value("animation-delay", "20s, 10s"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-computed.html new file mode 100644 index 0000000000..9e0326b387 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-computed.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<div id="target"></div> +<script> +test_computed_value("animation-delay-start", "initial", "0s"); +test_computed_value("animation-delay-start", "-500ms", "-0.5s"); +test_computed_value("animation-delay-start", "calc(2 * 3s)", "6s"); +test_computed_value("animation-delay-start", "20s, 10s"); + +test_computed_value("animation-delay-start", "cover 0%"); +test_computed_value("animation-delay-start", "COVER 0%", "cover 0%"); +test_computed_value("animation-delay-start", "cover 100%"); +test_computed_value("animation-delay-start", "cover 120%"); +test_computed_value("animation-delay-start", "cover 42%"); +test_computed_value("animation-delay-start", "cover -42%"); +test_computed_value("animation-delay-start", "contain 42%"); +test_computed_value("animation-delay-start", "exit 42%"); +test_computed_value("animation-delay-start", "exit calc(41% + 1%)", "exit 42%"); +test_computed_value("animation-delay-start", "exit 1%, cover 2%, contain 100%"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-invalid.html new file mode 100644 index 0000000000..bff31f3789 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-invalid.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_invalid_value("animation-delay-start", "infinite"); +test_invalid_value("animation-delay-start", "0"); +test_invalid_value("animation-delay-start", "1s 2s"); +test_invalid_value("animation-delay-start", "1s / 2s"); +test_invalid_value("animation-delay-start", "100px"); +test_invalid_value("animation-delay-start", "100%"); + +test_invalid_value("animation-delay-start", "peek 50%"); +test_invalid_value("animation-delay-start", "50% contain"); +test_invalid_value("animation-delay-start", "50% cover"); +test_invalid_value("animation-delay-start", "50% entry"); +test_invalid_value("animation-delay-start", "50% enter"); +test_invalid_value("animation-delay-start", "50% exit"); +test_invalid_value("animation-delay-start", "contain contain"); +test_invalid_value("animation-delay-start", "auto"); +test_invalid_value("animation-delay-start", "none"); +test_invalid_value("animation-delay-start", "cover 50% enter 50%"); +test_invalid_value("animation-delay-start", "cover 100px"); +test_invalid_value("animation-delay-start", "cover"); +test_invalid_value("animation-delay-start", "contain"); +test_invalid_value("animation-delay-start", "enter"); +test_invalid_value("animation-delay-start", "exit"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-valid.html new file mode 100644 index 0000000000..22ab9fe3b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-end-valid.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_valid_value("animation-delay-start", "-5ms"); +test_valid_value("animation-delay-start", "0s"); +test_valid_value("animation-delay-start", "10s"); +test_valid_value("animation-delay-start", "20s, 10s"); + +// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges +test_valid_value("animation-delay-start", "cover 0%"); +test_valid_value("animation-delay-start", "cover 100%"); +test_valid_value("animation-delay-start", "cover 120%"); +test_valid_value("animation-delay-start", "cover 42%"); +test_valid_value("animation-delay-start", "cover -42%"); +test_valid_value("animation-delay-start", "contain 42%"); +test_valid_value("animation-delay-start", "exit 42%"); +test_valid_value("animation-delay-start", "exit 1%, cover 2%, contain 100%"); + +// There's an open issue in the spec about "enter" vs "entry". +// +// https://drafts.csswg.org/scroll-animations-1/#valdef-animation-timeline-range-entry +test_valid_value("animation-delay-start", "enter 42%"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-invalid.html new file mode 100644 index 0000000000..1569497bb6 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-invalid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-delay with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-delay"> +<meta name="assert" content="animation-delay supports only the grammar '<single-animation-play-state> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-delay", "infinite"); +test_invalid_value("animation-delay", "0"); +test_invalid_value("animation-delay", "1s 2s 3s"); + +test_invalid_value("animation-delay", "initial, -3s"); +test_invalid_value("animation-delay", "-3s, initial"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand-computed.html new file mode 100644 index 0000000000..01b1375dc9 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand-computed.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<title>animation-delay shorthand (computed values)</title> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<div id="target"></div> +<script> +test_computed_value("animation-delay", "1s"); +test_computed_value("animation-delay", "-1s"); +test_computed_value("animation-delay", "1s 2s"); +test_computed_value("animation-delay", "1s, 2s"); +test_computed_value("animation-delay", "1s 2s, 3s"); +test_computed_value("animation-delay", "1s, 2s 3s"); +test_computed_value("animation-delay", "1s, 2s, 3s"); + +test_computed_value("animation-delay", "cover"); +test_computed_value("animation-delay", "contain"); +test_computed_value("animation-delay", "enter"); +test_computed_value("animation-delay", "exit"); +test_computed_value("animation-delay", "enter, exit"); + +test_computed_value("animation-delay", "enter 0% enter 100%", "enter"); +test_computed_value("animation-delay", "exit 0% exit 100%", "exit"); +test_computed_value("animation-delay", "cover 0% cover 100%", "cover"); +test_computed_value("animation-delay", "contain 0% contain 100%", "contain"); + +test_computed_value("animation-delay", "cover 50%"); +test_computed_value("animation-delay", "contain 50%"); +test_computed_value("animation-delay", "enter 50%"); +test_computed_value("animation-delay", "exit 50%"); + +test_computed_value("animation-delay", "enter 50% 0s", "enter 50%"); +test_computed_value("animation-delay", "0s enter 50%"); +test_computed_value("animation-delay", "enter 50% exit 50%"); +test_computed_value("animation-delay", "cover 50% enter 50%, contain 50% exit 50%"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand.html new file mode 100644 index 0000000000..f09f0a948c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-shorthand.html @@ -0,0 +1,103 @@ +<!DOCTYPE html> +<title>animation-delay shorthand</title> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> + +test_valid_value("animation-delay", "1s"); +test_valid_value("animation-delay", "-1s"); +test_valid_value("animation-delay", "1s 2s"); +test_valid_value("animation-delay", "1s, 2s"); +test_valid_value("animation-delay", "1s 2s, 3s"); +test_valid_value("animation-delay", "1s, 2s 3s"); +test_valid_value("animation-delay", "1s, 2s, 3s"); + +test_valid_value("animation-delay", "cover"); +test_valid_value("animation-delay", "contain"); +test_valid_value("animation-delay", "enter"); +test_valid_value("animation-delay", "exit"); +test_valid_value("animation-delay", "enter, exit"); + +test_valid_value("animation-delay", "enter 0% enter 100%", "enter"); +test_valid_value("animation-delay", "exit 0% exit 100%", "exit"); +test_valid_value("animation-delay", "cover 0% cover 100%", "cover"); +test_valid_value("animation-delay", "contain 0% contain 100%", "contain"); + +test_valid_value("animation-delay", "cover 50%"); +test_valid_value("animation-delay", "contain 50%"); +test_valid_value("animation-delay", "enter 50%"); +test_valid_value("animation-delay", "exit 50%"); + +test_valid_value("animation-delay", "enter 50% 0s", "enter 50%"); +test_valid_value("animation-delay", "0s enter 50%"); +test_valid_value("animation-delay", "enter 50% exit 50%"); +test_valid_value("animation-delay", "cover 50% enter 50%, contain 50% exit 50%"); + +test_invalid_value("animation-delay", "1s 2s 3s"); +test_invalid_value("animation-delay", "0s, 1s 2s 3s"); +test_invalid_value("animation-delay", "1s / 2s"); +test_invalid_value("animation-delay", "1s, 2px"); +test_invalid_value("animation-delay", "#ff0000"); +test_invalid_value("animation-delay", "red"); +test_invalid_value("animation-delay", "thing"); +test_invalid_value("animation-delay", "thing 0%"); +test_invalid_value("animation-delay", "thing 42%"); +test_invalid_value("animation-delay", "thing 100%"); +test_invalid_value("animation-delay", "thing 100px"); +test_invalid_value("animation-delay", "100% thing"); + +test_shorthand_value('animation-delay', '1s 2s', { + 'animation-delay-start': '1s', + 'animation-delay-end': '2s', +}); + +test_shorthand_value('animation-delay', '1s', { + 'animation-delay-start': '1s', + 'animation-delay-end': '0s', +}); + +test_shorthand_value('animation-delay', 'cover', { + 'animation-delay-start': 'cover 0%', + 'animation-delay-end': 'cover 100%', +}); + +test_shorthand_value('animation-delay', 'contain', { + 'animation-delay-start': 'contain 0%', + 'animation-delay-end': 'contain 100%', +}); + +test_shorthand_value('animation-delay', 'enter 10% exit 20%', { + 'animation-delay-start': 'enter 10%', + 'animation-delay-end': 'exit 20%', +}); + +test_shorthand_value('animation-delay', '1s 2s, 3s 4s', { + 'animation-delay-start': '1s, 3s', + 'animation-delay-end': '2s, 4s', +}); + +test_shorthand_value('animation-delay', '1s 2s, 3s, 4s 5s', { + 'animation-delay-start': '1s, 3s, 4s', + 'animation-delay-end': '2s, 0s, 5s', +}); + +test_shorthand_value('animation-delay', 'enter, exit', { + 'animation-delay-start': 'enter 0%, exit 0%', + 'animation-delay-end': 'enter 100%, exit 100%', +}); + +test_shorthand_value('animation-delay', 'enter 0%, exit', { + 'animation-delay-start': 'enter 0%, exit 0%', + 'animation-delay-end': '0s, exit 100%', +}); + +test_shorthand_value('animation-delay', 'enter 0% 1s, 2s exit 50%', { + 'animation-delay-start': 'enter 0%, 2s', + 'animation-delay-end': '1s, exit 50%', +}); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-computed.html new file mode 100644 index 0000000000..9e0326b387 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-computed.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<div id="target"></div> +<script> +test_computed_value("animation-delay-start", "initial", "0s"); +test_computed_value("animation-delay-start", "-500ms", "-0.5s"); +test_computed_value("animation-delay-start", "calc(2 * 3s)", "6s"); +test_computed_value("animation-delay-start", "20s, 10s"); + +test_computed_value("animation-delay-start", "cover 0%"); +test_computed_value("animation-delay-start", "COVER 0%", "cover 0%"); +test_computed_value("animation-delay-start", "cover 100%"); +test_computed_value("animation-delay-start", "cover 120%"); +test_computed_value("animation-delay-start", "cover 42%"); +test_computed_value("animation-delay-start", "cover -42%"); +test_computed_value("animation-delay-start", "contain 42%"); +test_computed_value("animation-delay-start", "exit 42%"); +test_computed_value("animation-delay-start", "exit calc(41% + 1%)", "exit 42%"); +test_computed_value("animation-delay-start", "exit 1%, cover 2%, contain 100%"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-invalid.html new file mode 100644 index 0000000000..bff31f3789 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-invalid.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_invalid_value("animation-delay-start", "infinite"); +test_invalid_value("animation-delay-start", "0"); +test_invalid_value("animation-delay-start", "1s 2s"); +test_invalid_value("animation-delay-start", "1s / 2s"); +test_invalid_value("animation-delay-start", "100px"); +test_invalid_value("animation-delay-start", "100%"); + +test_invalid_value("animation-delay-start", "peek 50%"); +test_invalid_value("animation-delay-start", "50% contain"); +test_invalid_value("animation-delay-start", "50% cover"); +test_invalid_value("animation-delay-start", "50% entry"); +test_invalid_value("animation-delay-start", "50% enter"); +test_invalid_value("animation-delay-start", "50% exit"); +test_invalid_value("animation-delay-start", "contain contain"); +test_invalid_value("animation-delay-start", "auto"); +test_invalid_value("animation-delay-start", "none"); +test_invalid_value("animation-delay-start", "cover 50% enter 50%"); +test_invalid_value("animation-delay-start", "cover 100px"); +test_invalid_value("animation-delay-start", "cover"); +test_invalid_value("animation-delay-start", "contain"); +test_invalid_value("animation-delay-start", "enter"); +test_invalid_value("animation-delay-start", "exit"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-valid.html new file mode 100644 index 0000000000..22ab9fe3b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-start-valid.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#propdef-animation-delay-start"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_valid_value("animation-delay-start", "-5ms"); +test_valid_value("animation-delay-start", "0s"); +test_valid_value("animation-delay-start", "10s"); +test_valid_value("animation-delay-start", "20s, 10s"); + +// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges +test_valid_value("animation-delay-start", "cover 0%"); +test_valid_value("animation-delay-start", "cover 100%"); +test_valid_value("animation-delay-start", "cover 120%"); +test_valid_value("animation-delay-start", "cover 42%"); +test_valid_value("animation-delay-start", "cover -42%"); +test_valid_value("animation-delay-start", "contain 42%"); +test_valid_value("animation-delay-start", "exit 42%"); +test_valid_value("animation-delay-start", "exit 1%, cover 2%, contain 100%"); + +// There's an open issue in the spec about "enter" vs "entry". +// +// https://drafts.csswg.org/scroll-animations-1/#valdef-animation-timeline-range-entry +test_valid_value("animation-delay-start", "enter 42%"); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-delay-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-valid.html new file mode 100644 index 0000000000..5ff0416cc6 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-delay-valid.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-delay with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-delay"> +<meta name="assert" content="animation-delay supports the full grammar '<single-animation-play-state> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-delay", "-5ms"); +test_valid_value("animation-delay", "0s"); +test_valid_value("animation-delay", "10s"); +test_valid_value("animation-delay", "20s, 10s"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-direction-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-computed.html new file mode 100644 index 0000000000..d99038b3ba --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationDirection</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-direction"> +<meta name="assert" content="animation-direction computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-direction", "normal, reverse, alternate, alternate-reverse"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-direction-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-invalid.html new file mode 100644 index 0000000000..5c96216d04 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-invalid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-direction with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-direction"> +<meta name="assert" content="animation-direction supports only the grammar '<single-animation-direction> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-direction", "auto"); +test_invalid_value("animation-direction", "normal reverse"); + +test_invalid_value("animation-direction", "reverse, initial"); +test_invalid_value("animation-direction", "initial, reverse"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-direction-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-valid.html new file mode 100644 index 0000000000..bcc9acc342 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-direction-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-direction with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-direction"> +<meta name="assert" content="animation-direction supports the full grammar '<single-animation-direction> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-direction", "normal"); +test_valid_value("animation-direction", "reverse"); +test_valid_value("animation-direction", "alternate"); +test_valid_value("animation-direction", "alternate-reverse"); +test_valid_value("animation-direction", "normal, reverse, alternate, alternate-reverse"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-duration-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-computed.html new file mode 100644 index 0000000000..ef2ef69059 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationDuration</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-duration"> +<meta name="assert" content="animation-duration converts to seconds."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-duration", "500ms", "0.5s"); +test_computed_value("animation-duration", "calc(2 * 3s)", "6s"); +test_computed_value("animation-duration", "20s, 10s"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-duration-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-invalid.html new file mode 100644 index 0000000000..bd8cf2adfe --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-duration with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-duration"> +<meta name="assert" content="animation-duration supports only the grammar '<time> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-duration", '-3s'); +test_invalid_value("animation-duration", '0'); +test_invalid_value("animation-duration", 'infinite'); +test_invalid_value("animation-duration", '1s 2s'); + +test_invalid_value("animation-duration", 'initial, 1s'); +test_invalid_value("animation-duration", '1s, initial'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-duration-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-valid.html new file mode 100644 index 0000000000..e65a1a7072 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-duration-valid.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-duration with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-duration"> +<meta name="assert" content="animation-duration supports the full grammar '<time> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-duration", '3s'); +test_valid_value("animation-duration", '500ms'); +test_valid_value("animation-duration", '1s, 2s, 3s'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-computed.html new file mode 100644 index 0000000000..f4083b3833 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationFillMode</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode"> +<meta name="assert" content="animation-fill-mode computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-fill-mode", "none, forwards, backwards, both"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-invalid.html new file mode 100644 index 0000000000..2a82f23546 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-invalid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-fill-mode with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode"> +<meta name="assert" content="animation-fill-mode supports only the grammar '<single-animation-fill-mode> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-fill-mode", "auto"); +test_invalid_value("animation-fill-mode", "forwards backwards"); + +test_invalid_value("animation-fill-mode", "both, initial"); +test_invalid_value("animation-fill-mode", "initial, both"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-valid.html new file mode 100644 index 0000000000..1f73a821d1 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-fill-mode-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-fill-mode with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode"> +<meta name="assert" content="animation-fill-mode supports the full grammar '<single-animation-fill-mode> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-fill-mode", "none"); +test_valid_value("animation-fill-mode", "forwards"); +test_valid_value("animation-fill-mode", "backwards"); +test_valid_value("animation-fill-mode", "both"); +test_valid_value("animation-fill-mode", "none, forwards, backwards, both"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-invalid.html new file mode 100644 index 0000000000..dce5c29fdc --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-invalid.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation"> +<meta name="assert" content="animation supports only the grammar '<single-animation> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation", "1s 2s 3s"); +test_invalid_value("animation", "-1s -2s"); + +test_invalid_value("animation", "steps(1) steps(2)"); + +test_invalid_value("animation", "1 2"); + +test_invalid_value("animation", "reverse alternate alternate-reverse anim"); + +test_invalid_value("animation", "both backwards forwards anim"); + +test_invalid_value("animation", "paused running paused anim"); + +test_invalid_value("animation", "anim1 timeline1 anim2"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-computed.html new file mode 100644 index 0000000000..0ac53aa651 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationIterationCount</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count"> +<meta name="assert" content="animation-iteration-count computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-iteration-count", "0, infinite, 3"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-invalid.html new file mode 100644 index 0000000000..621340f7c3 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-invalid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-iteration-count with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count"> +<meta name="assert" content="animation-iteration-count supports only the grammar '<single-animation-iteration-count> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-iteration-count", "auto"); +test_invalid_value("animation-iteration-count", "-2"); +test_invalid_value("animation-iteration-count", "3 4"); + +test_invalid_value("animation-iteration-count", "initial, 4"); +test_invalid_value("animation-iteration-count", "4, initial"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-valid.html new file mode 100644 index 0000000000..be8a837989 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-iteration-count-valid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-iteration-count with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count"> +<meta name="assert" content="animation-iteration-count supports the full grammar '<single-animation-iteration-count> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-iteration-count", "0"); +test_valid_value("animation-iteration-count", "3"); +test_valid_value("animation-iteration-count", "4.5"); +test_valid_value("animation-iteration-count", "infinite"); + +test_valid_value("animation-iteration-count", "0, infinite, 3"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-name-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-name-computed.html new file mode 100644 index 0000000000..f05dd8b013 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-name-computed.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationName</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-name"> +<meta name="assert" content="animation-name computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-name", 'none'); + +test_computed_value("animation-name", 'foo'); +test_computed_value("animation-name", 'Both'); +test_computed_value("animation-name", 'ease-in'); +test_computed_value("animation-name", 'infinite'); +test_computed_value("animation-name", 'paused'); +test_computed_value("animation-name", 'first, second, third'); +test_computed_value("animation-name", '"something"', ["something", '"something"']); + +// TODO: Test more strings, after https://github.com/w3c/csswg-drafts/issues/2435 +// is resolved. +// Examples that need testing either here or in animation-name-invalid.html : +// '"Initial"', '"initial"', '"None"', '"Default"', '" x "', "1", '" "', '""', +// '"multi word string"', '"---\\22---"' +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-name-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-name-invalid.html new file mode 100644 index 0000000000..739641a51d --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-name-invalid.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-name with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-name"> +<meta name="assert" content="animation-name supports only the grammar '[ none | <keyframes-name> ]#'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-name", '12'); +test_invalid_value("animation-name", 'one two'); + +test_invalid_value("animation-name", 'one, initial'); +test_invalid_value("animation-name", 'one, inherit'); +test_invalid_value("animation-name", 'one, unset'); +test_invalid_value("animation-name", 'default, two'); +test_invalid_value("animation-name", 'revert, three'); +test_invalid_value("animation-name", 'revert-layer, four'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-name-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-name-valid.html new file mode 100644 index 0000000000..1906f9bdb7 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-name-valid.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-name with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-name"> +<meta name="assert" content="animation-name supports the full grammar '[ none | <keyframes-name> ]#'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-name", 'NONE', 'none'); + +test_valid_value("animation-name", 'foo'); +test_valid_value("animation-name", 'Both'); +test_valid_value("animation-name", 'ease-in'); +test_valid_value("animation-name", 'infinite'); +test_valid_value("animation-name", 'paused'); +test_valid_value("animation-name", 'first, second, third'); + +test_valid_value("animation-name", '"string"', ['"string"', "string"]); +test_valid_value("animation-name", '"multi word string"', ['"multi word string"', "multi\\ word\\ string"]); +test_valid_value("animation-name", '"initial"'); +test_valid_value("animation-name", '"---\\22---"', ['\"---\\\"---\"', '---\\\"---']); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-computed.html b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-computed.html new file mode 100644 index 0000000000..c3ff2aa95f --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: getComputedStyle().animationPlayState</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-play-state"> +<meta name="assert" content="animation-play-state computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("animation-play-state", "running, paused"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-invalid.html new file mode 100644 index 0000000000..91a6f01737 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-invalid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-play-state with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-play-state"> +<meta name="assert" content="animation-play-state supports only the grammar '<single-animation-play-state> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("animation-play-state", "auto"); +test_invalid_value("animation-play-state", "paused running"); + +test_invalid_value("animation-play-state", "paused, initial"); +test_invalid_value("animation-play-state", "initial, paused"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-valid.html new file mode 100644 index 0000000000..ce6d053ec2 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-play-state-valid.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation-play-state with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation-play-state"> +<meta name="assert" content="animation-play-state supports the full grammar '<single-animation-play-state> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("animation-play-state", "running"); +test_valid_value("animation-play-state", "paused"); +test_valid_value("animation-play-state", "running, paused"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.html b/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.html new file mode 100644 index 0000000000..b981ad3ad6 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: animation sets longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation"> +<meta name="assert" content="animation supports the full grammar '<single-animation> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('animation', 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)', { + 'animation-duration': '1s', + 'animation-timing-function': 'cubic-bezier(0, -2, 1, 3)', + 'animation-delay': '-3s', + 'animation-iteration-count': '4', + 'animation-direction': 'reverse', + 'animation-fill-mode': 'both', + 'animation-play-state': 'paused', + 'animation-name': 'anim', + 'animation-timeline': 'auto' +}); + +test_shorthand_value('animation', 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)', { + 'animation-duration': '0s, 1s', + 'animation-timing-function': 'ease, cubic-bezier(0, -2, 1, 3)', + 'animation-delay': '0s, -3s', + 'animation-iteration-count': '1, 4', + 'animation-direction': 'reverse, normal', + 'animation-fill-mode': 'both, none', + 'animation-play-state': 'paused, running', + 'animation-name': 'anim, none', + 'animation-timeline': 'auto, auto' +}); + +test_shorthand_value('animation', '4 1s -3s cubic-bezier(0, -2, 1, 3), anim paused both reverse', { + 'animation-duration': '1s, 0s', + 'animation-timing-function': 'cubic-bezier(0, -2, 1, 3), ease', + 'animation-delay': '-3s, 0s', + 'animation-iteration-count': '4, 1', + 'animation-direction': 'normal, reverse', + 'animation-fill-mode': 'none, both', + 'animation-play-state': 'running, paused', + 'animation-name': 'none, anim', + 'animation-timeline': 'auto, auto' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.tentative.html b/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.tentative.html new file mode 100644 index 0000000000..ac3613cf82 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-shorthand.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation"> +<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +<script> +// TODO(https://github.com/w3c/csswg-drafts/issues/8054): When support for +// animation-delay-start and -end is added to the animation shorthand, this +// file should just merge with animation-shorthand.html. +test_shorthand_value('animation', 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)', { + 'animation-duration': '1s', + 'animation-timing-function': 'cubic-bezier(0, -2, 1, 3)', + 'animation-delay-start': '-3s', + 'animation-delay-end': '0s', + 'animation-iteration-count': '4', + 'animation-direction': 'reverse', + 'animation-fill-mode': 'both', + 'animation-play-state': 'paused', + 'animation-name': 'anim', + 'animation-timeline': 'auto' +}); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/animation-valid.html b/testing/web-platform/tests/css/css-animations/parsing/animation-valid.html new file mode 100644 index 0000000000..4027ee1226 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/animation-valid.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: parsing animation with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#propdef-animation"> +<meta name="assert" content="animation supports the full grammar '<single-animation> #'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +// <single-animation> = <time> || <easing-function> || <time> || +// <single-animation-iteration-count> || <single-animation-direction> || +// <single-animation-fill-mode> || <single-animation-play-state> || +// [ none | <keyframes-name> ] +test_valid_value("animation", "1s", ["1s", "1s ease 0s 1 normal none running none"]); +test_valid_value("animation", "cubic-bezier(0, -2, 1, 3)", ["cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none"]); +test_valid_value("animation", "1s -3s", ["1s -3s", "1s ease -3s 1 normal none running none"]); +test_valid_value("animation", "4", ["4", "0s ease 0s 4 normal none running none"]); +test_valid_value("animation", "reverse", ["reverse", "0s ease 0s 1 reverse none running none"]); +test_valid_value("animation", "both", ["both", "0s ease 0s 1 normal both running none"]); +test_valid_value("animation", "paused", ["paused", "0s ease 0s 1 normal none paused none"]); +test_valid_value("animation", "none", ["0s", "none", "0s ease 0s 1 normal none running none"]); +test_valid_value("animation", "anim", ["anim", "0s ease 0s 1 normal none running anim"]); + +test_valid_value("animation", "anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)", + "1s cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim"); + +test_valid_value("animation", "anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)", + ["reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4", "0s ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none"]); + +// TODO: Add test with a single negative time. +// TODO: Add test with a single timing-function keyword. +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/parsing/keyframes-allowed-properties.html b/testing/web-platform/tests/css/css-animations/parsing/keyframes-allowed-properties.html new file mode 100644 index 0000000000..f682920b35 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/keyframes-allowed-properties.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title>Tests which properties are allowed in @keyframes</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#typedef-keyframe-block"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style id=sheet> +@keyframes foo { + from { + /* Non-animation properties are allowed */ + margin-top: 10px; + /* animation-timing-function is specially allowed */ + animation-timing-function: ease; + /* All other animation properties are not allowed */ + animation-name: none; + animation-duration: 1s; + animation-iteration-count: 1; + animation-direction: normal; + animation-play-state: running; + animation-delay: 0s; + animation-fill-mode: none; + /* The animation shorthand is also not allowed */ + animation: bar 1s infinite; + } +} +</style> +<script> +test(() => { + const keyframe = sheet.sheet.cssRules[0].cssRules[0]; + const style = keyframe.style; + assert_equals(style.length, 2); + assert_equals(style.marginTop, '10px'); + assert_equals(style.animationTimingFunction, 'ease'); +}, '@keyframes allows all non-animation properties and animation-timing-function'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html new file mode 100644 index 0000000000..f14dff3e2c --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-invalid.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>CSS Animations: parsing @keyframes name with invalid values</title> + <link rel="author" title="yisibl(一丝)" href="https://github.com/yisibl"/> + <link rel="help" href="https://drafts.csswg.org/css-animations/#typedef-keyframes-name"> + <meta name="assert" content="@keyframes name supports the full grammar '<custom-ident> | <string>'."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> + <div> + <main id="main"></main> + </div> + <script> + test_keyframes_name_invalid('none'); + + // The CSS-wide keywords are not valid <custom-ident>s. The default keyword is reserved and is also not a valid <custom-ident>. + // Spec: https://drafts.csswg.org/css-values-4/#identifier-value + test_keyframes_name_invalid('default'); + test_keyframes_name_invalid('initial'); + test_keyframes_name_invalid('inherit'); + test_keyframes_name_invalid('unset'); + test_keyframes_name_invalid('revert'); + test_keyframes_name_invalid('revert-layer'); + + test_keyframes_name_invalid('12'); + test_keyframes_name_invalid('-12'); + test_keyframes_name_invalid('12foo'); + test_keyframes_name_invalid('foo.bar'); + test_keyframes_name_invalid('one two'); + test_keyframes_name_invalid('one, two'); + + test_keyframes_name_invalid('one, initial'); + test_keyframes_name_invalid('one, inherit'); + test_keyframes_name_invalid('one, unset'); + test_keyframes_name_invalid('default, two'); + test_keyframes_name_invalid('revert, three'); + test_keyframes_name_invalid('revert-layer, four'); + // TODO: https://bugs.chromium.org/p/chromium/issues/detail?id=1342609 + // test_keyframes_name_invalid('--foo'); + </script> +</body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-valid.html b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-valid.html new file mode 100644 index 0000000000..f44540e924 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/keyframes-name-valid.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>CSS Animations: parsing @keyframes name with valid values</title> + <link rel="author" title="yisibl(一丝)" href="https://github.com/yisibl"/> + <link rel="help" href="https://drafts.csswg.org/css-animations/#typedef-keyframes-name"> + <meta name="assert" content="@keyframes name supports the full grammar '<custom-ident> | <string>'."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> + <div> + <main id="main"></main> + </div> + <script> + // Test <custom-ident> + test_keyframes_name_valid(' foo '); + test_keyframes_name_valid(' foo'); + test_keyframes_name_valid('-foo'); + test_keyframes_name_valid('_bar'); + test_keyframes_name_valid('__bar'); + test_keyframes_name_valid('__bar__'); + test_keyframes_name_valid('ease-out'); + test_keyframes_name_valid('example'); + test_keyframes_name_valid('EXAMPLE'); + + test_keyframes_name_valid('not'); + test_keyframes_name_valid('and'); + test_keyframes_name_valid('all'); + test_keyframes_name_valid('or'); + + // <custom-ident> may disable the `auto/normal` keywords in the future + // https://github.com/w3c/csswg-drafts/issues/7431 + test_keyframes_name_valid('auto'); + test_keyframes_name_valid('normal'); + + // Test <string> + test_keyframes_name_valid('" foo "'); + test_keyframes_name_valid('" foo"'); + test_keyframes_name_valid('"-foo"'); + test_keyframes_name_valid('"_bar"'); + test_keyframes_name_valid('"__bar"'); + test_keyframes_name_valid('"__bar__"'); + test_keyframes_name_valid('"ease-out"'); + test_keyframes_name_valid('"example"'); + test_keyframes_name_valid('"EXAMPLE"'); + + test_keyframes_name_valid('"one two"'); + test_keyframes_name_valid('"one, two"'); + + test_keyframes_name_valid('"not"'); + test_keyframes_name_valid('"and"'); + test_keyframes_name_valid('"all"'); + test_keyframes_name_valid('"or"'); + + test_keyframes_name_valid('"auto"'); + test_keyframes_name_valid('"normal"'); + test_keyframes_name_valid('"none"'); + + test_keyframes_name_valid('"default"'); + test_keyframes_name_valid('"initial"'); + test_keyframes_name_valid('"inherit"'); + test_keyframes_name_valid('"unset"'); + test_keyframes_name_valid('"revert"'); + test_keyframes_name_valid('"revert-layer"'); + </script> +</body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-animations/pending-style-changes-001.html b/testing/web-platform/tests/css/css-animations/pending-style-changes-001.html new file mode 100644 index 0000000000..fb74d7fa7d --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/pending-style-changes-001.html @@ -0,0 +1,34 @@ +<meta charset=utf-8> +<title>CSS Animations Test: requirement on pending style changes - getAnimations</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#requirements-on-pending-style-changes"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +@keyframes anim {} + +.animate { + animation: anim 10s; +} +</style> + +<div id="target"></div> + +<script> +test (t => { + assert_equals(target.getAnimations().length, 0, 'Test precondition.'); + target.classList.add('animate'); + assert_equals(target.getAnimations().length, 1, + 'target.getAnimations() should include the CSS animation after animate class added.'); + target.classList.remove('animate'); +}, 'Animatable::getAnimations() should be able to see a style-created CSS animation immediately'); + +test(t => { + assert_equals(document.getAnimations().length, 0, 'Test precondition.'); + target.classList.add('animate'); + assert_equals(document.getAnimations().length, 1, + 'document.getAnimations() should include the CSS animation after animate class added.'); + target.classList.remove('animate'); +}, 'Document::getAnimations() should be able to see a style-created CSS animation immediately'); +</script> diff --git a/testing/web-platform/tests/css/css-animations/responsive/column-rule-color-001.html b/testing/web-platform/tests/css/css-animations/responsive/column-rule-color-001.html new file mode 100644 index 0000000000..9953db4354 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/responsive/column-rule-color-001.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: column-rule-color animations respond to style changes</title> +<link rel="help" href="https://drafts.csswg.org/css-multicol-1/#crc"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .paused { + animation-duration: 4s; + animation-timing-function: linear; + animation-delay: -2s; + animation-play-state: paused; + } + #container { + color: rgb(80, 0, 0); + } + #first { + animation-name: first-anim; + color: rgb(60, 0, 0); + } + #second { + animation-name: second-anim; + } + @keyframes first-anim { + from { column-rule-color: currentColor; } + to { column-rule-color: rgb(0, 60, 0); } + } + @keyframes second-anim { + from { column-rule-color: inherit; } + to { column-rule-color: rgb(0, 0, 80); } + } +</style> +</head> +<body> +<div id="container"> + <div id="first" class="paused"></div> + <div id="second" class="paused"></div> +</div> +<script> +'use strict'; +var container = document.getElementById('container'); + +test(() => { + const first = document.getElementById('first'); + assert_equals(getComputedStyle(first).columnRuleColor, 'rgb(30, 30, 0)'); + first.style.color = 'rgb(0, 0, 60)'; + assert_equals(getComputedStyle(first).columnRuleColor, 'rgb(0, 30, 30)'); +}, 'column-rule-color responds to currentColor changes'); + +test(() => { + const container = document.getElementById('container'); + const second = document.getElementById('second'); + assert_equals(getComputedStyle(second).columnRuleColor, 'rgb(40, 0, 40)'); + container.style.columnRuleColor = 'rgb(0, 80, 0)'; + assert_equals(getComputedStyle(second).columnRuleColor, 'rgb(0, 40, 40)'); +}, 'column-rule-color responds to inherited changes'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/responsive/column-width-001.html b/testing/web-platform/tests/css/css-animations/responsive/column-width-001.html new file mode 100644 index 0000000000..7697eec324 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/responsive/column-width-001.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Animations: column-width animations respond to style changes</title> +<link rel="help" href="https://drafts.csswg.org/css-multicol-1/#cw"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .paused { + animation-duration: 4s; + animation-timing-function: linear; + animation-delay: -2s; + animation-play-state: paused; + } + #container { + column-width: 40px; + font-size: 10px; + } + #first { + animation-name: first-anim; + } + #second { + animation-name: second-anim; + } + #third { + animation-name: third-anim; + } + @keyframes first-anim { + from { column-width: 3em; } + to { column-width: 5em; } + } + @keyframes second-anim { + from { column-width: 40px; } + to { column-width: calc(40px - 2em); } + } + @keyframes third-anim { + from { column-width: 20px; } + to { column-width: inherit; } + } +</style> +</head> +<body> +<div id="container"> + <div id="first" class="paused"></div> + <div id="second" class="paused"></div> + <div id="third" class="paused"></div> +</div> +<script> +'use strict'; +var container = document.getElementById('container'); + +test(() => { + const first = document.getElementById('first'); + assert_equals(getComputedStyle(first).columnWidth, '40px'); + first.style.fontSize = '20px'; + assert_equals(getComputedStyle(first).columnWidth, '80px'); +}, 'column-width responds to font-size changes'); + +test(() => { + const second = document.getElementById('second'); + assert_equals(getComputedStyle(second).columnWidth, '30px'); + second.style.fontSize = '90px'; + assert_equals(getComputedStyle(second).columnWidth, '0px'); +}, 'column-width clamps to 0px'); + +test(() => { + const container = document.getElementById('container'); + const third = document.getElementById('third'); + assert_equals(getComputedStyle(third).columnWidth, '30px'); + container.style.columnWidth = 'auto'; + assert_equals(getComputedStyle(third).columnWidth, 'auto'); +}, 'column-width responds to inherited changes'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-animations/simultaneous-animations-crash.html b/testing/web-platform/tests/css/css-animations/simultaneous-animations-crash.html new file mode 100644 index 0000000000..223741157e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/simultaneous-animations-crash.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html class="test-wait"> +<title>CSS animations test: No crash should occur when an animation ends while other animations continue</title> +<link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org"> +<link rel="help" href="https://crbug.com/1213307"> +<style> + @keyframes animationKeyframes { + from { opacity: 0.1; filter: blur(1px); } + to { opacity: 0.9; filter: blur(5px); } + } +</style> +<div id="longerAnimEl" style="animation: animationKeyframes 64ms;">a</div> +<div id="shorterAnimEl" style="animation: both animationKeyframes 32ms;">b</div> +<script> + longerAnimEl.addEventListener('animationend', () => { + document.documentElement.classList.remove('test-wait'); + }); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-animations/style-animation-parsing.html b/testing/web-platform/tests/css/css-animations/style-animation-parsing.html new file mode 100644 index 0000000000..792fda33ed --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/style-animation-parsing.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Animations: parsing style.animation</title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#animation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="test1"></div> +<div id="test2"></div> +<script> +function testStyle(style1, style2, name) { + assert_equals(style1.animationName, name, "style1.animationName"); + assert_equals(style2.animationName, name, "style2.animationName"); + assert_equals(style1.animation, style2.animation, + "style1 and style2 should have the same animation"); +} + +function testAnimation(input, name) { + var style1 = test1.style; + var style2 = test2.style; + + style1.animation = input; + style2.animation = style1.animation; + testStyle(style1, style2, name); +} + +test(() => { + testAnimation("", ""); +}, "Test animation name being empty."); + +test(() => { + testAnimation("myShorthandAnim", "myShorthandAnim"); +}, "Test a non-conflicting animation name."); + +test(() => { + testAnimation("none", "none"); + testAnimation("forwards", "none"); + testAnimation("none forwards", "forwards"); +}, "Test an animation name that is the same as a possible animation fill-mode."); + +test(() => { + testAnimation("normal", "none"); + testAnimation("reverse", "none"); + testAnimation("normal normal", "normal"); + testAnimation("normal reverse", "reverse"); +}, "Test an animation name that is the same as a possible animation direction."); + +test(() => { + testAnimation("running", "none"); + testAnimation("paused", "none"); + testAnimation("running running", "running"); + testAnimation("running paused", "paused"); +}, "Test an animation name that is the same as a possible running state."); +</script> diff --git a/testing/web-platform/tests/css/css-animations/support/empty-sheet.css b/testing/web-platform/tests/css/css-animations/support/empty-sheet.css new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/support/empty-sheet.css diff --git a/testing/web-platform/tests/css/css-animations/support/testcommon.js b/testing/web-platform/tests/css/css-animations/support/testcommon.js new file mode 100644 index 0000000000..654aaa51c5 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/support/testcommon.js @@ -0,0 +1,261 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Use this variable if you specify duration or some other properties + * for script animation. + * E.g., div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC); + * + * NOTE: Creating animations with short duration may cause intermittent + * failures in asynchronous test. For example, the short duration animation + * might be finished when animation.ready has been fulfilled because of slow + * platforms or busyness of the main thread. + * Setting short duration to cancel its animation does not matter but + * if you don't want to cancel the animation, consider using longer duration. + */ +const MS_PER_SEC = 1000; + +/* The recommended minimum precision to use for time values[1]. + * + * [1] https://drafts.csswg.org/web-animations/#precision-of-time-values + */ +var TIME_PRECISION = 0.0005; // ms + +/* + * Allow implementations to substitute an alternative method for comparing + * times based on their precision requirements. + */ +function assert_times_equal(actual, expected, description) { + assert_approx_equals(actual, expected, TIME_PRECISION * 2, description); +} + +/* + * Compare a time value based on its precision requirements with a fixed value. + */ +function assert_time_equals_literal(actual, expected, description) { + assert_approx_equals(actual, expected, TIME_PRECISION, description); +} + +/* + * Compare two keyframes + */ +function assert_frames_equal(actual, expected, name) { + // TODO: Make this skip the 'composite' member when it is not specified in + // `expected` or when the implementation does not support it. + assert_array_equals( + Object.keys(actual).sort(), + Object.keys(expected).sort(), + `properties on ${name} should match` + ); + + // Iterates sorted keys to ensure stable failures. + for (const prop of Object.keys(actual).sort()) { + if ( + // 'offset' can be null + (prop === 'offset' && typeof actual[prop] === 'number') || + prop === 'computedOffset' + ) { + assert_approx_equals( + actual[prop], + expected[prop], + 0.00001, + "value for '" + prop + "' on " + name + ); + } else { + assert_equals( + actual[prop], + expected[prop], + `value for '${prop}' on ${name} should match` + ); + } + } +} + +/* + * Compare two lists of keyframes + */ +function assert_frame_lists_equal(actual, expected) { + assert_equals( + actual.length, + expected.length, + 'Number of keyframes should match' + ); + + for (let i = 0; i < actual.length; i++) { + assert_frames_equal(actual[i], expected[i], `Keyframe #${i}`); + } +} + +/** + * Appends an element to the document body. + * + * @param t The testharness.js Test object. If provided, this will be used + * to register a cleanup callback to remove the div when the test + * finishes. + * + * @param name A string specifying the element name. + * + * @param attrs A dictionary object with attribute names and values to set on + * the div. + */ +function addElement(t, name, attrs) { + var element = document.createElement(name); + if (attrs) { + for (var attrName in attrs) { + element.setAttribute(attrName, attrs[attrName]); + } + } + document.body.appendChild(element); + if (t && typeof t.add_cleanup === 'function') { + t.add_cleanup(() => element.remove()); + } + return element; +} + +/** + * Appends a div to the document body. + * + * @param t The testharness.js Test object. If provided, this will be used + * to register a cleanup callback to remove the div when the test + * finishes. + * + * @param attrs A dictionary object with attribute names and values to set on + * the div. + */ +function addDiv(t, attrs) { + return addElement(t, "div", attrs); +} + +/** + * Appends a style div to the document head. + * + * @param t The testharness.js Test object. If provided, this will be used + * to register a cleanup callback to remove the style element + * when the test finishes. + * + * @param rules A dictionary object with selector names and rules to set on + * the style sheet. + */ +function addStyle(t, rules) { + var extraStyle = document.createElement('style'); + document.head.appendChild(extraStyle); + if (rules) { + var sheet = extraStyle.sheet; + for (var selector in rules) { + sheet.insertRule(selector + '{' + rules[selector] + '}', + sheet.cssRules.length); + } + } + + if (t && typeof t.add_cleanup === 'function') { + t.add_cleanup(function() { + extraStyle.remove(); + }); + } +} + +/** + * Promise wrapper for requestAnimationFrame. + */ +function waitForFrame() { + return new Promise(function(resolve, reject) { + window.requestAnimationFrame(resolve); + }); +} + +/** + * Waits for a requestAnimationFrame callback in the next refresh driver tick. + */ +function waitForNextFrame() { + const timeAtStart = document.timeline.currentTime; + return new Promise(resolve => { + window.requestAnimationFrame(() => { + if (timeAtStart === document.timeline.currentTime) { + window.requestAnimationFrame(resolve); + } else { + resolve(); + } + }); + }); +} + +/** + * Returns a Promise that is resolved after the given number of consecutive + * animation frames have occured (using requestAnimationFrame callbacks). + * + * @param frameCount The number of animation frames. + * @param onFrame An optional function to be processed in each animation frame. + */ +function waitForAnimationFrames(frameCount, onFrame) { + const timeAtStart = document.timeline.currentTime; + return new Promise(function(resolve, reject) { + function handleFrame() { + if (onFrame && typeof onFrame === 'function') { + onFrame(); + } + if (timeAtStart != document.timeline.currentTime && + --frameCount <= 0) { + resolve(); + } else { + window.requestAnimationFrame(handleFrame); // wait another frame + } + } + window.requestAnimationFrame(handleFrame); + }); +} + +/** + * Timeout function used for tests with EventWatchers when all animation events + * should be received on the next animation frame. If two frames pass before + * receiving the expected events, then we can immediate fail the test. + */ +function fastEventsTimeout() { + return waitForAnimationFrames(2); +}; + +/** + * Timeout function used for tests with EventWatchers. The client agent has no + * strict requirement for how long it takes to resolve the ready promise. Once + * the promise is resolved a secondary timeout promise is armed that may have + * a tight deadline measured in animation frames. + */ +function armTimeoutWhenReady(animation, timeoutPromise) { + return () => { + if (animation.pending) + return animation.ready.then(() => { return timeoutPromise(); }); + else + return timeoutPromise(); + }; +} + +/** + * Wrapper that takes a sequence of N animations and returns: + * + * Promise.all([animations[0].ready, animations[1].ready, ... animations[N-1].ready]); + */ +function waitForAllAnimations(animations) { + return Promise.all(animations.map(animation => animation.ready)); +} + +/** + * Flush the computed style for the given element. This is useful, for example, + * when we are testing a transition and need the initial value of a property + * to be computed so that when we synchronouslyet set it to a different value + * we actually get a transition instead of that being the initial value. + */ +function flushComputedStyle(elem) { + var cs = getComputedStyle(elem); + cs.marginLeft; +} +// Waits for a given animation being ready to restyle. +async function waitForAnimationReadyToRestyle(aAnimation) { + await aAnimation.ready; + // If |aAnimation| begins at the current timeline time, we will not process + // restyling in the initial frame because of aligning with the refresh driver, + // the animation frame in which the ready promise is resolved happens to + // coincide perfectly with the start time of the animation. In this case no + // restyling is needed in the frame so we have to wait one more frame. + if (animationStartsRightNow(aAnimation)) { + await waitForNextFrame(); + } +} diff --git a/testing/web-platform/tests/css/css-animations/svg-transform-animation-ref.html b/testing/web-platform/tests/css/css-animations/svg-transform-animation-ref.html new file mode 100644 index 0000000000..d8666e8a73 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/svg-transform-animation-ref.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<div style="width: 100px; height: 100px; position: relative; top: 100px; left: 100px; + background: green"></div> diff --git a/testing/web-platform/tests/css/css-animations/svg-transform-animation.html b/testing/web-platform/tests/css/css-animations/svg-transform-animation.html new file mode 100644 index 0000000000..b08629c2eb --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/svg-transform-animation.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<title>Transform animation on SVG element with zoom</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-1/"> +<link rel="match" href="svg-transform-animation-ref.html"> +<style> +@keyframes transform { + from {transform: translate(100px, 100px)} + to {transform: translate(100px, 100px)} +} +</style> +<svg width="200" height="200"> + <rect x="100" y="100" width="100" height="100" fill="red"/> + <rect style="animation: transform 2s infinite" x="0" y="0" width="100" height="100" fill="green"/> +</svg> diff --git a/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale-ref.html b/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale-ref.html new file mode 100644 index 0000000000..14b4136626 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale-ref.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<div style="width: 200px; height: 100px; background: blue"></div> +<div style="width: 200px; height: 100px; background: green"></div> diff --git a/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale.html b/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale.html new file mode 100644 index 0000000000..359380d698 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/transform-animation-under-large-scale.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>Transform animation under large scale</title> +<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org"> +<link rel="help" href="https://crbug.com/1153428"> +<link rel="match" href="transform-animation-under-large-scale-ref.html"> +<style> +@keyframes move { + 0% {transform: translateX(-1px);} + 100% {transform: translateX(0);} +} +</style> +<div style="width: 2px; height: 2px; transform: scale(100); transform-origin: 0 0; overflow: hidden"> + <div style="animation: move 1s infinite alternate"> + <div style="width: 4px; height: 1px; background: blue"></div> + <div style="width: 4px; height: 1px; background: green"></div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property-ref.html b/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property-ref.html new file mode 100644 index 0000000000..97110dd54e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property-ref.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<title>Translation animation on important property</title> +<link rel="help" href="https://crbug.com/1324679"> +<style> +#target { + width: 100px; + height: 100px; + background: green; + transform: none !important; +} +</style> +<div id="target"></div> diff --git a/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property.html b/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property.html new file mode 100644 index 0000000000..4f02942764 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/translation-animation-on-important-property.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<title>Translation animation on important property</title> +<link rel="help" href="https://crbug.com/1324679"> +<link rel="match" href="translation-animation-on-important-property-ref.html"> +<style> +@keyframes move { + 0% {transform: translateX(0px);} + 100% {transform: translateX(100px);} +} +#target { + width: 100px; + height: 100px; + background: green; + transform: none !important; + animation: move 10000s cubic-bezier(0, 1, 1, 0) -5000s; +} +</style> +<div id="target"></div> diff --git a/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset-ref.html b/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset-ref.html new file mode 100644 index 0000000000..5fc04d972b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset-ref.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<style> +div { + position: absolute; + top: 11px; + left: 1px; + width: 100px; + height: 100px; + background: green; +} +</style> +<div></div> diff --git a/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset.html b/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset.html new file mode 100644 index 0000000000..5692a08232 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/translation-animation-subpixel-offset.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<title>Translation-only transform animation with subpixel offset</title> +<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org"> +<link rel="help" href="https://crbug.com/1155029"> +<link rel="match" href="translation-animation-subpixel-offset-ref.html"> +<style> +@keyframes move { + 0% {transform: translateY(10px);} + 100% {transform: translateY(10px);} +} +#red { + position: absolute; + top: 11px; + left: 1px; + width: 100px; + height: 100px; + background: red; +} +#container { + position: absolute; + top: 0.4px; + left: 0.6px; +} +#target { + position: relative; + top: 0.4px; + left: 0.6px; + width: 100px; + height: 100px; + background: green; + animation: move 1s infinite alternate; +} +</style> +<div id="red"></div> +<div id="container"> + <div id="target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-animations/webkit-writing-mode-crash.html b/testing/web-platform/tests/css/css-animations/webkit-writing-mode-crash.html new file mode 100644 index 0000000000..8c851a7572 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/webkit-writing-mode-crash.html @@ -0,0 +1,20 @@ +<!doctype html> +<html class="test-wait"> +<link rel="help" href="https://crbug.com/1077501"> +<style> + @keyframes test { + from { -webkit-writing-mode: var(--x) } + to { -webkit-writing-mode: var(--y) } + } + div { + animation: test 1s; + } +</style> +<div id=div></div> +<script> +window.onload = () => { + div.offsetTop; + document.documentElement.classList.remove('test-wait'); +}; +</script> +</html> |