Tests: Import some css/css-properties-values-api WPT tests

Casual import of 4 WPT tests related to `CSS.registerProperty`
This commit is contained in:
norbiros 2025-07-21 13:14:07 +02:00 committed by Sam Atkins
commit ab574deb93
Notes: github-actions[bot] 2025-07-22 09:59:12 +00:00
9 changed files with 881 additions and 0 deletions

View file

@ -0,0 +1,75 @@
<!DOCTYPE HTML>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property" />
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="./resources/utils.js"></script>
<div id=target></div>
<script>
// Tests for error checking during property registration
test(function() {
assert_throws_js(TypeError, () => CSS.registerProperty());
assert_throws_js(TypeError, () => CSS.registerProperty(undefined));
assert_throws_js(TypeError, () => CSS.registerProperty(true));
assert_throws_js(TypeError, () => CSS.registerProperty(2));
assert_throws_js(TypeError, () => CSS.registerProperty("css"));
assert_throws_js(TypeError, () => CSS.registerProperty(null));
}, "registerProperty requires a Dictionary type");
test(function() {
// Valid property names, shouldn't throw
CSS.registerProperty({name: '--name1', inherits: false});
CSS.registerProperty({name: '--name2, no need for escapes', inherits: false});
CSS.registerProperty({name: ['--name', 3], inherits: false});
// Invalid property names
assert_throws_js(TypeError, () => CSS.registerProperty({}));
assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: 'no-leading-dash', inherits: false}));
assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: '', inherits: false}));
assert_throws_dom("SyntaxError", () => CSS.registerProperty({name: '\\--name', inherits: false}));
}, "registerProperty requires a name matching <custom-property-name>");
test(function() {
CSS.registerProperty({name: '--syntax-test-1', syntax: '*', inherits: false});
CSS.registerProperty({name: '--syntax-test-2', syntax: ' * ', inherits: false});
assert_throws_dom("SyntaxError",
() => CSS.registerProperty({name: '--syntax-test-3', syntax: 'length', inherits: false}));
}, "registerProperty only allows omitting initialValue if syntax is '*'");
test(function() {
CSS.registerProperty({name: '--re-register', syntax: '<length>', initialValue: '0px', inherits: false});
assert_throws_dom('InvalidModificationError',
() => CSS.registerProperty({name: '--re-register', syntax: '<percentage>', initialValue: '0%', inherits: false}));
}, "registerProperty fails for an already registered property");
test(function(){
CSS.registerProperty({name: '--inherit-test-1', syntax: '<length>', initialValue: '0px', inherits: true});
CSS.registerProperty({name: '--inherit-test-2', syntax: '<length>', initialValue: '0px', inherits: false});
assert_throws_js(TypeError, () => CSS.registerProperty({name: '--inherit-test-3', syntax: '<length>', initialValue: '0px'}));
}, "registerProperty requires inherits");
test(function(){
try {
let name = generate_name();
target.style.setProperty(name, 'green');
target.style.transitionProperty = name;
target.style.transitionDuration = '1s';
target.style.transitionTimingFunction = 'steps(1, end)';
assert_equals(getComputedStyle(target).getPropertyValue(name), 'green');
CSS.registerProperty({
name: name,
syntax: '<color>',
initialValue: 'red',
inherits: false
});
assert_equals(getComputedStyle(target).getPropertyValue(name), 'rgb(0, 128, 0)');
} finally {
target.style = '';
}
}, 'Registering a property should not cause a transition');
</script>

View file

