Source code

Revision control

Copy as Markdown

Other Tools

<!DOCTYPE html>
<title>Custom Functions: Evaluating a &lt;dashed-function></title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<div id=parent>
<div id=target></div>
</div>
<div id=main></div>
<!-- To pass, a test must produce matching computed values for --actual and
--expected on #target. -->
<!-- Return value -->
<template data-name="Literal result">
<style>
@function --f() {
result: 12px;
}
#target {
--actual: --f();
--expected: 12px;
}
</style>
</template>
<template data-name="Literal result, typed return">
<style>
@function --f() returns <length> {
result: 12px;
}
#target {
--actual: --f();
--expected: 12px;
}
</style>
</template>
<template data-name="Literal result, typed return, calc">
<style>
@function --f() returns <length> {
result: calc(12px + 1px);
}
#target {
--actual: --f();
--expected: 13px;
}
</style>
</template>
<template data-name="Literal result, typed return, mismatch">
<style>
@function --f() returns <length> {
result: 12s;
}
#target {
--actual: --f();
/* --expected: <guaranteed-invalid> */
}
</style>
</template>
<template data-name="Missing result descriptor">
<style>
@function --f() {
}
#target {
--actual: --f();
/* --expected: <guaranteed-invalid> */
}
</style>
</template>
<template data-name="Literal result, empty">
<style>
@function --f() {
result:;
}
#target {
--actual: --f();
--expected:;
}
</style>
</template>
<template data-name="result cascading behavior">
<style>
@function --f() returns <length> {
result: 12px;
result: 24px; /* Overwrites previous */
}
#target {
--actual: --f();
--expected: 24px;
}
</style>
</template>
<template data-name="Another dashed-function in result">
<style>
@function --f() {
result: --g();
}
@function --g() {
result: 12px;
}
#target {
--actual: --f();
--expected: 12px;
}
</style>
</template>
<!-- Parameters / Arguments -->
<template data-name="Unused argument">
<style>
@function --f(--x) {
result: 12px;
}
#target {
--actual: --f(100px);
--expected: 12px;
}
</style>
</template>
<template data-name="Single parameter">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--actual: --f(100px);
--expected: 100px;
}
</style>
</template>
<template data-name="Multiple parameters">
<style>
@function --f(--x, --y, --z) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(100px, auto, red);
--expected: 100px auto red;
}
</style>
</template>
<template data-name="Single parameter, typed">
<style>
@function --f(--x <length>) {
result: var(--x);
}
#target {
--actual: --f(100px);
--expected: 100px;
}
</style>
</template>
<template data-name="Typed parameter with calc()">
<style>
@function --f(--x <length>) {
result: var(--x);
}
#target {
--actual: --f(calc(100px + 1px));
--expected: 101px;
}
</style>
</template>
<template data-name="Untyped parameter with calc()">
<style>
@function --f(--x type(*)) {
result: var(--x);
}
#target {
--actual: --f(calc(100px + 1px));
--expected: calc(100px + 1px);
}
</style>
</template>
<template data-name="Various typed parameters">
<style>
@function --f(--x <length>, --y <angle>, --z <time>) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(calc(100px + 1px), 1turn, 1000ms);
--expected: 101px 360deg 1s;
}
</style>
</template>
<template data-name="Parameter with complex type (auto)">
<style>
@function --f(--x type(<length> | auto)) {
result: var(--x);
}
#target {
--actual: --f(auto);
--expected: auto;
}
</style>
</template>
<template data-name="Parameter with complex type (px)">
<style>
@function --f(--x type(<length> | auto)) {
result: var(--x);
}
#target {
--actual: --f(10px);
--expected: 10px;
}
</style>
</template>
<template data-name="Passing argument to inner function">
<style>
@function --f(--x) {
result: --g(var(--x));
}
@function --g(--y) {
result: var(--y);
}
#target {
--actual: --f(12px);
--expected: 12px;
}
</style>
</template>
<!-- Arguments + var() -->
<template data-name="var() in argument resolved before call">
<style>
@function --f(--x) {
--one: FAIL;
result: var(--x);
}
#target {
--one: 1px;
--actual: --f(calc(100px + var(--one)));
--expected: calc(100px + 1px);
}
</style>
</template>
<template data-name="var() in argument resolved before call, typed">
<style>
@function --f(--x <length>) {
--one: FAIL;
result: var(--x);
}
#target {
--one: 1px;
--actual: --f(calc(100px + var(--one)));
--expected: 101px;
}
</style>
</template>
<template data-name="Argument captures IACVT due to invalid var()">
<style>
@function --f(--x) {
result: var(--x, PASS);
}
#target {
--actual: --f(var(--unknown));
--expected: PASS;
}
</style>
</template>
<template data-name="Argument captures IACVT due to invalid var(), typed">
<style>
@function --f(--x <length>) {
result: var(--x, PASS);
}
#target {
--actual: --f(var(--unknown));
--expected: PASS;
}
</style>
</template>
<template data-name="Argument captures IACVT due to type mismatch">
<style>
@function --f(--x <length>) {
result: var(--x, PASS);
}
#target {
--actual: --f(red);
--expected: PASS;
}
</style>
</template>
<!-- Defaults -->
<template data-name="Single parameter with default value">
<style>
@function --f(--x: PASS) {
result: var(--x);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Multiple parameters with defaults">
<style>
@function --f(--x, --y: 2px, --z: 3px) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(1px, 5px);
--expected: 1px 5px 3px;
}
</style>
</template>
<template data-name="Multiple parameters with defaults, typed">
<style>
@function --f(--x, --y <length>: 2px, --z <length>: 3px) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(1px, 5px);
--expected: 1px 5px 3px;
}
</style>
</template>
<template data-name="Default referencing another parameter">
<style>
@function --f(--x, --y: var(--x)) {
result: var(--x) var(--y);
}
#target {
--x: FAIL;
--y: FAIL;
--actual: --f(5px);
--expected: 5px 5px;
}
</style>
</template>
<template data-name="Default referencing another parameter, local interference">
<style>
@function --f(--x, --y: var(--x)) {
--x: 17px;
result: var(--x) var(--y);
}
#target {
--x: FAIL;
--y: FAIL;
--actual: --f(5px);
--expected: 17px 5px;
}
</style>
</template>
<template data-name="Default referencing another defaulted parameter">
<style>
@function --f(--x: 5px, --y: var(--x)) {
result: var(--x) var(--y);
}
#target {
--x: FAIL;
--y: FAIL;
--actual: --f();
--expected: 5px 5px;
}
</style>
</template>
<template data-name="Typed default with reference">
<style>
@function --f(--x: 5px, --y <length>: calc(var(--x) + 1px)) {
result: var(--x) var(--y);
}
#target {
--x: FAIL;
--y: FAIL;
--actual: --f();
--expected: 5px 6px;
}
</style>
</template>
<template data-name="IACVT arguments are defaulted">
<style>
@function --f(--x: 1, --y, --z: 3) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(var(--unknown), 2, var(--unknown));
--expected: 1 2 3;
}
</style>
</template>
<template data-name="IACVT arguments are defaulted, typed">
<style>
@function --f(--x <number>: 1, --y <number>, --z <number>: 3) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(var(--unknown), 2, var(--unknown));
--expected: 1 2 3;
}
</style>
</template>
<template data-name="Arguments are defaulted on type mismatch">
<style>
@function --f(--x <number>: 1, --y <number>, --z <number>: 3) {
result: var(--x) var(--y) var(--z);
}
#target {
--actual: --f(red, 2, 360deg);
--expected: 1 2 3;
}
</style>
</template>
<!-- Locals -->
<template data-name="Unused local">
<style>
@function --f() {
--x: 10px;
result: 1px;
}
#target {
--actual: --f();
--expected: 1px;
}
</style>
</template>
<template data-name="Local does not affect outer scope">
<style>
@function --f() {
--x: 10px;
result: 1px;
}
#target {
--x: 20px;
--actual: --f() var(--x);
--expected: 1px 20px;
}
</style>
</template>
<template data-name="Substituting local in result">
<style>
@function --f() {
--x: 10px;
result: var(--x);
}
#target {
--actual: --f();
--expected: 10px;
}
</style>
</template>
<template data-name="Substituting multiple locals in result">
<style>
@function --f() {
--x: 10px;
--y: 17px;
result: var(--x) var(--y);
}
#target {
--actual: --f();
--expected: 10px 17px;
}
</style>
</template>
<template data-name="Local referring to another local">
<style>
@function --f() {
--x: 10px;
--y: var(--x);
result: var(--y);
}
#target {
--actual: --f();
--expected: 10px;
}
</style>
</template>
<template data-name="Locals appearing after result">
<style>
@function --f() {
result: var(--y);
--x: 10px;
--y: var(--x);
}
#target {
--actual: --f();
--expected: 10px;
}
</style>
</template>
<template data-name="Locals cascading behavior">
<style>
@function --f() {
--x: 10px;
--y: var(--x);
result: var(--y);
--x: 20px; /* Surprise! */
}
#target {
--actual: --f();
--expected: 20px;
}
</style>
</template>
<!-- Scoping -->
<template data-name="Custom properties are visible inside function">
<style>
@function --f() {
result: var(--x);
}
#target {
--x: 10px;
--actual: --f();
--expected: 10px;
}
</style>
</template>
<template data-name="Substitute local from outer scope">
<style>
@function --f() {
--x: PASS;
result: --g();
}
@function --g() {
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Substitute argument from outer scope">
<style>
@function --f(--x) {
result: --g();
}
@function --g() {
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f(PASS);
--expected: PASS;
}
</style>
</template>
<template data-name="Inner argument shadowing outer argument">
<style>
@function --f(--x) {
result: --g(PASS);
}
@function --g(--x) {
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f(FAIL);
--expected: PASS;
}
</style>
</template>
<template data-name="Inner argument shadowing outer local">
<style>
@function --f() {
--x: FAIL;
result: --g(PASS);
}
@function --g(--x) {
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Inner local shadowing outer argument">
<style>
@function --f(--x) {
result: --g();
}
@function --g() {
--x: PASS;
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f(FAIL);
--expected: PASS;
}
</style>
</template>
<template data-name="Inner local shadowing outer local">
<style>
@function --f() {
--x: FAIL;
result: --g();
}
@function --g() {
--x: PASS;
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Referencing outer local containing var()">
<style>
@function --f() {
--y: 1;
--x: var(--y);
result: --g();
}
@function --g() {
--y: 0;
result: var(--x);
}
#target {
--y: 0;
--x: FAIL;
--actual: --f();
--expected: 1;
}
</style>
</template>
<template data-name="Referencing outer typed argument">
<style>
@function --f(--l <length>: 10.00px) {
result: --g();
}
@function --g() {
result: var(--l);
}
#target {
--actual: --f();
--expected: 10px;
}
</style>
</template>
<template data-name="Same function with different scopes">
<style>
@function --one() {
--x: 1;
result: --f();
}
@function --two() {
--x: 2;
result: --f();
}
@function --three() {
--x: 3;
result: --f();
}
@function --f() {
result: var(--x);
}
#target {
--x: 0;
--actual: --one() --two() --three() --f();
--expected: 1 2 3 0;
}
</style>
</template>
<template data-name="Referencing local two frames up">
<style>
@function --a() {
--x: 1;
result: --b();
}
@function --b() {
result: --c();
}
@function --c() {
result: var(--x);
}
#target {
--x: 0;
--actual: --a();
--expected: 1;
}
</style>
</template>
<template data-name="IACVT outer local shadows property">
<style>
@function --a() {
--x: var(--unknown);
result: --b();
}
@function --b() {
result: var(--x, PASS);
}
#target {
--x: FAIL;
--actual: --a();
--expected: PASS;
}
</style>
</template>
<template data-name="Inner function call should see resolved outer locals">
<style>
@function --a() {
--x: --b();
--y: var(--px);
result: var(--x);
}
@function --b() {
result: var(--y, FAIL);
}
#target {
--px: 10px;
--actual: --a();
--expected: 10px;
}
</style>
</template>
<!--
This the same test as the one above, but the *values* of --x and --y
are flipped, as are the references to those vars. If there is a bug
related to this behavior, it may be masked by "lucky" ordering of items
in a hash backing. Testing both ways ensures that at least one test
fails.
-->
<template data-name="Inner function call should see resolved outer locals (reverse)">
<style>
@function --a() {
--x: var(--px);
--y: --b();
result: var(--y);
}
@function --b() {
result: var(--x, FAIL);
}
#target {
--px: 10px;
--actual: --a();
--expected: 10px;
}
</style>
</template>
<!-- Shadowing -->
<template data-name="Parameter shadows custom property">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--x: FAIL;
--actual: --f(PASS);
--expected: PASS;
}
</style>
</template>
<template data-name="Local shadows parameter">
<style>
@function --f(--x) {
--x: PASS;
result: var(--x);
}
#target {
--x: FAIL1;
--actual: --f(FAIL2);
--expected: PASS;
}
</style>
</template>
<template data-name="IACVT argument shadows outer scope">
<style>
@function --f(--x) {
result: var(--x, PASS);
}
#target {
--x: FAIL;
--actual: --f(var(--unknown));
--expected: PASS;
}
</style>
</template>
<template data-name="IACVT argument shadows outer scope, typed">
<style>
@function --f(--x <length>) {
result: var(--x, PASS);
}
#target {
--x: FAIL;
--actual: --f(var(--unknown));
--expected: PASS;
}
</style>
</template>
<template data-name="IACVT argument shadows outer scope, type mismatch">
<style>
@function --f(--x <length>) {
result: var(--x, PASS);
}
#target {
--x: FAIL;
--actual: --f(red);
--expected: PASS;
}
</style>
</template>
<!-- Invalid invocations -->
<template data-name="Missing only argument">
<style>
@function --f(--x) {
result: 10px;
}
#target {
--actual: --f();
/* --expected: <guaranteed-invalid> */
}
</style>
</template>
<template data-name="Missing one argument of several">
<style>
@function --f(--x, --y, --z) {
result: 10px;
}
#target {
--actual: --f(1, 2);
/* --expected: <guaranteed-invalid> */
}
</style>
</template>
<!--
{}-wrappers.
-->
<template data-name="Passing list as only argument">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--actual: --f({1px,2px});
--expected: 1px,2px;
}
</style>
</template>
<template data-name="Passing list as first argument">
<style>
@function --f(--x, --y) {
result: var(--x) | var(--y);
}
#target {
--actual: --f({1px, 2px}, 3px);
--expected: 1px, 2px | 3px;
}
</style>
</template>
<template data-name="Passing list as second argument">
<style>
@function --f(--x, --y) {
result: var(--x) | var(--y);
}
#target {
--actual: --f(1px, {2px, 3px});
--expected: 1px | 2px, 3px;
}
</style>
</template>
<template data-name="Passing comma as argument">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--actual: --f({,});
--expected: ,;
}
</style>
</template>
<template data-name="Passing {} as argument">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--actual: --f({{}});
--expected: {};
}
</style>
</template>
<template data-name="Passing non-whole-value {} as argument">
<style>
@function --f(--x) {
result: var(--x);
}
#target {
--actual: --f({foo{}});
--expected: foo{};
}
</style>
</template>
<!-- CSS-wide keywords -->
<!-- initial -->
<template data-name="Local variable with initial keyword">
<style>
@function --f(--x: FAIL1) {
--x: FAIL2;
--x: initial;
result: var(--x);
}
#target {
--actual: --f(PASS);
--expected: PASS;
}
</style>
</template>
<template data-name="Local variable with initial keyword, defaulted">
<style>
@function --f(--x: PASS) {
--x: FAIL;
--x: initial;
result: var(--x);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Local variable with initial keyword, no value via IACVT-capture">
<style>
@function --f(--x) {
--x: FAIL;
--x: initial;
result: var(--x, PASS);
}
#target {
--actual: --f(var(--unknown));
--expected: PASS;
}
</style>
</template>
<template data-name="Default with initial keyword">
<style>
@function --f(--x: initial) {
result: var(--x, PASS);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="initial appearing via fallback">
<style>
@function --f(--x: PASS) {
--x: var(--unknown, initial);
result: var(--x);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<!-- inherit -->
<template data-name="Local variable with inherit keyword">
<style>
@function --f(--x: FAIL1) {
--x: FAIL2;
--x: inherit;
result: var(--x);
}
#target {
--x: PASS;
--actual: --f(FAIL3);
--expected: PASS;
}
</style>
</template>
<template data-name="Local variable with inherit keyword (nested)">
<style>
@function --f(--x: FAIL1) {
--x: FAIL2;
--x: inherit;
result: var(--x);
}
@function --g(--x) {
--x: PASS;
result: --f(FAIL3);
}
#target {
--actual: --g(FAIL4);
--expected: PASS;
}
</style>
</template>
<template data-name="Inheriting an invalid value">
<style>
@function --f(--x) {
--x: FAIL2;
--x: inherit;
result: var(--x, PASS);
}
@function --g() {
--x: var(--unknown);
result: --f(FAIL1);
}
#target {
--actual: --g();
--expected: PASS;
}
</style>
</template>
<template data-name="Default with inherit keyword">
<style>
@function --f(--x: inherit) {
result: var(--x);
}
#target {
--x: PASS1;
--actual: --f() --f(PASS2);
--expected: PASS1 PASS2;
}
</style>
</template>
<template data-name="Default with inherit keyword (nested)">
<style>
@function --f(--x: inherit) {
result: var(--x);
}
@function --g() {
--x: PASS1;
result: --f() --f(PASS2);
}
#target {
--x: FAIL;
--actual: --g();
--expected: PASS1 PASS2;
}
</style>
</template>
<!-- Other CSS-wide keywords (invalid in locals) -->
<template data-name="Local with the unset keyword">
<style>
@function --f() {
--x: unset;
result: var(--x, PASS);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Local with the revert keyword">
<style>
@function --f() {
--x: revert;
result: var(--x, PASS);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<template data-name="Local with the revert-layer keyword">
<style>
@function --f() {
--x: revert-layer;
result: var(--x, PASS);
}
#target {
--actual: --f();
--expected: PASS;
}
</style>
</template>
<!-- Keywords in 'result' descriptor -->
<template data-name="initial keyword left unresolved on result descriptor">
<style>
@function --f() {
result: initial;
}
#target {
--tmp: --f();
--actual: var(--tmp, PASS);
--expected: PASS;
}
</style>
</template>
<template data-name="inherit keyword left unresolved on result descriptor">
<style>
@function --f() {
result: inherit;
}
#parent {
--tmp: PASS;
}
#target {
--tmp: --f();
--actual: var(--tmp, FAIL);
--expected: PASS;
}
</style>
</template>
<template data-name="unset keyword left unresolved on result descriptor">
<style>
@function --f() {
result: unset;
}
#parent {
--tmp: PASS;
}
#target {
--tmp: --f();
--actual: var(--tmp, FAIL);
--expected: PASS;
}
</style>
</template>
<template data-name="revert keyword left unresolved on result descriptor">
<style>
@function --f() {
result: revert;
}
#parent {
--tmp: PASS;
}
#target {
--tmp: --f();
--actual: var(--tmp, FAIL);
--expected: PASS;
}
</style>
</template>
<template data-name="revert-layer keyword left unresolved on result descriptor">
<style>
@function --f() {
result: revert-layer;
}
@layer one {
#target {
--tmp: PASS;
}
}
@layer two {
#target {
--tmp: --f();
--actual: var(--tmp, FAIL);
--expected: PASS;
}
}
</style>
</template>
<template data-name="Keyword can be returned from function into local variable">
<style>
@function --f() {
result: initial;
}
@function --g(--x: PASS) {
--x: FAIL1;
--x: --f();
result: var(--x, FAIL2);
}
#target {
--actual: --g();
--expected: PASS;
}
</style>
</template>
<template data-name="Can not return CSS-wide keyword as length">
<style>
@function --f() returns <length> {
result: revert-layer;
}
@layer one {
#target {
--tmp: FAIL;
}
}
@layer two {
#target {
--tmp: --f();
--actual: var(--tmp, PASS);
--expected: PASS;
}
}
</style>
</template>
<script>
test_all_templates();
</script>