Issue #2862 - Modify the css lowerer so that the lowering pass now recognizes bare type-selector nested rules and combinator-led cases

This commit is contained in:
Basilisk-Dev 2026-03-11 22:25:48 -04:00 • committed by wuggy
commit c794e74cf0
2 changed files with 242 additions and 118 deletions

View file

@ -597,6 +597,103 @@ private:
}
}
static bool
StartsPotentialTypeSelector(char16_t aChar)
{
return (aChar >= 'a' && aChar <= 'z') ||
(aChar >= 'A' && aChar <= 'Z') ||
aChar == '_' ||
aChar == '\\' ||
aChar >= 0x80;
}
bool
LooksLikeTypeSelectorRule() const
{
if (AtEnd() || !StartsPotentialTypeSelector(Peek())) {
return false;
}
uint32_t pos = mPos;
int32_t parenDepth = 0;
int32_t bracketDepth = 0;
bool inComment = false;
char16_t stringQuote = 0;
while (pos < mInput.Length()) {
char16_t c = mInput.CharAt(pos);
if (inComment) {
if (c == '*' && pos + 1 < mInput.Length() &&
mInput.CharAt(pos + 1) == '/') {
inComment = false;
++pos;
}
++pos;
continue;
}
if (stringQuote) {
if (c == '\\' && pos + 1 < mInput.Length()) {
pos += 2;
continue;
}
if (c == stringQuote) {
stringQuote = 0;
}
++pos;
continue;
}
if (c == '/' && pos + 1 < mInput.Length() &&
mInput.CharAt(pos + 1) == '*') {
inComment = true;
pos += 2;
continue;
}
if (c == '"' || c == '\'') {
stringQuote = c;
++pos;
continue;
}
if (c == '(') {
++parenDepth;
++pos;
continue;
}
if (c == ')' && parenDepth > 0) {
--parenDepth;
++pos;
continue;
}
if (c == '[') {
++bracketDepth;
++pos;
continue;
}
if (c == ']' && bracketDepth > 0) {
--bracketDepth;
++pos;
continue;
}
if (parenDepth == 0 && bracketDepth == 0) {
if (c == '{') {
return true;
}
if (c == ':' || c == ';' || c == '}') {
return false;
}
}
++pos;
}
return false;
}
static bool
IsAtRuleNameChar(char16_t aChar)
{
@ -986,7 +1083,7 @@ private:
continue;
}
if (StartsNestedSelector(c)) {
if (StartsNestedSelector(c) || LooksLikeTypeSelectorRule()) {
FlushDeclarations(aSelectors, declarations, aOutput);
if (!ParseQualifiedRule(aOutput, &aSelectors)) {
return false;

View file

@ -1,130 +1,157 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test for Basic CSS Nesting Lowering</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<div id="scope" class="scope order">
<span id="desc" class="desc"></span>
</div>
<div id="button" class="button active"></div>
<pre id="standalone-log"></pre>
<script>
"use strict";
<head>
<meta charset="utf-8" />
<title>Test for Basic CSS Nesting Lowering</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="scope" class="scope order">
<span id="desc" class="desc"></span>
</div>
<div id="button" class="button active"></div>
<pre id="standalone-log"></pre>
<script>
"use strict";
function colorOf(win, element, property) {
return win.getComputedStyle(element).getPropertyValue(property);
}
function colorOf(win, element, property) {
return win.getComputedStyle(element).getPropertyValue(property);
}
function appendTestSheet() {
var style = document.createElement("style");
style.textContent =
".scope {\n" +
" color: rgb(1, 2, 3);\n" +
" .desc {\n" +
" color: rgb(4, 5, 6);\n" +
" }\n" +
" background-color: rgb(7, 8, 9);\n" +
"}\n" +
"\n" +
".button {\n" +
" &.active {\n" +
" color: rgb(10, 11, 12);\n" +
" }\n" +
"}\n" +
"\n" +
".order {\n" +
" border-top-style: solid;\n" +
" @media all {\n" +
" & {\n" +
" border-top-color: rgb(13, 14, 15);\n" +
" }\n" +
" }\n" +
" border-right-style: solid;\n" +
" border-right-color: rgb(16, 17, 18);\n" +
"}\n";
document.head.appendChild(style);
return style;
}
function appendTestSheet() {
var style = document.createElement("style");
style.textContent =
".scope {\n" +
" color: rgb(1, 2, 3);\n" +
" .desc {\n" +
" color: rgb(4, 5, 6);\n" +
" }\n" +
" span {\n" +
" border-left-style: solid;\n" +
" border-left-color: rgb(19, 20, 21);\n" +
" }\n" +
" background-color: rgb(7, 8, 9);\n" +
"}\n" +
"\n" +
".button {\n" +
" &.active {\n" +
" color: rgb(10, 11, 12);\n" +
" }\n" +
"}\n" +
"\n" +
".order {\n" +
" border-top-style: solid;\n" +
" @media all {\n" +
" & {\n" +
" border-top-color: rgb(13, 14, 15);\n" +
" }\n" +
" }\n" +
" border-right-style: solid;\n" +
" border-right-color: rgb(16, 17, 18);\n" +
"}\n";
document.head.appendChild(style);
return style;
}
function runChecks(style, report) {
var scope = document.getElementById("scope");
var desc = document.getElementById("desc");
var button = document.getElementById("button");
function runChecks(style, report) {
var scope = document.getElementById("scope");
var desc = document.getElementById("desc");
var button = document.getElementById("button");
report(colorOf(window, scope, "color") === "rgb(1, 2, 3)",
"outer rule declarations should apply",
colorOf(window, scope, "color"),
"rgb(1, 2, 3)");
report(colorOf(window, desc, "color") === "rgb(4, 5, 6)",
"nested descendant rule should be lowered",
colorOf(window, desc, "color"),
"rgb(4, 5, 6)");
report(colorOf(window, scope, "background-color") === "rgb(7, 8, 9)",
"declarations after a nested rule should preserve order",
colorOf(window, scope, "background-color"),
"rgb(7, 8, 9)");
report(colorOf(window, button, "color") === "rgb(10, 11, 12)",
"ampersand selectors should be expanded",
colorOf(window, button, "color"),
"rgb(10, 11, 12)");
report(colorOf(window, scope, "border-top-color") === "rgb(13, 14, 15)",
"nested group rules should target the parent selector",
colorOf(window, scope, "border-top-color"),
"rgb(13, 14, 15)");
report(colorOf(window, scope, "border-right-color") === "rgb(16, 17, 18)",
"declarations after nested group rules should still apply",
colorOf(window, scope, "border-right-color"),
"rgb(16, 17, 18)");
report(style.sheet.cssRules.length === 7,
"lowering should produce flat top-level rules",
String(style.sheet.cssRules.length),
"7");
}
report(
colorOf(window, scope, "color") === "rgb(1, 2, 3)",
"outer rule declarations should apply",
colorOf(window, scope, "color"),
"rgb(1, 2, 3)",
);
report(
colorOf(window, desc, "color") === "rgb(4, 5, 6)",
"nested descendant rule should be lowered",
colorOf(window, desc, "color"),
"rgb(4, 5, 6)",
);
report(
colorOf(window, desc, "border-left-color") === "rgb(19, 20, 21)",
"nested type selector rule should be lowered",
colorOf(window, desc, "border-left-color"),
"rgb(19, 20, 21)",
);
report(
colorOf(window, scope, "background-color") === "rgb(7, 8, 9)",
"declarations after a nested rule should preserve order",
colorOf(window, scope, "background-color"),
"rgb(7, 8, 9)",
);
report(
colorOf(window, button, "color") === "rgb(10, 11, 12)",
"ampersand selectors should be expanded",
colorOf(window, button, "color"),
"rgb(10, 11, 12)",
);
report(
colorOf(window, scope, "border-top-color") === "rgb(13, 14, 15)",
"nested group rules should target the parent selector",
colorOf(window, scope, "border-top-color"),
"rgb(13, 14, 15)",
);
report(
colorOf(window, scope, "border-right-color") === "rgb(16, 17, 18)",
"declarations after nested group rules should still apply",
colorOf(window, scope, "border-right-color"),
"rgb(16, 17, 18)",
);
report(
style.sheet.cssRules.length === 8,
"lowering should produce flat top-level rules",
String(style.sheet.cssRules.length),
"8",
);
}
function runStandalone() {
var log = document.getElementById("standalone-log");
var lines = [
"Standalone mode.",
"This page only demonstrates nesting if layout.css.nesting.enabled is already true in the browser.",
""
];
var style = appendTestSheet();
function runStandalone() {
var log = document.getElementById("standalone-log");
var lines = [
"Standalone mode.",
"This page only demonstrates nesting if layout.css.nesting.enabled is already true in the browser.",
"",
];
var style = appendTestSheet();
runChecks(style, function(pass, message, actual, expected) {
lines.push((pass ? "PASS" : "FAIL") + ": " + message);
if (!pass) {
lines.push(" expected: " + expected);
lines.push(" actual: " + actual);
}
});
runChecks(style, function (pass, message, actual, expected) {
lines.push((pass ? "PASS" : "FAIL") + ": " + message);
if (!pass) {
lines.push(" expected: " + expected);
lines.push(" actual: " + actual);
}
});
log.textContent = lines.join("\n");
}
log.textContent = lines.join("\n");
}
function runMochitest() {
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({
set: [["layout.css.nesting.enabled", true]]
}, function() {
var style = appendTestSheet();
function runMochitest() {
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{
set: [["layout.css.nesting.enabled", true]],
},
function () {
var style = appendTestSheet();
runChecks(style, function(pass, message, actual, expected) {
is(actual, expected, message);
});
runChecks(style, function (pass, message, actual, expected) {
is(actual, expected, message);
});
SimpleTest.finish();
});
}
SimpleTest.finish();
},
);
}
if (typeof SimpleTest !== "undefined" && typeof SpecialPowers !== "undefined") {
runMochitest();
} else {
runStandalone();
}
</script>
</body>
if (typeof SimpleTest !== "undefined" && typeof SpecialPowers !== "undefined") {
runMochitest();
} else {
runStandalone();
}
</script>
</body>
</html>