summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/support
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/css/support')
-rw-r--r--testing/web-platform/tests/css/support/1x1-green.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/support/1x1-lime.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/support/1x1-maroon.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/support/1x1-navy.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/support/1x1-red.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/support/1x1-white.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/support/60x60-gg-rr.pngbin0 -> 224 bytes
-rw-r--r--testing/web-platform/tests/css/support/60x60-green.pngbin0 -> 218 bytes
-rw-r--r--testing/web-platform/tests/css/support/60x60-red.pngbin0 -> 217 bytes
-rw-r--r--testing/web-platform/tests/css/support/META.yml2
-rw-r--r--testing/web-platform/tests/css/support/README28
-rw-r--r--testing/web-platform/tests/css/support/a-green.css1
-rw-r--r--testing/web-platform/tests/css/support/alignment.css367
-rw-r--r--testing/web-platform/tests/css/support/b-green.css1
-rw-r--r--testing/web-platform/tests/css/support/blue-100.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/support/blue32x32.icobin0 -> 326 bytes
-rw-r--r--testing/web-platform/tests/css/support/c-red.css1
-rw-r--r--testing/web-platform/tests/css/support/cat.pngbin0 -> 1883 bytes
-rw-r--r--testing/web-platform/tests/css/support/computed-testcommon.js212
-rw-r--r--testing/web-platform/tests/css/support/delete-other-rule-crash.css2
-rw-r--r--testing/web-platform/tests/css/support/green.icobin0 -> 894 bytes
-rw-r--r--testing/web-platform/tests/css/support/grid.css289
-rw-r--r--testing/web-platform/tests/css/support/height-keyword-classes.css39
-rw-r--r--testing/web-platform/tests/css/support/import-green.css1
-rw-r--r--testing/web-platform/tests/css/support/import-red.css1
-rw-r--r--testing/web-platform/tests/css/support/inheritance-testcommon.js96
-rw-r--r--testing/web-platform/tests/css/support/interpolation-testcommon.js467
-rw-r--r--testing/web-platform/tests/css/support/numeric-testcommon.js196
-rw-r--r--testing/web-platform/tests/css/support/parsing-testcommon.js166
-rw-r--r--testing/web-platform/tests/css/support/pattern-grg-rgr-grg.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/css/support/pattern-grg-rrg-rgg.pngbin0 -> 231 bytes
-rw-r--r--testing/web-platform/tests/css/support/pattern-rgr-grg-rgr.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/css/support/pattern-tr.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/css/support/query-testcommon.js18
-rw-r--r--testing/web-platform/tests/css/support/red-rect.svg6
-rw-r--r--testing/web-platform/tests/css/support/red.icobin0 -> 894 bytes
-rw-r--r--testing/web-platform/tests/css/support/ruler-h-50%.pngbin0 -> 691 bytes
-rw-r--r--testing/web-platform/tests/css/support/ruler-h-50px.pngbin0 -> 671 bytes
-rw-r--r--testing/web-platform/tests/css/support/ruler-v-100px.pngbin0 -> 760 bytes
-rw-r--r--testing/web-platform/tests/css/support/ruler-v-50px.pngbin0 -> 757 bytes
-rw-r--r--testing/web-platform/tests/css/support/serialize-testcommon.js94
-rw-r--r--testing/web-platform/tests/css/support/shorthand-testcommon.js40
-rw-r--r--testing/web-platform/tests/css/support/square-purple.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/support/square-teal.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/support/square-white.pngbin0 -> 78 bytes
-rw-r--r--testing/web-platform/tests/css/support/support/README4
-rw-r--r--testing/web-platform/tests/css/support/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-blue.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-lime.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-orange.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-teal.pngbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-white.pngbin0 -> 85 bytes
-rw-r--r--testing/web-platform/tests/css/support/swatch-yellow.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-bl.pngbin0 -> 1368 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-br.pngbin0 -> 1045 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-inner-half-size.pngbin0 -> 180 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-outer.pngbin0 -> 2412 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-tl.pngbin0 -> 1025 bytes
-rw-r--r--testing/web-platform/tests/css/support/test-tr.pngbin0 -> 1235 bytes
-rw-r--r--testing/web-platform/tests/css/support/width-keyword-classes.css43
63 files changed, 2074 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/support/1x1-green.png b/testing/web-platform/tests/css/support/1x1-green.png
new file mode 100644
index 0000000000..b98ca0ba0a
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/1x1-lime.png b/testing/web-platform/tests/css/support/1x1-lime.png
new file mode 100644
index 0000000000..cb397fb090
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/1x1-maroon.png b/testing/web-platform/tests/css/support/1x1-maroon.png
new file mode 100644
index 0000000000..3f86b07219
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-maroon.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/1x1-navy.png b/testing/web-platform/tests/css/support/1x1-navy.png
new file mode 100644
index 0000000000..9b9a03955b
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-navy.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/1x1-red.png b/testing/web-platform/tests/css/support/1x1-red.png
new file mode 100644
index 0000000000..6bd73ac101
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/1x1-white.png b/testing/web-platform/tests/css/support/1x1-white.png
new file mode 100644
index 0000000000..dd43faec54
--- /dev/null
+++ b/testing/web-platform/tests/css/support/1x1-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/60x60-gg-rr.png b/testing/web-platform/tests/css/support/60x60-gg-rr.png
new file mode 100644
index 0000000000..84f5b2a4f1
--- /dev/null
+++ b/testing/web-platform/tests/css/support/60x60-gg-rr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/60x60-green.png b/testing/web-platform/tests/css/support/60x60-green.png
new file mode 100644
index 0000000000..b3c8cf3eb4
--- /dev/null
+++ b/testing/web-platform/tests/css/support/60x60-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/60x60-red.png b/testing/web-platform/tests/css/support/60x60-red.png
new file mode 100644
index 0000000000..823f125b8e
--- /dev/null
+++ b/testing/web-platform/tests/css/support/60x60-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/META.yml b/testing/web-platform/tests/css/support/META.yml
new file mode 100644
index 0000000000..babe8bdaeb
--- /dev/null
+++ b/testing/web-platform/tests/css/support/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - frivoal
diff --git a/testing/web-platform/tests/css/support/README b/testing/web-platform/tests/css/support/README
new file mode 100644
index 0000000000..2e5f2ad073
--- /dev/null
+++ b/testing/web-platform/tests/css/support/README
@@ -0,0 +1,28 @@
+CSS Global Support Directory
+============================
+
+This directory contains common support files (such as images and external
+style sheets). These are sync'ed into the support directories of all our
+test suites. If you have test-suite-specific support files, please add
+them to the appropriate test-suite-specific support/ directory.
+
+If you add to a support/ directory, please run the tools/supportprop.py
+script from the top of the repository to cascade support files into the
+lower-level support directories.
+
+Description of the Common Support File Collection
+-------------------------------------------------
+
+The 1x1-* images are all exactly one pixel.
+
+The swatch-* images all use 15x15 cells.
+
+The square-* images all use 15x15 cells with one pixel borders.
+
+The pattern-* images use cells of various sizes:
+
+ pattern-grg-rgr-grg.png 20x20
+ pattern-rgr-grg-rgr.png 20x20
+ pattern-tr.png 15x15
+ pattern-grg-rrg-rgg.png 15x15
+
diff --git a/testing/web-platform/tests/css/support/a-green.css b/testing/web-platform/tests/css/support/a-green.css
new file mode 100644
index 0000000000..b0dbb071d5
--- /dev/null
+++ b/testing/web-platform/tests/css/support/a-green.css
@@ -0,0 +1 @@
+.a { color: green; }
diff --git a/testing/web-platform/tests/css/support/alignment.css b/testing/web-platform/tests/css/support/alignment.css
new file mode 100644
index 0000000000..d4c970c52b
--- /dev/null
+++ b/testing/web-platform/tests/css/support/alignment.css
@@ -0,0 +1,367 @@
+/* align-self */
+.alignSelfAuto { align-self: auto; }
+.alignSelfNormal { align-self: normal; }
+.alignSelfStretch { align-self: stretch; }
+.alignSelfStart { align-self: start; }
+.alignSelfEnd { align-self: end; }
+.alignSelfCenter { align-self: center; }
+.alignSelfRight { align-self: right; }
+.alignSelfLeft { align-self: left; }
+
+.alignSelfFlexStart { align-self: flex-start; }
+.alignSelfFlexEnd { align-self: flex-end; }
+
+.alignSelfSelfStart { align-self: self-start; }
+.alignSelfSelfEnd { align-self: self-end; }
+
+.alignSelfSafeCenter { align-self: safe center; }
+.alignSelfUnsafeCenter { align-self: unsafe center; }
+.alignSelfSafeEnd { align-self: safe end; }
+.alignSelfUnsafeEnd { align-self: unsafe end; }
+.alignSelfSafeSelfEnd { align-self: safe self-end; }
+.alignSelfUnsafeSelfEnd { align-self: unsafe self-end; }
+.alignSelfSafeSelfStart { align-self: safe self-start; }
+.alignSelfUnsafeSelfStart { align-self: unsafe self-start; }
+.alignSelfSafeRight { align-self: safe right; }
+.alignSelfUnsafeRight { align-self: unsafe right; }
+.alignSelfSafeLeft { align-self: safe left; }
+.alignSelfUnsafeLeft { align-self: unsafe left; }
+.alignSelfSafeFlexEnd { align-self: safe flex-end; }
+.alignSelfUnsafeFlexEnd { align-self: unsafe flex-end; }
+.alignSelfSafeFlexStart { align-self: safe flex-start; }
+.alignSelfUnsafeFlexStart { align-self: unsafe flex-start; }
+
+.alignSelfBaseline { align-self: baseline; }
+.alignSelfFirstBaseline { align-self: first baseline; }
+.alignSelfLastBaseline { align-self: last baseline; }
+
+/* align-items */
+.alignItemsAuto { align-items: auto; }
+.alignItemsNormal { align-items: normal; }
+.alignItemsStretch { align-items: stretch; }
+.alignItemsStart { align-items: start; }
+.alignItemsCenter { align-items: center; }
+.alignItemsEnd { align-items: end; }
+.alignItemsLeft { align-items: left; }
+.alignItemsRight { align-items: right; }
+
+.alignItemsFlexStart { align-items: flex-start; }
+.alignItemsFlexEnd { align-items: flex-end; }
+
+.alignItemsSelfStart { align-items: self-start; }
+.alignItemsSelfEnd { align-items: self-end; }
+
+.alignItemsSafeCenter { align-items: safe center; }
+.alignItemsUnsafeCenter { align-items: unsafe center; }
+.alignItemsSafeEnd { align-items: safe end; }
+.alignItemsUnsafeEnd { align-items: unsafe end; }
+.alignItemsSafeSelfEnd { align-items: safe self-end; }
+.alignItemsUnsafeSelfEnd { align-items: unsafe self-end; }
+.alignItemsSafeSelfStart { align-items: safe self-start; }
+.alignItemsUnsafeSelfStart { align-items: unsafe self-start; }
+.alignItemsSafeRight { align-items: safe right; }
+.alignItemsUnsafeRight { align-items: unsafe right; }
+.alignItemsSafeLeft { align-items: safe left; }
+.alignItemsUnsafeLeft { align-items: unsafe left; }
+.alignItemsSafeFlexEnd { align-items: safe flex-end; }
+.alignItemsUnsafeFlexEnd { align-items: unsafe flex-end; }
+.alignItemsSafeFlexStart { align-items: safe flex-start; }
+.alignItemsUnsafeFlexStart { align-items: unsafe flex-start; }
+
+.alignItemsBaseline { align-items: baseline; }
+.alignItemsFirstBaseline { align-items: first baseline; }
+.alignItemsLastBaseline { align-items: last baseline; }
+
+/* align-content */
+.alignContentBaseline { align-content: baseline; }
+.alignContentLastBaseline { align-content: last-baseline; }
+.alignContentStart { align-content: start; }
+.alignContentEnd { align-content: end; }
+.alignContentCenter { align-content: center; }
+.alignContentLeft { align-content: left; }
+.alignContentRight { align-content: right; }
+
+.alignContentFlexStart { align-content: flex-start; }
+.alignContentFlexEnd { align-content: flex-end; }
+
+.alignContentSpaceBetween { align-content: space-between; }
+.alignContentSpaceAround { align-content: space-around; }
+.alignContentSpaceEvenly { align-content: space-evenly; }
+.alignContentStretch { align-content: stretch; }
+
+.alignContentSafeCenter { align-content: safe center; }
+.alignContentUnsafeCenter { align-content: unsafe center; }
+.alignContentSafeEnd { align-content: safe end; }
+.alignContentUnsafeEnd { align-content: unsafe end; }
+.alignContentSafeRight { align-content: safe right; }
+.alignContentUnsafeRight { align-content: unsafe right; }
+.alignContentSafeLeft { align-content: safe left; }
+.alignContentUnsafeLeft { align-content: unsafe left; }
+.alignContentSafeFlexEnd { align-content: safe flex-end; }
+.alignContentUnsafeFlexEnd { align-content: unsafe flex-end; }
+.alignContentSafeFlexStart { align-content: safe flex-start; }
+.alignContentUnsafeFlexStart { align-content: unsafe flex-start; }
+
+.alignContentBaseline { align-content: baseline; }
+.alignContentFirstBaseline { align-content: first baseline; }
+.alignContentLastBaseline { align-content: last baseline; }
+
+/* justify-self */
+.justifySelfAuto { justify-self: auto; }
+.justifySelfNormal { justify-self: normal; }
+.justifySelfStretch { justify-self: stretch; }
+.justifySelfStart { justify-self: start; }
+.justifySelfCenter { justify-self: center; }
+.justifySelfEnd { justify-self: end; }
+.justifySelfRight { justify-self: right; }
+.justifySelfLeft { justify-self: left; }
+
+.justifySelfFlexStart { justify-self: flex-start; }
+.justifySelfFlexEnd { justify-self: flex-end; }
+
+.justifySelfSelfStart { justify-self: self-start; }
+.justifySelfSelfEnd { justify-self: self-end; }
+
+.justifySelfSafeCenter { justify-self: safe center; }
+.justifySelfUnsafeCenter { justify-self: unsafe center; }
+.justifySelfSafeEnd { justify-self: safe end; }
+.justifySelfUnsafeEnd { justify-self: unsafe end; }
+.justifySelfSafeSelfEnd { justify-self: safe self-end; }
+.justifySelfUnsafeSelfEnd { justify-self: unsafe self-end; }
+.justifySelfSafeSelfStart { justify-self: safe self-start; }
+.justifySelfUnsafeSelfStart { justify-self: unsafe self-start; }
+.justifySelfSafeRight { justify-self: safe right; }
+.justifySelfUnsafeRight { justify-self: unsafe right; }
+.justifySelfSafeLeft { justify-self: safe left; }
+.justifySelfUnsafeLeft { justify-self: unsafe left; }
+.justifySelfSafeFlexEnd { justify-self: safe flex-end; }
+.justifySelfUnsafeFlexEnd { justify-self: unsafe flex-end; }
+.justifySelfSafeFlexStart { justify-self: safe flex-start; }
+.justifySelfUnsafeFlexStart { justify-self: unsafe flex-start; }
+
+.justifySelfBaseline { justify-self: baseline; }
+.justifySelfFirstBaseline { justify-self: first baseline; }
+.justifySelfLastBaseline { justify-self: last baseline; }
+
+/* justify-items */
+.justifyItemsAuto { justify-items: auto; }
+.justifyItemsNormal { justify-items: normal; }
+.justifyItemsStretch { justify-items: stretch; }
+.justifyItemsStart { justify-items: start; }
+.justifyItemsCenter { justify-items: center; }
+.justifyItemsEnd { justify-items: end; }
+.justifyItemsLeft { justify-items: left; }
+.justifyItemsRight { justify-items: right; }
+
+.justifyItemsFlexStart { justify-items: flex-start; }
+.justifyItemsFlexEnd { justify-items: flex-end; }
+
+.justifyItemsSelfStart { justify-items: self-start; }
+.justifyItemsSelfEnd { justify-items: self-end; }
+
+.justifyItemsLegacy { justify-items: legacy; }
+.justifyItemsLegacyLeft { justify-items: legacy left; }
+.justifyItemsLegacyCenter { justify-items: legacy center; }
+.justifyItemsLegacyRight { justify-items: legacy right; }
+.justifyItemsLeftLegacy { justify-items: left legacy; }
+.justifyItemsCenterLegacy { justify-items: center legacy; }
+.justifyItemsRightLegacy { justify-items: right legacy; }
+
+.justifyItemsSafeCenter { justify-items: safe center; }
+.justifyItemsUnsafeCenter { justify-items: unsafe center; }
+.justifyItemsSafeEnd { justify-items: safe end; }
+.justifyItemsUnsafeEnd { justify-items: unsafe end; }
+.justifyItemsSafeSelfEnd { justify-items: safe self-end; }
+.justifyItemsUnsafeSelfEnd { justify-items: unsafe self-end; }
+.justifyItemsSafeSelfStart { justify-items: safe self-start; }
+.justifyItemsUnsafeSelfStart { justify-items: unsafe self-start; }
+.justifyItemsSafeRight { justify-items: safe right; }
+.justifyItemsUnsafeRight { justify-items: unsafe right; }
+.justifyItemsSafeLeft { justify-items: safe left; }
+.justifyItemsUnsafeLeft { justify-items: unsafe left; }
+.justifyItemsSafeFlexEnd { justify-items: safe flex-end; }
+.justifyItemsUnsafeFlexEnd { justify-items: unsafe flex-end; }
+.justifyItemsSafeFlexStart { justify-items: safe flex-start; }
+.justifyItemsUnsafeFlexStart { justify-items: unsafe flex-start; }
+
+.justifyItemsTest { justify-items: safe end; }
+
+.justifyItemsBaseline { justify-items: baseline; }
+.justifyItemsFirstBaseline { justify-items: first baseline; }
+.justifyItemsLastBaseline { justify-items: last baseline; }
+
+/* justify-content */
+.justifyContentBaseline { justify-content: baseline; }
+.justifyContentLastBaseline { justify-content: last-baseline; }
+.justifyContentStart { justify-content: start; }
+.justifyContentEnd { justify-content: end; }
+.justifyContentCenter { justify-content: center; }
+.justifyContentLeft { justify-content: left; }
+.justifyContentRight { justify-content: right; }
+
+.justifyContentFlexStart { justify-content: flex-start; }
+.justifyContentFlexEnd { justify-content: flex-end; }
+
+.justifyContentSpaceBetween { justify-content: space-between; }
+.justifyContentSpaceAround { justify-content: space-around; }
+.justifyContentSpaceEvenly { justify-content: space-evenly; }
+.justifyContentStretch { justify-content: stretch; }
+
+.justifyContentSafeCenter { justify-content: safe center; }
+.justifyContentUnsafeCenter { justify-content: unsafe center; }
+.justifyContentSafeEnd { justify-content: safe end; }
+.justifyContentUnsafeEnd { justify-content: unsafe end; }
+.justifyContentSafeRight { justify-content: safe right; }
+.justifyContentUnsafeRight { justify-content: unsafe right; }
+.justifyContentSafeLeft { justify-content: safe left; }
+.justifyContentUnsafeLeft { justify-content: unsafe left; }
+.justifyContentSafeFlexEnd { justify-content: safe flex-end; }
+.justifyContentUnsafeFlexEnd { justify-content: unsafe flex-end; }
+.justifyContentSafeFlexStart { justify-content: safe flex-start; }
+.justifyContentUnsafeFlexStart { justify-content: unsafe flex-start; }
+
+.justifyContentBaseline { justify-content: baseline; }
+.justifyContentFirstBaseline { justify-content: first baseline; }
+.justifyContentLastBaseline { justify-content: last baseline; }
+
+/* Both align-items and justify-items */
+.itemsNormal {
+ align-items: normal;
+ justify-items: normal;
+}
+
+.itemsStretch {
+ align-items: stretch;
+ justify-items: stretch;
+}
+
+.itemsStart {
+ align-items: start;
+ justify-items: start;
+}
+
+.itemsCenter {
+ align-items: center;
+ justify-items: center;
+}
+
+.itemsEnd {
+ align-items: end;
+ justify-items: end;
+}
+
+.itemsLeft {
+ align-items: left;
+ justify-items: left;
+}
+
+.itemsRight {
+ align-items: right;
+ justify-items: right;
+}
+
+.itemsSelfStart {
+ align-items: self-start;
+ justify-items: self-start;
+}
+
+.itemsSelfEnd {
+ align-items: self-end;
+ justify-items: self-end;
+}
+.itemsBaseline {
+ align-items: baseline;
+ justify-items: baseline;
+}
+
+/* Both align-self and justify-self */
+.selfStretch {
+ align-self: stretch;
+ justify-self: stretch;
+}
+.selfStart {
+ align-self: start;
+ justify-self: start;
+}
+.selfEnd {
+ align-self: end;
+ justify-self: end;
+}
+.selfCenter {
+ align-self: center;
+ justify-self: center;
+}
+.selfRight {
+ align-self: right;
+ justify-self: right;
+}
+.selfLeft {
+ align-self: left;
+ justify-self: left;
+}
+.selfSelfStart {
+ align-self: self-start;
+ justify-self: self-start;
+}
+.selfSelfEnd {
+ align-self: self-end;
+ justify-self: self-end;
+}
+.selfBaseline {
+ align-self: baseline;
+ justify-self: baseline;
+}
+
+/* Both align-content and justify-content */
+.contentStart {
+ align-content: start;
+ justify-content: start;
+}
+.contentCenter {
+ align-content: center;
+ justify-content: center;
+}
+.contentEnd {
+ align-content: end;
+ justify-content: end;
+}
+
+.contentCenterSafe {
+ align-content: safe center;
+ justify-content: safe center;
+}
+
+.contentCenterUnsafe {
+ align-content: unsafe center;
+ justify-content: unsafe center;
+}
+
+.contentEndSafe {
+ align-content: safe end;
+ justify-content: safe end;
+}
+
+.contentEndUnsafe {
+ align-content: unsafe end;
+ justify-content: unsafe end;
+}
+
+.contentSpaceBetween {
+ justify-content: space-between;
+ align-content: space-between;
+}
+
+.contentSpaceAround {
+ justify-content: space-around;
+ align-content: space-around;
+}
+
+.contentSpaceEvenly {
+ justify-content: space-evenly;
+ align-content: space-evenly;
+}
+
+.contentStretch {
+ justify-content: stretch;
+ align-content: stretch;
+}
diff --git a/testing/web-platform/tests/css/support/b-green.css b/testing/web-platform/tests/css/support/b-green.css
new file mode 100644
index 0000000000..a0473f5ca2
--- /dev/null
+++ b/testing/web-platform/tests/css/support/b-green.css
@@ -0,0 +1 @@
+.b { color: green; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/support/blue-100.png b/testing/web-platform/tests/css/support/blue-100.png
new file mode 100644
index 0000000000..f578ae7253
--- /dev/null
+++ b/testing/web-platform/tests/css/support/blue-100.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/blue32x32.ico b/testing/web-platform/tests/css/support/blue32x32.ico
new file mode 100644
index 0000000000..5844dd614c
--- /dev/null
+++ b/testing/web-platform/tests/css/support/blue32x32.ico
Binary files differ
diff --git a/testing/web-platform/tests/css/support/c-red.css b/testing/web-platform/tests/css/support/c-red.css
new file mode 100644
index 0000000000..d4ba5c64e9
--- /dev/null
+++ b/testing/web-platform/tests/css/support/c-red.css
@@ -0,0 +1 @@
+.c { color: red; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/support/cat.png b/testing/web-platform/tests/css/support/cat.png
new file mode 100644
index 0000000000..85dd732481
--- /dev/null
+++ b/testing/web-platform/tests/css/support/cat.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/computed-testcommon.js b/testing/web-platform/tests/css/support/computed-testcommon.js
new file mode 100644
index 0000000000..831baf311a
--- /dev/null
+++ b/testing/web-platform/tests/css/support/computed-testcommon.js
@@ -0,0 +1,212 @@
+'use strict';
+
+/**
+ * Create test that a CSS property computes to the expected value.
+ * The document element #target is used to perform the test.
+ *
+ * @param {string} property The name of the CSS property being tested.
+ * @param {string} specified A specified value for the property.
+ * @param {string|array} computed The expected computed value,
+ * or an array of permitted computed value.
+ * If omitted, defaults to specified.
+ */
+function test_computed_value(property, specified, computed, titleExtra) {
+ if (!computed)
+ computed = specified;
+
+ test(() => {
+ const target = document.getElementById('target');
+ assert_true(property in getComputedStyle(target), property + " doesn't seem to be supported in the computed style");
+ assert_true(CSS.supports(property, specified), "'" + specified + "' is a supported value for " + property + ".");
+ target.style[property] = '';
+ target.style[property] = specified;
+
+ let readValue = getComputedStyle(target)[property];
+ if (Array.isArray(computed)) {
+ assert_in_array(readValue, computed);
+ } else {
+ if (property == "color")
+ colorValuesAlmostEqual(readValue, computed, 0.0001, 1);
+ else
+ assert_equals(readValue, computed);
+ }
+ if (readValue !== specified) {
+ target.style[property] = '';
+ target.style[property] = readValue;
+ assert_equals(getComputedStyle(target)[property], readValue,
+ 'computed value should round-trip');
+ }
+ }, `Property ${property} value '${specified}'${titleExtra ? ' ' + titleExtra : ''}`);
+}
+
+function colorValuesAlmostEqual(color1, color2, float_epsilon, integer_epsilon) {
+ // Legacy color formats use integers in [0, 255] and thus will have wider epsilons
+ const epsilon = getNonNumbers(color1).startsWith("rgb") ? integer_epsilon : float_epsilon;
+ // Colors can be split by spaces, commas or the '(' character.
+ const colorElementDividers = /( |\(|,)/;
+ // Return the string stripped of numbers.
+ function getNonNumbers(color) {
+ return color.replace(/[0-9]/g, '');
+ }
+ // Return an array of all numbers in the color.
+ function getNumbers(color) {
+ const result = [];
+ // const entries = color.split(colorElementDividers);
+ color.split(colorElementDividers).forEach(element => {
+ const numberElement = parseFloat(element);
+ if (!isNaN(numberElement)) {
+ result.push(numberElement);
+ }
+ });
+ return result;
+ }
+
+ assert_array_approx_equals(getNumbers(color1), getNumbers(color2), epsilon, "Numeric parameters are approximately equal.");
+ // Assert that the text of the two colors are equal. i.e. colorSpace, colorFunction and format.
+ assert_equals(getNonNumbers(color1), getNonNumbers(color2), "Color format is correct.");
+}
+
+function testComputedValueGreaterOrLowerThan(property, specified, expected, titleExtra) {
+ test(() => {
+ const target = document.getElementById('target');
+ assert_true(property in getComputedStyle(target), property + " doesn't seem to be supported in the computed style");
+ assert_true(CSS.supports(property, specified), "'" + specified + "' is a supported value for " + property + ".");
+ target.style[property] = '';
+ target.style[property] = specified;
+ let readValue = parseFloat(getComputedStyle(target)[property]);
+ assert_true(isFinite(readValue), specified + " expected finite value but got " + readValue)
+ assert_false(isNaN(readValue), specified + " expected finite value but got " + readValue)
+ if (expected > 0)
+ assert_greater_than_equal(readValue, expected, specified);
+ else
+ assert_less_than_equal(readValue, expected, specified);
+ }, `Property ${property} value '${specified}'${titleExtra ? ' ' + titleExtra : ''}`);
+}
+
+function testTransformValuesCloseTo(specified, epsilon, expectedValue, description)
+{
+ if(!description) {
+ description = `Property ${specified} value expected same with ${expectedValue} in +/-${epsilon}`
+ }
+
+ test(function()
+ {
+ var targetElement = document.getElementById("target");
+ targetElement.style.setProperty('transform', "initial");
+
+ /*
+ Since we are running many consecutive tests on the same
+ element, then it is necessary to reset its property
+ to an initial value before actually re-testing it.
+ */
+
+ targetElement.style.setProperty('transform', specified);
+
+ var computedCalcValue = getComputedStyle(targetElement)['transform'];
+
+ /*
+ We first strip out the word "matrix" with the
+ opening parenthesis "(" and the closing
+ parenthesis ")"
+ */
+
+ computedCalcValue = computedCalcValue.replace("matrix(", "").replace(")", "");
+
+ /*
+ Then, we split the string at each comma ","
+ and store the resulting 6 sub-strings into
+ tableSplitComputedCalcValue
+ */
+
+ var tableSplitCalcValue = computedCalcValue.split(",");
+
+ /*
+ We convert the 6 sub-strings into numerical floating values
+ so that mathematical operations (subtraction, absolute value,
+ comparison) can be performed.
+ */
+
+ tableSplitCalcValue[0] = parseFloat(tableSplitCalcValue[0]);
+ tableSplitCalcValue[1] = parseFloat(tableSplitCalcValue[1]);
+ tableSplitCalcValue[2] = parseFloat(tableSplitCalcValue[2]);
+ tableSplitCalcValue[3] = parseFloat(tableSplitCalcValue[3]);
+ tableSplitCalcValue[4] = parseFloat(tableSplitCalcValue[4]);
+ tableSplitCalcValue[5] = parseFloat(tableSplitCalcValue[5]);
+
+ /*
+ Now, we execute the same steps with the expectedValue
+ */
+
+ targetElement.style.setProperty('transform', expectedValue);
+
+ var computedExpectedValue = getComputedStyle(targetElement)['transform'];
+
+ /*
+ We first strip out the word "matrix" with the
+ opening parenthesis "(" and the closing
+ parenthesis ")"
+ */
+
+ computedExpectedValue = computedExpectedValue.replace("matrix(", "").replace(")", "");
+
+ /*
+ Then, we split the string at each comma ","
+ and store the resulting 6 sub-strings into
+ tableSplitComputedCalcValue
+ */
+
+ var tableSplitExpectedValue = computedExpectedValue.split(",");
+
+ /*
+ We convert the 6 sub-strings into numerical floating values
+ so that mathematical operations (subtraction, absolute value,
+ comparison) can be performed.
+ */
+
+ tableSplitExpectedValue[0] = parseFloat(tableSplitExpectedValue[0]);
+ tableSplitExpectedValue[1] = parseFloat(tableSplitExpectedValue[1]);
+ tableSplitExpectedValue[2] = parseFloat(tableSplitExpectedValue[2]);
+ tableSplitExpectedValue[3] = parseFloat(tableSplitExpectedValue[3]);
+ tableSplitExpectedValue[4] = parseFloat(tableSplitExpectedValue[4]);
+ tableSplitExpectedValue[5] = parseFloat(tableSplitExpectedValue[5]);
+
+ assert_array_approx_equals(tableSplitCalcValue, tableSplitExpectedValue, epsilon);
+ } , description);
+
+}
+
+function test_pseudo_computed_value(pseudo, property, specified, computed, titleExtra) {
+ if (!computed)
+ computed = specified;
+
+ test(() => {
+ assert_true(/^::\w+$/.test(pseudo), pseudo + " doesn't seem to be a pseudo-element");
+ const styleElement = document.createElement("style");
+ document.documentElement.appendChild(styleElement);
+ try {
+ const {sheet} = styleElement;
+ sheet.insertRule("#target" + pseudo + "{}");
+ const {style} = sheet.cssRules[0];
+ const target = document.getElementById('target');
+
+ assert_true(property in getComputedStyle(target, pseudo), property + " doesn't seem to be supported in the computed style");
+ assert_true(CSS.supports(property, specified), "'" + specified + "' is a supported value for " + property + ".");
+ style[property] = specified;
+
+ let readValue = getComputedStyle(target, pseudo)[property];
+ if (Array.isArray(computed)) {
+ assert_in_array(readValue, computed);
+ } else {
+ assert_equals(readValue, computed);
+ }
+ if (readValue !== specified) {
+ style[property] = '';
+ style[property] = readValue;
+ assert_equals(getComputedStyle(target, pseudo)[property], readValue,
+ 'computed value should round-trip');
+ }
+ } finally {
+ document.documentElement.removeChild(styleElement);
+ }
+ }, `Property ${property} value '${specified}' in ${pseudo}${titleExtra ? ' ' + titleExtra : ''}`);
+}
diff --git a/testing/web-platform/tests/css/support/delete-other-rule-crash.css b/testing/web-platform/tests/css/support/delete-other-rule-crash.css
new file mode 100644
index 0000000000..13f5d6acf0
--- /dev/null
+++ b/testing/web-platform/tests/css/support/delete-other-rule-crash.css
@@ -0,0 +1,2 @@
+.a {}
+.b { .c {} }
diff --git a/testing/web-platform/tests/css/support/green.ico b/testing/web-platform/tests/css/support/green.ico
new file mode 100644
index 0000000000..691f61166f
--- /dev/null
+++ b/testing/web-platform/tests/css/support/green.ico
Binary files differ
diff --git a/testing/web-platform/tests/css/support/grid.css b/testing/web-platform/tests/css/support/grid.css
new file mode 100644
index 0000000000..4007ebba43
--- /dev/null
+++ b/testing/web-platform/tests/css/support/grid.css
@@ -0,0 +1,289 @@
+.grid {
+ display: grid;
+ background-color: grey;
+}
+
+.inline-grid {
+ display: inline-grid;
+ background-color: grey;
+}
+
+.firstRowFirstColumn {
+ background-color: blue;
+ grid-column: 1;
+ grid-row: 1;
+}
+
+.onlyFirstRowOnlyFirstColumn {
+ background-color: blue;
+ grid-column: 1 / 2;
+ grid-row: 1 / 2;
+}
+
+.firstRowSecondColumn {
+ background-color: lime;
+ grid-column: 2;
+ grid-row: 1;
+}
+
+.onlyFirstRowOnlySecondColumn {
+ background-color: lime;
+ grid-column: 2 / 3;
+ grid-row: 1 / 2;
+}
+
+.firstRowThirdColumn {
+ background-color: magenta;
+ grid-column: 3;
+ grid-row: 1;
+}
+
+.firstRowFourthColumn {
+ background-color: green;
+ grid-column: 4;
+ grid-row: 1;
+}
+
+.secondRowFirstColumn {
+ background-color: purple;
+ grid-column: 1;
+ grid-row: 2;
+}
+
+.onlySecondRowOnlyFirstColumn {
+ background-color: purple;
+ grid-column: 1 / 2;
+ grid-row: 2 / 3;
+}
+
+.secondRowSecondColumn {
+ background-color: orange;
+ grid-column: 2;
+ grid-row: 2;
+}
+
+.onlySecondRowOnlySecondColumn {
+ background-color: orange;
+ grid-column: 2 / 3;
+ grid-row: 2 / 3;
+}
+
+.endSecondRowEndSecondColumn {
+ background-color: orange;
+ grid-column-end: 3;
+ grid-row-end: 3;
+}
+
+.secondRowThirdColumn {
+ background-color: navy;
+ grid-column: 3;
+ grid-row: 2;
+}
+
+.secondRowFourthColumn {
+ background-color: pink;
+ grid-column: 4;
+ grid-row: 2;
+}
+
+.thirdRowFirstColumn {
+ background-color: green;
+ grid-column: 1;
+ grid-row: 3;
+}
+
+.thirdRowSecondColumn {
+ background-color: red;
+ grid-column: 2;
+ grid-row: 3;
+}
+
+.thirdRowThirdColumn {
+ background-color: salmon;
+ grid-column: 3;
+ grid-row: 3;
+}
+
+.firstAutoRowSecondAutoColumn {
+ grid-row: 1 / auto;
+ grid-column: 2 / auto;
+}
+
+.autoLastRowAutoLastColumn {
+ grid-row: auto / -1;
+ grid-column: auto / -1;
+}
+
+.autoSecondRowAutoFirstColumn {
+ grid-row: auto / 2;
+ grid-column: auto / 1;
+}
+
+.firstRowBothColumn {
+ grid-row: 1;
+ grid-column: 1 / -1;
+}
+
+.secondRowBothColumn {
+ grid-row: 2;
+ grid-column: 1 / -1;
+}
+
+.bothRowFirstColumn {
+ grid-row: 1 / -1;
+ grid-column: 1;
+}
+
+.bothRowSecondColumn {
+ grid-row: 1 / -1;
+ grid-column: 2;
+}
+
+.bothRowBothColumn {
+ grid-row: 1 / -1;
+ grid-column: 1 / -1;
+}
+
+/* Auto column / row. */
+.autoRowAutoColumn {
+ background-color: pink;
+ grid-column: auto;
+ grid-row: auto;
+}
+
+.firstRowAutoColumn {
+ background-color: blue;
+ grid-column: auto;
+ grid-row: 1;
+}
+
+.secondRowAutoColumn {
+ background-color: purple;
+ grid-column: auto;
+ grid-row: 2;
+}
+
+.thirdRowAutoColumn {
+ background-color: navy;
+ grid-column: auto;
+ grid-row: 3;
+}
+
+.autoRowFirstColumn {
+ background-color: lime;
+ grid-column: 1;
+ grid-row: auto;
+}
+
+.autoRowSecondColumn {
+ background-color: orange;
+ grid-column: 2;
+ grid-row: auto;
+}
+
+.autoRowThirdColumn {
+ background-color: magenta;
+ grid-column: 3;
+ grid-row: auto;
+}
+
+.autoRowAutoColumnSpanning2 {
+ background-color: maroon;
+ grid-column: span 2;
+ grid-row: auto;
+}
+
+.autoRowSpanning2AutoColumn {
+ background-color: aqua;
+ grid-column: auto;
+ grid-row: span 2;
+}
+
+.autoRowSpanning2AutoColumnSpanning3 {
+ background-color: olive;
+ grid-column: span 3;
+ grid-row: span 2;
+}
+
+.autoRowSpanning3AutoColumnSpanning2 {
+ background-color: indigo;
+ grid-column: span 2;
+ grid-row: span 3;
+}
+
+.autoRowFirstColumnSpanning2 {
+ background-color: maroon;
+ grid-column: 1 / span 2;
+ grid-row: auto;
+}
+
+.autoRowSecondColumnSpanning2 {
+ background-color: olive;
+ grid-column: 2 / span 2;
+ grid-row: auto;
+}
+
+.firstRowSpanning2AutoColumn {
+ background-color: maroon;
+ grid-column: auto;
+ grid-row: 1 / span 2;
+ height: 100%;
+}
+
+.secondRowSpanning2AutoColumn {
+ background-color: olive;
+ grid-column: auto;
+ grid-row: 2 / span 2;
+ height: 100%;
+}
+
+/* Grid element flow. */
+.gridAutoFlowColumnSparse {
+ grid-auto-flow: column;
+}
+
+.gridAutoFlowColumnDense {
+ grid-auto-flow: column dense;
+}
+
+.gridAutoFlowRowSparse {
+ grid-auto-flow: row;
+}
+
+.gridAutoFlowRowDense {
+ grid-auto-flow: row dense;
+}
+
+/* This rule makes sure the container is smaller than any grid items to avoid distributing any extra logical space to them. */
+.constrainedContainer {
+ width: 10px;
+ height: 10px;
+}
+
+.unconstrainedContainer {
+ width: 1000px;
+ height: 1000px;
+}
+
+.sizedToGridArea {
+ font: 10px/1 Ahem;
+ /* Make us fit our grid area. */
+ width: 100%;
+ height: 100%;
+}
+
+.verticalRL {
+ writing-mode: vertical-rl;
+}
+.verticalLR {
+ writing-mode: vertical-lr;
+}
+.horizontalTB {
+ writing-mode: horizontal-tb;
+}
+.directionRTL {
+ direction: rtl;
+}
+.directionLTR {
+ direction: ltr;
+}
diff --git a/testing/web-platform/tests/css/support/height-keyword-classes.css b/testing/web-platform/tests/css/support/height-keyword-classes.css
new file mode 100644
index 0000000000..d26f564442
--- /dev/null
+++ b/testing/web-platform/tests/css/support/height-keyword-classes.css
@@ -0,0 +1,39 @@
+/* In the current spec for heights, min-content, max-content and fit-content are
+ * equivalent.
+ */
+
+.min-content {
+ height: min-content;
+}
+
+.max-content {
+ height: max-content;
+}
+
+.fit-content {
+ height: fit-content;
+}
+
+.max-height-min-content {
+ max-height: min-content;
+}
+
+.max-height-max-content {
+ max-height: max-content;
+}
+
+.max-height-fit-content {
+ max-height: fit-content;
+}
+
+.min-height-min-content {
+ min-height: min-content;
+}
+
+.min-height-max-content {
+ min-height: max-content;
+}
+
+.min-height-fit-content {
+ min-height: fit-content;
+}
diff --git a/testing/web-platform/tests/css/support/import-green.css b/testing/web-platform/tests/css/support/import-green.css
new file mode 100644
index 0000000000..537104e663
--- /dev/null
+++ b/testing/web-platform/tests/css/support/import-green.css
@@ -0,0 +1 @@
+.import { color: green; }
diff --git a/testing/web-platform/tests/css/support/import-red.css b/testing/web-platform/tests/css/support/import-red.css
new file mode 100644
index 0000000000..9945ef4711
--- /dev/null
+++ b/testing/web-platform/tests/css/support/import-red.css
@@ -0,0 +1 @@
+.import { color: red; }
diff --git a/testing/web-platform/tests/css/support/inheritance-testcommon.js b/testing/web-platform/tests/css/support/inheritance-testcommon.js
new file mode 100644
index 0000000000..38ac94eb12
--- /dev/null
+++ b/testing/web-platform/tests/css/support/inheritance-testcommon.js
@@ -0,0 +1,96 @@
+'use strict';
+
+(function() {
+
+function assert_initial(property, initial) {
+ let initialDesc = initial;
+ if (Array.isArray(initial))
+ initialDesc = '[' + initial.map(e => "'" + e + "'").join(' or ') + ']';
+
+ test(() => {
+ const target = document.getElementById('target');
+ assert_true(property in getComputedStyle(target), property + " doesn't seem to be supported in the computed style");
+ target.style[property] = 'initial';
+ if (Array.isArray(initial)) {
+ assert_in_array(getComputedStyle(target)[property], initial);
+ } else {
+ assert_equals(getComputedStyle(target)[property], initial);
+ }
+ target.style[property] = '';
+ }, 'Property ' + property + ' has initial value ' + initialDesc);
+}
+
+/**
+ * Create tests that a CSS property inherits and has the given initial value.
+ *
+ * The current document must have an element #target within element #container.
+ *
+ * @param {string} property The name of the CSS property being tested.
+ * @param {string|array} initial The computed value for 'initial' or a list
+ * of acceptable computed value serializations.
+ * @param {string} other An arbitrary value for the property that
+ * round trips and is distinct from the initial
+ * value.
+ */
+function assert_inherited(property, initial, other) {
+ if (initial)
+ assert_initial(property, initial);
+
+ test(() => {
+ const container = document.getElementById('container');
+ const target = document.getElementById('target');
+ assert_true(property in getComputedStyle(target), property + " doesn't seem to be supported in the computed style");
+ container.style[property] = 'initial';
+ target.style[property] = 'unset';
+ assert_not_equals(getComputedStyle(container)[property], other);
+ assert_not_equals(getComputedStyle(target)[property], other);
+ container.style[property] = other;
+ assert_equals(getComputedStyle(container)[property], other);
+ assert_equals(getComputedStyle(target)[property], other);
+ target.style[property] = 'initial';
+ assert_equals(getComputedStyle(container)[property], other);
+ assert_not_equals(getComputedStyle(target)[property], other);
+ target.style[property] = 'inherit';
+ assert_equals(getComputedStyle(target)[property], other);
+ container.style[property] = '';
+ target.style[property] = '';
+ }, 'Property ' + property + ' inherits');
+}
+
+/**
+ * Create tests that a CSS property does not inherit, and that it has the
+ * given initial value.
+ *
+ * The current document must have an element #target within element #container.
+ *
+ * @param {string} property The name of the CSS property being tested.
+ * @param {string|array} initial The computed value for 'initial' or a list
+ * of acceptable computed value serializations.
+ * @param {string} other An arbitrary value for the property that
+ * round trips and is distinct from the initial
+ * value.
+ */
+function assert_not_inherited(property, initial, other) {
+ assert_initial(property, initial);
+
+ test(() => {
+ const container = document.getElementById('container');
+ const target = document.getElementById('target');
+ assert_true(property in getComputedStyle(target));
+ container.style[property] = 'initial';
+ target.style[property] = 'unset';
+ assert_not_equals(getComputedStyle(container)[property], other);
+ assert_not_equals(getComputedStyle(target)[property], other);
+ container.style[property] = other;
+ assert_equals(getComputedStyle(container)[property], other);
+ assert_not_equals(getComputedStyle(target)[property], other);
+ target.style[property] = 'inherit';
+ assert_equals(getComputedStyle(target)[property], other);
+ container.style[property] = '';
+ target.style[property] = '';
+ }, 'Property ' + property + ' does not inherit');
+}
+
+window.assert_inherited = assert_inherited;
+window.assert_not_inherited = assert_not_inherited;
+})();
diff --git a/testing/web-platform/tests/css/support/interpolation-testcommon.js b/testing/web-platform/tests/css/support/interpolation-testcommon.js
new file mode 100644
index 0000000000..2ee00e457a
--- /dev/null
+++ b/testing/web-platform/tests/css/support/interpolation-testcommon.js
@@ -0,0 +1,467 @@
+'use strict';
+(function() {
+ var interpolationTests = [];
+ var compositionTests = [];
+ var cssAnimationsData = {
+ sharedStyle: null,
+ nextID: 0,
+ };
+ var expectNoInterpolation = {};
+ var expectNotAnimatable = {};
+ var neutralKeyframe = {};
+ function isNeutralKeyframe(keyframe) {
+ return keyframe === neutralKeyframe;
+ }
+
+ // For the CSS interpolation methods set the delay to be negative half the
+ // duration, so we are immediately at the halfway point of the animation.
+ // We then use an easing function that maps halfway to whatever progress
+ // we actually want.
+
+ var cssAnimationsInterpolation = {
+ name: 'CSS Animations',
+ isSupported: function() {return true;},
+ supportsProperty: function() {return true;},
+ supportsValue: function() {return true;},
+ setup: function() {},
+ nonInterpolationExpectations: function(from, to) {
+ return expectFlip(from, to, 0.5);
+ },
+ notAnimatableExpectations: function(from, to, underlying) {
+ return expectFlip(underlying, underlying, -Infinity);
+ },
+ interpolate: function(property, from, to, at, target) {
+ var id = cssAnimationsData.nextID++;
+ if (!cssAnimationsData.sharedStyle) {
+ cssAnimationsData.sharedStyle = createElement(document.body, 'style');
+ }
+ cssAnimationsData.sharedStyle.textContent += '' +
+ '@keyframes animation' + id + ' {' +
+ (isNeutralKeyframe(from) ? '' : `from {${property}:${from};}`) +
+ (isNeutralKeyframe(to) ? '' : `to {${property}:${to};}`) +
+ '}';
+ target.style.animationName = 'animation' + id;
+ target.style.animationDuration = '100s';
+ target.style.animationDelay = '-50s';
+ target.style.animationTimingFunction = createEasing(at);
+ },
+ };
+
+ var cssTransitionsInterpolation = {
+ name: 'CSS Transitions',
+ isSupported: function() {return true;},
+ supportsProperty: function() {return true;},
+ supportsValue: function() {return true;},
+ setup: function(property, from, target) {
+ target.style.setProperty(property, isNeutralKeyframe(from) ? '' : from);
+ },
+ nonInterpolationExpectations: function(from, to) {
+ return expectFlip(from, to, 0.5);
+ },
+ notAnimatableExpectations: function(from, to, underlying) {
+ return expectFlip(from, to, -Infinity);
+ },
+ interpolate: function(property, from, to, at, target) {
+ // Force a style recalc on target to set the 'from' value.
+ getComputedStyle(target).getPropertyValue(property);
+ target.style.transitionDuration = '100s';
+ target.style.transitionDelay = '-50s';
+ target.style.transitionTimingFunction = createEasing(at);
+ target.style.transitionProperty = property;
+ target.style.setProperty(property, isNeutralKeyframe(to) ? '' : to);
+ },
+ };
+
+ var cssTransitionAllInterpolation = {
+ name: 'CSS Transitions with transition: all',
+ isSupported: function() {return true;},
+ // The 'all' value doesn't cover custom properties.
+ supportsProperty: function(property) {return property.indexOf('--') !== 0;},
+ supportsValue: function() {return true;},
+ setup: function(property, from, target) {
+ target.style.setProperty(property, isNeutralKeyframe(from) ? '' : from);
+ },
+ nonInterpolationExpectations: function(from, to) {
+ return expectFlip(from, to, -Infinity);
+ },
+ notAnimatableExpectations: function(from, to, underlying) {
+ return expectFlip(from, to, -Infinity);
+ },
+ interpolate: function(property, from, to, at, target) {
+ // Force a style recalc on target to set the 'from' value.
+ getComputedStyle(target).getPropertyValue(property);
+ target.style.transitionDuration = '100s';
+ target.style.transitionDelay = '-50s';
+ target.style.transitionTimingFunction = createEasing(at);
+ target.style.transitionProperty = 'all';
+ target.style.setProperty(property, isNeutralKeyframe(to) ? '' : to);
+ },
+ };
+
+ var webAnimationsInterpolation = {
+ name: 'Web Animations',
+ isSupported: function() {return 'animate' in Element.prototype;},
+ supportsProperty: function(property) {return true;},
+ supportsValue: function(value) {return value !== '';},
+ setup: function() {},
+ nonInterpolationExpectations: function(from, to) {
+ return expectFlip(from, to, 0.5);
+ },
+ notAnimatableExpectations: function(from, to, underlying) {
+ return expectFlip(underlying, underlying, -Infinity);
+ },
+ interpolate: function(property, from, to, at, target) {
+ this.interpolateComposite(property, from, 'replace', to, 'replace', at, target);
+ },
+ interpolateComposite: function(property, from, fromComposite, to, toComposite, at, target) {
+ // This case turns into a test error later on.
+ if (!this.isSupported())
+ return;
+
+ // Convert standard properties to camelCase.
+ if (!property.startsWith('--')) {
+ for (var i = property.length - 2; i > 0; --i) {
+ if (property[i] === '-') {
+ property = property.substring(0, i) + property[i + 1].toUpperCase() + property.substring(i + 2);
+ }
+ }
+ if (property === 'offset') {
+ property = 'cssOffset';
+ } else if (property === 'float') {
+ property = 'cssFloat';
+ }
+ }
+ var keyframes = [];
+ if (!isNeutralKeyframe(from)) {
+ keyframes.push({
+ offset: 0,
+ composite: fromComposite,
+ [property]: from,
+ });
+ }
+ if (!isNeutralKeyframe(to)) {
+ keyframes.push({
+ offset: 1,
+ composite: toComposite,
+ [property]: to,
+ });
+ }
+ var animation = target.animate(keyframes, {
+ fill: 'forwards',
+ duration: 100 * 1000,
+ easing: createEasing(at),
+ });
+ animation.pause();
+ animation.currentTime = 50 * 1000;
+ },
+ };
+
+ function expectFlip(from, to, flipAt) {
+ return [-0.3, 0, 0.3, 0.5, 0.6, 1, 1.5].map(function(at) {
+ return {
+ at: at,
+ expect: at < flipAt ? from : to
+ };
+ });
+ }
+
+ // Constructs a timing function which produces 'y' at x = 0.5
+ function createEasing(y) {
+ if (y == 0) {
+ return 'steps(1, end)';
+ }
+ if (y == 1) {
+ return 'steps(1, start)';
+ }
+ if (y == 0.5) {
+ return 'linear';
+ }
+ // Approximate using a bezier.
+ var b = (8 * y - 1) / 6;
+ return 'cubic-bezier(0, ' + b + ', 1, ' + b + ')';
+ }
+
+ function createElement(parent, tag, text) {
+ var element = document.createElement(tag || 'div');
+ element.textContent = text || '';
+ parent.appendChild(element);
+ return element;
+ }
+
+ function createTargetContainer(parent, className) {
+ var targetContainer = createElement(parent);
+ targetContainer.classList.add('container');
+ var template = document.querySelector('#target-template');
+ if (template) {
+ targetContainer.appendChild(template.content.cloneNode(true));
+ }
+ var target = targetContainer.querySelector('.target') || targetContainer;
+ target.classList.add('target', className);
+ target.parentElement.classList.add('parent');
+ targetContainer.target = target;
+ return targetContainer;
+ }
+
+ function roundNumbers(value) {
+ return value.
+ // Round numbers to two decimal places.
+ replace(/-?\d*\.\d+(e-?\d+)?/g, function(n) {
+ return (parseFloat(n).toFixed(2)).
+ replace(/\.\d+/, function(m) {
+ return m.replace(/0+$/, '');
+ }).
+ replace(/\.$/, '').
+ replace(/^-0$/, '0');
+ });
+ }
+
+ var anchor = document.createElement('a');
+ function sanitizeUrls(value) {
+ var matches = value.match(/url\("([^#][^\)]*)"\)/g);
+ if (matches !== null) {
+ for (var i = 0; i < matches.length; ++i) {
+ var url = /url\("([^#][^\)]*)"\)/g.exec(matches[i])[1];
+ anchor.href = url;
+ anchor.pathname = '...' + anchor.pathname.substring(anchor.pathname.lastIndexOf('/'));
+ value = value.replace(matches[i], 'url(' + anchor.href + ')');
+ }
+ }
+ return value;
+ }
+
+ function normalizeValue(value) {
+ return roundNumbers(sanitizeUrls(value)).
+ // Place whitespace between tokens.
+ replace(/([\w\d.]+|[^\s])/g, '$1 ').
+ replace(/\s+/g, ' ');
+ }
+
+ function stringify(text) {
+ if (!text.includes("'")) {
+ return `'${text}'`;
+ }
+ return `"${text.replace('"', '\\"')}"`;
+ }
+
+ function keyframeText(keyframe) {
+ return isNeutralKeyframe(keyframe) ? 'neutral' : `[${keyframe}]`;
+ }
+
+ function keyframeCode(keyframe) {
+ return isNeutralKeyframe(keyframe) ? 'neutralKeyframe' : `${stringify(keyframe)}`;
+ }
+
+ function createInterpolationTestTargets(interpolationMethod, interpolationMethodContainer, interpolationTest) {
+ var property = interpolationTest.options.property;
+ var from = interpolationTest.options.from;
+ var to = interpolationTest.options.to;
+ var comparisonFunction = interpolationTest.options.comparisonFunction;
+
+ if ((interpolationTest.options.method && interpolationTest.options.method != interpolationMethod.name)
+ || !interpolationMethod.supportsProperty(property)
+ || !interpolationMethod.supportsValue(from)
+ || !interpolationMethod.supportsValue(to)) {
+ return;
+ }
+
+ var testText = `${interpolationMethod.name}: property <${property}> from ${keyframeText(from)} to ${keyframeText(to)}`;
+ var testContainer = createElement(interpolationMethodContainer, 'div');
+ createElement(testContainer);
+ var expectations = interpolationTest.expectations;
+ var applyUnderlying = false;
+ if (expectations === expectNoInterpolation) {
+ expectations = interpolationMethod.nonInterpolationExpectations(from, to);
+ } else if (expectations === expectNotAnimatable) {
+ expectations = interpolationMethod.notAnimatableExpectations(from, to, interpolationTest.options.underlying);
+ applyUnderlying = true;
+ } else if (interpolationTest.options[interpolationMethod.name]) {
+ expectations = interpolationTest.options[interpolationMethod.name];
+ }
+
+ // Setup a standard equality function if an override is not provided.
+ if (!comparisonFunction) {
+ comparisonFunction = (actual, expected) => {
+ assert_equals(normalizeValue(actual), normalizeValue(expected));
+ };
+ }
+
+ return expectations.map(function(expectation) {
+ var actualTargetContainer = createTargetContainer(testContainer, 'actual');
+ var expectedTargetContainer = createTargetContainer(testContainer, 'expected');
+ var expectedProperties = expectation.option || expectation.expect;
+ if (typeof expectedProperties !== "object") {
+ expectedProperties = {[property]: expectedProperties};
+ }
+ var target = actualTargetContainer.target;
+ if (applyUnderlying) {
+ let underlying = interpolationTest.options.underlying;
+ assert_true(typeof underlying !== 'undefined', '\'underlying\' value must be provided');
+ assert_true(CSS.supports(property, underlying), '\'underlying\' value must be supported');
+ target.style.setProperty(property, underlying);
+ }
+ interpolationMethod.setup(property, from, target);
+ target.interpolate = function() {
+ interpolationMethod.interpolate(property, from, to, expectation.at, target);
+ };
+ target.measure = function() {
+ for (var [expectedProp, expectedStr] of Object.entries(expectedProperties)) {
+ if (!isNeutralKeyframe(expectedStr)) {
+ expectedTargetContainer.target.style.setProperty(expectedProp, expectedStr);
+ }
+ var expectedValue = getComputedStyle(expectedTargetContainer.target).getPropertyValue(expectedProp);
+ let testName = `${testText} at (${expectation.at}) should be [${sanitizeUrls(expectedStr)}]`;
+ if (property !== expectedProp) {
+ testName += ` for <${expectedProp}>`;
+ }
+ test(function() {
+ assert_true(interpolationMethod.isSupported(), `${interpolationMethod.name} should be supported`);
+
+ if (from && from !== neutralKeyframe) {
+ assert_true(CSS.supports(property, from), '\'from\' value should be supported');
+ }
+ if (to && to !== neutralKeyframe) {
+ assert_true(CSS.supports(property, to), '\'to\' value should be supported');
+ }
+ if (typeof underlying !== 'undefined') {
+ assert_true(CSS.supports(property, underlying), '\'underlying\' value should be supported');
+ }
+
+ comparisonFunction(
+ getComputedStyle(target).getPropertyValue(expectedProp),
+ expectedValue);
+ }, testName);
+ }
+ };
+ return target;
+ });
+ }
+
+ function createCompositionTestTargets(compositionContainer, compositionTest) {
+ var options = compositionTest.options;
+ var property = options.property;
+ var underlying = options.underlying;
+ var comparisonFunction = options.comparisonFunction;
+ var from = options.accumulateFrom || options.addFrom || options.replaceFrom;
+ var to = options.accumulateTo || options.addTo || options.replaceTo;
+ var fromComposite = 'accumulateFrom' in options ? 'accumulate' : 'addFrom' in options ? 'add' : 'replace';
+ var toComposite = 'accumulateTo' in options ? 'accumulate' : 'addTo' in options ? 'add' : 'replace';
+ const invalidFrom = 'addFrom' in options === 'replaceFrom' in options
+ && 'addFrom' in options === 'accumulateFrom' in options;
+ const invalidTo = 'addTo' in options === 'replaceTo' in options
+ && 'addTo' in options === 'accumulateTo' in options;
+ if (invalidFrom || invalidTo) {
+ test(function() {
+ assert_false(invalidFrom, 'Exactly one of accumulateFrom, addFrom, or replaceFrom must be specified');
+ assert_false(invalidTo, 'Exactly one of accumulateTo, addTo, or replaceTo must be specified');
+ }, `Composition tests must have valid setup`);
+ }
+
+ var testText = `Compositing: property <${property}> underlying [${underlying}] from ${fromComposite} [${from}] to ${toComposite} [${to}]`;
+ var testContainer = createElement(compositionContainer, 'div');
+ createElement(testContainer);
+
+ // Setup a standard equality function if an override is not provided.
+ if (!comparisonFunction) {
+ comparisonFunction = (actual, expected) => {
+ assert_equals(normalizeValue(actual), normalizeValue(expected));
+ };
+ }
+
+ return compositionTest.expectations.map(function(expectation) {
+ var actualTargetContainer = createTargetContainer(testContainer, 'actual');
+ var expectedTargetContainer = createTargetContainer(testContainer, 'expected');
+ var expectedStr = expectation.option || expectation.expect;
+ if (!isNeutralKeyframe(expectedStr)) {
+ expectedTargetContainer.target.style.setProperty(property, expectedStr);
+ }
+ var target = actualTargetContainer.target;
+ target.style.setProperty(property, underlying);
+ target.interpolate = function() {
+ webAnimationsInterpolation.interpolateComposite(property, from, fromComposite, to, toComposite, expectation.at, target);
+ };
+ target.measure = function() {
+ var expectedValue = getComputedStyle(expectedTargetContainer.target).getPropertyValue(property);
+ test(function() {
+
+ if (from && from !== neutralKeyframe) {
+ assert_true(CSS.supports(property, from), '\'from\' value should be supported');
+ }
+ if (to && to !== neutralKeyframe) {
+ assert_true(CSS.supports(property, to), '\'to\' value should be supported');
+ }
+ if (typeof underlying !== 'undefined') {
+ assert_true(CSS.supports(property, underlying), '\'underlying\' value should be supported');
+ }
+
+ comparisonFunction(
+ getComputedStyle(target).getPropertyValue(property),
+ expectedValue);
+ }, `${testText} at (${expectation.at}) should be [${sanitizeUrls(expectedStr)}]`);
+ };
+ return target;
+ });
+ }
+
+
+
+ function createTestTargets(interpolationMethods, interpolationTests, compositionTests, container) {
+ var targets = [];
+ for (var interpolationMethod of interpolationMethods) {
+ var interpolationMethodContainer = createElement(container);
+ for (var interpolationTest of interpolationTests) {
+ if(!interpolationTest.options.target_names ||
+ interpolationTest.options.target_names.includes(interpolationMethod.name)) {
+ [].push.apply(targets, createInterpolationTestTargets(interpolationMethod, interpolationMethodContainer, interpolationTest));
+ }
+ }
+ }
+ var compositionContainer = createElement(container);
+ for (var compositionTest of compositionTests) {
+ [].push.apply(targets, createCompositionTestTargets(compositionContainer, compositionTest));
+ }
+ return targets;
+ }
+
+ function test_no_interpolation(options) {
+ test_interpolation(options, expectNoInterpolation);
+ }
+ function test_not_animatable(options) {
+ test_interpolation(options, expectNotAnimatable);
+ }
+ function create_tests() {
+ var interpolationMethods = [
+ cssTransitionsInterpolation,
+ cssTransitionAllInterpolation,
+ cssAnimationsInterpolation,
+ webAnimationsInterpolation,
+ ];
+ var container = createElement(document.body);
+ var targets = createTestTargets(interpolationMethods, interpolationTests, compositionTests, container);
+ // Separate interpolation and measurement into different phases to avoid O(n^2) of the number of targets.
+ for (var target of targets) {
+ target.interpolate();
+ }
+ for (var target of targets) {
+ target.measure();
+ }
+ container.remove();
+ }
+
+ function test_interpolation(options, expectations) {
+ interpolationTests.push({options, expectations});
+ create_tests();
+ interpolationTests = [];
+ }
+ function test_composition(options, expectations) {
+ compositionTests.push({options, expectations});
+ create_tests();
+ compositionTests = [];
+ }
+ window.test_interpolation = test_interpolation;
+ window.test_no_interpolation = test_no_interpolation;
+ window.test_not_animatable = test_not_animatable;
+ window.test_composition = test_composition;
+ window.neutralKeyframe = neutralKeyframe;
+ window.roundNumbers = roundNumbers;
+ window.normalizeValue = normalizeValue;
+})();
diff --git a/testing/web-platform/tests/css/support/numeric-testcommon.js b/testing/web-platform/tests/css/support/numeric-testcommon.js
new file mode 100644
index 0000000000..3a39e2a973
--- /dev/null
+++ b/testing/web-platform/tests/css/support/numeric-testcommon.js
@@ -0,0 +1,196 @@
+'use strict';
+
+/*
+Provides functions to help test that two numeric values are equivalent.
+These *do not* rely on you predicting what one value will serialize to;
+instead, they set and serialize *both* values,
+and just ensure that they serialize to the same thing.
+
+They rely on a #target element existing in the document,
+as this might rely on layout to resolve styles,
+and so it needs to be in the document.
+
+Three main functions are defined, with the same signatures:
+test_math_used() (for testing used values),
+test_math_computed() (for testing computed values),
+and test_math_specified() (for testing specified values).
+Signature for all is:
+
+test_math_X(
+ testString, // A numeric value; required.
+ expectedString, // A hopefully-equivalent numeric value; required.
+ { // all of these are optional
+ type, // "number", "length", etc. See impl for full list. Defaults to "length".
+ msg, // The message to display for the test; autogenned if not provided.
+ msgExtra, // Extra info to put after the auto-genned message.
+ prop, // If you want to override the automatic choice of tested property.
+ extraStyle, // Styles that need to be set at the same time to properly test the value.
+ }
+);
+
+
+Additionally, five specialized functions are provided
+to test that a given value is ±∞, ±0, or NaN:
+
+* test_plus_infinity(testString)
+* test_minus_infinity(testString)
+* test_plus_zero(testString)
+* test_minus_zero(testString)
+* test_nan(testString)
+
+*/
+
+
+
+function test_math_used(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) {
+ if(type === undefined) type = "length";
+ if(!prop) {
+ switch(type) {
+ case "number": prop = "transform"; prefix="scale("; suffix=")"; break;
+ case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
+ case "length": prop = "margin-left"; break;
+ case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break;
+ case "time": prop = "transition-delay"; break;
+ case "resolution": prop = "image-resolution"; break;
+ case "flex": prop = "grid-template-rows"; break;
+ default: throw Exception(`Value type '${type}' isn't capable of math.`);
+ }
+
+ }
+ _test_math({stage:'used', testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle});
+}
+
+function test_math_computed(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) {
+ if(type === undefined) type = "length";
+ if(!prop) {
+ switch(type) {
+ case "number": prop = "transform"; prefix="scale("; suffix=")"; break;
+ case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
+ case "length": prop = "flex-basis"; break;
+ case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break;
+ case "time": prop = "transition-delay"; break;
+ case "resolution": prop = "image-resolution"; break;
+ case "flex": prop = "grid-template-rows"; break;
+ default: throw Exception(`Value type '${type}' isn't capable of math.`);
+ }
+
+ }
+ _test_math({stage:'computed', testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle});
+}
+
+function test_math_specified(testString, expectedString, {approx, msg, msgExtra, type, prop, prefix, suffix, extraStyle={}}={}) {
+ if(type === undefined) type = "length";
+ const stage = "specified";
+ if(!prop) {
+ switch(type) {
+ case "number": prop = "transform"; prefix="scale("; suffix=")"; break;
+ case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
+ case "length": prop = "flex-basis"; break;
+ case "angle": prop = "transform"; prefix="rotate("; suffix=")"; break;
+ case "time": prop = "transition-delay"; break;
+ case "resolution": prop = "image-resolution"; break;
+ case "flex": prop = "grid-template-rows"; break;
+ default: throw Exception(`Value type '${type}' isn't capable of math.`);
+ }
+
+ }
+ // Find the test element
+ const testEl = document.getElementById('target');
+ if(testEl == null) throw "Couldn't find #target element to run tests on.";
+ // Then reset its styles
+ testEl.style = "";
+ for(const p in extraStyle) {
+ testEl.style[p] = extraStyle[p];
+ }
+ if(!msg) {
+ msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`;
+ if(msgExtra) msg += "; " + msgExtra;
+ }
+ let t = testString;
+ let e = expectedString;
+ if(prefix) {
+ t = prefix + t;
+ e = prefix + e;
+ }
+ if(suffix) {
+ t += suffix;
+ e += suffix;
+ }
+ test(()=>{
+ testEl.style[prop] = '';
+ testEl.style[prop] = t;
+ const usedValue = testEl.style[prop];
+ assert_not_equals(usedValue, '', `${testString} isn't valid in '${prop}'; got the default value instead.`);
+ testEl.style[prop] = '';
+ testEl.style[prop] = e;
+ const expectedValue = testEl.style[prop];
+ assert_not_equals(expectedValue, '', `${expectedString} isn't valid in '${prop}'; got the default value instead.`)
+ assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`);
+ }, msg);
+}
+
+/*
+All of these expect the testString to evaluate to a <number>.
+*/
+function test_plus_infinity(testString) {
+ test_math_used(testString, "calc(infinity)", {type:"number"});
+}
+function test_minus_infinity(testString) {
+ test_math_used(testString, "calc(-infinity)", {type:"number"});
+}
+function test_plus_zero(testString) {
+ test_math_used(`calc(1 / ${testString})`, "calc(infinity)", {type:"number"});
+}
+function test_minus_zero(testString) {
+ test_math_used(`calc(1 / ${testString})`, "calc(-infinity)", {type:"number"});
+}
+function test_nan(testString) {
+ // Make sure that it's NaN, not an infinity,
+ // by making sure that it's the same value both pos and neg.
+ test_math_used(testString, "calc(NaN)", {type:"number"});
+ test_math_used(`calc(-1 * ${testString})`, "calc(NaN)", {type:"number"});
+}
+
+
+function _test_math({stage, testEl, testString, expectedString, type, approx, msg, msgExtra, prop, prefix, suffix, extraStyle}={}) {
+ // Find the test element
+ if(!testEl) testEl = document.getElementById('target');
+ if(testEl == null) throw "Couldn't find #target element to run tests on.";
+ // Then reset its styles
+ testEl.style = "";
+ for(const p in extraStyle) {
+ testEl.style[p] = extraStyle[p];
+ }
+ if(!msg) {
+ msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`;
+ if(msgExtra) msg += "; " + msgExtra;
+ }
+ let t = testString;
+ let e = expectedString;
+ if(prefix) {
+ t = prefix + t;
+ e = prefix + e;
+ }
+ if(suffix) {
+ t += suffix;
+ e += suffix;
+ }
+ test(()=>{
+ testEl.style[prop] = '';
+ const defaultValue = getComputedStyle(testEl)[prop];
+ testEl.style[prop] = t;
+ const usedValue = getComputedStyle(testEl)[prop];
+ assert_not_equals(usedValue, defaultValue, `${testString} isn't valid in '${prop}'; got the default value instead.`);
+ testEl.style[prop] = '';
+ testEl.style[prop] = e;
+ const expectedValue = getComputedStyle(testEl)[prop];
+ assert_not_equals(expectedValue, defaultValue, `${expectedString} isn't valid in '${prop}'; got the default value instead.`)
+ if(approx && (type == "number" || type == "angle")){
+ let parsedUsed = usedValue.split('(')[1].split(')')[0].split(',').map(parseFloat);
+ let parsedExpected = expectedValue.split('(')[1].split(')')[0].split(',').map(parseFloat);
+ assert_array_approx_equals(parsedUsed, parsedExpected, approx, `${testString} and ${expectedString} ${approx} serialize to the same thing in ${stage} values.`);
+ } else {
+ assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`);
+ }
+ }, msg);
+}
diff --git a/testing/web-platform/tests/css/support/parsing-testcommon.js b/testing/web-platform/tests/css/support/parsing-testcommon.js
new file mode 100644
index 0000000000..cc040be511
--- /dev/null
+++ b/testing/web-platform/tests/css/support/parsing-testcommon.js
@@ -0,0 +1,166 @@
+'use strict';
+
+// serializedValue can be the expected serialization of value,
+// or an array of permitted serializations,
+// or omitted if value should serialize as value.
+function test_valid_value(property, value, serializedValue) {
+ if (arguments.length < 3)
+ serializedValue = value;
+
+ var stringifiedValue = JSON.stringify(value);
+
+ test(function(){
+ var div = document.getElementById('target') || document.createElement('div');
+ div.style[property] = "";
+ div.style[property] = value;
+ var readValue = div.style.getPropertyValue(property);
+ assert_not_equals(readValue, "", "property should be set");
+ if (Array.isArray(serializedValue))
+ assert_in_array(readValue, serializedValue, "serialization should be sound");
+ else
+ assert_equals(readValue, serializedValue, "serialization should be canonical");
+
+ div.style[property] = readValue;
+ assert_equals(div.style.getPropertyValue(property), readValue, "serialization should round-trip");
+
+ }, "e.style['" + property + "'] = " + stringifiedValue + " should set the property value");
+}
+
+function test_invalid_value(property, value) {
+ var stringifiedValue = JSON.stringify(value);
+
+ test(function(){
+ var div = document.getElementById('target') || document.createElement('div');
+ div.style[property] = "";
+ div.style[property] = value;
+ assert_equals(div.style.getPropertyValue(property), "");
+ }, "e.style['" + property + "'] = " + stringifiedValue + " should not set the property value");
+}
+
+// serializedSelector can be the expected serialization of selector,
+// or an array of permitted serializations,
+// or omitted if value should serialize as selector.
+function test_valid_selector(selector, serializedSelector) {
+ if (arguments.length < 2)
+ serializedSelector = selector;
+
+ const stringifiedSelector = JSON.stringify(selector);
+
+ test(function(){
+ document.querySelector(selector);
+ assert_true(true, stringifiedSelector + " should not throw in querySelector");
+
+ const style = document.createElement("style");
+ document.head.append(style);
+ const {sheet} = style;
+ document.head.removeChild(style);
+ const {cssRules} = sheet;
+
+ assert_equals(cssRules.length, 0, "Sheet should have no rule");
+ sheet.insertRule(selector + "{}");
+ assert_equals(cssRules.length, 1, "Sheet should have 1 rule");
+
+ const readSelector = cssRules[0].selectorText;
+ if (Array.isArray(serializedSelector))
+ assert_in_array(readSelector, serializedSelector, "serialization should be sound");
+ else
+ assert_equals(readSelector, serializedSelector, "serialization should be canonical");
+
+ sheet.deleteRule(0);
+ assert_equals(cssRules.length, 0, "Sheet should have no rule");
+ sheet.insertRule(readSelector + "{}");
+ assert_equals(cssRules.length, 1, "Sheet should have 1 rule");
+
+ assert_equals(cssRules[0].selectorText, readSelector, "serialization should round-trip");
+ }, stringifiedSelector + " should be a valid selector");
+}
+
+function test_invalid_selector(selector) {
+ const stringifiedSelector = JSON.stringify(selector);
+
+ test(function(){
+ assert_throws_dom(
+ DOMException.SYNTAX_ERR,
+ () => document.querySelector(selector),
+ stringifiedSelector + " should throw in querySelector");
+
+ const style = document.createElement("style");
+ document.head.append(style);
+ const {sheet} = style;
+ document.head.removeChild(style);
+
+ assert_throws_dom(
+ DOMException.SYNTAX_ERR,
+ () => sheet.insertRule(selector + "{}"),
+ stringifiedSelector + " should throw in insertRule");
+ }, stringifiedSelector + " should be an invalid selector");
+}
+
+// serialized can be the expected serialization of rule, or an array of
+// permitted serializations, or omitted if value should serialize as rule.
+function test_valid_rule(rule, serialized) {
+ if (serialized === undefined)
+ serialized = rule;
+
+ test(function(){
+ const style = document.createElement("style");
+ document.head.append(style);
+ const {sheet} = style;
+ document.head.removeChild(style);
+ const {cssRules} = sheet;
+
+ assert_equals(cssRules.length, 0, "Sheet should have no rules");
+ sheet.insertRule(rule);
+ assert_equals(cssRules.length, 1, "Sheet should have 1 rule");
+
+ const serialization = cssRules[0].cssText;
+ if (Array.isArray(serialized))
+ assert_in_array(serialization, serialized, "serialization should be sound");
+ else
+ assert_equals(serialization, serialized, "serialization should be canonical");
+
+ sheet.deleteRule(0);
+ assert_equals(cssRules.length, 0, "Sheet should have no rule");
+ sheet.insertRule(serialization);
+ assert_equals(cssRules.length, 1, "Sheet should have 1 rule");
+
+ assert_equals(cssRules[0].cssText, serialization, "serialization should round-trip");
+ }, rule + " should be a valid rule");
+}
+
+function test_invalid_rule(rule) {
+ test(function(){
+ const style = document.createElement("style");
+ document.head.append(style);
+ const {sheet} = style;
+ document.head.removeChild(style);
+
+ assert_throws_dom(
+ DOMException.SYNTAX_ERR,
+ () => sheet.insertRule(rule),
+ rule + " should throw in insertRule");
+ }, rule + " should be an invalid rule");
+}
+
+function _set_style(rule) {
+ const style = document.createElement('style');
+ style.innerText = rule;
+ document.head.append(style);
+ const { sheet } = style;
+ document.head.removeChild(style);
+ return sheet;
+}
+
+function test_keyframes_name_valid(keyframes_name) {
+ test(t => {
+ const sheet = _set_style(`@keyframes ${keyframes_name} {}`);
+ assert_equals(sheet.cssRules.length, 1);
+ }, `valid: @keyframes ${keyframes_name} { }`);
+}
+
+function test_keyframes_name_invalid(keyframes_name) {
+ test(t => {
+ const sheet = _set_style(`@keyframes ${keyframes_name} {}`);
+ assert_equals(sheet.cssRules.length, 0);
+ }, `invalid: @keyframes ${keyframes_name} { }`);
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/css/support/pattern-grg-rgr-grg.png b/testing/web-platform/tests/css/support/pattern-grg-rgr-grg.png
new file mode 100644
index 0000000000..9b88fbd811
--- /dev/null
+++ b/testing/web-platform/tests/css/support/pattern-grg-rgr-grg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/pattern-grg-rrg-rgg.png b/testing/web-platform/tests/css/support/pattern-grg-rrg-rgg.png
new file mode 100644
index 0000000000..fcf4f3fd7d
--- /dev/null
+++ b/testing/web-platform/tests/css/support/pattern-grg-rrg-rgg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/pattern-rgr-grg-rgr.png b/testing/web-platform/tests/css/support/pattern-rgr-grg-rgr.png
new file mode 100644
index 0000000000..d454e3a630
--- /dev/null
+++ b/testing/web-platform/tests/css/support/pattern-rgr-grg-rgr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/pattern-tr.png b/testing/web-platform/tests/css/support/pattern-tr.png
new file mode 100644
index 0000000000..8b4b25364e
--- /dev/null
+++ b/testing/web-platform/tests/css/support/pattern-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/query-testcommon.js b/testing/web-platform/tests/css/support/query-testcommon.js
new file mode 100644
index 0000000000..73246e17e8
--- /dev/null
+++ b/testing/web-platform/tests/css/support/query-testcommon.js
@@ -0,0 +1,18 @@
+'use strict';
+
+function test_query_selector(parentNode, selector, expected) {
+ if (!Array.isArray(expected))
+ expected = [ expected ];
+
+ test(function(){
+ const elementList = parentNode.querySelectorAll(selector);
+ assert_equals(elementList.length, expected.length);
+
+ for (let i = 0; i < elementList.length; ++i) {
+ if (typeof expected[i] === 'string')
+ assert_equals(elementList[i].id, expected[i]);
+ else
+ assert_equals(elementList[i], expected[i]);
+ }
+ }, "Selector '" + selector + '" should find the expected elements');
+}
diff --git a/testing/web-platform/tests/css/support/red-rect.svg b/testing/web-platform/tests/css/support/red-rect.svg
new file mode 100644
index 0000000000..3fe10a2b6f
--- /dev/null
+++ b/testing/web-platform/tests/css/support/red-rect.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
+ <rect width="200" height="100" style="fill:red;" />
+</svg>
diff --git a/testing/web-platform/tests/css/support/red.ico b/testing/web-platform/tests/css/support/red.ico
new file mode 100644
index 0000000000..22a7b36fe3
--- /dev/null
+++ b/testing/web-platform/tests/css/support/red.ico
Binary files differ
diff --git a/testing/web-platform/tests/css/support/ruler-h-50%.png b/testing/web-platform/tests/css/support/ruler-h-50%.png
new file mode 100644
index 0000000000..cf2eea6b43
--- /dev/null
+++ b/testing/web-platform/tests/css/support/ruler-h-50%.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/ruler-h-50px.png b/testing/web-platform/tests/css/support/ruler-h-50px.png
new file mode 100644
index 0000000000..9f46583665
--- /dev/null
+++ b/testing/web-platform/tests/css/support/ruler-h-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/ruler-v-100px.png b/testing/web-platform/tests/css/support/ruler-v-100px.png
new file mode 100644
index 0000000000..a837eca222
--- /dev/null
+++ b/testing/web-platform/tests/css/support/ruler-v-100px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/ruler-v-50px.png b/testing/web-platform/tests/css/support/ruler-v-50px.png
new file mode 100644
index 0000000000..8414102802
--- /dev/null
+++ b/testing/web-platform/tests/css/support/ruler-v-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/serialize-testcommon.js b/testing/web-platform/tests/css/support/serialize-testcommon.js
new file mode 100644
index 0000000000..18cebf538e
--- /dev/null
+++ b/testing/web-platform/tests/css/support/serialize-testcommon.js
@@ -0,0 +1,94 @@
+"use strict";
+
+
+/* Functions to test serialization of properties.
+
+Each takes (property, testString, expectedSerialization) arguments.
+
+These functions depend on a #target element existing in the page,
+and will error if they don't find one.
+
+Note that test_computed_serialization and test_used_serialization
+are identical except for assertion messages;
+you need to choose properties with the correct resolved values
+to test the value stage that you want.
+
+
+For ease of use, it's recommended that you define and use
+the following function in your test page:
+
+function test_serialization(t,s,c,u, {prop}={}) {
+ test_specified_serialization(prop || 'text-indent', t, s);
+ test_computed_serialization(prop || 'text-indent', t, c);
+ if(u) test_used_serialization(prop || 'margin-left', t, u);
+}
+
+(swapping the property names for what you're expecting to test)
+
+Then you can write tests easily as:
+
+test_serialization(
+ 'calc(min(1%, 2%) + max(3%, 4%) + 10%)', // test string
+ 'calc(15%)', // expected specified value
+ '15%', // expected computed value
+ '15px'); // expected used value
+
+*/
+
+
+
+
+function test_specified_serialization(prop, t, e) {
+ const el = document.querySelector("#target");
+ if(!el) throw new Exception("Couldn't find #target element to run tests on.");
+ test(()=>{
+ el.style[prop] = '';
+ el.style[prop] = t;
+ const tValue = el.style[prop];
+ assert_not_equals(tValue, '', `'${t}' should be valid in ${prop}.`);
+
+ el.style[prop] = '';
+ el.style[prop] = e;
+ const eValue = el.style[prop];
+ assert_not_equals(eValue, '', `'${e}' should be valid in ${prop}.`);
+ assert_equals(eValue, e, `'${e}' should round-trip exactly in specified values.`);
+
+ assert_equals(tValue, e, `'${t}' and '${e}' should serialize the same in specified values.`);
+ }, `'${t}' as a specified value should serialize as '${e}'.`);
+}
+function test_computed_serialization(prop, t, e) {
+ const el = document.querySelector("#target");
+ if(!el) throw new Exception("Couldn't find #target element to run tests on.");
+ test(()=>{
+ el.style[prop] = '';
+ el.style[prop] = t;
+ const tValue = getComputedStyle(el)[prop];
+ assert_not_equals(tValue, '', `'${t}' should be valid in ${prop}.`);
+
+ el.style[prop] = '';
+ el.style[prop] = e;
+ const eValue = getComputedStyle(el)[prop];
+ assert_not_equals(eValue, '', `'${e}' should be valid in ${prop}.`);
+ assert_equals(eValue, e, `'${e}' should round-trip exactly in computed values.`);
+
+ assert_equals(tValue, e, `'${t}' and '${e}' should serialize the same in computed values.`);
+ }, `'${t}' as a computed value should serialize as '${e}'.`);
+}
+function test_used_serialization(prop, t, e) {
+ const el = document.querySelector("#target");
+ if(!el) throw new Exception("Couldn't find #target element to run tests on.");
+ test(()=>{
+ el.style[prop] = '';
+ el.style[prop] = t;
+ const tValue = getComputedStyle(el)[prop];
+ assert_not_equals(tValue, '', `'${t}' should be valid in ${prop}.`);
+
+ el.style[prop] = '';
+ el.style[prop] = e;
+ const eValue = getComputedStyle(el)[prop];
+ assert_not_equals(eValue, '', `'${e}' should be valid in ${prop}.`);
+ assert_equals(eValue, e, `'${e}' should round-trip exactly in used values.`);
+
+ assert_equals(tValue, e, `'${t}' and '${e}' should serialize the same in used values.`);
+ }, `'${t}' as a used value should serialize as '${e}'.`);
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/css/support/shorthand-testcommon.js b/testing/web-platform/tests/css/support/shorthand-testcommon.js
new file mode 100644
index 0000000000..ab1f3794c8
--- /dev/null
+++ b/testing/web-platform/tests/css/support/shorthand-testcommon.js
@@ -0,0 +1,40 @@
+'use strict';
+
+function test_shorthand_value(property, value, longhands) {
+ const stringifiedValue = JSON.stringify(value);
+
+ for (let longhand of Object.keys(longhands).sort()) {
+ test(function(){
+ var div = document.getElementById('target') || document.createElement('div');
+ div.style[property] = "";
+ try {
+ div.style[property] = value;
+
+ const readValue = div.style[longhand];
+ assert_equals(readValue, longhands[longhand], longhand + " should be canonical");
+
+ div.style[longhand] = "";
+ div.style[longhand] = readValue;
+ assert_equals(div.style[longhand], readValue, "serialization should round-trip");
+ } finally {
+ div.style[property] = "";
+ }
+ }, "e.style['" + property + "'] = " + stringifiedValue + " should set " + longhand);
+ }
+
+ test(function(){
+ var div = document.getElementById('target') || document.createElement('div');
+ div.style[property] = "";
+ try {
+ const expectedLength = div.style.length;
+ div.style[property] = value;
+ assert_true(CSS.supports(property, value));
+ for (let longhand of Object.keys(longhands).sort()) {
+ div.style[longhand] = "";
+ }
+ assert_equals(div.style.length, expectedLength);
+ } finally {
+ div.style[property] = "";
+ }
+ }, "e.style['" + property + "'] = " + stringifiedValue + " should not set unrelated longhands");
+}
diff --git a/testing/web-platform/tests/css/support/square-purple.png b/testing/web-platform/tests/css/support/square-purple.png
new file mode 100644
index 0000000000..0f522d7872
--- /dev/null
+++ b/testing/web-platform/tests/css/support/square-purple.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/square-teal.png b/testing/web-platform/tests/css/support/square-teal.png
new file mode 100644
index 0000000000..e567f51b91
--- /dev/null
+++ b/testing/web-platform/tests/css/support/square-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/square-white.png b/testing/web-platform/tests/css/support/square-white.png
new file mode 100644
index 0000000000..5853cbb238
--- /dev/null
+++ b/testing/web-platform/tests/css/support/square-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/support/README b/testing/web-platform/tests/css/support/support/README
new file mode 100644
index 0000000000..ea8cb9ef35
--- /dev/null
+++ b/testing/web-platform/tests/css/support/support/README
@@ -0,0 +1,4 @@
+The swatch-green.png file in this directory is really a RED swatch,
+and the swatch-red.png file is really a green swatch.
+
+This directory is used to test relative URIs. \ No newline at end of file
diff --git a/testing/web-platform/tests/css/support/support/swatch-green.png b/testing/web-platform/tests/css/support/support/swatch-green.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/support/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/support/swatch-red.png b/testing/web-platform/tests/css/support/support/swatch-red.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/support/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-blue.png b/testing/web-platform/tests/css/support/swatch-blue.png
new file mode 100644
index 0000000000..bf2759634d
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-blue.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-green.png b/testing/web-platform/tests/css/support/swatch-green.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-lime.png b/testing/web-platform/tests/css/support/swatch-lime.png
new file mode 100644
index 0000000000..55fd7fdaed
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-orange.png b/testing/web-platform/tests/css/support/swatch-orange.png
new file mode 100644
index 0000000000..d3cd498b52
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-orange.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-red.png b/testing/web-platform/tests/css/support/swatch-red.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-teal.png b/testing/web-platform/tests/css/support/swatch-teal.png
new file mode 100644
index 0000000000..0293ce89de
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-white.png b/testing/web-platform/tests/css/support/swatch-white.png
new file mode 100644
index 0000000000..1a7d4323d7
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/swatch-yellow.png b/testing/web-platform/tests/css/support/swatch-yellow.png
new file mode 100644
index 0000000000..1591aa0e2e
--- /dev/null
+++ b/testing/web-platform/tests/css/support/swatch-yellow.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-bl.png b/testing/web-platform/tests/css/support/test-bl.png
new file mode 100644
index 0000000000..904e24e996
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-bl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-br.png b/testing/web-platform/tests/css/support/test-br.png
new file mode 100644
index 0000000000..f413ff5c1a
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-br.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-inner-half-size.png b/testing/web-platform/tests/css/support/test-inner-half-size.png
new file mode 100644
index 0000000000..e473bf80ef
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-inner-half-size.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-outer.png b/testing/web-platform/tests/css/support/test-outer.png
new file mode 100644
index 0000000000..82eeace7fc
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-outer.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-tl.png b/testing/web-platform/tests/css/support/test-tl.png
new file mode 100644
index 0000000000..f6ac0ef7e8
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-tl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/test-tr.png b/testing/web-platform/tests/css/support/test-tr.png
new file mode 100644
index 0000000000..59843ae54b
--- /dev/null
+++ b/testing/web-platform/tests/css/support/test-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/support/width-keyword-classes.css b/testing/web-platform/tests/css/support/width-keyword-classes.css
new file mode 100644
index 0000000000..e777527603
--- /dev/null
+++ b/testing/web-platform/tests/css/support/width-keyword-classes.css
@@ -0,0 +1,43 @@
+/*
+ Take every possible line break so the box width is the width of largest
+ unbreakable line box.
+*/
+.min-content {
+ width: min-content;
+}
+
+.max-content {
+ width: max-content;
+}
+
+/*
+ Shrink wrap just like floating.
+ max(min-content, min(max-content, fill-available))
+*/
+.fit-content {
+ width: fit-content;
+}
+
+.max-width-min-content {
+ max-width: min-content;
+}
+
+.max-width-max-content {
+ max-width: max-content;
+}
+
+.max-width-fit-content {
+ max-width: fit-content;
+}
+
+.min-width-min-content {
+ min-width: min-content;
+}
+
+.min-width-max-content {
+ min-width: max-content;
+}
+
+.min-width-fit-content {
+ min-width: fit-content;
+}