orinium_browser/engine/css/
matcher.rs1use super::parser::{Combinator, ComplexSelector, Selector};
4
5#[derive(Debug, Clone)]
6pub struct ElementInfo {
7 pub tag_name: String,
8 pub id: Option<String>,
9 pub classes: Vec<String>,
10}
11
12pub type ElementChain = Vec<ElementInfo>;
14
15impl Selector {
16 pub fn matches(&self, tag_name: &str, id: Option<&str>, class_list: &[String]) -> bool {
18 if let Some(tag) = &self.tag
20 && tag != tag_name
21 {
22 return false;
23 }
24
25 if let Some(expected_id) = &self.id {
27 match id {
28 Some(actual_id) if actual_id == expected_id => {}
29 _ => return false,
30 }
31 }
32
33 for class in &self.classes {
35 if !class_list.iter().any(|c| c == class) {
36 return false;
37 }
38 }
39
40 if let Some(_pseudo) = &self.pseudo_class {
41 return false;
43 }
44
45 if let Some(_pseudo) = &self.pseudo_element {
46 return false;
48 }
49
50 true
51 }
52}
53
54impl ComplexSelector {
55 pub fn matches(&self, chain: &[ElementInfo]) -> bool {
56 if chain.is_empty() || self.parts.is_empty() {
57 return false;
58 }
59 self.match_from(chain, 0, 0)
60 }
61
62 fn match_from(&self, chain: &[ElementInfo], chain_index: usize, selector_index: usize) -> bool {
63 let element = &chain[chain_index];
64 let part = &self.parts[selector_index];
65
66 if !part
67 .selector
68 .matches(&element.tag_name, element.id.as_deref(), &element.classes)
69 {
70 return false;
71 }
72
73 if selector_index + 1 == self.parts.len() {
75 return true;
76 }
77
78 match part.combinator {
79 Some(Combinator::Descendant) => {
80 for next in (chain_index + 1)..chain.len() {
81 if self.match_from(chain, next, selector_index + 1) {
82 return true;
83 }
84 }
85 false
86 }
87 None => false,
88 }
89 }
90
91 pub fn specificity(&self) -> (u32, u32, u32) {
92 let mut a = 0; let mut b = 0; let mut c = 0; for part in &self.parts {
97 let sel = &part.selector;
98
99 if sel.id.is_some() {
100 a += 1;
101 }
102 b += sel.classes.len() as u32;
103 if sel.tag.is_some() {
104 c += 1;
105 }
106 }
107
108 (a, b, c)
109 }
110}