@ -0,0 +1,96 @@
<!DOCTYPE HTML>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-propertydescriptor-inherits" />
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property" />
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#outer {
--inherited-length-1: 10px;
--inherited-length-2: var(--non-inherited-length-1);
--inherited-length-3: 30px;
--non-inherited-length-1: 22px;
--non-inherited-length-3: calc(var(--non-inherited-length-2) * 10);
}
#inner {
--inherited-length-3: 15px;
--non-inherited-length-1: 40px;
--non-inherited-length-2: 90px;
}
</style>
<div id=outer><div id=inner></div></div>
<script>
test(function() {
CSS.registerProperty({name: '--inherited-length-1', syntax: '<length>', initialValue: '1px', inherits: true});
CSS.registerProperty({name: '--inherited-length-2', syntax: '<length>', initialValue: '2px', inherits: true});
CSS.registerProperty({name: '--inherited-length-3', syntax: '<length>', initialValue: '3px', inherits: true});
CSS.registerProperty({name: '--non-inherited-length-1', syntax: '<length>', initialValue: '4px', inherits: false});
CSS.registerProperty({name: '--non-inherited-length-2', syntax: '<length>', initialValue: '5px', inherits: false});
CSS.registerProperty({name: '--non-inherited-length-3', syntax: '<length>', initialValue: '6px', inherits: false});
outerComputedStyle = getComputedStyle(outer);
assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-1'), '10px');
assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-2'), '22px');
assert_equals(outerComputedStyle.getPropertyValue('--inherited-length-3'), '30px');
assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-1'), '22px');
assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-2'), '5px');
assert_equals(outerComputedStyle.getPropertyValue('--non-inherited-length-3'), '50px');
innerComputedStyle = getComputedStyle(inner);
assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-1'), '10px');
assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-2'), '22px');
assert_equals(innerComputedStyle.getPropertyValue('--inherited-length-3'), '15px');
assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-1'), '40px');
assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-2'), '90px');
assert_equals(innerComputedStyle.getPropertyValue('--non-inherited-length-3'), '6px');
}, "Registered properties are correctly inherited (or not) depending on the inherits flag.");
test(function(){
CSS.registerProperty({name: '--initial-length-1', syntax: '<length>', initialValue: '0px', inherits: false});
outer.style = '--initial-length-1: notalength';
inner.style = '--initial-length-1: inherit';
assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-1'), '0px');
}, "Explicitly inheriting from a parent with an invalid value results in initial value.");
test(function(){
CSS.registerProperty({name: '--initial-length-2', syntax: '<length>', initialValue: '0px', inherits: false});
inner.style = '--initial-length-2: inherit';
assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-2'), '0px');
}, "Explicitly inheriting from a parent with no value results in initial value.");
test(function(){
CSS.registerProperty({name: '--initial-length-3', syntax: '<length>', initialValue: '0px', inherits: false});
outer.style = '--initial-length-3: 100px';
inner.style = '--initial-length-3: inherit';
assert_equals(getComputedStyle(inner).getPropertyValue('--initial-length-3'), '100px');
}, "Explicitly inheriting from a parent with a value results in that value.");
test(function(){
CSS.registerProperty({name: '--inherited-length-4', syntax: '<length>', initialValue: '0px', inherits: true});
outer.style = '--inherited-length-4: 42px';
inner.style = '--inherited-length-4: var(--undefined)';
assert_equals(getComputedStyle(inner).getPropertyValue('--inherited-length-4'), '42px');
}, "Reference to undefined variable results in inherited value");
test(function(){
CSS.registerProperty({name: '--inherited-length-5', syntax: '<length>', initialValue: '0px', inherits: true});
outer.style = '--inherited-length-5: 42px';
inner.style = '--incompatible: nolength; --inherited-length-5: var(--incompatible)';
assert_equals(getComputedStyle(inner).getPropertyValue('--inherited-length-5'), '42px');
}, "Reference to syntax-incompatible variable results in inherited value");
test(function(){
CSS.registerProperty({name: '--inherited-em', syntax: '<length>', initialValue: '0px', inherits: true});
outer.style = 'font-size: 11px; --inherited-em: 10em;';
inner.style = 'font-size: 22px; --unregistered:var(--inherited-em);';
assert_equals(getComputedStyle(inner).getPropertyValue('--unregistered'), '110px');
}, "Font-relative units are absolutized before before inheritance");
test(function(){
CSS.registerProperty({name: '--calc-length', syntax: '<length>', initialValue: '0px', inherits: true});
outer.style = '--calc-length: calc(10px + 10px);';
inner.style = '--unregistered:var(--calc-length);';
assert_equals(getComputedStyle(inner).getPropertyValue('--unregistered'), '20px');
}, "Calc expressions are resolved before inheritance");
</script>

