Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<title>CSS Values: ident() as an arbitrary substitution function</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="target" data-name="myname" data-suffix="-suffix"></div>
<div id="siblings"><div></div><div></div><div id="third-child"></div></div>
<script>
"use strict";
// ident() is substituted at computed-value time, so its argument may contain other substitution
// functions and the identifier it produces may itself name a custom property.
const target = document.getElementById("target");
function computed(declaration, property) {
target.setAttribute("style", declaration);
const value = getComputedStyle(target).getPropertyValue(property);
target.removeAttribute("style");
return value;
}
test(() => {
assert_equals(computed('--part: "name"; --result: ident("my" var(--part));', "--result"), "myname");
}, "var() in the ident() argument");
test(() => {
assert_equals(computed('--result: ident("my" attr(data-name type(*)));', "--result"), "mymyname");
}, "attr() in the ident() argument");
test(() => {
assert_equals(computed('--result: ident(ident("my") ident("name"));', "--result"), "myname");
}, "ident() in the ident() argument");
test(() => {
assert_equals(computed('--myname: PASS; --result: var(ident("--" attr(data-name type(*))));', "--result"), "PASS");
}, "ident() building the var() name argument from an attribute");
// <integer> parts include math functions, and the tree-counting ones resolve against the element
// the value is being computed for.
test(() => {
const third = document.getElementById("third-child");
third.style.viewTransitionName = 'ident("vtl-" sibling-index())';
assert_equals(getComputedStyle(third).viewTransitionName, "vtl-3");
third.removeAttribute("style");
}, "sibling-index() in the ident() argument");
test(() => {
const third = document.getElementById("third-child");
third.style.viewTransitionName = 'ident("vtl-" calc(sibling-index() * 2))';
assert_equals(getComputedStyle(third).viewTransitionName, "vtl-6");
third.removeAttribute("style");
}, "a math function over sibling-index() in the ident() argument");
test(() => {
assert_equals(computed('--result: ident(var(--missing));', "--result"), "");
}, "an unresolvable argument makes ident() invalid at computed-value time");
test(() => {
assert_equals(computed('--result: ident(5px);', "--result"), "");
}, "an argument that is not <ident-arg>+ makes ident() invalid at computed-value time");
// A cycle through the argument leaves the property guaranteed-invalid, it does not hang.
test(() => {
assert_equals(computed('--result: ident("x" var(--result));', "--result"), "");
}, "a cycle through the ident() argument");
// An identifier built from an attribute taints the value it substitutes into, so picking which
// property to substitute cannot smuggle out a URL the attribute never contained.
test(() => {
assert_equals(computed(`--myname: url("${url}"); background-image: var(ident("--" attr(data-name type(*))));`,
"background-image"), "none");
}, "an ident() built from an attribute taints the property it names");
test(() => {
assert_equals(computed(`--myname: url("${url}"); --n: ident("--" attr(data-name type(*))); background-image: var(var(--n));`,
"background-image"), "none");
}, "the taint survives the ident() being held in a custom property");
test(() => {
assert_equals(computed(`--myname: url("${url}"); background-image: var(ident("--" "myname"));`,
"background-image"), `url("${url}")`);
}, "an ident() with no attr() involved does not taint the property it names");
// A value the property cannot accept is invalid at computed-value time, which behaves as unset
// rather than falling back to the earlier declaration.
test(() => {
assert_equals(computed('width: 10px; width: ident("wid" "th");', "width"),
computed('width: 10px; width: unset;', "width"));
}, "ident() substituting a value the property does not accept");
</script>