aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2023-08-12 00:11:24 +0200
committerMartin Robinson <mrobinson@igalia.com>2023-08-16 17:46:41 +0200
commitd1a281ebbd7a1c135dc769721c7cc8f62f5721b9 (patch)
treea0f5fd7d8ba0f679a43a2e89b6d623f31e9e820f
parent819ebc57104f2f2a705e91c2d727b04918f6f595 (diff)
downloadservo-d1a281ebbd7a1c135dc769721c7cc8f62f5721b9.tar.gz
servo-d1a281ebbd7a1c135dc769721c7cc8f62f5721b9.zip
style: Fix insertRule with layer statements before imports
We need to do a bit more nuanced check because @layer statements might go before imports. Differential Revision: https://phabricator.services.mozilla.com/D144996
-rw-r--r--components/style/stylesheets/mod.rs10
-rw-r--r--components/style/stylesheets/rule_list.rs16
-rw-r--r--components/style/stylesheets/rule_parser.rs63
3 files changed, 58 insertions, 31 deletions
diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs
index c72f182ebf0..344fda5ef3c 100644
--- a/components/style/stylesheets/mod.rs
+++ b/components/style/stylesheets/mod.rs
@@ -385,16 +385,6 @@ impl CssRule {
}
}
- fn rule_state(&self) -> State {
- match *self {
- // CssRule::Charset(..) => State::Start,
- CssRule::Import(..) => State::Imports,
- CssRule::Namespace(..) => State::Namespaces,
- // TODO(emilio): Do we need something for EarlyLayers?
- _ => State::Body,
- }
- }
-
/// Parse a CSS rule.
///
/// Returns a parsed CSS rule and the final state of the parser.
diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs
index d84a738bca0..c246d7ae6bb 100644
--- a/components/style/stylesheets/rule_list.rs
+++ b/components/style/stylesheets/rule_list.rs
@@ -153,21 +153,17 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
}
// Computes the parser state at the given index
+ let insert_rule_context = InsertRuleContext {
+ rule_list: &rules.0,
+ index,
+ };
+
let state = if nested {
State::Body
} else if index == 0 {
State::Start
} else {
- rules
- .0
- .get(index - 1)
- .map(CssRule::rule_state)
- .unwrap_or(State::Body)
- };
-
- let insert_rule_context = InsertRuleContext {
- rule_list: &rules.0,
- index,
+ insert_rule_context.max_rule_state_at_index(index - 1)
};
// Steps 3, 4, 5, 6
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
index b5deecf2bb0..08c2ab6e4d1 100644
--- a/components/style/stylesheets/rule_parser.rs
+++ b/components/style/stylesheets/rule_parser.rs
@@ -48,6 +48,36 @@ pub struct InsertRuleContext<'a> {
pub index: usize,
}
+impl<'a> InsertRuleContext<'a> {
+ /// Returns the max rule state allowable for insertion at a given index in
+ /// the rule list.
+ pub fn max_rule_state_at_index(&self, index: usize) -> State {
+ let rule = match self.rule_list.get(index) {
+ Some(rule) => rule,
+ None => return State::Body,
+ };
+ match rule {
+ CssRule::Import(..) => State::Imports,
+ CssRule::Namespace(..) => State::Namespaces,
+ CssRule::LayerStatement(..) => {
+ // If there are @import / @namespace after this layer, then
+ // we're in the early-layers phase, otherwise we're in the body
+ // and everything is fair game.
+ let next_non_layer_statement_rule = self.rule_list[index + 1..]
+ .iter()
+ .find(|r| !matches!(*r, CssRule::LayerStatement(..)));
+ if let Some(non_layer) = next_non_layer_statement_rule {
+ if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) {
+ return State::EarlyLayers;
+ }
+ }
+ State::Body
+ }
+ _ => State::Body,
+ }
+ }
+}
+
/// The parser for the top-level rules in a stylesheet.
pub struct TopLevelRuleParser<'a> {
/// A reference to the lock we need to use to create rules.
@@ -105,12 +135,8 @@ impl<'b> TopLevelRuleParser<'b> {
None => return true,
};
- let next_rule_state = match ctx.rule_list.get(ctx.index) {
- None => return true,
- Some(rule) => rule.rule_state(),
- };
-
- if new_state > next_rule_state {
+ let max_rule_state = ctx.max_rule_state_at_index(ctx.index);
+ if new_state > max_rule_state {
self.dom_error = Some(RulesMutateError::HierarchyRequest);
return false;
}
@@ -267,11 +293,23 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
self.dom_error = Some(RulesMutateError::HierarchyRequest);
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
},
- _ => {}
- }
-
- if !self.check_state(State::Body) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ "layer" => {
+ let state_to_check = if self.state <= State::EarlyLayers {
+ // The real state depends on whether there's a block or not.
+ // We don't know that yet, but the parse_block check deals
+ // with that.
+ State::EarlyLayers
+ } else {
+ State::Body
+ };
+ if !self.check_state(state_to_check) {
+ return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ }
+ },
+ _ => {
+ // All other rules have blocks, so we do this check early in
+ // parse_block instead.
+ }
}
AtRuleParser::parse_prelude(&mut self.nested(), name, input)
@@ -284,6 +322,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i>> {
+ if !self.check_state(State::Body) {
+ return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ }
let rule = AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
self.state = State::Body;
Ok((start.position(), rule))