View file

@ -0,0 +1,261 @@
let next_property_id = 1;
// Generate a unique property name on the form --prop-N.
function generate_name() {
return `--prop-${next_property_id++}`;
}
// Produce a compatible initial value for the specified syntax.
function any_initial_value(syntax) {
let components = syntax.split('|').map(x => x.trim())
let first_component = components[0];
if (first_component.endsWith('+') || first_component.endsWith('#'))
first_component = first_component.slice(0, -1);
switch (first_component) {
case '*':
case '<custom-ident>':
return 'NULL';
case '<angle>':
return '0deg';
case '<color>':
return 'rgb(0, 0, 0)';
case '<image>':
case '<url>':
return 'url(0)';
case '<integer>':
case '<length-percentage>':
case '<length>':
case '<number>':
return '0';
case '<percentage>':
return '0%';
case '<resolution>':
return '0dpi';
case '<time>':
return '0s';
case '<transform-function>':
case '<transform-list>':
return 'matrix(0, 0, 0, 0, 0, 0)';
default:
// We assume syntax is a specific custom ident.
return first_component;
}
}
// Registers a unique property on the form '--prop-N' and returns the name.
// Any value except 'syntax' may be omitted, in which case the property will
// not inherit, and some undefined (but compatible) initial value will be
// generated. If a single string is used as the argument, it is assumed to be
// the syntax.
function generate_property(reg) {
// Verify that only valid keys are specified. This prevents the caller from
// accidentally supplying 'inherited' instead of 'inherits', for example.
if (typeof(reg) === 'object') {
const permitted = new Set(['name', 'syntax', 'initialValue', 'inherits']);
if (!Object.keys(reg).every(k => permitted.has(k)))
throw new Error('generate_property: invalid parameter');
}
let syntax = typeof(reg) === 'string' ? reg : reg.syntax;
let initial = typeof(reg.initialValue) === 'undefined' ? any_initial_value(syntax)
: reg.initialValue;
let inherits = typeof(reg.inherits) === 'undefined' ? false : reg.inherits;
let name = generate_name();
CSS.registerProperty({
name: name,
syntax: syntax,
initialValue: initial,
inherits: inherits
});
return name;
}
function all_syntaxes() {
return [
'*',
'<angle>',
'<color>',
'<custom-ident>',
'<image>',
'<integer>',
'<length-percentage>',
'<length>',
'<number>',
'<percentage>',
'<resolution>',
'<time>',
'<transform-function>',
'<transform-list>',
'<url>'
]
}
function with_style_node(text, fn) {
let node = document.createElement('style');
node.textContent = text;
try {
document.body.append(node);
fn(node);
} finally {
node.remove();
}
}
function with_at_property(desc, fn) {
let name = typeof(desc.name) === 'undefined' ? generate_name() : desc.name;
let text = `@property ${name} {`;
if (typeof(desc.syntax) !== 'undefined')
text += `syntax:${desc.syntax};`;
if (typeof(desc.initialValue) !== 'undefined')
text += `initial-value:${desc.initialValue};`;
if (typeof(desc.inherits) !== 'undefined')
text += `inherits:${desc.inherits};`;
text += '}';
with_style_node(text, (node) => fn(name, node.sheet.rules[0]));
}
function test_with_at_property(desc, fn, description) {
test(() => with_at_property(desc, fn), description);
}
function test_with_style_node(text, fn, description) {
test(() => with_style_node(text, fn), description);
}
function animation_test(property, values, description) {
const name = generate_name();
property.name = name;
CSS.registerProperty(property);
test(() => {
const duration = 1000;
const keyframes = {};
keyframes[name] = values.keyframes;
const iterations = 3;
const composite = values.composite || "replace";
const iterationComposite = values.iterationComposite || "replace";
const animation = target.animate(keyframes, { composite, iterationComposite, iterations, duration });
animation.pause();
// We seek to the middle of the third iteration which will allow to test cases where
// iterationComposite is set to something other than "replace".
animation.currentTime = duration * 2.5;
const assert_equals_function = values.assert_function || assert_equals;
assert_equals_function(getComputedStyle(target).getPropertyValue(name), values.expected);
}, description);
};
function discrete_animation_test(syntax, fromValue, toValue, description) {
test(() => {
const name = generate_name();
CSS.registerProperty({
name,
syntax,
inherits: false,
initialValue: fromValue
});
const duration = 1000;
const keyframes = [];
keyframes[name] = toValue;
const animation = target.animate(keyframes, duration);
animation.pause();
const checkAtProgress = (progress, expected) => {
animation.currentTime = duration * 0.25;
assert_equals(getComputedStyle(target).getPropertyValue(name), fromValue, `The correct value is used at progress = ${progress}`);
};
checkAtProgress(0, fromValue);
checkAtProgress(0.25, fromValue);
checkAtProgress(0.49, fromValue);
checkAtProgress(0.5, toValue);
checkAtProgress(0.75, toValue);
checkAtProgress(1, toValue);
}, description || `Animating a custom property of type ${syntax} is discrete`);
}
function transition_test(options, description) {
promise_test(async () => {
const customProperty = generate_name();
options.transitionProperty ??= customProperty;
CSS.registerProperty({
name: customProperty,
syntax: options.syntax,
inherits: false,
initialValue: options.from
});
assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.from, "Element has the expected initial value");
const transitionEventPromise = new Promise(resolve => {
let listener = event => {
target.removeEventListener("transitionrun", listener);
assert_equals(event.propertyName, customProperty, "TransitionEvent has the expected property name");
resolve();
};
target.addEventListener("transitionrun", listener);
});
target.style.transition = `${options.transitionProperty} 1s -500ms linear`;
if (options.behavior) {
target.style.transitionBehavior = options.behavior;
}
target.style.setProperty(customProperty, options.to);
const animations = target.getAnimations();
assert_equals(animations.length, 1, "A single animation is running");
const transition = animations[0];
assert_class_string(transition, "CSSTransition", "A CSSTransition is running");
transition.pause();
assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.expected, "Element has the expected animated value");
await transitionEventPromise;
}, description);
}
function no_transition_test(options, description) {
test(() => {
const customProperty = generate_name();
CSS.registerProperty({
name: customProperty,
syntax: options.syntax,
inherits: false,
initialValue: options.from
});
assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.from, "Element has the expected initial value");
target.style.transition = `${customProperty} 1s -500ms linear`;
target.style.setProperty(customProperty, options.to);
assert_equals(target.getAnimations().length, 0, "No animation was created");
assert_equals(getComputedStyle(target).getPropertyValue(customProperty), options.to, "Element has the expected final value");
}, description);
};
function test_initial_value_valid(syntax, initialValue) {
// No actual assertions, this just shouldn't throw
test(() => {
var name = generate_name();
CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false});
}, "syntax:'" + syntax + "', initialValue:'" + initialValue + "' is valid");
}
function test_initial_value_invalid(syntax, initialValue) {
test(() =>{
var name = generate_name();
assert_throws_dom("SyntaxError",
() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false}));
}, "syntax:'" + syntax + "', initialValue:'" + initialValue + "' is invalid");
}

