Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 34 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /css/css-mixins/at-function-cssom.tentative.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<title>CSS Custom Functions: CSSOM</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {}
`);
assert_equals(sheet.cssRules.length, 1);
let functionRule = sheet.cssRules[0];
assert_true(functionRule instanceof CSSFunctionRule);
assert_equals(functionRule.cssRules.length, 0);
}, 'Empty CSSFunctionRule');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
result: 100px;
}
`);
assert_equals(sheet.cssRules.length, 1);
let functionRule = sheet.cssRules[0];
assert_true(functionRule instanceof CSSFunctionRule);
assert_equals(functionRule.cssRules.length, 1);
let declarationsRule = functionRule.cssRules[0];
assert_true(declarationsRule instanceof CSSFunctionDeclarations);
let descriptors = declarationsRule.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
}, 'Single CSSFunctionDeclarations');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
result: 100px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.result, '100px');
}, 'CSSFunctionDescriptors (result)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
result: 100px;
result: 101px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.result, '101px');
}, 'CSSFunctionDescriptors (result, repeated)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
--y: 2px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.getPropertyValue('--x'), '1px');
assert_equals(descriptors.getPropertyValue('--y'), '2px');
assert_equals(descriptors.getPropertyValue('--unknown'), '');
assert_equals(descriptors.result, '');
}, 'CSSFunctionDescriptors (local variables)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
--y: 2px;
--x: 3px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.getPropertyValue('--x'), '3px');
assert_equals(descriptors.getPropertyValue('--y'), '2px');
assert_equals(descriptors.getPropertyValue('--unknown'), '');
assert_equals(descriptors.result, '');
}, 'CSSFunctionDescriptors (local variables, repeated)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
--y: 2px;
result: 3px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.getPropertyValue('--x'), '1px');
assert_equals(descriptors.getPropertyValue('--y'), '2px');
assert_equals(descriptors.getPropertyValue('--unknown'), '');
assert_equals(descriptors.result, '3px');
}, 'CSSFunctionDescriptors (local variables and result)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
--y: 2px;
result: 3px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.cssText, '--x: 1px; --y: 2px; result: 3px;');
}, 'CSSFunctionDescriptors serialization');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
syntax: "<length>";
--y: 2px;
color: red;
unknown: unknown;
result: 3px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.length, 3);
assert_equals(descriptors.cssText, '--x: 1px; --y: 2px; result: 3px;');
}, 'Unknown descriptors');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.length, 1);
descriptors.cssText = '--x: 1px; color: red; result: 3px; --y: 2px;';
assert_equals(descriptors.length, 3);
assert_equals(descriptors.cssText, '--x: 1px; result: 3px; --y: 2px;');
}, 'Unknown descriptors (mutation)');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
result: 2px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.length, 2);
assert_equals(descriptors.item(0), '--x');
assert_equals(descriptors.item(1), 'result');
}, 'item()');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
--x: 1px;
result: 2px;
}
`);
let descriptors = sheet.cssRules[0]?.cssRules[0]?.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_array_equals(Array.from(descriptors), ['--x', 'result']);;
}, 'Indexed property getter');
// Conditional rules
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {
@supports (width: 100px) {
result: 100px;
}
result: 200px;
}
`);
assert_equals(sheet.cssRules.length, 1);
let functionRule = sheet.cssRules[0];
assert_true(functionRule instanceof CSSFunctionRule);
assert_equals(functionRule.cssRules.length, 2);
let supportsRule = functionRule.cssRules[0];
assert_true(supportsRule instanceof CSSSupportsRule);
assert_true(supportsRule.cssRules[0] instanceof CSSFunctionDeclarations);
assert_equals(supportsRule.cssRules[0].style?.result, '100px');
let declarationsRule = functionRule.cssRules[1];
assert_true(declarationsRule instanceof CSSFunctionDeclarations);
let descriptors = declarationsRule.style;
assert_true(descriptors instanceof CSSFunctionDescriptors);
assert_equals(descriptors.result, '200px');
}, '@supports in body');
// CSSFunctionRule
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --foo() {}
`);
assert_equals(sheet.cssRules.length, 1);
let functionRule = sheet.cssRules[0];
assert_true(functionRule instanceof CSSFunctionRule);
assert_equals(functionRule.name, '--foo');
}, 'CSSFunctionRule.name');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --f0() {}
@function --f1(--x) {}
@function --f2(--x <length>) {}
@function --f3(--x: 10px) {}
@function --f4(--x <length>: 10px) {}
@function --f5(--x type(<length> | auto): 10px, --y, --z: red) {}
`);
assert_equals(sheet.cssRules.length, 6);
assert_object_equals(sheet.cssRules[0].getParameters(), []);
assert_object_equals(sheet.cssRules[1].getParameters(), [
{name: '--x', type: '*'},
]);
assert_object_equals(sheet.cssRules[2].getParameters(), [
{name: '--x', type: '<length>'},
]);
assert_object_equals(sheet.cssRules[3].getParameters(), [
{name: '--x', type: '*', defaultValue: '10px'},
]);
assert_object_equals(sheet.cssRules[4].getParameters(), [
{name: '--x', type: '<length>', defaultValue: '10px'},
]);
assert_object_equals(sheet.cssRules[5].getParameters(), [
{name: '--x', type: '<length> | auto', defaultValue: '10px'},
{name: '--y', type: '*'},
{name: '--z', type: '*', defaultValue: 'red'},
], '--f5');
}, 'CSSFunctionRule.getParameters()');
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(`
@function --f0() {}
@function --f1() returns <length> {}
@function --f2() returns <length>+ {}
@function --f3() returns type(*) {}
@function --f4() returns type(<length> | auto) {}
`);
assert_equals(sheet.cssRules.length, 5);
assert_equals(sheet.cssRules[0]?.returnType, '*');
assert_equals(sheet.cssRules[1]?.returnType, '<length>');
assert_equals(sheet.cssRules[2]?.returnType, '<length>+');
assert_equals(sheet.cssRules[3]?.returnType, '*');
assert_equals(sheet.cssRules[4]?.returnType, '<length> | auto');
}, 'CSSFunctionRule.returnType');
test(t => {
let sheet = new CSSStyleSheet();
// U+0009 CHARACTER TABULATION
sheet.replaceSync(`@function --escaped-\\9 -tab(--param-\\9 -tab) { --local-\\9 -tab: 1px; }`);
assert_equals(sheet.cssRules.length, 1);
let rule = sheet.cssRules[0];
assert_equals(rule.name, '--escaped-\t-tab');
assert_equals(rule.getParameters()[0]?.name, '--param-\t-tab');
assert_equals(rule.cssRules[0].style.getPropertyValue('--local-\t-tab'), '1px');
}, 'CSSFunctionRule escapes');
function test_cssText(actual, expected) {
expected ??= actual;
let name = actual.match(/@function (--[-\w]+)/)[1];
test(t => {
let sheet = new CSSStyleSheet();
sheet.replaceSync(actual);
assert_equals(sheet.cssRules[0]?.cssText, expected);
}, `CSSFunctionRule.cssText (${name})`);
}
// The function name is used by the test description; please make sure it's
// unique among all test_cssText cases.
test_cssText(`@function --empty() { }`);
test_cssText(`@function --ret-length() returns <length> { }`);
test_cssText(`@function --ret-length-auto() returns type(<length> | auto) { }`);
test_cssText(`@function --param-single(--x) { }`);
test_cssText(`@function --param-typed(--x <length>) { }`);
test_cssText(`@function --param-typed-default(--x <length>: 10px) { }`);
test_cssText(`@function --param-default(--x: 10px) { }`);
test_cssText(`@function --param-multi(--x, --y) { }`);
test_cssText(`@function --param-multi-mixed(--x: 10px, --y, --z <length>) { }`);
test_cssText(`@function --body-result() { result: 10px; }`);
test_cssText(`@function --body-locals() { --x: 1px; --y: 2px; result: 10px; }`);
// Cases that don't round-trip as-is:
test_cssText(`@function --param-type-fn(--x type(<length>)) { }`,
`@function --param-type-fn(--x <length>) { }`);
test_cssText(`@function --param-type-fn-uni(--x type(*)) { }`,
`@function --param-type-fn-uni(--x) { }`);
test_cssText(`@function --ret-type-fn() returns type(*) { }`,
`@function --ret-type-fn() { }`);
test_cssText(`@function --ret-type-fn-uni() returns type(*) { }`,
`@function --ret-type-fn-uni() { }`);
test_cssText(`@function --body-result-multi() { result: 10px; result: 20px; }`,
`@function --body-result-multi() { result: 20px; }`);
// Escapes (U+0009 CHARACTER TABULATION):
test_cssText(`@function --escaped-\\9 -tab(--param-\\9 -tab) { --local-\\9 -tab: 1px; }`);
</script>