Tests: Import attr()-related WPT tests

This commit is contained in:
Sam Atkins
2025-07-16 13:58:19 +01:00
committed by Tim Ledbetter
parent 72a7a18502
commit 5d1ba658c9
Notes: github-actions[bot] 2025-07-16 13:49:19 +00:00
24 changed files with 958 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<title>CSS Values Test: attr() IACVT</title>
<meta name="assert" content="Fail to substitute attr() should be IACVT.">
<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notation">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#expected {
background-color: red;
}
#test {
background-color: red;
background-color: attr(data-color type(<color>));
}
</style>
<div id="test" data-color=333></div>
<div id="expected"></div>
<script>
var elem = document.getElementById("test");
var value = window.getComputedStyle(elem).getPropertyValue("background-color");
var expected_elem = document.getElementById("expected");
var expected_value = window.getComputedStyle(expected_elem).getPropertyValue("background-color");
test(() => {
assert_not_equals(value, expected_value);
});
</script>

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<title>CSS Values and Units Test: attr() in container style queries</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-queries">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#container {
--foo: bar;
}
@container style(--foo: attr(data-foo type(<custom-ident>))) {
#target { color: green; }
}
</style>
<div id="container" data-foo="bar">
<div id="target">Should be green</div>
</div>
<script>
test(() => {
assert_equals(getComputedStyle(target).color, "rgb(0, 128, 0)");
}, "style query should implement to true");
</script>

View File