View file

@ -0,0 +1,183 @@
<!DOCTYPE HTML>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" />
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#test1 {
--registered-1-a: var(--registered-1-b, 10px);
--registered-1-b: var(--registered-1-a, 20px);
--registered-1-c: var(--registered-1-b, 30px);
--registered-1-d: var(--registered-1-b);
--unregistered-1-a:var(--registered-1-a,40px);
--unregistered-1-a:var(--registered-1-a);
left: var(--registered-1-a, 50px);
top: var(--registered-1-b, 60px);
}
</style>
<div id=test1></div>
<script>
test(function() {
CSS.registerProperty({name: '--registered-1-a', syntax: '<length>', initialValue: '1px', inherits: false});
CSS.registerProperty({name: '--registered-1-b', syntax: '<length>', initialValue: '2px', inherits: false});
CSS.registerProperty({name: '--registered-1-c', syntax: '<length>', initialValue: '3px', inherits: false});
CSS.registerProperty({name: '--registered-1-d', syntax: '<length>', initialValue: '4px', inherits: false});
computedStyle = getComputedStyle(test1);
assert_equals(computedStyle.getPropertyValue('--registered-1-a'), '1px');
assert_equals(computedStyle.getPropertyValue('--registered-1-b'), '2px');
assert_equals(computedStyle.getPropertyValue('--registered-1-c'), '2px');
assert_equals(computedStyle.getPropertyValue('--registered-1-d'), '2px');
assert_equals(computedStyle.getPropertyValue('--unregistered-1-a'), '1px');
assert_equals(computedStyle.left, '1px');
assert_equals(computedStyle.top, '2px');
}, "A var() cycle between two registered properties is handled correctly.");
</script>
<style>
#test2 {
--registered-2-a: var(--unregistered-2-a, 10px);
--unregistered-2-a:var(--registered-2-a,20px);
--registered-2-b: var(--registered-2-a, 30px);
--registered-2-c: var(--registered-2-a);
--registered-2-d: var(--unregistered-2-a, 40px);
--registered-2-e: var(--unregistered-2-a);
--unregistered-2-b:var(--registered-2-a,50px);
--unregistered-2-c:var(--registered-2-a);
--unregistered-2-d:var(--unregistered-2-a,60px);
--unregistered-2-e:var(--unregistered-2-a);
left: var(--registered-2-a, 70px);
top: var(--unregistered-2-a, 80px);
}
</style>
<div id=test2></div>
<script>
test(function() {
CSS.registerProperty({name: '--registered-2-a', syntax: '<length>', initialValue: '1px', inherits: false});
CSS.registerProperty({name: '--registered-2-b', syntax: '<length>', initialValue: '2px', inherits: false});
CSS.registerProperty({name: '--registered-2-c', syntax: '<length>', initialValue: '3px', inherits: false});
CSS.registerProperty({name: '--registered-2-d', syntax: '<length>', initialValue: '4px', inherits: false});
CSS.registerProperty({name: '--registered-2-e', syntax: '<length>', initialValue: '5px', inherits: false});
computedStyle = getComputedStyle(test2);
assert_equals(computedStyle.getPropertyValue('--registered-2-a'), '1px');
assert_equals(computedStyle.getPropertyValue('--unregistered-2-a'), '');
assert_equals(computedStyle.getPropertyValue('--registered-2-b'), '1px');
assert_equals(computedStyle.getPropertyValue('--registered-2-c'), '1px');
assert_equals(computedStyle.getPropertyValue('--registered-2-d'), '40px');
assert_equals(computedStyle.getPropertyValue('--registered-2-e'), '5px');
assert_equals(computedStyle.getPropertyValue('--unregistered-2-b'), '1px');
assert_equals(computedStyle.getPropertyValue('--unregistered-2-c'), '1px');
assert_equals(computedStyle.getPropertyValue('--unregistered-2-d'), '60px');
assert_equals(computedStyle.getPropertyValue('--unregistered-2-e'), '');
assert_equals(computedStyle.left, '1px');
assert_equals(computedStyle.top, '80px');
}, "A var() cycle between a registered properties and an unregistered property is handled correctly.");
</script>
<style>
#test3 {
--unregistered-3-a:var(--unregistered-3-b,10px);
--unregistered-3-b:var(--unregistered-3-a,20px);
--registered-3-a: var(--unregistered-3-a, 30px);
--registered-3-b: var(--unregistered-3-a);
--registered-3-c: var(--unregistered-3-b, 40px);
--registered-3-d: var(--registered-3-c, 50px);
left: var(--registered-3-d, 60px);
top: var(--registered-3-b, 70px);
}
</style>
<div id=test3></div>
<script>
test(function() {
CSS.registerProperty({name: '--registered-3-a', syntax: '<length>', initialValue: '1px', inherits: false});
CSS.registerProperty({name: '--registered-3-b', syntax: '<length>', initialValue: '2px', inherits: false});
CSS.registerProperty({name: '--registered-3-c', syntax: '<length>', initialValue: '3px', inherits: false});
CSS.registerProperty({name: '--registered-3-d', syntax: '<length>', initialValue: '4px', inherits: false});
computedStyle = getComputedStyle(test3);
assert_equals(computedStyle.getPropertyValue('--unregistered-3-a'), '');
assert_equals(computedStyle.getPropertyValue('--unregistered-3-b'), '');
assert_equals(computedStyle.getPropertyValue('--registered-3-a'), '30px');
assert_equals(computedStyle.getPropertyValue('--registered-3-b'), '2px');
assert_equals(computedStyle.getPropertyValue('--registered-3-c'), '40px');
assert_equals(computedStyle.getPropertyValue('--registered-3-d'), '40px');
assert_equals(computedStyle.left, '40px');
assert_equals(computedStyle.top, '2px');
}, "A var() cycle between a two unregistered properties is handled correctly.");
</script>
<style>
#test4 {
--registered-4-a:var(--unregistered-4-a,hello);
--unregistered-4-a:var(--registered-4-a,world);
--registered-4-b:var(--unregistered-4-a,meow);
--registered-4-c:var(--unregistered-4-a);
--unregistered-4-b:var(--unregistered-4-a,woof);
--unregistered-4-c:var(--unregistered-4-a);
transition-property: var(--registered-4-a, water);
}
</style>
<div id=test4></div>
<script>
test(function() {
CSS.registerProperty({name: '--registered-4-a', syntax: '*', inherits: false});
CSS.registerProperty({name: '--registered-4-b', syntax: '*', initialValue: 'moo', inherits: false});
CSS.registerProperty({name: '--registered-4-c', syntax: '*', initialValue: 'circle', inherits: false});
computedStyle = getComputedStyle(test4);
assert_equals(computedStyle.getPropertyValue('--registered-4-a'), '');
assert_equals(computedStyle.getPropertyValue('--unregistered-4-a'), '');
assert_equals(computedStyle.getPropertyValue('--registered-4-b'), 'meow');
assert_equals(computedStyle.getPropertyValue('--registered-4-c'), '');
assert_equals(computedStyle.getPropertyValue('--unregistered-4-b'), 'woof');
assert_equals(computedStyle.getPropertyValue('--unregistered-4-c'), '');
assert_equals(computedStyle.transitionProperty, 'water');
}, "A var() cycle between a syntax:'*' property and an unregistered property is handled correctly.");
</script>
<style>
#test5_parent {
--registered-5-c:foo;
--registered-5-d:bar;
--registered-5-e:baz;
color: green;
}
#test5 {
--registered-5-a:var(--registered-5-b,hello);
--registered-5-b:var(--registered-5-a,world);
--registered-5-c:var(--registered-5-a);
--registered-5-d:var(--registered-5-b);
--registered-5-e:var(--unknown);
color: var(--registered-5-e);
}
</style>
<div id=test5_parent>
<div id=test5></div>
</div>
<script>
test(function() {
CSS.registerProperty({name: '--registered-5-a', syntax: '*', inherits: true});
CSS.registerProperty({name: '--registered-5-b', syntax: '*', inherits: true});
CSS.registerProperty({name: '--registered-5-c', syntax: '*', inherits: true});
CSS.registerProperty({name: '--registered-5-d', syntax: '*', inherits: true});
CSS.registerProperty({name: '--registered-5-e', syntax: '*', inherits: true});
let computedStyle = getComputedStyle(test5);
assert_equals(computedStyle.getPropertyValue('--registered-5-a'), '');
assert_equals(computedStyle.getPropertyValue('--registered-5-b'), '');
assert_equals(computedStyle.getPropertyValue('--registered-5-c'), '');
assert_equals(computedStyle.getPropertyValue('--registered-5-d'), '');
assert_equals(computedStyle.getPropertyValue('--registered-5-e'), '');
assert_equals(computedStyle.getPropertyValue('color'), 'rgb(0, 128, 0)');
}, "Custom properties with universal syntax become guaranteed-invalid when " +
"invalid at computed-value time");
</script>

