summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/animation-model
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/web-animations/animation-model
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/web-animations/animation-model')
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html135
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/animation-types/spacing-keyframes-shapes.html152
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/animation-types/type-per-property.html1198
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-context.html103
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/keyframe-effects/spacing-keyframes.html391
-rw-r--r--testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html114
6 files changed, 2093 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html b/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
new file mode 100644
index 000000000..e43f26d16
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/discrete-animation.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for discrete animation</title>
+<link rel="help" href="http://w3c.github.io/web-animations/#animatable-as-string-section">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = createDiv(t);
+
+ var anim = div.animate({ fontStyle: [ 'normal', 'italic' ] },
+ { duration: 1000, fill: 'forwards' });
+
+ assert_equals(getComputedStyle(div).fontStyle, 'normal',
+ 'Animation produces \'from\' value at start of interval');
+ anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
+ assert_equals(getComputedStyle(div).fontStyle, 'normal',
+ 'Animation produces \'from\' value just before the middle of'
+ + ' the interval');
+ anim.currentTime++;
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'to\' value at exact middle of'
+ + ' the interval');
+ anim.finish();
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'to\' value during forwards fill');
+}, 'Test animating discrete values');
+
+test(function(t) {
+ var div = createDiv(t);
+ var originalHeight = getComputedStyle(div).height;
+
+ var anim = div.animate({ height: [ 'auto', '200px' ] },
+ { duration: 1000, fill: 'forwards' });
+
+ assert_equals(getComputedStyle(div).height, originalHeight,
+ 'Animation produces \'from\' value at start of interval');
+ anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
+ assert_equals(getComputedStyle(div).height, originalHeight,
+ 'Animation produces \'from\' value just before the middle of'
+ + ' the interval');
+ anim.currentTime++;
+ assert_equals(getComputedStyle(div).height, '200px',
+ 'Animation produces \'to\' value at exact middle of'
+ + ' the interval');
+ anim.finish();
+ assert_equals(getComputedStyle(div).height, '200px',
+ 'Animation produces \'to\' value during forwards fill');
+}, 'Test discrete animation is used when interpolation fails');
+
+test(function(t) {
+ var div = createDiv(t);
+ var originalHeight = getComputedStyle(div).height;
+
+ var anim = div.animate({ height: [ 'auto',
+ '200px',
+ '300px',
+ 'auto',
+ '400px' ] },
+ { duration: 1000, fill: 'forwards' });
+
+ // There are five values, so there are four pairs to try to interpolate.
+ // We test at the middle of each pair.
+ assert_equals(getComputedStyle(div).height, originalHeight,
+ 'Animation produces \'from\' value at start of interval');
+ anim.currentTime = 125;
+ assert_equals(getComputedStyle(div).height, '200px',
+ 'First non-interpolable pair uses discrete interpolation');
+ anim.currentTime += 250;
+ assert_equals(getComputedStyle(div).height, '250px',
+ 'Second interpolable pair uses linear interpolation');
+ anim.currentTime += 250;
+ assert_equals(getComputedStyle(div).height, originalHeight,
+ 'Third non-interpolable pair uses discrete interpolation');
+ anim.currentTime += 250;
+ assert_equals(getComputedStyle(div).height, '400px',
+ 'Fourth non-interpolable pair uses discrete interpolation');
+}, 'Test discrete animation is used only for pairs of values that cannot'
+ + ' be interpolated');
+
+test(function(t) {
+ var div = createDiv(t);
+ var originalHeight = getComputedStyle(div).height;
+
+ // Easing: http://cubic-bezier.com/#.68,0,1,.01
+ // With this curve, we don't reach the 50% point until about 95% of
+ // the time has expired.
+ var anim = div.animate({ fontStyle: [ 'italic', 'oblique' ] },
+ { duration: 1000, fill: 'forwards',
+ easing: 'cubic-bezier(0.68,0,1,0.01)' });
+
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'from\' value at start of interval');
+ anim.currentTime = 940;
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'from\' value at 94% of the iteration'
+ + ' time');
+ anim.currentTime = 960;
+ assert_equals(getComputedStyle(div).fontStyle, 'oblique',
+ 'Animation produces \'to\' value at 96% of the iteration'
+ + ' time');
+}, 'Test the 50% switch point for discrete animation is based on the'
+ + ' effect easing');
+
+test(function(t) {
+ var div = createDiv(t);
+ var originalHeight = getComputedStyle(div).height;
+
+ // Easing: http://cubic-bezier.com/#.68,0,1,.01
+ // With this curve, we don't reach the 50% point until about 95% of
+ // the time has expired.
+ var anim = div.animate([ { fontStyle: 'italic',
+ easing: 'cubic-bezier(0.68,0,1,0.01)' },
+ { fontStyle: 'oblique' } ],
+ { duration: 1000, fill: 'forwards' });
+
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'from\' value at start of interval');
+ anim.currentTime = 940;
+ assert_equals(getComputedStyle(div).fontStyle, 'italic',
+ 'Animation produces \'from\' value at 94% of the iteration'
+ + ' time');
+ anim.currentTime = 960;
+ assert_equals(getComputedStyle(div).fontStyle, 'oblique',
+ 'Animation produces \'to\' value at 96% of the iteration'
+ + ' time');
+}, 'Test the 50% switch point for discrete animation is based on the'
+ + ' keyframe easing');
+
+</script>
diff --git a/testing/web-platform/tests/web-animations/animation-model/animation-types/spacing-keyframes-shapes.html b/testing/web-platform/tests/web-animations/animation-model/animation-types/spacing-keyframes-shapes.html
new file mode 100644
index 000000000..9f7cfaea1
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/spacing-keyframes-shapes.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Keyframe spacing tests on shapes</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#spacing-keyframes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+"use strict";
+
+// Help function for testing the computed offsets by the distance array.
+function assert_animation_offsets(anim, dist) {
+ const frames = anim.effect.getKeyframes();
+ const cumDist = dist.reduce( (prev, curr) => {
+ prev.push(prev.length == 0 ? curr : curr + prev[prev.length - 1]);
+ return prev;
+ }, []);
+
+ const total = cumDist[cumDist.length - 1];
+ for (var i = 0; i < frames.length; ++i) {
+ assert_equals(frames[i].computedOffset, cumDist[i] / total,
+ 'computedOffset of frame ' + i);
+ }
+}
+
+test(function(t) {
+ var anim =
+ createDiv(t).animate([ { clipPath: 'circle(20px)' },
+ { clipPath: 'ellipse(10px 20px)' },
+ { clipPath: 'polygon(50px 0px, 100px 50px, ' +
+ ' 50px 100px, 0px 50px)' },
+ { clipPath: 'inset(20px round 10px)' } ],
+ { spacing: 'paced(clip-path)' });
+
+ const frames = anim.effect.getKeyframes();
+ const slots = frames.length - 1;
+ for (var i = 0; i < frames.length; ++i) {
+ assert_equals(frames[i].computedOffset, i / slots,
+ 'computedOffset of frame ' + i);
+ }
+}, 'Test falling back to distribute spacing when using different basic shapes');
+
+test(function(t) {
+ var anim =
+ createDiv(t).animate([ { clipPath: 'circle(10px)' },
+ { clipPath: 'circle(20px) content-box' },
+ { clipPath: 'circle(70px)' },
+ { clipPath: 'circle(10px) padding-box' } ],
+ { spacing: 'paced(clip-path)' });
+
+ const frames = anim.effect.getKeyframes();
+ const slots = frames.length - 1;
+ for (var i = 0; i < frames.length; ++i) {
+ assert_equals(frames[i].computedOffset, i / slots,
+ 'computedOffset of frame ' + i);
+ }
+}, 'Test falling back to distribute spacing when using different ' +
+ 'reference boxes');
+
+test(function(t) {
+ // 1st: circle(calc(20px) at calc(20px + 0%) calc(10px + 0%))
+ // 2nd: circle(calc(50px) at calc(10px + 0%) calc(10px + 0%))
+ // 3rd: circle(70px at calc(10px + 0%) calc(50px + 0%))
+ // 4th: circle(10px at calc(50px + 0%) calc(30px + 0%))
+ var anim =
+ createDiv(t).animate([ { clipPath: 'circle(calc(10px + 10px) ' +
+ ' at 20px 10px)' },
+ { clipPath: 'circle(calc(20px + 30px) ' +
+ ' at 10px 10px)' },
+ { clipPath: 'circle(70px at 10px 50px)' },
+ { clipPath: 'circle(10px at 50px 30px)' } ],
+ { spacing: 'paced(clip-path)' });
+
+ var dist = [ 0,
+ Math.sqrt(30 * 30 + 10 * 10),
+ Math.sqrt(20 * 20 + 40 * 40),
+ Math.sqrt(60 * 60 + 40 * 40 + 20 * 20) ];
+ assert_animation_offsets(anim, dist);
+}, 'Test spacing on circle' );
+
+test(function(t) {
+ // 1st: ellipse(20px calc(20px) at calc(0px + 50%) calc(0px + 50%))
+ // 2nd: ellipse(20px calc(50px) at calc(0px + 50%) calc(0px + 50%))
+ // 3rd: ellipse(30px 70px at calc(0px + 50%) calc(0px + 50%))
+ // 4th: ellipse(30px 10px at calc(0px + 50%) calc(0px + 50%))
+ var anim =
+ createDiv(t).animate([ { clipPath: 'ellipse(20px calc(10px + 10px))' },
+ { clipPath: 'ellipse(20px calc(20px + 30px))' },
+ { clipPath: 'ellipse(30px 70px)' },
+ { clipPath: 'ellipse(30px 10px)' } ],
+ { spacing: 'paced(clip-path)' });
+
+ var dist = [ 0,
+ Math.sqrt(30 * 30),
+ Math.sqrt(10 * 10 + 20 * 20),
+ Math.sqrt(60 * 60) ];
+ assert_animation_offsets(anim, dist);
+}, 'Test spacing on ellipse' );
+
+test(function(t) {
+ // 1st: polygon(nonzero, 50px 0px, 100px 50px, 50px 100px, 0px 50px)
+ // 2nd: polygon(nonzero, 40px 0px, 100px 70px, 10px 100px, 0px 70px)
+ // 3rd: polygon(nonzero, 100px 0px, 100px 100px, 10px 80px, 0px 50px)
+ // 4th: polygon(nonzero, 100px 100px, -10px 100px, 20px 80px, 20px 50px)
+ var anim =
+ createDiv(t).animate([ { clipPath: 'polygon(50px 0px, 100px 50px, ' +
+ ' 50px 100px, 0px 50px)' },
+ { clipPath: 'polygon(40px 0px, 100px 70px, ' +
+ ' 10px 100px, 0px 70px)' },
+ { clipPath: 'polygon(100px 0px, 100px 100px, ' +
+ ' 10px 80px, 0px 50px)' },
+ { clipPath: 'polygon(100px 100px, -10px 100px, ' +
+ ' 20px 80px, 20px 50px)' } ],
+ { spacing: 'paced(clip-path)' });
+
+ var dist = [ 0,
+ Math.sqrt(10 * 10 + 20 * 20 + 40 * 40 + 20 * 20),
+ Math.sqrt(60 * 60 + 30 * 30 + 20 * 20 + 20 * 20),
+ Math.sqrt(100 * 100 + 110 * 110 + 10 * 10 + 20 * 20) ];
+ assert_animation_offsets(anim, dist);
+}, 'Test spacing on polygon' );
+
+test(function(t) {
+ // Note: Rounding corners are 4 CSS pair values and
+ // each pair has x & y components.
+ // 1st: inset(5px 5px 5px 5px round 40px 30px 20px 5px / 40px 30px 20px 5px)
+ // 2nd: inset(10px 5px 10px 5px round 50px 60px / 50px 60px)
+ // 3rd: inset(40px 40px 40px 40px round 10px / 10px)
+ // 4th: inset(30px 40px 30px 40px round 20px / 20px)
+ var anim =
+ createDiv(t).animate([ { clipPath: 'inset(5px 5px 5px 5px ' +
+ ' round 40px 30px 20px 5px)' },
+ { clipPath: 'inset(10px 5px round 50px 60px)' },
+ { clipPath: 'inset(40px 40px round 10px)' },
+ { clipPath: 'inset(30px 40px round 20px)' } ],
+ { spacing: 'paced(clip-path)' });
+
+ var dist = [ 0,
+ Math.sqrt(5 * 5 * 2 + (50 - 40) * (50 - 40) * 2 +
+ (60 - 30) * (60 - 30) * 2 +
+ (50 - 20) * (50 - 20) * 2 +
+ (60 - 5) * (60 - 5) * 2),
+ Math.sqrt(30 * 30 * 2 + 35 * 35 * 2 + (50 - 10) * (50 - 10) * 4 +
+ (60 - 10) * (60 - 10) * 4),
+ Math.sqrt(10 * 10 * 2 + (20 - 10) * (20 - 10) * 8) ];
+ assert_animation_offsets(anim, dist);
+}, 'Test spacing on inset' );
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/animation-model/animation-types/type-per-property.html b/testing/web-platform/tests/web-animations/animation-model/animation-types/type-per-property.html
new file mode 100644
index 000000000..9e41b753e
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/type-per-property.html
@@ -0,0 +1,1198 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for animation types</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#animation-types">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<style>
+html {
+ font-size: 10px;
+}
+</style>
+<body>
+<div id="log"></div>
+<script>
+"use strict";
+
+var gCSSProperties = {
+ "align-content": {
+ // https://drafts.csswg.org/css-align/#propdef-align-content
+ tests: [
+ discrete("flex-start", "flex-end")
+ ]
+ },
+ "align-items": {
+ // https://drafts.csswg.org/css-align/#propdef-align-items
+ tests: [
+ discrete("flex-start", "flex-end")
+ ]
+ },
+ "align-self": {
+ // https://drafts.csswg.org/css-align/#propdef-align-self
+ tests: [
+ discrete("flex-start", "flex-end")
+ ]
+ },
+ "backface-visibility": {
+ // https://drafts.csswg.org/css-transforms/#propdef-backface-visibility
+ "tests": [
+ discrete("visible", "hidden")
+ ]
+ },
+ "background-attachment": {
+ // https://drafts.csswg.org/css-backgrounds-3/#background-attachment
+ "tests": [
+ discrete("fixed", "local")
+ ]
+ },
+ "background-blend-mode": {
+ // https://drafts.fxtf.org/compositing-1/#propdef-background-blend-mode
+ "tests": [
+ discrete("multiply", "screen")
+ ]
+ },
+ "background-clip": {
+ // https://drafts.csswg.org/css-backgrounds-3/#background-clip
+ "tests": [
+ discrete("padding-box", "content-box")
+ ]
+ },
+ "background-image": {
+ // https://drafts.csswg.org/css-backgrounds-3/#background-image
+ "tests": [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "background-origin": {
+ // https://drafts.csswg.org/css-backgrounds-3/#background-origin
+ "tests": [
+ discrete("padding-box", "content-box")
+ ]
+ },
+ "background-repeat": {
+ // https://drafts.csswg.org/css-backgrounds-3/#background-repeat
+ "tests": [
+ discrete("space", "round")
+ ]
+ },
+ "border-bottom-style": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-bottom-style
+ "tests": [
+ discrete("dotted", "solid")
+ ]
+ },
+ "border-collapse": {
+ // https://drafts.csswg.org/css-tables/#propdef-border-collapse
+ "tests": [
+ discrete("collapse", "separate")
+ ]
+ },
+ "border-image-outset": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-image-outset
+ "tests": [
+ discrete("1 1 1 1", "5 5 5 5")
+ ]
+ },
+ "border-image-repeat": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-image-repeat
+ "tests": [
+ discrete("stretch stretch", "repeat repeat")
+ ]
+ },
+ "border-image-slice": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-image-slice
+ "tests": [
+ discrete("1 1 1 1", "5 5 5 5")
+ ]
+ },
+ "border-image-source": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-image-source
+ "tests": [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "border-image-width": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-image-width
+ "tests": [
+ discrete("1 1 1 1", "5 5 5 5")
+ ]
+ },
+ "border-left-style": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-left-style
+ "tests": [
+ discrete("dotted", "solid")
+ ]
+ },
+ "border-right-style": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-right-style
+ "tests": [
+ discrete("dotted", "solid")
+ ]
+ },
+ "border-top-style": {
+ // https://drafts.csswg.org/css-backgrounds-3/#border-top-style
+ "tests": [
+ discrete("dotted", "solid")
+ ]
+ },
+ "box-decoration-break": {
+ // https://drafts.csswg.org/css-break/#propdef-box-decoration-break
+ "tests": [
+ discrete("slice", "clone")
+ ]
+ },
+ "box-sizing": {
+ // https://drafts.csswg.org/css-ui-4/#box-sizing
+ "tests": [
+ discrete("content-box", "border-box")
+ ]
+ },
+ "caption-side": {
+ // https://drafts.csswg.org/css-tables/#propdef-caption-side
+ "tests": [
+ discrete("top", "bottom")
+ ]
+ },
+ "clear": {
+ // https://drafts.csswg.org/css-page-floats/#propdef-clear
+ "tests": [
+ discrete("left", "right")
+ ]
+ },
+ "clip-rule": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-clip-rule
+ tests: [
+ discrete("evenodd", "nonzero")
+ ]
+ },
+ "color-adjust": {
+ // https://drafts.csswg.org/css-color-4/#color-adjust
+ tests: [
+ discrete("economy", "exact")
+ ]
+ },
+ "color-interpolation": {
+ // https://svgwg.org/svg2-draft/painting.html#ColorInterpolationProperty
+ tests: [
+ discrete("linearRGB", "auto")
+ ]
+ },
+ "color-interpolation-filters": {
+ // https://drafts.fxtf.org/filters-1/#propdef-color-interpolation-filters
+ tests: [
+ discrete("sRGB", "linearRGB")
+ ]
+ },
+ "column-fill": {
+ // https://drafts.csswg.org/css-multicol/#propdef-column-fill
+ tests: [
+ discrete("auto", "balance")
+ ]
+ },
+ "column-rule-style": {
+ // https://drafts.csswg.org/css-multicol/#propdef-column-rule-style
+ tests: [
+ discrete("none", "dotted")
+ ]
+ },
+ "contain": {
+ // https://drafts.csswg.org/css-containment/#propdef-contain
+ tests: [
+ discrete("strict", "none")
+ ]
+ },
+ "content": {
+ // https://drafts.csswg.org/css-content-3/#propdef-content
+ tests: [
+ discrete("\"a\"", "\"b\"")
+ ],
+ tagName: "::before"
+ },
+ "counter-increment": {
+ // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
+ tests: [
+ discrete("ident-1 1", "ident-2 2")
+ ]
+ },
+ "counter-reset": {
+ // https://drafts.csswg.org/css-lists-3/#propdef-counter-reset
+ tests: [
+ discrete("ident-1 1", "ident-2 2")
+ ]
+ },
+ "cursor": {
+ // https://drafts.csswg.org/css2/ui.html#propdef-cursor
+ tests: [
+ discrete("pointer", "wait")
+ ]
+ },
+ "direction": {
+ // https://drafts.csswg.org/css-writing-modes-3/#propdef-direction
+ tests: [
+ discrete("ltr", "rtl")
+ ]
+ },
+ "dominant-baseline": {
+ // https://drafts.csswg.org/css-inline/#propdef-dominant-baseline
+ tests: [
+ discrete("ideographic", "alphabetic")
+ ]
+ },
+ "empty-cells": {
+ // https://drafts.csswg.org/css-tables/#propdef-empty-cells
+ tests: [
+ discrete("show", "hide")
+ ]
+ },
+ "fill-rule": {
+ // https://svgwg.org/svg2-draft/painting.html#FillRuleProperty
+ tests: [
+ discrete("evenodd", "nonzero")
+ ]
+ },
+ "flex-basis": {
+ // https://drafts.csswg.org/css-flexbox/#propdef-flex-basis
+ tests: [
+ lengthPercentageOrCalc(),
+ discrete("auto", "10px")
+ ]
+ },
+ "flex-direction": {
+ // https://drafts.csswg.org/css-flexbox/#propdef-flex-direction
+ tests: [
+ discrete("row", "row-reverse")
+ ]
+ },
+ "flex-grow": {
+ // https://drafts.csswg.org/css-flexbox/#flex-grow-property
+ tests: [
+ positiveNumber()
+ ]
+ },
+ "flex-shrink": {
+ // https://drafts.csswg.org/css-flexbox/#propdef-flex-shrink
+ tests: [
+ positiveNumber()
+ ]
+ },
+ "flex-wrap": {
+ // https://drafts.csswg.org/css-flexbox/#propdef-flex-wrap
+ tests: [
+ discrete("nowrap", "wrap")
+ ]
+ },
+ "font-style": {
+ // https://drafts.csswg.org/css-fonts/#propdef-font-style
+ tests: [
+ discrete("italic", "oblique")
+ ]
+ },
+ "float": {
+ // https://drafts.csswg.org/css-page-floats/#propdef-float
+ tests: [
+ discrete("left", "right")
+ ]
+ },
+ "font-family": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-family
+ tests: [
+ discrete("helvetica", "verdana")
+ ]
+ },
+ "font-feature-settings": {
+ // https://drafts.csswg.org/css-fonts/#descdef-font-feature-settings
+ tests: [
+ discrete("\"liga\" 5", "normal")
+ ]
+ },
+ "font-kerning": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-kerning
+ tests: [
+ discrete("auto", "normal")
+ ]
+ },
+ "font-language-override": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override
+ tests: [
+ discrete("\"eng\"", "normal")
+ ]
+ },
+ "font-style": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-style
+ tests: [
+ discrete("italic", "oblique")
+ ]
+ },
+ "font-synthesis": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-synthesis
+ tests: [
+ discrete("none", "weight style")
+ ]
+ },
+ "font-variant-alternates": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-alternates
+ tests: [
+ discrete("swash(unknown)", "stylistic(unknown)")
+ ]
+ },
+ "font-variant-caps": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-caps
+ tests: [
+ discrete("small-caps", "unicase")
+ ]
+ },
+ "font-variant-east-asian": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-east-asian
+ tests: [
+ discrete("full-width", "proportional-width")
+ ]
+ },
+ "font-variant-ligatures": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-ligatures
+ tests: [
+ discrete("common-ligatures", "no-common-ligatures")
+ ]
+ },
+ "font-variant-numeric": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-numeric
+ tests: [
+ discrete("lining-nums", "oldstyle-nums")
+ ]
+ },
+ "font-variant-position": {
+ // https://drafts.csswg.org/css-fonts-3/#propdef-font-variant-position
+ tests: [
+ discrete("sub", "super")
+ ]
+ },
+ "grid-auto-columns": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-auto-columns
+ tests: [
+ discrete("1px", "5px")
+ ]
+ },
+ "grid-auto-flow": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow
+ tests: [
+ discrete("row", "column")
+ ]
+ },
+ "grid-auto-rows": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-auto-rows
+ tests: [
+ discrete("1px", "5px")
+ ]
+ },
+ "grid-column-end": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-column-end
+ tests: [
+ discrete("1", "5")
+ ]
+ },
+ "grid-column-start": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-column-start
+ tests: [
+ discrete("1", "5")
+ ]
+ },
+ "grid-row-end": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-row-end
+ tests: [
+ discrete("1", "5")
+ ]
+ },
+ "grid-row-start": {
+ // https://drafts.csswg.org/css-grid/#propdef-grid-row-start
+ tests: [
+ discrete("1", "5")
+ ]
+ },
+ "grid-template-areas": {
+ // https://drafts.csswg.org/css-template/#grid-template-areas
+ tests: [
+ discrete("\". . a b\" \". .a b\"", "none")
+ ]
+ },
+ "grid-template-columns": {
+ // https://drafts.csswg.org/css-template/#grid-template-columns
+ tests: [
+ discrete("1px", "5px")
+ ]
+ },
+ "grid-template-rows": {
+ // https://drafts.csswg.org/css-template/#grid-template-rows
+ tests: [
+ discrete("1px", "5px")
+ ]
+ },
+ "hyphens": {
+ // https://drafts.csswg.org/css-text-3/#propdef-hyphens
+ tests: [
+ discrete("manual", "auto")
+ ]
+ },
+ "image-orientation": {
+ // https://drafts.csswg.org/css-images-3/#propdef-image-orientation
+ tests: [
+ discrete("0deg", "90deg")
+ ]
+ },
+ "ime-mode": {
+ // https://drafts.csswg.org/css-ui/#input-method-editor
+ tests: [
+ discrete("disabled", "auto")
+ ]
+ },
+ "initial-letter": {
+ // https://drafts.csswg.org/css-inline/#propdef-initial-letter
+ tests: [
+ discrete("1 2", "3 4")
+ ]
+ },
+ "isolation": {
+ // https://drafts.fxtf.org/compositing-1/#propdef-isolation
+ tests: [
+ discrete("auto", "isolate")
+ ]
+ },
+ "justify-content": {
+ // https://drafts.csswg.org/css-align/#propdef-justify-content
+ tests: [
+ discrete("baseline", "last baseline")
+ ]
+ },
+ "justify-items": {
+ // https://drafts.csswg.org/css-align/#propdef-justify-items
+ tests: [
+ discrete("baseline", "last baseline")
+ ]
+ },
+ "justify-self": {
+ // https://drafts.csswg.org/css-align/#propdef-justify-self
+ tests: [
+ discrete("baseline", "last baseline")
+ ]
+ },
+ "list-style-image": {
+ // https://drafts.csswg.org/css-lists-3/#propdef-list-style-image
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "list-style-position": {
+ // https://drafts.csswg.org/css-lists-3/#propdef-list-style-position
+ tests: [
+ discrete("inside", "outside")
+ ]
+ },
+ "list-style-type": {
+ // https://drafts.csswg.org/css-lists-3/#propdef-list-style-type
+ tests: [
+ discrete("circle", "square")
+ ]
+ },
+ "marker-end": {
+ // https://svgwg.org/specs/markers/#MarkerEndProperty
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "marker-mid": {
+ // https://svgwg.org/specs/markers/#MarkerMidProperty
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "marker-start": {
+ // https://svgwg.org/specs/markers/#MarkerStartProperty
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "mask": {
+ // https://drafts.fxtf.org/css-masking-1/#the-mask
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "mask-clip": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-clip
+ tests: [
+ discrete("content-box", "border-box")
+ ]
+ },
+ "mask-composite": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-composite
+ tests: [
+ discrete("add", "subtract")
+ ]
+ },
+ "mask-image": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-image
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "mask-mode": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-mode
+ tests: [
+ discrete("alpha", "luminance")
+ ]
+ },
+ "mask-origin": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-origin
+ tests: [
+ discrete("content-box", "border-box")
+ ]
+ },
+ "mask-repeat": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-repeat
+ tests: [
+ discrete("space", "round")
+ ]
+ },
+ "mask-type": {
+ // https://drafts.fxtf.org/css-masking-1/#propdef-mask-type
+ tests: [
+ discrete("alpha", "luminance")
+ ]
+ },
+ "mix-blend-mode": {
+ // https://drafts.fxtf.org/compositing-1/#propdef-mix-blend-mode
+ tests: [
+ discrete("multiply", "screen")
+ ]
+ },
+ "object-fit": {
+ // https://drafts.csswg.org/css-images-3/#propdef-object-fit
+ tests: [
+ discrete("fill", "contain")
+ ]
+ },
+ "order": {
+ // https://drafts.csswg.org/css-flexbox/#propdef-order
+ tests: [
+ integer()
+ ]
+ },
+ "outline-style": {
+ // https://drafts.csswg.org/css-ui/#propdef-outline-style
+ tests: [
+ discrete("none", "dotted")
+ ]
+ },
+ "overflow-clip-box": {
+ // https://developer.mozilla.org/en/docs/Web/CSS/overflow-clip-box
+ tests: [
+ discrete("padding-box", "content-box")
+ ]
+ },
+ "overflow-wrap": {
+ // https://drafts.csswg.org/css-text-3/#propdef-overflow-wrap
+ tests: [
+ discrete("normal", "break-word")
+ ]
+ },
+ "overflow-x": {
+ // https://drafts.csswg.org/css-overflow-3/#propdef-overflow-x
+ tests: [
+ discrete("visible", "hidden")
+ ]
+ },
+ "overflow-y": {
+ // https://drafts.csswg.org/css-overflow-3/#propdef-overflow-y
+ tests: [
+ discrete("visible", "hidden")
+ ]
+ },
+ "page-break-after": {
+ // https://drafts.csswg.org/css-break-3/#propdef-break-after
+ tests: [
+ discrete("always", "auto")
+ ]
+ },
+ "page-break-before": {
+ // https://drafts.csswg.org/css-break-3/#propdef-break-before
+ tests: [
+ discrete("always", "auto")
+ ]
+ },
+ "page-break-inside": {
+ // https://drafts.csswg.org/css-break-3/#propdef-break-inside
+ tests: [
+ discrete("auto", "avoid")
+ ]
+ },
+ "paint-order": {
+ // https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty
+ tests: [
+ discrete("fill", "stroke")
+ ]
+ },
+ "pointer-events": {
+ // https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty
+ tests: [
+ discrete("fill", "none")
+ ]
+ },
+ "position": {
+ // https://drafts.csswg.org/css-position/#propdef-position
+ tests: [
+ discrete("absolute", "fixed")
+ ]
+ },
+ "quotes": {
+ // https://drafts.csswg.org/css-content-3/#propdef-quotes
+ tests: [
+ discrete("\"“\" \"”\" \"‘\" \"’\"", "\"‘\" \"’\" \"“\" \"”\"")
+ ]
+ },
+ "resize": {
+ // https://drafts.csswg.org/css-ui/#propdef-resize
+ tests: [
+ discrete("both", "horizontal")
+ ]
+ },
+ "ruby-align": {
+ // https://drafts.csswg.org/css-ruby-1/#propdef-ruby-align
+ tests: [
+ discrete("start", "center")
+ ]
+ },
+ "ruby-position": {
+ // https://drafts.csswg.org/css-ruby-1/#propdef-ruby-position
+ tests: [
+ discrete("under", "over")
+ ],
+ tagName: "ruby"
+ },
+ "scroll-behavior": {
+ // https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior
+ tests: [
+ discrete("auto", "smooth")
+ ]
+ },
+ "scroll-snap-type-x": {
+ // https://developer.mozilla.org/en/docs/Web/CSS/scroll-snap-type-x
+ tests: [
+ discrete("mandatory", "proximity")
+ ]
+ },
+ "scroll-snap-type-y": {
+ // https://developer.mozilla.org/en/docs/Web/CSS/scroll-snap-type-y
+ tests: [
+ discrete("mandatory", "proximity")
+ ]
+ },
+ "shape-outside": {
+ // http://dev.w3.org/csswg/css-shapes/#propdef-shape-outside
+ tests: [
+ discrete("url(\"http://localhost/test-1\")", "url(\"http://localhost/test-2\")")
+ ]
+ },
+ "shape-rendering": {
+ // https://svgwg.org/svg2-draft/painting.html#ShapeRenderingProperty
+ tests: [
+ discrete("optimizeSpeed", "crispEdges")
+ ]
+ },
+ "stroke-linecap": {
+ // https://svgwg.org/svg2-draft/painting.html#StrokeLinecapProperty
+ tests: [
+ discrete("round", "square")
+ ]
+ },
+ "stroke-linejoin": {
+ // https://svgwg.org/svg2-draft/painting.html#StrokeLinejoinProperty
+ tests: [
+ discrete("round", "miter")
+ ],
+ tagName: "rect"
+ },
+ "table-layout": {
+ // https://drafts.csswg.org/css-tables/#propdef-table-layout
+ tests: [
+ discrete("auto", "fixed")
+ ]
+ },
+ "text-align": {
+ // https://drafts.csswg.org/css-text-3/#propdef-text-align
+ tests: [
+ discrete("start", "end")
+ ]
+ },
+ "text-align-last": {
+ // https://drafts.csswg.org/css-text-3/#propdef-text-align-last
+ tests: [
+ discrete("start", "end")
+ ]
+ },
+ "text-anchor": {
+ // https://svgwg.org/svg2-draft/text.html#TextAnchorProperty
+ tests: [
+ discrete("middle", "end")
+ ]
+ },
+ "text-combine-upright": {
+ // https://drafts.csswg.org/css-writing-modes-3/#propdef-text-combine-upright
+ tests: [
+ discrete("all", "none")
+ ]
+ },
+ "text-decoration-line": {
+ // https://drafts.csswg.org/css-text-decor-3/#propdef-text-decoration-line
+ tests: [
+ discrete("underline", "overline")
+ ]
+ },
+ "text-decoration-style": {
+ // http://dev.w3.org/csswg/css-text-decor-3/#propdef-text-decoration-style
+ tests: [
+ discrete("solid", "dotted")
+ ]
+ },
+ "text-emphasis-position": {
+ // http://dev.w3.org/csswg/css-text-decor-3/#propdef-text-emphasis-position
+ tests: [
+ discrete("over right", "under left")
+ ]
+ },
+ "text-emphasis-style": {
+ // http://dev.w3.org/csswg/css-text-decor-3/#propdef-text-emphasis-style
+ tests: [
+ discrete("filled circle", "open dot")
+ ]
+ },
+ "text-orientation": {
+ // https://drafts.csswg.org/css-writing-modes-3/#propdef-text-orientation
+ tests: [
+ discrete("upright", "sideways")
+ ]
+ },
+ "text-overflow": {
+ // https://drafts.csswg.org/css-ui/#propdef-text-overflow
+ tests: [
+ discrete("clip", "ellipsis")
+ ]
+ },
+ "text-rendering": {
+ // https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty
+ tests: [
+ discrete("optimizeSpeed", "optimizeLegibility")
+ ]
+ },
+ "text-transform": {
+ // https://drafts.csswg.org/css-text-3/#propdef-text-transform
+ tests: [
+ discrete("capitalize", "uppercase")
+ ]
+ },
+ "touch-action": {
+ // https://w3c.github.io/pointerevents/#the-touch-action-css-property
+ tests: [
+ discrete("auto", "none")
+ ]
+ },
+ "transform-box": {
+ // https://drafts.csswg.org/css-transforms/#propdef-transform-box
+ tests: [
+ discrete("fill-box", "border-box")
+ ]
+ },
+ "transform-style": {
+ // https://drafts.csswg.org/css-transforms/#propdef-transform-style
+ tests: [
+ discrete("flat", "preserve-3d")
+ ]
+ },
+ "unicode-bidi": {
+ // https://drafts.csswg.org/css-writing-modes-3/#propdef-unicode-bidi
+ tests: [
+ discrete("embed", "bidi-override")
+ ]
+ },
+ "vector-effect": {
+ // https://svgwg.org/svg2-draft/coords.html#VectorEffectProperty
+ tests: [
+ discrete("none", "non-scaling-stroke")
+ ]
+ },
+ "visibility": {
+ // https://drafts.csswg.org/css2/visufx.html#propdef-visibility
+ tests: [
+ visibility()
+ ]
+ },
+ "white-space": {
+ // https://drafts.csswg.org/css-text-4/#propdef-white-space
+ tests: [
+ discrete("pre", "nowrap")
+ ]
+ },
+ "word-break": {
+ // https://drafts.csswg.org/css-text-3/#propdef-word-break
+ tests: [
+ discrete("keep-all", "break-all")
+ ]
+ },
+ "will-change": {
+ // http://dev.w3.org/csswg/css-will-change/#propdef-will-change
+ tests: [
+ discrete("scroll-position", "contents")
+ ]
+ },
+ "writing-mode": {
+ // https://drafts.csswg.org/css-writing-modes-3/#propdef-writing-mode
+ tests: [
+ discrete("vertical-rl", "sideways-rl")
+ ]
+ },
+}
+
+for (var property in gCSSProperties) {
+ if (!isSupported(property)) {
+ continue;
+ }
+ var testData = gCSSProperties[property];
+ testData.tests.forEach(function(testFunction) {
+ testFunction(property, testData);
+ });
+}
+
+function discrete(from, to) {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = [from, to];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: from.toLowerCase() },
+ { time: 499, expected: from.toLowerCase() },
+ { time: 500, expected: to.toLowerCase() },
+ { time: 1000, expected: to.toLowerCase() }]);
+ }, property + " uses discrete animation when animating between '"
+ + from + "' and '" + to + "' with linear easing");
+
+ test(function(t) {
+ // Easing: http://cubic-bezier.com/#.68,0,1,.01
+ // With this curve, we don't reach the 50% point until about 95% of
+ // the time has expired.
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = [from, to];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both",
+ easing: "cubic-bezier(0.68,0,1,0.01)" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: from.toLowerCase() },
+ { time: 940, expected: from.toLowerCase() },
+ { time: 960, expected: to.toLowerCase() }]);
+ }, property + " uses discrete animation when animating between '"
+ + from + "' and '" + to + "' with effect easing");
+
+ test(function(t) {
+ // Easing: http://cubic-bezier.com/#.68,0,1,.01
+ // With this curve, we don't reach the 50% point until about 95% of
+ // the time has expired.
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = [from, to];
+ keyframes.easing = "cubic-bezier(0.68,0,1,0.01)";
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: from.toLowerCase() },
+ { time: 940, expected: from.toLowerCase() },
+ { time: 960, expected: to.toLowerCase() }]);
+ }, property + " uses discrete animation when animating between '"
+ + from + "' and '" + to + "' with keyframe easing");
+ }
+}
+
+function length() {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["10px", "50px"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10px" },
+ { time: 500, expected: "30px" },
+ { time: 1000, expected: "50px" }]);
+ }, property + " supports animating as a length");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["1rem", "5rem"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10px" },
+ { time: 500, expected: "30px" },
+ { time: 1000, expected: "50px" }]);
+ }, property + " supports animating as a length of rem");
+ }
+}
+
+function percentage() {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["10%", "50%"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10%" },
+ { time: 500, expected: "30%" },
+ { time: 1000, expected: "50%" }]);
+ }, property + " supports animating as a percentage");
+ }
+}
+
+function integer() {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = [-2, 2];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "-2" },
+ { time: 500, expected: "0" },
+ { time: 1000, expected: "2" }]);
+ }, property + " supports animating as an integer");
+ }
+}
+
+function positiveNumber() {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = [1.1, 1.5];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "1.1" },
+ { time: 500, expected: "1.3" },
+ { time: 1000, expected: "1.5" }]);
+ }, property + " supports animating as a positive number");
+ }
+}
+
+function lengthPercentageOrCalc() {
+ return function(property, options) {
+ length()(property, options);
+ percentage()(property, options);
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["10px", "20%"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10px" },
+ { time: 500, expected: "calc(5px + 10%)" },
+ { time: 1000, expected: "20%" }]);
+ }, property + " supports animating as combination units 'px' and '%'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["10%", "2em"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10%" },
+ { time: 500, expected: "calc(10px + 5%)" },
+ { time: 1000, expected: "20px" }]);
+ }, property + " supports animating as combination units '%' and 'em'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["1em", "2rem"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10px" },
+ { time: 500, expected: "15px" },
+ { time: 1000, expected: "20px" }]);
+ }, property + " supports animating as combination units 'em' and 'rem'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["10px", "calc(1em + 20%)"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "10px" },
+ { time: 500, expected: "calc(10px + 10%)" },
+ { time: 1000, expected: "calc(10px + 20%)" }]);
+ }, property + " supports animating as combination units 'px' and 'calc'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["calc(10px + 10%)", "calc(1em + 1rem + 20%)"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0,
+ expected: "calc(10px + 10%)" },
+ { time: 500,
+ expected: "calc(15px + 15%)" },
+ { time: 1000,
+ expected: "calc(20px + 20%)" }]);
+ }, property + " supports animating as a calc");
+ }
+}
+
+function visibility() {
+ return function(property, options) {
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["visible", "hidden"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "visible" },
+ { time: 999, expected: "visible" },
+ { time: 1000, expected: "hidden" }]);
+ }, property + " uses visibility animation when animating "
+ + "from 'visible' to 'hidden'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["hidden", "visible"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "hidden" },
+ { time: 1, expected: "visible" },
+ { time: 1000, expected: "visible" }]);
+ }, property + " uses visibility animation when animating "
+ + "from 'hidden' to 'visible'");
+
+ test(function(t) {
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["hidden", "collapse"];
+ var target = createTestElement(t, options.tagName);
+ var animation = target.animate(keyframes,
+ { duration: 1000, fill: "both" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "hidden" },
+ { time: 499, expected: "hidden" },
+ { time: 500, expected: "collapse" },
+ { time: 1000, expected: "collapse" }]);
+ }, property + " uses visibility animation when animating "
+ + "from 'hidden' to 'collapse'");
+
+ test(function(t) {
+ // Easing: http://cubic-bezier.com/#.68,-.55,.26,1.55
+ // With this curve, the value is less than 0 till about 34%
+ // also more than 1 since about 63%
+ var idlName = propertyToIDL(property);
+ var keyframes = {};
+ keyframes[idlName] = ["visible", "hidden"];
+ var target = createTestElement(t, options.tagName);
+ var animation =
+ target.animate(keyframes,
+ { duration: 1000, fill: "both",
+ easing: "cubic-bezier(0.68, -0.55, 0.26, 1.55)" });
+ testAnimationSamples(animation, idlName,
+ [{ time: 0, expected: "visible" },
+ { time: 1, expected: "visible" },
+ { time: 330, expected: "visible" },
+ { time: 340, expected: "visible" },
+ { time: 620, expected: "visible" },
+ { time: 630, expected: "hidden" },
+ { time: 1000, expected: "hidden" }]);
+ }, property + " uses visibility animation when animating "
+ + "from 'visible' to 'hidden' with easeInOutBack easing");
+ }
+}
+
+function testAnimationSamples(animation, idlName, testSamples) {
+ var type = animation.effect.target.type;
+ var target = type
+ ? animation.effect.target.parentElement
+ : animation.effect.target;
+ testSamples.forEach(function(testSample) {
+ animation.currentTime = testSample.time;
+ assert_equals(getComputedStyle(target, type)[idlName],
+ testSample.expected,
+ "The value should be " + testSample.expected +
+ " at " + testSample.time + "ms");
+ });
+}
+
+function createTestElement(t, tagName) {
+ return tagName && tagName.startsWith("::")
+ ? createPseudo(t, tagName.substring(2))
+ : createElement(t, tagName);
+}
+
+function isSupported(property) {
+ var testKeyframe = new TestKeyframe(propertyToIDL(property));
+ try {
+ // Since TestKeyframe returns 'undefined' for |property|,
+ // the KeyframeEffect constructor will throw
+ // if the string "undefined" is not a valid value for the property.
+ new KeyframeEffect(null, testKeyframe);
+ } catch(e) {}
+ return testKeyframe.propAccessCount !== 0;
+}
+
+function TestKeyframe(testProp) {
+ var _propAccessCount = 0;
+
+ Object.defineProperty(this, testProp, {
+ get: function() { _propAccessCount++; },
+ enumerable: true
+ });
+
+ Object.defineProperty(this, 'propAccessCount', {
+ get: function() { return _propAccessCount; }
+ });
+}
+
+function propertyToIDL(property) {
+ // https://w3c.github.io/web-animations/#animation-property-name-to-idl-attribute-name
+ if (property === "float") {
+ return "cssFloat";
+ }
+ return property.replace(/-[a-z]/gi,
+ function (str) {
+ return str.substr(1).toUpperCase(); });
+}
+
+</script>
diff --git a/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-context.html b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-context.html
new file mode 100644
index 000000000..07fb6097c
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/effect-value-context.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests that property values respond to changes to their context</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#keyframes-section">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+
+test(function(t) {
+ var div = createDiv(t);
+ div.style.fontSize = '10px';
+ var animation = div.animate([ { marginLeft: '10em' },
+ { marginLeft: '20em' } ], 1000);
+ animation.currentTime = 500;
+ assert_equals(getComputedStyle(div).marginLeft, '150px',
+ 'Effect value before updating font-size');
+ div.style.fontSize = '20px';
+ assert_equals(getComputedStyle(div).marginLeft, '300px',
+ 'Effect value after updating font-size');
+}, 'Effect values reflect changes to font-size on element');
+
+test(function(t) {
+ var parentDiv = createDiv(t);
+ var div = createDiv(t);
+ parentDiv.appendChild(div);
+ parentDiv.style.fontSize = '10px';
+
+ var animation = div.animate([ { marginLeft: '10em' },
+ { marginLeft: '20em' } ], 1000);
+ animation.currentTime = 500;
+ assert_equals(getComputedStyle(div).marginLeft, '150px',
+ 'Effect value before updating font-size on parent element');
+ parentDiv.style.fontSize = '20px';
+ assert_equals(getComputedStyle(div).marginLeft, '300px',
+ 'Effect value after updating font-size on parent element');
+}, 'Effect values reflect changes to font-size on parent element');
+
+promise_test(function(t) {
+ var parentDiv = createDiv(t);
+ var div = createDiv(t);
+ parentDiv.appendChild(div);
+ parentDiv.style.fontSize = '10px';
+ var animation = div.animate([ { marginLeft: '10em' },
+ { marginLeft: '20em' } ], 1000);
+
+ animation.pause();
+ animation.currentTime = 500;
+ parentDiv.style.fontSize = '20px';
+
+ return animation.ready.then(function() {
+ assert_equals(getComputedStyle(div).marginLeft, '300px',
+ 'Effect value after updating font-size on parent element');
+ });
+}, 'Effect values reflect changes to font-size when computed style is not'
+ + ' immediately flushed');
+
+promise_test(function(t) {
+ var divWith10pxFontSize = createDiv(t);
+ divWith10pxFontSize.style.fontSize = '10px';
+ var divWith20pxFontSize = createDiv(t);
+ divWith20pxFontSize.style.fontSize = '20px';
+
+ var div = createDiv(t);
+ div.remove(); // Detach
+ var animation = div.animate([ { marginLeft: '10em' },
+ { marginLeft: '20em' } ], 1000);
+ animation.pause();
+
+ return animation.ready.then(function() {
+ animation.currentTime = 500;
+
+ divWith10pxFontSize.appendChild(div);
+ assert_equals(getComputedStyle(div).marginLeft, '150px',
+ 'Effect value after attaching to font-size:10px parent');
+ divWith20pxFontSize.appendChild(div);
+ assert_equals(getComputedStyle(div).marginLeft, '300px',
+ 'Effect value after attaching to font-size:20px parent');
+ });
+}, 'Effect values reflect changes to font-size from reparenting');
+
+test(function(t) {
+ var divA = createDiv(t);
+ divA.style.fontSize = '10px';
+
+ var divB = createDiv(t);
+ divB.style.fontSize = '20px';
+
+ var animation = divA.animate([ { marginLeft: '10em' },
+ { marginLeft: '20em' } ], 1000);
+ animation.currentTime = 500;
+ assert_equals(getComputedStyle(divA).marginLeft, '150px',
+ 'Effect value before updating target element');
+
+ animation.effect.target = divB;
+ assert_equals(getComputedStyle(divB).marginLeft, '300px',
+ 'Effect value after updating target element');
+}, 'Effect values reflect changes to target element');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/spacing-keyframes.html b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/spacing-keyframes.html
new file mode 100644
index 000000000..90e26d276
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/spacing-keyframes.html
@@ -0,0 +1,391 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Keyframe spacing tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#spacing-keyframes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC });
+
+ var frames = anim.effect.getKeyframes();
+ var slots = frames.length - 1;
+ assert_equals(frames[0].computedOffset, 0.0, '1st frame offset');
+ assert_equals(frames[1].computedOffset, 1.0 / slots, '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 2.0 / slots, '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0, 'last frame offset');
+}, 'Test distribute spacing');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px', offset: 0.5 },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'distribute' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames[0].computedOffset, 0.0, '1st frame offset');
+ assert_equals(frames[1].computedOffset, 0.5 * 1 / 2, '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 0.5, '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0, 'last frame offset');
+}, 'Test distribute spacing with specific offsets');
+
+test(function(t) {
+ var anim = createDiv(t).animate(null,
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames.length, 0, "empty keyframe list");
+}, 'Test paced spacing without any keyframe');
+
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 20, 140, 190];
+ assert_equals(frames[0].computedOffset, 0.0,
+ '1st frame offset');
+ assert_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0,
+ 'last frame offset');
+}, 'Test paced spacing');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px', offset: 0.5 },
+ { marginLeft: '120px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist1 = [ 0, 20, 140 ];
+ var cumDist2 = [ 0, 20, 90 ];
+ assert_equals(frames[1].computedOffset, 0.5 * cumDist1[1] / cumDist1[2],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 0.5,
+ '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 0.5 + 0.5 * cumDist2[1] / cumDist2[2],
+ '4th frame offset');
+}, 'Test paced spacing with specific offsets');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '0px' },
+ { marginLeft: '100px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 0, 100, 150];
+ assert_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ '3rd frame offset');
+}, 'Test paced spacing if some paced property values are equal');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '0px' },
+ { marginLeft: '0px' },
+ { marginLeft: '0px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ var slots = frames.length - 1;
+ assert_equals(frames[1].computedOffset, 1.0 / slots, '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 2.0 / slots, '3rd frame offset');
+}, 'Test falling back to distribute spacing if all paced property value ' +
+ 'are equal');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginTop: '-20px' },
+ { marginLeft: '100px' },
+ { margin: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames[1].computedOffset, frames[2].computedOffset * 1 / 2,
+ '2nd frame offset using distribute spacing');
+ assert_equals(frames[2].computedOffset, 100 / 150,
+ '3rd frame offset using paced spacing');
+}, 'Test paced spacing if there a keyframe without the paced property');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginTop: '40px' },
+ { marginTop: '-20px' },
+ { marginLeft: '40px' },
+ { marginTop: '60px' },
+ { margin: '10px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 0, 0, 40, 40, 70];
+ assert_equals(frames[1].computedOffset, frames[3].computedOffset * 1 / 3,
+ '2nd frame offset using distribute spacing');
+ assert_equals(frames[2].computedOffset, frames[3].computedOffset * 2 / 3,
+ '3rd frame offset using distribute spacing');
+ assert_equals(frames[3].computedOffset, cumDist[3] / cumDist[5],
+ '4th frame offset using paced spacing');
+ assert_equals(frames[4].computedOffset,
+ frames[3].computedOffset +
+ (1 - frames[3].computedOffset) * 1 / 2,
+ '5th frame offset using distribute spacing');
+}, 'Test paced spacing if a paced property that appears on only some ' +
+ 'keyframes');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginTop: '-20px', offset: 0.5 },
+ { marginLeft: '40px' },
+ { marginLeft: '100px' },
+ { margin: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames[2].computedOffset, 0.5 + 0.5 * 1 / 3,
+ '3rd frame offset using distribute spacing because it is the ' +
+ 'first paceable keyframe');
+ assert_equals(frames[3].computedOffset,
+ frames[2].computedOffset +
+ (1.0 - frames[2].computedOffset) * 60 / 110,
+ '4th frame offset using paced spacing');
+}, 'Test paced spacing if a paced property that appears on only some ' +
+ 'keyframes and there is a specific offset');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginTop: '20px', offset: 0.2 },
+ { marginTop: '40px' },
+ { marginTop: '-20px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '40px' },
+ { marginTop: '60px' },
+ { marginLeft: '100px' },
+ { marginTop: '50px' },
+ { marginTop: '100px', offset: 0.8 },
+ { margin: '0px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+ var frames = anim.effect.getKeyframes();
+ // Test distribute spacing in (A, Paced A] and [Paced B, frame B).
+ var slots = frames.length - 3;
+ var start = 0.2;
+ var diff = 0.8 - start;
+ assert_equals(frames[2].computedOffset, start + diff * 1.0 / slots,
+ '3nd frame offset using distribute spacing');
+ assert_equals(frames[3].computedOffset, start + diff * 2.0 / slots,
+ '4rd frame offset using distribute spacing');
+ assert_equals(frames[4].computedOffset, start + diff * 3.0 / slots,
+ '5th frame offset using distribute spacing because it is ' +
+ 'the first paceable keyframe');
+ assert_equals(frames[7].computedOffset, start + diff * 6.0 / slots,
+ '8th frame offset using distribute spacing because it is ' +
+ 'the last paceable keyframe');
+ assert_equals(frames[8].computedOffset, start + diff * 7.0 / slots,
+ '9th frame offset using distribute spacing');
+ // Test paced spacing and other null computed offsets in (Paced A, Paced B).
+ var cumDist = [0, 60, 60, 120];
+ assert_equals(frames[5].computedOffset,
+ frames[4].computedOffset + cumDist[2] / cumDist[3] *
+ (frames[7].computedOffset - frames[4].computedOffset),
+ '6th frame offset using paced spacing');
+ assert_equals(frames[6].computedOffset,
+ frames[5].computedOffset + 1.0 / 2.0 *
+ (frames[7].computedOffset - frames[5].computedOffset),
+ '7th frame offset using distribute spacing');
+}, 'Test paced spacing where there are some keyframes without offsets and ' +
+ 'without the paced property before the first paceable keyframe and ' +
+ 'after the last paceable keyframe');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { margin: '-20px' },
+ { margin: '100px' },
+ { margin: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin)' });
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 20, 140, 190];
+ assert_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ '3rd frame offset');
+}, 'Test paced spacing for using shorthand property');
+
+test(function(t) {
+ var anim =
+ createDiv(t).animate([ { marginLeft: '0px', marginRight: '0px',
+ marginTop: '10px', marginBottom: '10px' },
+ { marginLeft: '-20px', marginRight: '-20px',
+ marginTop: '0px', marginBottom: '0px' },
+ { marginLeft: '100px', marginRight: '100px',
+ marginTop: '-50px', marginBottom: '-50px' },
+ { marginLeft: '50px', marginRight: '50px',
+ marginTop: '80px', marginBottom: '80px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin)' });
+
+ var frames = anim.effect.getKeyframes();
+ var dist = [ 0,
+ Math.sqrt(20 * 20 * 2 + 10 * 10 * 2),
+ Math.sqrt(120 * 120 * 2 + 50 * 50 * 2),
+ Math.sqrt(50 * 50 * 2 + 130 * 130 * 2) ];
+ var cumDist = [];
+ dist.reduce(function(prev, curr, i) { return cumDist[i] = prev + curr; }, 0);
+ assert_approx_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ 0.0001, '2nd frame offset');
+ assert_approx_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ 0.0001, '3rd frame offset');
+}, 'Test paced spacing using shorthand property where only the longhand ' +
+ 'components are specified');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px', marginTop: '0px' },
+ { marginLeft: '-20px', marginTop: '-20px' },
+ { marginLeft: '100px', marginTop: '100px' },
+ { marginLeft: '50px', marginTop: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin)' });
+
+ var frames = anim.effect.getKeyframes();
+ var slots = frames.length - 1;
+ assert_equals(frames[1].computedOffset, 1 / slots, '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 2 / slots, '3rd frame offset');
+}, 'Test falling back to distribute spacing if all keyframe miss some ' +
+ 'components');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginLeft: '-20px' },
+ { marginTop: '40px' },
+ { margin: '100px' },
+ { margin: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin)' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames[1].computedOffset, frames[3].computedOffset * 1 / 3,
+ '2nd frame offset using distribute spacing');
+ assert_equals(frames[2].computedOffset, frames[3].computedOffset * 2 / 3,
+ '3rd frame offset using distribute spacing');
+ assert_equals(frames[3].computedOffset, 100 / 150,
+ '4th frame offset using paced spacing');
+}, 'Test paced spacing only for keyframes specifying all longhand ' +
+ 'components, and falling back to distribute spacing for the reset');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { margin: '0px' },
+ { marginLeft: '-20px' },
+ { marginTop: '40px', offset: 0.5 },
+ { margin: '100px' },
+ { margin: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin)' });
+
+ var frames = anim.effect.getKeyframes();
+ assert_equals(frames[1].computedOffset, 0.5 * 1 / 2,
+ '2nd frame offset using distribute spacing');
+ assert_equals(frames[3].computedOffset, 0.5 + 0.5 * 1 / 2,
+ '4th frame offset using distribute spacing because it is the ' +
+ 'first paceable keyframe from a non-null offset keyframe');
+}, 'Test paced spacing only for keyframes specifying all some components, ' +
+ 'and falling back to distribute spacing for the reset with some specific ' +
+ 'offsets');
+
+// Tests for setting spacing by KeyframeEffect.spacing.
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC });
+
+ anim.effect.spacing = 'paced(margin-left)';
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 20, 140, 190];
+ assert_equals(frames[0].computedOffset, 0.0,
+ '1st frame offset');
+ assert_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0,
+ 'last frame offset');
+}, 'Test paced spacing by setter');
+
+test(function(t) {
+ var anim = createDiv(t).animate([ { marginLeft: '0px' },
+ { marginLeft: '-20px' },
+ { marginLeft: '100px' },
+ { marginLeft: '50px' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ anim.effect.spacing = 'distribute';
+
+ var frames = anim.effect.getKeyframes();
+ var slots = frames.length - 1;
+ assert_equals(frames[0].computedOffset, 0.0, '1st frame offset');
+ assert_equals(frames[1].computedOffset, 1.0 / slots, '2nd frame offset');
+ assert_equals(frames[2].computedOffset, 2.0 / slots, '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0, 'last frame offset');
+}, 'Test distribute spacing by setter');
+
+test(function(t) {
+ var anim =
+ createDiv(t).animate([ { marginLeft: '0px', borderRadius: '0%' },
+ { marginLeft: '-20px', borderRadius: '50%' },
+ { marginLeft: '100px', borderRadius: '25%' },
+ { marginLeft: '50px', borderRadius: '100%' } ],
+ { duration: 100 * MS_PER_SEC,
+ spacing: 'paced(margin-left)' });
+
+ anim.effect.spacing = 'paced(border-radius)';
+
+ var frames = anim.effect.getKeyframes();
+ var cumDist = [0, 50, 50 + 25, 50 + 25 + 75];
+
+ assert_equals(frames[0].computedOffset, 0.0,
+ '1st frame offset');
+ assert_equals(frames[1].computedOffset, cumDist[1] / cumDist[3],
+ '2nd frame offset');
+ assert_equals(frames[2].computedOffset, cumDist[2] / cumDist[3],
+ '3rd frame offset');
+ assert_equals(frames[3].computedOffset, 1.0,
+ 'last frame offset');
+}, 'Test paced spacing by changing the paced property');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html
new file mode 100644
index 000000000..eb67f669a
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/animation-model/keyframe-effects/the-effect-value-of-a-keyframe-effect.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Keyframe handling tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#the-effect-value-of-a-keyframe-animation-effect">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<div id="target"></div>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = createDiv(t);
+ var anim = div.animate([ { offset: 0, opacity: 0 },
+ { offset: 0, opacity: 0.1 },
+ { offset: 0, opacity: 0.2 },
+ { offset: 1, opacity: 0.8 },
+ { offset: 1, opacity: 0.9 },
+ { offset: 1, opacity: 1 } ],
+ { duration: 1000,
+ easing: 'cubic-bezier(0.5, -0.5, 0.5, 1.5)' });
+ assert_equals(getComputedStyle(div).opacity, '0.2',
+ 'When progress is zero the last keyframe with offset 0 should'
+ + ' be used');
+ // http://cubic-bezier.com/#.5,-0.5,.5,1.5
+ // At t=0.15, the progress should be negative
+ anim.currentTime = 150;
+ assert_equals(getComputedStyle(div).opacity, '0',
+ 'When progress is negative, the first keyframe with a 0 offset'
+ + ' should be used');
+ // At t=0.71, the progress should be just less than 1
+ anim.currentTime = 710;
+ assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.8, 0.01,
+ 'When progress is just less than 1, the first keyframe with an'
+ + ' offset of 1 should be used as the interval endpoint');
+ // At t=0.85, the progress should be > 1
+ anim.currentTime = 850;
+ assert_equals(getComputedStyle(div).opacity, '1',
+ 'When progress is greater than 1.0, the last keyframe with a 1'
+ + ' offset should be used');
+ anim.finish();
+ assert_equals(getComputedStyle(div).opacity, '1',
+ 'When progress is equal to 1.0, the last keyframe with a 1'
+ + ' offset should be used');
+}, 'Overlapping keyframes at 0 and 1 use the appropriate value when the'
+ + ' progress is outside the range [0, 1]');
+
+test(function(t) {
+ var div = createDiv(t);
+ var anim = div.animate([ { offset: 0, opacity: 0 },
+ { offset: 0.5, opacity: 0.3 },
+ { offset: 0.5, opacity: 0.5 },
+ { offset: 0.5, opacity: 0.7 },
+ { offset: 1, opacity: 1 } ], 1000);
+ anim.currentTime = 250;
+ assert_equals(getComputedStyle(div).opacity, '0.15',
+ 'Before the overlap point, the first keyframe from the'
+ + ' overlap point should be used as interval endpoint');
+ anim.currentTime = 500;
+ assert_equals(getComputedStyle(div).opacity, '0.7',
+ 'At the overlap point, the last keyframe from the'
+ + ' overlap point should be used as interval startpoint');
+ anim.currentTime = 750;
+ assert_equals(getComputedStyle(div).opacity, '0.85',
+ 'After the overlap point, the last keyframe from the'
+ + ' overlap point should be used as interval startpoint');
+}, 'Overlapping keyframes between 0 and 1 use the appropriate value on each'
+ + ' side of the overlap point');
+
+test(function(t) {
+ var div = createDiv(t);
+ var anim = div.animate({ visibility: ['hidden','visible'] },
+ { duration: 100 * MS_PER_SEC, fill: 'both' });
+
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(div).visibility, 'hidden',
+ 'Visibility when progress = 0.');
+
+ anim.currentTime = 10 * MS_PER_SEC + 1;
+ assert_equals(getComputedStyle(div).visibility, 'visible',
+ 'Visibility when progress > 0 due to linear easing.');
+
+ anim.finish();
+ assert_equals(getComputedStyle(div).visibility, 'visible',
+ 'Visibility when progress = 1.');
+
+}, "Test visibility clamping behavior.");
+
+test(function(t) {
+ var div = createDiv(t);
+ var anim = div.animate({ visibility: ['hidden', 'visible'] },
+ { duration: 100 * MS_PER_SEC, fill: 'both',
+ easing: 'cubic-bezier(0.25, -0.6, 0, 0.5)' });
+
+ anim.currentTime = 0;
+ assert_equals(getComputedStyle(div).visibility, 'hidden',
+ 'Visibility when progress = 0.');
+
+ // Timing function is below zero. So we expected visibility is hidden.
+ anim.currentTime = 10 * MS_PER_SEC + 1;
+ assert_equals(getComputedStyle(div).visibility, 'hidden',
+ 'Visibility when progress < 0 due to cubic-bezier easing.');
+
+ anim.currentTime = 60 * MS_PER_SEC;
+ assert_equals(getComputedStyle(div).visibility, 'visible',
+ 'Visibility when progress > 0 due to cubic-bezier easing.');
+
+}, "Test visibility clamping behavior with an easing that has a negative component");
+
+done();
+</script>
+</body>