@@ -0,0 +1,124 @@
<!DOCTYPE html>
<title>CSS Values and Units Test: attr</title>
<meta name="assert" content="test attr values">
<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notations">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div id="attr"></div>
<div id="expected"></div>
<script>
function test_attr_cycle(property, propertyValue, attrValue) {
var elem = document.getElementById("attr");
var expectedValue = window.getComputedStyle(elem).getPropertyValue(property);
elem.setAttribute("data-foo", attrValue);
elem.style.setProperty(property, propertyValue);
test(() => {
assert_equals(window.getComputedStyle(elem).getPropertyValue(property), expectedValue,
"Setting property \'" + property + "\' to the value \'" + propertyValue +
"\', where \'data-foo=" + attrValue + "\' should not change it's value.");
});
elem.style.setProperty(property, null);
}
function test_attr_no_cycle(property, propertyValue, attrValue, expectedValue) {
var elem = document.getElementById("attr");
elem.setAttribute("data-foo", attrValue);
elem.style.setProperty(property, propertyValue);
var expectedElem = document.getElementById("expected");
expectedElem.style.setProperty(property, expectedValue);
test(() => {
assert_equals(window.getComputedStyle(elem).getPropertyValue(property),
window.getComputedStyle(expectedElem).getPropertyValue(property),
"Value \'" + propertyValue + "\', where \'data-foo=" + attrValue +
"\' should be valid for the property \'" + property + "\'.");
});
elem.style.setProperty(property, null);
expectedElem.style.setProperty(property, null);
}
/* Simple cycle */
test_attr_cycle('--x', 'attr(data-foo type(<ident>))', 'attr(data-foo)');
test_attr_cycle('--x', 'attr(data-foo type(<length>))', 'attr(data-foo type(<length>))');
test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-foo type(*))');
var attrElem = document.getElementById("attr");
attrElem.setAttribute('data-bar', 'attr(data-foo)');
test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*))');
attrElem.removeAttribute('data-bar');
/* Cycle with attr() and var() */
attrElem.style.setProperty('--x', 'attr(data-foo type(*))');
test_attr_cycle('--y', 'attr(data-foo type(*))', 'var(--x)');
attrElem.style.setProperty('--x', null);
attrElem.setAttribute('data-bar', 'var(--y)');
test_attr_cycle('--y', 'attr(data-foo type(<string>))', 'attr(data-bar type(<string>))');
attrElem.removeAttribute('data-bar');
attrElem.setAttribute('data-bar', 'var(--x)');
test_attr_cycle('--x', 'attr(data-foo type(*), attr(data-bar))', 'attr(data-foo type(*))');
attrElem.removeAttribute('data-bar');
attrElem.style.setProperty('--x', 'attr(data-foo type(*))');
attrElem.setAttribute('data-bar', 'var(--x)');
test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*), 11) var(--x)');
attrElem.style.setProperty('--x', null);
attrElem.removeAttribute('data-bar');
/* Cycle with fallback */
test_attr_cycle('--x', 'attr(data-foo type(<length>), 11px)', 'attr(data-foo type(<length>))');
test_attr_cycle('--x', 'attr(data-foo type(<length>))', 'attr(data-foo type(<length>), 11px)');
test_attr_cycle('--y', 'attr(data-foo type(*), 11px)', 'attr(data-foo type(*))');
attrElem.setAttribute('data-bar', '11px');
test_attr_cycle('--x', 'attr(data-foo type(<length>), attr(data-bar type(<length>)))', 'attr(data-foo type(*))');
attrElem.removeAttribute('data-bar');
attrElem.setAttribute('data-bar', 'abc');
test_attr_cycle('--y', 'attr(data-foo type(*), attr(data-bar))', 'attr(data-foo type(*))');
attrElem.removeAttribute('data-bar');
/* Cycle with var() and fallback */
attrElem.style.setProperty('--x', 'var(--y)');
test_attr_cycle('--y', 'var(--x, 100)', 'var(--y)');
attrElem.style.setProperty('--x', null);
attrElem.setAttribute('data-bar', 'var(--y)');
attrElem.style.setProperty('--x', 'attr(data-foo)');
test_attr_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar type(*), 11) var(--x, 3)');
attrElem.style.setProperty('--x', null);
attrElem.removeAttribute('data-bar');
/* Cycle in unused fallbacks */
test_attr_no_cycle('--y', 'attr(data-foo type(*), var(--y))', '3', '3');
test_attr_no_cycle('--y', 'attr(data-foo type(*), attr(data-foo))', '3', '3');
attrElem.style.setProperty('--x', 'var(--y)');
test_attr_no_cycle('--y', 'attr(data-foo type(*), var(--x))', '3', '3');
attrElem.style.setProperty('--x', null);
attrElem.setAttribute('data-bar', 'attr(data-foo type(*))');
test_attr_no_cycle('--y', 'attr(data-foo type(*), attr(data-bar type(*)))', '3', '3');
attrElem.removeAttribute('data-bar');
/* Cycle in fallback */
test_attr_cycle('--y', 'attr(data-unknown type(*), var(--y))', '3');
/* No cycle, use raw CSS string without substitution */
attrElem.setAttribute('data-bar', 'var(--y)');
test_attr_no_cycle('--y', 'attr(data-foo type(<string>))', 'attr(data-bar type(<string>))', '');
attrElem.removeAttribute('data-bar');
attrElem.style.setProperty('--x', 'attr(data-foo)');
attrElem.setAttribute('data-bar', 'var(--x)');
test_attr_no_cycle('--y', 'attr(data-foo type(*))', 'attr(data-bar, 11) var(--x, 3)', '"var(--x)" "attr(data-bar, 11) var(--x, 3)"');
attrElem.removeAttribute('data-bar');
attrElem.style.setProperty('--x', null);
</script>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<title>CSS Values and Units Test: attr() invalidation</title>
<meta name="assert" content="Test attr() invalidation">
<link rel="help" href="https://drafts.csswg.org/css-values/#attr-notation">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
div {
width: attr(data-foo type(<length>));
}
</style>
<html>
<body>
<div id="div" data-foo="10px"></div>
</body>
</html>
<script>
setup({ single_test: true });
let elem = document.getElementById("div");
let old_width = window.getComputedStyle(elem).getPropertyValue("width");
elem.setAttribute("data-foo", "30px");
let new_width = window.getComputedStyle(elem).getPropertyValue("width");
assert_not_equals(new_width, old_width);
done();
</script>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:nsfoo="http://nsfoo.org"
xmlns:nsbar="http://nsbar.org">
<head>
<title>CSS Values: attr() substitution with implicit null namespace</title>
<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notation"/>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="target"
nsfoo:data-x="value1"
nsbar:data-x="value2"
data-x="value3"
nsfoo:data-y="value4"
nsbar:data-y="value5">
Test
</div>
<style>
#target {
--x: attr(data-x type(*), fallback);
--y: attr(data-y type(*), fallback);
--z: attr(data-z type(*), fallback);
--w: attr(data-w type(*), fallback);
}
</style>
<script>
test(() => {
let e = document.getElementById("target");
assert_equals("value1", e.getAttributeNS("http://nsfoo.org", "data-x"));
assert_equals("value2", e.getAttributeNS("http://nsbar.org", "data-x"));
assert_equals("value3", e.getAttributeNS(null, "data-x"));
assert_equals("value4", e.getAttributeNS("http://nsfoo.org", "data-y"));
assert_equals("value5", e.getAttributeNS("http://nsbar.org", "data-y"));
assert_equals(null, e.getAttributeNS(null, "data-y"));
}, "Sanity check");
test(() => {
let e = document.getElementById("target");
assert_equals(getComputedStyle(e).getPropertyValue("--x"), "value3");
}, "Attribute in null-namespace is substituted");
test(() => {
let e = document.getElementById("target");
assert_equals(getComputedStyle(e).getPropertyValue("--y"), "fallback");
}, "Fallback is taken when attribute does not exist in null-namespace");
test((t) => {
let e = document.getElementById("target");
t.add_cleanup(() => {
e.removeAttributeNS("http://nsfoo.org", "data-z");
e.removeAttributeNS("http://nsbar.org", "data-z");
e.removeAttributeNS(null, "data-z");
});
e.setAttributeNS("http://nsfoo.org", "data-z", "value6");
e.setAttributeNS("http://nsbar.org", "data-z", "value7");
e.setAttributeNS(null, "data-z", "value8");
assert_equals(getComputedStyle(e).getPropertyValue("--z"), "value8");
}, "Attribute in null-namespace is substituted (JS)");
test((t) => {
let e = document.getElementById("target");
t.add_cleanup(() => {
e.removeAttributeNS("http://nsfoo.org", "data-w");
e.removeAttributeNS("http://nsbar.org", "data-w");
});
e.setAttributeNS("http://nsfoo.org", "data-w", "value9");
e.setAttributeNS("http://nsbar.org", "data-w", "value10");
assert_equals(getComputedStyle(e).getPropertyValue("--w"), "fallback");
}, "Fallback is taken when attribute does not does exist in null-namespace (JS)");
</script>
</body>
</html>

