1use alloc::{rc::Rc, string::String, vec::Vec};
4use core::{hash::Hasher, num::NonZeroUsize};
5
6use crate::length_num::LengthNum;
7use crate::property::{
8 NodeProperties, NodePropertiesOrder, Property, PropertyMeta, PropertyValueWithGlobal,
9};
10use crate::sheet::Rule;
11use crate::sheet::{RuleWeight, Theme};
12use crate::typing::{Length, LengthType};
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct MediaQueryStatus<L: LengthNum> {
17 pub is_screen: bool,
19 pub width: L,
21 pub height: L,
23 pub pixel_ratio: f32,
25 pub base_font_size: L,
27 pub theme: Theme,
29 pub env: EnvValues<L>,
31}
32
33#[derive(Debug, Clone, PartialEq)]
35#[allow(missing_docs)]
36pub struct EnvValues<L: LengthNum> {
37 pub safe_area_inset_left: L,
38 pub safe_area_inset_top: L,
39 pub safe_area_inset_right: L,
40 pub safe_area_inset_bottom: L,
41}
42
43impl<L: LengthNum> Default for EnvValues<L> {
44 fn default() -> Self {
45 Self {
46 safe_area_inset_left: L::zero(),
47 safe_area_inset_top: L::zero(),
48 safe_area_inset_right: L::zero(),
49 safe_area_inset_bottom: L::zero(),
50 }
51 }
52}
53
54impl<L: LengthNum> Default for MediaQueryStatus<L> {
55 fn default() -> Self {
56 Self::default_screen()
57 }
58}
59
60impl<L: LengthNum> MediaQueryStatus<L> {
61 pub fn default_screen() -> Self {
63 Self {
64 is_screen: true,
65 width: L::from_i32(800),
66 height: L::from_i32(600),
67 pixel_ratio: 1.,
68 base_font_size: L::from_i32(16),
69 theme: Theme::Light,
70 env: Default::default(),
71 }
72 }
73
74 pub fn default_screen_with_size(width: L, height: L) -> Self {
76 Self {
77 is_screen: true,
78 width,
79 height,
80 pixel_ratio: 1.,
81 base_font_size: L::from_i32(16),
82 theme: Theme::Light,
83 env: Default::default(),
84 }
85 }
86}
87
88pub trait StyleNodeClass {
90 fn name(&self) -> &str;
92
93 fn scope(&self) -> Option<NonZeroUsize>;
95}
96
97impl StyleNodeClass for (String, Option<NonZeroUsize>) {
98 fn name(&self) -> &str {
99 &self.0
100 }
101
102 fn scope(&self) -> Option<NonZeroUsize> {
103 self.1
104 }
105}
106
107pub enum StyleNodeAttributeCaseSensitivity {
109 CaseSensitive,
111
112 CaseInsensitive,
114}
115
116impl StyleNodeAttributeCaseSensitivity {
117 pub fn eq(&self, a: &str, b: &str) -> bool {
119 match self {
120 Self::CaseSensitive => a == b,
121 Self::CaseInsensitive => a.eq_ignore_ascii_case(b),
122 }
123 }
124
125 pub fn starts_with(&self, a: &str, b: &str) -> bool {
127 match self {
129 Self::CaseSensitive => a.starts_with(b),
130 Self::CaseInsensitive => a.to_ascii_lowercase().starts_with(&b.to_ascii_lowercase()),
131 }
132 }
133
134 pub fn ends_with(&self, a: &str, b: &str) -> bool {
136 match self {
138 Self::CaseSensitive => a.ends_with(b),
139 Self::CaseInsensitive => a.to_ascii_lowercase().ends_with(&b.to_ascii_lowercase()),
140 }
141 }
142
143 pub fn contains(&self, a: &str, b: &str) -> bool {
145 match self {
147 Self::CaseSensitive => a.contains(b),
148 Self::CaseInsensitive => a.to_ascii_lowercase().contains(&b.to_ascii_lowercase()),
149 }
150 }
151}
152
153pub trait StyleNode {
155 type Class: StyleNodeClass;
157
158 type ClassIter<'a>: Iterator<Item = &'a Self::Class>
160 where
161 Self: 'a;
162
163 fn style_scope(&self) -> Option<NonZeroUsize>;
165
166 fn extra_style_scope(&self) -> Option<NonZeroUsize>;
168
169 fn host_style_scope(&self) -> Option<NonZeroUsize>;
171
172 fn tag_name(&self) -> &str;
174
175 fn id(&self) -> Option<&str>;
177
178 fn classes(&self) -> Self::ClassIter<'_>;
180
181 fn attribute(&self, name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)>;
183
184 fn contain_scope(&self, scope: Option<NonZeroUsize>) -> bool {
186 scope.is_none()
187 || self.style_scope() == scope
188 || self.extra_style_scope() == scope
189 || self.host_style_scope() == scope
190 }
191}
192
193#[derive(Debug)]
195pub struct StyleQuery<'a> {
196 pub(super) style_scope: Option<NonZeroUsize>,
197 pub(super) extra_style_scope: Option<NonZeroUsize>,
198 pub(super) host_style_scope: Option<NonZeroUsize>,
199 pub(super) tag_name: &'a str,
200 pub(super) id: &'a str,
201 pub(super) classes: &'a [(String, Option<NonZeroUsize>)],
202}
203
204impl Clone for StyleQuery<'_> {
205 fn clone(&self) -> Self {
206 Self {
207 style_scope: self.style_scope,
208 extra_style_scope: self.extra_style_scope,
209 host_style_scope: self.host_style_scope,
210 tag_name: self.tag_name,
211 id: self.id,
212 classes: self.classes,
213 }
214 }
215}
216
217impl<'a> StyleQuery<'a> {
218 pub fn single(
220 style_scope: Option<NonZeroUsize>,
221 extra_style_scope: Option<NonZeroUsize>,
222 host_style_scope: Option<NonZeroUsize>,
223 tag_name: &'a str,
224 id: &'a str,
225 classes: &'a [(String, Option<NonZeroUsize>)],
226 ) -> Self {
227 Self {
228 style_scope,
229 extra_style_scope,
230 host_style_scope,
231 tag_name,
232 id,
233 classes,
234 }
235 }
236}
237
238impl<'a> StyleNode for StyleQuery<'a> {
239 type Class = (String, Option<NonZeroUsize>);
240 type ClassIter<'c>
241 = core::slice::Iter<'c, Self::Class>
242 where
243 'a: 'c;
244
245 fn style_scope(&self) -> Option<NonZeroUsize> {
246 self.style_scope
247 }
248
249 fn extra_style_scope(&self) -> Option<NonZeroUsize> {
250 self.extra_style_scope
251 }
252
253 fn host_style_scope(&self) -> Option<NonZeroUsize> {
254 self.host_style_scope
255 }
256
257 fn tag_name(&self) -> &str {
258 self.tag_name
259 }
260
261 fn id(&self) -> Option<&str> {
262 Some(self.id)
263 }
264
265 fn classes(&self) -> Self::ClassIter<'_> {
266 self.classes.iter()
267 }
268
269 fn attribute(&self, _name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
270 None
271 }
272}
273
274impl<'b, 'a: 'b> StyleNode for &'b StyleQuery<'a> {
275 type Class = (String, Option<NonZeroUsize>);
276 type ClassIter<'c>
277 = core::slice::Iter<'c, Self::Class>
278 where
279 'b: 'c;
280
281 fn style_scope(&self) -> Option<NonZeroUsize> {
282 self.style_scope
283 }
284
285 fn extra_style_scope(&self) -> Option<NonZeroUsize> {
286 self.extra_style_scope
287 }
288
289 fn host_style_scope(&self) -> Option<NonZeroUsize> {
290 self.host_style_scope
291 }
292
293 fn tag_name(&self) -> &str {
294 self.tag_name
295 }
296
297 fn id(&self) -> Option<&str> {
298 Some(self.id)
299 }
300
301 fn classes(&self) -> Self::ClassIter<'_> {
302 self.classes.iter()
303 }
304
305 fn attribute(&self, _name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
306 None
307 }
308}
309
310#[derive(Debug, Clone)]
312pub struct MatchedRuleRef<'a> {
313 pub rule: &'a Rc<Rule>,
315 pub weight: RuleWeight,
317}
318
319#[derive(Debug, Clone)]
321pub struct MatchedRule {
322 pub rule: Rc<Rule>,
324 pub weight: RuleWeight,
326 pub style_scope: Option<NonZeroUsize>,
328}
329
330impl PartialEq for MatchedRule {
331 fn eq(&self, other: &Self) -> bool {
332 self.weight.normal() == other.weight.normal()
333 }
334}
335
336impl PartialOrd for MatchedRule {
337 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
338 Some(self.weight.normal().cmp(&other.weight.normal()))
339 }
340}
341
342impl Ord for MatchedRule {
343 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
344 self.weight.normal().cmp(&other.weight.normal())
345 }
346}
347
348impl Eq for MatchedRule {}
349
350#[derive(Debug, Clone)]
352pub struct MatchedRuleList {
353 pub rules: Vec<MatchedRule>,
355}
356
357impl MatchedRuleList {
358 pub fn new_empty() -> Self {
360 Self {
361 rules: Vec::with_capacity(0),
362 }
363 }
364
365 pub fn get_current_font_size<L: LengthNum>(
369 &self,
370 parent_font_size: f32,
371 parent_node_properties: Option<&NodeProperties>,
372 extra_styles: &[PropertyMeta],
373 media_query_status: &MediaQueryStatus<L>,
374 ) -> f32 {
375 let mut font_size_p = None;
377 let mut font_size_w: u64 = 0;
378 fn handle_property_meta<'a>(
379 font_size_p: &mut Option<&'a LengthType>,
380 font_size_w: &mut u64,
381 pm: &'a PropertyMeta,
382 rw: RuleWeight,
383 ) {
384 match pm {
385 PropertyMeta::Normal { property: p } => {
386 if let Property::FontSize(x) = p {
387 let w = rw.normal();
388 if w >= *font_size_w {
389 *font_size_w = w;
390 *font_size_p = Some(x);
391 }
392 }
393 }
394 PropertyMeta::Important { property: p } => {
395 if let Property::FontSize(x) = p {
396 let w = rw.important();
397 if w >= *font_size_w {
398 *font_size_w = w;
399 *font_size_p = Some(x);
400 }
401 }
402 }
403 PropertyMeta::DebugGroup {
404 properties,
405 important,
406 disabled,
407 ..
408 } => {
409 if !disabled {
410 let w = if *important {
411 rw.important()
412 } else {
413 rw.normal()
414 };
415 if w >= *font_size_w {
416 for p in &**properties {
417 if let Property::FontSize(x) = p {
418 *font_size_w = w;
419 *font_size_p = Some(x);
420 }
421 }
422 }
423 }
424 }
425 }
426 }
427 for pm in extra_styles.iter() {
428 handle_property_meta(&mut font_size_p, &mut font_size_w, pm, RuleWeight::inline());
429 }
430 for matched_rule in self.rules.iter() {
431 let rw = matched_rule.weight;
432 if !matched_rule.rule.has_font_size {
433 continue;
434 };
435 for pm in matched_rule.rule.properties.iter() {
436 handle_property_meta(&mut font_size_p, &mut font_size_w, pm, rw);
437 }
438 }
439
440 let default_font_size = media_query_status.base_font_size.to_f32();
442 let parent_font_size_p = parent_node_properties.map(|x| x.font_size_ref());
443 let parent_font_size = parent_font_size.to_f32();
444 let current_font_size = if let Some(p) = font_size_p {
445 p.to_inner(parent_font_size_p, Length::Px(default_font_size), true)
446 .and_then(|x| x.resolve_to_f32(media_query_status, parent_font_size, true))
447 .unwrap_or(parent_font_size)
448 } else {
449 parent_font_size
450 };
451
452 current_font_size
453 }
454
455 pub fn merge_node_properties(
457 &self,
458 node_properties: &mut NodeProperties,
459 parent_node_properties: Option<&NodeProperties>,
460 current_font_size: f32,
461 extra_styles: &[PropertyMeta],
462 ) {
463 let mut order = NodePropertiesOrder::new();
464 let mut merge_property_meta = |pm: &PropertyMeta, rw: RuleWeight| match pm {
465 PropertyMeta::Normal { property: p } => {
466 if order.compare_property(p, rw.normal()) {
467 node_properties.merge_property(p, parent_node_properties, current_font_size)
468 }
469 }
470 PropertyMeta::Important { property: p } => {
471 if order.compare_property(p, rw.important()) {
472 node_properties.merge_property(p, parent_node_properties, current_font_size)
473 }
474 }
475 PropertyMeta::DebugGroup {
476 properties,
477 important,
478 disabled,
479 ..
480 } => {
481 if !disabled {
482 let w = if *important {
483 rw.important()
484 } else {
485 rw.normal()
486 };
487 for p in &**properties {
488 if order.compare_property(p, w) {
489 node_properties.merge_property(
490 p,
491 parent_node_properties,
492 current_font_size,
493 )
494 }
495 }
496 }
497 }
498 };
499 for pm in extra_styles.iter() {
500 merge_property_meta(pm, RuleWeight::inline());
501 }
502 for matched_rule in self.rules.iter() {
503 for pm in matched_rule.rule.properties.iter() {
504 merge_property_meta(pm, matched_rule.weight);
505 }
506 }
507 }
508
509 pub fn animation_name_style_scope(&self) -> Option<NonZeroUsize> {
514 let mut w = u64::MIN;
515 let mut ret = None;
516 let mut check_property_meta = |pm: &PropertyMeta, rw: RuleWeight, scope| {
517 for p in pm.iter() {
518 if let Property::AnimationName(..) = p {
519 let self_w = if pm.is_important() {
520 rw.important()
521 } else {
522 rw.normal()
523 };
524 if self_w >= w {
525 w = self_w;
526 ret = scope;
527 }
528 }
529 }
530 };
531 for matched_rule in self.rules.iter() {
532 for pm in matched_rule.rule.properties.iter() {
533 check_property_meta(pm, matched_rule.weight, matched_rule.style_scope);
534 }
535 }
536 ret
537 }
538
539 pub fn fast_hash_value(&self) -> u64 {
543 let mut hasher = ahash::AHasher::default();
544 for matched_rule in self.rules.iter() {
545 let rule: &Rule = &matched_rule.rule;
546 hasher.write_usize(rule as *const Rule as usize);
547 hasher.write_u64(matched_rule.weight.normal());
548 }
549 hasher.finish()
550 }
551}