View file

@ -0,0 +1,207 @@
<!DOCTYPE HTML>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" />
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="./resources/utils.js"></script>
<style>
div {
--registered-length-1: 10px;
--registered-length-2: var(--registered-length-1);
--registered-length-3: var(--length-1);
--registered-length-4: calc(var(--length-1) + 40px);
--registered-length-5: var(--invalid, 70px);
--registered-length-6: calc(var(--registered-length-3)*4);
--registered-length-7: var(--123px, 6px);
--length-1: 20px;
--length-2: var(--registered-length-1);
--length-3: calc(var(--123px, 6px) + var(--123px));
--percentage: 10%;
--registered-length-invalid: var(--percentage);
--registered-token-stream-1:var(--invalid);
--registered-token-stream-2:var(--invalid,fallback);
--token-stream-1:var(--registered-token-stream-1,moo);
--registered-length-list-1: 1px, var(--registered-length-1), 2px;
--registered-length-list-2: 1px, var(--length-1), var(--registered-length-1), 2px;
--registered-length-list-3: var(--registered-length-list-1), var(--registered-length-list-2);
}
</style>
<div id=element></div>
<script>
test(function() {
CSS.registerProperty({name: '--123px', syntax: '<length>', initialValue: '123px', inherits: false});
CSS.registerProperty({name: '--registered-length-1', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-2', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-3', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-4', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-5', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-6', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-7', syntax: '<length>', initialValue: '0px', inherits: false});
CSS.registerProperty({name: '--registered-length-invalid', syntax: '<length>', initialValue: '15px', inherits: false});
CSS.registerProperty({name: '--registered-token-stream-1', syntax: '*', inherits: false});
CSS.registerProperty({name: '--registered-token-stream-2', syntax: '*', inherits: false});
computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--registered-length-1'), '10px');
assert_equals(computedStyle.getPropertyValue('--registered-length-2'), '10px');
assert_equals(computedStyle.getPropertyValue('--registered-length-3'), '20px');
assert_equals(computedStyle.getPropertyValue('--registered-length-4'), '60px');
assert_equals(computedStyle.getPropertyValue('--registered-length-5'), '70px');
assert_equals(computedStyle.getPropertyValue('--registered-length-6'), '80px');
assert_equals(computedStyle.getPropertyValue('--registered-length-7'), '123px');
assert_equals(computedStyle.getPropertyValue('--length-1'), '20px');
assert_equals(computedStyle.getPropertyValue('--length-2'), '10px');
assert_equals(computedStyle.getPropertyValue('--length-3'), 'calc(123px + 123px)');
assert_equals(computedStyle.getPropertyValue('--registered-length-invalid'), '15px');
assert_equals(computedStyle.getPropertyValue('--registered-token-stream-1'), '');
assert_equals(computedStyle.getPropertyValue('--registered-token-stream-2'), 'fallback');
assert_equals(computedStyle.getPropertyValue('--token-stream-1'), 'moo');
}, "var() references work with registered properties");
test(function(){
CSS.registerProperty({
name: '--registered-length-list-1',
syntax: '<length>#',
initialValue: '0px',
inherits: false
});
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--registered-length-list-1'), '1px, 10px, 2px');
}, 'References to registered var()-properties work in registered lists');
test(function(){
CSS.registerProperty({
name: '--registered-length-list-2',
syntax: '<length>#',
initialValue: '0px',
inherits: false
});
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--registered-length-list-2'), '1px, 20px, 10px, 2px');
}, 'References to mixed registered and unregistered var()-properties work in registered lists');
test(function(){
CSS.registerProperty({
name: '--registered-length-list-3',
syntax: '<length>#',
initialValue: '0px',
inherits: false
});
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--registered-length-list-3'), '1px, 10px, 2px, 1px, 20px, 10px, 2px');
}, 'Registered lists may be concatenated');
test(function(){
CSS.registerProperty({
name: '--length-em',
syntax: '<length>',
initialValue: '0px',
inherits: false
});
element.style = 'font-size: 11px; --length-em: 10em; --unregistered:var(--length-em);';
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px');
element.style = '';
}, 'Font-relative units are absolutized when substituting');
test(function(){
CSS.registerProperty({
name: '--length-calc',
syntax: '<length>',
initialValue: '0px',
inherits: false
});
element.style = 'font-size: 11px; --length-calc: calc(10em + 10px); --unregistered:var(--length-calc);';
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--unregistered'), '120px');
element.style = '';
}, 'Calc expressions are resolved when substituting');
test(function(){
CSS.registerProperty({
name: '--length-calc-list',
syntax: '<length>#',
initialValue: '0px',
inherits: false
});
element.style = 'font-size: 11px; --length-calc-list: 10em, calc(10em + 10px); --unregistered:var(--length-calc-list);';
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px, 120px');
element.style = '';
}, 'Lists with relative units are absolutized when substituting');
test(function(){
let length = generate_property('none | <length>');
let universal = generate_property('*');
element.style = `font-size: 10px; ${length}: 10em; ${universal}: var(${length})`;
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue(universal), '100px');
element.style = '';
}, 'Values are absolutized when substituting into properties with universal syntax');
test(function(){
let name = generate_property('<length>');
let unregistered = '--unregistered'
element.style = `${name}: red; ${unregistered}: var(${name})`
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue(unregistered), '');
}, 'Invalid values for registered properties are serialized as the empty string')
function test_valid_fallback(syntax, value, fallback) {
test(function(){
let name = generate_property(syntax);
try {
element.style = `${name}: ${value}; --x:var(${name},${fallback})`;
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--x'), value);
} finally {
element.style = '';
}
}, `Valid fallback does not invalidate var()-reference [${syntax}, ${fallback}]`);
}
function test_invalid_fallback(syntax, value, fallback) {
test(function(){
let name = generate_property(syntax);
try {
element.style = `${name}: ${value}; --x:var(${name},${fallback})`;
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('--x'), value);
} finally {
element.style = '';
}
}, `Invalid fallback doesn't invalidate var()-reference [${syntax}, ${fallback}]`);
}
test_valid_fallback('<length>', '40px', '10px');
test_valid_fallback('<length> | <color>', '40px', 'red');
test_valid_fallback('<length> | none', '40px', 'none');
test_invalid_fallback('<length>', '40px', 'red');
test_invalid_fallback('<length> | none', '40px', 'nolength');
test_invalid_fallback('<length>', '40px', 'var(--novar)');
test(function(t){
CSS.registerProperty({
name: '--registered-universal-no-initial',
syntax: '*',
inherits: false
});
t.add_cleanup(() => {
element.style = '';
});
element.style = [
'--registered-universal-no-initial:;',
'background-color: var(--registered-universal-no-initial) green',
].join(';');
let computedStyle = getComputedStyle(element);
assert_equals(computedStyle.getPropertyValue('background-color'), 'rgb(0, 128, 0)');
}, 'Empty universal custom property can be substituted with var()');
</script>