Add WPTs for SVG presentation attribute support.
Differential Revision: https://phabricator.services.mozilla.com/D63036
bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1615944
gecko-commit: 0c55843ce3af5a70617499e04d7bb9cc2aadcf69
gecko-integration-branch: autoland
gecko-reviewers: longsonr
diff --git a/svg/styling/presentation-attributes-irrelevant.html b/svg/styling/presentation-attributes-irrelevant.html
new file mode 100644
index 0000000..f96d1d9
--- /dev/null
+++ b/svg/styling/presentation-attributes-irrelevant.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SVG presentation attributes - on irrelevant elements</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="presentation-attributes.js"></script>
+<svg id="svg"></svg>
+<script>
+// Test that all of the presentation attributes with no special rules are
+// also supported on elements that the corresponding property does not
+// apply to.
+
+for (let p in PROPERTIES) {
+ if (CSS.supports(p, "initial") && PROPERTIES[p].irrelevantElement) {
+ test(function() {
+ assertPresentationAttributeIsSupported(PROPERTIES[p].irrelevantElement, p, PROPERTIES[p].value, p);
+ }, `${p} presentation attribute supported on an irrelevant element`);
+ }
+}
+</script>
diff --git a/svg/styling/presentation-attributes-relevant.html b/svg/styling/presentation-attributes-relevant.html
new file mode 100644
index 0000000..b5f9343
--- /dev/null
+++ b/svg/styling/presentation-attributes-relevant.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SVG presentation attributes - on relevant elements</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="presentation-attributes.js"></script>
+<svg id="svg"></svg>
+<script>
+// Test that all of the presentation attributes with no special rules are
+// supported on elements that the corresponding properties apply to.
+
+for (let p in PROPERTIES) {
+ if (CSS.supports(p, "initial")) {
+ test(function() {
+ assertPresentationAttributeIsSupported(PROPERTIES[p].relevantElement, p, PROPERTIES[p].value, p);
+ }, `${p} presentation attribute supported on a relevant element`);
+ }
+}
+</script>
diff --git a/svg/styling/presentation-attributes-special-cases.html b/svg/styling/presentation-attributes-special-cases.html
new file mode 100644
index 0000000..c544c9c
--- /dev/null
+++ b/svg/styling/presentation-attributes-special-cases.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SVG presentation attributes - special cases</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="presentation-attributes.js"></script>
+<svg id="svg"></svg>
+<script>
+// 1. Test the special cases where presentation attributes are only allowed on
+// a specific set of elements.
+
+if (propertiesAreSupported(["cx", "cy"])) {
+ for (let e of ["circle", "ellipse"]) {
+ test(function() {
+ for (let p of ["cx", "cy"]) {
+ assertPresentationAttributeIsSupported(e, p, "1", p);
+ }
+ }, `cx and cy presentation attributes supported on ${e} element`);
+ }
+}
+
+if (propertiesAreSupported(["x", "y", "width", "height"])) {
+ for (let e of ["foreignObject", "image", "rect", "svg", "symbol", "use"]) {
+ test(function() {
+ for (let p of ["x", "y", "width", "height"]) {
+ assertPresentationAttributeIsSupported(e, p, "1", p);
+ }
+ }, `x, y, width, and height presentation attributes supported on ${e} element`);
+ }
+}
+
+if (CSS.supports("r", "initial")) {
+ test(function() {
+ assertPresentationAttributeIsSupported("circle", "r", "1", "r");
+ }, `r presentation attribute supported on circle element`);
+}
+
+if (propertiesAreSupported(["rx", "ry"])) {
+ for (let e of ["ellipse", "rect"]) {
+ test(function() {
+ for (let p of ["rx", "ry"]) {
+ assertPresentationAttributeIsSupported(e, p, "1", p);
+ }
+ }, `rx and ry presentation attributes supported on ${e} element`);
+ }
+}
+
+if (CSS.supports("d", "initial")) {
+ test(function() {
+ assertPresentationAttributeIsSupported("path", "d", "M0,0 L1,1", "d");
+ }, `d presentation attribute supported on path element`);
+}
+
+
+// 2. Test that for those presentation attributes only allowed on a specific
+// set of elements, that they are not supported on some other SVG element.
+// (We use 'g' as that element for testing.)
+
+if (propertiesAreSupported(["cx", "cy"])) {
+ test(function() {
+ for (let p of ["cx", "cy"]) {
+ assertPresentationAttributeIsNotSupported("g", p, "1", p);
+ }
+ }, `cx and cy presentation attributes not supported on other elements`);
+}
+
+if (propertiesAreSupported(["x", "y", "width", "height"])) {
+ test(function() {
+ for (let p of ["x", "y", "width", "height"]) {
+ assertPresentationAttributeIsNotSupported("g", p, "1", p);
+ }
+ }, `x, y, width, and height presentation attributes not supported on other elements`);
+}
+
+if (CSS.supports("r", "initial")) {
+ test(function() {
+ assertPresentationAttributeIsNotSupported("g", "r", "1", "r");
+ }, `r presentation attribute not supported on other elements`);
+}
+
+if (propertiesAreSupported(["rx", "ry"])) {
+ test(function() {
+ for (let p of ["rx", "ry"]) {
+ assertPresentationAttributeIsNotSupported("g", p, "1", p);
+ }
+ }, `rx and ry presentation attributes not supported on other elements`);
+}
+
+if (CSS.supports("d", "initial")) {
+ test(function() {
+ assertPresentationAttributeIsSupported("g", "d", "M0,0 L1,1", "d");
+ }, `d presentation attribute not supported on other elements`);
+}
+
+
+// 3. Test that the fill presentation attribute is not supported on any
+// animation elements.
+
+if (CSS.supports("fill", "initial")) {
+ test(function() {
+ for (let e of ["animate", "animateMotion", "animateTransform", "discard", "set"]) {
+ assertPresentationAttributeIsNotSupported(e, "fill", "blue", "fill");
+ }
+ }, `fill presentation attribute not supported on animation elements`);
+}
+
+
+if (CSS.supports("transform", "initial")) {
+ // 4. Test support for the presentation attributes of the transform property,
+ // which have a different spelling depending on which element they're on.
+
+ test(function() {
+ assertPresentationAttributeIsSupported("g", "transform", "scale(2)", "transform");
+ }, `transform presentation attribute supported on g`);
+
+ test(function() {
+ assertPresentationAttributeIsSupported("pattern", "patternTransform", "scale(2)", "transform");
+ }, `patternTransform presentation attribute supported on pattern`);
+
+ for (let e of ["linearGradient", "radialGradient"]) {
+ test(function() {
+ assertPresentationAttributeIsSupported(e, "gradientTransform", "scale(2)", "transform");
+ }, `patternTransform presentation attribute supported on ${e}`);
+ }
+
+
+ // 5. Test that the wrong spellings of the presentation attributes of the
+ // transform property are not supported.
+
+ test(function() {
+ for (let e of ["pattern", "linearGradient", "radialGradient"]) {
+ assertPresentationAttributeIsNotSupported(e, "transform", "scale(2)", "transform");
+ }
+ }, `transform presentation attribute not supported on pattern or gradient elements`);
+
+ test(function() {
+ for (let e of ["g", "linearGradient", "radialGradient"]) {
+ assertPresentationAttributeIsNotSupported(e, "patternTransform", "scale(2)", "transform");
+ }
+ }, `patternTransform presentation attribute not supported on g or gradient elements`);
+
+ test(function() {
+ for (let e of ["g", "pattern"]) {
+ assertPresentationAttributeIsNotSupported(e, "gradientTransform", "scale(2)", "transform");
+ }
+ }, `gradientTransform presentation attribute not supported on g or pattern elements`);
+}
+</script>
diff --git a/svg/styling/presentation-attributes-unknown.html b/svg/styling/presentation-attributes-unknown.html
new file mode 100644
index 0000000..487175b
--- /dev/null
+++ b/svg/styling/presentation-attributes-unknown.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SVG presentation attributes - on unknown SVG elements</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/styling.html#PresentationAttributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="presentation-attributes.js"></script>
+<svg id="svg"></svg>
+<script>
+// Test that all of the presentation attributes with no special
+// rules are supported on unknown SVG elements.
+
+for (let p in PROPERTIES) {
+ if (CSS.supports(p, "initial") && PROPERTIES[p].irrelevantElement) {
+ test(function() {
+ assertPresentationAttributeIsSupported("unknown", p, PROPERTIES[p].value, p);
+ }, `${p} presentation attribute supported on an unknown SVG element`);
+ }
+}
+</script>
diff --git a/svg/styling/presentation-attributes.js b/svg/styling/presentation-attributes.js
new file mode 100644
index 0000000..c9dc14a
--- /dev/null
+++ b/svg/styling/presentation-attributes.js
@@ -0,0 +1,385 @@
+const PROPERTIES = {
+ "alignment-baseline": {
+ value: "middle",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "baseline-shift": {
+ value: "1",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "clip-path": {
+ value: "url(#e)",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "clip-rule": {
+ value: "evenodd",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "color": {
+ value: "blue",
+ relevantElement: "g",
+ irrelevantElement: "image",
+ },
+ "color-interpolation-filters": {
+ value: "sRGB",
+ relevantElement: "filter",
+ irrelevantElement: "linearGradient",
+ },
+ "color-interpolation": {
+ value: "linearRGB",
+ relevantElement: "linearGradient",
+ irrelevantElement: "image",
+ },
+ "cursor": {
+ value: "pointer",
+ relevantElement: "g",
+ irrelevantElement: "defs",
+ },
+ "cx": {
+ value: "1",
+ relevantElement: "circle",
+ irrelevantElement: null,
+ },
+ "cy": {
+ value: "1",
+ relevantElement: "circle",
+ irrelevantElement: null,
+ },
+ "direction": {
+ value: "rtl",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "display": {
+ value: "block",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "d": {
+ value: "M0,0 L1,1",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "dominant-baseline": {
+ value: "middle",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "fill": {
+ value: "blue",
+ relevantElement: "g",
+ irrelevantElement: "image",
+ },
+ "fill-opacity": {
+ value: "0.5",
+ relevantElement: "g",
+ irrelevantElement: "image",
+ },
+ "fill-rule": {
+ value: "evenodd",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "filter": {
+ value: "url(#e)",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "flood-color": {
+ value: "blue",
+ relevantElement: "feFlood",
+ irrelevantElement: "rect",
+ },
+ "flood-opacity": {
+ value: "0.5",
+ relevantElement: "feFlood",
+ irrelevantElement: "rect",
+ },
+ "font-family": {
+ value: "Test Family",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-size": {
+ value: "50",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-size-adjust": {
+ value: "0.5",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-stretch": {
+ value: "expanded",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-style": {
+ value: "italic",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-variant": {
+ value: "small-caps",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "font-weight": {
+ value: "900",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "glyph-orientation-vertical": {
+ value: "90",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "height": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+ "image-rendering": {
+ value: "optimizeSpeed",
+ relevantElement: "image",
+ irrelevantElement: "path",
+ },
+ "letter-spacing": {
+ value: "1px",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "lighting-color": {
+ value: "blue",
+ relevantElement: "feDiffuseLighting",
+ irrelevantElement: "rect",
+ },
+ "marker-end": {
+ value: "url(#e)",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "marker-mid": {
+ value: "url(#e)",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "marker-start": {
+ value: "url(#e)",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "mask-type": {
+ value: "alpha",
+ relevantElement: "mask",
+ irrelevantElement: "rect",
+ },
+ "mask": {
+ value: "url(#e)",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "opacity": {
+ value: "0.5",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "overflow": {
+ value: "scroll",
+ relevantElement: "svg",
+ irrelevantElement: "rect",
+ },
+ "paint-order": {
+ value: "fill stroke",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "pointer-events": {
+ value: "none",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "r": {
+ value: "1",
+ relevantElement: "circle",
+ irrelevantElement: "rect",
+ },
+ "rx": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+ "ry": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+ "shape-rendering": {
+ value: "geometricPrecision",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stop-color": {
+ value: "blue",
+ relevantElement: "stop",
+ irrelevantElement: "rect",
+ },
+ "stop-opacity": {
+ value: "0.5",
+ relevantElement: "stop",
+ irrelevantElement: "rect",
+ },
+ "stroke": {
+ value: "blue",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-dasharray": {
+ value: "1 1",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-dashoffset": {
+ value: "1",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-linecap": {
+ value: "round",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-linejoin": {
+ value: "round",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-miterlimit": {
+ value: "1",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-opacity": {
+ value: "0.5",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "stroke-width": {
+ value: "2",
+ relevantElement: "path",
+ irrelevantElement: "image",
+ },
+ "text-anchor": {
+ value: "middle",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "text-decoration": {
+ value: "underline",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "text-overflow": {
+ value: "ellipsis",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "text-rendering": {
+ value: "geometricPrecision",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "transform-origin": {
+ value: "1px 1px",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "transform": {
+ value: "scale(2)",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "unicode-bidi": {
+ value: "embed",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "vector-effect": {
+ value: "non-scaling-stroke",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "visibility": {
+ value: "hidden",
+ relevantElement: "g",
+ irrelevantElement: "linearGradient",
+ },
+ "white-space": {
+ value: "pre",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "width": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+ "word-spacing": {
+ value: "1",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "writing-mode": {
+ value: "vertical-rl",
+ relevantElement: "text",
+ irrelevantElement: "rect",
+ },
+ "x": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+ "y": {
+ value: "1",
+ relevantElement: "rect",
+ irrelevantElement: "path",
+ },
+};
+
+function presentationAttributeIsSupported(element, attribute, value, property) {
+ let e = document.createElementNS("http://www.w3.org/2000/svg", element);
+ svg.append(e);
+ let propertyValueBefore = getComputedStyle(e).getPropertyValue(property);
+ e.setAttribute(attribute, value);
+ let propertyValueAfter = getComputedStyle(e).getPropertyValue(property);
+ e.remove();
+ return propertyValueBefore != propertyValueAfter;
+}
+
+function assertPresentationAttributeIsSupported(element, attribute, value, property) {
+ assert_true(
+ presentationAttributeIsSupported(element, attribute, value, property),
+ `Presentation attribute ${attribute}="${value}" should be supported on ${element} element`
+ );
+}
+
+function assertPresentationAttributeIsNotSupported(element, attribute, value, property) {
+ assert_false(
+ presentationAttributeIsSupported(element, attribute, value, property),
+ `Presentation attribute ${attribute}="${value}" should be supported on ${element} element`
+ );
+}
+
+function propertiesAreSupported(properties) {
+ for (let p of properties) {
+ if (!CSS.supports(p, "initial")) {
+ return false;
+ }
+ }
+ return true;
+}