diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/css/css-properties-values-api | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-properties-values-api')
95 files changed, 7198 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-properties-values-api/META.yml b/testing/web-platform/tests/css/css-properties-values-api/META.yml new file mode 100644 index 0000000000..38cd166ca8 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/META.yml @@ -0,0 +1,4 @@ +spec: https://drafts.css-houdini.org/css-properties-values-api/ +suggested_reviewers: + - tabatkins + - astearns diff --git a/testing/web-platform/tests/css/css-properties-values-api/animate-invalid.html b/testing/web-platform/tests/css/css-properties-values-api/animate-invalid.html new file mode 100644 index 0000000000..41cbd067b5 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animate-invalid.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<title>Do not crash when animating to unresolved var()</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty"> +<link rel="help" href="https://crbug.com/1185524"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="target"></div> +<script> + promise_test(async function(){ + CSS.registerProperty({ + name: '--x', + syntax: '<number>', + initialValue: '1', + inherits: false + }); + let animation = target.animate({'--x': [ 'var(--unknown)']}, 100); + await animation.ready; + assert_equals(getComputedStyle(target).getPropertyValue('--x'), '1'); + }, 'Do not crash when animating to unresolved var()'); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-comma-list.html new file mode 100644 index 0000000000..a14b0bb09e --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<angle>#", + inherits: false, + initialValue: "0deg" +}, { + keyframes: ["100deg, 150deg", "200deg, 250deg"], + expected: "150deg, 200deg" +}, 'Animating a custom property of type <angle>#'); + +animation_test({ + syntax: "<angle>#", + inherits: false, + initialValue: "100deg, 150deg" +}, { + keyframes: "200deg, 250deg", + expected: "150deg, 200deg" +}, 'Animating a custom property of type <angle># with a single keyframe'); + +animation_test({ + syntax: "<angle>#", + inherits: false, + initialValue: "50deg, 100deg" +}, { + composite: "add", + keyframes: ["150deg, 200deg", "250deg, 300deg"], + expected: "250deg, 350deg" +}, 'Animating a custom property of type <angle># with additivity'); + +animation_test({ + syntax: "<angle>#", + inherits: false, + initialValue: "50deg, 100deg" +}, { + composite: "add", + keyframes: "150deg, 200deg", + expected: "125deg, 200deg" +}, 'Animating a custom property of type <angle># with a single keyframe and additivity'); + +animation_test({ + syntax: "<angle>#", + inherits: false, + initialValue: "0deg, 0deg" +}, { + iterationComposite: "accumulate", + keyframes: ["0deg, 50deg", "100deg, 100deg"], + expected: "250deg, 275deg" +}, 'Animating a custom property of type <angle># with iterationComposite'); + +discrete_animation_test("<angle>#", '10deg, 20deg', '30deg', 'Animating a custom property of type <angle># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-space-list.html new file mode 100644 index 0000000000..8ce7a9deab --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<angle>+", + inherits: false, + initialValue: "0deg" +}, { + keyframes: ["100deg 150deg", "200deg 250deg"], + expected: "150deg 200deg" +}, 'Animating a custom property of type <angle>+'); + +animation_test({ + syntax: "<angle>+", + inherits: false, + initialValue: "100deg 150deg" +}, { + keyframes: "200deg 250deg", + expected: "150deg 200deg" +}, 'Animating a custom property of type <angle>+ with a single keyframe'); + +animation_test({ + syntax: "<angle>+", + inherits: false, + initialValue: "50deg 100deg" +}, { + composite: "add", + keyframes: ["150deg 200deg", "250deg 300deg"], + expected: "250deg 350deg" +}, 'Animating a custom property of type <angle>+ with additivity'); + +animation_test({ + syntax: "<angle>+", + inherits: false, + initialValue: "50deg 100deg" +}, { + composite: "add", + keyframes: "150deg 200deg", + expected: "125deg 200deg" +}, 'Animating a custom property of type <angle>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<angle>+", + inherits: false, + initialValue: "0deg 0deg" +}, { + iterationComposite: "accumulate", + keyframes: ["0deg 50deg", "100deg 100deg"], + expected: "250deg 275deg" +}, 'Animating a custom property of type <angle>+ with iterationComposite'); + +discrete_animation_test("<angle>+", '10deg 20deg', '30deg', 'Animating a custom property of type <angle>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle.html new file mode 100644 index 0000000000..0cb06aef0b --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-angle.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<angle>", + inherits: false, + initialValue: "0deg" +}, { + keyframes: ["100deg", "200deg"], + expected: "150deg" +}, 'Animating a custom property of type <angle>'); + +animation_test({ + syntax: "<angle>", + inherits: false, + initialValue: "100deg" +}, { + keyframes: "200deg", + expected: "150deg" +}, 'Animating a custom property of type <angle> with a single keyframe'); + +animation_test({ + syntax: "<angle>", + inherits: false, + initialValue: "100deg" +}, { + composite: "add", + keyframes: ["200deg", "300deg"], + expected: "350deg" +}, 'Animating a custom property of type <angle> with additivity'); + +animation_test({ + syntax: "<angle>", + inherits: false, + initialValue: "100deg" +}, { + composite: "add", + keyframes: "300deg", + expected: "250deg" +}, 'Animating a custom property of type <angle> with a single keyframe and additivity'); + +animation_test({ + syntax: "<angle>", + inherits: false, + initialValue: "100deg" +}, { + iterationComposite: "accumulate", + keyframes: ["0deg", "100deg"], + expected: "250deg" +}, 'Animating a custom property of type <angle> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-comma-list.html new file mode 100644 index 0000000000..7148df0724 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<color>#", + inherits: false, + initialValue: "black" +}, { + keyframes: ["rgb(100, 100, 100), rgb(150, 150, 150)", "rgb(200, 200, 200), rgb(250, 250, 250)"], + expected: "rgb(150, 150, 150), rgb(200, 200, 200)" +}, 'Animating a custom property of type <color>#'); + +animation_test({ + syntax: "<color>#", + inherits: false, + initialValue: "rgb(100, 100, 100), rgb(150, 150, 150)" +}, { + keyframes: "rgb(200, 200, 200), rgb(250, 250, 250)", + expected: "rgb(150, 150, 150), rgb(200, 200, 200)" +}, 'Animating a custom property of type <color># with a single keyframe'); + +animation_test({ + syntax: "<color>#", + inherits: false, + initialValue: "rgb(100, 100, 100), rgb(150, 150, 150)" +}, { + composite: "add", + keyframes: ["rgb(25, 25, 25), rgb(50, 50, 50)", "rgb(75, 75, 75), rgb(100, 100, 100)"], + expected: "rgb(150, 150, 150), rgb(225, 225, 225)" +}, 'Animating a custom property of type <color># with additivity'); + +animation_test({ + syntax: "<color>#", + inherits: false, + initialValue: "rgb(100, 100, 100), rgb(150, 150, 150)" +}, { + composite: "add", + keyframes: "rgb(50, 50, 50), rgb(100, 100, 100)", + expected: "rgb(125, 125, 125), rgb(200, 200, 200)" +}, 'Animating a custom property of type <color># with a single keyframe and additivity'); + +animation_test({ + syntax: "<color>#", + inherits: false, + initialValue: "black" +}, { + iterationComposite: "accumulate", + keyframes: ["rgb(0, 0, 0), rgb(0, 0, 0)", "rgb(50, 50, 50), rgb(100, 100, 100)"], + expected: "rgb(125, 125, 125), rgb(250, 250, 250)" +}, 'Animating a custom property of type <color># with iterationComposite'); + +discrete_animation_test("<color>#", 'rgb(255, 0, 0), rgb(0, 255, 0)', 'rgb(0, 0, 255)', 'Animating a custom property of type <color># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-space-list.html new file mode 100644 index 0000000000..679e244667 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<color>+", + inherits: false, + initialValue: "black" +}, { + keyframes: ["rgb(100, 100, 100) rgb(150, 150, 150)", "rgb(200, 200, 200) rgb(250, 250, 250)"], + expected: "rgb(150, 150, 150) rgb(200, 200, 200)" +}, 'Animating a custom property of type <color>+'); + +animation_test({ + syntax: "<color>+", + inherits: false, + initialValue: "rgb(100, 100, 100) rgb(150, 150, 150)" +}, { + keyframes: "rgb(200, 200, 200) rgb(250, 250, 250)", + expected: "rgb(150, 150, 150) rgb(200, 200, 200)" +}, 'Animating a custom property of type <color>+ with a single keyframe'); + +animation_test({ + syntax: "<color>+", + inherits: false, + initialValue: "rgb(100, 100, 100) rgb(150, 150, 150)" +}, { + composite: "add", + keyframes: ["rgb(25, 25, 25) rgb(50, 50, 50)", "rgb(75, 75, 75) rgb(100, 100, 100)"], + expected: "rgb(150, 150, 150) rgb(225, 225, 225)" +}, 'Animating a custom property of type <color>+ with additivity'); + +animation_test({ + syntax: "<color>+", + inherits: false, + initialValue: "rgb(100, 100, 100) rgb(150, 150, 150)" +}, { + composite: "add", + keyframes: "rgb(50, 50, 50) rgb(100, 100, 100)", + expected: "rgb(125, 125, 125) rgb(200, 200, 200)" +}, 'Animating a custom property of type <color>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<color>+", + inherits: false, + initialValue: "black" +}, { + iterationComposite: "accumulate", + keyframes: ["rgb(0, 0, 0) rgb(0, 0, 0)", "rgb(50, 50, 50) rgb(100, 100, 100)"], + expected: "rgb(125, 125, 125) rgb(250, 250, 250)" +}, 'Animating a custom property of type <color>+ with iterationComposite'); + +discrete_animation_test("<color>+", 'rgb(255, 0, 0) rgb(0, 255, 0)', 'rgb(0, 0, 255)', 'Animating a custom property of type <color>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color.html new file mode 100644 index 0000000000..001fc407e7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-color.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<color>", + inherits: false, + initialValue: "black" +}, { + keyframes: ["rgb(100, 100, 100)", "rgb(200, 200, 200)"], + expected: "rgb(150, 150, 150)" +}, 'Animating a custom property of type <color>'); + +animation_test({ + syntax: "<color>", + inherits: false, + initialValue: "rgb(100, 100, 100)" +}, { + keyframes: "rgb(200, 200, 200)", + expected: "rgb(150, 150, 150)" +}, 'Animating a custom property of type <color> with a single keyframe'); + +animation_test({ + syntax: "<color>", + inherits: false, + initialValue: "rgb(100, 100, 100)" +}, { + composite: "add", + keyframes: ["rgb(50, 50, 50)", "rgb(150, 150, 150)"], + expected: "rgb(200, 200, 200)" +}, 'Animating a custom property of type <color> with additivity'); + +animation_test({ + syntax: "<color>", + inherits: false, + initialValue: "rgb(100, 100, 100)" +}, { + composite: "add", + keyframes: "rgb(150, 150, 150)", + expected: "rgb(175, 175, 175)" +}, 'Animating a custom property of type <color> with a single keyframe and additivity'); + +animation_test({ + syntax: "<color>", + inherits: false, + initialValue: "black" +}, { + iterationComposite: "accumulate", + keyframes: ["rgb(0, 0, 0)", "rgb(100, 100, 100)"], + expected: "rgb(250, 250, 250)" +}, 'Animating a custom property of type <color> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-custom-ident.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-custom-ident.html new file mode 100644 index 0000000000..5bc2bed343 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-custom-ident.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +discrete_animation_test("<custom-ident>", "from", "to"); +discrete_animation_test("<custom-ident>+", "from1 from2", "to1 to2"); +discrete_animation_test("<custom-ident>#", "from1, from2", "to1, to2"); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-image.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-image.html new file mode 100644 index 0000000000..4f9505f9a8 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-image.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +discrete_animation_test("<image>", 'url("https://example.com/from")', 'url("https://example.com/to")'); +discrete_animation_test("<image>+", 'url("https://example.com/from1") url("https://example.com/from2")', 'url("https://example.com/to1") url("https://example.com/to2")'); +discrete_animation_test("<image>#", 'url("https://example.com/from1"), url("https://example.com/from2")', 'url("https://example.com/to1"), url("https://example.com/to2")'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-inherited-used-by-standard-property.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-inherited-used-by-standard-property.html new file mode 100644 index 0000000000..231ecca8c7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-inherited-used-by-standard-property.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="container"> + <div id="target"></div> +</div> +<script> + +test(() => { + CSS.registerProperty({ + name: "--my-length", + syntax: "<length>", + inherits: true, + initialValue: "0px" + }); + + target.style.marginLeft = "var(--my-length)"; + + const duration = 1000; + const animation = container.animate({ "--my-length": "100px" }, duration); + animation.pause(); + animation.currentTime = duration / 4; + + assert_equals(getComputedStyle(target).marginLeft, "25px"); +}, "Animating an inherited CSS variable on a parent is reflected on a standard property using that variable as a value on a child"); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-comma-list.html new file mode 100644 index 0000000000..3f4beaedf1 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<integer>#", + inherits: false, + initialValue: "0" +}, { + keyframes: ["100, 150", "200, 250"], + expected: "150, 200" +}, 'Animating a custom property of type <integer>#'); + +animation_test({ + syntax: "<integer>#", + inherits: false, + initialValue: "100, 150" +}, { + keyframes: "200, 250", + expected: "150, 200" +}, 'Animating a custom property of type <integer># with a single keyframe'); + +animation_test({ + syntax: "<integer>#", + inherits: false, + initialValue: "50, 100" +}, { + composite: "add", + keyframes: ["150, 200", "250, 300"], + expected: "250, 350" +}, 'Animating a custom property of type <integer># with additivity'); + +animation_test({ + syntax: "<integer>#", + inherits: false, + initialValue: "50, 100" +}, { + composite: "add", + keyframes: "150, 200", + expected: "125, 200" +}, 'Animating a custom property of type <integer># with a single keyframe and additivity'); + +animation_test({ + syntax: "<integer>#", + inherits: false, + initialValue: "0, 0" +}, { + iterationComposite: "accumulate", + keyframes: ["0, 50", "100, 100"], + expected: "250, 275" +}, 'Animating a custom property of type <integer># with iterationComposite'); + +discrete_animation_test("<integer>#", '10, 20', '30', 'Animating a custom property of type <integer># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-space-list.html new file mode 100644 index 0000000000..89feb3af15 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<integer>+", + inherits: false, + initialValue: "0" +}, { + keyframes: ["100 150", "200 250"], + expected: "150 200" +}, 'Animating a custom property of type <integer>+'); + +animation_test({ + syntax: "<integer>+", + inherits: false, + initialValue: "100 150" +}, { + keyframes: "200 250", + expected: "150 200" +}, 'Animating a custom property of type <integer>+ with a single keyframe'); + +animation_test({ + syntax: "<integer>+", + inherits: false, + initialValue: "50 100" +}, { + composite: "add", + keyframes: ["150 200", "250 300"], + expected: "250 350" +}, 'Animating a custom property of type <integer>+ with additivity'); + +animation_test({ + syntax: "<integer>+", + inherits: false, + initialValue: "50 100" +}, { + composite: "add", + keyframes: "150 200", + expected: "125 200" +}, 'Animating a custom property of type <integer>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<integer>+", + inherits: false, + initialValue: "0 0" +}, { + iterationComposite: "accumulate", + keyframes: ["0 50", "100 100"], + expected: "250 275" +}, 'Animating a custom property of type <integer>+ with iterationComposite'); + +discrete_animation_test("<integer>+", '10 20', '30', 'Animating a custom property of type <integer>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer.html new file mode 100644 index 0000000000..400308a92e --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-integer.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<integer>", + inherits: false, + initialValue: 0 +}, { + keyframes: [100, 200], + expected: "150" +}, 'Animating a custom property of type <integer>'); + +animation_test({ + syntax: "<integer>", + inherits: false, + initialValue: 100 +}, { + keyframes: 200, + expected: "150" +}, 'Animating a custom property of type <integer> with a single keyframe'); + +animation_test({ + syntax: "<integer>", + inherits: false, + initialValue: 100 +}, { + composite: "add", + keyframes: [200, 300], + expected: "350" +}, 'Animating a custom property of type <integer> with additivity'); + +animation_test({ + syntax: "<integer>", + inherits: false, + initialValue: 100 +}, { + composite: "add", + keyframes: 300, + expected: "250" +}, 'Animating a custom property of type <integer> with a single keyframe and additivity'); + +animation_test({ + syntax: "<integer>", + inherits: false, + initialValue: 100 +}, { + iterationComposite: "accumulate", + keyframes: [0, 100], + expected: "250" +}, 'Animating a custom property of type <integer> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-comma-list.html new file mode 100644 index 0000000000..6f1fefb76f --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length>#", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px, 150px", "200px, 250px"], + expected: "150px, 200px" +}, 'Animating a custom property of type <length>#'); + +animation_test({ + syntax: "<length>#", + inherits: false, + initialValue: "100px, 150px" +}, { + keyframes: "200px, 250px", + expected: "150px, 200px" +}, 'Animating a custom property of type <length># with a single keyframe'); + +animation_test({ + syntax: "<length>#", + inherits: false, + initialValue: "50px, 100px" +}, { + composite: "add", + keyframes: ["150px, 200px", "250px, 300px"], + expected: "250px, 350px" +}, 'Animating a custom property of type <length># with additivity'); + +animation_test({ + syntax: "<length>#", + inherits: false, + initialValue: "50px, 100px" +}, { + composite: "add", + keyframes: "150px, 200px", + expected: "125px, 200px" +}, 'Animating a custom property of type <length># with a single keyframe and additivity'); + +animation_test({ + syntax: "<length>#", + inherits: false, + initialValue: "0px, 0px" +}, { + iterationComposite: "accumulate", + keyframes: ["0px, 50px", "100px, 100px"], + expected: "250px, 275px" +}, 'Animating a custom property of type <length># with iterationComposite'); + +discrete_animation_test("<length>#", '10px, 20px', '30px', 'Animating a custom property of type <length># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-comma-list.html new file mode 100644 index 0000000000..bf3e75b791 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length-percentage>#", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px, 150%", "200%, 250px"], + expected: "calc(100% + 50px), calc(75% + 125px)" +}, 'Animating a custom property of type <length-percentage>#'); + +animation_test({ + syntax: "<length-percentage>#", + inherits: false, + initialValue: "100px, 150%" +}, { + keyframes: "200%, 250px", + expected: "calc(100% + 50px), calc(75% + 125px)" +}, 'Animating a custom property of type <length-percentage># with a single keyframe'); + +animation_test({ + syntax: "<length-percentage>#", + inherits: false, + initialValue: "50px, 100%" +}, { + composite: "add", + keyframes: ["150%, 200px", "250%, 300px"], + expected: "calc(200% + 50px), calc(100% + 250px)" +}, 'Animating a custom property of type <length-percentage># with additivity'); + +animation_test({ + syntax: "<length-percentage>#", + inherits: false, + initialValue: "50px, 100%" +}, { + composite: "add", + keyframes: "150%, 200px", + expected: "calc(75% + 50px), calc(100% + 100px)" +}, 'Animating a custom property of type <length-percentage># with a single keyframe and additivity'); + +animation_test({ + syntax: "<length-percentage>#", + inherits: false, + initialValue: "0px, 0px" +}, { + iterationComposite: "accumulate", + keyframes: ["0px, 50%", "100%, 100px"], + expected: "250%, calc(25% + 50px)" +}, 'Animating a custom property of type <length-percentage># with iterationComposite'); + +discrete_animation_test("<length-percentage>#", '10px, 20%', '30px', 'Animating a custom property of type <length-percentage># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-space-list.html new file mode 100644 index 0000000000..9497093b6e --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length-percentage>+", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px 150%", "200% 250px"], + expected: "calc(100% + 50px) calc(75% + 125px)" +}, 'Animating a custom property of type <length-percentage>+'); + +animation_test({ + syntax: "<length-percentage>+", + inherits: false, + initialValue: "100px 150%" +}, { + keyframes: "200% 250px", + expected: "calc(100% + 50px) calc(75% + 125px)" +}, 'Animating a custom property of type <length-percentage>+ with a single keyframe'); + +animation_test({ + syntax: "<length-percentage>+", + inherits: false, + initialValue: "50px 100%" +}, { + composite: "add", + keyframes: ["150% 200px", "250% 300px"], + expected: "calc(200% + 50px) calc(100% + 250px)" +}, 'Animating a custom property of type <length-percentage>+ with additivity'); + +animation_test({ + syntax: "<length-percentage>+", + inherits: false, + initialValue: "50px 100%" +}, { + composite: "add", + keyframes: "150% 200px", + expected: "calc(75% + 50px) calc(100% + 100px)" +}, 'Animating a custom property of type <length-percentage>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<length-percentage>+", + inherits: false, + initialValue: "0px 0px" +}, { + iterationComposite: "accumulate", + keyframes: ["0px 50%", "100% 100px"], + expected: "250% calc(25% + 50px)" +}, 'Animating a custom property of type <length-percentage>+ with iterationComposite'); + +discrete_animation_test("<length-percentage>+", '10px 20%', '30px', 'Animating a custom property of type <length-percentage>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage.html new file mode 100644 index 0000000000..096efd6ba7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-percentage.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length-percentage>", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px", "100%"], + expected: "calc(50% + 50px)" +}, 'Animating a custom property of type <length-percentage>'); + +animation_test({ + syntax: "<length-percentage>", + inherits: false, + initialValue: "100px" +}, { + keyframes: "100%", + expected: "calc(50% + 50px)" +}, 'Animating a custom property of type <length-percentage> with a single keyframe'); + +animation_test({ + syntax: "<length-percentage>", + inherits: false, + initialValue: "100px" +}, { + composite: "add", + keyframes: ["200%", "300%"], + expected: "calc(250% + 100px)" +}, 'Animating a custom property of type <length-percentage> with additivity'); + +animation_test({ + syntax: "<length-percentage>", + inherits: false, + initialValue: "100px" +}, { + composite: "add", + keyframes: "300%", + expected: "calc(150% + 100px)" +}, 'Animating a custom property of type <length-percentage> with a single keyframe and additivity'); + +animation_test({ + syntax: "<length-percentage>", + inherits: false, + initialValue: "100px" +}, { + iterationComposite: "accumulate", + keyframes: ["50px", "100%"], + expected: "calc(250% + 25px)" +}, 'Animating a custom property of type <length-percentage> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-space-list.html new file mode 100644 index 0000000000..102259a070 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length>+", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px 150px", "200px 250px"], + expected: "150px 200px" +}, 'Animating a custom property of type <length>+'); + +animation_test({ + syntax: "<length>+", + inherits: false, + initialValue: "100px 150px" +}, { + keyframes: "200px 250px", + expected: "150px 200px" +}, 'Animating a custom property of type <length>+ with a single keyframe'); + +animation_test({ + syntax: "<length>+", + inherits: false, + initialValue: "50px 100px" +}, { + composite: "add", + keyframes: ["150px 200px", "250px 300px"], + expected: "250px 350px" +}, 'Animating a custom property of type <length>+ with additivity'); + +animation_test({ + syntax: "<length>+", + inherits: false, + initialValue: "50px 100px" +}, { + composite: "add", + keyframes: "150px 200px", + expected: "125px 200px" +}, 'Animating a custom property of type <length>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<length>+", + inherits: false, + initialValue: "0px 0px" +}, { + iterationComposite: "accumulate", + keyframes: ["0px 50px", "100px 100px"], + expected: "250px 275px" +}, 'Animating a custom property of type <length>+ with iterationComposite'); + +discrete_animation_test("<length>+", '10px 20px', '30px', 'Animating a custom property of type <length>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length.html new file mode 100644 index 0000000000..8849bf52d6 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-length.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<length>", + inherits: false, + initialValue: "0px" +}, { + keyframes: ["100px", "200px"], + expected: "150px" +}, 'Animating a custom property of type <length>'); + +animation_test({ + syntax: "<length>", + inherits: false, + initialValue: "100px" +}, { + keyframes: "200px", + expected: "150px" +}, 'Animating a custom property of type <length> with a single keyframe'); + +animation_test({ + syntax: "<length>", + inherits: false, + initialValue: "100px" +}, { + composite: "add", + keyframes: ["200px", "300px"], + expected: "350px" +}, 'Animating a custom property of type <length> with additivity'); + +animation_test({ + syntax: "<length>", + inherits: false, + initialValue: "100px" +}, { + composite: "add", + keyframes: "300px", + expected: "250px" +}, 'Animating a custom property of type <length> with a single keyframe and additivity'); + +animation_test({ + syntax: "<length>", + inherits: false, + initialValue: "100px" +}, { + iterationComposite: "accumulate", + keyframes: ["0px", "100px"], + expected: "250px" +}, 'Animating a custom property of type <length> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html new file mode 100644 index 0000000000..95757445ae --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<number>+ | <transform-list>", + inherits: false, + initialValue: "translateX(100px)" +}, { + keyframes: ["200"], + expected: "200" +}, 'Animating a custom property allowing multiple list types with two mismatching types'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-non-inherited-used-by-standard-property.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-non-inherited-used-by-standard-property.html new file mode 100644 index 0000000000..11d3f6740d --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-non-inherited-used-by-standard-property.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +test(() => { + CSS.registerProperty({ + name: "--my-length", + syntax: "<length>", + inherits: false, + initialValue: "0px" + }); + + target.style.marginLeft = "var(--my-length)"; + + const duration = 1000; + const animation = target.animate({ "--my-length": "100px" }, duration); + animation.pause(); + animation.currentTime = duration / 4; + + assert_equals(getComputedStyle(target).marginLeft, "25px"); +}, "Animating a non-inherited CSS variable is reflected on a standard property using that variable as a value"); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-comma-list.html new file mode 100644 index 0000000000..937b6caeeb --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<number>#", + inherits: false, + initialValue: "0" +}, { + keyframes: ["100, 150", "200, 250"], + expected: "150, 200" +}, 'Animating a custom property of type <number>#'); + +animation_test({ + syntax: "<number>#", + inherits: false, + initialValue: "100, 150" +}, { + keyframes: "200, 250", + expected: "150, 200" +}, 'Animating a custom property of type <number># with a single keyframe'); + +animation_test({ + syntax: "<number>#", + inherits: false, + initialValue: "50, 100" +}, { + composite: "add", + keyframes: ["150, 200", "250, 300"], + expected: "250, 350" +}, 'Animating a custom property of type <number># with additivity'); + +animation_test({ + syntax: "<number>#", + inherits: false, + initialValue: "50, 100" +}, { + composite: "add", + keyframes: "150, 200", + expected: "125, 200" +}, 'Animating a custom property of type <number># with a single keyframe and additivity'); + +animation_test({ + syntax: "<number>#", + inherits: false, + initialValue: "0, 0" +}, { + iterationComposite: "accumulate", + keyframes: ["0, 50", "100, 100"], + expected: "250, 275" +}, 'Animating a custom property of type <number># with iterationComposite'); + +discrete_animation_test("<number>#", '10, 20', '30', 'Animating a custom property of type <number># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-space-list.html new file mode 100644 index 0000000000..61f177eb05 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<number>+", + inherits: false, + initialValue: "0" +}, { + keyframes: ["100 150", "200 250"], + expected: "150 200" +}, 'Animating a custom property of type <number>+'); + +animation_test({ + syntax: "<number>+", + inherits: false, + initialValue: "100 150" +}, { + keyframes: "200 250", + expected: "150 200" +}, 'Animating a custom property of type <number>+ with a single keyframe'); + +animation_test({ + syntax: "<number>+", + inherits: false, + initialValue: "50 100" +}, { + composite: "add", + keyframes: ["150 200", "250 300"], + expected: "250 350" +}, 'Animating a custom property of type <number>+ with additivity'); + +animation_test({ + syntax: "<number>+", + inherits: false, + initialValue: "50 100" +}, { + composite: "add", + keyframes: "150 200", + expected: "125 200" +}, 'Animating a custom property of type <number>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<number>+", + inherits: false, + initialValue: "0 0" +}, { + iterationComposite: "accumulate", + keyframes: ["0 50", "100 100"], + expected: "250 275" +}, 'Animating a custom property of type <number>+ with iterationComposite'); + +discrete_animation_test("<number>+", '10 20', '30', 'Animating a custom property of type <number>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number.html new file mode 100644 index 0000000000..ea28acacf6 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-number.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<number>", + inherits: false, + initialValue: 0 +}, { + keyframes: [100, 200], + expected: "150" +}, 'Animating a custom property of type <number>'); + +animation_test({ + syntax: "<number>", + inherits: false, + initialValue: 100 +}, { + keyframes: 200, + expected: "150" +}, 'Animating a custom property of type <number> with a single keyframe'); + +animation_test({ + syntax: "<number>", + inherits: false, + initialValue: 100 +}, { + composite: "add", + keyframes: [200, 300], + expected: "350" +}, 'Animating a custom property of type <number> with additivity'); + +animation_test({ + syntax: "<number>", + inherits: false, + initialValue: 100 +}, { + composite: "add", + keyframes: 300, + expected: "250" +}, 'Animating a custom property of type <number> with a single keyframe and additivity'); + +animation_test({ + syntax: "<number>", + inherits: false, + initialValue: 100 +}, { + iterationComposite: "accumulate", + keyframes: [0, 100], + expected: "250" +}, 'Animating a custom property of type <number> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-comma-list.html new file mode 100644 index 0000000000..c690046646 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<percentage>#", + inherits: false, + initialValue: "0%" +}, { + keyframes: ["100%, 150%", "200%, 250%"], + expected: "150%, 200%" +}, 'Animating a custom property of type <percentage>#'); + +animation_test({ + syntax: "<percentage>#", + inherits: false, + initialValue: "100%, 150%" +}, { + keyframes: "200%, 250%", + expected: "150%, 200%" +}, 'Animating a custom property of type <percentage># with a single keyframe'); + +animation_test({ + syntax: "<percentage>#", + inherits: false, + initialValue: "50%, 100%" +}, { + composite: "add", + keyframes: ["150%, 200%", "250%, 300%"], + expected: "250%, 350%" +}, 'Animating a custom property of type <percentage># with additivity'); + +animation_test({ + syntax: "<percentage>#", + inherits: false, + initialValue: "50%, 100%" +}, { + composite: "add", + keyframes: "150%, 200%", + expected: "125%, 200%" +}, 'Animating a custom property of type <percentage># with a single keyframe and additivity'); + +animation_test({ + syntax: "<percentage>#", + inherits: false, + initialValue: "0%, 0%" +}, { + iterationComposite: "accumulate", + keyframes: ["0%, 50%", "100%, 100%"], + expected: "250%, 275%" +}, 'Animating a custom property of type <percentage># with iterationComposite'); + +discrete_animation_test("<percentage>#", '10%, 20%', '30%', 'Animating a custom property of type <percentage># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-space-list.html new file mode 100644 index 0000000000..d012b851fd --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<percentage>+", + inherits: false, + initialValue: "0%" +}, { + keyframes: ["100% 150%", "200% 250%"], + expected: "150% 200%" +}, 'Animating a custom property of type <percentage>+'); + +animation_test({ + syntax: "<percentage>+", + inherits: false, + initialValue: "100% 150%" +}, { + keyframes: "200% 250%", + expected: "150% 200%" +}, 'Animating a custom property of type <percentage>+ with a single keyframe'); + +animation_test({ + syntax: "<percentage>+", + inherits: false, + initialValue: "50% 100%" +}, { + composite: "add", + keyframes: ["150% 200%", "250% 300%"], + expected: "250% 350%" +}, 'Animating a custom property of type <percentage>+ with additivity'); + +animation_test({ + syntax: "<percentage>+", + inherits: false, + initialValue: "50% 100%" +}, { + composite: "add", + keyframes: "150% 200%", + expected: "125% 200%" +}, 'Animating a custom property of type <percentage>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<percentage>+", + inherits: false, + initialValue: "0% 0%" +}, { + iterationComposite: "accumulate", + keyframes: ["0% 50%", "100% 100%"], + expected: "250% 275%" +}, 'Animating a custom property of type <percentage>+ with iterationComposite'); + +discrete_animation_test("<percentage>+", '10% 20%', '30%', 'Animating a custom property of type <percentage>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage.html new file mode 100644 index 0000000000..6af498e702 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-percentage.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<percentage>", + inherits: false, + initialValue: "0%" +}, { + keyframes: ["100%", "200%"], + expected: "150%" +}, 'Animating a custom property of type <percentage>'); + +animation_test({ + syntax: "<percentage>", + inherits: false, + initialValue: "100%" +}, { + keyframes: "200%", + expected: "150%" +}, 'Animating a custom property of type <percentage> with a single keyframe'); + +animation_test({ + syntax: "<percentage>", + inherits: false, + initialValue: "100%" +}, { + composite: "add", + keyframes: ["200%", "300%"], + expected: "350%" +}, 'Animating a custom property of type <percentage> with additivity'); + +animation_test({ + syntax: "<percentage>", + inherits: false, + initialValue: "100%" +}, { + composite: "add", + keyframes: "300%", + expected: "250%" +}, 'Animating a custom property of type <percentage> with a single keyframe and additivity'); + +animation_test({ + syntax: "<percentage>", + inherits: false, + initialValue: "100%" +}, { + iterationComposite: "accumulate", + keyframes: ["0%", "100%"], + expected: "250%" +}, 'Animating a custom property of type <percentage> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-comma-list.html new file mode 100644 index 0000000000..22201947ff --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<resolution>#", + inherits: false, + initialValue: "0dppx" +}, { + keyframes: ["100dppx, 150dppx", "200dppx, 250dppx"], + expected: "150dppx, 200dppx" +}, 'Animating a custom property of type <resolution>#'); + +animation_test({ + syntax: "<resolution>#", + inherits: false, + initialValue: "100dppx, 150dppx" +}, { + keyframes: "200dppx, 250dppx", + expected: "150dppx, 200dppx" +}, 'Animating a custom property of type <resolution># with a single keyframe'); + +animation_test({ + syntax: "<resolution>#", + inherits: false, + initialValue: "50dppx, 100dppx" +}, { + composite: "add", + keyframes: ["150dppx, 200dppx", "250dppx, 300dppx"], + expected: "250dppx, 350dppx" +}, 'Animating a custom property of type <resolution># with additivity'); + +animation_test({ + syntax: "<resolution>#", + inherits: false, + initialValue: "50dppx, 100dppx" +}, { + composite: "add", + keyframes: "150dppx, 200dppx", + expected: "125dppx, 200dppx" +}, 'Animating a custom property of type <resolution># with a single keyframe and additivity'); + +animation_test({ + syntax: "<resolution>#", + inherits: false, + initialValue: "0dppx, 0dppx" +}, { + iterationComposite: "accumulate", + keyframes: ["0dppx, 50dppx", "100dppx, 100dppx"], + expected: "250dppx, 275dppx" +}, 'Animating a custom property of type <resolution># with iterationComposite'); + +discrete_animation_test("<resolution>#", '10dppx, 20dppx', '30dppx', 'Animating a custom property of type <resolution># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-space-list.html new file mode 100644 index 0000000000..d4765d4e32 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<resolution>+", + inherits: false, + initialValue: "0dppx" +}, { + keyframes: ["100dppx 150dppx", "200dppx 250dppx"], + expected: "150dppx 200dppx" +}, 'Animating a custom property of type <resolution>+'); + +animation_test({ + syntax: "<resolution>+", + inherits: false, + initialValue: "100dppx 150dppx" +}, { + keyframes: "200dppx 250dppx", + expected: "150dppx 200dppx" +}, 'Animating a custom property of type <resolution>+ with a single keyframe'); + +animation_test({ + syntax: "<resolution>+", + inherits: false, + initialValue: "50dppx 100dppx" +}, { + composite: "add", + keyframes: ["150dppx 200dppx", "250dppx 300dppx"], + expected: "250dppx 350dppx" +}, 'Animating a custom property of type <resolution>+ with additivity'); + +animation_test({ + syntax: "<resolution>+", + inherits: false, + initialValue: "50dppx 100dppx" +}, { + composite: "add", + keyframes: "150dppx 200dppx", + expected: "125dppx 200dppx" +}, 'Animating a custom property of type <resolution>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<resolution>+", + inherits: false, + initialValue: "0dppx 0dppx" +}, { + iterationComposite: "accumulate", + keyframes: ["0dppx 50dppx", "100dppx 100dppx"], + expected: "250dppx 275dppx" +}, 'Animating a custom property of type <resolution>+ with iterationComposite'); + +discrete_animation_test("<resolution>+", '10dppx 20dppx', '30dppx', 'Animating a custom property of type <resolution>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution.html new file mode 100644 index 0000000000..3d05139edc --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-resolution.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<resolution>", + inherits: false, + initialValue: "0dppx" +}, { + keyframes: ["100dppx", "200dppx"], + expected: "150dppx" +}, 'Animating a custom property of type <resolution>'); + +animation_test({ + syntax: "<resolution>", + inherits: false, + initialValue: "100dppx" +}, { + keyframes: "200dppx", + expected: "150dppx" +}, 'Animating a custom property of type <resolution> with a single keyframe'); + +animation_test({ + syntax: "<resolution>", + inherits: false, + initialValue: "100dppx" +}, { + composite: "add", + keyframes: ["200dppx", "300dppx"], + expected: "350dppx" +}, 'Animating a custom property of type <resolution> with additivity'); + +animation_test({ + syntax: "<resolution>", + inherits: false, + initialValue: "100dppx" +}, { + composite: "add", + keyframes: "300dppx", + expected: "250dppx" +}, 'Animating a custom property of type <resolution> with a single keyframe and additivity'); + +animation_test({ + syntax: "<resolution>", + inherits: false, + initialValue: "100dppx" +}, { + iterationComposite: "accumulate", + keyframes: ["0dppx", "100dppx"], + expected: "250dppx" +}, 'Animating a custom property of type <resolution> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-comma-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-comma-list.html new file mode 100644 index 0000000000..bf07baf130 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-comma-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<time>#", + inherits: false, + initialValue: "0s" +}, { + keyframes: ["100s, 150s", "200s, 250s"], + expected: "150s, 200s" +}, 'Animating a custom property of type <time>#'); + +animation_test({ + syntax: "<time>#", + inherits: false, + initialValue: "100s, 150s" +}, { + keyframes: "200s, 250s", + expected: "150s, 200s" +}, 'Animating a custom property of type <time># with a single keyframe'); + +animation_test({ + syntax: "<time>#", + inherits: false, + initialValue: "50s, 100s" +}, { + composite: "add", + keyframes: ["150s, 200s", "250s, 300s"], + expected: "250s, 350s" +}, 'Animating a custom property of type <time># with additivity'); + +animation_test({ + syntax: "<time>#", + inherits: false, + initialValue: "50s, 100s" +}, { + composite: "add", + keyframes: "150s, 200s", + expected: "125s, 200s" +}, 'Animating a custom property of type <time># with a single keyframe and additivity'); + +animation_test({ + syntax: "<time>#", + inherits: false, + initialValue: "0s, 0s" +}, { + iterationComposite: "accumulate", + keyframes: ["0s, 50s", "100s, 100s"], + expected: "250s, 275s" +}, 'Animating a custom property of type <time># with iterationComposite'); + +discrete_animation_test("<time>#", '10s, 20s', '30s', 'Animating a custom property of type <time># with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-space-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-space-list.html new file mode 100644 index 0000000000..c0fe206ea7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time-space-list.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<time>+", + inherits: false, + initialValue: "0s" +}, { + keyframes: ["100s 150s", "200s 250s"], + expected: "150s 200s" +}, 'Animating a custom property of type <time>+'); + +animation_test({ + syntax: "<time>+", + inherits: false, + initialValue: "100s 150s" +}, { + keyframes: "200s 250s", + expected: "150s 200s" +}, 'Animating a custom property of type <time>+ with a single keyframe'); + +animation_test({ + syntax: "<time>+", + inherits: false, + initialValue: "50s 100s" +}, { + composite: "add", + keyframes: ["150s 200s", "250s 300s"], + expected: "250s 350s" +}, 'Animating a custom property of type <time>+ with additivity'); + +animation_test({ + syntax: "<time>+", + inherits: false, + initialValue: "50s 100s" +}, { + composite: "add", + keyframes: "150s 200s", + expected: "125s 200s" +}, 'Animating a custom property of type <time>+ with a single keyframe and additivity'); + +animation_test({ + syntax: "<time>+", + inherits: false, + initialValue: "0s 0s" +}, { + iterationComposite: "accumulate", + keyframes: ["0s 50s", "100s 100s"], + expected: "250s 275s" +}, 'Animating a custom property of type <time>+ with iterationComposite'); + +discrete_animation_test("<time>+", '10s 20s', '30s', 'Animating a custom property of type <time>+ with different lengths is discrete'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time.html new file mode 100644 index 0000000000..b9f5984db7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-time.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<time>", + inherits: false, + initialValue: "0s" +}, { + keyframes: ["100s", "200s"], + expected: "150s" +}, 'Animating a custom property of type <time>'); + +animation_test({ + syntax: "<time>", + inherits: false, + initialValue: "100s" +}, { + keyframes: "200s", + expected: "150s" +}, 'Animating a custom property of type <time> with a single keyframe'); + +animation_test({ + syntax: "<time>", + inherits: false, + initialValue: "100s" +}, { + composite: "add", + keyframes: ["200s", "300s"], + expected: "350s" +}, 'Animating a custom property of type <time> with additivity'); + +animation_test({ + syntax: "<time>", + inherits: false, + initialValue: "100s" +}, { + composite: "add", + keyframes: "300s", + expected: "250s" +}, 'Animating a custom property of type <time> with a single keyframe and additivity'); + +animation_test({ + syntax: "<time>", + inherits: false, + initialValue: "100s" +}, { + iterationComposite: "accumulate", + keyframes: ["0s", "100s"], + expected: "250s" +}, 'Animating a custom property of type <time> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-function.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-function.html new file mode 100644 index 0000000000..c4fcd5ce4c --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-function.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<transform-function>", + inherits: false, + initialValue: "translateX(0px)" +}, { + keyframes: ["translateX(100px)", "translateX(200px)"], + expected: "translateX(150px)" +}, 'Animating a custom property of type <transform-function>'); + +animation_test({ + syntax: "<transform-function>", + inherits: false, + initialValue: "translateX(100px)" +}, { + keyframes: "translateX(200px)", + expected: "translateX(150px)" +}, 'Animating a custom property of type <transform-function> with a single keyframe'); + +animation_test({ + syntax: "<transform-function>", + inherits: false, + initialValue: "translateX(100px)" +}, { + composite: "add", + keyframes: ["translateX(200px)", "translateX(300px)"], + expected: "translateX(350px)" +}, 'Animating a custom property of type <transform-function> with additivity'); + +animation_test({ + syntax: "<transform-function>", + inherits: false, + initialValue: "translateX(100px)" +}, { + composite: "add", + keyframes: "translateX(300px)", + expected: "translateX(250px)" +}, 'Animating a custom property of type <transform-function> with a single keyframe and additivity'); + +animation_test({ + syntax: "<transform-function>", + inherits: false, + initialValue: "translateX(100px)" +}, { + iterationComposite: "accumulate", + keyframes: ["translateX(0px)", "translateX(100px)"], + expected: "translateX(250px)" +}, 'Animating a custom property of type <transform-function> with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html new file mode 100644 index 0000000000..9ffaec830f --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(0px)" +}, { + keyframes: ["translateX(100px) scale(2)", "translateX(200px) scale(4)"], + expected: "translateX(150px) scale(3)" +}, 'Animating a custom property of type <transform-list> containing multiple values'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px) scale(2)" +}, { + keyframes: "translateX(200px) scale(4)", + expected: "translateX(150px) scale(3)" +}, 'Animating a custom property of type <transform-list> containing multiple values with a single keyframe'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px) scale(2)" +}, { + composite: "add", + keyframes: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"], + expected: "translateX(100px) scale(2) translateX(250px) scale(3.5)" +}, 'Animating a custom property of type <transform-list> containing multiple values with additivity'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px) scale(2)" +}, { + composite: "add", + keyframes: "translateX(300px) scale(3)", + expected: "translateX(100px) scale(2) translateX(150px) scale(2)" +}, 'Animating a custom property of type <transform-list> containing multiple values with a single keyframe and additivity'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px) scale(2)" +}, { + iterationComposite: "accumulate", + keyframes: ["translateX(0px) scale(3)", "translateX(100px) scale(4)"], + expected: "translateX(250px) scale(11.5)" +}, 'Animating a custom property of type <transform-list> containing multiple values with iterationComposite'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(0px)" +}, { + keyframes: ["translateX(100px) scale(2)", "rotate(180deg)"], + expected: "matrix(0, -1.5, 1.5, 0, 50, 0)" +}, 'Animating a custom property of type <transform-list> containing multiple values and with mismatching list lengths'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-single-values.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-single-values.html new file mode 100644 index 0000000000..b9bce6dd0b --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-transform-list-single-values.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(0px)" +}, { + keyframes: ["translateX(100px)", "translateX(200px)"], + expected: "translateX(150px)" +}, 'Animating a custom property of type <transform-list> containing a single value'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px)" +}, { + keyframes: "translateX(200px)", + expected: "translateX(150px)" +}, 'Animating a custom property of type <transform-list> containing a single value with a single keyframe'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px)" +}, { + composite: "add", + keyframes: ["translateX(200px)", "translateX(300px)"], + expected: "translateX(100px) translateX(250px)" +}, 'Animating a custom property of type <transform-list> containing a single value with additivity'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px)" +}, { + composite: "add", + keyframes: "translateX(300px)", + expected: "translateX(100px) translateX(150px)" +}, 'Animating a custom property of type <transform-list> containing a single value with a single keyframe and additivity'); + +animation_test({ + syntax: "<transform-list>", + inherits: false, + initialValue: "translateX(100px)" +}, { + iterationComposite: "accumulate", + keyframes: ["translateX(0px)", "translateX(100px)"], + expected: "translateX(250px)" +}, 'Animating a custom property of type <transform-list> containing a single value with iterationComposite'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-url.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-url.html new file mode 100644 index 0000000000..830b9e1f49 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-url.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +discrete_animation_test("<url>", 'url("https://example.com/from")', 'url("https://example.com/to")'); +discrete_animation_test("<url>+", 'url("https://example.com/from1") url("https://example.com/from2")', 'url("https://example.com/to1") url("https://example.com/to2")'); +discrete_animation_test("<url>#", 'url("https://example.com/from1"), url("https://example.com/from2")', 'url("https://example.com/to1"), url("https://example.com/to2")'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-used-in-shorthand.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-used-in-shorthand.html new file mode 100644 index 0000000000..63f7fd3fe7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-animation-used-in-shorthand.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> + +@property --angle { + syntax: "<angle>"; + inherits: false; + initial-value: 0deg; +} + +@keyframes angle-animation { + from { --angle: 0deg } + to { --angle: 180deg } +} + +#target { + animation: angle-animation 1000s linear -500s; + background: linear-gradient(var(--angle), black, white); +} + +</style> + +<div id="target"></div> + +<script> + +test(() => { + assert_equals(getComputedStyle(target).backgroundImage, 'linear-gradient(90deg, rgb(0, 0, 0), rgb(255, 255, 255))') +}, "Animated custom property is applied in a shorthand property."); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-angle.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-angle.html new file mode 100644 index 0000000000..974fc6c0db --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-angle.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<angle>", + from: "100deg", + to: "200deg", + expected: "150deg" +}, 'A custom property of type <angle> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-color.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-color.html new file mode 100644 index 0000000000..445a2a6391 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-color.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<color>", + from: "rgb(100, 100, 100)", + to: "rgb(200, 200, 200)", + expected: "rgb(150, 150, 150)" +}, 'A custom property of type <color> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html new file mode 100644 index 0000000000..73898d350d --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<custom-ident>", + from: "from", + to: "to", + expected: "to", +}, 'A custom property of type <custom-ident> can yield a discrete CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-image.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-image.html new file mode 100644 index 0000000000..30fe35db2b --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-image.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<image>", + from: 'url("https://example.com/from")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <image> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-inherited-used-by-standard-property.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-inherited-used-by-standard-property.html new file mode 100644 index 0000000000..0680722d4a --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-inherited-used-by-standard-property.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="container"> + <div id="target"></div> +</div> +<script> + +test(() => { + const customProperty = "--my-length"; + + CSS.registerProperty({ + name: customProperty, + syntax: "<length>", + inherits: true, + initialValue: "100px" + }); + + target.style.marginLeft = `var(${customProperty})`; + assert_equals(getComputedStyle(target).marginLeft, "100px"); + assert_equals(getComputedStyle(target).getPropertyValue(customProperty), "100px"); + + container.style.transition = `${customProperty} 1s -500ms linear`; + container.style.setProperty(customProperty, "200px"); + + assert_equals(getComputedStyle(target).marginLeft, "150px"); +}, "Running a transition an inherited CSS variable is reflected on a standard property using that variable as a value"); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-integer.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-integer.html new file mode 100644 index 0000000000..64685fe07c --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-integer.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<integer>", + from: "100", + to: "200", + expected: "150" +}, 'A custom property of type <integer> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length-percentage.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length-percentage.html new file mode 100644 index 0000000000..f1ed1dec26 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length-percentage.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<length-percentage>", + from: "100px", + to: "100%", + expected: "calc(50% + 50px)" +}, 'A custom property of type <length-percentage> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length.html new file mode 100644 index 0000000000..f5a76490cc --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-length.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<length>", + from: "100px", + to: "200px", + expected: "150px" +}, 'A custom property of type <length> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-inherited-property-numbers.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-inherited-property-numbers.html new file mode 100644 index 0000000000..292e23b1fe --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-inherited-property-numbers.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="container"><div id="target"></div></div> +<script> + +test(() => { + const customProperty = generate_name(); + CSS.registerProperty({ + name: customProperty, + syntax: "<number>", + inherits: false, + initialValue: "1" + }); + + // Create transitions for our custom property with + // a longer list of transition-duration values. + const container = document.getElementById("container"); + container.style.transitionProperty = customProperty; + container.style.transitionDuration = "100s, 200s"; + + const target = document.getElementById("target"); + target.style.transitionProperty = "inherit"; + target.style.transitionDuration = "inherit"; + + // Trigger a style change by getting the custom property + // value from the computed style. + getComputedStyle(target).getPropertyValue(customProperty); + + // Set a new value for the custom property, which will yield a + // transition. + target.style.setProperty(customProperty, "2"); + const animations = target.getAnimations(); + assert_equals(animations.length, 1, "A single transition was generated"); + + const transition = animations[0]; + assert_class_string(transition, "CSSTransition", "A CSSTransition is running"); + assert_equals(transition.transitionProperty, customProperty); +}, 'Using a single "transition-property" value set to a custom property and two "transition-duration" values correctly yields a CSS Transition when the transition properties are set on a parent and the child inherits.'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html new file mode 100644 index 0000000000..84473ca916 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html @@ -0,0 +1,177 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<angle>#", + from: '100deg, 200deg', + to: '300deg', + expected: '300deg', +}, 'A custom property of type <angle># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<angle>+", + from: '100deg 200deg', + to: '300deg', + expected: '300deg', +}, 'A custom property of type <angle>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<color>#", + from: 'rgb(100, 100, 100), rgb(150, 150, 150)', + to: 'rgb(200, 200, 200)', + expected: 'rgb(200, 200, 200)', +}, 'A custom property of type <color># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<color>+", + from: 'rgb(100, 100, 100) rgb(150, 150, 150)', + to: 'rgb(200, 200, 200)', + expected: 'rgb(200, 200, 200)', +}, 'A custom property of type <color>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<custom-ident>#", + from: 'foo, bar', + to: 'baz', + expected: 'baz', +}, 'A custom property of type <custom-ident># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<custom-ident>+", + from: 'foo bar', + to: 'baz', + expected: 'baz', +}, 'A custom property of type <custom-ident>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<image>#", + from: 'url("https://example.com/foo"), url("https://example.com/bar")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <image># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<image>+", + from: 'url("https://example.com/foo") url("https://example.com/bar")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <image>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<integer>#", + from: '100, 200', + to: '300', + expected: '300', +}, 'A custom property of type <integer># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<integer>+", + from: '100 200', + to: '300', + expected: '300', +}, 'A custom property of type <integer>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<length-percentage>#", + from: '100px, 200px', + to: '300%', + expected: '300%', +}, 'A custom property of type <length-percentage># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<length-percentage>+", + from: '100px 200px', + to: '300%', + expected: '300%', +}, 'A custom property of type <length-percentage>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<length>#", + from: '100px, 200px', + to: '300px', + expected: '300px', +}, 'A custom property of type <length># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<length>+", + from: '100px 200px', + to: '300px', + expected: '300px', +}, 'A custom property of type <length>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<number>#", + from: '100, 200', + to: '300', + expected: '300', +}, 'A custom property of type <number># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<number>+", + from: '100 200', + to: '300', + expected: '300', +}, 'A custom property of type <number>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<percentage>#", + from: '100%, 200%', + to: '300%', + expected: '300%', +}, 'A custom property of type <percentage># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<percentage>+", + from: '100% 200%', + to: '300%', + expected: '300%', +}, 'A custom property of type <percentage>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<resolution>#", + from: '100dppx, 200dppx', + to: '300dppx', + expected: '300dppx', +}, 'A custom property of type <resolution># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<resolution>+", + from: '100dppx 200dppx', + to: '300dppx', + expected: '300dppx', +}, 'A custom property of type <resolution>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<time>#", + from: '100s, 200s', + to: '300s', + expected: '300s', +}, 'A custom property of type <time># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<time>+", + from: '100s 200s', + to: '300s', + expected: '300s', +}, 'A custom property of type <time>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<url>#", + from: 'url("https://example.com/foo"), url("https://example.com/bar")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <url># yields a discrete CSS Transition if the lists do not contain the same number of values'); + +transition_test({ + syntax: "<url>+", + from: 'url("https://example.com/foo") url("https://example.com/bar")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <url>+ yields a discrete CSS Transition if the lists do not contain the same number of values'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-property-numbers.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-property-numbers.html new file mode 100644 index 0000000000..713a035320 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-mismatched-property-numbers.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +test(() => { + const customProperty = generate_name(); + CSS.registerProperty({ + name: customProperty, + syntax: "<number>", + inherits: false, + initialValue: "1" + }); + + // Create transitions for our custom property with + // a longer list of transition-duration values. + const target = document.getElementById("target"); + target.style.transitionProperty = customProperty; + target.style.transitionDuration = "100s, 200s"; + + // Trigger a style change by getting the custom property + // value from the computed style. + getComputedStyle(target).getPropertyValue(customProperty); + + // Set a new value for the custom property, which will yield a + // transition. + target.style.setProperty(customProperty, "2"); + const animations = target.getAnimations(); + assert_equals(animations.length, 1, "A single transition was generated"); + + const transition = animations[0]; + assert_class_string(transition, "CSSTransition", "A CSSTransition is running"); + assert_equals(transition.transitionProperty, customProperty); +}, 'Using a single "transition-property" value set to a custom property and two "transition-duration" values correctly yields a CSS Transition.'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-non-inherited-used-by-standard-property.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-non-inherited-used-by-standard-property.html new file mode 100644 index 0000000000..b96c28bc88 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-non-inherited-used-by-standard-property.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +test(() => { + const customProperty = "--my-length"; + + CSS.registerProperty({ + name: customProperty, + syntax: "<length>", + inherits: false, + initialValue: "100px" + }); + + target.style.marginLeft = `var(${customProperty})`; + assert_equals(getComputedStyle(target).marginLeft, "100px"); + + target.style.transition = `${customProperty} 1s -500ms linear`; + target.style.setProperty(customProperty, "200px"); + + assert_equals(getComputedStyle(target).marginLeft, "150px"); +}, "Running a transition a non-inherited CSS variable is reflected on a standard property using that variable as a value"); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-number.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-number.html new file mode 100644 index 0000000000..a96e319686 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-number.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<number>", + from: "100", + to: "200", + expected: "150" +}, 'A custom property of type <number> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-percentage.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-percentage.html new file mode 100644 index 0000000000..71d51b0e27 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-percentage.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<percentage>", + from: "100%", + to: "200%", + expected: "150%" +}, 'A custom property of type <percentage> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-property-all.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-property-all.html new file mode 100644 index 0000000000..6fc724cd35 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-property-all.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<length>", + from: "100px", + to: "200px", + expected: "150px", + transitionProperty: "all" +}, 'A custom property can yield a CSS Transition with transition-property: all'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-resolution.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-resolution.html new file mode 100644 index 0000000000..5631910b7c --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-resolution.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<resolution>", + from: "100dppx", + to: "200dppx", + expected: "150dppx" +}, 'A custom property of type <resolution> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-time.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-time.html new file mode 100644 index 0000000000..eb579b71f1 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-time.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<time>", + from: "100s", + to: "200s", + expected: "150s" +}, 'A custom property of type <time> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-function.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-function.html new file mode 100644 index 0000000000..aa491555c7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-function.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<transform-function>", + from: "translateX(100px)", + to: "translateX(200px)", + expected: "translateX(150px)" +}, 'A custom property of type <transform-function> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-list.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-list.html new file mode 100644 index 0000000000..8b7424fec5 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-transform-list.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<transform-list>", + from: "translateX(100px) scale(2)", + to: "translateX(200px) scale(4)", + expected: "translateX(150px) scale(3)" +}, 'A custom property of type <transform-list> can yield a CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-url.html b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-url.html new file mode 100644 index 0000000000..4f22a91325 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/custom-property-transition-url.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<div id="target"></div> +<script> + +transition_test({ + syntax: "<url>", + from: 'url("https://example.com/from")', + to: 'url("https://example.com/to")', + expected: 'url("https://example.com/to")', +}, 'A custom property of type <url> can yield a discrete CSS Transition'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-animation.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-animation.html new file mode 100644 index 0000000000..233b63239f --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-animation.html @@ -0,0 +1,451 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=outer> + <div id=div></div> +</div> +<script> + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + @keyframes test { + from { ${name}: 100px; } + to { ${name}: 200px; } + } + #div { animation: test 100s -50s linear; } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '150px'); + }); +}, '@keyframes works with @property'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + @property ${name} { + syntax: "<color>"; + inherits: false; + initial-value: black; + } + @keyframes test { + from { ${name}: rgb(100, 100, 100); } + to { ${name}: rgb(200, 200, 200); } + } + #div { animation: test 100s -50s linear; } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(150, 150, 150)'); + }); +}, '@keyframes picks up the latest @property in the document'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + // These keyframes are initially invalid for the declared custom property. + let animation = div.animate([ + { [name]: 'rgb(100, 100, 100)'}, + { [name]: 'rgb(200, 200, 200)'}, + ], { duration: 10000, delay: -5000, easing: 'linear' }); + let cs = getComputedStyle(div); + assert_equals(cs.getPropertyValue(name), '0px'); + + // Redeclare the property as a <color>, effectively making the existing + // keyframes valid. + with_at_property({ + name: name, + syntax: '"<color>"', + inherits: false, + initialValue: 'black' + }, (name) => { + assert_equals(cs.getPropertyValue(name), 'rgb(150, 150, 150)'); + }); + + animation.finish(); +}, 'Ongoing animation picks up redeclared custom property'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + // These keyframes are initially invalid for the declared custom property. + let animation = div.animate([ + { [name]: 'rgb(100, 100, 100)'}, + { [name]: 'rgb(200, 200, 200)'}, + ], { duration: 10000, delay: -5000, easing: 'linear' }); + let cs = getComputedStyle(div); + assert_equals(cs.getPropertyValue(name), '0px'); + + // Setting the keyframes to something that matches <length> makes the + // interpolation valid. + animation.effect.setKeyframes([ + {[name]: '100px'}, + {[name]: '200px'} + ]); + assert_equals(cs.getPropertyValue(name), '150px'); + + animation.finish(); +}, 'Ongoing animation matches new keyframes against the current registration'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + let animation = div.animate([ + { [name]: 'initial'}, + { [name]: '400px'}, + ], { duration: 10000, delay: -5000, easing: 'linear' }); + let cs = getComputedStyle(div); + assert_equals(cs.getPropertyValue(name), '200px'); + + // Change initial value. + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '100px' + }, (name) => { + assert_equals(cs.getPropertyValue(name), '250px'); + }); + + animation.finish(); +}, 'Ongoing animation picks up redeclared intial value'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + try { + document.body.style = `${name}: 100px`; + // Note that 'inherit' here refers to #outer, which has the initial + // value. (#outer did not inherit from body, since the property is not + // yet declared as inherited). + let animation = div.animate([ + { [name]: 'inherit'}, + { [name]: '400px'}, + ], { duration: 10000, delay: -5000, easing: 'linear' }); + let cs = getComputedStyle(div); + assert_equals(cs.getPropertyValue(name), '200px'); + + // Change inherits to 'true'. The value should now propagate from body + // to #outer. + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: true, + initialValue: '0px' + }, (name) => { + assert_equals(cs.getPropertyValue(name), '250px'); + }); + + animation.finish(); + } finally { + document.body.style = ''; + } +}, 'Ongoing animation picks up redeclared inherits flag'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + try { + outer.style = `${name}: 100px`; + // 'unset' should take the initial value (not the value from #outer), since + // the property is not declared as inherited. + let animation = div.animate([ + { [name]: 'unset'}, + { [name]: '400px'}, + ], { duration: 10000, delay: -5000, easing: 'linear' }); + let cs = getComputedStyle(div); + assert_equals(cs.getPropertyValue(name), '200px'); + + // Change inherits to 'true'. 'unset' now refers to #outer's value. + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: true, + initialValue: '0px' + }, (name) => { + assert_equals(cs.getPropertyValue(name), '250px'); + }); + + animation.finish(); + } finally { + outer.style = ''; + } +}, 'Ongoing animation picks up redeclared meaning of \'unset\''); + +test_with_at_property({ + syntax: '"<color>"', + inherits: false, + initialValue: 'red' +}, (name) => { + try { + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(255, 0, 0)'); + div.style = `transition: ${name} steps(2, start) 100s; ${name}: blue`; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(128, 0, 128)'); + } finally { + div.style = ''; + } +}, 'Transitioning from initial value'); + +test_with_at_property({ + syntax: '"<color>"', + inherits: false, + initialValue: 'red' +}, (name) => { + try { + div.style = `${name}: blue;`; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(0, 0, 255)'); + div.style = `transition: ${name} steps(2, start) 100s; ${name}: green`; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(0, 64, 128)'); + } finally { + div.style = ''; + } +}, 'Transitioning from specified value'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '100px' +}, (name) => { + with_style_node(`div { transition: ${name} steps(2, start) 100s; }`, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '100px'); + // Re-declaring the property with a different initial value effectively + // means the computed value has changed. This means we should transition + // from the old initial value to the new initial value. + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '200px' + }, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '150px'); + }); + }); +}, 'Transition triggered by initial value change'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '100px' +}, (name) => { + with_style_node(`div { transition: ${name} steps(2, start) 100s; }`, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '100px'); + with_at_property({ + name: name, + syntax: '"<color>"', + inherits: false, + initialValue: 'green' + }, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(0, 128, 0)'); + }); + }); +}, 'No transition when changing types'); + +test(() => { + let name = generate_name(); + with_style_node(`div { ${name}: 100px; transition: ${name} steps(2, start) 100s; }`, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '100px'); + + let style1 = document.createElement('style'); + style1.textContent = ` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 200px; + } + `; + + let style2 = document.createElement('style'); + style2.textContent = `div { ${name}: 400px; }`; + + try { + // Register the property: + document.body.append(style1); + // The token sequence ' 100px' is now interpreted as a length '100px'. + assert_equals(getComputedStyle(div).getPropertyValue(name), '100px'); + + // Change the computed value: + document.body.append(style2); + // This should cause an interpolation between 100px and 400px: + assert_equals(getComputedStyle(div).getPropertyValue(name), '250px'); + + // In the middle of the transition above, remove the @property rule + // (making the computed value a token sequence again). We should snap + // to the new token sequence. + style1.remove(); + assert_equals(getComputedStyle(div).getPropertyValue(name), '400px'); + } finally { + style1.remove(); + style2.remove(); + } + }); +}, 'No transition when removing @property rule'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + @keyframes test { + from { ${name}: 100px; } + to { ${name}: 200px; } + } + #div { + animation: test 100s -50s linear; + --unregistered: var(${name}); + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue('--unregistered'), '150px'); + }); +}, 'Unregistered properties referencing animated properties update correctly.'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + @keyframes test { + from { ${name}: 100px; } + to { ${name}: 200px; } + } + @property --registered { + syntax: "<length>"; + inherits: false; + initialValue: 0px; + } + #div { + animation: test 100s -50s linear; + --registered: var(${name}); + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue('--registered'), '150px'); + }); +}, 'Registered properties referencing animated properties update correctly.'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + @keyframes test { + from { ${name}: inherit; } + to { ${name}: 300px; } + } + #outer { + ${name}: 100px; + } + #div { + animation: test 100s -50s linear paused; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '200px'); + + outer.style.setProperty(name, '200px'); + assert_equals(getComputedStyle(div).getPropertyValue(name), '250px'); + + outer.style.setProperty(name, '0px'); + assert_equals(getComputedStyle(div).getPropertyValue(name), '150px'); + + outer.style.removeProperty(name); + }); +}, 'CSS animation setting "inherit" for a custom property on a keyframe is responsive to changing that custom property on the parent.'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name) => { + with_style_node(` + #outer { + ${name}: 100px; + } + `, () => { + const animation = div.animate({ [name]: ["inherit", "300px"]}, 1000); + animation.currentTime = 500; + animation.pause(); + + assert_equals(getComputedStyle(div).getPropertyValue(name), '200px'); + + outer.style.setProperty(name, '200px'); + assert_equals(getComputedStyle(div).getPropertyValue(name), '250px'); + + outer.style.setProperty(name, '0px'); + assert_equals(getComputedStyle(div).getPropertyValue(name), '150px'); + + outer.style.removeProperty(name); + }); +}, 'JS-originated animation setting "inherit" for a custom property on a keyframe is responsive to changing that custom property on the parent.'); + +test_with_at_property({ + syntax: '"<color>"', + inherits: false, + initialValue: 'black' +}, (name) => { + with_style_node(` + @keyframes test { + from { ${name}: currentcolor; } + to { ${name}: rgb(200, 200, 200); } + } + #outer { + color: rgb(100, 100, 100); + } + #div { + animation: test 100s -50s linear paused; + } + `, (style) => { + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(150, 150, 150)'); + + outer.style.color = 'rgb(50, 50, 50)'; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(125, 125, 125)'); + + outer.style.color = 'rgb(150, 150, 150)'; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(175, 175, 175)'); + + outer.style.removeProperty("color"); + }); +}, 'CSS animation setting "currentColor" for a custom property on a keyframe is responsive to changing "color" on the parent.'); + +test_with_at_property({ + syntax: '"<color>"', + inherits: false, + initialValue: 'black' +}, (name) => { + with_style_node(` + #outer { + color: rgb(100, 100, 100); + } + `, () => { + const animation = div.animate({ [name]: ["currentcolor", "rgb(200, 200, 200)"]}, 1000); + animation.currentTime = 500; + animation.pause(); + + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(150, 150, 150)'); + + outer.style.color = 'rgb(50, 50, 50)'; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(125, 125, 125)'); + + outer.style.color = 'rgb(150, 150, 150)'; + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(175, 175, 175)'); + + outer.style.removeProperty("color"); + }); +}, 'JS-originated animation setting "currentColor" for a custom property on a keyframe is responsive to changing "color" on the parent.'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-cssom.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-cssom.html new file mode 100644 index 0000000000..f7981f245a --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-cssom.html @@ -0,0 +1,197 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#cssom"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @property --valid { + syntax: "<color> | none"; + inherits: false; + initial-value: red; + } + @property --valid-reverse { + initial-value: 0px; + inherits: true; + syntax: "<length>"; + } + @property --valid-universal { + syntax: "*"; + inherits: false; + } + @property --valid-whitespace { + syntax: " <color>+ "; + inherits: false; + initial-value: red, blue; + } + @property --vALId { + syntax: "<color> | none"; + inherits: false; + initial-value: red; + } + @property --no-descriptors { + + } + @property --no-syntax { + inherits: false; + initial-value: red; + } + @property --no-inherits { + syntax: "<color> | none"; + initial-value: red; + } + @property --no-initial-value { + syntax: "<color> | none"; + inherits: false; + } + @property --syntax-only { + syntax: "<color> | none"; + } + @property --inherits-only { + inherits: true; + } + @property --initial-value-only { + initial-value: red; + } + /* U+0009 CHARACTER TABULATION */ + @property --tab\9 tab { } +</style> +<script> + +function find_at_property_rule(name) { + for (let rule of document.styleSheets[0].cssRules) { + if (rule.constructor.name != "CSSPropertyRule") + continue; + if (rule.name == name) + return rule; + } + return null; +} + +function test_css_text(name, expected) { + test(() => { + let rule = find_at_property_rule(name); + assert_true(!!rule); + assert_equals(rule.cssText, expected); + }, `Rule for ${name} has expected cssText`); +} + +function test_name(name) { + test(() => { + let rule = find_at_property_rule(name); + assert_true(!!rule); + assert_equals(rule.name, name); + }, `Rule for ${name} returns expected value for CSSPropertyRule.name`); +} + +function test_syntax(name, expected) { + test(() => { + let rule = find_at_property_rule(name); + assert_true(!!rule); + assert_equals(rule.syntax, expected); + }, `Rule for ${name} returns expected value for CSSPropertyRule.syntax`); +} + +function test_inherits(name, expected) { + test(() => { + let rule = find_at_property_rule(name); + assert_true(!!rule); + assert_equals(rule.inherits, expected); + }, `Rule for ${name} returns expected value for CSSPropertyRule.inherits`); +} + +function test_initial_value(name, expected) { + test(() => { + let rule = find_at_property_rule(name); + assert_true(!!rule); + assert_equals(rule.initialValue, expected); + }, `Rule for ${name} returns expected value for CSSPropertyRule.initialValue`); +} + +// CSSPropertyRule.cssText + +test_css_text('--valid', '@property --valid { syntax: "<color> | none"; inherits: false; initial-value: red; }'); +test_css_text('--valid-reverse', '@property --valid-reverse { syntax: "<length>"; inherits: true; initial-value: 0px; }'); +test_css_text('--valid-universal', '@property --valid-universal { syntax: "*"; inherits: false; }'); +test_css_text('--valid-whitespace', '@property --valid-whitespace { syntax: " <color>+ "; inherits: false; initial-value: red, blue; }'); +test_css_text('--vALId', '@property --vALId { syntax: "<color> | none"; inherits: false; initial-value: red; }'); + +test_css_text('--no-descriptors', '@property --no-descriptors { }'); +test_css_text('--no-syntax', '@property --no-syntax { inherits: false; initial-value: red; }'); +test_css_text('--no-inherits', '@property --no-inherits { syntax: "<color> | none"; initial-value: red; }'); +test_css_text('--no-initial-value', '@property --no-initial-value { syntax: "<color> | none"; inherits: false; }'); +test_css_text('--syntax-only', '@property --syntax-only { syntax: "<color> | none"; }'); +test_css_text('--inherits-only', '@property --inherits-only { inherits: true; }'); +test_css_text('--initial-value-only', '@property --initial-value-only { initial-value: red; }'); +test_css_text('--tab\ttab', '@property --tab\\9 tab { }'); + +// CSSRule.type + +test(() => { + let rule = find_at_property_rule('--valid'); + assert_equals(rule.type, 0); +}, 'CSSRule.type returns 0'); + +// CSSPropertyRule.name + +test_name('--valid'); +test_name('--valid-reverse'); +test_name('--valid-universal'); +test_name('--valid-whitespace'); +test_name('--vALId'); + +test_name('--no-descriptors'); +test_name('--no-syntax'); +test_name('--no-inherits'); +test_name('--no-initial-value'); +test_name('--syntax-only'); +test_name('--inherits-only'); +test_name('--initial-value-only'); + +// CSSPropertyRule.syntax + +test_syntax('--valid', '<color> | none'); +test_syntax('--valid-reverse', '<length>'); +test_syntax('--valid-universal', '*'); +test_syntax('--valid-whitespace', ' <color>+ '); +test_syntax('--vALId', '<color> | none'); + +test_syntax('--no-descriptors', ''); +test_syntax('--no-syntax', ''); +test_syntax('--no-inherits', '<color> | none'); +test_syntax('--no-initial-value', '<color> | none'); +test_syntax('--syntax-only', '<color> | none'); +test_syntax('--inherits-only', ''); +test_syntax('--initial-value-only', ''); + +// CSSPropertyRule.inherits + +test_inherits('--valid', false); +test_inherits('--valid-reverse', true); +test_inherits('--valid-universal', false); +test_inherits('--valid-whitespace', false); +test_inherits('--vALId', false); + +test_inherits('--no-descriptors', false); +test_inherits('--no-syntax', false); +test_inherits('--no-inherits', false); +test_inherits('--no-initial-value', false); +test_inherits('--syntax-only', false); +test_inherits('--inherits-only', true); +test_inherits('--initial-value-only', false); + +// CSSPropertyRule.initialValue + +test_initial_value('--valid', 'red'); +test_initial_value('--valid-reverse', '0px'); +test_initial_value('--valid-universal', null); +test_initial_value('--valid-whitespace', 'red, blue'); +test_initial_value('--vALId', 'red'); + +test_initial_value('--no-descriptors', null); +test_initial_value('--no-syntax', 'red'); +test_initial_value('--no-inherits', 'red'); +test_initial_value('--no-initial-value', null); +test_initial_value('--syntax-only', null); +test_initial_value('--inherits-only', null); +test_initial_value('--initial-value-only', 'red'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-non-matching-media-crash.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-non-matching-media-crash.html new file mode 100644 index 0000000000..c5b894f349 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-non-matching-media-crash.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html class="test-wait"> + <link rel="help" href="https://crbug.com/1085994"> + <style id="style"> + @property --x { + syntax: "<length>"; + inherits: false; + initial-value: 0px; + } + </style> + <script> + document.documentElement.offsetTop; + style.setAttribute('media', 'braille'); + document.documentElement.className = ''; + </script> + <p> + PASS if no crash + </p> +</html> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-shadow.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-shadow.html new file mode 100644 index 0000000000..db282d10b0 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-shadow.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<style> + @property --x { + syntax: "<length>"; + inherits: false; + initial-value: 0px; + } + #outside { + --x: calc(1px + 1px); + --y: calc(1px + 1px); + } +</style> +<template id=template> + <style> + /* This rule should have no effect */ + @property --y { + syntax: "<length>"; + inherits: false; + initial-value: 0px; + } + #inside { + --x: calc(1px + 1px); + --y: calc(1px + 1px); + } + </style> + <div id=inside></div> +</template> +<div id=host></div> +<div id=outside></div> +<script> + +test(() => { + let root = host.attachShadow({ mode: 'open' }); + root.append(template.content.cloneNode(true)); + let inside = root.querySelector('#inside'); + assert_equals(getComputedStyle(outside).getPropertyValue('--x'), '2px'); + assert_equals(getComputedStyle(outside).getPropertyValue('--y'), 'calc(1px + 1px)'); + assert_equals(getComputedStyle(inside).getPropertyValue('--x'), '2px'); + assert_equals(getComputedStyle(inside).getPropertyValue('--y'), 'calc(1px + 1px)'); +}, '@property rules in shadow trees should have no effect'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-stylesheets.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-stylesheets.html new file mode 100644 index 0000000000..a7f3ea3948 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-stylesheets.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<title>Verify that the correct registration is selected for mutated stylesheets</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=div></div> +<script> + +test(() => { + with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '1px' + }, (name) => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, '@property detected when stylesheet appears'); + +test(() => { + let name = generate_name(); + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '1px' + }, (name) => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); + assert_equals(getComputedStyle(div).getPropertyValue(name), ''); +}, '@property removal detected when last @property rule disappears'); + +test(() => { + with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '1px' + }, (name1) => { + with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '2px' + }, (name2) => { + assert_equals(getComputedStyle(div).getPropertyValue(name2), '2px'); + }); + }); +}, '@property detected in second stylesheet'); + +test(() => { + let name2 = generate_name(); + with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '1px' + }, (name1) => { + with_at_property({ + name2: name2, + syntax: '"<length>"', + inherits: false, + initialValue: '2px' + }, (name2) => { + assert_equals(getComputedStyle(div).getPropertyValue(name2), '2px'); + }); + assert_equals(getComputedStyle(div).getPropertyValue(name2), ''); + }); +}, '@property removal detected with removal of second stylesheet'); + +test(() => { + let name1 = generate_name(); + let name2 = generate_name(); + + let sheet1 = ` + @property ${name1} { + inherits: false; + syntax: "<length>"; + initial-value: 1px; + } + `; + let sheet2 = ` + @property ${name2} { + inherits: false; + syntax: "<length>"; + initial-value: 2px; + } + `; + + let node1 = document.createElement('style'); + let node2 = document.createElement('style'); + + node1.textContent = sheet1; + node2.textContent = sheet2; + + try { + document.body.append(node1, node2); + assert_equals(getComputedStyle(div).getPropertyValue(name1), '1px'); + assert_equals(getComputedStyle(div).getPropertyValue(name2), '2px'); + node1.remove(); + assert_equals(getComputedStyle(div).getPropertyValue(name1), ''); + assert_equals(getComputedStyle(div).getPropertyValue(name2), '2px'); + } finally { + node1.remove(); + node2.remove(); + } +}, '@property removal detected with removal of first stylesheet'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-typedom.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-typedom.html new file mode 100644 index 0000000000..beee032429 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-typedom.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#css-style-value-reification"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=div></div> +<script> + +test(() => { + let name = generate_name(); + with_style_node(`div { ${name}: 100px; }`, () => { + // Before registering the property, ${name} should reify as a + // a token sequence. + assert_equals(div.computedStyleMap().get(name).constructor.name, 'CSSUnparsedValue'); + assert_equals(div.computedStyleMap().get(name).toString(), '100px'); + + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '0px' + }, () => { + // After registering, it should reify as a <length>. + assert_equals(div.computedStyleMap().get(name).constructor.name, 'CSSUnitValue'); + assert_equals(div.computedStyleMap().get(name).value, 100); + assert_equals(div.computedStyleMap().get(name).unit, 'px'); + }); + + // After @property is removed, the computed value is once again a token + // sequence. + assert_equals(div.computedStyleMap().get(name).constructor.name, 'CSSUnparsedValue'); + assert_equals(div.computedStyleMap().get(name).toString(), '100px'); + }); +}, 'Properties declared with @property reify correctly'); + +test(() => { + let name = generate_name(); + // 0 is valid as a both <length> and <integer>, which reify differently. + with_style_node(`div { ${name}: 0; }`, () => { + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '1000px' + }, () => { + assert_equals(div.computedStyleMap().get(name).constructor.name, 'CSSUnitValue'); + assert_equals(div.computedStyleMap().get(name).value, 0); + assert_equals(div.computedStyleMap().get(name).unit, 'px'); + + with_at_property({ + name: name, + syntax: '"<integer>"', + inherits: false, + initialValue: '1000' + }, () => { + assert_equals(div.computedStyleMap().get(name).constructor.name, 'CSSUnitValue'); + assert_equals(div.computedStyleMap().get(name).value, 0); + assert_equals(div.computedStyleMap().get(name).unit, 'number'); + }); + }); + }); +}, 'Re-declaring a property with a different type affects reification'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units-dynamic.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units-dynamic.html new file mode 100644 index 0000000000..68adff6887 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units-dynamic.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>@property: viewport units in initial value (dynamic)</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + iframe { + width: 400px; + height: 200px; + } +</style> +<iframe id=iframe srcdoc=" + <style> + @property --10vw { syntax: '<length>'; inherits: true; initial-value: 10vw} + @property --10vh { syntax: '<length>'; inherits: true; initial-value: 10vh} + div { + background: green; + width: var(--10vw); + height: var(--10vh); + } + </style> + <div style='width:10vw'></div> +"></iframe> +<script> + iframe.offsetTop; + + function waitForLoad(w) { + return new Promise(resolve => w.addEventListener('load', resolve)); + } + + promise_test(async (t) => { + await waitForLoad(window); + let element = iframe.contentDocument.querySelector('div'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vw'), '40px'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vh'), '20px'); + + iframe.style.width = '100px'; + assert_equals(getComputedStyle(element).getPropertyValue('--10vw'), '10px'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vh'), '20px'); + }); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units.html b/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units.html new file mode 100644 index 0000000000..51520c2a70 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property-viewport-units.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<title>@property: viewport units in initial value</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + iframe { + width: 400px; + height: 200px; + } +</style> +<iframe id=iframe srcdoc=" + <style> + @property --10vw { syntax: '<length>'; inherits: true; initial-value: 10vw} + @property --10vh { syntax: '<length>'; inherits: true; initial-value: 10vh} + @property --10vi { syntax: '<length>'; inherits: true; initial-value: 10vi} + @property --10vb { syntax: '<length>'; inherits: true; initial-value: 10vb} + @property --10vmin { syntax: '<length>'; inherits: true; initial-value: 10vmin} + @property --10vmax { syntax: '<length>'; inherits: true; initial-value: 10vmax} + + @property --10svw { syntax: '<length>'; inherits: true; initial-value: 10svw} + @property --10svh { syntax: '<length>'; inherits: true; initial-value: 10svh} + @property --10svi { syntax: '<length>'; inherits: true; initial-value: 10svi} + @property --10svb { syntax: '<length>'; inherits: true; initial-value: 10svb} + @property --10svmin { syntax: '<length>'; inherits: true; initial-value: 10svmin} + @property --10svmax { syntax: '<length>'; inherits: true; initial-value: 10svmax} + + @property --10lvw { syntax: '<length>'; inherits: true; initial-value: 10lvw} + @property --10lvh { syntax: '<length>'; inherits: true; initial-value: 10lvh} + @property --10lvi { syntax: '<length>'; inherits: true; initial-value: 10lvi} + @property --10lvb { syntax: '<length>'; inherits: true; initial-value: 10lvb} + @property --10lvmin { syntax: '<length>'; inherits: true; initial-value: 10lvmin} + @property --10lvmax { syntax: '<length>'; inherits: true; initial-value: 10lvmax} + + @property --10dvw { syntax: '<length>'; inherits: true; initial-value: 10dvw} + @property --10dvh { syntax: '<length>'; inherits: true; initial-value: 10dvh} + @property --10dvi { syntax: '<length>'; inherits: true; initial-value: 10dvi} + @property --10dvb { syntax: '<length>'; inherits: true; initial-value: 10dvb} + @property --10dvmin { syntax: '<length>'; inherits: true; initial-value: 10dvmin} + @property --10dvmax { syntax: '<length>'; inherits: true; initial-value: 10dvmax} + </style> + <div></div> +"></iframe> +<script> + iframe.offsetTop; + + function waitForLoad(w) { + return new Promise(resolve => { + if (w.document.readyState == 'complete') + resolve(); + else + w.addEventListener('load', resolve) + }); + } + + function test_unit(element, actual, expected) { + promise_test(async (t) => { + await waitForLoad(window); + let element = iframe.contentDocument.querySelector('div'); + assert_equals(getComputedStyle(element).getPropertyValue(`--${actual}`), expected); + },`${actual} is ${expected}`); + } + + test_unit(iframe, '10vw', '40px'); + test_unit(iframe, '10vh', '20px'); + test_unit(iframe, '10vi', '40px'); + test_unit(iframe, '10vb', '20px'); + test_unit(iframe, '10vmin', '20px'); + test_unit(iframe, '10vmax', '40px'); + + test_unit(iframe, '10svw', '40px'); + test_unit(iframe, '10svh', '20px'); + test_unit(iframe, '10svi', '40px'); + test_unit(iframe, '10svb', '20px'); + test_unit(iframe, '10svmin', '20px'); + test_unit(iframe, '10svmax', '40px'); + + test_unit(iframe, '10lvw', '40px'); + test_unit(iframe, '10lvh', '20px'); + test_unit(iframe, '10lvi', '40px'); + test_unit(iframe, '10lvb', '20px'); + test_unit(iframe, '10lvmin', '20px'); + test_unit(iframe, '10lvmax', '40px'); + + test_unit(iframe, '10dvw', '40px'); + test_unit(iframe, '10dvh', '20px'); + test_unit(iframe, '10dvi', '40px'); + test_unit(iframe, '10dvb', '20px'); + test_unit(iframe, '10dvmin', '20px'); + test_unit(iframe, '10dvmax', '40px'); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/at-property.html b/testing/web-platform/tests/css/css-properties-values-api/at-property.html new file mode 100644 index 0000000000..95d30af15a --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/at-property.html @@ -0,0 +1,243 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id="outer"> + <div id="target"></div> +</div> +<script> + +// Parsing: + +let uppercase_first = (x) => x.charAt(0).toUpperCase() + x.slice(1); +let to_camel_case = (x) => x.split('-')[0] + x.split('-').slice(1).map(uppercase_first).join(''); + +function get_cssom_descriptor_value(rule, descriptor) { + switch (descriptor) { + case 'syntax': + return rule.syntax; + case 'inherits': + return rule.inherits; + case 'initial-value': + return rule.initialValue; + default: + assert_true(false, 'Should not reach here'); + return null; + } +} + +// Test that for the given descriptor (e.g. 'syntax'), the specified value +// will yield the expected_value when observed using CSSOM. If the expected_value +// is omitted, it is the same as the specified value. +function test_descriptor(descriptor, specified_value, expected_value) { + let camel = to_camel_case(descriptor); + if (typeof(expected_value) === 'undefined') + expected_value = specified_value; + test_with_at_property({ [camel]: specified_value }, (name, rule) => { + assert_equals(get_cssom_descriptor_value(rule, descriptor), expected_value); + }, `Attribute '${descriptor}' returns expected value for [${specified_value}]`); +} + +// syntax +test_descriptor('syntax', '"<color>"', '<color>'); +test_descriptor('syntax', '"<color> | none"', '<color> | none'); +test_descriptor('syntax', '"<color># | <image> | none"', '<color># | <image> | none'); +test_descriptor('syntax', '"foo | <length>#"', 'foo | <length>#'); +test_descriptor('syntax', '"foo | bar | baz"', 'foo | bar | baz'); +test_descriptor('syntax', '"notasyntax"', 'notasyntax'); + +// syntax: universal +for (const syntax of ["*", " * ", "* ", "\t*\t"]) { + test_descriptor('syntax', `"${syntax}"`, syntax); +} + +test_descriptor('syntax', 'red', ''); +test_descriptor('syntax', 'rgb(255, 0, 0)', ''); +test_descriptor('syntax', '<color>', ''); +test_descriptor('syntax', 'foo | bar', ''); + +// syntax: pipe between components +test_descriptor('syntax', 'foo bar', ''); +test_descriptor('syntax', 'Foo <length>', ''); +test_descriptor('syntax', 'foo, bar', ''); +test_descriptor('syntax', '<length> <percentage>', ''); + +// syntax: leaading bar +test_descriptor('syntax', '|<length>', ''); + +// initial-value +test_descriptor('initial-value', '10px'); +test_descriptor('initial-value', 'rgb(1, 2, 3)'); +test_descriptor('initial-value', 'red'); +test_descriptor('initial-value', 'foo'); +test_descriptor('initial-value', 'if(){}'); +test_descriptor('initial-value', 'var(--x)'); + +// inherits +test_descriptor('inherits', 'true', true); +test_descriptor('inherits', 'false', false); + +test_descriptor('inherits', 'none', false); +test_descriptor('inherits', '0', false); +test_descriptor('inherits', '1', false); +test_descriptor('inherits', '"true"', false); +test_descriptor('inherits', '"false"', false); +test_descriptor('inherits', 'calc(0)', false); + +test_with_style_node('@property foo { }', (node) => { + assert_equals(node.sheet.rules.length, 0); +}, 'Invalid property name does not parse [foo]'); + +test_with_style_node('@property -foo { }', (node) => { + assert_equals(node.sheet.rules.length, 0); +}, 'Invalid property name does not parse [-foo]'); + +// Applying @property rules + +function test_applied(syntax, initial, inherits, expected) { + test_with_at_property({ + syntax: `"${syntax}"`, + initialValue: initial, + inherits: inherits + }, (name, rule) => { + let actual = getComputedStyle(target).getPropertyValue(name); + assert_equals(actual, expected); + }, `Rule applied [${syntax}, ${initial}, ${inherits}]`); +} + +function test_not_applied(syntax, initial, inherits) { + test_with_at_property({ + syntax: `"${syntax}"`, + initialValue: initial, + inherits: inherits + }, (name, rule) => { + let actual = getComputedStyle(target).getPropertyValue(name); + assert_equals(actual, ''); + }, `Rule not applied [${syntax}, ${initial}, ${inherits}]`); +} + +// syntax, initialValue, inherits, expected +test_applied('*', 'if(){}', false, 'if(){}'); +test_applied('<angle>', '42deg', false, '42deg'); +test_applied('<angle>', '1turn', false, '360deg'); +test_applied('<color>', 'green', false, 'rgb(0, 128, 0)'); +test_applied('<color>', 'rgb(1, 2, 3)', false, 'rgb(1, 2, 3)'); +test_applied('<image>', 'url("http://a/")', false, 'url("http://a/")'); +test_applied('<integer>', '5', false, '5'); +test_applied('<length-percentage>', '10px', false, '10px'); +test_applied('<length-percentage>', '10%', false, '10%'); +test_applied('<length-percentage>', 'calc(10% + 10px)', false, 'calc(10% + 10px)'); +test_applied('<length>', '10px', false, '10px'); +test_applied('<number>', '2.5', false, '2.5'); +test_applied('<percentage>', '10%', false, '10%'); +test_applied('<resolution>', '50dppx', false, '50dppx'); +test_applied('<resolution>', '96dpi', false, '1dppx'); +test_applied('<time>', '10s', false, '10s'); +test_applied('<time>', '1000ms', false, '1s'); +test_applied('<transform-function>', 'rotateX(0deg)', false, 'rotateX(0deg)'); +test_applied('<transform-list>', 'rotateX(0deg)', false, 'rotateX(0deg)'); +test_applied('<transform-list>', 'rotateX(0deg) translateX(10px)', false, 'rotateX(0deg) translateX(10px)'); +test_applied('<url>', 'url("http://a/")', false, 'url("http://a/")'); + +// inherits: true/false +test_applied('<color>', 'tomato', false, 'rgb(255, 99, 71)'); +test_applied('<color>', 'tomato', true, 'rgb(255, 99, 71)'); + +test_with_at_property({ syntax: '"*"', inherits: true }, (name, rule) => { + try { + outer.style.setProperty(name, 'foo'); + let actual = getComputedStyle(target).getPropertyValue(name); + assert_equals(actual, 'foo'); + } finally { + outer.style = ''; + } +}, 'Rule applied for "*", even with no initial value'); + +test_not_applied(undefined, 'green', false); +test_not_applied('<color>', undefined, false); +test_not_applied('<color>', 'green', undefined); +test_not_applied('<gandalf>', 'grey', false); +test_not_applied('gandalf', 'grey', false); +test_not_applied('<color>', 'notacolor', false); +test_not_applied('<length>', '10em', false); + +test_not_applied('<transform-function>', 'translateX(1em)', false); +test_not_applied('<transform-function>', 'translateY(1lh)', false); +test_not_applied('<transform-list>', 'rotate(10deg) translateX(1em)', false); +test_not_applied('<transform-list>', 'rotate(10deg) translateY(1lh)', false); + +// Inheritance + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '0px' +}, (name, rule) => { + try { + outer.style = `${name}: 40px`; + assert_equals(getComputedStyle(outer).getPropertyValue(name), '40px'); + assert_equals(getComputedStyle(target).getPropertyValue(name), '0px'); + } finally { + outer.style = ''; + } +}, 'Non-inherited properties do not inherit'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: true, + initialValue: '0px' +}, (name, rule) => { + try { + outer.style = `${name}: 40px`; + assert_equals(getComputedStyle(outer).getPropertyValue(name), '40px'); + assert_equals(getComputedStyle(target).getPropertyValue(name), '40px'); + } finally { + outer.style = ''; + } +}, 'Inherited properties inherit'); + +// Initial values + +test_with_at_property({ + syntax: '"<color>"', + inherits: true, + initialValue: 'green' +}, (name, rule) => { + try { + target.style = `--x:var(${name})`; + assert_equals(getComputedStyle(target).getPropertyValue(name), 'rgb(0, 128, 0)'); + } finally { + target.style = ''; + } +}, 'Initial values substituted as computed value'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: undefined +}, (name, rule) => { + try { + target.style = `${name}: calc(1px + 1px);`; + assert_equals(getComputedStyle(target).getPropertyValue(name), 'calc(1px + 1px)'); + } finally { + target.style = ''; + } +}, 'Non-universal registration are invalid without an initial value'); + +test_with_at_property({ + syntax: '"*"', + inherits: false, + initialValue: undefined +}, (name, rule) => { + try { + // If the registration suceeded, ${name} does *not* inherit, and hence + // the computed value on 'target' should be empty. + outer.style = `${name}: calc(1px + 1px);`; + assert_equals(getComputedStyle(target).getPropertyValue(name), ''); + } finally { + outer.style = ''; + } +}, 'Initial value may be omitted for universal registration'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/conditional-rules.html b/testing/web-platform/tests/css/css-properties-values-api/conditional-rules.html new file mode 100644 index 0000000000..0bff879856 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/conditional-rules.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#conditional-rules"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> + CSS.registerProperty({ + name: '--length', + syntax: '<length>', + initialValue: '0px', + inherits: false + }); +</script> + +<style> + #target { color: red; } + @supports(--length: green) { + #target { color: rgb(1, 2, 3); } + } +</style> + +<div id=target></div> + +<script> + +test(function() { + let cs = getComputedStyle(target); + assert_equals(cs.getPropertyValue('color'), 'rgb(1, 2, 3)'); +}, '@supports should ignore registered syntax'); + +test(function() { + assert_true(CSS.supports('--length: red')); + assert_true(CSS.supports('--length: 10px')); + assert_true(CSS.supports('--length: anything, really')); +}, 'CSS.supports(conditionText) should ignore registered syntax'); + +test(function() { + assert_true(CSS.supports('--length', 'red')); + assert_true(CSS.supports('--length', '10px')); + assert_true(CSS.supports('--length', ' anything, really')); +}, 'CSS.supports(property, value) should ignore registered syntax'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/crashtests/initial-in-audio-crash.html b/testing/web-platform/tests/css/css-properties-values-api/crashtests/initial-in-audio-crash.html new file mode 100644 index 0000000000..dfc2b850a5 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/crashtests/initial-in-audio-crash.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/1356699"> +<audio> + <div> + <div id=test1></div> + </div> +</audio> +<script> +getComputedStyle(test1).getPropertyValue('--a'); +CSS.registerProperty({name: '--a', syntax: '<length>', initialValue: '2px', inherits: false}); +test1.style.setProperty('--a', 'initial'); +getComputedStyle(test1).getPropertyValue('--a'); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/determine-registration.html b/testing/web-platform/tests/css/css-properties-values-api/determine-registration.html new file mode 100644 index 0000000000..20cddc48ad --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/determine-registration.html @@ -0,0 +1,232 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=outer> + <div id=div></div> +</div> +<script> + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '1px' +}, (name) => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); +}, '@property determines the registration when uncontested'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '2px' +}, (name) => { + CSS.registerProperty({ + name: name, + syntax: '<color>', + inherits: false, + initialValue: 'green' + }); + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(0, 128, 0)'); +}, 'CSS.registerProperty wins over @property'); + +test_with_at_property({ + syntax: '"<length>"', + inherits: false, + initialValue: '3px' +}, (name1) => { + with_at_property({ + name: name1, + syntax: '"<integer>"', + inherits: false, + initialValue: '6' + }, (name2) => { + assert_equals(name1, name2); + assert_equals(getComputedStyle(div).getPropertyValue(name2), '6'); + }); +}, '@property later in document order wins'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 4px; + } + + @property ${name} { + syntax: "<color>"; + inherits: false; + initial-value: red; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(255, 0, 0)'); + }); +}, '@property later in stylesheet wins'); + +test(() => { + let name = generate_name(); + CSS.registerProperty({ + name: name, + syntax: '<color>', + inherits: false, + initialValue: 'green' + }); + assert_equals(getComputedStyle(div).getPropertyValue(name), 'rgb(0, 128, 0)'); +}, 'CSS.registerProperty determines the registration when uncontested'); + +test(() => { + let name = generate_name(); + + // ${name} is initially not registered, hence has no initial value. + assert_equals(getComputedStyle(div).getPropertyValue(name), ''); + + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '10px' + }, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '10px'); + }); + + // When the style node is removed, ${name} should be unregistered again. + assert_equals(getComputedStyle(div).getPropertyValue(name), ''); +}, '@property registrations are cleared when rule removed'); + +test(() => { + let name = generate_name(); + + with_style_node(`div { ${name}: calc(1px + 1px); }`, () => { + // ${name} should be a token sequence at this point. + assert_equals(getComputedStyle(div).getPropertyValue(name), 'calc(1px + 1px)'); + + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '0px' + }, () => { + // ${name} is now a <length>, hence the calc() should be simplified. + assert_equals(getComputedStyle(div).getPropertyValue(name), '2px'); + }); + + // ${name} should be a token sequence again. + assert_equals(getComputedStyle(div).getPropertyValue(name), 'calc(1px + 1px)'); + }); +}, 'Computed value becomes token sequence when @property is removed'); + +test(() => { + let name = generate_name(); + + with_style_node(`#outer { ${name}: 10px; }`, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '10px'); + + with_at_property({ + name: name, + syntax: '"<length>"', + inherits: false, + initialValue: '0px' + }, () => { + // ${name} is no longer inherited + assert_equals(getComputedStyle(div).getPropertyValue(name), '0px'); + }); + + assert_equals(getComputedStyle(div).getPropertyValue(name), '10px'); + }); +}, 'Inherited status is reflected in computed styles when @property is removed'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 1px; + } + + @property ${name} { + inherits: false; + initial-value: green; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, 'Invalid @property rule (missing syntax) does not overwrite previous valid rule'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 1px; + } + + @property ${name} { + syntax: "<color>"; + initial-value: green; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, 'Invalid @property rule (missing inherits descriptor) does not overwrite previous valid rule'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 1px; + } + + @property ${name} { + syntax: "<color>"; + inherits: false; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, 'Invalid @property rule (missing initial-value) does not overwrite previous valid rule'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<color>"; + inherits: false; + } + + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 1px; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, 'Previous invalid rule does not prevent valid rule from causing registration'); + +test(() => { + let name = generate_name(); + + with_style_node(` + @property ${name} { + syntax: "<length>"; + inherits: false; + initial-value: 1px; + quite-unknown: 200; + } + `, () => { + assert_equals(getComputedStyle(div).getPropertyValue(name), '1px'); + }); +}, 'Unknown descriptors are ignored and do not invalidate rule'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/font-size-animation.html b/testing/web-platform/tests/css/css-properties-values-api/font-size-animation.html new file mode 100644 index 0000000000..4b8ce1c255 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/font-size-animation.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3751"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + CSS.registerProperty({ + name: '--length', + syntax: '<length>', + initialValue: '0px', + inherits: false + }); +</script> +<style> + @keyframes font_size_animation { + from { + font-size: 10px; + width: 10em; + --length: 10em; + } + to { + font-size: 20px; + width: 20em; + --length: 20em; + } + } + #target1 { + font-size: 1px; + animation: font_size_animation 10s -5s linear paused; + } +</style> +<div id=target1></div> +<script> + test(function() { + // At the time of writing, the correct (absolute) answer is not + // yet defined. However, whatever the correct answer is, there should + // be no difference in 'width' and a custom property registered with + // "<length>". + // + // See https://github.com/w3c/csswg-drafts/issues/3751 + assert_equals(getComputedStyle(target1).getPropertyValue('width'), + getComputedStyle(target1).getPropertyValue('--length')); + }, 'Animating font-size handled identically for standard and custom properties'); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/idlharness.html b/testing/web-platform/tests/css/css-properties-values-api/idlharness.html new file mode 100644 index 0000000000..6f053757c3 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/idlharness.html @@ -0,0 +1,16 @@ +<!doctype html> +<title>CSS Properties Values API IDL tests</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<script> + "use strict"; + + idl_test( + ["css-properties-values-api"], + ["cssom"] + // No objects + ); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/property-cascade.html b/testing/web-platform/tests/css/css-properties-values-api/property-cascade.html new file mode 100644 index 0000000000..bb50213d5c --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/property-cascade.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function" /> +<meta name="assert" content="Verifies that registering a property does not affect the cascade" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> + +#outer { color: rgb(1, 1, 1); } +#inner { + --my-color: rgb(2, 2, 2); + --my-color: url(not-a-color); + color: var(--my-color); +} + +</style> + +<div id=outer> + <div id=inner></div> +</div> + +<script> + +test(function(){ + // Because var(--my-color) is invalid, our color declaration should behave + // like color:unset, i.e. it should compute to the inherited color. + assert_equals(inner.computedStyleMap().get('color').toString(), 'rgb(1, 1, 1)'); + + CSS.registerProperty({ + name: '--my-color', + syntax: '<color>', + initialValue: 'rgb(3, 3, 3)', + inherits: false + }); + + // After registering, var(--my-color) is still invalid. The important thing + // here is that the computed value of color is the initialValue of + // --my-color, and not rgb(2, 2, 2). + assert_equals(inner.computedStyleMap().get('color').toString(), 'rgb(3, 3, 3)'); +}, 'Registering a property does not affect cascade'); + +test(function(){ + CSS.registerProperty({ + name: '--my-color-2', + syntax: '<color>', + initialValue: 'rgb(4, 4, 4)', + inherits: false + }); + + let element = document.createElement('div'); + element.style = ` + --my-color-2: rgb(2, 2, 2); + --my-color-2: url(not-a-color); + color: var(--my-color-2); + `; + + outer.appendChild(element); + + assert_equals(element.computedStyleMap().get('color').toString(), 'rgb(4, 4, 4)'); +}, 'Registering a property does not affect parsing'); + + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/register-property-syntax-parsing.html b/testing/web-platform/tests/css/css-properties-values-api/register-property-syntax-parsing.html new file mode 100644 index 0000000000..16cbdf0c57 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/register-property-syntax-parsing.html @@ -0,0 +1,256 @@ +<!DOCTYPE HTML> +<meta charset="utf-8"> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" /> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#supported-syntax-strings" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test_count = 0; + +function assert_valid(syntax, initialValue) { + // No actual assertions, this just shouldn't throw + test(function() { + var name = '--syntax-test-' + (test_count++); + CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false}); + }, "syntax:'" + syntax + "', initialValue:'" + initialValue + "' is valid"); +} + +function assert_invalid(syntax, initialValue) { + test(function(){ + var name = '--syntax-test-' + (test_count++); + assert_throws_dom("SyntaxError", + () => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})); + }, "syntax:'" + syntax + "', initialValue:'" + initialValue + "' is invalid"); +} + +assert_valid("*", "a"); +assert_valid(" * ", "b"); +assert_valid("<length>", "2px"); +assert_valid(" <number>", "5"); +assert_valid("<percentage> ", "10%"); +assert_valid("<color>+", "red"); +assert_valid(" <length>+ | <percentage>", "2px 8px"); +assert_valid(" <length>+ | <color>#", "red, blue"); +assert_valid("<length>|<percentage>|<length-percentage>", "2px"); // Valid but silly +assert_valid("<color> | <image> | <url> | <integer> | <angle>", "red"); +assert_valid("<time> | <resolution> | <transform-list> | <custom-ident>", "red"); +assert_valid("\t<color>\n| foo", "foo"); + +assert_valid("*", ":> hello"); +assert_valid("*", "([ brackets ]) { yay (??)}"); +assert_valid("*", "yep 'this is valid too'"); +assert_valid("*", "unmatched opening bracket is valid :("); +assert_valid("*", '"'); +assert_valid("*", "default"); + +assert_valid("<length>", "0"); +assert_valid("<length>", "10px /*:)*/"); +assert_valid("<length>", " calc(-2px)"); +assert_valid("<length>", "calc(2px*4 + 10px)"); +assert_valid("<length>", "7.1e-4cm"); +assert_valid("<length>", "calc(7in - 12px)"); +assert_valid("<length>+", "2px 7px calc(8px)"); +assert_valid("<length>#", "2px, 7px, calc(8px)"); +assert_valid("<percentage>", "-9.3e3%"); +assert_valid("<length-percentage>", "-54%"); +assert_valid("<length-percentage>", "0"); +assert_valid("<length-percentage>", "calc(-11px + 10.4%)"); +assert_valid("<length>", "10vmin"); +assert_valid("<percentage> | <length>+", "calc(100vh - 10px) 30px"); + +assert_valid("<number>", "-109"); +assert_valid("<number>", "2.3e4"); +assert_valid("<number>", "calc(1 / 2)"); +assert_valid("<integer>", "-109"); +assert_valid("<integer>", "19"); +assert_valid("<integer>", "calc(1)"); +assert_valid("<integer>", "calc(1 + 2)"); +assert_valid("<integer>", "calc(3.1415)"); +assert_valid("<integer>", "calc(3.1415 + 3.1415)"); + +assert_valid("<angle>", "10deg"); +assert_valid("<angle>", "20.5rad"); +assert_valid("<angle>", "calc(50grad + 3.14159rad)"); +assert_valid("<time>", "2s"); +assert_valid("<time>", "calc(2s - 9ms)"); +assert_valid("<resolution>", "10dpi"); +assert_valid("<resolution>", "3dPpX"); +assert_valid("<resolution>", "-5.3dpcm"); +assert_valid("<transform-function>", "translateX(2px)"); +assert_valid("<transform-function>|<integer>", "5"); +assert_valid("<transform-function>|<integer>", "scale(2)"); +assert_valid("<transform-function>+", "translateX(2px) rotate(42deg)"); +assert_valid("<transform-list>", "scale(2)"); +assert_valid("<transform-list>", "translateX(2px) rotate(20deg)"); + +assert_valid("<color>", "rgb(12, 34, 56)"); +assert_valid("<color>", "lightgoldenrodyellow"); +assert_valid("<image>", "url(a)"); +assert_valid("<image>", "linear-gradient(yellow, blue)"); +assert_valid("<url>", "url(a)"); + +assert_valid("<color>+", "yellow blue"); +assert_valid("<color>+ | <color>", "yellow blue"); +assert_valid("<color> | <color>+", "yellow blue"); +assert_valid("<color># | <color>", "yellow, blue"); +assert_valid("<color> | <color>#", "yellow, blue"); +assert_valid("<color># | <color>+", "yellow blue"); +assert_valid("<color>+ | <color>#", "yellow, blue"); +assert_valid("<color>+ | yellow", "yellow blue"); +assert_valid("yellow", "yellow"); +assert_valid("yellow | <color>+", "yellow blue"); +assert_valid("<color># | yellow", "yellow, blue"); +assert_valid("yellow | <color>#", "yellow, blue"); +assert_valid("<transform-list> | <transform-function> ", "scale(2) rotate(90deg)"); +assert_valid("<transform-function> | <transform-list>", "scale(2) rotate(90deg)"); +assert_valid("<transform-list> | <transform-function>+ ", "scale(2) rotate(90deg)"); +assert_valid("<transform-function>+ | <transform-list>", "scale(2) rotate(90deg)"); +assert_valid("<transform-list> | <transform-function># ", "scale(2) rotate(90deg)"); +assert_valid("<transform-function># | <transform-list>", "scale(2) rotate(90deg)"); +assert_valid("<transform-list> | <transform-function># ", "scale(2), rotate(90deg)"); +assert_valid("<transform-function># | <transform-list>", "scale(2), rotate(90deg)"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1 1"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1%"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1% 1%"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1px"); +assert_valid("<integer>+ | <percentage>+ | <length>+ ", "1px 1px"); + +assert_valid("banana", "banana"); +assert_valid("bAnAnA", "bAnAnA"); +assert_valid("ba-na-nya", "ba-na-nya"); +assert_valid("banana", "banan\\61"); +assert_valid("banan\\61", "banana"); +assert_valid("<custom-ident>", "banan\\61"); +assert_valid("big | bigger | BIGGER", "bigger"); +assert_valid("foo+|bar", "foo foo foo"); + +assert_valid("banana\t", "banana"); +assert_valid("\nbanana\r\n", "banana"); +assert_valid("ba\f\n|\tna\r|nya", "nya"); + +assert_valid(null, "null"); +assert_valid(undefined, "undefined"); +assert_valid(["array"], "array"); + +assert_valid("\\1F914", "🤔"); +assert_valid("hmm\\1F914", "hmm🤔"); +assert_valid("\\1F914hmm", "🤔hmm"); +assert_valid("\\1F914 hmm", "🤔hmm"); +assert_valid("\\1F914\\1F914", "🤔🤔"); + +// Invalid syntax +assert_invalid("banana,nya", "banana"); +assert_invalid("<\\6c ength>", "10px"); +assert_invalid("<banana>", "banana"); +assert_invalid("<Number>", "10"); +assert_invalid("<length", "10px"); +assert_invalid("<LENGTH>", "10px"); +assert_invalid("< length>", "10px"); +assert_invalid("<length >", "10px"); +assert_invalid("<length> +", "10px"); +assert_invalid("<transform-list>+", "scale(2)"); +assert_invalid("<transform-list>#", "scale(2)"); + +assert_invalid("<length>++", "10px"); +assert_invalid("<length>##", "10px"); +assert_invalid("<length>+#", "10px"); +assert_invalid("<length>#+", "10px"); +assert_invalid("<length> | *", "10px"); +assert_invalid("*|banana", "banana"); +assert_invalid("|banana", "banana"); +assert_invalid("*+", "banana"); +assert_invalid("|", "banana"); +assert_invalid(" |", "banana"); +assert_invalid("||", "banana"); +assert_invalid("foo bar", "foo bar"); +assert_invalid("foo foo foo", "foo foo foo"); +assert_invalid("foo § bar", "foo § bar"); +assert_invalid("foo \\1F914 bar", "foo \\1F914 bar"); +assert_invalid("<length> <number>", "0px 0"); +assert_invalid("<length> <length> <length>", "0px 0px 0px"); + +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1 1%"); +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1% 1"); +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1px 1"); +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1 1px"); +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1px 1%"); +assert_invalid("<integer>+ | <percentage>+ | <length>+ ", "1% 1px"); + +assert_invalid("initial", "initial"); +assert_invalid("inherit", "inherit"); +assert_invalid("unset", "unset"); +assert_invalid("revert", "revert"); +assert_invalid("revert-layer", "revert-layer"); +assert_invalid("default", "default"); +assert_invalid("<length>|initial", "10px"); +assert_invalid("<length>|INHERIT", "10px"); +assert_invalid("<percentage>|unsEt", "2%"); +assert_invalid("<color>|REVert", "red"); +assert_invalid("<integer>|deFAUlt", "1"); + +// Invalid initialValue +// The 5 tests that follow are not clearly backed by the specification, +// but they're probably a good idea and we should change the spec. See +// https://github.com/w3c/css-houdini-drafts/issues/1076 . +assert_invalid("*", "initial"); +assert_invalid("*", "inherit"); +assert_invalid("*", "unset"); +assert_invalid("*", "revert"); +assert_invalid("*", "revert-layer"); +// ... end possibly-invalid tests. +assert_invalid("<custom-ident>", "initial"); +assert_invalid("<custom-ident>", "inherit"); +assert_invalid("<custom-ident>", "unset"); +assert_invalid("<custom-ident>", "revert"); +assert_invalid("<custom-ident>", "revert-layer"); +assert_invalid("<custom-ident>", "default"); +assert_invalid("<custom-ident>+", "foo initial bar"); +assert_invalid("<custom-ident>+", "foo inherit bar"); +assert_invalid("<custom-ident>+", "foo unset bar"); +assert_invalid("<custom-ident>+", "foo revert bar"); +assert_invalid("<custom-ident>+", "foo revert-layer bar"); +assert_invalid("<custom-ident>+", "foo default bar"); + +assert_invalid("*", ")"); +assert_invalid("*", "([)]"); +assert_invalid("*", "whee!"); +assert_invalid("*", '"\n'); +assert_invalid("*", "url(moo '')"); +assert_invalid("*", "semi;colon"); +assert_invalid("*", "var(invalid var ref)"); +assert_invalid("*", "var(--foo)"); + +assert_invalid("banana", "bAnAnA"); +assert_invalid("<length>", "var(--moo)"); +assert_invalid("<length>", "10"); +assert_invalid("<length>", "10%"); +assert_invalid("<length>", "calc(5px + 10%)"); +assert_invalid("<length>", "calc(5px * 3px / 6px)"); +assert_invalid("<length>", "10em"); +assert_invalid("<length>", "calc(4px + 3em)"); +assert_invalid("<length>", "calc(4px + calc(8 * 2em))"); +assert_invalid("<length>+", "calc(2ex + 16px)"); +assert_invalid("<length>+", "10px calc(20px + 4rem)"); +assert_invalid("<length>+", ""); +assert_invalid("<length>#", ""); +assert_invalid("<length>", "10px;"); +assert_invalid("<length-percentage>", "calc(2px + 10% + 7ex)"); +assert_invalid("<percentage>", "0"); +assert_invalid("<integer>", "1.0"); +assert_invalid("<integer>", "1e0"); +assert_invalid("<number>|foo", "foo var(--foo, bla)"); +assert_invalid("Foo | bar", "foo"); +assert_invalid("Foo | bar", "Bar"); + +assert_invalid("<angle>", "0"); +assert_invalid("<angle>", "10%"); +assert_invalid("<time>", "2px"); +assert_invalid("<resolution>", "10"); +assert_invalid("<transform-function>", "scale()"); +assert_invalid("<transform-list>", "scale()"); +assert_invalid("<transform-list>+", "translateX(2px) rotate(20deg)"); +assert_invalid("<color>", "fancy-looking"); +assert_invalid("<image>", "banana.png"); +assert_invalid("<url>", "banana.png"); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/register-property.html b/testing/web-platform/tests/css/css-properties-values-api/register-property.html new file mode 100644 index 0000000000..feb6c28d47 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/register-property.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=target></div> +<script> +// Tests for error checking during property registration + +test(function() { + assert_throws_js(TypeError, () => CSS.registerProperty()); + assert_throws_js(TypeError, () => CSS.registerProperty(undefined)); + assert_throws_js(TypeError, () => CSS.registerProperty(true)); + assert_throws_js(TypeError, () => CSS.registerProperty(2)); + assert_throws_js(TypeError, () => CSS.registerProperty("css")); + assert_throws_js(TypeError, () => CSS.registerProperty(null)); +}, "registerProperty requires a Dictionary type"); + +test(function() { + // Valid property names, shouldn't throw + CSS.registerProperty({name: '--name1', inherits: false}); + CSS.registerProperty({name: '--name2, no need for escapes', inherits: false}); + CSS.registerProperty({name: ['--name', 3], inherits: false}); + + // Invalid property names + assert_throws_js(TypeError, () => CSS.registerProperty({})); + assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: 'no-leading-dash', inherits: false})); + assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: '', inherits: false})); + assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: '\\--name', inherits: false})); +}, "registerProperty requires a name matching <custom-property-name>"); + +test(function() { + CSS.registerProperty({name: '--syntax-test-1', syntax: '*', inherits: false}); + CSS.registerProperty({name: '--syntax-test-2', syntax: ' * ', inherits: false}); + assert_throws_dom("SyntaxError", + () => CSS.registerProperty({name: '--syntax-test-3', syntax: 'length', inherits: false})); +}, "registerProperty only allows omitting initialValue if syntax is '*'"); + +test(function() { + CSS.registerProperty({name: '--re-register', syntax: '<length>', initialValue: '0px', inherits: false}); + assert_throws_dom('InvalidModificationError', + () => CSS.registerProperty({name: '--re-register', syntax: '<percentage>', initialValue: '0%', inherits: false})); +}, "registerProperty fails for an already registered property"); + +test(function(){ + CSS.registerProperty({name: '--inherit-test-1', syntax: '<length>', initialValue: '0px', inherits: true}); + CSS.registerProperty({name: '--inherit-test-2', syntax: '<length>', initialValue: '0px', inherits: false}); + assert_throws_js(TypeError, () => CSS.registerProperty({name: '--inherit-test-3', syntax: '<length>', initialValue: '0px'})); +}, "registerProperty requires inherits"); + +test(function(){ + try { + let name = generate_name(); + + target.style.setProperty(name, 'green'); + target.style.transitionProperty = name; + target.style.transitionDuration = '1s'; + target.style.transitionTimingFunction = 'steps(1, end)'; + + assert_equals(getComputedStyle(target).getPropertyValue(name), 'green'); + + CSS.registerProperty({ + name: name, + syntax: '<color>', + initialValue: 'red', + inherits: false + }); + + assert_equals(getComputedStyle(target).getPropertyValue(name), 'rgb(0, 128, 0)'); + } finally { + target.style = ''; + } +}, 'Registering a property should not cause a transition'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-properties-inheritance.html b/testing/web-platform/tests/css/css-properties-values-api/registered-properties-inheritance.html new file mode 100644 index 0000000000..7c8c2656c6 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-properties-inheritance.html @@ -0,0 +1,96 @@ +<!DOCTYPE HTML> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-propertydescriptor-inherits" /> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#outer { + --inherited-length-1: 10px; + --inherited-length-2: var(--non-inherited-length-1); + --inherited-length-3: 30px; + --non-inherited-length-1: 22px; + --non-inherited-length-3: calc(var(--non-inherited-length-2) * 10); +} + +#inner { + --inherited-length-3: 15px; + --non-inherited-length-1: 40px; + --non-inherited-length-2: 90px; +} +</style> +<div id=outer><div id=inner></div></div> +<script> +test(function() { + CSS.registerProperty({name: '--inherited-length-1', syntax: '<length>', initialValue: '1px', inherits: true}); + CSS.registerProperty({name: '--inherited-length-2', syntax: '<length>', initialValue: '2px', inherits: true}); + CSS.registerProperty({name: '--inherited-length-3', syntax: '<length>', initialValue: '3px', inherits: true}); + CSS.registerProperty({name: '--non-inherited-length-1', syntax: '<length>', initialValue: '4px', inherits: false}); + CSS.registerProperty({name: '--non-inherited-length-2', syntax: '<length>', initialValue: '5px', inherits: false}); + CSS.registerProperty({name: '--non-inherited-length-3', syntax: '<length>', initialValue: '6px', inherits: false}); + + outerComputedStyle = getComputedStyle(outer); + assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-1'), '10px'); + assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-2'), '22px'); + assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-3'), '30px'); + assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-1'), '22px'); + assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-2'), '5px'); + assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-3'), '50px'); + + innerComputedStyle = getComputedStyle(inner); + assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-1'), '10px'); + assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-2'), '22px'); + assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-3'), '15px'); + assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-1'), '40px'); + assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-2'), '90px'); + assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-3'), '6px'); +}, "Registered properties are correctly inherited (or not) depending on the inherits flag."); + +test(function(){ + CSS.registerProperty({name: '--initial-length-1', syntax: '<length>', initialValue: '0px', inherits: false}); + outer.style = '--initial-length-1: notalength'; + inner.style = '--initial-length-1: inherit'; + assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-1'), '0px'); +}, "Explicitly inheriting from a parent with an invalid value results in initial value."); + +test(function(){ + CSS.registerProperty({name: '--initial-length-2', syntax: '<length>', initialValue: '0px', inherits: false}); + inner.style = '--initial-length-2: inherit'; + assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-2'), '0px'); +}, "Explicitly inheriting from a parent with no value results in initial value."); + +test(function(){ + CSS.registerProperty({name: '--initial-length-3', syntax: '<length>', initialValue: '0px', inherits: false}); + outer.style = '--initial-length-3: 100px'; + inner.style = '--initial-length-3: inherit'; + assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-3'), '100px'); +}, "Explicitly inheriting from a parent with a value results in that value."); + +test(function(){ + CSS.registerProperty({name: '--inherited-length-4', syntax: '<length>', initialValue: '0px', inherits: true}); + outer.style = '--inherited-length-4: 42px'; + inner.style = '--inherited-length-4: var(--undefined)'; + assert_equals(getComputedStyle(inner).getPropertyValue('--inherited-length-4'), '42px'); +}, "Reference to undefined variable results in inherited value"); + +test(function(){ + CSS.registerProperty({name: '--inherited-length-5', syntax: '<length>', initialValue: '0px', inherits: true}); + outer.style = '--inherited-length-5: 42px'; + inner.style = '--incompatible: nolength; --inherited-length-5: var(--incompatible)'; + assert_equals(getComputedStyle(inner).getPropertyValue('--inherited-length-5'), '42px'); +}, "Reference to syntax-incompatible variable results in inherited value"); + +test(function(){ + CSS.registerProperty({name: '--inherited-em', syntax: '<length>', initialValue: '0px', inherits: true}); + outer.style = 'font-size: 11px; --inherited-em: 10em;'; + inner.style = 'font-size: 22px; --unregistered:var(--inherited-em);'; + assert_equals(getComputedStyle(inner).getPropertyValue('--unregistered'), '110px'); +}, "Font-relative units are absolutized before before inheritance"); + +test(function(){ + CSS.registerProperty({name: '--calc-length', syntax: '<length>', initialValue: '0px', inherits: true}); + outer.style = '--calc-length: calc(10px + 10px);'; + inner.style = '--unregistered:var(--calc-length);'; + assert_equals(getComputedStyle(inner).getPropertyValue('--unregistered'), '20px'); +}, "Calc expressions are resolved before inheritance"); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-change-style-001.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-change-style-001.html new file mode 100644 index 0000000000..320b44d82e --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-change-style-001.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Variables Test: Style changes on registered properties using variables</title> +<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables"> +<meta name="assert" content="A change in the custom property declaration must be propagated to all the descendants"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="outer"> + <div id="inbetween"> + <div id="inner"></div> + </div> +</div> +<script> + "use strict"; + test( function () { + outer.style.cssText = ''; + inbetween.style.cssText = ''; + inner.style.cssText = 'color: var(--color1)'; + let initialValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(initialValue, "rgb(0, 0, 0)", "Initial value"); + + inbetween.style.cssText = 'color: green'; + let inheritedValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(inheritedValue, "rgb(0, 128, 0)", "Inherited value"); + + CSS.registerProperty({name: '--color1', syntax: '<color>', initialValue: 'red', inherits: true}); + let actualValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(actualValue, "rgb(255, 0, 0)", "Resolved value"); + }, "New registered property declaration"); + + test( function () { + outer.style.cssText = ''; + inbetween.style.cssText = ''; + inner.style.cssText = 'color: var(--color2)'; + let initialValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(initialValue, "rgb(0, 0, 0)", "Initial value"); + + outer.style.cssText = '--color2: blue'; + inbetween.style.cssText = 'color: green'; + let resolvedValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(resolvedValue, "rgb(0, 0, 255)", "Resolved value"); + + outer.style.cssText = ''; + CSS.registerProperty({name: '--color2', syntax: '<color>', initialValue: 'red', inherits: true}); + let actualValue = getComputedStyle(inner).getPropertyValue('color'); + assert_equals(actualValue, "rgb(255, 0, 0)", "Resolved value"); + }, "Registered property overrides a previous declaration "); +</script> + diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html new file mode 100644 index 0000000000..f4c718b139 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html @@ -0,0 +1,181 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#calculation-of-computed-values" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> + +<style> +#divWithFontSizeSet, #parentDiv { + font-size: 10px; + line-height: 20px; +} +</style> + +<div id=divWithFontSizeSet></div> +<div id=parentDiv> + <div id=divWithFontSizeInherited></div> +</div> +<div id="ref"></div> + +<script> + +// Generate a property and temporarily set its value. Then call 'fn' with +// the name of the generated property. +function with_custom_property(element, reg, value, fn) { + if (element.id.length == 0) + throw 'The specified element must have an ID'; + + let name = generate_property(reg); + + // Because we want to include the parsing step, insert a stylesheet + // node with textContent. + let node = document.createElement('style'); + node.textContent = `#${element.id} { ${name}:${value}; }`; + document.body.append(node); + + try { + fn(name); + } finally { + node.remove(); + } +} + +function assert_computed_value(element, syntax, value, expected) { + with_custom_property(element, syntax, value, (name) => { + let actual = getComputedStyle(element).getPropertyValue(name); + assert_equals(actual, expected); + }); +} + +// Computes an absolute reference value for some length. +// +// E.g. to figure out how many pixels '10vh' is, do length_ref('10vh'). +function length_ref(value, refnode = ref) { + try { + // The reference property 'min-height' is chosen arbitrarily, but + // avoid properties with "resolved value is used value"-behavior + // [1], as it may affect rounding, and custom properties do not + // have this behavior. + // + // [1] https://drafts.csswg.org/cssom/#resolved-values + const ref_property = 'min-height'; + refnode.style = `${ref_property}: ${value}`; + return getComputedStyle(refnode).getPropertyValue(ref_property); + } finally { + refnode.style = ''; + } +} + +function test_computed_value(syntax, value, expected) { + test(function() { + assert_computed_value(divWithFontSizeSet, syntax, value, expected); + }, `${syntax} values are computed correctly [${value}]`); +} + +test(function(){ + const element = divWithFontSizeSet; + with_custom_property(element, '<length>', '14em', (name) => { + assert_computed_value(element, '<length>', `var(${name})`, '140px'); + }); +}, '<length> values computed are correctly via var()-reference'); + +test(function(){ + const element = divWithFontSizeInherited; + with_custom_property(element, '<length>', '14em', (name) => { + assert_computed_value(element, '<length>', `var(${name})`, '140px'); + }); +}, '<length> values computed are correctly via var()-reference when font-size is inherited'); + +test(function(){ + const element = divWithFontSizeInherited; + assert_computed_value(element, '<length>', '14em', '140px'); +}, '<length> values are computed correctly when font-size is inherited [14em]'); + +test(function(){ + const element = divWithFontSizeInherited; + assert_computed_value(element, '<length>', 'calc(14em + 10px)', '150px'); +}, '<length> values are computed correctly when font-size is inherited [calc(14em + 10px)]'); + +test_computed_value('<length>', '12px', '12px'); +test_computed_value('<length>', '13vw', length_ref('13vw')); +test_computed_value('<length>', '14em', '140px'); +test_computed_value('<length>', '15vmin', length_ref('15vmin')); +test_computed_value('<length>', 'calc(16px - 7em + 10vh)', length_ref('calc(10vh - 54px)')); + +test_computed_value('<length>', '1in', '96px'); +test_computed_value('<length>', '2.54cm', '96px'); +test_computed_value('<length>', '25.4mm', '96px'); +test_computed_value('<length>', '6pc', '96px'); +test_computed_value('<length>', '72pt', '96px'); + +test_computed_value('<length>', '10lh', '200px'); + +test_computed_value('<length-percentage>', '17em', '170px'); +test_computed_value('<length-percentage>', '18%', '18%'); +test_computed_value('<length-percentage>', 'calc(19em - 2%)', 'calc(-2% + 190px)'); + +test_computed_value('<length>#', '10px, 3em', '10px, 30px'); +test_computed_value('<length>#', '4em ,9px', '40px, 9px'); +test_computed_value('<length>#', '8em', '80px'); + +test_computed_value('<length-percentage>#', '3% , 10vmax , 22px', ['3%', length_ref('10vmax'), '22px'].join(', ')); +test_computed_value('<length-percentage>#', 'calc(50% + 1em), 4px', 'calc(50% + 10px), 4px'); +test_computed_value('<length-percentage>#', 'calc(13% + 37px)', 'calc(13% + 37px)'); + +test_computed_value('<length>+', '10px 3em', '10px 30px'); +test_computed_value('<length>+', '4em 9px', '40px 9px'); + +test_computed_value('<length-percentage>+', '3% 10vmax 22px', ['3%', length_ref('10vmax'), '22px'].join(' ')); +test_computed_value('<length-percentage>+', 'calc(50% + 1em) 4px', 'calc(50% + 10px) 4px'); + +test_computed_value('<transform-function>', 'translateX(2px)', 'translateX(2px)'); +test_computed_value('<transform-function>', 'translateX(10em)', 'translateX(100px)'); +test_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(10% + 110px))'); +test_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)'); + +test_computed_value('<integer>', '15', '15'); +test_computed_value('<integer>', 'calc(15 + 15)', '30'); +test_computed_value('<integer>', 'calc(2.4)', '2'); +test_computed_value('<integer>', 'calc(2.6)', '3'); +test_computed_value('<integer>', 'calc(2.6 + 3.1)', '6'); + +test_computed_value('<integer>+', '15 calc(2.4) calc(2.6)', '15 2 3'); + +test_computed_value('<number>', '15', '15'); +test_computed_value('<number>', 'calc(15 + 15)', '30'); +test_computed_value('<number>', 'calc(24 / 10)', '2.4'); + +test_computed_value('<number>+', '15 calc(15 + 15) calc(24 / 10)', '15 30 2.4'); + +test_computed_value('<color>', '#ff0000', 'rgb(255, 0, 0)'); +test_computed_value('<color>', '#000f00', 'rgb(0, 15, 0)'); +test_computed_value('<color>', '#00000a', 'rgb(0, 0, 10)'); +test_computed_value('<color>', '#badbee', 'rgb(186, 219, 238)'); +test_computed_value('<color>', '#badbee33', 'rgba(186, 219, 238, 0.2)'); +test_computed_value('<color>', 'tomato', 'rgb(255, 99, 71)'); +test_computed_value('<color>', 'plum', 'rgb(221, 160, 221)'); +test_computed_value('<color>', 'currentcolor', 'currentcolor'); + +// Custom ident values that look like color keywords should not be converted. +test_computed_value('*', 'tomato', 'tomato'); +test_computed_value('tomato | plum', 'plum', 'plum'); +test_computed_value('tomato | plum | <color>', 'plum', 'plum'); + +test_computed_value('*', '-50grad', '-50grad'); +test_computed_value('<angle>', '180deg', '180deg'); +test_computed_value('<angle>', '400grad', '360deg'); +test_computed_value('<angle>', 'calc(360deg + 400grad)', '720deg'); + +test_computed_value('*', '50s', '50s'); +test_computed_value('<time>', '1s', '1s'); +test_computed_value('<time>', '1000ms', '1s'); +test_computed_value('<time>', 'calc(1000ms + 1s)', '2s'); + +test_computed_value('*', '50dpi', '50dpi'); +test_computed_value('<resolution>', '1dppx', '1dppx'); +test_computed_value('<resolution>', '96dpi', '1dppx'); +test_computed_value('<resolution>', 'calc(1dppx + 96dpi)', '2dppx'); + +test_computed_value('*', 'url(why)', 'url(why)'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-crosstalk.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-crosstalk.html new file mode 100644 index 0000000000..8108894369 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-crosstalk.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1" /> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1238686" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + + @property --x { + syntax: "<number>"; + inherits: true; + initial-value: 0; + } + + #a { + --y: 0; + } + + #b { + --z: 0; + } + + #c { + --x: 42; + } + +</style> + +<div id=a> + <div id=b> + <div id=c> + </div> + </div> +</div> + +<script> + +test(function(){ + assert_equals(getComputedStyle(a).getPropertyValue('--x'), '0'); + assert_equals(getComputedStyle(b).getPropertyValue('--x'), '0'); + assert_equals(getComputedStyle(c).getPropertyValue('--x'), '42'); +}, 'Only #c should be affected by --x:42'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-cssom.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-cssom.html new file mode 100644 index 0000000000..6231e27e77 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-cssom.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +#inner { + --length: 10px; + --color: red; +} +#outer { + --length: 77px; + --color: blue; +} +</style> + +<div id=outer> + <div id=inner></div> +</div> + +<script> +var computedStyle = getComputedStyle(inner); +var inlineStyle = inner.style; +var sheetStyle = document.styleSheets[0].cssRules[0].style; + +test(function() { + // Nothing registered yet, whatever you specify works + assert_equals(computedStyle.getPropertyValue('--length'), '10px'); + assert_equals(computedStyle.getPropertyValue('--color'), 'red'); +}, "Initially unregistered values from stylesheet"); + +test(function() { + inlineStyle.setProperty('--length', '5'); + inlineStyle.setProperty('--color', 'hello'); + + assert_equals(inlineStyle.getPropertyValue('--length'), '5'); + assert_equals(inlineStyle.getPropertyValue('--color'), 'hello'); + assert_equals(computedStyle.getPropertyValue('--length'), '5'); + assert_equals(computedStyle.getPropertyValue('--color'), 'hello'); +}, "CSSOM setters function as expected for unregistered properties"); + +test(function() { + CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--color', syntax: '<color>', initialValue: 'white', inherits: true}); +}, "CSS.registerProperty"); + +test(function() { + assert_equals(inlineStyle.getPropertyValue('--length'), '5'); + assert_equals(inlineStyle.getPropertyValue('--color'), 'hello'); + assert_equals(computedStyle.getPropertyValue('--length'), '0px'); + assert_equals(computedStyle.getPropertyValue('--color'), 'rgb(0, 0, 255)'); +}, "Formerly valid values are still readable from inline styles but are computed as the unset value"); + +test(function() { + inlineStyle.setProperty('--length', 'hi'); + inlineStyle.setProperty('--color', '20'); + assert_equals(inlineStyle.getPropertyValue('--length'), 'hi'); + assert_equals(inlineStyle.getPropertyValue('--color'), '20'); +}, "Values not matching the registered type can still be set"); + +test(function() { + inlineStyle.removeProperty('--length'); + inlineStyle.setProperty('--color', ''); + assert_equals(inlineStyle.getPropertyValue('--length'), ''); + assert_equals(inlineStyle.getPropertyValue('--color'), ''); + assert_equals(computedStyle.getPropertyValue('--length'), '10px'); + assert_equals(computedStyle.getPropertyValue('--color'), 'rgb(255, 0, 0)'); +}, "Values can be removed from inline styles"); + +test(function() { + // 'banana' is not a valid <length>, but still accepted. + sheetStyle.setProperty('--length', 'banana'); + assert_equals(computedStyle.getPropertyValue('--length'), '0px'); + sheetStyle.setProperty('--length', '20px'); + assert_equals(computedStyle.getPropertyValue('--length'), '20px'); + sheetStyle.setProperty('--length', 'initial'); + assert_equals(computedStyle.getPropertyValue('--length'), '0px'); +}, "Stylesheets can be modified by CSSOM"); + +test(function() { + inlineStyle.setProperty('--length', '30px'); + inlineStyle.setProperty('--color', 'pink'); + assert_equals(inlineStyle.getPropertyValue('--length'), '30px'); + assert_equals(inlineStyle.getPropertyValue('--color'), 'pink'); + assert_equals(computedStyle.getPropertyValue('--length'), '30px'); + assert_equals(computedStyle.getPropertyValue('--color'), 'rgb(255, 192, 203)'); + inlineStyle.setProperty('--color', 'inherit'); + assert_equals(inlineStyle.getPropertyValue('--color'), 'inherit'); + assert_equals(computedStyle.getPropertyValue('--color'), 'rgb(0, 0, 255)'); +}, "Valid values can be set on inline styles"); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-initial.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-initial.html new file mode 100644 index 0000000000..2fa062f310 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-initial.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#dom-propertydescriptor-initialvalue" /> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#register-a-custom-property" /> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#substitution" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=target></div> +<script> + +function test_initial_value(reg, expected) { + let suffix = reg.inherits === true ? ', inherits' : ''; + test(function(){ + let name = generate_property(reg); + let actual = getComputedStyle(target).getPropertyValue(name); + assert_equals(actual, expected); + }, `Initial value for ${reg.syntax} correctly computed [${reg.initialValue}${suffix}]`); +} + +test_initial_value({ syntax: '<length>', initialValue: 'calc(10px + 15px)' }, '25px'); +test_initial_value({ syntax: '<length>', initialValue: '1in' }, '96px'); +test_initial_value({ syntax: '<length>', initialValue: '2.54cm' }, '96px'); +test_initial_value({ syntax: '<length>', initialValue: '25.4mm' }, '96px'); +test_initial_value({ syntax: '<length>', initialValue: '6pc' }, '96px'); +test_initial_value({ syntax: '<length>', initialValue: '72pt' }, '96px'); +test_initial_value({ syntax: '<percentage>', initialValue: 'calc(10% + 20%)' }, '30%'); +test_initial_value({ syntax: '<length-percentage>', initialValue: 'calc(1in + 10% + 4px)' }, 'calc(10% + 100px)'); +test_initial_value({ syntax: '<color>', initialValue: 'pink', inherits: true }, 'rgb(255, 192, 203)'); +test_initial_value({ syntax: '<color>', initialValue: 'purple' }, 'rgb(128, 0, 128)'); +test_initial_value({ syntax: '<transform-function>', initialValue: 'rotate(42deg)' }, 'rotate(42deg)'); +test_initial_value({ syntax: '<transform-list>', initialValue: 'scale(calc(2 + 2))' }, 'scale(4)'); +test_initial_value({ syntax: '<transform-list>', initialValue: 'scale(calc(2 + 1)) translateX(calc(3px + 1px))' }, 'scale(3) translateX(4px)'); +test_initial_value({ syntax: '<url>', initialValue: 'url(a)' }, + `url("${new URL('a', document.baseURI)}")`); +test_initial_value({ syntax: '<url>+', initialValue: 'url(a) url(a)' }, + `url("${new URL('a', document.baseURI)}") url("${new URL('a', document.baseURI)}")`); + +// Test that the initial value of the custom property 'reg' is successfully +// substituted into 'property'. +function test_substituted_value(reg, property, expected) { + let inherits_text = reg.inherits === true ? 'inherited' : 'non-inherited'; + test(function(){ + try { + let name = generate_property(reg); + target.style = `${property}:var(${name});`; + assert_equals(getComputedStyle(target).getPropertyValue(property), expected); + } finally { + target.style = ''; + } + }, `Initial ${inherits_text} value can be substituted [${reg.initialValue}, ${property}]`); +} + +test_substituted_value({ syntax: '<color>', initialValue: 'purple', inherits: true }, 'color', 'rgb(128, 0, 128)'); +test_substituted_value({ syntax: '<color>', initialValue: 'pink' }, 'background-color', 'rgb(255, 192, 203)'); + +// Registered properties shall substitute as a token sequence equivalent to +// their computed value. +test_substituted_value({ syntax: 'foo', initialValue: '\tfoo\t' }, '--x', 'foo'); +test_substituted_value({ syntax: '<angle>', initialValue: '\t1turn' }, '--x', '360deg'); +test_substituted_value({ syntax: '<color>', initialValue: ' pink ' }, '--x', 'rgb(255, 192, 203)'); +test_substituted_value({ syntax: '<custom-ident>', initialValue: '\ttest' }, '--x', 'test'); +test_substituted_value({ syntax: '<integer>', initialValue: 'calc(20 + 20 + 10)' }, '--x', '50'); +test_substituted_value({ syntax: '<length-percentage>', initialValue: '\tcalc(13% + 37px)' }, '--x', 'calc(13% + 37px)'); +test_substituted_value({ syntax: '<length>', initialValue: 'calc(10px + 15px)' }, '--x', '25px'); +test_substituted_value({ syntax: '<number>', initialValue: 'calc(13 + 37)' }, '--x', '50'); +test_substituted_value({ syntax: '<percentage>', initialValue: 'calc(13% + 37%)' }, '--x', '50%'); +test_substituted_value({ syntax: '<time>', initialValue: '2000ms' }, '--x', '2s'); +test_substituted_value({ syntax: '<transform-function>', initialValue: 'scale(calc(2 + 2))' }, '--x', 'scale(4)'); +test_substituted_value({ syntax: '<transform-list>', initialValue: 'scale(calc(2 + 2)) translateX(calc(3px + 1px))' }, '--x', 'scale(4) translateX(4px)'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-revert.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-revert.html new file mode 100644 index 0000000000..33c395d2c2 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-revert.html @@ -0,0 +1,94 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1" /> +<link rel="help" href="https://drafts.csswg.org/css-cascade/#default" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #parent { + --inherited: 10px; + --non-inherited: 10px; + } + #child { + --inherited: 20px; + --non-inherited: 20px; + --inherited: revert; + --non-inherited: revert; + } + + @keyframes revert_animation { + from { + --animated-inherited: revert; + --animated-non-inherited: revert; + } + to { + --animated-inherited: 100px; + --animated-non-inherited: 100px; + } + } + + #animated_parent { + --animated-inherited: 0px; + } + #animated_child { + animation: revert_animation 10s -5s linear paused; + } +</style> +<div id=parent> + <div id=child> + </div> +</div> +<div id=animated_parent> + <div id=animated_child> + </div> +</div> +<script> + +CSS.registerProperty({ + name: "--inherited", + syntax: "<length>", + initialValue: "0px", + inherits: true +}); + +CSS.registerProperty({ + name: "--non-inherited", + syntax: "<length>", + initialValue: "0px", + inherits: false +}); + +CSS.registerProperty({ + name: "--animated-non-inherited", + syntax: "<length>", + initialValue: "0px", + inherits: false +}); + +CSS.registerProperty({ + name: "--animated-inherited", + syntax: "<length>", + initialValue: "10000px", + inherits: true +}); + +test(function(){ + let cs = getComputedStyle(child); + assert_equals(cs.getPropertyValue('--inherited'), '10px'); +}, 'Inherited registered custom property can be reverted'); + +test(function(){ + let cs = getComputedStyle(child); + assert_equals(cs.getPropertyValue('--non-inherited'), '0px'); +}, 'Non-inherited registered custom property can be reverted'); + +test(function(){ + let cs = getComputedStyle(animated_child); + assert_equals(cs.getPropertyValue('--animated-non-inherited'), '50px'); +}, 'Non-inherited registered custom property can be reverted in animation'); + +test(function(){ + let cs = getComputedStyle(animated_child); + assert_equals(cs.getPropertyValue('--animated-inherited'), '50px'); +}, 'Inherited registered custom property can be reverted in animation'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js b/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js new file mode 100644 index 0000000000..8cf16e367f --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js @@ -0,0 +1,241 @@ +let next_property_id = 1; + +// Generate a unique property name on the form --prop-N. +function generate_name() { + return `--prop-${next_property_id++}`; +} + +// Produce a compatible initial value for the specified syntax. +function any_initial_value(syntax) { + let components = syntax.split('|').map(x => x.trim()) + let first_component = components[0]; + + if (first_component.endsWith('+') || first_component.endsWith('#')) + first_component = first_component.slice(0, -1); + + switch (first_component) { + case '*': + case '<custom-ident>': + return 'NULL'; + case '<angle>': + return '0deg'; + case '<color>': + return 'rgb(0, 0, 0)'; + case '<image>': + case '<url>': + return 'url(0)'; + case '<integer>': + case '<length-percentage>': + case '<length>': + case '<number>': + return '0'; + case '<percentage>': + return '0%'; + case '<resolution>': + return '0dpi'; + case '<time>': + return '0s'; + case '<transform-function>': + case '<transform-list>': + return 'matrix(0, 0, 0, 0, 0, 0)'; + default: + // We assume syntax is a specific custom ident. + return first_component; + } +} + +// Registers a unique property on the form '--prop-N' and returns the name. +// Any value except 'syntax' may be omitted, in which case the property will +// not inherit, and some undefined (but compatible) initial value will be +// generated. If a single string is used as the argument, it is assumed to be +// the syntax. +function generate_property(reg) { + // Verify that only valid keys are specified. This prevents the caller from + // accidentally supplying 'inherited' instead of 'inherits', for example. + if (typeof(reg) === 'object') { + const permitted = new Set(['name', 'syntax', 'initialValue', 'inherits']); + if (!Object.keys(reg).every(k => permitted.has(k))) + throw new Error('generate_property: invalid parameter'); + } + + let syntax = typeof(reg) === 'string' ? reg : reg.syntax; + let initial = typeof(reg.initialValue) === 'undefined' ? any_initial_value(syntax) + : reg.initialValue; + let inherits = typeof(reg.inherits) === 'undefined' ? false : reg.inherits; + + let name = generate_name(); + CSS.registerProperty({ + name: name, + syntax: syntax, + initialValue: initial, + inherits: inherits + }); + return name; +} + +function all_syntaxes() { + return [ + '*', + '<angle>', + '<color>', + '<custom-ident>', + '<image>', + '<integer>', + '<length-percentage>', + '<length>', + '<number>', + '<percentage>', + '<resolution>', + '<time>', + '<transform-function>', + '<transform-list>', + '<url>' + ] +} + +function with_style_node(text, fn) { + let node = document.createElement('style'); + node.textContent = text; + try { + document.body.append(node); + fn(node); + } finally { + node.remove(); + } +} + +function with_at_property(desc, fn) { + let name = typeof(desc.name) === 'undefined' ? generate_name() : desc.name; + let text = `@property ${name} {`; + if (typeof(desc.syntax) !== 'undefined') + text += `syntax:${desc.syntax};`; + if (typeof(desc.initialValue) !== 'undefined') + text += `initial-value:${desc.initialValue};`; + if (typeof(desc.inherits) !== 'undefined') + text += `inherits:${desc.inherits};`; + text += '}'; + with_style_node(text, (node) => fn(name, node.sheet.rules[0])); +} + +function test_with_at_property(desc, fn, description) { + test(() => with_at_property(desc, fn), description); +} + +function test_with_style_node(text, fn, description) { + test(() => with_style_node(text, fn), description); +} + +function animation_test(property, values, description) { + const name = generate_name(); + property.name = name; + CSS.registerProperty(property); + + test(() => { + const duration = 1000; + const keyframes = {}; + keyframes[name] = values.keyframes; + + const iterations = 3; + const composite = values.composite || "replace"; + const iterationComposite = values.iterationComposite || "replace"; + const animation = target.animate(keyframes, { composite, iterationComposite, iterations, duration }); + animation.pause(); + // We seek to the middle of the third iteration which will allow to test cases where + // iterationComposite is set to something other than "replace". + animation.currentTime = duration * 2.5; + + assert_equals(getComputedStyle(target).getPropertyValue(name), values.expected); + }, description); +}; + +function discrete_animation_test(syntax, fromValue, toValue, description) { + test(() => { + const name = generate_name(); + + CSS.registerProperty({ + name, + syntax, + inherits: false, + initialValue: fromValue + }); + + const duration = 1000; + const keyframes = []; + keyframes[name] = toValue; + const animation = target.animate(keyframes, duration); + animation.pause(); + + const checkAtProgress = (progress, expected) => { + animation.currentTime = duration * 0.25; + assert_equals(getComputedStyle(target).getPropertyValue(name), fromValue, `The correct value is used at progress = ${progress}`); + }; + + checkAtProgress(0, fromValue); + checkAtProgress(0.25, fromValue); + checkAtProgress(0.49, fromValue); + checkAtProgress(0.5, toValue); + checkAtProgress(0.75, toValue); + checkAtProgress(1, toValue); + }, description || `Animating a custom property of type ${syntax} is discrete`); +} + +function transition_test(options, description) { + promise_test(async () => { + const customProperty = generate_name(); + + options.transitionProperty ??= customProperty; + + CSS.registerProperty({ + name: customProperty, + syntax: options.syntax, + inherits: false, + initialValue: options.from + }); + + assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.from, "Element has the expected initial value"); + + const transitionEventPromise = new Promise(resolve => { + let listener = event => { + target.removeEventListener("transitionrun", listener); + assert_equals(event.propertyName, customProperty, "TransitionEvent has the expected property name"); + resolve(); + }; + target.addEventListener("transitionrun", listener); + }); + + target.style.transition = `${options.transitionProperty} 1s -500ms linear`; + target.style.setProperty(customProperty, options.to); + + const animations = target.getAnimations(); + assert_equals(animations.length, 1, "A single animation is running"); + + const transition = animations[0]; + assert_class_string(transition, "CSSTransition", "A CSSTransition is running"); + + transition.pause(); + assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.expected, "Element has the expected animated value"); + + await transitionEventPromise; + }, description); +} + +function no_transition_test(options, description) { + test(() => { + const customProperty = generate_name(); + + CSS.registerProperty({ + name: customProperty, + syntax: options.syntax, + inherits: false, + initialValue: options.from + }); + + assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.from, "Element has the expected initial value"); + + target.style.transition = `${customProperty} 1s -500ms linear`; + target.style.setProperty(customProperty, options.to); + + assert_equals(target.getAnimations().length, 0, "No animation was created"); + assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.to, "Element has the expected final value"); + }, description); +}; diff --git a/testing/web-platform/tests/css/css-properties-values-api/self-utils.html b/testing/web-platform/tests/css/css-properties-values-api/self-utils.html new file mode 100644 index 0000000000..b770c86c38 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/self-utils.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Self-test for utils.js</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<div id=outer><div id=inner></div></div> +<script> + +test(function(){ + let syntaxes = all_syntaxes().concat([ + 'foo', + 'bar | <length>', + '<angle> | <length>' + ]); + // Don't throw: + syntaxes.forEach(generate_property); +}, 'Default initial values of generated properties are valid (self-test).'); + +test(function(){ + try { + let inherited = generate_property({ syntax: '<length>', inherits: true }); + let non_inherited = generate_property({ syntax: '<length>', inherits: false, initialValue: '5px' }); + outer.style = `${inherited}: 10px; ${non_inherited}: 11px;`; + assert_equals(getComputedStyle(outer).getPropertyValue(inherited), '10px'); + assert_equals(getComputedStyle(outer).getPropertyValue(non_inherited), '11px'); + assert_equals(getComputedStyle(inner).getPropertyValue(inherited), '10px'); + assert_equals(getComputedStyle(inner).getPropertyValue(non_inherited), '5px'); + } finally { + outer.style = ''; + inner.style = ''; + } +}, 'Generated properties respect inherits flag'); + +test(function(){ + assert_throws_js(Error, () => generate_property({syntax: '<length>', foo: 1})); + assert_throws_js(Error, () => generate_property({syntax: '<length>', inherited: false})); + assert_throws_js(Error, () => generate_property({syntax: '<length>', initial: '10px'})); +}, 'Can\'t generate property with unknown fields'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.css b/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.css new file mode 100644 index 0000000000..aeb6ad5abe --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.css @@ -0,0 +1,4 @@ +#target { + --reg-alt-non-inherited-url: url(foo.jpg); + --reg-alt-non-inherited-func: url("foo.jpg"); +} diff --git a/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.js b/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.js new file mode 100644 index 0000000000..f25e18beeb --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/support/alt/alt.js @@ -0,0 +1,11 @@ +function reg_url(name, inherits) { + CSS.registerProperty({ + name: name, + syntax: '<url> | none', + inherits: inherits, + initialValue: 'none' + }); +} + +reg_url('--reg-alt-non-inherited-url', false); +reg_url('--reg-alt-non-inherited-func', false); diff --git a/testing/web-platform/tests/css/css-properties-values-api/support/main/main.css b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.css new file mode 100644 index 0000000000..6b81abdee7 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.css @@ -0,0 +1,22 @@ +#target { + --unreg-url: url(foo.jpg); + --unreg-func: url("foo.jpg"); + + --reg-inherited-url: url(foo.jpg); + --reg-non-inherited-url: url(foo.jpg); + + --reg-inherited-func: url("foo.jpg"); + --reg-non-inherited-func: url("foo.jpg"); + + --reg-ref-to-unreg-url: var(--unreg-url); + --reg-ref-to-unreg-func: var(--unreg-func); + + --reg-ref-to-reg-url: var(--reg-alt-non-inherited-url); + --reg-ref-to-reg-func: var(--reg-alt-non-inherited-func); + + --unreg-ref-to-reg-url: var(--reg-alt-non-inherited-url); + --unreg-ref-to-reg-func: var(--reg-alt-non-inherited-func); + + --unreg-multi-ref-to-reg-urls: var(--reg-non-inherited-url), var(--reg-alt-non-inherited-url); + --unreg-multi-ref-to-reg-funcs: var(--reg-non-inherited-func), var(--reg-alt-non-inherited-func); +} diff --git a/testing/web-platform/tests/css/css-properties-values-api/support/main/main.js b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.js new file mode 100644 index 0000000000..169ed7b53c --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.js @@ -0,0 +1,25 @@ +function reg_url(name, inherits) { + CSS.registerProperty({ + name: name, + syntax: '<url> | none', + inherits: inherits, + initialValue: 'none' + }); +} + +reg_url('--reg-non-inherited-url', false); +reg_url('--reg-non-inherited-func', false); + +reg_url('--reg-inherited-url', true); +reg_url('--reg-inherited-func', true); + +reg_url('--reg-ref-to-unreg-url', false); +reg_url('--reg-ref-to-unreg-func', false); + +reg_url('--reg-ref-to-reg-url', false); +reg_url('--reg-ref-to-reg-func', false); + +reg_url('--reg-merged-func', false); + +reg_url('--reg-utf16be-url', false); +reg_url('--reg-utf16be-func', false); diff --git a/testing/web-platform/tests/css/css-properties-values-api/support/main/main.utf16be.css b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.utf16be.css Binary files differnew file mode 100644 index 0000000000..26485da32b --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/support/main/main.utf16be.css diff --git a/testing/web-platform/tests/css/css-properties-values-api/typedom.html b/testing/web-platform/tests/css/css-properties-values-api/typedom.html new file mode 100644 index 0000000000..51940e298d --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/typedom.html @@ -0,0 +1,993 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#css-style-value-reification" /> +<meta name="assert" content="Verifies that registered custom properties interact correctly with CSS Typed OM" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<style id=style> + div {} +</style> +<div id=target></div> + +<script> + +// Cleans style rules used for testing between every test. +add_result_callback(function(){ + target.attributeStyleMap.clear(); + // Clears 'div' rule in #style: + style.sheet.rules[0].styleMap.clear(); +}); + +// In the following utility functions, the 'map' parameter (if present) +// can be any StylePropertyMap. (Not StylePropertyMapReadOnly). + +// Verifies that get()/getAll() reifies the specified property to a +// CSSUnparsedValue, with a string serialization equal to 'value'. +function verify_map_get_unparsed(map, name, value) { + map.set(name, value); + + let specifiedValue = map.get(name); + assert_true(specifiedValue instanceof CSSUnparsedValue); + assert_equals(specifiedValue.toString(), value); + + let allSpecifiedValues = map.getAll(name); + assert_equals(allSpecifiedValues.length, 1); + assert_true(allSpecifiedValues[0] instanceof CSSUnparsedValue); + assert_equals(allSpecifiedValues[0].toString(), value); +} + +// Verifies that the specified value is accepted by set(). +function verify_map_set(map, name, value) { + map.set(name, value); + assert_equals(map.get(name).toString(), value.toString()); +} + +// Verifies that the specified value is NOT accepted by set(). +function verify_map_not_set(map, name, value) { + assert_throws_js(TypeError, () => { + map.set(name, value); + }); +} + +// Verifies that the specified value is NOT accepted by append(). +function verify_map_no_append(map, name, value) { + assert_throws_js(TypeError, () => { + map.append(name, value); + }); +} + +// Verifies that the property 'name' shows up on iteration, that it's reified +// as a CSSUnparsedValue, and that the string representation is equal to +// 'value'. +function verify_map_iteration_unparsed(map, name, value) { + map.set(name, value); + let result = Array.from(map).filter(e => e[0] == name)[0]; + assert_equals(result.length, 2); + let iter_value = result[1]; + assert_equals(iter_value.length, 1); + assert_true(iter_value[0] instanceof CSSUnparsedValue); + assert_equals(iter_value[0].toString(), value); +} + +// Verifies that CSSStyleValue.parse/parseAll results in a CSSStyleValue with +// the 'expected' type. +function verify_parsed_type(prop, value, expected) { + let parse_value = CSSStyleValue.parse(prop, value); + let parse_all_value = CSSStyleValue.parseAll(prop, value); + + assert_true(parse_value instanceof expected); + assert_true(parse_all_value.every(x => x instanceof expected)) +} + +// On the target element, verify that computed value of 'name' is an instance +// of 'expected' and not an instance of CSSUnparsedValue. +// +// If 'value' is non-null, that value is first set using the style attribute +// of the target element. +function verify_computed_type(name, value, expected) { + if (expected == CSSUnparsedValue) { + throw 'CSSUnparsedValue may not be used as expected type'; + } + + try { + if (value != null) { + target.style = `${name}: ${value}`; + } + + let computedValue = target.computedStyleMap().get(name); + + assert_false(computedValue instanceof CSSUnparsedValue); + assert_true(computedValue instanceof expected); + } finally { + if (value != null) { + target.style = ''; + } + } +} + +// Verifies that the property 'name' shows up on iteration, that it's reified +// to the specified type, and that the string representation is equal to 'value'. +function verify_computed_iteration_type(name, value, type) { + target.attributeStyleMap.set(name, value); + let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0]; + assert_equals(result.length, 2); + let iter_value = result[1]; + assert_equals(iter_value.length, 1); + assert_true(iter_value[0] instanceof type); + assert_equals(iter_value[0].toString(), value); +} + +// Run the same test twice: once for each StylePropertyMap. +// +// https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap +function test_specified_maps(func, description) { + test(function(){ + func(target.attributeStyleMap) + }, description + ' [attributeStyleMap]'); + + test(function(){ + let rule = style.sheet.rules[0]; + func(rule.styleMap) + }, description + ' [styleMap]'); +} + +// StylePropertyMapReadOnly.get + +test(function(){ + let name = generate_property('*', 'if(){}'); + assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue); + + target.attributeStyleMap.set(name, 'as{}df'); + assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue); + target.attributeStyleMap.delete(name); +}, 'Computed * is reified as CSSUnparsedValue'); + +test(function(){ + verify_computed_type(generate_property('<angle>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <angle> '), '42deg', CSSUnitValue); +}, 'Computed <angle> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<color>'), null, CSSStyleValue); + verify_computed_type(generate_property('fail | <color> '), null, CSSStyleValue); +}, 'Computed <color> is reified as CSSStyleValue'); + +test(function(){ + verify_computed_type(generate_property('<custom-ident>'), null, CSSKeywordValue); + verify_computed_type(generate_property('<custom-ident> | <length>'), 'none', CSSKeywordValue); +}, 'Computed <custom-ident> is reified as CSSKeywordValue'); + +test(function(){ + verify_computed_type(generate_property('<image>'), null, CSSImageValue); + verify_computed_type(generate_property('fail | <image> '), 'url(thing.png)', CSSImageValue); +}, 'Computed <image> [url] is reified as CSSImageValue'); + +test(function(){ + verify_computed_type(generate_property('<integer>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <integer> '), '100', CSSUnitValue); +}, 'Computed <integer> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <length-percentage> '), '10%', CSSUnitValue); +}, 'Computed <length-percentage> [%] is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<length-percentage>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <length-percentage> '), '10px', CSSUnitValue); +}, 'Computed <length-percentage> [px] is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property({syntax: '<length-percentage>', initialValue: 'calc(10% + 10px)'}), null, CSSMathSum); + verify_computed_type(generate_property('fail | <length-percentage> '), 'calc(10px + 10%)', CSSMathSum); +}, 'Computed <length-percentage> [px + %] is reified as CSSMathSum'); + +test(function(){ + verify_computed_type(generate_property('<length>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <length> '), '10px', CSSUnitValue); +}, 'Computed <length> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<number>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <number> '), '42', CSSUnitValue); +}, 'Computed <number> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<percentage>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <percentage> '), '10%', CSSUnitValue); +}, 'Computed <percentage> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<resolution>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <resolution> '), '300dpi', CSSUnitValue); +}, 'Computed <resolution> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<time>'), null, CSSUnitValue); + verify_computed_type(generate_property('fail | <time> '), '42s', CSSUnitValue); +}, 'Computed <time> is reified as CSSUnitValue'); + +test(function(){ + verify_computed_type(generate_property('<url>'), null, CSSStyleValue); + verify_computed_type(generate_property('fail | <url> '), 'url(a)', CSSStyleValue); +}, 'Computed <url> is reified as CSSStyleValue'); + +test(function(){ + verify_computed_type(generate_property('thing1 | THING2'), null, CSSKeywordValue); + verify_computed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSKeywordValue); +}, 'Computed ident is reified as CSSKeywordValue'); + +test(function(){ + verify_computed_type(generate_property('<length>+'), null, CSSUnitValue); + verify_computed_type(generate_property('<length>+'), '10px 20px', CSSUnitValue); +}, 'First computed value correctly reified in space-separated list'); + +test(function(){ + verify_computed_type(generate_property('<length>#'), null, CSSUnitValue); + verify_computed_type(generate_property('<length>#'), '10px, 20px', CSSUnitValue); +}, 'First computed value correctly reified in comma-separated list'); + +// StylePropertyMapReadOnly.getAll + +test(function(){ + let name = generate_property({syntax: '<length>+', initialValue: '10px 20px'}); + assert_equals(target.computedStyleMap().getAll(name).length, 2); + assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); + + target.style = `${name}: 10px 20px 30px`; + assert_equals(target.computedStyleMap().getAll(name).length, 3); + assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); +}, 'All computed values correctly reified in space-separated list'); + +test(function(){ + let name = generate_property({syntax: '<length>#', initialValue: '10px, 20px'}); + assert_equals(target.computedStyleMap().getAll(name).length, 2); + assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); + + target.style = `${name}: 10px, 20px, 30px`; + assert_equals(target.computedStyleMap().getAll(name).length, 3); + assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue)); +}, 'All computed values correctly reified in comma-separated list'); + +// StylePropertyMap.get/All + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('*'), 'foo'); +}, 'Specified * is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('foo'), 'foo'); +}, 'Specified foo is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<angle>'), '10deg'); +}, 'Specified <angle> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<color>'), 'green'); +}, 'Specified <color> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<custom-ident>'), 'foo'); +}, 'Specified <custom-ident> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<image>'), 'url("a")'); +}, 'Specified <image> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<integer>'), '1'); +}, 'Specified <integer> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)'); +}, 'Specified <length-percentage> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<length>'), '10px'); +}, 'Specified <length> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<number>'), '1'); +}, 'Specified <number> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<percentage>'), '10%'); +}, 'Specified <percentage> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<resolution>'), '10dpi'); +}, 'Specified <resolution> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<time>'), '1s'); +}, 'Specified <time> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified <transform-function> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified <transform-list> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<url>'), 'url("a")'); +}, 'Specified <url> is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<length>+'), '10px 11px'); +}, 'Specified <length>+ is reified as CSSUnparsedValue from get/getAll'); + +test_specified_maps(function(map){ + verify_map_get_unparsed(map, generate_property('<length>#'), '10px, 11px'); +}, 'Specified <length># is reified as CSSUnparsedValue from get/getAll'); + +// StylePropertyMap.set + +// The following strings are valid for the specified syntax, and should be +// accepted by set(). + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('*'), 'foo'); +}, 'Specified string "foo" accepted by set() for syntax *'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('foo'), 'foo'); +}, 'Specified string "foo" accepted by set() for syntax foo'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<angle>'), '10deg'); +}, 'Specified string "10deg" accepted by set() for syntax <angle>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<color>'), 'green'); +}, 'Specified string "green" accepted by set() for syntax <color>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<custom-ident>'), 'foo'); +}, 'Specified string "foo" accepted by set() for syntax <custom-ident>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<image>'), 'url("a")'); +}, 'Specified string "url("a")" accepted by set() for syntax <image>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<integer>'), '1'); +}, 'Specified string "1" accepted by set() for syntax <integer>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length-percentage>'), 'calc(10% + 10px)'); +}, 'Specified string "calc(10% + 10px)" accepted by set() for syntax <length-percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>'), '10px'); +}, 'Specified string "10px" accepted by set() for syntax <length>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<number>'), '1'); +}, 'Specified string "1" accepted by set() for syntax <number>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<percentage>'), '10%'); +}, 'Specified string "10%" accepted by set() for syntax <percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<resolution>'), '10dpi'); +}, 'Specified string "10dpi" accepted by set() for syntax <resolution>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<time>'), '1s'); +}, 'Specified string "1s" accepted by set() for syntax <time>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-function>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified string "matrix(0, 0, 0, 0, 0, 0)" accepted by set() for syntax <transform-list>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<url>'), 'url("a")'); +}, 'Specified string "url("a")" accepted by set() for syntax <url>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>+'), '10px 11px'); +}, 'Specified string "10px 11px" accepted by set() for syntax <length>+'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>#'), '10px, 11px'); +}, 'Specified string "10px, 11px" accepted by set() for syntax <length>#'); + +// The following strings are invalid for the specified syntax, but should +// should be accepted by set(). + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('foo'), 'bar'); +}, 'Specified string "bar" accepted by set() for syntax foo'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<angle>'), '10px'); +}, 'Specified string "10px" accepted by set() for syntax <angle>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<color>'), '10px'); +}, 'Specified string "10px" accepted by set() for syntax <color>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<custom-ident>'), '10px'); +}, 'Specified string "10px" accepted by set() for syntax <custom-ident>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<image>'), 'a'); +}, 'Specified string "a" accepted by set() for syntax <image>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<integer>'), 'float'); +}, 'Specified string "float" accepted by set() for syntax <integer>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length-percentage>'), 'red'); +}, 'Specified string "red" accepted by set() for syntax <length-percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>'), 'red'); +}, 'Specified string "red" accepted by set() for syntax <length>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<number>'), 'red'); +}, 'Specified string "red" accepted by set() for syntax <number>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<percentage>'), 'var(--x)'); +}, 'Specified string "var(--x)" accepted by set() for syntax <percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<resolution>'), 'blue'); +}, 'Specified string "blue" accepted by set() for syntax <resolution>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<time>'), '1meter'); +}, 'Specified string "1meter" accepted by set() for syntax <time>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-function>'), 'foo(0)'); +}, 'Specified string "foo(0)" accepted by set() for syntax <transform-function>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-list>'), 'bar(1)'); +}, 'Specified string "bar(1)" accepted by set() for syntax <transform-list>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<url>'), 'a'); +}, 'Specified string "a" accepted by set() for syntax <url>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>+'), 'a b'); +}, 'Specified string "a b" accepted by set() for syntax <length>+'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>#'), 'a, b'); +}, 'Specified string "a, b" accepted by set() for syntax <length>#'); + +// CSSUnparsedValue should always be accepted by any custom property, +// regardless of registation status. + +const unparsed = CSSStyleValue.parse('--x', 'foo bar thing'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('*'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax *'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('foo'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax foo'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<angle>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <angle>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<color>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <color>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<custom-ident>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <custom-ident>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<image>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <image>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<integer>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <integer>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length-percentage>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <length-percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <length>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<number>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <number>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<percentage>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <percentage>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<resolution>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <resolution>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<time>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <time>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-function>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <transform-function>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<transform-list>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <transform-list>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<url>'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <url>'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>+'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <length>+'); + +test_specified_maps(function(map){ + verify_map_set(map, generate_property('<length>#'), unparsed); +}, 'CSSUnparsedValue is accepted via set() for syntax <length>#'); + +// CSSStyleValues which aren't CSSUnparsedValues aren't accepted by set(), +// even if they're a value which is compatible with the syntax. +// +// https://drafts.css-houdini.org/css-properties-values-api-1/#cssom +const zero_matrix = CSSStyleValue.parse('transform', 'matrix(0, 0, 0, 0, 0, 0)'); +const image_value = CSSStyleValue.parse('background-image', 'url(a)'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('*'), new CSSKeywordValue('foo')); +}, 'CSSKeywordValue rejected by set() for syntax *'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('foo'), new CSSKeywordValue('foo')); +}, 'CSSKeywordValue rejected by set() for syntax foo'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<angle>'), CSS.deg(10)); +}, 'CSSUnitValue rejected by set() for syntax <angle>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<custom-ident>'), new CSSKeywordValue('foo')); +}, 'CSSKeywordValue rejected by set() for syntax <custom-ident>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<image>'), image_value); +}, 'CSSImageValue rejected by set() for syntax <image>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<integer>'), CSS.number(1)); +}, 'CSSUnitValue rejected by set() for syntax <integer>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<length-percentage>'), CSS.px(10)); +}, 'CSSUnitValue rejected by set() for syntax <length-percentage>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<length>'), CSS.px(10)); +}, 'CSSUnitValue rejected by set() for syntax <length>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<number>'), CSS.number(10)); +}, 'CSSUnitValue rejected by set() for syntax <number>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<percentage>'), CSS.percent(10)); +}, 'CSSUnitValue rejected by set() for syntax <percentage>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<resolution>'), CSS.dpi(10)); +}, 'CSSUnitValue rejected by set() for syntax <resolution>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<time>'), CSS.s(10)); +}, 'CSSUnitValue rejected by set() for syntax <time>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<transform-list>'), zero_matrix); +}, 'CSSTransformValue rejected by set() for syntax <transform-list>'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<length>+'), CSS.px(10), CSS.px(10)); +}, 'CSSUnitValue rejected by set() for syntax <length>+'); + +test_specified_maps(function(map){ + verify_map_not_set(map, generate_property('<length>#'), CSS.px(10), CSS.px(10)); +}, 'CSSUnitValue rejected by set() for syntax <length>#'); + +// <color> has no CSSStyleValue subclass yet. +// <url> has no CSSStyleValue subclass yet. +// <transform-function> has no CSSStyleValue subclass yet. + +// StylePropertyMap.append + +// It is not allowed to append CSSStyleValues to custom properties, even +// when the string matches the syntax of the custom property. + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('*'), 'a'); +}, 'Appending a string to * is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('foo+'), 'foo'); +}, 'Appending a string to foo+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<angle>+'), '10deg'); +}, 'Appending a string to <angle>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<color>+'), 'red'); +}, 'Appending a string to <color>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<custom-ident>+'), 'foo'); +}, 'Appending a string to <custom-ident>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<image>+'), 'url(a)'); +}, 'Appending a string to <image>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<integer>+'), 'a'); +}, 'Appending a string to <integer>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<length-percentage>+'), 'calc(10*% + 10px)'); +}, 'Appending a string to <length-percentage>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<length>+'), '10px'); +}, 'Appending a string to <length>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<number>+'), '1.3'); +}, 'Appending a string to <number>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<percentage>+'), '10%'); +}, 'Appending a string to <percentage>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<resolution>+'), '10dpi'); +}, 'Appending a string to <resolution>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<time>+'), '1s'); +}, 'Appending a string to <time>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<transform-function>+'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Appending a string to <transform-function>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Appending a string to <transform-list> is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<url>+'), 'url(a)'); +}, 'Appending a string to <url>+ is not allowed'); + +test_specified_maps(function(map){ + verify_map_no_append(map, generate_property('<length>#'), '10px'); +}, 'Appending a string to <length># is not allowed'); + +// It is not allowed to append CSSStyleValues to custom properties, even +// when the CSSStyleValue matches the syntax of the custom property. + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('*'), new CSSKeywordValue('foo')); +}, 'Appending a CSSKeywordValue to * is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('foo+'), new CSSKeywordValue('foo')); +}, 'Appending a CSSKeywordValue to foo+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<angle>+'), CSS.deg(10)); +}, 'Appending a CSSUnitValue to <angle>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<custom-ident>+'), new CSSKeywordValue('foo')); +}, 'Appending a CSSKeywordValue to <custom-ident>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<image>+'), image_value); +}, 'Appending a CSSImageValue to <image>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<integer>+'), CSS.number(1)); +}, 'Appending a CSSUnitValue to <integer>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<length-percentage>+'), CSS.px(10)); +}, 'Appending a CSSUnitValue to <length-percentage>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<length>+'), CSS.px(10)); +}, 'Appending a CSSUnitValue to <length>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<number>+'), CSS.number(10)); +}, 'Appending a CSSUnitValue to <number>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<percentage>+'), CSS.percent(10)); +}, 'Appending a CSSUnitValue to <percentage>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<resolution>+'), CSS.dpi(10)); +}, 'Appending a CSSUnitValue to <resolution>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<time>+'), CSS.s(10)); +}, 'Appending a CSSUnitValue to <time>+ is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<transform-list>'), zero_matrix); +}, 'Appending a CSSKeywordValue to <transform-list> is not allowed'); + +test_specified_maps(function(map) { + verify_map_no_append(map, generate_property('<length>#'), CSS.px(10)); +}, 'Appending a CSSUnitValue to <length># is not allowed'); + +// <color> has no CSSStyleValue subclass yet. +// <url> has no CSSStyleValue subclass yet. +// <transform-function> has no CSSStyleValue subclass yet. + +// CSSStyleValue.parse/parseAll + +test(function(){ + verify_parsed_type(generate_property('*'), 'while(){}', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax *', CSSUnparsedValue); + +test(function(){ + verify_parsed_type(generate_property('<angle>'), '42deg', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <angle>'); + +test(function(){ + verify_parsed_type(generate_property('<color>'), '#fefefe', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <color>'); + +test(function(){ + verify_parsed_type(generate_property('<custom-ident> | <length>'), 'none', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <custom-ident> | <length>'); + +test(function(){ + verify_parsed_type(generate_property('<image>'), 'url(thing.png)', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <image>'); + +test(function(){ + verify_parsed_type(generate_property('<integer>'), '100', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <integer>'); + +test(function(){ + verify_parsed_type(generate_property('<length-percentage>'), '10%', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10%)'); + +test(function(){ + verify_parsed_type(generate_property('<length-percentage>'), '10px', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (10px)'); + +test(function(){ + verify_parsed_type(generate_property('<length-percentage>'), 'calc(10px + 10%)', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length-percentage> (calc(10px + 10%))'); + +test(function(){ + verify_parsed_type(generate_property('<length>'), '10px', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>'); + +test(function(){ + verify_parsed_type(generate_property('<number>'), '42', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <number>'); + +test(function(){ + verify_parsed_type(generate_property('<percentage>'), '10%', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <percentage>'); + +test(function(){ + verify_parsed_type(generate_property('<resolution>'), '300dpi', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <resolution>'); + +test(function(){ + verify_parsed_type(generate_property('<time>'), '42s', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <time>'); + +test(function(){ + verify_parsed_type(generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-function>'); + +test(function(){ + verify_parsed_type(generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <transform-list>'); + +test(function(){ + verify_parsed_type(generate_property('<url>'), 'url(a)', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <url>'); + +test(function(){ + verify_parsed_type(generate_property('thing1 | THING2 | <url>'), 'THING2', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax thing1 | THING2 | <url>'); + +test(function(){ + verify_parsed_type(generate_property('<length>+'), '10px 20px', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>+'); + +test(function(){ + verify_parsed_type(generate_property('<length>#'), '10px, 20px', CSSUnparsedValue); +}, 'CSSStyleValue.parse[All] returns CSSUnparsedValue for syntax <length>#'); + +// Direct CSSStyleValue objects: + +test_specified_maps(function(map){ + for (let syntax of all_syntaxes()) { + let name = generate_property(syntax); + + let initialValue = target.computedStyleMap().get(name); + + // We only care about direct CSSStyleValue instances in this test. + // Ultimately, in some future version of CSS TypedOM, we may have no + // direct CSSStyleValue instances at all, which is fine. + if (initialValue.constructor !== CSSStyleValue) { + continue; + } + + // Verify that direct CSSStyleValues are rejected by set(). Two things + // should prevent this: 1) direct CSSStyleValues are not + // CSSUnparsedValues, and 2) direct CSSStyleValues are only valid for + // the property they were reified from. + verify_map_not_set(map, generate_property(syntax), initialValue); + } +}, 'Direct CSSStyleValue may not be set'); + +// StylePropertyMap iteration + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('*'), 'foo'); +}, 'Specified * is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('foo'), 'foo'); +}, 'Specified foo is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<angle>'), '10deg'); +}, 'Specified <angle> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<color>'), 'green'); +}, 'Specified <color> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<custom-ident>'), 'foo'); +}, 'Specified <custom-ident> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<image>'), 'url("a")'); +}, 'Specified <image> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<integer>'), '1'); +}, 'Specified <integer> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<length-percentage>'), 'calc(10% + 10px)'); +}, 'Specified <length-percentage> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<length>'), '10px'); +}, 'Specified <length> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<number>'), '1'); +}, 'Specified <number> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<percentage>'), '10%'); +}, 'Specified <percentage> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<resolution>'), '10dpi'); +}, 'Specified <resolution> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<time>'), '1s'); +}, 'Specified <time> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<transform-function>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified <transform-function> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<transform-list>'), 'matrix(0, 0, 0, 0, 0, 0)'); +}, 'Specified <transform-list> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<url>'), 'url("a")'); +}, 'Specified <url> is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<length>+'), '10px 11px'); +}, 'Specified <length>+ is reified CSSUnparsedValue by iterator'); + +test_specified_maps(function(map){ + verify_map_iteration_unparsed(map, generate_property('<length>#'), '10px, 11px'); +}, 'Specified <length># is reified CSSUnparsedValue by iterator'); + +// StylePropertyMapReadOnly iteration + +test(function(){ + let name = generate_property({syntax: '<length>', initialValue: '10px'}); + let result = Array.from(target.computedStyleMap()).filter(e => e[0] == name)[0]; + assert_true(typeof(result) !== 'undefined'); +}, 'Registered property with initial value show up on iteration of computedStyleMap'); + +test(function(){ + verify_computed_iteration_type(generate_property('*'), 'thing', CSSUnparsedValue); +}, 'Computed * is reified as CSSUnparsedValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<angle>'), '42deg', CSSUnitValue); +}, 'Computed <angle> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<custom-ident>'), 'thing', CSSKeywordValue); +}, 'Computed <custom-ident> is reified as CSSKeywordValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<image>'), 'url(\"a\")', CSSImageValue); +}, 'Computed <image> is reified as CSSImageValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<integer>'), '100', CSSUnitValue); +}, 'Computed <integer> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<length>'), '10px', CSSUnitValue); +}, 'Computed <length> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<number>'), '42', CSSUnitValue); +}, 'Computed <number> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<percentage>'), '10%', CSSUnitValue); +}, 'Computed <percentage> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<resolution>'), '300dppx', CSSUnitValue); +}, 'Computed <resolution> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<time>'), '10s', CSSUnitValue); +}, 'Computed <time> is reified as CSSUnitValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('none | thing | THING'), 'THING', CSSKeywordValue); +}, 'Computed none | thing | THING is reified as CSSKeywordValue by iterator'); + +test(function(){ + verify_computed_iteration_type(generate_property('<angle> | <length>'), '10px', CSSUnitValue); +}, 'Computed <angle> | <length> is reified as CSSUnitValue by iterator'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/unit-cycles.html b/testing/web-platform/tests/css/css-properties-values-api/unit-cycles.html new file mode 100644 index 0000000000..9454c95ac3 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/unit-cycles.html @@ -0,0 +1,248 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#dependency-cycles-via-relative-units" /> +<meta name="assert" content="This test verifies that reference cycles via units are detected" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + function register_length(name, inherits=false) { + CSS.registerProperty({ + name: name, + syntax: '<length>', + initialValue: '0px', + inherits: inherits + }); + } + + register_length('--font-size-em'); + register_length('--font-size-rem'); + register_length('--font-size-ex'); + register_length('--font-size-ch'); + register_length('--font-size-px'); + register_length('--font-size-lh'); + register_length('--font-size-em-via-var'); + register_length('--font-size-rem-via-var'); + register_length('--font-size-ex-via-var'); + register_length('--font-size-ch-via-var'); + register_length('--font-size-em-inherited', true); + register_length('--font-size-ex-inherited', true); + register_length('--font-size-ch-inherited', true); + register_length('--line-height-lh'); + register_length('--line-height-lh-via-var'); + register_length('--line-height-lh-inherited', true); +</script> +<style> + :root { + --unregistered-em: 10em; + --unregistered-rem: 10rem; + --unregistered-ex: 10ex; + --unregistered-ch: 10ch; + --unregistered-lh: 10lh; + } + + :root, #target { + --font-size-em: 2em; + --font-size-rem: 2rem; + --font-size-ex: 2ex; + --font-size-ch: 2ch; + --font-size-px: 42px; + --font-size-lh: 2lh; + --line-height-lh: 2lh; + --font-size-em-via-var: var(--unregistered-em); + --font-size-rem-via-var: var(--unregistered-rem); + --font-size-ex-via-var: var(--unregistered-ex); + --font-size-ch-via-var: var(--unregistered-ch); + --line-height-lh-via-var: var(--unregistered-lh); + } + + #parent { + --font-size-em-inherited: 4em; + --font-size-ex-inherited: 4ex; + --font-size-ch-inherited: 4ch; + --line-height-lh-inherited: 4lh; + } + + #target { + font-size: 11px; + line-height: 13px; + } +</style> + +<div id=parent> + <div id=target></div> +</div> +<div id=ref></div> + +<script> + + // Compute a dimension (e.g. 1em) given a certain fontSize. + function compute_dimension(dimension, fontSize, element = ref) { + try { + element.attributeStyleMap.set('font-size', fontSize); + element.attributeStyleMap.set('margin-bottom', dimension); + return getComputedStyle(element).marginBottom; + } finally { + element.attributeStyleMap.clear(); + } + } + + function assert_property_equals(name, value, element = target) { + var computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue(name), value); + } + + let unsetFontSize = compute_dimension('1em', 'unset'); + const unsetLineHeight = "normal"; + + add_result_callback(function(){ + target.attributeStyleMap.clear(); + document.documentElement.attributeStyleMap.clear(); + }); + + test(function() { + target.style = 'font-size: var(--font-size-px);'; + assert_property_equals('font-size', '42px'); + assert_property_equals('--font-size-px', '42px'); + }, 'Non-font-dependent variables can be used in font-size'); + + test(function() { + target.style = 'font-size: var(--font-size-em);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-em', '0px'); + }, 'Lengths with em units may not be referenced from font-size'); + + test(function() { + target.style = 'font-size: var(--font-size-ex);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-ex', '0px'); + }, 'Lengths with ex units may not be referenced from font-size'); + + test(function() { + target.style = 'font-size: var(--font-size-ch);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-ch', '0px'); + }, 'Lengths with ch units may not be referenced from font-size'); + + test(function() { + target.style = 'font-size: var(--font-size-lh);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-lh', '0px'); + }, 'Lengths with lh units may not be referenced from font-size'); + + test(function() { + target.style = 'font-size: var(--font-size-rem);'; + let expected = compute_dimension('2rem', 'unset', document.documentElement); + assert_property_equals('--font-size-rem', expected); + assert_property_equals('font-size', expected); + }, 'Lengths with rem units may be referenced from font-size on non-root element'); + + test(function() { + let root = document.documentElement; + root.style = 'font-size: var(--font-size-rem);'; + assert_property_equals('font-size', unsetFontSize, root); + assert_property_equals('--font-size-rem', '0px', root); + }, 'Lengths with rem units may not be referenced from font-size on root element'); + + test(function() { + target.style = 'line-height: var(--line-height-lh);'; + assert_property_equals('line-height', unsetLineHeight); + assert_property_equals('--line-height-lh', '0px'); + }, 'Lengths with lh units may not be referenced from line-height'); + + test(function() { + target.style = 'font-size: var(--noexist, var(--font-size-em));'; + assert_property_equals('font-size', unsetFontSize); + }, 'Fallback may not use font-relative units'); + + test(function() { + target.style = 'line-height: var(--noexist, var(--line-height-lh));'; + assert_property_equals('line-height', unsetLineHeight); + }, 'Fallback may not use line-height-relative units'); + + test(function() { + target.style = 'font-size: var(--font-size-em, 42px);'; + assert_property_equals('font-size', unsetFontSize); + }, 'Fallback not triggered while inside em unit cycle'); + + test(function() { + target.style = 'font-size: var(--font-size-ex, 42px);'; + assert_property_equals('font-size', unsetFontSize); + }, 'Fallback not triggered while inside ex unit cycle'); + + test(function() { + target.style = 'font-size: var(--font-size-ch, 42px);'; + assert_property_equals('font-size', unsetFontSize); + }, 'Fallback not triggered while inside ch unit cycle'); + + test(function() { + let root = document.documentElement; + root.style = 'font-size: var(--font-size-rem, 42px);'; + assert_property_equals('font-size', unsetFontSize, root); + root.style = 'font-size: unset;'; + }, 'Fallback not triggered while inside rem unit cycle on root element'); + + test(function() { + target.style = 'line-height: var(--line-height-lh, 42px);'; + assert_property_equals('line-height', unsetLineHeight); + }, 'Fallback not triggered while inside lh unit cycle'); + + test(function() { + target.style = 'font-size: var(--font-size-em-via-var);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-em-via-var', '0px'); + }, 'Lengths with em units are detected via var references'); + + test(function() { + target.style = 'font-size: var(--font-size-ex-via-var);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-ex-via-var', '0px'); + }, 'Lengths with ex units are detected via var references'); + + test(function() { + target.style = 'font-size: var(--font-size-ch-via-var);'; + assert_property_equals('font-size', unsetFontSize); + assert_property_equals('--font-size-ch-via-var', '0px'); + }, 'Lengths with ch units are detected via var references'); + + test(function() { + let root = document.documentElement; + root.style = 'font-size: var(--font-size-rem-via-var);'; + assert_property_equals('font-size', unsetFontSize, root); + assert_property_equals('--font-size-rem-via-var', '0px', root); + root.style = 'font-size: unset'; + }, 'Lengths with rem units are detected via var references'); + + test(function() { + target.style = 'line-height: var(--line-height-lh-via-var);'; + assert_property_equals('line-height', unsetLineHeight); + assert_property_equals('--line-height-lh-via-var', '0px'); + }, 'Lengths with lh units are detected via var references'); + + test(function() { + let expected4em = compute_dimension('4em', 'unset'); + target.style = 'font-size: var(--font-size-em-inherited);'; + assert_property_equals('font-size', expected4em); + assert_property_equals('--font-size-em-inherited', expected4em); + }, 'Inherited lengths with em units may be used'); + + test(function() { + let expected4ex = compute_dimension('4ex', 'unset'); + target.style = 'font-size: var(--font-size-ex-inherited);'; + assert_property_equals('font-size', expected4ex); + assert_property_equals('--font-size-ex-inherited', expected4ex); + }, 'Inherited lengths with ex units may be used'); + + test(function() { + let expected4ch = compute_dimension('4ch', 'unset'); + target.style = 'font-size: var(--font-size-ch-inherited);'; + assert_property_equals('font-size', expected4ch); + assert_property_equals('--font-size-ch-inherited', expected4ch); + }, 'Inherited lengths with ch units may be used'); + + test(function() { + let expected4lh = compute_dimension('4lh', 'unset'); + target.style = 'line-height: var(--line-height-lh-inherited);'; + assert_property_equals('line-height', expected4lh); + assert_property_equals('--line-height-lh-inherited', expected4lh); + }, 'Inherited lengths with lh units may be used'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/url-resolution.html b/testing/web-platform/tests/css/css-properties-values-api/url-resolution.html new file mode 100644 index 0000000000..d932789649 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/url-resolution.html @@ -0,0 +1,137 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#relative-urls" /> +<meta name="assert" content="This test verifies that relative URLs in registered properties resolve correctly" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script src="support/main/main.js"></script> +<script src="support/alt/alt.js"></script> +<link id="main" rel="stylesheet" type="text/css" href="support/main/main.css" /> +<link id="main_utf16be" rel="stylesheet" type="text/css" href="support/main/main.utf16be.css" /> +<link id="alt" rel="stylesheet" type="text/css" href="support/alt/alt.css" /> +<div id=target> + <div id=inner></div> +</div> +<script> +function parse_url(urlstr) { + urlstr = urlstr.trim(); + if (!urlstr.startsWith('url("') || !urlstr.endsWith('")')) + throw 'Unknown url format: ' + urlstr; + return urlstr.slice(5, -2); +} + +function get_bg_url(element) { + return parse_url(getComputedStyle(element)['background-image']); +} + +function get_bg_urls(element) { + return getComputedStyle(element)['background-image'] + .split(',') + .map(x => x.trim()) + .map(x => parse_url(x)); +} + +function assert_base_path_equal(actual, expected) { + let actual_base = new URL(actual).pathname.split('/').slice(0, -1); + let expected_base = new URL(expected).pathname.split('/').slice(0, -1); + assert_equals(actual_base.join('/'), expected_base.join('/')); +} + +function assert_base_paths_equal(actual, expected) { + assert_equals(actual.length, expected.length); + for (let i = 0; i < actual.length; i++) { + assert_base_path_equal(actual[i], expected[i]); + } +} + +test(function() { + target.style = 'background-image: var(--unreg-url);'; + assert_base_path_equal(get_bg_url(target), document.baseURI); +}, 'Unregistered property resolves against document (URL token)'); + +test(function() { + target.style = 'background-image: var(--unreg-func);'; + assert_base_path_equal(get_bg_url(target), document.baseURI); +}, 'Unregistered property resolves against document (URL function)'); + +test(function() { + target.style = 'background-image: var(--reg-non-inherited-url);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered non-inherited <url> resolves against sheet (URL token)'); + +test(function() { + target.style = 'background-image: var(--reg-non-inherited-func);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered non-inherited <url> resolves against sheet (URL function)'); + +test(function() { + target.style = 'background-image: var(--reg-inherited-url);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered inherited <url> resolves against sheet (URL token)'); + +test(function() { + target.style = 'background-image: var(--reg-inherited-func);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered inherited <url> resolves against sheet (URL function)'); + +test(function() { + inner.style = 'background-image: var(--reg-inherited-url);'; + assert_base_path_equal(get_bg_url(inner), main.sheet.href); +}, 'Registered inherited <url> resolves against sheet (Child node, URL token)'); + +test(function() { + inner.style = 'background-image: var(--reg-inherited-func);'; + assert_base_path_equal(get_bg_url(inner), main.sheet.href); +}, 'Registered inherited <url> resolves against sheet (Child node, URL function)'); + +test(function() { + target.style = 'background-image: var(--reg-ref-to-unreg-url);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered property with unregistered var reference resolves against sheet (URL token)'); + +test(function() { + target.style = 'background-image: var(--reg-ref-to-unreg-func);'; + assert_base_path_equal(get_bg_url(target), main.sheet.href); +}, 'Registered property with unregistered var reference resolves against sheet. (URL function)'); + +test(function() { + target.style = 'background-image: var(--reg-ref-to-reg-url);'; + assert_base_path_equal(get_bg_url(target), alt.sheet.href); +}, 'Registered property with registered var reference resolves against sheet of referenced property (URL token)'); + +test(function() { + target.style = 'background-image: var(--reg-ref-to-reg-func);'; + assert_base_path_equal(get_bg_url(target), alt.sheet.href); +}, 'Registered property with registered var reference resolves against sheet of referenced property (URL function)'); + +test(function() { + target.style = 'background-image: var(--unreg-ref-to-reg-url);'; + assert_base_path_equal(get_bg_url(target), alt.sheet.href); +}, 'Unregistered property with registered var reference resolves against sheet of referenced property (URL token)'); + +test(function() { + target.style = 'background-image: var(--unreg-ref-to-reg-func);'; + assert_base_path_equal(get_bg_url(target), alt.sheet.href); +}, 'Unregistered property with registered var reference resolves against sheet of referenced property (URL function)'); + +test(function() { + target.style = 'background-image: var(--unreg-multi-ref-to-reg-urls);'; + assert_base_paths_equal(get_bg_urls(target), [main.sheet.href, alt.sheet.href]); +}, 'Multiple (registered) var reference resolve against respective sheets (URL token)'); + +test(function() { + target.style = 'background-image: var(--unreg-multi-ref-to-reg-funcs);'; + assert_base_paths_equal(get_bg_urls(target), [main.sheet.href, alt.sheet.href]); +}, 'Multiple (registered) var reference resolve against respective sheets (URL function)'); + +test(function() { + target.style = 'background-image: var(--reg-utf16be-url);'; + assert_base_path_equal(get_bg_url(target), main_utf16be.sheet.href); +}, 'Registered UTF16BE-encoded var reference resolve against sheet (URL token)'); + +test(function() { + target.style = 'background-image: var(--reg-utf16be-func);'; + assert_base_path_equal(get_bg_url(target), main_utf16be.sheet.href); +}, 'Registered UTF16BE-encoded var reference resolve against sheet (URL function)'); + +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties-cycles.html b/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties-cycles.html new file mode 100644 index 0000000000..91792ef51d --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties-cycles.html @@ -0,0 +1,183 @@ +<!DOCTYPE HTML> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +#test1 { + --registered-1-a: var(--registered-1-b, 10px); + --registered-1-b: var(--registered-1-a, 20px); + + --registered-1-c: var(--registered-1-b, 30px); + --registered-1-d: var(--registered-1-b); + --unregistered-1-a:var(--registered-1-a,40px); + --unregistered-1-a:var(--registered-1-a); + left: var(--registered-1-a, 50px); + top: var(--registered-1-b, 60px); +} +</style> +<div id=test1></div> +<script> +test(function() { + CSS.registerProperty({name: '--registered-1-a', syntax: '<length>', initialValue: '1px', inherits: false}); + CSS.registerProperty({name: '--registered-1-b', syntax: '<length>', initialValue: '2px', inherits: false}); + CSS.registerProperty({name: '--registered-1-c', syntax: '<length>', initialValue: '3px', inherits: false}); + CSS.registerProperty({name: '--registered-1-d', syntax: '<length>', initialValue: '4px', inherits: false}); + + computedStyle = getComputedStyle(test1); + assert_equals(computedStyle.getPropertyValue('--registered-1-a'), '1px'); + assert_equals(computedStyle.getPropertyValue('--registered-1-b'), '2px'); + assert_equals(computedStyle.getPropertyValue('--registered-1-c'), '2px'); + assert_equals(computedStyle.getPropertyValue('--registered-1-d'), '2px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-1-a'), '1px'); + assert_equals(computedStyle.left, '1px'); + assert_equals(computedStyle.top, '2px'); +}, "A var() cycle between two registered properties is handled correctly."); +</script> + +<style> +#test2 { + --registered-2-a: var(--unregistered-2-a, 10px); + --unregistered-2-a:var(--registered-2-a,20px); + + --registered-2-b: var(--registered-2-a, 30px); + --registered-2-c: var(--registered-2-a); + --registered-2-d: var(--unregistered-2-a, 40px); + --registered-2-e: var(--unregistered-2-a); + --unregistered-2-b:var(--registered-2-a,50px); + --unregistered-2-c:var(--registered-2-a); + --unregistered-2-d:var(--unregistered-2-a,60px); + --unregistered-2-e:var(--unregistered-2-a); + left: var(--registered-2-a, 70px); + top: var(--unregistered-2-a, 80px); +} +</style> +<div id=test2></div> +<script> +test(function() { + CSS.registerProperty({name: '--registered-2-a', syntax: '<length>', initialValue: '1px', inherits: false}); + CSS.registerProperty({name: '--registered-2-b', syntax: '<length>', initialValue: '2px', inherits: false}); + CSS.registerProperty({name: '--registered-2-c', syntax: '<length>', initialValue: '3px', inherits: false}); + CSS.registerProperty({name: '--registered-2-d', syntax: '<length>', initialValue: '4px', inherits: false}); + CSS.registerProperty({name: '--registered-2-e', syntax: '<length>', initialValue: '5px', inherits: false}); + + computedStyle = getComputedStyle(test2); + assert_equals(computedStyle.getPropertyValue('--registered-2-a'), '1px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-2-a'), ''); + + assert_equals(computedStyle.getPropertyValue('--registered-2-b'), '1px'); + assert_equals(computedStyle.getPropertyValue('--registered-2-c'), '1px'); + assert_equals(computedStyle.getPropertyValue('--registered-2-d'), '40px'); + assert_equals(computedStyle.getPropertyValue('--registered-2-e'), '5px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-2-b'), '1px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-2-c'), '1px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-2-d'), '60px'); + assert_equals(computedStyle.getPropertyValue('--unregistered-2-e'), ''); + assert_equals(computedStyle.left, '1px'); + assert_equals(computedStyle.top, '80px'); +}, "A var() cycle between a registered properties and an unregistered property is handled correctly."); +</script> + +<style> +#test3 { + --unregistered-3-a:var(--unregistered-3-b,10px); + --unregistered-3-b:var(--unregistered-3-a,20px); + + --registered-3-a: var(--unregistered-3-a, 30px); + --registered-3-b: var(--unregistered-3-a); + --registered-3-c: var(--unregistered-3-b, 40px); + --registered-3-d: var(--registered-3-c, 50px); + left: var(--registered-3-d, 60px); + top: var(--registered-3-b, 70px); +} +</style> +<div id=test3></div> +<script> +test(function() { + CSS.registerProperty({name: '--registered-3-a', syntax: '<length>', initialValue: '1px', inherits: false}); + CSS.registerProperty({name: '--registered-3-b', syntax: '<length>', initialValue: '2px', inherits: false}); + CSS.registerProperty({name: '--registered-3-c', syntax: '<length>', initialValue: '3px', inherits: false}); + CSS.registerProperty({name: '--registered-3-d', syntax: '<length>', initialValue: '4px', inherits: false}); + + computedStyle = getComputedStyle(test3); + assert_equals(computedStyle.getPropertyValue('--unregistered-3-a'), ''); + assert_equals(computedStyle.getPropertyValue('--unregistered-3-b'), ''); + + assert_equals(computedStyle.getPropertyValue('--registered-3-a'), '30px'); + assert_equals(computedStyle.getPropertyValue('--registered-3-b'), '2px'); + assert_equals(computedStyle.getPropertyValue('--registered-3-c'), '40px'); + assert_equals(computedStyle.getPropertyValue('--registered-3-d'), '40px'); + assert_equals(computedStyle.left, '40px'); + assert_equals(computedStyle.top, '2px'); +}, "A var() cycle between a two unregistered properties is handled correctly."); +</script> + +<style> +#test4 { + --registered-4-a:var(--unregistered-4-a,hello); + --unregistered-4-a:var(--registered-4-a,world); + + --registered-4-b:var(--unregistered-4-a,meow); + --registered-4-c:var(--unregistered-4-a); + --unregistered-4-b:var(--unregistered-4-a,woof); + --unregistered-4-c:var(--unregistered-4-a); + transition-property: var(--registered-4-a, water); +} +</style> +<div id=test4></div> +<script> +test(function() { + CSS.registerProperty({name: '--registered-4-a', syntax: '*', inherits: false}); + CSS.registerProperty({name: '--registered-4-b', syntax: '*', initialValue: 'moo', inherits: false}); + CSS.registerProperty({name: '--registered-4-c', syntax: '*', initialValue: 'circle', inherits: false}); + + computedStyle = getComputedStyle(test4); + assert_equals(computedStyle.getPropertyValue('--registered-4-a'), ''); + assert_equals(computedStyle.getPropertyValue('--unregistered-4-a'), ''); + + assert_equals(computedStyle.getPropertyValue('--registered-4-b'), 'meow'); + assert_equals(computedStyle.getPropertyValue('--registered-4-c'), ''); + assert_equals(computedStyle.getPropertyValue('--unregistered-4-b'), 'woof'); + assert_equals(computedStyle.getPropertyValue('--unregistered-4-c'), ''); + assert_equals(computedStyle.transitionProperty, 'water'); +}, "A var() cycle between a syntax:'*' property and an unregistered property is handled correctly."); +</script> + +<style> +#test5_parent { + --registered-5-c:foo; + --registered-5-d:bar; + --registered-5-e:baz; + color: green; +} +#test5 { + --registered-5-a:var(--registered-5-b,hello); + --registered-5-b:var(--registered-5-a,world); + + --registered-5-c:var(--registered-5-a); + --registered-5-d:var(--registered-5-b); + --registered-5-e:var(--unknown); + color: var(--registered-5-e); +} +</style> +<div id=test5_parent> + <div id=test5></div> +</div> +<script> +test(function() { + CSS.registerProperty({name: '--registered-5-a', syntax: '*', inherits: true}); + CSS.registerProperty({name: '--registered-5-b', syntax: '*', inherits: true}); + CSS.registerProperty({name: '--registered-5-c', syntax: '*', inherits: true}); + CSS.registerProperty({name: '--registered-5-d', syntax: '*', inherits: true}); + CSS.registerProperty({name: '--registered-5-e', syntax: '*', inherits: true}); + + let computedStyle = getComputedStyle(test5); + assert_equals(computedStyle.getPropertyValue('--registered-5-a'), ''); + assert_equals(computedStyle.getPropertyValue('--registered-5-b'), ''); + assert_equals(computedStyle.getPropertyValue('--registered-5-c'), ''); + assert_equals(computedStyle.getPropertyValue('--registered-5-d'), ''); + assert_equals(computedStyle.getPropertyValue('--registered-5-e'), ''); + assert_equals(computedStyle.getPropertyValue('color'), 'rgb(0, 128, 0)'); +}, "Custom properties with universal syntax become guaranteed-invalid when " + + "invalid at computed-value time"); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties.html b/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties.html new file mode 100644 index 0000000000..a6f26429e8 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/var-reference-registered-properties.html @@ -0,0 +1,182 @@ +<!DOCTYPE HTML> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/utils.js"></script> +<style> +div { + --registered-length-1: 10px; + --registered-length-2: var(--registered-length-1); + --registered-length-3: var(--length-1); + --registered-length-4: calc(var(--length-1) + 40px); + --registered-length-5: var(--invalid, 70px); + --registered-length-6: calc(var(--registered-length-3)*4); + --registered-length-7: var(--123px, 6px); + + --length-1: 20px; + --length-2: var(--registered-length-1); + --length-3: calc(var(--123px, 6px) + var(--123px)); + + --percentage: 10%; + --registered-length-invalid: var(--percentage); + + --registered-token-stream-1:var(--invalid); + --registered-token-stream-2:var(--invalid,fallback); + --token-stream-1:var(--registered-token-stream-1,moo); + + --registered-length-list-1: 1px, var(--registered-length-1), 2px; + --registered-length-list-2: 1px, var(--length-1), var(--registered-length-1), 2px; + --registered-length-list-3: var(--registered-length-list-1), var(--registered-length-list-2); +} +</style> +<div id=element></div> +<script> +test(function() { + CSS.registerProperty({name: '--123px', syntax: '<length>', initialValue: '123px', inherits: false}); + + CSS.registerProperty({name: '--registered-length-1', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-2', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-3', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-4', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-5', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-6', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-7', syntax: '<length>', initialValue: '0px', inherits: false}); + CSS.registerProperty({name: '--registered-length-invalid', syntax: '<length>', initialValue: '15px', inherits: false}); + + CSS.registerProperty({name: '--registered-token-stream-1', syntax: '*', inherits: false}); + CSS.registerProperty({name: '--registered-token-stream-2', syntax: '*', inherits: false}); + + computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--registered-length-1'), '10px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-2'), '10px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-3'), '20px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-4'), '60px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-5'), '70px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-6'), '80px'); + assert_equals(computedStyle.getPropertyValue('--registered-length-7'), '123px'); + assert_equals(computedStyle.getPropertyValue('--length-1'), '20px'); + assert_equals(computedStyle.getPropertyValue('--length-2'), '10px'); + assert_equals(computedStyle.getPropertyValue('--length-3'), 'calc(123px + 123px)'); + assert_equals(computedStyle.getPropertyValue('--registered-length-invalid'), '15px'); + + assert_equals(computedStyle.getPropertyValue('--registered-token-stream-1'), ''); + assert_equals(computedStyle.getPropertyValue('--registered-token-stream-2'), 'fallback'); + assert_equals(computedStyle.getPropertyValue('--token-stream-1'), 'moo'); +}, "var() references work with registered properties"); + +test(function(){ + CSS.registerProperty({ + name: '--registered-length-list-1', + syntax: '<length>#', + initialValue: '0px', + inherits: false + }); + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--registered-length-list-1'), '1px, 10px, 2px'); +}, 'References to registered var()-properties work in registered lists'); + +test(function(){ + CSS.registerProperty({ + name: '--registered-length-list-2', + syntax: '<length>#', + initialValue: '0px', + inherits: false + }); + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--registered-length-list-2'), '1px, 20px, 10px, 2px'); +}, 'References to mixed registered and unregistered var()-properties work in registered lists'); + +test(function(){ + CSS.registerProperty({ + name: '--registered-length-list-3', + syntax: '<length>#', + initialValue: '0px', + inherits: false + }); + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--registered-length-list-3'), '1px, 10px, 2px, 1px, 20px, 10px, 2px'); +}, 'Registered lists may be concatenated'); + +test(function(){ + CSS.registerProperty({ + name: '--length-em', + syntax: '<length>', + initialValue: '0px', + inherits: false + }); + element.style = 'font-size: 11px; --length-em: 10em; --unregistered:var(--length-em);'; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px'); + element.style = ''; +}, 'Font-relative units are absolutized when substituting'); + +test(function(){ + CSS.registerProperty({ + name: '--length-calc', + syntax: '<length>', + initialValue: '0px', + inherits: false + }); + element.style = 'font-size: 11px; --length-calc: calc(10em + 10px); --unregistered:var(--length-calc);'; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--unregistered'), '120px'); + element.style = ''; +}, 'Calc expressions are resolved when substituting'); + +test(function(){ + CSS.registerProperty({ + name: '--length-calc-list', + syntax: '<length>#', + initialValue: '0px', + inherits: false + }); + element.style = 'font-size: 11px; --length-calc-list: 10em, calc(10em + 10px); --unregistered:var(--length-calc-list);'; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px, 120px'); + element.style = ''; +}, 'Lists with relative units are absolutized when substituting'); + +test(function(){ + let length = generate_property('none | <length>'); + let universal = generate_property('*'); + element.style = `font-size: 10px; ${length}: 10em; ${universal}: var(${length})`; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue(universal), '100px'); + element.style = ''; +}, 'Values are absolutized when substituting into properties with universal syntax'); + +function test_valid_fallback(syntax, value, fallback) { + test(function(){ + let name = generate_property(syntax); + try { + element.style = `${name}: ${value}; --x:var(${name},${fallback})`; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--x'), value); + } finally { + element.style = ''; + } + }, `Valid fallback does not invalidate var()-reference [${syntax}, ${fallback}]`); +} + +function test_invalid_fallback(syntax, value, fallback) { + test(function(){ + let name = generate_property(syntax); + try { + element.style = `${name}: ${value}; --x:var(${name},${fallback})`; + let computedStyle = getComputedStyle(element); + assert_equals(computedStyle.getPropertyValue('--x'), ''); + } finally { + element.style = ''; + } + }, `Invalid fallback invalidates var()-reference [${syntax}, ${fallback}]`); +} + +test_valid_fallback('<length>', '40px', '10px'); +test_valid_fallback('<length> | <color>', '40px', 'red'); +test_valid_fallback('<length> | none', '40px', 'none'); + +test_invalid_fallback('<length>', '40px', 'red'); +test_invalid_fallback('<length> | none', '40px', 'nolength'); +test_invalid_fallback('<length>', '40px', 'var(--novar)'); + +</script> |