Skip to main content

oxc_css_parser/parser/at_rule/
keyframes.rs

1use super::Parser;
2use crate::{
3    Parse,
4    ast::*,
5    error::{Error, ErrorKind, PResult},
6    parser::state::ParserState,
7    pos::Span,
8    tokenizer::Token,
9    util,
10};
11
12// https://drafts.csswg.org/css-animations/#keyframes
13impl<'a> Parse<'a> for KeyframeBlock<'a> {
14    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
15        let first_selector = input.parse::<KeyframeSelector>()?;
16        let start = first_selector.span().start;
17
18        let mut selectors = input.vec_with_capacity(2);
19        selectors.push(first_selector);
20        let mut comma_spans = input.vec();
21        while let Some((_, comma_span)) = input.cursor.eat_comma()? {
22            comma_spans.push(comma_span);
23            selectors.push(input.parse()?);
24        }
25        debug_assert_eq!(comma_spans.len() + 1, selectors.len());
26
27        let block = input
28            .with_state(ParserState { in_keyframes_at_rule: false, ..input.state.clone() })
29            .parse::<SimpleBlock>()?;
30
31        let span = Span { start, end: block.span.end };
32        Ok(KeyframeBlock { selectors, comma_spans, block, span })
33    }
34}
35
36impl<'a> Parse<'a> for KeyframeSelector<'a> {
37    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
38        match &input.cursor.peek()?.token {
39            Token::Percentage(..) => Ok(KeyframeSelector::Percentage(input.parse()?)),
40            _ => {
41                let ident = input.parse()?;
42                match &ident {
43                    InterpolableIdent::Literal(ident)
44                        if !ident.name.eq_ignore_ascii_case("from")
45                            && !ident.name.eq_ignore_ascii_case("to") =>
46                    {
47                        input.recoverable_errors.push(Error {
48                            kind: ErrorKind::UnknownKeyframeSelectorIdent,
49                            span: ident.span.clone(),
50                        });
51                    }
52                    _ => {}
53                }
54                Ok(KeyframeSelector::Ident(ident))
55            }
56        }
57    }
58}
59
60// https://drafts.csswg.org/css-animations/#keyframes
61impl<'a> Parse<'a> for KeyframesName<'a> {
62    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
63        match &input.cursor.peek()?.token {
64            Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
65                let ident = input.parse()?;
66                match &ident {
67                    InterpolableIdent::Literal(ident)
68                        if util::is_css_wide_keyword(ident.name)
69                            || ident.name.eq_ignore_ascii_case("default") =>
70                    {
71                        input.recoverable_errors.push(Error {
72                            kind: ErrorKind::CSSWideKeywordDisallowed,
73                            span: ident.span.clone(),
74                        });
75                    }
76                    _ => {}
77                }
78                Ok(KeyframesName::Ident(ident))
79            }
80            Token::AtKeyword(..) => input.parse().map(KeyframesName::LessVariable),
81            Token::Tilde(..) => input.parse().map(KeyframesName::LessEscapedStr),
82            _ => input.parse().map(KeyframesName::Str),
83        }
84    }
85}