View File

@@ -0,0 +1,272 @@
<!DOCTYPE html>
<title>CSS Values and Units Test: attr() security limitations</title>
<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-security">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@property --some-string {
syntax: "<string>";
inherits: false;
initial-value: "empty";
}
@property --some-string-list {
syntax: "<string>+";
inherits: false;
initial-value: "empty";
}
div {
--condition-val: 3;
--str: text;
--true: true;
--some-string: attr(data-foo);
--some-string-list: "https://does-not-exist2.test/404.png" attr(data-foo);
--some-other-url: attr(data-foo);
--image-set-valid: url("https://does-not-exist.test/404.png") type(attr(data-foo));
--image-set-invalid: attr(data-foo type(<url>)) 1x;
}
</style>
<html>
<body>
<div id="attr"></div>
</body>
</html>
<script>
function test_attr(property, attrString, attrValue, expectedValue) {
var elem = document.getElementById("attr");
elem.setAttribute("data-foo", attrValue);
elem.style.setProperty(property, attrString);
test(() => {
assert_equals(window.getComputedStyle(elem).getPropertyValue(property),
expectedValue);
}, `'${property}: ${attrString}' with data-foo="${attrValue}"`);
elem.style.setProperty(property, null);
}
function test_registered_custom_property(customPropertyName, customPropertySyntax, customPropertyInitialValue,
attrValue, expectedValue) {
window.CSS.registerProperty({
name: customPropertyName,
syntax: customPropertySyntax,
inherits: false,
initialValue: customPropertyInitialValue,
});
var elem = document.getElementById("attr");
elem.setAttribute("data-foo", attrValue);
var attrString = "attr(data-foo type(" + customPropertySyntax + "))";
elem.style.setProperty(customPropertyName, attrString);
test(() => {
assert_equals(window.getComputedStyle(elem).getPropertyValue(customPropertyName),
expectedValue);
}, `'${customPropertyName}: ${attrString}' with data-foo="${attrValue}"`);
elem.style.setProperty(customPropertyName, null);
}
// Direct use.
test_attr('--x',
'image-set(attr(data-foo))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(attr(data-foo))',
'https://does-not-exist.test/404.png',
'none');
test_attr('background-image',
'image-set("https://does-not-exist.test/404.png")',
'https://does-not-exist.test/404.png',
'image-set(url("https://does-not-exist.test/404.png") 1dppx)');
test_attr('--x',
'src(attr(data-foo))',
'https://does-not-exist.test/404.png',
'src("https://does-not-exist.test/404.png")');
test_attr('background-image',
'src(attr(data-foo))',
'https://does-not-exist.test/404.png',
'none');
test_attr('background-image',
'src("https://does-not-exist.test/404.png")',
'https://does-not-exist.test/404.png',
'src(url("https://does-not-exist.test/404.png"))');
// The following string() function is under discussion in the working group and does not exist yet.
test_attr('--x',
'src(string("https://does-not-exist.test" attr(data-foo)))',
'/404.png',
'src(string("https://does-not-exist.test" "/404.png"))');
test_attr('background-image',
'src(string("https://does-not-exist.test" attr(data-foo)))',
'/404.png',
'none');
test_attr('background-image',
'src(string("https://does-not-exist.test/""404.png"))',
'/404.png',
'src(url("https://does-not-exist.test/404.png"))');
test_attr('--x',
'attr(data-foo type(<url>))',
'url(https://does-not-exist.test/404.png)',
'url("https://does-not-exist.test/404.png")');
test_attr('background-image',
'attr(data-foo type(<url>))',
'url(https://does-not-exist.test/404.png)',
'none');
test_attr('background-image',
'url("https://does-not-exist.test/404.png")',
'url(https://does-not-exist.test/404.png)',
'url("https://does-not-exist.test/404.png")');
test_attr('--x',
'image(attr(data-foo))',
'https://does-not-exist.test/404.png',
'image("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image(attr(data-foo))',
'https://does-not-exist.test/404.png',
'none');
test_attr('background-image',
'image("https://does-not-exist.test/404.png")',
'https://does-not-exist.test/404.png',
'image(url("https://does-not-exist.test/404.png"))');
test_attr('background-image',
'url(https://does-not-exist.test/404.png), attr(data-foo type(<image>))',
'linear-gradient(#000000, #ffffff)',
'url("https://does-not-exist.test/404.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 255, 255))');
// The remaining tests use image-set(), but should be equivalent for image() etc.
// Test in a fallback.
test_attr('--x',
'image-set(var(--y, attr(data-foo)))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(var(--y, attr(data-foo)))',
'https://does-not-exist.test/404.png',
'none');
// Test via a registered custom property.
test_attr('--x',
'image-set(var(--some-string))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(var(--some-string))',
'https://does-not-exist.test/404.png',
'none');
// Test via a registered custom property (list).
test_attr('--x',
'image-set(var(--some-string-list))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist2.test/404.png" "https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(var(--some-string-list))',
'https://does-not-exist.test/404.png',
'none');
test_registered_custom_property('--registered-url', '<url>', 'url("https://does-not-exist.test/empty-url")', 'https://does-not-exist.test/404.png', 'url("https://does-not-exist.test/empty-url")');
test_registered_custom_property('--registered-color', '<color>', 'red', 'blue', 'rgb(0, 0, 255)');
// Test via a non-registered custom property.
test_attr('--x',
'image-set(var(--some-other-url))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(var(--some-other-url))',
'https://does-not-exist.test/404.png',
'none');
// Test multiple token substitution
test_attr('background-image',
'attr(data-foo type(*))',
'url(https://does-not-exist.test/404.png), linear-gradient(black, white)',
'none');
// Test total attr()-tainting for substitution values
test_attr('background-image',
'image-set(var(--image-set-valid))',
'image/jpeg',
'none');
test_attr('background-image',
'image-set(var(--image-set-invalid))',
'https://does-not-exist.test/404.png',
'none');
// Test attr-tainting carries through if() function.
test_attr('--x',
'image-set(if(style(--true): attr(data-foo);))',
'https://does-not-exist.test/404.png',
'image-set("https://does-not-exist.test/404.png")');
test_attr('background-image',
'image-set(if(style(--true): attr(data-foo);))',
'https://does-not-exist.test/404.png',
'none');
test_attr('background-image',
`image-set(
if(style(--true): url(https://does-not-exist-2.test/404.png);
else: attr(data-foo);))`,
'https://does-not-exist-2.test/404.png',
'image-set(url("https://does-not-exist-2.test/404.png") 1dppx)');
test_attr('background-image',
`image-set(
if(style(--some-string): url(https://does-not-exist.test/404.png);))`,
'https://does-not-exist.test/404.png',
'none');
test_attr('background-image',
`image-set(
if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'3',
'none');
test_attr('background-image',
`image-set(
if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png);
style(--true): url(https://does-not-exist.test/404.png);
else: url(https://does-not-exist.test/404.png);))`,
'1',
'none');
test_attr('background-image',
`image-set(if(style(--true): url(https://does-not-exist.test/404.png);
style(--condition-val): url(https://does-not-exist.test/404.png);
else: url(https://does-not-exist.test/404.png);))`,
'attr(data-foo type(*))',
'image-set(url("https://does-not-exist.test/404.png") 1dppx)');
test_attr('background-image',
`image-set(
if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`,
'3',
'none');
test_attr('--x',
`image-set(if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`,
'3',
'image-set(url(https://does-not-exist.test/404.png))');
test_attr('--x',
`image-set(if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'3',
'image-set(url(https://does-not-exist.test/404.png))');
test_attr('background-image',
`image-set(
if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'3',
'none');
test_attr('background-image',
`image-set(
if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'3',
'none');
test_attr('background-image',
`image-set(
if(style(--str < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'3',
'none');
test_attr('background-image',
`image-set(
if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`,
'text',
'none');
</script>

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<title>CSS Values and Units Test: attr() security limitations in universal selector </title>
<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-security">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@property --some-string {
syntax: "<string>";
inherits: true;
initial-value: "empty";
}
* {
--some-string: "https://does-not-exist.test/404.png";
}
div {
--some-string: attr(data-foo);
background-image: image-set(var(--some-string));
}
</style>
<html>
<body>
<div id="attr" data-foo="https://does-not-exist.test/404.png"></div>
</body>
</html>
<script>
var elem = document.getElementById("attr");
test(() => {
assert_equals(window.getComputedStyle(elem).getPropertyValue('background-image'),
'none',
"'background-image: attr(data-foo)' with data-foo='https://does-not-exist.test/404.png'");
});
</script>