commit 68ccf3e074835ced2da2c6aaec4963d4dcb3bbeb
parent 018711f85b87608abae619a78343d7bf5c91b0b3
Author: Divyansh Mangal <dmangal@microsoft.com>
Date: Wed, 3 Dec 2025 14:42:40 +0000
Bug 2003485 [wpt PR 56390] - [SVG] Handle non-primitives values like `var()` for SVGLength, a=testonly
Automatic update from web-platform-tests
[SVG] Handle non-primitives values like `var()` for SVGLength
Currently, SVGLength attributes (e.g., `width`, `radius`) only
support primitive CSS values such as `100px` or `100%`. CSS
constructs like `var()` fail during parsing because they cannot be
resolved immediately.
This change introduces support for unparsed CSS values (e.g., var())
in SVGLength by adopting a lazy resolution approach:
- During parsing phase, values like `var()` are stored as
`CSSUnparsedDeclarationValue` instead of being rejected.
- When the actual parsed value is needed (through use of `Value()`
and `ConvertToLength()` functions) we resolve the unparsed value
(if needed) by calling `StyleCascade::ResolveSubstitutions`.
(inspired from the code in [1])
This aligns the behaviour of CSS var() in presentation attributes
with Firefox and Safari.
[1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_css_agent.cc;drc=db569cd7847d479358bd391a18b2c28393803333;l=2512
Bug: 40801413
Change-Id: I480d4caa0292b58cd510c7c378fad2c2f5eded35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7123806
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Reviewed-by: Vinay Singh <vinaysingh@microsoft.com>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Divyansh Mangal <dmangal@microsoft.com>
Commit-Queue: Vinay Singh <vinaysingh@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1552726}
--
wpt-commits: f819f3b14843b54d4f4b62348424c984bdf97c0a
wpt-pr: 56390
Diffstat:
7 files changed, 183 insertions(+), 0 deletions(-)
diff --git a/testing/web-platform/tests/svg/styling/css-var-dom-interface.html b/testing/web-platform/tests/svg/styling/css-var-dom-interface.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<title>SVGLength DOM interface with CSS variables</title>
+<link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com">
+<link rel="help" href="https://github.com/w3c/svgwg/issues/1038">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ :root {
+ --length-100px: 100px;
+ --length-50: 50;
+ --length-75percent: 75%;
+ --invalid-length: not-a-length;
+ }
+</style>
+
+<svg id="svg" width="200" height="200">
+ <rect id="rect1-px" width="var(--length-100px)" height="50"/>
+ <rect id="rect2-px" width="var(--length-100px)" height="50"/>
+ <rect id="rect3-px" width="var(--length-100px)" height="50"/>
+ <rect id="rect-number" width="var(--length-50)" height="50"/>
+ <rect id="rect-percent" width="var(--length-75percent)" height="50"/>
+ <rect id="rect-invalid" width="var(--invalid-length)" height="50"/>
+ <rect id="rect-normal" width="100px" height="50"/>
+</svg>
+
+<script>
+test(() => {
+ const rect = document.getElementById('rect1-px');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.value, 0);
+ assert_equals(width.valueInSpecifiedUnits, 0);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength DOM interface with CSS variable containing px unit');
+
+test(() => {
+ const rect = document.getElementById('rect-number');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.value, 0);
+ assert_equals(width.valueInSpecifiedUnits, 0);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength DOM interface with CSS variable containing number');
+
+test(() => {
+ const rect = document.getElementById('rect-percent');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.value, 0);
+ assert_equals(width.valueInSpecifiedUnits, 0);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength DOM interface with CSS variable containing percentage');
+
+test(() => {
+ const rect = document.getElementById('rect-invalid');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.value, 0);
+ assert_equals(width.valueInSpecifiedUnits, 0);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength DOM interface with invalid CSS variable');
+
+test(() => {
+ const rect = document.getElementById('rect1-px');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.valueAsString, 'var(--length-100px)');
+
+ // Setting value should work (converts to user units)
+ width.value = 120;
+ assert_equals(width.value, 120);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
+ assert_equals(width.valueAsString, '120');
+
+}, 'SVGLength DOM interface methods with CSS variables');
+
+test(() => {
+ const rect = document.getElementById('rect2-px');
+ const width = rect.width.baseVal;
+
+ assert_equals(width.valueAsString, 'var(--length-100px)');
+
+ assert_throws_dom("NotSupportedError", function() { width.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM); });
+
+ assert_equals(width.valueAsString, 'var(--length-100px)');
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+ assert_equals(width.value, 0);
+
+}, 'SVGLength.convertToSpecifiedUnits with CSS variables');
+
+test(() => {
+ const rect = document.getElementById('rect3-px');
+ const width = rect.width.baseVal;
+
+ width.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM, 2.54);
+ assert_equals(width.value, 96);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_CM);
+ assert_equals(width.valueAsString, '2.54cm');
+
+}, 'SVGLength.newValueSpecifiedUnits with CSS variables');
+
+test(() => {
+ const detachedSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ const detachedRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+ detachedSvg.appendChild(detachedRect);
+
+ detachedRect.setAttribute('width', 'var(--length-100px)');
+
+ const width = detachedRect.width.baseVal;
+
+ assert_equals(width.value, 0);
+ assert_equals(width.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength should handle detached elements gracefully for CSS variables');
+
+test(() => {
+ const svg = document.getElementById('svg');
+ const new_length = svg.createSVGLength();
+
+ new_length.valueAsString = 'var(--length-100px)';
+
+ assert_equals(new_length.value, 0);
+ assert_equals(new_length.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+
+}, 'SVGLength should handle standalone lengths gracefully for CSS variables');
+</script>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-01.svg b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-01.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+ <title>Usage of css var() on svg width and height attributes (units specified)</title>
+ <html:link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com"/>
+ <html:link rel="match" href="../embedded/reference/green-rect-100x100.svg"/>
+ <style>
+ rect {
+ --length: 100px;
+ }
+ </style>
+ <rect width="var(--length)" height="var(--length)" fill="green"/>
+</svg>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-02.svg b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-02.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+ <title>Usage of css var() on svg width and height attributes (units not specified)</title>
+ <html:link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com"/>
+ <html:link rel="match" href="../embedded/reference/green-rect-100x100.svg"/>
+ <style>
+ rect {
+ --length: 100;
+ }
+ </style>
+ <rect width="var(--length)" height="var(--length)" fill="green"/>
+</svg>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-03.svg b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-03.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+ <title>Usage of css var() with default fallback on svg width and height attributes (units specified)</title>
+ <html:link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com"/>
+ <html:link rel="match" href="../embedded/reference/green-rect-100x100.svg"/>
+ <rect width="var(--length,100px)" height="var(--length,100px)" fill="green"/>
+</svg>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-04.svg b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-04.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+ <title>Usage of css var() with default fallback on svg width and height attributes (units not specified)</title>
+ <html:link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com"/>
+ <html:link rel="match" href="../embedded/reference/green-rect-100x100.svg"/>
+ <rect width="var(--length,100)" height="var(--length,100)" fill="green"/>
+</svg>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-05.svg b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-05.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+ <title>Usage of css var() with when no substitution is provided</title>
+ <html:link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com"/>
+ <html:link rel="match" href="about:blank"/>
+ <rect width="var(--length)" height="var(--length)" fill="red"/>
+</svg>
diff --git a/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-06.html b/testing/web-platform/tests/svg/styling/css-var-on-length-attributes-06.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Usage of css var() on circle radius attribute (units specified)</title>
+<link rel="author" title="Divyansh Mangal" href="mailto:dmangal@microsoft.com">
+<link rel="match" href="css-linked-parameters/circle-green-ref.html">
+<style>
+ circle {
+ --radii: 48px
+ }
+</style>
+<svg width="100px" height="100px">
+ <circle cx="50px" cy="50px" r="var(--radii)" fill="green" stroke-width="2"/>
+</svg>