LibWeb: Ensure valid placement of @import and @namespace rules

These rules should appear before all other rules (excluding @layer
statements and @charset) with @import appearing first.
This commit is contained in:
Callum Law 2025-06-21 03:11:59 +12:00 committed by Sam Atkins
commit 6144154e4f
Notes: github-actions[bot] 2025-06-23 11:54:05 +00:00
7 changed files with 139 additions and 0 deletions

View file

@ -125,6 +125,9 @@ GC::Ref<CSS::CSSStyleSheet> Parser::parse_as_css_stylesheet(Optional<::URL::URL>
// To parse a CSS stylesheet, first parse a stylesheet.
auto const& style_sheet = parse_a_stylesheet(m_token_stream, location);
bool import_rules_valid = true;
bool namespace_rules_valid = true;
// Interpret all of the resulting top-level qualified rules as style rules, defined below.
GC::RootVector<GC::Ref<CSSRule>> rules(realm().heap());
for (auto const& raw_rule : style_sheet.rules) {
@ -135,6 +138,36 @@ GC::Ref<CSS::CSSStyleSheet> Parser::parse_as_css_stylesheet(Optional<::URL::URL>
log_parse_error();
continue;
}
// "Any @import rules must precede all other valid at-rules and style rules in a style sheet
// (ignoring @charset and @layer statement rules) and must not have any other valid at-rules
// or style rules between it and previous @import rules, or else the @import rule is invalid."
// https://drafts.csswg.org/css-cascade-5/#at-import
//
// "Any @namespace rules must follow all @charset and @import rules and precede all other
// non-ignored at-rules and style rules in a style sheet.
// ...
// A syntactically invalid @namespace rule (whether malformed or misplaced) must be ignored."
// https://drafts.csswg.org/css-namespaces/#syntax
switch (rule->type()) {
case CSSRule::Type::LayerStatement:
break;
case CSSRule::Type::Import:
if (!import_rules_valid)
continue;
break;
case CSSRule::Type::Namespace:
import_rules_valid = false;
if (!namespace_rules_valid)
continue;
break;
default:
import_rules_valid = false;
namespace_rules_valid = false;
break;
}
rules.append(*rule);
}

View file

@ -0,0 +1,46 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x463 [BFC] children: not-inline
BlockContainer <body> at (8,16) content-size 784x434 children: not-inline
BlockContainer <p> at (8,16) content-size 784x18 children: inline
frag 0 from TextNode start: 0, length: 35, rect: [8,16 290.765625x18] baseline: 13.796875
"All divs below should be 100px wide"
TextNode <#text>
BlockContainer <(anonymous)> at (8,50) content-size 784x0 children: inline
TextNode <#text>
BlockContainer <div#target1> at (8,50) content-size 100x100 children: not-inline
BlockContainer <(anonymous)> at (8,150) content-size 784x0 children: inline
TextNode <#text>
BlockContainer <div#target2> at (8,150) content-size 100x100 children: not-inline
BlockContainer <(anonymous)> at (8,250) content-size 784x0 children: inline
TextNode <#text>
BlockContainer <div#target3> at (8,250) content-size 100x100 children: not-inline
BlockContainer <(anonymous)> at (8,350) content-size 784x0 children: inline
TextNode <#text>
BlockContainer <div#target4> at (8,350) content-size 100x100 children: not-inline
BlockContainer <(anonymous)> at (8,450) content-size 784x0 children: inline
TextNode <#text>
TextNode <#text>
TextNode <#text>
TextNode <#text>
TextNode <#text>
TextNode <#text>
BlockContainer <pre#out> at (8,463) content-size 784x0 children: not-inline
BlockContainer <(anonymous)> at (8,463) content-size 784x0 children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x463]
PaintableWithLines (BlockContainer<BODY>) [8,16 784x434]
PaintableWithLines (BlockContainer<P>) [8,16 784x18]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,50 784x0]
PaintableWithLines (BlockContainer<DIV>#target1) [8,50 100x100]
PaintableWithLines (BlockContainer(anonymous)) [8,150 784x0]
PaintableWithLines (BlockContainer<DIV>#target2) [8,150 100x100]
PaintableWithLines (BlockContainer(anonymous)) [8,250 784x0]
PaintableWithLines (BlockContainer<DIV>#target3) [8,250 100x100]
PaintableWithLines (BlockContainer(anonymous)) [8,350 784x0]
PaintableWithLines (BlockContainer<DIV>#target4) [8,350 100x100]
PaintableWithLines (BlockContainer(anonymous)) [8,450 784x0]
PaintableWithLines (BlockContainer<PRE>#out) [8,463 784x0]
PaintableWithLines (BlockContainer(anonymous)) [8,463 784x0]

View file

@ -0,0 +1,3 @@
#target1 {
width: 100px;
}

View file

@ -0,0 +1,3 @@
#target2 {
width: 200px;
}

View file

@ -0,0 +1,3 @@
#target3 {
width: 200px;
}

View file

@ -0,0 +1,3 @@
#target4 {
width: 200px;
}

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<p>All divs below should be 100px wide</p>
<div id="target1"></div>
<div id="target2"></div>
<div id="target3"></div>
<div id="target4"></div>
<style>
div {
height: 100px;
width: 100px;
background-color: green;
}
#target1 {
width: 200px;
}
#target2,
#target3,
#target4 {
background-color: 100px;
}
</style>
<style>
/* Imports are allowed after invalid (malformed) namespace rules */
@namespace abc;
@import "./import-after-namespace-1.css";
</style>
<style>
/* Imports are not allowed after valid namespace rules */
@namespace def url("https://www.w3.org/1999/xhtml");
@import "./import-after-namespace-2.css";
</style>
<style>
/* Imports are not allowed after invalid supports rules */
@supports (display: block;) {
}
@import "./import-after-namespace-3.css";
</style>
<style>
/* Imports are not allowed after valid namespaces */
@supports (display: block) {
}
@import "./import-after-namespace-4.css";
</style>
<pre id="out"></pre>
</html>