diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2023-08-12 00:11:24 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2023-08-16 17:46:41 +0200 |
commit | d1a281ebbd7a1c135dc769721c7cc8f62f5721b9 (patch) | |
tree | a0f5fd7d8ba0f679a43a2e89b6d623f31e9e820f | |
parent | 819ebc57104f2f2a705e91c2d727b04918f6f595 (diff) | |
download | servo-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.rs | 10 | ||||
-rw-r--r-- | components/style/stylesheets/rule_list.rs | 16 | ||||
-rw-r--r-- | components/style/stylesheets/rule_parser.rs | 63 |
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)) |