1use std::collections::VecDeque;
19use std::fmt;
20
21use crate::engine::css::values::CssIdent;
22
23use super::tokenizer::{Token, Tokenizer};
24use super::values::{CssValue, Unit};
25
26#[derive(Debug, Clone)]
31pub enum CssNodeType {
32 Stylesheet,
34
35 Rule {
37 selectors: Vec<ComplexSelector>,
39 },
40
41 AtRule {
43 name: String,
45
46 params: AtQuery,
47 },
48
49 Keyframes {
51 name: String,
52 stops: Vec<KeyframeStop>,
53 },
54
55 Declaration {
57 name: String,
59
60 value: CssValue,
61 },
62}
63
64#[derive(Debug, Clone, PartialEq)]
66pub struct KeyframeSelector(pub CssValue);
67
68#[derive(Debug, Clone)]
70pub struct KeyframeStop {
71 pub selectors: Vec<KeyframeSelector>,
72 pub declarations: Vec<(String, CssValue, bool)>,
73}
74
75#[derive(Debug, Clone, PartialEq)]
76pub enum AtQuery {
77 Keyword(String), Condition {
79 name: String, value: CssValue, },
82 Range {
83 left: Option<(CssValue, RangeOperator)>,
84 name: String,
85 right: Option<(RangeOperator, CssValue)>,
86 },
87 Group(Vec<AtQuery>), }
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum RangeOperator {
92 Less,
93 LessEqual,
94 Equal,
95 GreaterEqual,
96 Greater,
97}
98
99#[derive(Debug)]
104pub struct CssNode {
105 node: CssNodeType,
107
108 children: Vec<CssNode>,
110}
111
112impl CssNode {
113 pub fn node(&self) -> &CssNodeType {
114 &self.node
115 }
116 pub fn children(&self) -> &Vec<CssNode> {
117 &self.children
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub struct Selector {
123 pub is_nesting: bool,
130
131 pub tag: Option<String>,
136
137 pub id: Option<String>,
139
140 pub classes: Vec<String>,
142
143 pub attributes: Vec<AttributeSelector>,
145
146 pub pseudo_classes: Vec<PseudoClass>,
148
149 pub pseudo_element: Option<String>,
151}
152
153impl Selector {
154 fn is_compound(&self) -> bool {
157 self.tag.is_some()
158 || self.id.is_some()
159 || !self.classes.is_empty()
160 || !self.attributes.is_empty()
161 || !self.pseudo_classes.is_empty()
162 || self.pseudo_element.is_some()
163 }
164
165 fn merge_from(&mut self, other: &Selector) {
167 if let Some(tag) = &other.tag {
168 self.tag = Some(tag.clone());
169 }
170 if let Some(id) = &other.id {
171 self.id = Some(id.clone());
172 }
173 self.classes.extend(other.classes.iter().cloned());
174 self.attributes.extend(other.attributes.iter().cloned());
175 self.pseudo_classes
176 .extend(other.pseudo_classes.iter().cloned());
177 if let Some(pe) = &other.pseudo_element {
178 self.pseudo_element = Some(pe.clone());
179 }
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
185pub enum PseudoClass {
186 Simple(String),
188 SelectorList {
190 name: String,
192 selectors: Vec<ComplexSelector>,
194 },
195 Lang(String),
198 Nth {
200 name: String,
202 a: i32,
204 b: i32,
206 },
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Hash)]
211pub struct AttributeSelector {
212 pub name: String,
214 pub operator: AttributeSelectorOperator,
216 pub value: Option<String>,
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
222pub enum AttributeSelectorOperator {
223 Exists,
225 Equals,
227 Includes,
229 DashMatch,
231 Prefix,
233 Suffix,
235 Substring,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242pub enum Combinator {
243 Descendant,
245 Child,
247 NextSibling,
249 SubsequentSibling,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, Hash)]
254pub struct SelectorPart {
255 pub selector: Selector,
257
258 pub combinator: Option<Combinator>,
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, Hash)]
282pub struct ComplexSelector {
283 pub parts: Vec<SelectorPart>,
284}
285
286impl ComplexSelector {
287 pub fn empty() -> Self {
288 Self { parts: Vec::new() }
289 }
290
291 pub fn nest(&self, child: &Self) -> Self {
292 if self.parts.is_empty() {
293 return child.clone();
294 }
295 if child.parts.is_empty() {
296 return self.clone();
297 }
298
299 let has_nesting = child.parts.iter().any(|p| p.selector.is_nesting);
300
301 if !has_nesting {
302 let mut parts = child.parts.clone();
304 parts
305 .last_mut()
306 .expect("child selector is known to be non-empty")
307 .combinator = Some(Combinator::Descendant);
308 parts.extend(self.parts.iter().cloned());
309 return Self { parts };
310 }
311
312 let mut result_parts: Vec<SelectorPart> = Vec::new();
314
315 for child_part in &child.parts {
316 if child_part.selector.is_nesting {
317 if child_part.selector.is_compound() {
318 let mut merged = self.parts[0].selector.clone();
322 merged.merge_from(&child_part.selector);
323 merged.is_nesting = false;
324
325 let combinator = child_part.combinator.or(self.parts[0].combinator);
326
327 result_parts.push(SelectorPart {
328 selector: merged,
329 combinator,
330 });
331
332 for parent_part in self.parts.iter().skip(1) {
333 result_parts.push(parent_part.clone());
334 }
335 } else {
336 let child_combinator = child_part.combinator;
340 let parent_len = self.parts.len();
341 for (i, parent_part) in self.parts.iter().enumerate() {
342 let mut part = parent_part.clone();
343 if i == parent_len - 1 {
344 part.combinator = child_combinator;
345 }
346 result_parts.push(part);
347 }
348 }
349 } else {
350 result_parts.push(child_part.clone());
351 }
352 }
353
354 Self {
355 parts: result_parts,
356 }
357 }
358}
359
360fn parse_an_plus_b(tokens: &[Token<'_>]) -> Option<(i32, i32)> {
362 let mut expression = String::new();
363 for token in tokens {
364 match token {
365 Token::Whitespace | Token::Comment(_) => {}
366 Token::Ident(value) => expression.push_str(&value.to_ascii_lowercase()),
367 Token::Number(value) if value.fract() == 0.0 => {
368 expression.push_str(&(*value as i32).to_string());
369 }
370 Token::Dimension(value, unit) if value.fract() == 0.0 => {
371 expression.push_str(&(*value as i32).to_string());
372 expression.push_str(&unit.to_ascii_lowercase());
373 }
374 Token::Delim(value @ ('+' | '-')) => expression.push(*value),
375 _ => return None,
376 }
377 }
378
379 match expression.as_str() {
380 "odd" => return Some((2, 1)),
381 "even" => return Some((2, 0)),
382 _ => {}
383 }
384
385 if let Some(n_index) = expression.find('n') {
386 if expression[n_index + 1..].contains('n') {
387 return None;
388 }
389 let coefficient = match &expression[..n_index] {
390 "" | "+" => 1,
391 "-" => -1,
392 value => value.parse().ok()?,
393 };
394 let offset = match &expression[n_index + 1..] {
395 "" => 0,
396 value => value.parse().ok()?,
397 };
398 Some((coefficient, offset))
399 } else {
400 Some((0, expression.parse().ok()?))
401 }
402}
403
404#[derive(Clone)]
406pub struct Parser<'a> {
407 tokenizer: Tokenizer<'a>,
409
410 brace_depth: usize,
412
413 lookahead: VecDeque<Token<'a>>,
417}
418
419#[derive(Debug, Clone, PartialEq, Eq)]
421pub enum ParserErrorKind {
422 UnexpectedToken {
424 expected: &'static str,
425 found: String, },
427
428 UnexpectedEOF,
430
431 InvalidSyntax,
433
434 MismatchedDelimiter { expected: char, found: char },
436}
437
438impl fmt::Display for ParserErrorKind {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 write!(f, "{:?}", self)
441 }
442}
443
444#[derive(Debug, Clone, PartialEq, Eq)]
446pub struct ParserError {
447 pub kind: ParserErrorKind,
449 pub context: Vec<String>,
451}
452
453impl ParserError {
454 pub fn with_context(mut self, ctx: impl Into<String>) -> Self {
455 self.context.push(ctx.into());
456 self
457 }
458}
459
460impl fmt::Display for ParserError {
461 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462 let mut ctx = self.context.clone();
463 ctx.reverse();
464 write!(
465 f,
466 "CssParserError: {}, (Context:[{}])",
467 self.kind,
468 ctx.join(" <-")
469 )
470 }
471}
472
473impl std::error::Error for ParserError {}
474
475pub type ParseResult<T> = Result<T, ParserError>;
477
478impl<'a> Parser<'a> {
479 pub fn new(input: &'a str) -> Self {
481 Self {
482 tokenizer: Tokenizer::new(input),
483 brace_depth: 0,
484 lookahead: VecDeque::new(),
485 }
486 }
487
488 fn ensure_lookahead(&mut self, n: usize) {
489 while self.lookahead.len() <= n {
490 let tok = self.tokenizer.next_token();
491 self.lookahead.push_back(tok);
492 }
493 }
494
495 fn peek_next_token(&mut self, cursor_size: usize) -> &Token<'a> {
496 self.ensure_lookahead(cursor_size);
497 &self.lookahead[cursor_size]
498 }
499
500 fn peek_token(&mut self) -> &Token<'a> {
502 self.peek_next_token(0)
503 }
504
505 pub fn parse_declarations(&mut self) -> ParseResult<Vec<CssNode>> {
511 self.parse_declaration_and_nested_rule_list()
512 }
513
514 fn consume_token(&mut self) -> Token<'a> {
515 if let Some(tok) = self.lookahead.pop_front() {
516 tok
517 } else {
518 self.tokenizer.next_token()
519 }
520 }
521
522 pub fn parse(&mut self) -> ParseResult<CssNode> {
532 let mut stylesheet = CssNode {
533 node: CssNodeType::Stylesheet,
534 children: vec![],
535 };
536
537 loop {
538 let token = self.peek_token().clone();
539
540 match token {
541 Token::EOF => break,
542 Token::Whitespace | Token::Comment(_) => {
543 self.consume_token();
544 }
545 Token::AtKeyword(_) => {
546 let node = self
547 .parse_at_rule()
548 .map_err(|e| e.with_context("parse: failed to parse at-rule"))?;
549 log::debug!(target: "CssParser", "AtRule parsed: {:?}", node);
550 stylesheet.children.push(node);
551 }
552 _ => {
553 let node = self
554 .parse_rule()
555 .map_err(|e| e.with_context("parse: failed to parse rule"))?;
556 log::debug!(target: "CssParser", "Rule parsed: {:?}", node);
557 stylesheet.children.push(node);
558 }
559 }
560 }
561
562 Ok(stylesheet)
563 }
564
565 pub fn parse_lossy(&mut self) -> CssNode {
574 let mut stylesheet = CssNode {
575 node: CssNodeType::Stylesheet,
576 children: vec![],
577 };
578
579 loop {
580 let token = self.peek_token().clone();
581 let checkpoint = self.clone();
582 match token {
583 Token::EOF => break,
584 Token::Whitespace | Token::Comment(_) => {
585 self.consume_token();
586 }
587 Token::AtKeyword(_) => match self.parse_at_rule() {
588 Ok(node) => stylesheet.children.push(node),
589 Err(error) => {
590 log::warn!(
591 target: "CssParser",
592 "Skipping unsupported at-rule: {error}"
593 );
594 *self = checkpoint;
595 self.recover_top_level_item();
596 }
597 },
598 _ => match self.parse_rule() {
599 Ok(node) => stylesheet.children.push(node),
600 Err(error) => {
601 log::warn!(
602 target: "CssParser",
603 "Skipping unsupported CSS rule: {error}"
604 );
605 *self = checkpoint;
606 self.recover_top_level_item();
607 }
608 },
609 }
610 }
611
612 stylesheet
613 }
614
615 fn recover_top_level_item(&mut self) {
616 let mut depth = 0_usize;
617 let mut consumed_any = false;
618 loop {
619 match self.peek_token().clone() {
620 Token::EOF => break,
621 Token::AtKeyword(_) if depth == 0 && consumed_any => break,
626 Token::Delim('{') => {
627 depth += 1;
628 self.consume_token();
629 consumed_any = true;
630 }
631 Token::Delim('}') => {
632 self.consume_token();
633 if depth <= 1 {
634 break;
635 }
636 depth -= 1;
637 consumed_any = true;
638 }
639 Token::Delim(';') if depth == 0 => {
640 self.consume_token();
641 break;
642 }
643 _ => {
644 self.consume_token();
645 consumed_any = true;
646 }
647 }
648 }
649 }
650
651 fn parse_at_rule(&mut self) -> ParseResult<CssNode> {
652 let at_name = if let Token::AtKeyword(name) = self.consume_token() {
654 name.into_owned()
655 } else {
656 return Err(ParserError {
657 kind: ParserErrorKind::UnexpectedToken {
658 expected: "@keyword",
659 found: format!("{:?}", self.peek_token()),
660 },
661 context: vec![],
662 });
663 };
664
665 let mut prelude = vec![];
667 let mut paren_depth = 0;
668
669 loop {
670 match self.peek_token() {
671 Token::Delim('{') if paren_depth == 0 => break,
672 Token::Delim(';') if paren_depth == 0 => break,
673 Token::Delim('(') => {
674 paren_depth += 1;
675 prelude.push(self.consume_token());
676 }
677 Token::Delim(')') => {
678 paren_depth -= 1;
679 prelude.push(self.consume_token());
680 }
681 Token::EOF => break,
682 _ => prelude.push(self.consume_token()),
683 }
684 }
685
686 let params = Self::parse_at_query(prelude).map_err(|e| {
688 e.with_context("parse_at_rule: failed to parse params via parse_at_query")
689 })?;
690
691 if at_name.eq_ignore_ascii_case("keyframes") {
693 if self.peek_token() != &Token::Delim('{') {
694 if self.peek_token() == &Token::Delim(';') {
696 self.consume_token();
697 }
698 let name = Self::at_rule_name_from_params(¶ms).unwrap_or_default();
699 return Ok(CssNode {
700 node: CssNodeType::Keyframes {
701 name,
702 stops: vec![],
703 },
704 children: vec![],
705 });
706 }
707 self.consume_token(); self.brace_depth += 1;
709 let stops = self
710 .parse_keyframes_block()
711 .map_err(|e| e.with_context("parse_at_rule: failed to parse @keyframes block"))?;
712 if self.peek_token() == &Token::Delim('}') {
713 self.consume_token();
714 self.brace_depth = self.brace_depth.saturating_sub(1);
715 }
716 let name = Self::at_rule_name_from_params(¶ms).unwrap_or_default();
717 return Ok(CssNode {
718 node: CssNodeType::Keyframes { name, stops },
719 children: vec![],
720 });
721 }
722
723 let children = if self.peek_token() == &Token::Delim('{') {
724 self.consume_token();
725 self.brace_depth += 1;
726
727 let mut children = vec![];
728 while self.peek_token() != &Token::Delim('}') {
729 match self.peek_token() {
730 Token::EOF => {
731 return Err(ParserError {
732 kind: ParserErrorKind::UnexpectedEOF,
733 context: vec![],
734 });
735 }
736 Token::Whitespace => {
737 self.consume_token();
738 }
739 Token::AtKeyword(_) => {
740 let node = self.parse_at_rule().map_err(|e| {
741 e.with_context("parse_at_rule: failed to parse nested at-rule")
742 })?;
743 children.push(node);
744 }
745 _ => {
746 let mut cursor = 0;
747 let mut is_declaration = false;
748
749 loop {
750 match self.peek_next_token(cursor) {
751 Token::Delim('{') => {
752 break;
753 }
754 Token::Delim('}') => {
755 is_declaration = true;
756 break;
757 }
758 Token::EOF => {
759 return Err(ParserError {
760 kind: ParserErrorKind::UnexpectedEOF,
761 context: vec![],
762 });
763 }
764 _ => {}
765 }
766 cursor += 1;
767 }
768
769 let nodes = if is_declaration {
770 self.parse_declaration_and_nested_rule_list().map_err(|e| {
771 e.with_context(
772 "parse_at_rule: failed to parse declaration in block",
773 )
774 })?
775 } else {
776 vec![self.parse_rule().map_err(|e| {
777 e.with_context("parse_at_rule: failed to parse rule in block")
778 })?]
779 };
780
781 children.extend(nodes);
782 }
783 }
784 }
785
786 self.consume_token(); self.brace_depth -= 1;
788 children
789 } else {
790 if self.consume_token() != Token::Delim(';') {
791 return Err(ParserError {
792 kind: ParserErrorKind::UnexpectedToken {
793 expected: ";",
794 found: format!("{:?}", self.peek_token()),
795 },
796 context: vec![],
797 });
798 }
799 vec![]
800 };
801
802 Ok(CssNode {
803 node: CssNodeType::AtRule {
804 name: at_name,
805 params,
806 },
807 children,
808 })
809 }
810
811 fn at_rule_name_from_params(params: &AtQuery) -> Option<String> {
812 fn from_query(query: &AtQuery) -> Option<String> {
813 match query {
814 AtQuery::Keyword(name) => Some(name.clone()),
815 AtQuery::Group(items) => items.iter().find_map(from_query),
816 _ => None,
817 }
818 }
819 from_query(params)
820 }
821
822 fn parse_keyframes_block(&mut self) -> ParseResult<Vec<KeyframeStop>> {
823 let mut stops = Vec::new();
824 while self.peek_token() != &Token::Delim('}') {
825 match self.peek_token() {
826 Token::EOF => {
827 return Err(ParserError {
828 kind: ParserErrorKind::UnexpectedEOF,
829 context: vec!["parse_keyframes_block".into()],
830 });
831 }
832 Token::Whitespace | Token::Comment(_) => {
833 self.consume_token();
834 }
835 _ => {
836 let mut selectors: Vec<KeyframeSelector> = Vec::new();
838 loop {
839 match self.peek_token() {
840 Token::Delim('{') | Token::EOF | Token::Delim('}') => break,
841 Token::Whitespace | Token::Comment(_) => {
842 self.consume_token();
843 }
844 Token::Delim(',') => {
845 self.consume_token();
846 }
847 Token::Ident(name) => {
848 selectors.push(KeyframeSelector(CssValue::Keyword(
849 name.clone().into_owned().into(),
850 )));
851 self.consume_token();
852 }
853 Token::Number(n) => {
854 let n = *n;
855 self.consume_token();
856 selectors
858 .push(KeyframeSelector(CssValue::Length(n, Unit::Percent)));
859 }
860 Token::Dimension(n, unit) => {
861 let value = if unit.as_ref() == "%" {
862 CssValue::Length(*n, Unit::Percent)
863 } else {
864 CssValue::Length(
865 *n,
866 match unit.as_ref() {
867 "px" => Unit::Px,
868 _ => Unit::Unknown,
869 },
870 )
871 };
872 selectors.push(KeyframeSelector(value));
873 self.consume_token();
874 }
875 _ => {
876 self.consume_token();
877 }
878 }
879 }
880 if self.peek_token() != &Token::Delim('{') {
881 if self.peek_token() != &Token::EOF
883 && self.peek_token() != &Token::Delim('}')
884 {
885 self.consume_token();
886 }
887 continue;
888 }
889 self.consume_token(); self.brace_depth += 1;
891 let declaration_nodes =
892 self.parse_declaration_and_nested_rule_list().map_err(|e| {
893 e.with_context(
894 "parse_keyframes_block: failed to parse stop declarations",
895 )
896 })?;
897 if self.peek_token() == &Token::Delim('}') {
898 self.consume_token();
899 self.brace_depth = self.brace_depth.saturating_sub(1);
900 }
901 if !selectors.is_empty() {
902 let mut declarations = Vec::new();
903 for node in declaration_nodes {
904 if let CssNodeType::Declaration { name, value } = node.node {
905 let (value, important) = Self::extract_important_value(&value);
906 declarations.push((name, value, important));
907 }
908 }
909 stops.push(KeyframeStop {
910 selectors,
911 declarations,
912 });
913 }
914 }
915 }
916 }
917 Ok(stops)
918 }
919
920 fn extract_important_value(value: &CssValue) -> (CssValue, bool) {
921 match value {
922 CssValue::List(list) if list.len() >= 2 => {
923 let len = list.len();
924 let is_important = matches!(
925 (&list[len - 2], &list[len - 1]),
926 (CssValue::Keyword(bang), CssValue::Keyword(ident))
927 if bang == "!" && ident.eq_ignore_ascii_case("important")
928 );
929 if is_important {
930 let value = if len - 2 == 1 {
931 list[0].clone()
932 } else {
933 CssValue::List(list[..len - 2].to_vec())
934 };
935 (value, true)
936 } else {
937 (value.clone(), false)
938 }
939 }
940 _ => (value.clone(), false),
941 }
942 }
943
944 fn parse_at_query(tokens: Vec<Token<'_>>) -> ParseResult<AtQuery> {
945 let mut cursor = 0;
946 let items = Self::parse_at_query_list(&tokens, &mut cursor)?;
947 Ok(AtQuery::Group(items))
948 }
949
950 fn parse_at_query_list(tokens: &[Token<'_>], cursor: &mut usize) -> ParseResult<Vec<AtQuery>> {
951 let mut items = Vec::new();
952
953 while *cursor < tokens.len() {
954 match &tokens[*cursor] {
955 Token::Whitespace => {
956 *cursor += 1;
957 }
958
959 Token::Delim('(') => {
960 *cursor += 1;
961 let group = Self::parse_at_query_list(tokens, cursor)?;
962 items.push(AtQuery::Group(group));
963 }
964
965 Token::Delim(')') => {
966 *cursor += 1;
967 break;
968 }
969
970 Token::Ident(_) | Token::Number(_) | Token::Dimension(_, _) => {
971 items.push(Self::parse_at_query_item(tokens, cursor)?);
972 }
973
974 Token::Delim(',') => {
975 items.push(AtQuery::Keyword(",".into()));
976 *cursor += 1;
977 }
978
979 _ => {
980 *cursor += 1;
981 }
982 }
983 }
984
985 Ok(items)
986 }
987
988 fn parse_at_query_item(tokens: &[Token<'_>], cursor: &mut usize) -> ParseResult<AtQuery> {
989 let start = *cursor;
990
991 if let Some(range) = Self::parse_at_query_range(tokens, cursor)? {
993 return Ok(range);
994 }
995
996 *cursor = start;
997
998 let name = match &tokens[*cursor] {
999 Token::Ident(s) => s.clone().into_owned(),
1000 _ => {
1001 return Err(ParserError {
1002 kind: ParserErrorKind::UnexpectedToken {
1003 expected: "Ident(_)",
1004 found: format!("{:?}", tokens[*cursor]),
1005 },
1006 context: vec![],
1007 });
1008 }
1009 };
1010 *cursor += 1;
1011
1012 let mut colon = *cursor;
1013 while matches!(
1014 tokens.get(colon),
1015 Some(Token::Whitespace | Token::Comment(_))
1016 ) {
1017 colon += 1;
1018 }
1019 if matches!(tokens.get(colon), Some(Token::Delim(':'))) {
1020 *cursor = colon + 1;
1021 let value = Self::parse_at_query_value(tokens, cursor)?;
1022 Ok(AtQuery::Condition { name, value })
1023 } else {
1024 Ok(AtQuery::Keyword(name))
1025 }
1026 }
1027
1028 fn parse_at_query_range(
1029 tokens: &[Token<'_>],
1030 cursor: &mut usize,
1031 ) -> ParseResult<Option<AtQuery>> {
1032 let start = *cursor;
1033
1034 if let Token::Ident(name) = tokens.get(*cursor).cloned().unwrap() {
1036 *cursor += 1;
1037 Self::skip_at_query_whitespace(tokens, cursor);
1038
1039 if let Some(operator) = Self::parse_range_operator(tokens, cursor) {
1040 Self::skip_at_query_whitespace(tokens, cursor);
1041
1042 if let Some(value) = Self::try_parse_at_query_value(tokens, cursor)? {
1043 return Ok(Some(AtQuery::Range {
1044 left: None,
1045 name: name.into_owned(),
1046 right: Some((operator, value)),
1047 }));
1048 }
1049 }
1050 }
1051
1052 *cursor = start;
1053
1054 let Some(left) = Self::try_parse_at_query_value(tokens, cursor)? else {
1056 return Ok(None);
1057 };
1058
1059 Self::skip_at_query_whitespace(tokens, cursor);
1060
1061 let Some(left_operator) = Self::parse_range_operator(tokens, cursor) else {
1062 *cursor = start;
1063 return Ok(None);
1064 };
1065
1066 Self::skip_at_query_whitespace(tokens, cursor);
1067
1068 let name = match tokens.get(*cursor) {
1069 Some(Token::Ident(name)) => name.clone().into_owned(),
1070 _ => {
1071 *cursor = start;
1072 return Ok(None);
1073 }
1074 };
1075 *cursor += 1;
1076
1077 Self::skip_at_query_whitespace(tokens, cursor);
1078
1079 let right = if let Some(operator) = Self::parse_range_operator(tokens, cursor) {
1081 Self::skip_at_query_whitespace(tokens, cursor);
1082
1083 let Some(value) = Self::try_parse_at_query_value(tokens, cursor)? else {
1084 *cursor = start;
1085 return Ok(None);
1086 };
1087
1088 Some((operator, value))
1089 } else {
1090 None
1091 };
1092
1093 Ok(Some(AtQuery::Range {
1094 left: Some((left, left_operator)),
1095 name,
1096 right,
1097 }))
1098 }
1099
1100 fn parse_range_operator(tokens: &[Token<'_>], cursor: &mut usize) -> Option<RangeOperator> {
1101 match tokens.get(*cursor) {
1102 Some(Token::Delim('<')) => {
1103 if matches!(tokens.get(*cursor + 1), Some(Token::Delim('='))) {
1104 *cursor += 2;
1105 Some(RangeOperator::LessEqual)
1106 } else {
1107 *cursor += 1;
1108 Some(RangeOperator::Less)
1109 }
1110 }
1111
1112 Some(Token::Delim('>')) => {
1113 if matches!(tokens.get(*cursor + 1), Some(Token::Delim('='))) {
1114 *cursor += 2;
1115 Some(RangeOperator::GreaterEqual)
1116 } else {
1117 *cursor += 1;
1118 Some(RangeOperator::Greater)
1119 }
1120 }
1121
1122 Some(Token::Delim('=')) => {
1123 *cursor += 1;
1124 Some(RangeOperator::Equal)
1125 }
1126
1127 _ => None,
1128 }
1129 }
1130
1131 fn skip_at_query_whitespace(tokens: &[Token<'_>], cursor: &mut usize) {
1132 while matches!(
1133 tokens.get(*cursor),
1134 Some(Token::Whitespace | Token::Comment(_))
1135 ) {
1136 *cursor += 1;
1137 }
1138 }
1139
1140 fn try_parse_at_query_value(
1141 tokens: &[Token<'_>],
1142 cursor: &mut usize,
1143 ) -> ParseResult<Option<CssValue>> {
1144 let start = *cursor;
1145
1146 match tokens.get(*cursor) {
1147 Some(Token::Number(_)) | Some(Token::Dimension(_, _)) | Some(Token::Function(_)) => {
1148 match Self::parse_at_query_value(tokens, cursor) {
1149 Ok(value) => Ok(Some(value)),
1150 Err(_) => {
1151 *cursor = start;
1152 Ok(None)
1153 }
1154 }
1155 }
1156
1157 _ => Ok(None),
1158 }
1159 }
1160
1161 fn parse_at_query_value(tokens: &[Token<'_>], cursor: &mut usize) -> ParseResult<CssValue> {
1162 let mut buf = Vec::new();
1163 let mut paren_depth = 0;
1164
1165 while *cursor < tokens.len() {
1166 match &tokens[*cursor] {
1167 Token::Delim('(') => {
1168 paren_depth += 1;
1169 buf.push(tokens[*cursor].clone());
1170 *cursor += 1;
1171 }
1172 Token::Delim(')') if paren_depth == 0 => break,
1173 Token::Delim(')') => {
1174 paren_depth -= 1;
1175 buf.push(tokens[*cursor].clone());
1176 *cursor += 1;
1177 }
1178 _ => {
1179 buf.push(tokens[*cursor].clone());
1180 *cursor += 1;
1181 }
1182 }
1183 }
1184
1185 Self::parse_tokens_to_css_value(buf)
1186 }
1187
1188 fn parse_rule(&mut self) -> ParseResult<CssNode> {
1192 let selectors = self.parse_selector_list();
1194
1195 match self.consume_token() {
1197 Token::Delim('{') => self.brace_depth += 1,
1198 token => {
1199 return Err(ParserError {
1200 kind: ParserErrorKind::UnexpectedToken {
1201 expected: "{",
1202 found: format!("{:?}", token),
1203 },
1204 context: vec![format!(
1205 "While parsing rule with selectors: {}",
1206 selectors
1207 .iter()
1208 .map(|s| format!("{:?}", s))
1209 .collect::<Vec<_>>()
1210 .join(", ")
1211 )],
1212 });
1213 }
1214 }
1215
1216 let mut children = vec![];
1218 loop {
1219 let token = self.peek_token().clone();
1220 match token {
1221 Token::Delim('}') => {
1222 self.consume_token();
1223 self.brace_depth -= 1;
1224 break;
1225 }
1226 Token::EOF => {
1227 return Err(ParserError {
1228 kind: ParserErrorKind::UnexpectedEOF,
1229 context: vec![],
1230 });
1231 }
1232 _ => {
1233 let mut child = self.parse_declaration_and_nested_rule_list().map_err(|e| {
1234 e.with_context("parse_rule: failed to parse declaration list")
1235 })?;
1236 children.append(&mut child);
1237 }
1238 }
1239 }
1240
1241 Ok(CssNode {
1242 node: CssNodeType::Rule { selectors },
1243 children,
1244 })
1245 }
1246
1247 fn parse_selector_list(&mut self) -> Vec<ComplexSelector> {
1251 self.parse_selector_list_until(None)
1252 }
1253
1254 fn parse_selector_list_until(&mut self, terminator: Option<char>) -> Vec<ComplexSelector> {
1257 let mut selectors = vec![];
1258 let mut parts = vec![];
1259
1260 let mut current_selector: Option<Selector> = None;
1261 let mut current_combinator: Option<Combinator> = None;
1262
1263 loop {
1264 let token = self.peek_token().clone();
1265 match token {
1266 Token::Ident(name) => {
1267 let sel = current_selector.get_or_insert_with(|| Selector {
1268 is_nesting: false,
1269 tag: None,
1270 id: None,
1271 classes: vec![],
1272 attributes: vec![],
1273 pseudo_classes: vec![],
1274 pseudo_element: None,
1275 });
1276
1277 if sel.tag.is_none() {
1278 sel.tag = Some(name.into_owned());
1279 }
1280
1281 self.consume_token();
1282 }
1283
1284 Token::Hash(id) => {
1285 let sel = current_selector.get_or_insert_with(|| Selector {
1286 is_nesting: false,
1287 tag: None,
1288 id: None,
1289 classes: vec![],
1290 attributes: vec![],
1291 pseudo_classes: vec![],
1292 pseudo_element: None,
1293 });
1294 sel.id = Some(id.into_owned());
1295 self.consume_token();
1296 }
1297
1298 Token::Delim('.') => {
1299 self.consume_token();
1300 if let Token::Ident(class) = self.consume_token() {
1301 let sel = current_selector.get_or_insert_with(|| Selector {
1302 is_nesting: false,
1303 tag: None,
1304 id: None,
1305 classes: vec![],
1306 attributes: vec![],
1307 pseudo_classes: vec![],
1308 pseudo_element: None,
1309 });
1310 sel.classes.push(class.into_owned());
1311 }
1312 }
1313
1314 Token::Delim(':') => {
1315 self.consume_token();
1316 if self.peek_token() == &Token::Delim(':') {
1317 self.consume_token();
1319 if let Token::Ident(name) = self.consume_token() {
1320 let sel = current_selector.get_or_insert_with(|| Selector {
1321 is_nesting: false,
1322 tag: None,
1323 id: None,
1324 classes: vec![],
1325 attributes: vec![],
1326 pseudo_classes: vec![],
1327 pseudo_element: None,
1328 });
1329 sel.pseudo_element = Some(name.into_owned());
1330 }
1331 } else {
1332 let pseudo_class = match self.consume_token() {
1333 Token::Ident(name)
1334 if matches!(
1338 name.to_ascii_lowercase().as_str(),
1339 "before" | "after" | "first-line" | "first-letter"
1340 ) =>
1341 {
1342 let sel = current_selector.get_or_insert_with(|| Selector {
1343 is_nesting: false,
1344 tag: None,
1345 id: None,
1346 classes: vec![],
1347 attributes: vec![],
1348 pseudo_classes: vec![],
1349 pseudo_element: None,
1350 });
1351 sel.pseudo_element = Some(name.into_owned());
1352 continue;
1353 }
1354 Token::Ident(name) => Some(PseudoClass::Simple(name.into_owned())),
1355 Token::Function(name) => {
1356 if self.peek_token() == &Token::Delim('(') {
1357 self.consume_token();
1358 }
1359 let lower_name = name.to_ascii_lowercase();
1360 let pseudo = match lower_name.as_str() {
1361 "is" | "where" | "not" => PseudoClass::SelectorList {
1362 name: lower_name,
1363 selectors: self.parse_selector_list_until(Some(')')),
1364 },
1365 "lang" => {
1366 let mut language = String::new();
1367 while let Token::Ident(part) = self.peek_token().clone() {
1371 self.consume_token();
1372 if !language.is_empty() {
1373 language.push('-');
1374 }
1375 language.push_str(&part);
1376 }
1377 PseudoClass::Lang(language)
1378 }
1379 "nth-child" | "nth-last-child" | "nth-of-type"
1380 | "nth-last-of-type" => {
1381 let tokens = self.consume_until_closing_parenthesis();
1382 let (a, b) =
1383 parse_an_plus_b(&tokens).unwrap_or((0, i32::MIN));
1384 PseudoClass::Nth {
1385 name: lower_name,
1386 a,
1387 b,
1388 }
1389 }
1390 _ => {
1391 self.consume_until_closing_parenthesis();
1392 PseudoClass::SelectorList {
1393 name: lower_name,
1394 selectors: Vec::new(),
1395 }
1396 }
1397 };
1398 if self.peek_token() == &Token::Delim(')') {
1399 self.consume_token();
1400 }
1401 Some(pseudo)
1402 }
1403 _ => None,
1404 };
1405 if let Some(pseudo_class) = pseudo_class {
1406 let sel = current_selector.get_or_insert_with(|| Selector {
1407 is_nesting: false,
1408 tag: None,
1409 id: None,
1410 classes: vec![],
1411 attributes: vec![],
1412 pseudo_classes: vec![],
1413 pseudo_element: None,
1414 });
1415 sel.pseudo_classes.push(pseudo_class);
1416 }
1417 }
1418 }
1419
1420 Token::Delim('[') => {
1421 self.consume_token();
1422 while matches!(self.peek_token(), Token::Whitespace | Token::Comment(_)) {
1423 self.consume_token();
1424 }
1425
1426 let name = match self.consume_token() {
1427 Token::Ident(name) => name.into_owned(),
1428 _ => continue,
1429 };
1430
1431 while matches!(self.peek_token(), Token::Whitespace | Token::Comment(_)) {
1432 self.consume_token();
1433 }
1434
1435 let operator = match self.peek_token() {
1436 Token::Delim('=') => {
1437 self.consume_token();
1438 AttributeSelectorOperator::Equals
1439 }
1440 Token::Delim('~')
1441 | Token::Delim('|')
1442 | Token::Delim('^')
1443 | Token::Delim('$')
1444 | Token::Delim('*') => {
1445 let operator = match self.consume_token() {
1446 Token::Delim(c) => c,
1447 _ => unreachable!(),
1448 };
1449
1450 if self.peek_token() != &Token::Delim('=') {
1451 continue;
1452 }
1453 self.consume_token();
1454
1455 match operator {
1456 '~' => AttributeSelectorOperator::Includes,
1457 '|' => AttributeSelectorOperator::DashMatch,
1458 '^' => AttributeSelectorOperator::Prefix,
1459 '$' => AttributeSelectorOperator::Suffix,
1460 '*' => AttributeSelectorOperator::Substring,
1461 _ => unreachable!(),
1462 }
1463 }
1464 _ => AttributeSelectorOperator::Exists,
1465 };
1466
1467 let value = if operator == AttributeSelectorOperator::Exists {
1468 None
1469 } else {
1470 while matches!(self.peek_token(), Token::Whitespace | Token::Comment(_)) {
1471 self.consume_token();
1472 }
1473
1474 match self.consume_token() {
1475 Token::Ident(value) | Token::String(value) => Some(value.into_owned()),
1476 _ => continue,
1477 }
1478 };
1479
1480 while matches!(self.peek_token(), Token::Whitespace | Token::Comment(_)) {
1481 self.consume_token();
1482 }
1483 if self.peek_token() == &Token::Delim(']') {
1484 self.consume_token();
1485 let sel = current_selector.get_or_insert_with(|| Selector {
1486 is_nesting: false,
1487 tag: None,
1488 id: None,
1489 classes: vec![],
1490 attributes: vec![],
1491 pseudo_classes: vec![],
1492 pseudo_element: None,
1493 });
1494 sel.attributes.push(AttributeSelector {
1495 name,
1496 operator,
1497 value,
1498 });
1499 }
1500 }
1501
1502 Token::Whitespace | Token::Comment(_) => {
1503 if let Some(sel) = current_selector.take() {
1505 parts.push(SelectorPart {
1506 selector: sel,
1507 combinator: current_combinator.take(),
1508 });
1509 }
1510 if current_combinator.is_none() {
1511 current_combinator = Some(Combinator::Descendant);
1512 }
1513 self.consume_token();
1514 }
1515
1516 Token::Delim('>') => {
1517 if let Some(sel) = current_selector.take() {
1518 parts.push(SelectorPart {
1519 selector: sel,
1520 combinator: current_combinator.take(),
1521 });
1522 }
1523 current_combinator = Some(Combinator::Child);
1524 self.consume_token();
1525 }
1526
1527 Token::Delim('+') | Token::Delim('~') => {
1528 if let Some(sel) = current_selector.take() {
1529 parts.push(SelectorPart {
1530 selector: sel,
1531 combinator: current_combinator.take(),
1532 });
1533 }
1534 current_combinator = Some(if token == Token::Delim('+') {
1535 Combinator::NextSibling
1536 } else {
1537 Combinator::SubsequentSibling
1538 });
1539 self.consume_token();
1540 }
1541
1542 Token::Delim('*') => {
1543 current_selector.get_or_insert_with(|| Selector {
1544 is_nesting: false,
1545 tag: None,
1546 id: None,
1547 classes: vec![],
1548 attributes: vec![],
1549 pseudo_classes: vec![],
1550 pseudo_element: None,
1551 });
1552 self.consume_token();
1553 }
1554
1555 Token::Delim('&') => {
1556 let sel = current_selector.get_or_insert_with(|| Selector {
1557 is_nesting: false,
1558 tag: None,
1559 id: None,
1560 classes: vec![],
1561 attributes: vec![],
1562 pseudo_classes: vec![],
1563 pseudo_element: None,
1564 });
1565 sel.is_nesting = true;
1566 self.consume_token();
1567 }
1568
1569 Token::Delim(',') => {
1570 if let Some(sel) = current_selector.take() {
1571 parts.push(SelectorPart {
1572 selector: sel,
1573 combinator: current_combinator.take(),
1574 });
1575 }
1576 parts.reverse();
1577 selectors.push(ComplexSelector {
1578 parts: parts.clone(),
1579 });
1580 parts.clear();
1581 current_combinator = None;
1582 self.consume_token();
1583
1584 while matches!(self.peek_token(), Token::Whitespace | Token::Comment(_)) {
1585 self.consume_token();
1586 }
1587 }
1588
1589 Token::Delim(')') if terminator == Some(')') => {
1590 if let Some(sel) = current_selector.take() {
1591 parts.push(SelectorPart {
1592 selector: sel,
1593 combinator: current_combinator.take(),
1594 });
1595 }
1596 if !parts.is_empty() {
1597 parts.reverse();
1598 selectors.push(ComplexSelector { parts });
1599 }
1600 break;
1601 }
1602
1603 Token::Delim('{') | Token::EOF => {
1604 if let Some(sel) = current_selector.take() {
1605 parts.push(SelectorPart {
1606 selector: sel,
1607 combinator: current_combinator.take(),
1608 });
1609 }
1610 if !parts.is_empty() {
1611 parts.reverse();
1612 selectors.push(ComplexSelector { parts });
1613 }
1614 break;
1615 }
1616
1617 Token::AtKeyword(_) => break,
1621
1622 _ => {
1623 self.consume_token();
1624 }
1625 }
1626 }
1627
1628 selectors
1629 }
1630
1631 fn consume_until_closing_parenthesis(&mut self) -> Vec<Token<'a>> {
1633 let mut tokens = Vec::new();
1634 let mut depth = 0;
1635 loop {
1636 match self.peek_token() {
1637 Token::EOF => break,
1638 Token::Delim(')') if depth == 0 => break,
1639 Token::Delim('(') => {
1640 depth += 1;
1641 tokens.push(self.consume_token());
1642 }
1643 Token::Delim(')') => {
1644 depth -= 1;
1645 tokens.push(self.consume_token());
1646 }
1647 _ => tokens.push(self.consume_token()),
1648 }
1649 }
1650 tokens
1651 }
1652
1653 fn parse_declaration_and_nested_rule_list(&mut self) -> ParseResult<Vec<CssNode>> {
1655 let mut children = vec![];
1656 let mut parsing_name = true;
1657 let mut name = String::new();
1658 let mut value_tokens = vec![];
1659
1660 loop {
1661 let mut cursor = 0;
1662
1663 loop {
1664 let token = self.peek_next_token(cursor);
1665
1666 match token {
1667 Token::Delim(':') if parsing_name => {
1668 for _ in 0..cursor {
1669 if let Token::Ident(s) = self.consume_token() {
1670 name.push_str(&s);
1671 }
1672 }
1673
1674 self.consume_token(); parsing_name = false;
1676 break;
1677 }
1678 Token::Delim(';') if !parsing_name => {
1679 for _ in 0..cursor {
1680 value_tokens.push(self.consume_token());
1681 }
1682
1683 self.consume_token(); children.push(CssNode {
1685 node: CssNodeType::Declaration {
1686 name: std::mem::take(&mut name),
1687 value: Self::parse_tokens_to_css_value(std::mem::take(
1688 &mut value_tokens,
1689 ))
1690 .map_err(|e| {
1691 e.with_context(
1692 "parse_declaration: failed to parse declaration value list",
1693 )
1694 })?,
1695 },
1696 children: vec![],
1697 });
1698
1699 parsing_name = true;
1700 break;
1701 }
1702 Token::Delim('{') => {
1703 children.push(self.parse_rule()?);
1704 cursor = 0;
1705 }
1706 Token::Delim('}') | Token::EOF => {
1707 if !parsing_name && !name.is_empty() {
1708 for _ in 0..cursor {
1709 value_tokens.push(self.consume_token());
1710 }
1711
1712 children.push(CssNode {
1713 node: CssNodeType::Declaration {
1714 name: std::mem::take(&mut name),
1715 value: Self::parse_tokens_to_css_value(std::mem::take(
1716 &mut value_tokens,
1717 ))?,
1718 },
1719 children: vec![],
1720 });
1721 } else {
1722 for _ in 0..cursor {
1723 self.consume_token();
1725 }
1726 }
1727
1728 break;
1729 }
1730 Token::Ident(s) if parsing_name => {
1731 if cursor == 0 {
1732 name.push_str(s);
1733 self.consume_token();
1734 break;
1735 }
1736
1737 cursor += 1;
1738 }
1739 _ => {
1740 cursor += 1;
1741 }
1742 }
1743 }
1744
1745 if matches!(self.peek_next_token(0), Token::Delim('}') | Token::EOF) {
1746 break;
1747 }
1748 }
1749
1750 Ok(children)
1751 }
1752
1753 fn parse_function_arguments(tokens: Vec<Token<'_>>) -> ParseResult<Vec<Vec<CssValue>>> {
1762 let mut arguments: Vec<Vec<Token<'_>>> = Vec::new();
1764 let mut current: Vec<Token<'_>> = Vec::new();
1765 let mut depth = 0usize;
1766
1767 for token in tokens {
1768 match token {
1769 Token::Delim('(') => {
1770 depth += 1;
1771 current.push(token);
1772 }
1773 Token::Delim(')') => {
1774 depth = depth.saturating_sub(1);
1775 current.push(token);
1776 }
1777 Token::Delim(',') if depth == 0 => {
1778 arguments.push(std::mem::take(&mut current));
1779 }
1780 _ => current.push(token),
1781 }
1782 }
1783 if !current.is_empty() || arguments.is_empty() {
1784 arguments.push(current);
1785 }
1786
1787 let mut result = Vec::new();
1789 for argument in arguments {
1790 let mut components = Vec::new();
1791 let mut component_tokens: Vec<Token<'_>> = Vec::new();
1792 let mut component_depth = 0usize;
1793
1794 for token in argument {
1795 match token {
1796 Token::Whitespace if component_depth == 0 => {
1797 if !component_tokens.is_empty() {
1798 let value = Self::parse_tokens_to_css_value(std::mem::take(
1799 &mut component_tokens,
1800 ))?;
1801 components.push(value);
1802 }
1803 }
1804 Token::Delim('(') => {
1805 component_depth += 1;
1806 component_tokens.push(token);
1807 }
1808 Token::Delim(')') => {
1809 component_depth = component_depth.saturating_sub(1);
1810 component_tokens.push(token);
1811 }
1812 _ => component_tokens.push(token),
1813 }
1814 }
1815 if !component_tokens.is_empty() {
1816 let value = Self::parse_tokens_to_css_value(component_tokens)?;
1817 components.push(value);
1818 }
1819 result.push(components);
1820 }
1821
1822 Ok(result)
1823 }
1824
1825 pub fn parse_media_query(query: &str) -> Option<AtQuery> {
1829 let mut tokenizer = crate::engine::css::tokenizer::Tokenizer::new(query);
1830 let tokens: Vec<Token<'_>> = std::iter::from_fn(|| {
1831 let token = tokenizer.next_token();
1832 (token != Token::EOF).then_some(token)
1833 })
1834 .collect();
1835 Self::parse_at_query(tokens).ok()
1836 }
1837
1838 pub fn parse_tokens_to_css_value(tokens: Vec<Token<'_>>) -> ParseResult<CssValue> {
1839 let mut values = vec![];
1840 let mut iter = tokens.into_iter().peekable();
1841
1842 while let Some(token) = iter.next() {
1843 log::debug!(target: "CssParser", "parse_tokens_to_css_value: token={:?}", token);
1844
1845 match token {
1846 Token::Ident(s) => values.push(CssValue::Keyword(s.into_owned().into())),
1847
1848 Token::Delim(',') => {
1849 values.push(CssValue::Separator);
1853 }
1854
1855 Token::Delim('(') => {
1856 let mut depth = 1;
1861 let mut group_tokens = vec![];
1862 for tok in iter.by_ref() {
1863 match &tok {
1864 Token::Delim('(') => {
1865 depth += 1;
1866 group_tokens.push(tok);
1867 }
1868 Token::Delim(')') => {
1869 depth -= 1;
1870 if depth == 0 {
1871 break;
1872 }
1873 group_tokens.push(tok);
1874 }
1875 _ => group_tokens.push(tok),
1876 }
1877 }
1878
1879 let inner = Self::parse_tokens_to_css_value(group_tokens)?;
1880 values.push(match inner {
1881 CssValue::List(values) => CssValue::List(values),
1882 other => CssValue::List(vec![other]),
1883 });
1884 }
1885
1886 Token::Delim(')') => {
1887 continue;
1889 }
1890
1891 Token::Delim(c) => {
1892 let mut s = [0_u8; 4];
1893 let s = c.encode_utf8(&mut s);
1894
1895 values.push(CssValue::Keyword(s.into()));
1896 }
1897
1898 Token::Number(n) => values.push(CssValue::Number(n)),
1899
1900 Token::String(s) => values.push(CssValue::String(s.into_owned())),
1901
1902 Token::Url(raw) => values.push(CssValue::Function(
1903 "url".to_string(),
1904 vec![vec![CssValue::String(raw.into_owned())]],
1905 )),
1906
1907 Token::Dimension(value, unit) => {
1908 let unit = match unit.as_ref() {
1909 "px" => Unit::Px,
1910 "cm" => Unit::Cm,
1911 "mm" => Unit::Mm,
1912 "in" => Unit::In,
1913 "pt" => Unit::Pt,
1914 "pc" => Unit::Pc,
1915 "em" => Unit::Em,
1916 "rem" => Unit::Rem,
1917 "%" => Unit::Percent,
1918 "vw" => Unit::Vw,
1919 "vh" => Unit::Vh,
1920 "vmin" => Unit::Vmin,
1921 "vmax" => Unit::Vmax,
1922 "deg" => Unit::Deg,
1923 "rad" => Unit::Rad,
1924 "grad" => Unit::Grad,
1925 "turn" => Unit::Turn,
1926 "s" => Unit::S,
1927 "ms" => Unit::Ms,
1928 "fr" => Unit::Fr,
1929 _ => Unit::Unknown,
1930 };
1931 values.push(CssValue::Length(value, unit));
1932 }
1933
1934 Token::Hash(s) => values.push(CssValue::Color(s.into_owned())),
1935
1936 Token::Function(name) => {
1937 let mut depth = 0;
1939 let mut func_tokens = vec![];
1940
1941 for tok in iter.by_ref() {
1942 match &tok {
1943 Token::Delim('(') => {
1944 depth += 1;
1945 if depth > 1 {
1946 func_tokens.push(tok);
1947 }
1948 }
1949 Token::Delim(')') => {
1950 depth -= 1;
1951 if depth == 0 {
1952 break;
1953 }
1954 func_tokens.push(tok);
1955 }
1956 _ => func_tokens.push(tok),
1957 }
1958 }
1959
1960 let args = Self::parse_function_arguments(func_tokens)
1961 .map_err(|e| e.with_context("parse function args"))?;
1962
1963 values.push(CssValue::Function(name.into_owned(), args));
1964 }
1965
1966 _ => continue,
1967 }
1968 }
1969
1970 Ok(match values.len() {
1972 0 => CssValue::Keyword(CssIdent::new_static("")),
1973 1 => values.remove(0),
1974 _ => CssValue::List(values),
1975 })
1976 }
1977}
1978
1979impl fmt::Display for CssNode {
1981 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1982 fmt_tree_node(self, f, &[])
1983 }
1984}
1985
1986fn fmt_tree_node(
1988 node: &CssNode,
1989 f: &mut fmt::Formatter<'_>,
1990 ancestors_last: &[bool],
1991) -> fmt::Result {
1992 let is_last = *ancestors_last.last().unwrap_or(&true);
1993 let connector = if ancestors_last.is_empty() {
1994 ""
1995 } else if is_last {
1996 "└── "
1997 } else {
1998 "├── "
1999 };
2000
2001 let mut prefix = String::new();
2002 for &ancestor_last in &ancestors_last[..ancestors_last.len().saturating_sub(1)] {
2003 prefix.push_str(if ancestor_last { " " } else { "│ " });
2004 }
2005
2006 writeln!(f, "{}{}{:?}", prefix, connector, node.node())?;
2007
2008 let child_count = node.children().len();
2009 for (i, child) in node.children().iter().enumerate() {
2010 let child_is_last = i == child_count - 1;
2011 let mut new_ancestors = ancestors_last.to_vec();
2012 new_ancestors.push(child_is_last);
2013 fmt_tree_node(child, f, &new_ancestors)?;
2014 }
2015
2016 Ok(())
2017}
2018
2019impl std::fmt::Display for Combinator {
2020 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2021 match self {
2022 Combinator::Descendant => f.write_str(" "),
2023 Combinator::Child => f.write_str(" > "),
2024 Combinator::NextSibling => f.write_str(" + "),
2025 Combinator::SubsequentSibling => f.write_str(" ~ "),
2026 }
2027 }
2028}
2029
2030fn write_an_plus_b(f: &mut std::fmt::Formatter<'_>, a: i32, b: i32) -> std::fmt::Result {
2033 if a == 0 {
2034 return write!(f, "{b}");
2035 }
2036 match a {
2037 1 => f.write_str("n")?,
2038 -1 => f.write_str("-n")?,
2039 _ => write!(f, "{a}n")?,
2040 }
2041 if b > 0 {
2042 write!(f, "+{b}")
2043 } else if b < 0 {
2044 write!(f, "{b}")
2045 } else {
2046 Ok(())
2047 }
2048}
2049
2050impl std::fmt::Display for PseudoClass {
2051 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2052 match self {
2053 PseudoClass::Simple(name) => write!(f, ":{name}"),
2054 PseudoClass::Lang(language) => write!(f, ":lang({language})"),
2055 PseudoClass::SelectorList { name, selectors } => {
2056 let arguments = selectors
2057 .iter()
2058 .map(ComplexSelector::to_string)
2059 .collect::<Vec<_>>()
2060 .join(", ");
2061 write!(f, ":{name}({arguments})")
2062 }
2063 PseudoClass::Nth { name, a, b } => {
2064 write!(f, ":{name}(")?;
2065 write_an_plus_b(f, *a, *b)?;
2066 f.write_str(")")
2067 }
2068 }
2069 }
2070}
2071
2072impl std::fmt::Display for Selector {
2073 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2074 if self.is_nesting {
2075 f.write_str("&")?;
2076 }
2077 if let Some(tag) = &self.tag {
2078 f.write_str(tag)?;
2079 }
2080 if let Some(id) = &self.id {
2081 write!(f, "#{id}")?;
2082 }
2083 for class in &self.classes {
2084 write!(f, ".{class}")?;
2085 }
2086 for attribute in &self.attributes {
2087 f.write_str("[")?;
2088 f.write_str(&attribute.name)?;
2089 match (&attribute.operator, &attribute.value) {
2090 (AttributeSelectorOperator::Exists, _) => {}
2091 (AttributeSelectorOperator::Equals, Some(value)) => {
2092 write!(f, "=\"{value}\"")?;
2093 }
2094 (AttributeSelectorOperator::Includes, Some(value)) => {
2095 write!(f, "~=\"{value}\"")?;
2096 }
2097 (AttributeSelectorOperator::DashMatch, Some(value)) => {
2098 write!(f, "|=\"{value}\"")?;
2099 }
2100 (AttributeSelectorOperator::Prefix, Some(value)) => {
2101 write!(f, "^=\"{value}\"")?;
2102 }
2103 (AttributeSelectorOperator::Suffix, Some(value)) => {
2104 write!(f, "$=\"{value}\"")?;
2105 }
2106 (AttributeSelectorOperator::Substring, Some(value)) => {
2107 write!(f, "*=\"{value}\"")?;
2108 }
2109
2110 _ => {}
2111 }
2112 f.write_str("]")?;
2113 }
2114 for pseudo_class in &self.pseudo_classes {
2115 write!(f, "{pseudo_class}")?;
2116 }
2117 if let Some(pseudo_element) = &self.pseudo_element {
2118 write!(f, "::{pseudo_element}")?;
2119 }
2120 Ok(())
2121 }
2122}
2123
2124impl std::fmt::Display for ComplexSelector {
2125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2126 for index in (0..self.parts.len()).rev() {
2131 write!(f, "{}", self.parts[index].selector)?;
2132 if index > 0
2133 && let Some(combinator) = self.parts[index - 1].combinator
2134 {
2135 write!(f, "{combinator}")?;
2136 }
2137 }
2138 Ok(())
2139 }
2140}
2141
2142#[cfg(test)]
2143mod tests {
2144 use super::*;
2145
2146 #[test]
2147 fn complex_selector_display_renders_combinators_and_compounds() {
2148 let stylesheet = Parser::new("div.a > p#x + span:hover { color: red; }")
2149 .parse()
2150 .unwrap();
2151 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2152 panic!("expected a rule");
2153 };
2154 assert_eq!(selectors[0].to_string(), "div.a > p#x + span:hover");
2155
2156 let stylesheet = Parser::new("ul li ~ a[rel=\"tag\"] { color: red; }")
2157 .parse()
2158 .unwrap();
2159 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2160 panic!("expected a rule");
2161 };
2162 assert_eq!(selectors[0].to_string(), "ul li ~ a[rel=\"tag\"]");
2163 }
2164
2165 #[test]
2166 fn parses_keyframes_rule() {
2167 let css = "@keyframes fade { from { opacity: 0; } 50% { opacity: 0.5; } to { opacity: 1 !important; } }";
2168 let sheet = Parser::new(css).parse().unwrap();
2169 assert_eq!(sheet.children().len(), 1);
2170 let CssNodeType::Keyframes { name, stops } = sheet.children()[0].node() else {
2171 panic!("expected keyframes node");
2172 };
2173 assert_eq!(name, "fade");
2174 assert_eq!(stops.len(), 3);
2175
2176 assert_eq!(stops[0].selectors.len(), 1);
2177 assert_eq!(stops[0].declarations.len(), 1);
2178 assert_eq!(stops[0].declarations[0].0, "opacity");
2179 assert!(!stops[0].declarations[0].2);
2180
2181 assert_eq!(stops[1].selectors.len(), 1);
2182 assert_eq!(stops[1].declarations[0].0, "opacity");
2183
2184 assert_eq!(stops[2].selectors.len(), 1);
2185 assert_eq!(stops[2].declarations[0].0, "opacity");
2186 assert!(stops[2].declarations[0].2);
2187 }
2188
2189 #[test]
2190 fn selector_display_renders_nth_arguments() {
2191 let stylesheet = Parser::new("li:nth-child(2n+1) { color: red; }")
2192 .parse()
2193 .unwrap();
2194 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2195 panic!("expected a rule");
2196 };
2197 assert_eq!(selectors[0].to_string(), "li:nth-child(2n+1)");
2198 }
2199
2200 #[test]
2201 fn parses_exact_attribute_selector() {
2202 let stylesheet = Parser::new(r#"input[type="hidden"] { display: none; }"#)
2203 .parse()
2204 .unwrap();
2205 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2206 panic!("expected CSS rule");
2207 };
2208 let selector = &selectors[0].parts[0].selector;
2209
2210 assert_eq!(selector.tag.as_deref(), Some("input"));
2211 assert_eq!(
2212 selector.attributes,
2213 vec![AttributeSelector {
2214 name: "type".into(),
2215 operator: AttributeSelectorOperator::Equals,
2216 value: Some("hidden".into()),
2217 }]
2218 );
2219 }
2220
2221 #[test]
2222 fn preserves_fractional_grid_units() {
2223 let stylesheet = Parser::new("main { grid-template-columns: 100px 2fr auto; }")
2224 .parse()
2225 .unwrap();
2226 let declaration = stylesheet.children()[0].children()[0].node();
2227 let CssNodeType::Declaration { value, .. } = declaration else {
2228 panic!("expected declaration");
2229 };
2230 assert_eq!(
2231 value,
2232 &CssValue::List(vec![
2233 CssValue::Length(100.0, Unit::Px),
2234 CssValue::Length(2.0, Unit::Fr),
2235 CssValue::Keyword("auto".into()),
2236 ])
2237 );
2238 }
2239
2240 #[test]
2241 fn preserves_grid_functions_and_area_strings() {
2242 let stylesheet = Parser::new(
2243 r#"main {
2244 grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
2245 grid-template-areas: "header header" "sidebar main";
2246 }"#,
2247 )
2248 .parse()
2249 .unwrap();
2250 let declarations = stylesheet.children()[0].children();
2251 let CssNodeType::Declaration { value, .. } = declarations[0].node() else {
2252 panic!("expected declaration");
2253 };
2254 assert_eq!(
2255 value,
2256 &CssValue::Function(
2257 "repeat".into(),
2258 vec![
2259 vec![CssValue::Keyword("auto-fit".into())],
2260 vec![CssValue::Function(
2261 "minmax".into(),
2262 vec![
2263 vec![CssValue::Length(100.0, Unit::Px)],
2264 vec![CssValue::Length(1.0, Unit::Fr)],
2265 ],
2266 )],
2267 ],
2268 )
2269 );
2270 let CssNodeType::Declaration { value, .. } = declarations[1].node() else {
2271 panic!("expected declaration");
2272 };
2273 assert_eq!(
2274 value,
2275 &CssValue::List(vec![
2276 CssValue::String("header header".into()),
2277 CssValue::String("sidebar main".into()),
2278 ])
2279 );
2280 }
2281
2282 #[test]
2283 fn function_arguments_preserve_comma_and_whitespace_boundaries() {
2284 let stylesheet = Parser::new("a { width: foo(a + b, c, d); }")
2285 .parse()
2286 .unwrap();
2287 let declarations = stylesheet.children()[0].children();
2288 let CssNodeType::Declaration { value, .. } = declarations[0].node() else {
2289 panic!("expected declaration");
2290 };
2291 assert_eq!(
2292 value,
2293 &CssValue::Function(
2294 "foo".into(),
2295 vec![
2296 vec![
2297 CssValue::Keyword("a".into()),
2298 CssValue::Keyword("+".into()),
2299 CssValue::Keyword("b".into()),
2300 ],
2301 vec![CssValue::Keyword("c".into())],
2302 vec![CssValue::Keyword("d".into())],
2303 ],
2304 )
2305 );
2306 }
2307
2308 #[test]
2309 fn github_calc_with_nested_parentheses_preserves_grouping() {
2310 let css =
2311 r#"main { font-size: calc( 8px + (12 - 8) * ( (100vw - 400px) / ( 800 - 400) ) ); }"#;
2312 let stylesheet = Parser::new(css).parse().unwrap();
2313 let declaration = stylesheet.children()[0].children()[0].node();
2314 let CssNodeType::Declaration { value, .. } = declaration else {
2315 panic!("expected declaration");
2316 };
2317 assert_eq!(
2319 value,
2320 &CssValue::Function(
2321 "calc".into(),
2322 vec![vec![
2323 CssValue::Length(8.0, Unit::Px),
2324 CssValue::Keyword("+".into()),
2325 CssValue::List(vec![
2326 CssValue::Number(12.0),
2327 CssValue::Keyword("-".into()),
2328 CssValue::Number(8.0),
2329 ]),
2330 CssValue::Keyword("*".into()),
2331 CssValue::List(vec![
2332 CssValue::List(vec![
2333 CssValue::Length(100.0, Unit::Vw),
2334 CssValue::Keyword("-".into()),
2335 CssValue::Length(400.0, Unit::Px),
2336 ]),
2337 CssValue::Keyword("/".into()),
2338 CssValue::List(vec![
2339 CssValue::Number(800.0),
2340 CssValue::Keyword("-".into()),
2341 CssValue::Number(400.0),
2342 ]),
2343 ]),
2344 ]],
2345 )
2346 );
2347 }
2348
2349 #[test]
2350 fn malformed_function_arguments_are_structurally_distinct() {
2351 let parse = |css: &str| {
2352 let stylesheet = Parser::new(&format!("a {{ width: {css}; }}"))
2353 .parse()
2354 .unwrap();
2355 let declarations = stylesheet.children()[0].children();
2356 let CssNodeType::Declaration { value, .. } = declarations[0].node() else {
2357 panic!("expected declaration");
2358 };
2359 value.clone()
2360 };
2361
2362 let comma_separated = parse("foo(a + b, c, d)");
2364 let whitespace_separated = parse("foo(a, + b c, d)");
2366
2367 assert_ne!(comma_separated, whitespace_separated);
2368 }
2369
2370 #[test]
2371 fn lossy_parser_resumes_after_recovering_a_failed_at_rule() {
2372 let mut parser = Parser::new("@media { @broken } .valid { color: green; }");
2373 let stylesheet = parser.parse_lossy();
2374
2375 assert_eq!(stylesheet.children().len(), 1);
2376 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2377 panic!("expected recovered CSS rule");
2378 };
2379 assert_eq!(selectors[0].parts[0].selector.tag.as_deref(), None);
2380 assert_eq!(
2381 selectors[0].parts[0].selector.classes,
2382 vec![String::from("valid")]
2383 );
2384 }
2385
2386 #[test]
2387 fn nesting_without_ampersand_uses_descendant_combinator() {
2388 let stylesheet = Parser::new(".parent { span { color: red; } }")
2389 .parse()
2390 .unwrap();
2391 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2392 panic!("expected parent rule");
2393 };
2394 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2395 else {
2396 panic!("expected child rule");
2397 };
2398
2399 assert_eq!(child[0].to_string(), "span");
2401
2402 let resolved = parent[0].nest(&child[0]);
2404 assert_eq!(resolved.to_string(), ".parent span");
2405 }
2406
2407 #[test]
2408 fn nesting_with_standalone_ampersand() {
2409 let stylesheet = Parser::new(".parent { & { color: red; } }")
2410 .parse()
2411 .unwrap();
2412 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2413 panic!("expected parent rule");
2414 };
2415 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2416 else {
2417 panic!("expected child rule");
2418 };
2419
2420 assert!(child[0].parts[0].selector.is_nesting);
2421
2422 let resolved = parent[0].nest(&child[0]);
2423 assert_eq!(resolved.to_string(), ".parent");
2424 }
2425
2426 #[test]
2427 fn nesting_with_ampersand_class_compound() {
2428 let stylesheet = Parser::new(".parent { &.highlight { color: red; } }")
2429 .parse()
2430 .unwrap();
2431 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2432 panic!("expected parent rule");
2433 };
2434 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2435 else {
2436 panic!("expected child rule");
2437 };
2438
2439 assert!(child[0].parts[0].selector.is_nesting);
2440 assert_eq!(child[0].parts[0].selector.classes, vec!["highlight"]);
2441
2442 let resolved = parent[0].nest(&child[0]);
2443 assert_eq!(resolved.to_string(), ".parent.highlight");
2444 }
2445
2446 #[test]
2447 fn nesting_with_ampersand_at_end_of_compound() {
2448 let stylesheet = Parser::new(".parent { .sidebar& { color: red; } }")
2449 .parse()
2450 .unwrap();
2451 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2452 panic!("expected parent rule");
2453 };
2454 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2455 else {
2456 panic!("expected child rule");
2457 };
2458
2459 assert!(child[0].parts[0].selector.is_nesting);
2460 assert_eq!(child[0].parts[0].selector.classes, vec!["sidebar"]);
2461
2462 let resolved = parent[0].nest(&child[0]);
2463 assert_eq!(resolved.to_string(), ".parent.sidebar");
2464 }
2465
2466 #[test]
2467 fn nesting_with_ampersand_and_child_combinator() {
2468 let stylesheet = Parser::new(".parent { & > span { color: red; } }")
2469 .parse()
2470 .unwrap();
2471 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2472 panic!("expected parent rule");
2473 };
2474 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2475 else {
2476 panic!("expected child rule");
2477 };
2478
2479 let resolved = parent[0].nest(&child[0]);
2480 assert_eq!(resolved.to_string(), ".parent > span");
2481 }
2482
2483 #[test]
2484 fn nesting_with_multiple_selectors_using_ampersand() {
2485 let stylesheet = Parser::new(".parent { &.a, &.b { color: red; } }")
2486 .parse()
2487 .unwrap();
2488 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2489 panic!("expected parent rule");
2490 };
2491 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2492 else {
2493 panic!("expected child rule");
2494 };
2495
2496 assert_eq!(child.len(), 2);
2497 let resolved_a = parent[0].nest(&child[0]);
2498 let resolved_b = parent[0].nest(&child[1]);
2499 assert_eq!(resolved_a.to_string(), ".parent.a");
2500 assert_eq!(resolved_b.to_string(), ".parent.b");
2501 }
2502
2503 #[test]
2504 fn nesting_with_descendant_then_ampersand() {
2505 let stylesheet = Parser::new(".outer { .parent { &.highlight { color: red; } } }")
2506 .parse()
2507 .unwrap();
2508 let CssNodeType::Rule { selectors: outer } = stylesheet.children()[0].node() else {
2509 panic!("expected outer rule");
2510 };
2511 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].children()[0].node()
2512 else {
2513 panic!("expected parent rule");
2514 };
2515 let CssNodeType::Rule { selectors: child } =
2516 stylesheet.children()[0].children()[0].children()[0].node()
2517 else {
2518 panic!("expected child rule");
2519 };
2520
2521 let resolved_parent = outer[0].nest(&parent[0]);
2523 assert_eq!(resolved_parent.to_string(), ".outer .parent");
2524
2525 let resolved_child = resolved_parent.nest(&child[0]);
2527 assert_eq!(resolved_child.to_string(), ".outer .parent.highlight");
2528 }
2529
2530 #[test]
2531 fn nesting_with_multilevel_ampersand_and_combinator() {
2532 let stylesheet = Parser::new("#id .parent { .a & .b > span { color: red; } }")
2533 .parse()
2534 .unwrap();
2535 let CssNodeType::Rule { selectors: parent } = stylesheet.children()[0].node() else {
2536 panic!("expected parent rule");
2537 };
2538 let CssNodeType::Rule { selectors: child } = stylesheet.children()[0].children()[0].node()
2539 else {
2540 panic!("expected child rule");
2541 };
2542
2543 let resolved = parent[0].nest(&child[0]);
2544 assert_eq!(resolved.to_string(), ".a #id .parent .b > span");
2545 }
2546
2547 #[test]
2548 fn nested_declaration_parsing() {
2549 let stylesheet = Parser::new(".parent { color: red; & { font-size: 14px; } }")
2550 .parse()
2551 .unwrap();
2552 let CssNodeType::Rule { selectors } = stylesheet.children()[0].node() else {
2553 panic!("expected parent rule");
2554 };
2555 assert_eq!(selectors[0].to_string(), ".parent");
2556
2557 let children = stylesheet.children()[0].children();
2558 assert_eq!(children.len(), 2);
2559
2560 let CssNodeType::Declaration { name, .. } = children[0].node() else {
2562 panic!("expected declaration");
2563 };
2564 assert_eq!(name, "color");
2565
2566 let CssNodeType::Rule { selectors: nested } = children[1].node() else {
2568 panic!("expected nested rule");
2569 };
2570 assert!(nested[0].parts[0].selector.is_nesting);
2571 }
2572}