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,12 @@
Harness status: OK
Found 6 tests
5 Pass
1 Fail
Pass registerProperty requires a Dictionary type
Pass registerProperty requires a name matching <custom-property-name>
Pass registerProperty only allows omitting initialValue if syntax is '*'
Pass registerProperty fails for an already registered property
Pass registerProperty requires inherits
Fail Registering a property should not cause a transition

View file

@ -0,0 +1,14 @@
Harness status: OK
Found 8 tests
2 Pass
6 Fail
Fail Registered properties are correctly inherited (or not) depending on the inherits flag.
Fail Explicitly inheriting from a parent with an invalid value results in initial value.
Pass Explicitly inheriting from a parent with no value results in initial value.
Pass Explicitly inheriting from a parent with a value results in that value.
Fail Reference to undefined variable results in inherited value
Fail Reference to syntax-incompatible variable results in inherited value
Fail Font-relative units are absolutized before before inheritance
Fail Calc expressions are resolved before inheritance

View file

@ -0,0 +1,11 @@
Harness status: OK
Found 5 tests
2 Pass
3 Fail
Fail A var() cycle between two registered properties is handled correctly.
Fail A var() cycle between a registered properties and an unregistered property is handled correctly.
Fail A var() cycle between a two unregistered properties is handled correctly.
Pass A var() cycle between a syntax:'*' property and an unregistered property is handled correctly.
Pass Custom properties with universal syntax become guaranteed-invalid when invalid at computed-value time

View file

@ -0,0 +1,22 @@
Harness status: OK
Found 16 tests
7 Pass
9 Fail
Fail var() references work with registered properties
Fail References to registered var()-properties work in registered lists
Fail References to mixed registered and unregistered var()-properties work in registered lists
Fail Registered lists may be concatenated
Fail Font-relative units are absolutized when substituting
Fail Calc expressions are resolved when substituting
Fail Lists with relative units are absolutized when substituting
Fail Values are absolutized when substituting into properties with universal syntax
Fail Invalid values for registered properties are serialized as the empty string
Pass Valid fallback does not invalidate var()-reference [<length>, 10px]
Pass Valid fallback does not invalidate var()-reference [<length> | <color>, red]
Pass Valid fallback does not invalidate var()-reference [<length> | none, none]
Pass Invalid fallback doesn't invalidate var()-reference [<length>, red]
Pass Invalid fallback doesn't invalidate var()-reference [<length> | none, nolength]
Pass Invalid fallback doesn't invalidate var()-reference [<length>, var(--novar)]
Pass Empty universal custom property can be substituted with var()

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>