summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cssparser/src/rules_and_declarations.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/cssparser/src/rules_and_declarations.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cssparser/src/rules_and_declarations.rs')
-rw-r--r--third_party/rust/cssparser/src/rules_and_declarations.rs496
1 files changed, 496 insertions, 0 deletions
diff --git a/third_party/rust/cssparser/src/rules_and_declarations.rs b/third_party/rust/cssparser/src/rules_and_declarations.rs
new file mode 100644
index 0000000000..34ba8e985d
--- /dev/null
+++ b/third_party/rust/cssparser/src/rules_and_declarations.rs
@@ -0,0 +1,496 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://drafts.csswg.org/css-syntax/#parsing
+
+use super::{
+ BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token,
+};
+use crate::cow_rc_str::CowRcStr;
+use crate::parser::{parse_nested_block, parse_until_after, parse_until_before, ParserState};
+
+/// Parse `!important`.
+///
+/// Typical usage is `input.try_parse(parse_important).is_ok()`
+/// at the end of a `DeclarationParser::parse_value` implementation.
+pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> {
+ input.expect_delim('!')?;
+ input.expect_ident_matching("important")
+}
+
+/// A trait to provide various parsing of declaration values.
+///
+/// For example, there could be different implementations for property declarations in style rules
+/// and for descriptors in `@font-face` rules.
+pub trait DeclarationParser<'i> {
+ /// The finished representation of a declaration.
+ type Declaration;
+
+ /// The error type that is included in the ParseError value that can be returned.
+ type Error: 'i;
+
+ /// Parse the value of a declaration with the given `name`.
+ ///
+ /// Return the finished representation for the declaration
+ /// as returned by `DeclarationListParser::next`,
+ /// or `Err(())` to ignore the entire declaration as invalid.
+ ///
+ /// Declaration name matching should be case-insensitive in the ASCII range.
+ /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`,
+ /// or with the `match_ignore_ascii_case!` macro.
+ ///
+ /// The given `input` is a "delimited" parser
+ /// that ends wherever the declaration value should end.
+ /// (In declaration lists, before the next semicolon or end of the current block.)
+ ///
+ /// If `!important` can be used in a given context,
+ /// `input.try_parse(parse_important).is_ok()` should be used at the end
+ /// of the implementation of this method and the result should be part of the return value.
+ fn parse_value<'t>(
+ &mut self,
+ name: CowRcStr<'i>,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
+ Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name))))
+ }
+}
+
+/// A trait to provide various parsing of at-rules.
+///
+/// For example, there could be different implementations for top-level at-rules
+/// (`@media`, `@font-face`, …)
+/// and for page-margin rules inside `@page`.
+///
+/// Default implementations that reject all at-rules are provided,
+/// so that `impl AtRuleParser<(), ()> for ... {}` can be used
+/// for using `DeclarationListParser` to parse a declarations list with only qualified rules.
+pub trait AtRuleParser<'i> {
+ /// The intermediate representation of prelude of an at-rule.
+ type Prelude;
+
+ /// The finished representation of an at-rule.
+ type AtRule;
+
+ /// The error type that is included in the ParseError value that can be returned.
+ type Error: 'i;
+
+ /// Parse the prelude of an at-rule with the given `name`.
+ ///
+ /// Return the representation of the prelude and the type of at-rule,
+ /// or `Err(())` to ignore the entire at-rule as invalid.
+ ///
+ /// The prelude is the part after the at-keyword
+ /// and before the `;` semicolon or `{ /* ... */ }` block.
+ ///
+ /// At-rule name matching should be case-insensitive in the ASCII range.
+ /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`,
+ /// or with the `match_ignore_ascii_case!` macro.
+ ///
+ /// The given `input` is a "delimited" parser
+ /// that ends wherever the prelude should end.
+ /// (Before the next semicolon, the next `{`, or the end of the current block.)
+ fn parse_prelude<'t>(
+ &mut self,
+ name: CowRcStr<'i>,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
+ Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
+ }
+
+ /// End an at-rule which doesn't have block. Return the finished
+ /// representation of the at-rule.
+ ///
+ /// The location passed in is source location of the start of the prelude.
+ ///
+ /// This is only called when `parse_prelude` returned `WithoutBlock`, and
+ /// either the `;` semicolon indeed follows the prelude, or parser is at
+ /// the end of the input.
+ fn rule_without_block(
+ &mut self,
+ prelude: Self::Prelude,
+ start: &ParserState,
+ ) -> Result<Self::AtRule, ()> {
+ let _ = prelude;
+ let _ = start;
+ Err(())
+ }
+
+ /// Parse the content of a `{ /* ... */ }` block for the body of the at-rule.
+ ///
+ /// The location passed in is source location of the start of the prelude.
+ ///
+ /// Return the finished representation of the at-rule
+ /// as returned by `RuleListParser::next` or `DeclarationListParser::next`,
+ /// or `Err(())` to ignore the entire at-rule as invalid.
+ ///
+ /// This is only called when `parse_prelude` returned `WithBlock`, and a block
+ /// was indeed found following the prelude.
+ fn parse_block<'t>(
+ &mut self,
+ prelude: Self::Prelude,
+ start: &ParserState,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
+ let _ = prelude;
+ let _ = start;
+ Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
+ }
+}
+
+/// A trait to provide various parsing of qualified rules.
+///
+/// For example, there could be different implementations for top-level qualified rules (i.e. style
+/// rules with Selectors as prelude) and for qualified rules inside `@keyframes` (keyframe rules
+/// with keyframe selectors as prelude).
+///
+/// Default implementations that reject all qualified rules are provided, so that
+/// `impl QualifiedRuleParser<(), ()> for ... {}` can be used for example for using
+/// `RuleListParser` to parse a rule list with only at-rules (such as inside
+/// `@font-feature-values`).
+pub trait QualifiedRuleParser<'i> {
+ /// The intermediate representation of a qualified rule prelude.
+ type Prelude;
+
+ /// The finished representation of a qualified rule.
+ type QualifiedRule;
+
+ /// The error type that is included in the ParseError value that can be returned.
+ type Error: 'i;
+
+ /// Parse the prelude of a qualified rule. For style rules, this is as Selector list.
+ ///
+ /// Return the representation of the prelude,
+ /// or `Err(())` to ignore the entire at-rule as invalid.
+ ///
+ /// The prelude is the part before the `{ /* ... */ }` block.
+ ///
+ /// The given `input` is a "delimited" parser
+ /// that ends where the prelude should end (before the next `{`).
+ fn parse_prelude<'t>(
+ &mut self,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
+ Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
+ }
+
+ /// Parse the content of a `{ /* ... */ }` block for the body of the qualified rule.
+ ///
+ /// The location passed in is source location of the start of the prelude.
+ ///
+ /// Return the finished representation of the qualified rule
+ /// as returned by `RuleListParser::next`,
+ /// or `Err(())` to ignore the entire at-rule as invalid.
+ fn parse_block<'t>(
+ &mut self,
+ prelude: Self::Prelude,
+ start: &ParserState,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
+ let _ = prelude;
+ let _ = start;
+ Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
+ }
+}
+
+/// Provides an iterator for rule bodies and declaration lists.
+pub struct RuleBodyParser<'i, 't, 'a, P, I, E> {
+ /// The input given to the parser.
+ pub input: &'a mut Parser<'i, 't>,
+ /// The parser given to `DeclarationListParser::new`
+ pub parser: &'a mut P,
+
+ _phantom: std::marker::PhantomData<(I, E)>,
+}
+
+/// A parser for a rule body item.
+pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>:
+ DeclarationParser<'i, Declaration = DeclOrRule, Error = Error>
+ + QualifiedRuleParser<'i, QualifiedRule = DeclOrRule, Error = Error>
+ + AtRuleParser<'i, AtRule = DeclOrRule, Error = Error>
+{
+ /// Whether we should attempt to parse declarations. If you know you won't, returning false
+ /// here is slightly faster.
+ fn parse_declarations(&self) -> bool;
+ /// Whether we should attempt to parse qualified rules. If you know you won't, returning false
+ /// would be slightly faster.
+ fn parse_qualified(&self) -> bool;
+}
+
+impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> {
+ /// Create a new `DeclarationListParser` for the given `input` and `parser`.
+ ///
+ /// Note that all CSS declaration lists can on principle contain at-rules.
+ /// Even if no such valid at-rule exists (yet),
+ /// this affects error handling: at-rules end at `{}` blocks, not just semicolons.
+ ///
+ /// The given `parser` therefore needs to implement
+ /// both `DeclarationParser` and `AtRuleParser` traits.
+ /// However, the latter can be an empty `impl`
+ /// since `AtRuleParser` provides default implementations of its methods.
+ ///
+ /// The return type for finished declarations and at-rules also needs to be the same,
+ /// since `<DeclarationListParser as Iterator>::next` can return either.
+ /// It could be a custom enum.
+ pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
+ Self {
+ input,
+ parser,
+ _phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+/// https://drafts.csswg.org/css-syntax/#consume-a-blocks-contents
+impl<'i, 't, 'a, I, P, E: 'i> Iterator for RuleBodyParser<'i, 't, 'a, P, I, E>
+where
+ P: RuleBodyItemParser<'i, I, E>,
+{
+ type Item = Result<I, (ParseError<'i, E>, &'i str)>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ self.input.skip_whitespace();
+ let start = self.input.state();
+ match self.input.next_including_whitespace_and_comments().ok()? {
+ Token::CloseCurlyBracket |
+ Token::WhiteSpace(..) |
+ Token::Semicolon |
+ Token::Comment(..) => continue,
+ Token::AtKeyword(ref name) => {
+ let name = name.clone();
+ return Some(parse_at_rule(&start, name, self.input, &mut *self.parser));
+ }
+ // https://drafts.csswg.org/css-syntax/#consume-a-declaration bails out just to
+ // keep parsing as a qualified rule if the token is not an ident, so we implement
+ // that in a slightly more straight-forward way
+ Token::Ident(ref name) if self.parser.parse_declarations() => {
+ let name = name.clone();
+ let result = {
+ let parser = &mut self.parser;
+ parse_until_after(self.input, Delimiter::Semicolon, |input| {
+ input.expect_colon()?;
+ parser.parse_value(name, input)
+ })
+ };
+ if result.is_err() && self.parser.parse_qualified() {
+ self.input.reset(&start);
+ // We ignore the resulting error here. The property declaration parse error
+ // is likely to be more relevant.
+ if let Ok(qual) = parse_qualified_rule(
+ &start,
+ self.input,
+ &mut *self.parser,
+ Delimiter::Semicolon | Delimiter::CurlyBracketBlock,
+ ) {
+ return Some(Ok(qual))
+ }
+ }
+
+ return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
+ }
+ token => {
+ let result = if self.parser.parse_qualified() {
+ self.input.reset(&start);
+ let delimiters = if self.parser.parse_declarations() {
+ Delimiter::Semicolon | Delimiter::CurlyBracketBlock
+ } else {
+ Delimiter::CurlyBracketBlock
+ };
+ parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters)
+ } else {
+ let token = token.clone();
+ self.input.parse_until_after(Delimiter::Semicolon, |_| {
+ Err(start.source_location().new_unexpected_token_error(token))
+ })
+ };
+ return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
+ }
+ }
+ }
+ }
+}
+
+/// Provides an iterator for rule list parsing at the top-level of a stylesheet.
+pub struct StyleSheetParser<'i, 't, 'a, P> {
+ /// The input given.
+ pub input: &'a mut Parser<'i, 't>,
+
+ /// The parser given.
+ pub parser: &'a mut P,
+
+ any_rule_so_far: bool,
+}
+
+impl<'i, 't, 'a, R, P, E: 'i> StyleSheetParser<'i, 't, 'a, P>
+where
+ P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ + AtRuleParser<'i, AtRule = R, Error = E>,
+{
+ /// The given `parser` needs to implement both `QualifiedRuleParser` and `AtRuleParser` traits.
+ /// However, either of them can be an empty `impl` since the traits provide default
+ /// implementations of their methods.
+ ///
+ /// The return type for finished qualified rules and at-rules also needs to be the same,
+ /// since `<RuleListParser as Iterator>::next` can return either. It could be a custom enum.
+ pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
+ Self {
+ input,
+ parser,
+ any_rule_so_far: false,
+ }
+ }
+}
+
+/// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or `Err(())` for an invalid one.
+impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P>
+where
+ P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ + AtRuleParser<'i, AtRule = R, Error = E>,
+{
+ type Item = Result<R, (ParseError<'i, E>, &'i str)>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ self.input.skip_cdc_and_cdo();
+ let start = self.input.state();
+ let at_keyword = match self.input.next_byte()? {
+ b'@' => match self.input.next_including_whitespace_and_comments() {
+ Ok(&Token::AtKeyword(ref name)) => Some(name.clone()),
+ _ => {
+ self.input.reset(&start);
+ None
+ }
+ },
+ _ => None,
+ };
+
+ if let Some(name) = at_keyword {
+ let first_stylesheet_rule = !self.any_rule_so_far;
+ self.any_rule_so_far = true;
+ if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
+ let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
+ let _: Result<(), ParseError<()>> =
+ self.input.parse_until_after(delimiters, |_| Ok(()));
+ } else {
+ return Some(parse_at_rule(
+ &start,
+ name.clone(),
+ self.input,
+ &mut *self.parser,
+ ));
+ }
+ } else {
+ self.any_rule_so_far = true;
+ let result = parse_qualified_rule(
+ &start,
+ self.input,
+ &mut *self.parser,
+ Delimiter::CurlyBracketBlock,
+ );
+ return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
+ }
+ }
+ }
+}
+
+/// Parse a single declaration, such as an `( /* ... */ )` parenthesis in an `@supports` prelude.
+pub fn parse_one_declaration<'i, 't, P, E>(
+ input: &mut Parser<'i, 't>,
+ parser: &mut P,
+) -> Result<<P as DeclarationParser<'i>>::Declaration, (ParseError<'i, E>, &'i str)>
+where
+ P: DeclarationParser<'i, Error = E>,
+{
+ let start_position = input.position();
+ input
+ .parse_entirely(|input| {
+ let name = input.expect_ident()?.clone();
+ input.expect_colon()?;
+ parser.parse_value(name, input)
+ })
+ .map_err(|e| (e, input.slice_from(start_position)))
+}
+
+/// Parse a single rule, such as for CSSOM’s `CSSStyleSheet.insertRule`.
+pub fn parse_one_rule<'i, 't, R, P, E>(
+ input: &mut Parser<'i, 't>,
+ parser: &mut P,
+) -> Result<R, ParseError<'i, E>>
+where
+ P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ + AtRuleParser<'i, AtRule = R, Error = E>,
+{
+ input.parse_entirely(|input| {
+ input.skip_whitespace();
+ let start = input.state();
+ let at_keyword = if input.next_byte() == Some(b'@') {
+ match *input.next_including_whitespace_and_comments()? {
+ Token::AtKeyword(ref name) => Some(name.clone()),
+ _ => {
+ input.reset(&start);
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ if let Some(name) = at_keyword {
+ parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
+ } else {
+ parse_qualified_rule(&start, input, parser, Delimiter::CurlyBracketBlock)
+ }
+ })
+}
+
+fn parse_at_rule<'i, 't, P, E>(
+ start: &ParserState,
+ name: CowRcStr<'i>,
+ input: &mut Parser<'i, 't>,
+ parser: &mut P,
+) -> Result<<P as AtRuleParser<'i>>::AtRule, (ParseError<'i, E>, &'i str)>
+where
+ P: AtRuleParser<'i, Error = E>,
+{
+ let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
+ let result = parse_until_before(input, delimiters, |input| parser.parse_prelude(name, input));
+ match result {
+ Ok(prelude) => {
+ let result = match input.next() {
+ Ok(&Token::Semicolon) | Err(_) => parser
+ .rule_without_block(prelude, start)
+ .map_err(|()| input.new_unexpected_token_error(Token::Semicolon)),
+ Ok(&Token::CurlyBracketBlock) => {
+ parse_nested_block(input, |input| parser.parse_block(prelude, start, input))
+ }
+ Ok(_) => unreachable!(),
+ };
+ result.map_err(|e| (e, input.slice_from(start.position())))
+ }
+ Err(error) => {
+ let end_position = input.position();
+ match input.next() {
+ Ok(&Token::CurlyBracketBlock) | Ok(&Token::Semicolon) | Err(_) => {}
+ _ => unreachable!(),
+ };
+ Err((error, input.slice(start.position()..end_position)))
+ }
+ }
+}
+
+fn parse_qualified_rule<'i, 't, P, E>(
+ start: &ParserState,
+ input: &mut Parser<'i, 't>,
+ parser: &mut P,
+ delimiters: Delimiters,
+) -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
+where
+ P: QualifiedRuleParser<'i, Error = E>,
+{
+ let prelude = parse_until_before(input, delimiters, |input| parser.parse_prelude(input));
+ input.expect_curly_bracket_block()?;
+ // Do this here so that we consume the `{` even if the prelude is `Err`.
+ let prelude = prelude?;
+ parse_nested_block(input, |input| parser.parse_block(prelude, &start, input))
+}