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
This commit is contained in:
Emilio Cobos Álvarez 2023-08-12 00:11:24 +02:00 committed by Martin Robinson
parent 819ebc5710
commit d1a281ebbd
3 changed files with 58 additions and 31 deletions

View file

@ -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. /// Parse a CSS rule.
/// ///
/// Returns a parsed CSS rule and the final state of the parser. /// Returns a parsed CSS rule and the final state of the parser.

View file

@ -153,21 +153,17 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
} }
// Computes the parser state at the given index // Computes the parser state at the given index
let insert_rule_context = InsertRuleContext {
rule_list: &rules.0,
index,
};
let state = if nested { let state = if nested {
State::Body State::Body
} else if index == 0 { } else if index == 0 {
State::Start State::Start
} else { } else {
rules insert_rule_context.max_rule_state_at_index(index - 1)
.0
.get(index - 1)
.map(CssRule::rule_state)
.unwrap_or(State::Body)
};
let insert_rule_context = InsertRuleContext {
rule_list: &rules.0,
index,
}; };
// Steps 3, 4, 5, 6 // Steps 3, 4, 5, 6

View file

@ -48,6 +48,36 @@ pub struct InsertRuleContext<'a> {
pub index: usize, 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. /// The parser for the top-level rules in a stylesheet.
pub struct TopLevelRuleParser<'a> { pub struct TopLevelRuleParser<'a> {
/// A reference to the lock we need to use to create rules. /// A reference to the lock we need to use to create rules.
@ -105,12 +135,8 @@ impl<'b> TopLevelRuleParser<'b> {
None => return true, None => return true,
}; };
let next_rule_state = match ctx.rule_list.get(ctx.index) { let max_rule_state = ctx.max_rule_state_at_index(ctx.index);
None => return true, if new_state > max_rule_state {
Some(rule) => rule.rule_state(),
};
if new_state > next_rule_state {
self.dom_error = Some(RulesMutateError::HierarchyRequest); self.dom_error = Some(RulesMutateError::HierarchyRequest);
return false; return false;
} }
@ -267,11 +293,23 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
self.dom_error = Some(RulesMutateError::HierarchyRequest); self.dom_error = Some(RulesMutateError::HierarchyRequest);
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule)) return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
}, },
_ => {} "layer" => {
} let state_to_check = if self.state <= State::EarlyLayers {
// The real state depends on whether there's a block or not.
if !self.check_state(State::Body) { // We don't know that yet, but the parse_block check deals
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); // 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) AtRuleParser::parse_prelude(&mut self.nested(), name, input)
@ -284,6 +322,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
start: &ParserState, start: &ParserState,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i>> { ) -> 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)?; let rule = AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
self.state = State::Body; self.state = State::Body;
Ok((start.position(), rule)) Ok((start.position(), rule))