In #7961, we resolved to allow nested style rules that begin with ident-token, and use parser restarts to solve the ambiguity that arose from that.
However, there's a serious problem that we missed: var() in standard properties.
div {
width:is(.x1) {
/* ... */
}
width:is(.x2) {
/* ... */
}
/* ... */
width:is(.xN) {
/* ... */
}
}
The idea from #7961 is that when standing at width :, we'll quickly back out of declaration parsing, because the next token (is() does not match width's property grammar. Restarting and trying again as a nested style rule is cheap, because we won't proceed too far into the tokens before discovering that it's not a valid declaration.
However, var() interferes with this plan:
div {
width:is(.x1) {
/* ... */
}
width:is(.x2) {
/* ... */
}
/* ... */
width:is(.xN) {
/* ... */
}
var(--x) /* <====== New */
}
Now we have a valid width declaration (parse-time) consisting of everything from the first width to the end of the div-block. The problem is not that var() is present, but that it can be present. Even when it's not there, we have to scan the whole rest of the block to check whether it is or not. If it's not there, we have O(N^2) behavior, because we'll consume the whole rest of the block at width:is(.x1), restart as a style rule, and then repeat at width:is(.x2).
Another related problem (from @tabatkins) is the following:
div {
width:focus {
color: var(--x);
}
}
Per #7961, this is a valid width declaration, which isn't a great result. Especially since you'd get a rule if you do the same thing minus the var().
We could solve this problem one of the following ways:
- Giving up on relaxed nesting completely.
- Ban
{} entirely from declarations. With this we can stop when we see {.
- Ban
{} from declarations, unless it's the whole value (from @tabatkins). This is probably also acceptable, because we can look at the first non-whitespace token after : and if that's not {, then we can stop when we do see {. Otherwise we stop after the first }.
I would not go any weaker than (3), in particular the previously discussed restriction of "blocks are valid, but only at the end" is not good enough, because authors can realistically put any amount of stuff inside that block (if it's intended as a nested style rule), and also it wouldn't fix the problem illustrated by the last code snippet in this issue.
cc @emilio @mdubet
In #7961, we resolved to allow nested style rules that begin with ident-token, and use parser restarts to solve the ambiguity that arose from that.
However, there's a serious problem that we missed:
var()in standard properties.The idea from #7961 is that when standing at
width:, we'll quickly back out of declaration parsing, because the next token (is() does not matchwidth's property grammar. Restarting and trying again as a nested style rule is cheap, because we won't proceed too far into the tokens before discovering that it's not a valid declaration.However,
var()interferes with this plan:Now we have a valid
widthdeclaration (parse-time) consisting of everything from the firstwidthto the end of thediv-block. The problem is not thatvar()is present, but that it can be present. Even when it's not there, we have to scan the whole rest of the block to check whether it is or not. If it's not there, we have O(N^2) behavior, because we'll consume the whole rest of the block atwidth:is(.x1), restart as a style rule, and then repeat atwidth:is(.x2).Another related problem (from @tabatkins) is the following:
Per #7961, this is a valid
widthdeclaration, which isn't a great result. Especially since you'd get a rule if you do the same thing minus thevar().We could solve this problem one of the following ways:
{}entirely from declarations. With this we can stop when we see{.{}from declarations, unless it's the whole value (from @tabatkins). This is probably also acceptable, because we can look at the first non-whitespace token after:and if that's not{, then we can stop when we do see{. Otherwise we stop after the first}.I would not go any weaker than (3), in particular the previously discussed restriction of "blocks are valid, but only at the end" is not good enough, because authors can realistically put any amount of stuff inside that block (if it's intended as a nested style rule), and also it wouldn't fix the problem illustrated by the last code snippet in this issue.
cc @emilio @mdubet