summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-logical/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/css/css-logical/resources
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-logical/resources')
-rw-r--r--testing/web-platform/tests/css/css-logical/resources/test-box-properties.js297
-rw-r--r--testing/web-platform/tests/css/css-logical/resources/test-logical-values.js27
-rw-r--r--testing/web-platform/tests/css/css-logical/resources/test-shared.js112
3 files changed, 436 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-logical/resources/test-box-properties.js b/testing/web-platform/tests/css/css-logical/resources/test-box-properties.js
new file mode 100644
index 0000000000..ef1854f97d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-logical/resources/test-box-properties.js
@@ -0,0 +1,297 @@
+import {
+ testElement,
+ writingModes,
+ testCSSValues,
+ testComputedValues,
+ makeDeclaration
+} from "./test-shared.js";
+
+// Values to use while testing
+const testValues = {
+ "length": ["1px", "2px", "3px", "4px", "5px"],
+ "color": ["rgb(1, 1, 1)", "rgb(2, 2, 2)", "rgb(3, 3, 3)", "rgb(4, 4, 4)", "rgb(5, 5, 5)"],
+ "border-style": ["solid", "dashed", "dotted", "double", "groove"],
+};
+
+/**
+ * Creates a group of physical and logical box properties, such as
+ *
+ * { physical: {
+ * left: "margin-left", right: "margin-right",
+ * top: "margin-top", bottom: "margin-bottom",
+ * }, logical: {
+ * inlineStart: "margin-inline-start", inlineEnd: "margin-inline-end",
+ * blockStart: "margin-block-start", blockEnd: "margin-block-end",
+ * }, shorthands: {
+ * "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"],
+ * "margin-inline": ["margin-inline-start", "margin-inline-end"],
+ * "margin-block": ["margin-block-start", "margin-block-end"],
+ * }, type: ["length"], prerequisites: "...", property: "margin-*" }
+ *
+ * @param {string} property
+ * A string representing the property names, like "margin-*".
+ * @param {Object} descriptor
+ * @param {string|string[]} descriptor.type
+ * Describes the kind of values accepted by the property, like "length".
+ * Must be a key or a collection of keys from the `testValues` object.
+ * @param {Object={}} descriptor.prerequisites
+ * Represents property declarations that are needed by `property` to work.
+ * For example, border-width properties require a border style.
+ */
+export function createBoxPropertyGroup(property, descriptor) {
+ const logical = {};
+ const physical = {};
+ const shorthands = {};
+ for (const axis of ["inline", "block"]) {
+ const shorthand = property.replace("*", axis);
+ const longhands = [];
+ shorthands[shorthand] = longhands;
+ for (const side of ["start", "end"]) {
+ const logicalSide = axis + "-" + side;
+ const camelCase = logicalSide.replace(/-(.)/g, (match, $1) => $1.toUpperCase());
+ const longhand = property.replace("*", logicalSide);
+ logical[camelCase] = longhand;
+ longhands.push(longhand);
+ }
+ }
+ const isInset = property === "inset-*";
+ let prerequisites = "";
+ for (const physicalSide of ["left", "right", "top", "bottom"]) {
+ physical[physicalSide] = isInset ? physicalSide : property.replace("*", physicalSide);
+ prerequisites += makeDeclaration(descriptor.prerequisites, physicalSide);
+ }
+ shorthands[property.replace("-*", "")] =
+ ["top", "right", "bottom", "left"].map(physicalSide => physical[physicalSide]);
+ const type = [].concat(descriptor.type);
+ return {logical, physical, shorthands, type, prerequisites, property};
+}
+
+/**
+ * Creates a group physical and logical box-corner properties.
+ *
+ * @param {string} property
+ * A string representing the property names, like "border-*-radius".
+ * @param {Object} descriptor
+ * @param {string|string[]} descriptor.type
+ * Describes the kind of values accepted by the property, like "length".
+ * Must be a key or a collection of keys from the `testValues` object.
+ * @param {Object={}} descriptor.prerequisites
+ * Represents property declarations that are needed by `property` to work.
+ * For example, border-width properties require a border style.
+ */
+export function createCornerPropertyGroup(property, descriptor) {
+ const logical = {};
+ const physical = {};
+ const shorthands = {};
+ for (const logicalCorner of ["start-start", "start-end", "end-start", "end-end"]) {
+ const prop = property.replace("*", logicalCorner);
+ const [block_side, inline_side] = logicalCorner.split("-");
+ const b = "block" + block_side.charAt(0).toUpperCase() + block_side.slice(1);
+ const i = "inline" + inline_side.charAt(0).toUpperCase() + inline_side.slice(1);
+ const index = b + "-" + i; // e.g. "blockStart-inlineEnd"
+ logical[index] = prop;
+ }
+ let prerequisites = "";
+ for (const physicalCorner of ["top-left", "top-right", "bottom-left", "bottom-right"]) {
+ const prop = property.replace("*", physicalCorner);
+ physical[physicalCorner] = prop;
+ prerequisites += makeDeclaration(descriptor.prerequisites, physicalCorner);
+ }
+ const type = [].concat(descriptor.type);
+ return {logical, physical, shorthands, type, prerequisites, property};
+}
+
+/**
+ * Creates a group of physical and logical sizing properties.
+ *
+ * @param {string} prefix
+ * One of "", "max-" or "min-".
+ */
+export function createSizingPropertyGroup(prefix) {
+ return {
+ logical: {
+ inline: `${prefix}inline-size`,
+ block: `${prefix}block-size`,
+ },
+ physical: {
+ horizontal: `${prefix}width`,
+ vertical: `${prefix}height`,
+ },
+ type: ["length"],
+ prerequisites: makeDeclaration({display: "block"}),
+ property: (prefix ? prefix.slice(0, -1) + " " : "") + "sizing",
+ };
+}
+
+/**
+ * Tests a grup of logical and physical properties in different writing modes.
+ *
+ * @param {Object} group
+ * An object returned by createBoxPropertyGroup or createSizingPropertyGroup.
+ */
+export function runTests(group) {
+ const values = testValues[group.type[0]].map(function(_, i) {
+ return group.type.map(type => testValues[type][i]).join(" ");
+ });
+ const logicals = Object.values(group.logical);
+ const physicals = Object.values(group.physical);
+ const shorthands = group.shorthands ? Object.entries(group.shorthands) : null;
+ const is_corner = group.property == "border-*-radius";
+
+ test(function() {
+ const expected = [];
+ for (const [i, logicalProp] of logicals.entries()) {
+ testElement.style.setProperty(logicalProp, values[i]);
+ expected.push([logicalProp, values[i]]);
+ }
+ testCSSValues("logical properties in inline style", testElement.style, expected);
+ }, `Test that logical ${group.property} properties are supported.`);
+ testElement.style.cssText = "";
+
+ const shorthandValues = {};
+ for (const [shorthand, longhands] of shorthands || []) {
+ let valueArray;
+ if (group.type.length > 1) {
+ valueArray = [values[0]];
+ } else {
+ valueArray = testValues[group.type].slice(0, longhands.length);
+ }
+ shorthandValues[shorthand] = valueArray;
+ const value = valueArray.join(" ");
+ const expected = [[shorthand, value]];
+ for (let [i, longhand] of longhands.entries()) {
+ expected.push([longhand, valueArray[group.type.length > 1 ? 0 : i]]);
+ }
+ test(function() {
+ testElement.style.setProperty(shorthand, value);
+ testCSSValues("shorthand in inline style", testElement.style, expected);
+ const stylesheet = `.test { ${group.prerequisites} }`;
+ testComputedValues("shorthand in computed style", stylesheet, expected);
+ }, `Test that ${shorthand} shorthand sets longhands and serializes correctly.`);
+ testElement.style.cssText = "";
+ }
+
+ for (const writingMode of writingModes) {
+ for (const style of writingMode.styles) {
+ const writingModeDecl = makeDeclaration(style);
+
+ const associated = {};
+ for (const [logicalSide, logicalProp] of Object.entries(group.logical)) {
+ let physicalProp;
+ if (is_corner) {
+ const [ block_side, inline_side] = logicalSide.split("-");
+ const physicalSide1 = writingMode[block_side];
+ const physicalSide2 = writingMode[inline_side];
+ let physicalCorner;
+ // mirror "left-top" to "top-left" etc
+ if (["top", "bottom"].includes(physicalSide1)) {
+ physicalCorner = physicalSide1 + "-" + physicalSide2;
+ } else {
+ physicalCorner = physicalSide2 + "-" + physicalSide1;
+ }
+ physicalProp = group.physical[physicalCorner];
+ } else {
+ physicalProp = group.physical[writingMode[logicalSide]];
+ }
+ associated[logicalProp] = physicalProp;
+ associated[physicalProp] = logicalProp;
+ }
+
+ // Test that logical properties are converted to their physical
+ // equivalent correctly when all in the group are present on a single
+ // declaration, with no overwriting of previous properties and
+ // no physical properties present. We put the writing mode properties
+ // on a separate declaration to test that the computed values of these
+ // properties are used, rather than those on the same declaration.
+ test(function() {
+ let decl = group.prerequisites;
+ const expected = [];
+ for (const [i, logicalProp] of logicals.entries()) {
+ decl += `${logicalProp}: ${values[i]}; `;
+ expected.push([logicalProp, values[i]]);
+ expected.push([associated[logicalProp], values[i]]);
+ }
+ testComputedValues("logical properties on one declaration, writing " +
+ `mode properties on another, '${writingModeDecl}'`,
+ `.test { ${writingModeDecl} } .test { ${decl} }`,
+ expected);
+ }, `Test that logical ${group.property} properties share computed values `
+ + `with their physical associates, with '${writingModeDecl}'.`);
+
+ // Test logical shorthand properties.
+ if (shorthands) {
+ test(function() {
+ for (const [shorthand, longhands] of shorthands) {
+ let valueArray = shorthandValues[shorthand];
+ const decl = group.prerequisites + `${shorthand}: ${valueArray.join(" ")}; `;
+ const expected = [];
+ for (let [i, longhand] of longhands.entries()) {
+ const longhandValue = valueArray[group.type.length > 1 ? 0 : i];
+ expected.push([longhand, longhandValue]);
+ expected.push([associated[longhand], longhandValue]);
+ }
+ testComputedValues("shorthand properties on one declaration, writing " +
+ `mode properties on another, '${writingModeDecl}'`,
+ `.test { ${writingModeDecl} } .test { ${decl} }`,
+ expected);
+ }
+ }, `Test that ${group.property} shorthands set the computed value of both `
+ + `logical and physical longhands, with '${writingModeDecl}'.`);
+ }
+
+ // Test that logical and physical properties are cascaded together,
+ // honoring their relative order on a single declaration
+ // (a) with a single logical property after the physical ones
+ // (b) with a single physical property after the logical ones
+ test(function() {
+ for (const lastIsLogical of [true, false]) {
+ const lasts = lastIsLogical ? logicals : physicals;
+ const others = lastIsLogical ? physicals : logicals;
+ for (const lastProp of lasts) {
+ let decl = writingModeDecl + group.prerequisites;
+ const expected = [];
+ for (const [i, prop] of others.entries()) {
+ decl += `${prop}: ${values[i]}; `;
+ const valueIdx = associated[prop] === lastProp ? others.length : i;
+ expected.push([prop, values[valueIdx]]);
+ expected.push([associated[prop], values[valueIdx]]);
+ }
+ decl += `${lastProp}: ${values[others.length]}; `;
+ testComputedValues(`'${lastProp}' last on single declaration, '${writingModeDecl}'`,
+ `.test { ${decl} }`,
+ expected);
+ }
+ }
+ }, `Test that ${group.property} properties honor order of appearance when both `
+ + `logical and physical associates are declared, with '${writingModeDecl}'.`);
+
+ // Test that logical and physical properties are cascaded properly when
+ // on different declarations
+ // (a) with a logical property in the high specificity rule
+ // (b) with a physical property in the high specificity rule
+ test(function() {
+ for (const highIsLogical of [true, false]) {
+ let lowDecl = writingModeDecl + group.prerequisites;
+ const high = highIsLogical ? logicals : physicals;
+ const others = highIsLogical ? physicals : logicals;
+ for (const [i, prop] of others.entries()) {
+ lowDecl += `${prop}: ${values[i]}; `;
+ }
+ for (const highProp of high) {
+ const highDecl = `${highProp}: ${values[others.length]}; `;
+ const expected = [];
+ for (const [i, prop] of others.entries()) {
+ const valueIdx = associated[prop] === highProp ? others.length : i;
+ expected.push([prop, values[valueIdx]]);
+ expected.push([associated[prop], values[valueIdx]]);
+ }
+ testComputedValues(`'${highProp}', two declarations, '${writingModeDecl}'`,
+ `#test { ${highDecl} } .test { ${lowDecl} }`,
+ expected);
+ }
+ }
+ }, `Test that ${group.property} properties honor selector specificty when both `
+ + `logical and physical associates are declared, with '${writingModeDecl}'.`);
+ }
+ }
+}
diff --git a/testing/web-platform/tests/css/css-logical/resources/test-logical-values.js b/testing/web-platform/tests/css/css-logical/resources/test-logical-values.js
new file mode 100644
index 0000000000..7a039379cb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-logical/resources/test-logical-values.js
@@ -0,0 +1,27 @@
+import {
+ testElement,
+ writingModes,
+ testCSSValues,
+ testComputedValues,
+ makeDeclaration
+} from "./test-shared.js";
+
+/**
+ * Tests flow-relative values for a CSS property in different writing modes.
+ *
+ * @param {string} property
+ * The CSS property to be tested.
+ * @param {string[]} values
+ * An array with the flow-relative values to be tested.
+ */
+export function runTests(property, values) {
+ for (const value of values) {
+ test(function() {
+ const {style} = testElement;
+ style.cssText = "";
+ style.setProperty(property, value);
+ testCSSValues("logical values in inline style", style, [[property, value]]);
+ testComputedValues("logical values in computed style", style, [[property, value]]);
+ }, `Test that '${property}: ${value}' is supported.`);
+ }
+}
diff --git a/testing/web-platform/tests/css/css-logical/resources/test-shared.js b/testing/web-platform/tests/css/css-logical/resources/test-shared.js
new file mode 100644
index 0000000000..7a1da2e649
--- /dev/null
+++ b/testing/web-platform/tests/css/css-logical/resources/test-shared.js
@@ -0,0 +1,112 @@
+const sheet = document.head.appendChild(document.createElement("style"));
+
+// Specify size for outer <div> to avoid unconstrained-size warnings
+// when writing-mode of the inner test <div> is vertical-*
+const wrapper = document.body.appendChild(document.createElement("div"));
+wrapper.style.cssText = "width:100px; height: 100px;";
+export const testElement = wrapper.appendChild(document.createElement("div"));
+testElement.id = testElement.className = "test";
+
+// Six unique overall writing modes for property-mapping purposes.
+export const writingModes = [
+ {
+ styles: [
+ {"writing-mode": "horizontal-tb", "direction": "ltr"},
+ ],
+ blockStart: "top", blockEnd: "bottom", inlineStart: "left", inlineEnd: "right",
+ over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
+ block: "vertical", inline: "horizontal" },
+ {
+ styles: [
+ {"writing-mode": "horizontal-tb", "direction": "rtl"},
+ ],
+ blockStart: "top", blockEnd: "bottom", inlineStart: "right", inlineEnd: "left",
+ over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
+ block: "vertical", inline: "horizontal" },
+ {
+ styles: [
+ {"writing-mode": "vertical-rl", "direction": "rtl"},
+ {"writing-mode": "sideways-rl", "direction": "rtl"},
+ ],
+ blockStart: "right", blockEnd: "left", inlineStart: "bottom", inlineEnd: "top",
+ over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
+ block: "horizontal", inline: "vertical" },
+ {
+ styles: [
+ {"writing-mode": "vertical-rl", "direction": "ltr"},
+ {"writing-mode": "sideways-rl", "direction": "ltr"},
+ ],
+ blockStart: "right", blockEnd: "left", inlineStart: "top", inlineEnd: "bottom",
+ over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
+ block: "horizontal", inline: "vertical" },
+ {
+ styles: [
+ {"writing-mode": "vertical-lr", "direction": "rtl"},
+ ],
+ blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top",
+ over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
+ block: "horizontal", inline: "vertical" },
+ {
+ styles: [
+ {"writing-mode": "sideways-lr", "direction": "ltr"},
+ ],
+ blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top",
+ over: "left", under: "right", lineLeft: "bottom", lineRight: "top",
+ block: "horizontal", inline: "vertical" },
+ {
+ styles: [
+ {"writing-mode": "vertical-lr", "direction": "ltr"},
+ ],
+ blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom",
+ over: "right", under: "left", lineLeft: "top", lineRight: "bottom",
+ block: "horizontal", inline: "vertical" },
+ {
+ styles: [
+ {"writing-mode": "sideways-lr", "direction": "rtl"},
+ ],
+ blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom",
+ over: "left", under: "right", lineLeft: "bottom", lineRight: "top",
+ block: "horizontal", inline: "vertical" },
+];
+
+// Check if logical properties work well in WebKit non-standard
+// '-webkit-writing-mode: horizontal-bt' mode
+if (CSS.supports("-webkit-writing-mode", "horizontal-bt")) {
+ writingModes.push (
+ {
+ styles: [
+ {"-webkit-writing-mode": "horizontal-bt", "direction": "ltr"},
+ ],
+ blockStart: "bottom", blockEnd: "top", inlineStart: "left", inlineEnd: "right",
+ over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
+ block: "vertical", inline: "horizontal" },
+ {
+ styles: [
+ {"-webkit-writing-mode": "horizontal-bt", "direction": "rtl"},
+ ],
+ blockStart: "bottom", blockEnd: "top", inlineStart: "right", inlineEnd: "left",
+ over: "top", under: "bottom", lineLeft: "left", lineRight: "right",
+ block: "vertical", inline: "horizontal" },
+ )
+}
+
+export function testCSSValues(testName, style, expectedValues) {
+ for (const [property, value] of expectedValues) {
+ assert_equals(style.getPropertyValue(property), value, `${testName}, ${property}`);
+ }
+}
+
+export function testComputedValues(testName, rules, expectedValues) {
+ sheet.textContent = rules;
+ const cs = getComputedStyle(testElement);
+ testCSSValues(testName, cs, expectedValues);
+ sheet.textContent = "";
+}
+
+export function makeDeclaration(object = {}, replacement = "*") {
+ let decl = "";
+ for (const [property, value] of Object.entries(object)) {
+ decl += `${property.replace("*", replacement)}: ${value}; `;
+ }
+ return decl;
+}