vizia_core/systems/
style.rs

1use crate::{cache::CachedData, prelude::*};
2#[cfg(feature = "rayon")]
3use dashmap::{DashMap, ReadOnlyView};
4use hashbrown::HashMap;
5#[cfg(feature = "rayon")]
6use rayon::prelude::*;
7use vizia_storage::{LayoutParentIterator, TreeBreadthIterator};
8use vizia_style::{
9    matches_selector,
10    precomputed_hash::PrecomputedHash,
11    selectors::{
12        attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint},
13        bloom::BloomFilter,
14        context::{MatchingForInvalidation, NeedsSelectorFlags, SelectorCaches},
15        matching::ElementSelectorFlags,
16        parser::{Component, NthType},
17        OpaqueElement, SelectorImpl,
18    },
19    Element, MatchingContext, MatchingMode, PseudoClass, QuirksMode, SelectorIdent, Selectors,
20};
21
22/// A node used for style matching.
23#[derive(Clone)]
24pub(crate) struct Node<'s, 't> {
25    entity: Entity,
26    store: &'s Style,
27    tree: &'t Tree<Entity>,
28}
29
30impl std::fmt::Debug for Node<'_, '_> {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(f, "{}", self.entity)
33    }
34}
35
36/// Used for selector matching.
37impl Element for Node<'_, '_> {
38    type Impl = Selectors;
39
40    fn opaque(&self) -> OpaqueElement {
41        OpaqueElement::new(self)
42    }
43
44    fn is_html_slot_element(&self) -> bool {
45        false
46    }
47
48    fn parent_node_is_shadow_root(&self) -> bool {
49        false
50    }
51
52    fn containing_shadow_host(&self) -> Option<Self> {
53        None
54    }
55
56    fn parent_element(&self) -> Option<Self> {
57        self.tree.get_layout_parent(self.entity).map(|parent| Node {
58            entity: parent,
59            store: self.store,
60            tree: self.tree,
61        })
62    }
63
64    fn prev_sibling_element(&self) -> Option<Self> {
65        self.tree.get_prev_layout_sibling(self.entity).map(|parent| Node {
66            entity: parent,
67            store: self.store,
68            tree: self.tree,
69        })
70    }
71
72    fn next_sibling_element(&self) -> Option<Self> {
73        self.tree.get_next_layout_sibling(self.entity).map(|parent| Node {
74            entity: parent,
75            store: self.store,
76            tree: self.tree,
77        })
78    }
79
80    fn is_empty(&self) -> bool {
81        !self.tree.has_children(self.entity)
82    }
83
84    fn is_root(&self) -> bool {
85        self.entity == Entity::root()
86    }
87
88    fn is_html_element_in_html_document(&self) -> bool {
89        false
90    }
91
92    fn has_local_name(&self, local_name: &SelectorIdent) -> bool {
93        if let Some(element) = self.store.element.get(self.entity) {
94            return element == &local_name.precomputed_hash();
95        }
96
97        false
98    }
99
100    fn has_namespace(&self, _ns: &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl) -> bool {
101        false
102    }
103
104    fn is_part(&self, _name: &<Self::Impl as SelectorImpl>::Identifier) -> bool {
105        false
106    }
107
108    fn imported_part(
109        &self,
110        _name: &<Self::Impl as SelectorImpl>::Identifier,
111    ) -> Option<<Self::Impl as SelectorImpl>::Identifier> {
112        None
113    }
114
115    fn is_pseudo_element(&self) -> bool {
116        false
117    }
118
119    fn is_same_type(&self, other: &Self) -> bool {
120        if let Some(element) = self.store.element.get(self.entity) {
121            if let Some(other_element) = self.store.element.get(other.entity) {
122                return element == other_element;
123            }
124        }
125
126        false
127    }
128
129    fn is_link(&self) -> bool {
130        false
131    }
132
133    fn has_id(
134        &self,
135        name: &<Self::Impl as SelectorImpl>::Identifier,
136        _case_sensitivity: CaseSensitivity,
137    ) -> bool {
138        if let Some(id) = self.store.ids.get(self.entity) {
139            *id == name.0
140        } else {
141            false
142        }
143    }
144
145    fn has_class(
146        &self,
147        name: &<Self::Impl as SelectorImpl>::Identifier,
148        _case_sensitivity: CaseSensitivity,
149    ) -> bool {
150        if let Some(classes) = self.store.classes.get(self.entity) {
151            return classes.contains(&name.0);
152        }
153
154        false
155    }
156
157    fn attr_matches(
158        &self,
159        _ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
160        _local_name: &<Self::Impl as SelectorImpl>::LocalName,
161        _operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>,
162    ) -> bool {
163        false
164    }
165
166    fn match_pseudo_element(
167        &self,
168        _pe: &<Self::Impl as SelectorImpl>::PseudoElement,
169        _context: &mut MatchingContext<'_, Self::Impl>,
170    ) -> bool {
171        false
172    }
173
174    fn match_non_ts_pseudo_class(
175        &self,
176        pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
177        _context: &mut MatchingContext<'_, Self::Impl>,
178    ) -> bool {
179        if let Some(psudeo_class_flag) = self.store.pseudo_classes.get(self.entity) {
180            match pc {
181                PseudoClass::Hover => psudeo_class_flag.contains(PseudoClassFlags::HOVER),
182                PseudoClass::Active => psudeo_class_flag.contains(PseudoClassFlags::ACTIVE),
183                PseudoClass::Over => psudeo_class_flag.contains(PseudoClassFlags::OVER),
184                PseudoClass::Focus => psudeo_class_flag.contains(PseudoClassFlags::FOCUS),
185                PseudoClass::FocusVisible => {
186                    psudeo_class_flag.contains(PseudoClassFlags::FOCUS_VISIBLE)
187                }
188                PseudoClass::FocusWithin => {
189                    psudeo_class_flag.contains(PseudoClassFlags::FOCUS_WITHIN)
190                }
191                PseudoClass::Enabled => {
192                    self.store.disabled.get(self.entity).map(|disabled| !*disabled).unwrap_or(true)
193                }
194                PseudoClass::Disabled => {
195                    self.store.disabled.get(self.entity).copied().unwrap_or_default()
196                }
197                PseudoClass::ReadOnly => psudeo_class_flag.contains(PseudoClassFlags::READ_ONLY),
198                PseudoClass::ReadWrite => psudeo_class_flag.contains(PseudoClassFlags::READ_WRITE),
199                PseudoClass::PlaceholderShown => {
200                    psudeo_class_flag.contains(PseudoClassFlags::PLACEHOLDER_SHOWN)
201                }
202                PseudoClass::Default => psudeo_class_flag.contains(PseudoClassFlags::DEFAULT),
203                PseudoClass::Checked => psudeo_class_flag.contains(PseudoClassFlags::CHECKED),
204                PseudoClass::Indeterminate => {
205                    psudeo_class_flag.contains(PseudoClassFlags::INDETERMINATE)
206                }
207                PseudoClass::Blank => psudeo_class_flag.contains(PseudoClassFlags::BLANK),
208                PseudoClass::Valid => psudeo_class_flag.contains(PseudoClassFlags::VALID),
209                PseudoClass::Invalid => psudeo_class_flag.contains(PseudoClassFlags::INVALID),
210                PseudoClass::InRange => psudeo_class_flag.contains(PseudoClassFlags::IN_RANGE),
211                PseudoClass::OutOfRange => {
212                    psudeo_class_flag.contains(PseudoClassFlags::OUT_OF_RANGE)
213                }
214                PseudoClass::Required => psudeo_class_flag.contains(PseudoClassFlags::REQUIRED),
215                PseudoClass::Optional => psudeo_class_flag.contains(PseudoClassFlags::OPTIONAL),
216                PseudoClass::UserValid => psudeo_class_flag.contains(PseudoClassFlags::USER_VALID),
217                PseudoClass::UserInvalid => {
218                    psudeo_class_flag.contains(PseudoClassFlags::USER_INVALID)
219                }
220                PseudoClass::Lang(_) => todo!(),
221                PseudoClass::Dir(_) => todo!(),
222                PseudoClass::Custom(name) => {
223                    println!("custom: {}", name);
224                    todo!()
225                }
226            }
227        } else {
228            false
229        }
230    }
231
232    fn first_element_child(&self) -> Option<Self> {
233        None
234    }
235
236    fn apply_selector_flags(&self, _flags: ElementSelectorFlags) {}
237
238    fn has_custom_state(&self, _name: &<Self::Impl as SelectorImpl>::Identifier) -> bool {
239        false
240    }
241
242    fn add_element_unique_hashes(
243        &self,
244        _filter: &mut vizia_style::selectors::bloom::BloomFilter,
245    ) -> bool {
246        false
247    }
248}
249
250/// Link inheritable inline properties to their parent.
251pub(crate) fn inline_inheritance_system(cx: &mut Context, redraw_entities: &mut Vec<Entity>) {
252    for entity in cx.tree.into_iter() {
253        if let Some(parent) = cx.tree.get_layout_parent(entity) {
254            if cx.style.disabled.inherit_inline(entity, parent)
255                | cx.style.caret_color.inherit_inline(entity, parent)
256                | cx.style.selection_color.inherit_inline(entity, parent)
257            {
258                redraw_entities.push(entity);
259            }
260
261            if cx.style.font_color.inherit_inline(entity, parent)
262                | cx.style.font_size.inherit_inline(entity, parent)
263                | cx.style.font_family.inherit_inline(entity, parent)
264                | cx.style.font_weight.inherit_inline(entity, parent)
265                | cx.style.font_slant.inherit_inline(entity, parent)
266                | cx.style.font_width.inherit_inline(entity, parent)
267                | cx.style.text_decoration_line.inherit_inline(entity, parent)
268                | cx.style.text_stroke_width.inherit_inline(entity, parent)
269                | cx.style.text_stroke_style.inherit_inline(entity, parent)
270                | cx.style.font_variation_settings.inherit_inline(entity, parent)
271            {
272                cx.style.needs_text_update(entity);
273            }
274        }
275    }
276}
277
278/// Link inheritable shared properties to their parent.
279pub(crate) fn shared_inheritance_system(cx: &mut Context, redraw_entities: &mut Vec<Entity>) {
280    for entity in cx.tree.into_iter() {
281        if let Some(parent) = cx.tree.get_layout_parent(entity) {
282            if cx.style.font_color.inherit_shared(entity, parent)
283                | cx.style.font_size.inherit_shared(entity, parent)
284                | cx.style.font_family.inherit_shared(entity, parent)
285                | cx.style.font_weight.inherit_shared(entity, parent)
286                | cx.style.font_slant.inherit_shared(entity, parent)
287                | cx.style.font_width.inherit_shared(entity, parent)
288                | cx.style.text_decoration_line.inherit_shared(entity, parent)
289                | cx.style.text_stroke_width.inherit_shared(entity, parent)
290                | cx.style.text_stroke_style.inherit_shared(entity, parent)
291                | cx.style.font_variation_settings.inherit_shared(entity, parent)
292            {
293                cx.style.needs_text_update(entity);
294            }
295
296            if cx.style.caret_color.inherit_shared(entity, parent)
297                | cx.style.selection_color.inherit_shared(entity, parent)
298            {
299                redraw_entities.push(entity);
300            }
301        }
302    }
303}
304
305fn link_style_data(
306    style: &mut Style,
307    cache: &mut CachedData,
308    tree: &Tree<Entity>,
309    entity: Entity,
310    redraw_entities: &mut Vec<Entity>,
311    matched_rules: &[(Rule, u32)],
312) {
313    let mut should_relayout = false;
314    let mut should_redraw = false;
315    let mut should_reflow = false;
316
317    // Display
318    if style.display.link(entity, matched_rules) {
319        should_relayout = true;
320        should_redraw = true;
321    }
322
323    if style.visibility.link(entity, matched_rules) {
324        should_relayout = true;
325        should_redraw = true;
326    }
327
328    if style.z_index.link(entity, matched_rules) {
329        should_redraw = true;
330    }
331
332    if style.overflowx.link(entity, matched_rules) {
333        should_redraw = true;
334    }
335
336    if style.overflowy.link(entity, matched_rules) {
337        should_redraw = true;
338    }
339
340    if style.clip_path.link(entity, matched_rules) {
341        should_redraw = true;
342    }
343
344    if style.backdrop_filter.link(entity, matched_rules) {
345        should_redraw = true;
346    }
347
348    if style.blend_mode.link(entity, matched_rules) {
349        should_redraw = true;
350    }
351
352    // Opacity
353    if style.opacity.link(entity, matched_rules) {
354        should_redraw = true;
355    }
356
357    // Grid
358    if style.grid_columns.link(entity, matched_rules) {
359        should_relayout = true;
360    }
361
362    if style.grid_rows.link(entity, matched_rules) {
363        should_relayout = true;
364    }
365
366    if style.column_start.link(entity, matched_rules) {
367        should_relayout = true;
368    }
369
370    if style.column_span.link(entity, matched_rules) {
371        should_relayout = true;
372    }
373
374    if style.row_start.link(entity, matched_rules) {
375        should_relayout = true;
376    }
377
378    if style.row_span.link(entity, matched_rules) {
379        should_relayout = true;
380    }
381
382    // Position
383
384    if style.left.link(entity, matched_rules) {
385        should_relayout = true;
386        should_redraw = true;
387    }
388
389    if style.right.link(entity, matched_rules) {
390        should_relayout = true;
391        should_redraw = true;
392    }
393
394    if style.top.link(entity, matched_rules) {
395        should_relayout = true;
396        should_redraw = true;
397    }
398
399    if style.bottom.link(entity, matched_rules) {
400        should_relayout = true;
401        should_redraw = true;
402    }
403
404    // Size
405    if style.width.link(entity, matched_rules) {
406        should_relayout = true;
407        should_redraw = true;
408    }
409
410    if style.height.link(entity, matched_rules) {
411        should_relayout = true;
412        should_redraw = true;
413    }
414
415    // Size Constraints
416    if style.max_width.link(entity, matched_rules) {
417        should_relayout = true;
418        should_redraw = true;
419    }
420
421    if style.min_width.link(entity, matched_rules) {
422        should_relayout = true;
423        should_redraw = true;
424    }
425
426    if style.max_height.link(entity, matched_rules) {
427        should_relayout = true;
428        should_redraw = true;
429    }
430
431    if style.min_height.link(entity, matched_rules) {
432        should_relayout = true;
433        should_redraw = true;
434    }
435
436    // Gap Constraints
437    if style.max_horizontal_gap.link(entity, matched_rules) {
438        should_relayout = true;
439        should_redraw = true;
440    }
441
442    if style.min_horizontal_gap.link(entity, matched_rules) {
443        should_relayout = true;
444        should_redraw = true;
445    }
446
447    if style.max_vertical_gap.link(entity, matched_rules) {
448        should_relayout = true;
449        should_redraw = true;
450    }
451
452    if style.min_vertical_gap.link(entity, matched_rules) {
453        should_relayout = true;
454        should_redraw = true;
455    }
456
457    // Border
458    if style.border_width.link(entity, matched_rules) {
459        should_relayout = true;
460        should_redraw = true;
461        cache.path.remove(entity);
462    }
463
464    if style.border_color.link(entity, matched_rules) {
465        should_redraw = true;
466    }
467
468    if style.border_style.link(entity, matched_rules) {
469        should_redraw = true;
470    }
471
472    // Corner
473
474    if style.corner_top_left_shape.link(entity, matched_rules) {
475        should_redraw = true;
476    }
477
478    if style.corner_top_right_shape.link(entity, matched_rules) {
479        should_redraw = true;
480    }
481
482    if style.corner_bottom_left_shape.link(entity, matched_rules) {
483        should_redraw = true;
484    }
485
486    if style.corner_bottom_right_shape.link(entity, matched_rules) {
487        should_redraw = true;
488    }
489
490    if style.corner_top_left_radius.link(entity, matched_rules) {
491        should_redraw = true;
492    }
493
494    if style.corner_top_right_radius.link(entity, matched_rules) {
495        should_redraw = true;
496    }
497
498    if style.corner_bottom_left_radius.link(entity, matched_rules) {
499        should_redraw = true;
500    }
501
502    if style.corner_bottom_right_radius.link(entity, matched_rules) {
503        should_redraw = true;
504    }
505
506    if style.outline_width.link(entity, matched_rules) {
507        should_redraw = true;
508    }
509
510    if style.outline_color.link(entity, matched_rules) {
511        should_redraw = true;
512    }
513
514    if style.outline_offset.link(entity, matched_rules) {
515        should_redraw = true;
516    }
517
518    if style.layout_type.link(entity, matched_rules) {
519        should_relayout = true;
520        should_redraw = true;
521    }
522
523    if style.position_type.link(entity, matched_rules) {
524        should_relayout = true;
525        should_redraw = true;
526    }
527
528    if style.alignment.link(entity, matched_rules) {
529        should_relayout = true;
530        should_redraw = true;
531    }
532
533    // Background
534    if style.background_color.link(entity, matched_rules) {
535        should_redraw = true;
536    }
537
538    if style.background_image.link(entity, matched_rules) {
539        should_redraw = true;
540    }
541
542    if style.background_size.link(entity, matched_rules) {
543        should_redraw = true;
544    }
545
546    // Font
547    if style.font_color.link(entity, matched_rules) {
548        should_redraw = true;
549        should_reflow = true;
550    }
551
552    if style.font_size.link(entity, matched_rules) {
553        should_relayout = true;
554        should_redraw = true;
555        should_reflow = true;
556    }
557
558    if style.font_family.link(entity, matched_rules) {
559        should_relayout = true;
560        should_redraw = true;
561        should_reflow = true;
562    }
563
564    if style.font_weight.link(entity, matched_rules) {
565        should_redraw = true;
566        should_relayout = true;
567        should_reflow = true;
568    }
569
570    if style.font_slant.link(entity, matched_rules) {
571        should_redraw = true;
572        should_relayout = true;
573        should_reflow = true;
574    }
575
576    if style.font_width.link(entity, matched_rules) {
577        should_redraw = true;
578        should_relayout = true;
579        should_reflow = true;
580    }
581
582    if style.font_variation_settings.link(entity, matched_rules) {
583        should_redraw = true;
584        should_relayout = true;
585        should_reflow = true;
586    }
587
588    if style.text_wrap.link(entity, matched_rules) {
589        should_redraw = true;
590        should_relayout = true;
591        should_reflow = true;
592    }
593
594    if style.text_align.link(entity, matched_rules) {
595        should_redraw = true;
596        should_reflow = true;
597    }
598
599    if style.text_overflow.link(entity, matched_rules) {
600        should_redraw = true;
601        should_reflow = true;
602    }
603
604    if style.line_clamp.link(entity, matched_rules) {
605        should_redraw = true;
606        should_reflow = true;
607    }
608
609    if style.selection_color.link(entity, matched_rules) {
610        should_redraw = true;
611    }
612
613    if style.caret_color.link(entity, matched_rules) {
614        should_redraw = true;
615    }
616
617    if style.text_decoration_line.link(entity, matched_rules) {
618        should_redraw = true;
619        should_reflow = true;
620    }
621
622    if style.text_stroke_width.link(entity, matched_rules) {
623        should_redraw = true;
624        should_reflow = true;
625    }
626
627    if style.text_stroke_style.link(entity, matched_rules) {
628        should_redraw = true;
629        should_reflow = true;
630    }
631
632    if style.underline_style.link(entity, matched_rules) {
633        should_redraw = true;
634        should_reflow = true;
635    }
636
637    if style.underline_color.link(entity, matched_rules) {
638        should_redraw = true;
639        should_reflow = true;
640    }
641
642    if style.overline_style.link(entity, matched_rules) {
643        should_redraw = true;
644        should_reflow = true;
645    }
646
647    if style.overline_color.link(entity, matched_rules) {
648        should_redraw = true;
649        should_reflow = true;
650    }
651
652    if style.strikethrough_style.link(entity, matched_rules) {
653        should_redraw = true;
654        should_reflow = true;
655    }
656
657    if style.strikethrough_color.link(entity, matched_rules) {
658        should_redraw = true;
659        should_reflow = true;
660    }
661
662    // Outer Shadow
663    if style.shadow.link(entity, matched_rules) {
664        should_redraw = true;
665    }
666
667    if style.padding_left.link(entity, matched_rules) {
668        should_relayout = true;
669        should_redraw = true;
670    }
671
672    if style.padding_right.link(entity, matched_rules) {
673        should_relayout = true;
674        should_redraw = true;
675    }
676
677    if style.padding_top.link(entity, matched_rules) {
678        should_relayout = true;
679        should_redraw = true;
680    }
681
682    if style.padding_bottom.link(entity, matched_rules) {
683        should_relayout = true;
684        should_redraw = true;
685    }
686
687    if style.vertical_gap.link(entity, matched_rules) {
688        should_relayout = true;
689        should_redraw = true;
690    }
691
692    if style.horizontal_gap.link(entity, matched_rules) {
693        should_relayout = true;
694        should_redraw = true;
695    }
696
697    if style.cursor.link(entity, matched_rules) {
698        should_redraw = true;
699    }
700
701    if style.pointer_events.link(entity, matched_rules) {
702        should_redraw = true;
703    }
704
705    // Transform
706    if style.transform.link(entity, matched_rules) {
707        should_redraw = true;
708    }
709
710    if style.transform_origin.link(entity, matched_rules) {
711        should_redraw = true;
712    }
713
714    if style.translate.link(entity, matched_rules) {
715        should_redraw = true;
716    }
717
718    if style.rotate.link(entity, matched_rules) {
719        should_redraw = true;
720    }
721
722    if style.scale.link(entity, matched_rules) {
723        should_redraw = true;
724    }
725
726    if style.fill.link(entity, matched_rules) {
727        should_redraw = true;
728    }
729
730    //
731    if should_relayout {
732        style.system_flags.set(SystemFlags::RELAYOUT, true);
733    }
734
735    if should_redraw {
736        redraw_entities.push(entity);
737    }
738
739    if should_reflow {
740        let iter = LayoutParentIterator::new(tree, entity);
741        for parent in iter {
742            if style.display.get(parent).copied().unwrap_or_default() != Display::None {
743                style.needs_text_update(parent);
744                break;
745            }
746        }
747    }
748}
749
750/// Compute a list of matching style rules for a given entity.
751pub(crate) fn compute_matched_rules(
752    entity: Entity,
753    store: &Style,
754    tree: &Tree<Entity>,
755    bloom: &BloomFilter,
756) -> Vec<(Rule, u32)> {
757    let mut matched_rules = Vec::with_capacity(16);
758
759    let mut cache = SelectorCaches::default();
760    let mut context = MatchingContext::new(
761        MatchingMode::Normal,
762        Some(bloom),
763        &mut cache,
764        QuirksMode::NoQuirks,
765        NeedsSelectorFlags::Yes,
766        MatchingForInvalidation::No,
767    );
768
769    let node = Node { entity, store, tree };
770
771    for (rule_id, rule) in store.rules.iter() {
772        let matches = matches_selector(&rule.selector, 0, Some(&rule.hashes), &node, &mut context);
773
774        if matches {
775            matched_rules.push((*rule_id, rule.selector.specificity()));
776        }
777    }
778
779    matched_rules.sort_by_key(|(_, s)| *s);
780    matched_rules.reverse();
781    matched_rules
782}
783
784fn has_same_selector(style: &Style, entity1: Entity, entity2: Entity) -> bool {
785    if let Some(element1) = style.element.get(entity1) {
786        if let Some(element2) = style.element.get(entity2) {
787            if element1 != element2 {
788                return false;
789            };
790        }
791    }
792
793    let id1 = if let Some(id) = style.ids.get(entity1) { id } else { "" };
794    let id2 = if let Some(id) = style.ids.get(entity2) { id } else { "" };
795
796    if id1 != id2 {
797        return false;
798    }
799
800    if let Some(classes1) = style.classes.get(entity1) {
801        if let Some(classes2) = style.classes.get(entity2) {
802            if !classes2.is_subset(classes1) || !classes1.is_subset(classes2) {
803                return false;
804            }
805        }
806    }
807
808    if let Some(psudeo_class_flag1) = style.pseudo_classes.get(entity1) {
809        if let Some(psudeo_class_flag2) = style.pseudo_classes.get(entity2) {
810            if psudeo_class_flag2.bits() != psudeo_class_flag1.bits() {
811                return false;
812            }
813        }
814    }
815
816    true
817}
818
819fn has_nth_child_rule(style: &Style, rules: &[(Rule, u32)]) -> bool {
820    for (rule, _) in rules {
821        let Some(style_rule) = style.rules.get(rule) else { continue };
822        for component in style_rule.selector.iter() {
823            let Component::Nth(n) = component else { continue };
824            if let NthType::Child | NthType::LastChild | NthType::OnlyChild = n.ty {
825                return true;
826            }
827        }
828    }
829    false
830}
831
832pub(crate) fn compute_element_hash(
833    entity: Entity,
834    tree: &Tree<Entity>,
835    style: &Style,
836    bloom: &mut BloomFilter,
837) {
838    let parent_iter = LayoutParentIterator::new(tree, entity);
839
840    for ancestor in parent_iter {
841        if let Some(element) = style.element.get(ancestor) {
842            bloom.insert_hash(*element);
843        }
844
845        if let Some(id) = style.ids.get(ancestor) {
846            bloom.insert_hash(fxhash::hash32(id));
847        }
848
849        if let Some(classes) = style.classes.get(ancestor) {
850            for class in classes {
851                bloom.insert_hash(fxhash::hash32(class));
852            }
853        }
854    }
855}
856
857struct MatchedRulesCache {
858    pub entity: Entity,
859    pub rules: Vec<(Rule, u32)>,
860}
861
862struct MatchedRules {
863    #[cfg(feature = "rayon")]
864    cache: ReadOnlyView<Entity, Vec<MatchedRulesCache>>,
865    #[cfg(not(feature = "rayon"))]
866    cache: HashMap<Entity, Vec<MatchedRulesCache>>,
867    // Stores the key/index into the cache to get the rules for a given entity.
868    rules: HashMap<Entity, (Entity, usize)>,
869}
870
871impl MatchedRules {
872    #[cfg(not(feature = "rayon"))]
873    fn build(entities: &[Entity], style: &Style, tree: &Tree<Entity>) -> Self {
874        let filter = &mut BloomFilter::default();
875
876        let mut cache = HashMap::new();
877        let rules = entities
878            .iter()
879            .filter_map(|entity| Self::build_inner(*entity, style, tree, filter, &mut cache))
880            .collect();
881
882        Self { rules, cache }
883    }
884
885    #[cfg(feature = "rayon")]
886    fn build_parallel(entities: &[Entity], style: &Style, tree: &Tree<Entity>) -> Self {
887        let num_threads = std::thread::available_parallelism().map_or(1, |n| n.get());
888
889        // Potential tuning oppertunity:
890        // Lower values make the BloomFilter more effective.
891        // Higher values allow more work be done in parellel.
892        let min_len = entities.len().div_ceil(num_threads);
893
894        let cache = DashMap::new();
895        let rules = entities
896            .par_iter()
897            .with_min_len(min_len)
898            .map_init(BloomFilter::default, |filter, entity| {
899                Self::build_inner(*entity, style, tree, filter, &cache)
900            })
901            .flatten_iter()
902            .collect();
903
904        Self { rules, cache: cache.into_read_only() }
905    }
906
907    fn build_inner(
908        entity: Entity,
909        style: &Style,
910        tree: &Tree<Entity>,
911        filter: &mut BloomFilter,
912        #[cfg(feature = "rayon")] rule_cache: &DashMap<Entity, Vec<MatchedRulesCache>>,
913        #[cfg(not(feature = "rayon"))] rule_cache: &mut HashMap<Entity, Vec<MatchedRulesCache>>,
914    ) -> Option<(Entity, (Entity, usize))> {
915        compute_element_hash(entity, tree, style, filter);
916
917        let parent = tree.get_layout_parent(entity).unwrap_or(Entity::root());
918
919        let mut matched_index = None;
920
921        if !tree.is_first_child(entity) && !tree.is_last_child(entity) {
922            if let Some(cache) = rule_cache.get(&parent) {
923                matched_index = cache.iter().position(|entry| {
924                    has_same_selector(style, entry.entity, entity)
925                        && !has_nth_child_rule(style, &entry.rules)
926                });
927            }
928        }
929
930        if matched_index.is_none() {
931            let rules = compute_matched_rules(entity, style, tree, filter);
932            if !rules.is_empty() {
933                #[cfg(feature = "rayon")]
934                {
935                    let mut entry = rule_cache.entry(parent).or_default();
936                    entry.value_mut().push(MatchedRulesCache { entity, rules });
937                    matched_index = Some(entry.value().len() - 1);
938                }
939                #[cfg(not(feature = "rayon"))]
940                {
941                    let entry = rule_cache.entry(parent).or_default();
942                    entry.push(MatchedRulesCache { entity, rules });
943                    matched_index = Some(entry.len() - 1);
944                }
945            }
946        }
947
948        matched_index.map(|i| (entity, (parent, i)))
949    }
950
951    fn get(&self, entity: &Entity) -> Option<&[(Rule, u32)]> {
952        let (parent, i) = self.rules.get(entity)?;
953        let parent_cache = self.cache.get(parent)?;
954        let entry = parent_cache.get(*i)?;
955        if entry.rules.is_empty() {
956            None
957        } else {
958            Some(&entry.rules)
959        }
960    }
961}
962
963// Iterates the tree and determines the matching style rules for each entity, then links the entity to the corresponding style rule data.
964pub(crate) fn style_system(cx: &mut Context) {
965    let mut redraw_entities = Vec::new();
966
967    inline_inheritance_system(cx, &mut redraw_entities);
968
969    if cx.style.restyle.is_empty() {
970        return;
971    }
972
973    let entities = TreeBreadthIterator::full(&cx.tree)
974        .filter(|e| cx.style.restyle.contains(*e))
975        .collect::<Vec<_>>();
976
977    let matched_rules = {
978        #[cfg(feature = "rayon")]
979        {
980            MatchedRules::build_parallel(&entities, &cx.style, &cx.tree)
981        }
982        #[cfg(not(feature = "rayon"))]
983        {
984            MatchedRules::build(&entities, &cx.style, &cx.tree)
985        }
986    };
987
988    //  Apply matched rules to entities
989    for entity in entities {
990        if let Some(matched_rules) = matched_rules.get(&entity) {
991            link_style_data(
992                &mut cx.style,
993                &mut cx.cache,
994                &cx.tree,
995                entity,
996                &mut redraw_entities,
997                matched_rules,
998            );
999        }
1000    }
1001    cx.style.restyle.clear();
1002
1003    shared_inheritance_system(cx, &mut redraw_entities);
1004
1005    for entity in redraw_entities {
1006        cx.needs_redraw(entity);
1007    }
1008}