Merge pull request #12956 from Loirooriol/css-computed-insets
Test resolved value for inset CSS properties
diff --git a/css/cssom/getComputedStyle-insets-absolute.html b/css/cssom/getComputedStyle-insets-absolute.html
new file mode 100644
index 0000000..196f5f2
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-absolute.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for absolute positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForAbspos} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: absolute",
+ containingBlockElement: containerForAbspos,
+ containingBlockArea: "padding",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: true,
+ staticPositionY: 1 + 2 + 4 + 8,
+ staticPositionX: 2 + 4 + 8 + 16,
+});
+</script>
diff --git a/css/cssom/getComputedStyle-insets-fixed.html b/css/cssom/getComputedStyle-insets-fixed.html
new file mode 100644
index 0000000..e57e774
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-fixed.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for fixed positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForFixed} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: fixed",
+ containingBlockElement: containerForFixed,
+ containingBlockArea: "padding",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: true,
+ staticPositionY: 1 + 2 + 4 + 8 + 16 + 32 + 64,
+ staticPositionX: 2 + 4 + 8 + 16 + 32 + 64 + 128,
+});
+</script>
diff --git a/css/cssom/getComputedStyle-insets-nobox.html b/css/cssom/getComputedStyle-insets-nobox.html
new file mode 100644
index 0000000..ca55ace
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-nobox.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties when the element generates no box</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "display: none",
+ containingBlockElement: null,
+ preservesPercentages: true,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/css/cssom/getComputedStyle-insets-relative.html b/css/cssom/getComputedStyle-insets-relative.html
new file mode 100644
index 0000000..c48f2eb
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-relative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for relative positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: relative",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/css/cssom/getComputedStyle-insets-static.html b/css/cssom/getComputedStyle-insets-static.html
new file mode 100644
index 0000000..854a8e3
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-static.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for static positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: static",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: true,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/css/cssom/getComputedStyle-insets-sticky.html b/css/cssom/getComputedStyle-insets-sticky.html
new file mode 100644
index 0000000..1052023
--- /dev/null
+++ b/css/cssom/getComputedStyle-insets-sticky.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for sticky positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: sticky; position: -webkit-sticky",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: false,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/css/cssom/support/getComputedStyle-insets.js b/css/cssom/support/getComputedStyle-insets.js
new file mode 100644
index 0000000..7bd34fe
--- /dev/null
+++ b/css/cssom/support/getComputedStyle-insets.js
@@ -0,0 +1,376 @@
+export const testEl = document.createElement("div");
+export const containerForInflow = document.createElement("div");
+export const containerForAbspos = document.createElement("div");
+export const containerForFixed = document.createElement("div");
+
+testEl.id = "test";
+containerForInflow.id = "container-for-inflow";
+containerForAbspos.id = "container-for-abspos";
+containerForFixed.id = "container-for-fixed";
+
+containerForInflow.appendChild(testEl);
+containerForAbspos.appendChild(containerForInflow);
+containerForFixed.appendChild(containerForAbspos);
+document.body.appendChild(containerForFixed);
+
+const stylesheet = document.createElement("style");
+stylesheet.textContent = `
+ #container-for-inflow {
+ /* Content area: 100px tall, 200px wide */
+ height: 100px;
+ width: 200px;
+ padding: 1px 2px;
+ border-width: 2px 4px;
+ margin: 4px 8px;
+ overflow: hidden;
+ }
+ #container-for-abspos {
+ /* Padding area: 200px tall, 400px wide */
+ height: 184px;
+ width: 368px;
+ padding: 8px 16px;
+ border-width: 16px 32px;
+ margin: 32px 64px;
+ position: relative;
+ }
+ #container-for-fixed {
+ /* Padding area: 300px tall, 600px wide */
+ height: 172px;
+ width: 344px;
+ padding: 64px 128px;
+ border-width: 128px 256px;
+ margin: 256px 512px;
+ position: absolute;
+ transform: scale(1);
+ visibility: hidden;
+ }
+ [id ^= container] {
+ border-style: solid;
+ }
+`;
+document.head.appendChild(stylesheet);
+
+function runTestsWithWM(data, testWM, cbWM) {
+ const {
+ style,
+ containingBlockElement,
+ containingBlockArea,
+ preservesPercentages,
+ preservesAuto,
+ canStretchAutoSize,
+ staticPositionX,
+ staticPositionY,
+ } = data;
+
+ let cbHeight = containingBlockElement ? containingBlockElement.clientHeight : NaN;
+ let cbWidth = containingBlockElement ? containingBlockElement.clientWidth : NaN;
+ if (containingBlockElement && containingBlockArea == "content") {
+ const cs = getComputedStyle(containingBlockElement);
+ cbHeight -= parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
+ cbWidth -= parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
+ }
+
+ const staticPositionTop = cbWM.blockStart == "top" || cbWM.inlineStart == "top"
+ ? staticPositionY : cbHeight - staticPositionY;
+ const staticPositionLeft = cbWM.blockStart == "left" || cbWM.inlineStart == "left"
+ ? staticPositionX : cbWidth - staticPositionX;
+ const staticPositionBottom = cbWM.blockStart == "bottom" || cbWM.inlineStart == "bottom"
+ ? staticPositionY : cbHeight - staticPositionY;
+ const staticPositionRight = cbWM.blockStart == "right" || cbWM.inlineStart == "right"
+ ? staticPositionX : cbWidth - staticPositionX;
+
+ function serialize(declarations) {
+ return Object.entries(declarations).map(([p, v]) => `${p}: ${v}; `).join("");
+ }
+
+ function wmName(wm) {
+ return Object.values(wm.style).join(" ");
+ }
+
+ function checkStyle(declarations, expected, msg) {
+ test(function() {
+ testEl.style.cssText = style + "; " + serialize({...declarations, ...testWM.style});
+ if (containingBlockElement) {
+ containingBlockElement.style.cssText = serialize({...cbWM.style});
+ }
+ const cs = getComputedStyle(testEl);
+ for (let [prop, value] of Object.entries(expected)) {
+ assert_equals(cs[prop], value, `'${prop}'`);
+ }
+ }, `${wmName(testWM)} inside ${wmName(cbWM)} - ${msg}`);
+
+ testEl.style.cssText = "";
+ if (containingBlockElement) {
+ containingBlockElement.style.cssText = "";
+ }
+ }
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, "Pixels resolve as-is");
+
+ checkStyle({
+ top: "1em",
+ left: "2em",
+ bottom: "3em",
+ right: "4em",
+ "font-size": "10px",
+ }, {
+ top: "10px",
+ left: "20px",
+ bottom: "30px",
+ right: "40px",
+ }, "Relative lengths are absolutized into pixels");
+
+ if (preservesPercentages) {
+ checkStyle({
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, {
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, "Percentages resolve as-is");
+ } else {
+ checkStyle({
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, {
+ top: .1 * cbHeight + "px",
+ left: .25 * cbWidth + "px",
+ bottom: .5 * cbHeight + "px",
+ right: .75 * cbWidth + "px",
+ }, "Percentages are absolutized into pixels");
+
+ checkStyle({
+ top: "calc(10% - 1px)",
+ left: "calc(25% - 2px)",
+ bottom: "calc(50% - 3px)",
+ right: "calc(75% - 4px)",
+ }, {
+ top: .1 * cbHeight - 1 + "px",
+ left: .25 * cbWidth - 2 + "px",
+ bottom: .5 * cbHeight - 3 + "px",
+ right: .75 * cbWidth - 4 + "px",
+ }, "calc() is absolutized into pixels");
+ }
+
+ if (canStretchAutoSize) {
+ // Force overconstraintment by setting size or with insets that would result in
+ // negative size. Then the resolved value should be the computed one according to
+ // https://drafts.csswg.org/cssom/#resolved-value-special-case-property-like-top
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ height: "0px",
+ width: "0px",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, "Pixels resolve as-is when overconstrained");
+
+ checkStyle({
+ top: "100%",
+ left: "100%",
+ bottom: "100%",
+ right: "100%",
+ }, {
+ top: cbHeight + "px",
+ left: cbWidth + "px",
+ bottom: cbHeight + "px",
+ right: cbWidth + "px",
+ }, "Percentages absolutize the computed value when overconstrained");
+ }
+
+ if (preservesAuto) {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves as-is");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves as-is");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, "If opposite sides are 'auto', they resolve as-is");
+ } else if (canStretchAutoSize) {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: cbHeight - 3 + "px",
+ left: cbWidth - 4 + "px",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: cbHeight - 1 + "px",
+ right: cbWidth - 2 + "px",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: staticPositionTop + "px",
+ left: staticPositionLeft + "px",
+ bottom: staticPositionBottom + "px",
+ right: staticPositionRight + "px",
+ }, "If opposite sides are 'auto', they resolve to used value");
+ } else {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "-3px",
+ left: "-4px",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "-1px",
+ right: "-2px",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "0px",
+ left: "0px",
+ bottom: "0px",
+ right: "0px",
+ }, "If opposite sides are 'auto', they resolve to used value");
+ }
+}
+
+const writingModes = [{
+ style: {
+ "writing-mode": "horizontal-tb",
+ "direction": "ltr",
+ },
+ blockStart: "top",
+ blockEnd: "bottom",
+ inlineStart: "left",
+ inlineEnd: "right",
+}, {
+ style: {
+ "writing-mode": "horizontal-tb",
+ "direction": "rtl",
+ },
+ blockStart: "top",
+ blockEnd: "bottom",
+ inlineStart: "right",
+ inlineEnd: "left",
+}, {
+ style: {
+ "writing-mode": "vertical-lr",
+ "direction": "ltr",
+ },
+ blockStart: "left",
+ blockEnd: "right",
+ inlineStart: "top",
+ inlineEnd: "bottom",
+}, {
+ style: {
+ "writing-mode": "vertical-lr",
+ "direction": "rtl",
+ },
+ blockStart: "left",
+ blockEnd: "right",
+ inlineStart: "bottom",
+ inlineEnd: "top",
+}, {
+ style: {
+ "writing-mode": "vertical-rl",
+ "direction": "ltr",
+ },
+ blockStart: "right",
+ blockEnd: "left",
+ inlineStart: "top",
+ inlineEnd: "bottom",
+}, {
+ style: {
+ "writing-mode": "vertical-rl",
+ "direction": "rtl",
+ },
+ blockStart: "right",
+ blockEnd: "left",
+ inlineStart: "bottom",
+ inlineEnd: "top",
+}];
+
+export function runTests(data) {
+ for (let testWM of writingModes) {
+ for (let cbWM of writingModes) {
+ runTestsWithWM(data, testWM, cbWM);
+ }
+ }
+}