orinium_browser/engine/layouter/
css_resolver.rs1use crate::engine::css::parser::{ComplexSelector, CssNode, CssNodeType};
4use crate::engine::css::values::CssValue;
5
6use std::collections::HashMap;
7
8type CustomProperties = HashMap<String, CssValue>;
9
10#[derive(Debug, Clone)]
27pub struct ResolvedDeclaration {
28 pub selector: ComplexSelector,
30
31 pub name: String,
33
34 pub value: CssValue,
37
38 pub specificity: (u32, u32, u32),
43
44 pub order: usize,
47
48 pub important: bool,
50}
51
52pub type ResolvedStyles = Vec<ResolvedDeclaration>;
53
54pub struct CssResolver;
55
56impl CssResolver {
57 pub fn resolve(stylesheet: &CssNode) -> ResolvedStyles {
58 let mut styles = Vec::new();
59 let mut order = 0;
60 Self::walk(stylesheet, &mut styles, &mut order);
61 styles
62 }
63
64 fn walk(node: &CssNode, styles: &mut ResolvedStyles, order: &mut usize) {
65 if let CssNodeType::Rule { selectors } = &node.node() {
66 let declarations = Self::collect_declarations(node);
67
68 for selector in selectors {
69 let specificity = selector.specificity();
70
71 for (name, value, important) in &declarations {
72 styles.push(ResolvedDeclaration {
73 selector: selector.clone(),
74 name: name.clone(),
75 value: value.clone(),
76 specificity,
77 order: *order,
78 important: *important,
79 });
80 *order += 1;
81 }
82 }
83 }
84
85 for child in node.children() {
86 Self::walk(child, styles, order);
87 }
88 }
89
90 fn collect_declarations(rule_node: &CssNode) -> Vec<(String, CssValue, bool)> {
91 let mut result = Vec::new();
92 let mut custom_props: CustomProperties = HashMap::new();
93
94 for child in rule_node.children() {
96 if let CssNodeType::Declaration { name, value } = &child.node()
97 && name.starts_with("--")
98 {
99 custom_props.insert(name.clone(), value.clone());
100 }
101 }
102
103 for child in rule_node.children() {
105 if let CssNodeType::Declaration { name, value } = &child.node() {
106 let (raw_value, important) = Self::extract_important(value);
107
108 if name.starts_with("--") {
109 result.push((name.clone(), raw_value, important));
110 } else if let Some(resolved) = Self::resolve_var(&raw_value, &custom_props) {
111 result.push((name.clone(), resolved, important));
112 }
113 }
114 }
115
116 result
117 }
118
119 fn extract_important(value: &CssValue) -> (CssValue, bool) {
120 match value {
121 CssValue::List(list) if list.len() >= 2 => {
122 let len = list.len();
123 let is_important = matches!(
124 (&list[len - 2], &list[len - 1]),
125 (
126 CssValue::Keyword(bang),
127 CssValue::Keyword(ident)
128 )
129 if bang == "!" && ident.eq_ignore_ascii_case("important")
130 );
131
132 if is_important {
133 let value = if len - 2 == 1 {
134 list.iter().next().unwrap().clone()
135 } else {
136 CssValue::List(list[..len - 2].to_vec())
137 };
138 return (value, true);
139 }
140
141 (value.clone(), false)
142 }
143 _ => (value.clone(), false),
144 }
145 }
146
147 fn resolve_var(value: &CssValue, custom_props: &CustomProperties) -> Option<CssValue> {
148 match value {
149 CssValue::Function(name, args) if name == "var" => {
150 let var_name = match args.first() {
152 Some(CssValue::Keyword(name)) => name,
153 _ => return None,
154 };
155
156 if let Some(v) = custom_props.get(var_name) {
157 Self::resolve_var(v, custom_props)
158 } else if let Some(fallback) = args.get(1) {
159 Self::resolve_var(fallback, custom_props)
160 } else {
161 None
162 }
163 }
164
165 CssValue::Function(name, args) => {
166 let resolved_args = args
167 .iter()
168 .map(|v| Self::resolve_var(v, custom_props))
169 .collect::<Option<Vec<_>>>()?;
170 Some(CssValue::Function(name.clone(), resolved_args))
171 }
172
173 CssValue::List(list) => {
174 let resolved = list
175 .iter()
176 .map(|v| Self::resolve_var(v, custom_props))
177 .collect::<Option<Vec<_>>>()?;
178 Some(CssValue::List(resolved))
179 }
180
181 _ => Some(value.clone()),
182 }
183 }
184}