tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

traversal.rs (31148B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! Traversing the DOM tree; the bloom filter.
      6 
      7 use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};
      8 use crate::data::{ElementData, ElementStyles, RestyleKind};
      9 use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode};
     10 use crate::invalidation::element::restyle_hints::RestyleHint;
     11 use crate::matching::{ChildRestyleRequirement, MatchMethods};
     12 use crate::selector_parser::PseudoElement;
     13 use crate::sharing::StyleSharingTarget;
     14 use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
     15 use crate::stylist::RuleInclusion;
     16 use crate::traversal_flags::TraversalFlags;
     17 use selectors::matching::SelectorCaches;
     18 #[cfg(feature = "gecko")]
     19 use selectors::parser::PseudoElement as PseudoElementTrait;
     20 use smallvec::SmallVec;
     21 use std::collections::HashMap;
     22 
     23 /// A cache from element reference to known-valid computed style.
     24 pub type UndisplayedStyleCache =
     25    HashMap<selectors::OpaqueElement, servo_arc::Arc<crate::properties::ComputedValues>>;
     26 
     27 /// A per-traversal-level chunk of data. This is sent down by the traversal, and
     28 /// currently only holds the dom depth for the bloom filter.
     29 ///
     30 /// NB: Keep this as small as possible, please!
     31 #[derive(Clone, Copy, Debug)]
     32 pub struct PerLevelTraversalData {
     33    /// The current dom depth.
     34    ///
     35    /// This is kept with cooperation from the traversal code and the bloom
     36    /// filter.
     37    pub current_dom_depth: usize,
     38 }
     39 
     40 /// We use this structure, rather than just returning a boolean from pre_traverse,
     41 /// to enfore that callers process root invalidations before starting the traversal.
     42 pub struct PreTraverseToken<E: TElement>(Option<E>);
     43 impl<E: TElement> PreTraverseToken<E> {
     44    /// Whether we should traverse children.
     45    pub fn should_traverse(&self) -> bool {
     46        self.0.is_some()
     47    }
     48 
     49    /// Returns the traversal root for the current traversal.
     50    pub(crate) fn traversal_root(self) -> Option<E> {
     51        self.0
     52    }
     53 }
     54 
     55 /// A global variable holding the state of
     56 /// `is_servo_nonincremental_layout()`.
     57 /// See [#22854](https://github.com/servo/servo/issues/22854).
     58 #[cfg(feature = "servo")]
     59 pub static IS_SERVO_NONINCREMENTAL_LAYOUT: std::sync::atomic::AtomicBool =
     60    std::sync::atomic::AtomicBool::new(false);
     61 
     62 #[cfg(feature = "servo")]
     63 #[inline]
     64 fn is_servo_nonincremental_layout() -> bool {
     65    use std::sync::atomic::Ordering;
     66 
     67    IS_SERVO_NONINCREMENTAL_LAYOUT.load(Ordering::Relaxed)
     68 }
     69 
     70 #[cfg(not(feature = "servo"))]
     71 #[inline]
     72 fn is_servo_nonincremental_layout() -> bool {
     73    false
     74 }
     75 
     76 /// A DOM Traversal trait, that is used to generically implement styling for
     77 /// Gecko and Servo.
     78 pub trait DomTraversal<E: TElement>: Sync {
     79    /// Process `node` on the way down, before its children have been processed.
     80    ///
     81    /// The callback is invoked for each child node that should be processed by
     82    /// the traversal.
     83    fn process_preorder<F>(
     84        &self,
     85        data: &PerLevelTraversalData,
     86        context: &mut StyleContext<E>,
     87        node: E::ConcreteNode,
     88        note_child: F,
     89    ) where
     90        F: FnMut(E::ConcreteNode);
     91 
     92    /// Process `node` on the way up, after its children have been processed.
     93    ///
     94    /// This is only executed if `needs_postorder_traversal` returns true.
     95    fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);
     96 
     97    /// Boolean that specifies whether a bottom up traversal should be
     98    /// performed.
     99    ///
    100    /// If it's false, then process_postorder has no effect at all.
    101    fn needs_postorder_traversal() -> bool {
    102        true
    103    }
    104 
    105    /// Handles the postorder step of the traversal, if it exists, by bubbling
    106    /// up the parent chain.
    107    ///
    108    /// If we are the last child that finished processing, recursively process
    109    /// our parent. Else, stop. Also, stop at the root.
    110    ///
    111    /// Thus, if we start with all the leaves of a tree, we end up traversing
    112    /// the whole tree bottom-up because each parent will be processed exactly
    113    /// once (by the last child that finishes processing).
    114    ///
    115    /// The only communication between siblings is that they both
    116    /// fetch-and-subtract the parent's children count. This makes it safe to
    117    /// call durign the parallel traversal.
    118    fn handle_postorder_traversal(
    119        &self,
    120        context: &mut StyleContext<E>,
    121        root: OpaqueNode,
    122        mut node: E::ConcreteNode,
    123        children_to_process: isize,
    124    ) {
    125        // If the postorder step is a no-op, don't bother.
    126        if !Self::needs_postorder_traversal() {
    127            return;
    128        }
    129 
    130        if children_to_process == 0 {
    131            // We are a leaf. Walk up the chain.
    132            loop {
    133                self.process_postorder(context, node);
    134                if node.opaque() == root {
    135                    break;
    136                }
    137                let parent = node.traversal_parent().unwrap();
    138                let remaining = parent.did_process_child();
    139                if remaining != 0 {
    140                    // The parent has other unprocessed descendants. We only
    141                    // perform postorder processing after the last descendant
    142                    // has been processed.
    143                    break;
    144                }
    145 
    146                node = parent.as_node();
    147            }
    148        } else {
    149            // Otherwise record the number of children to process when the time
    150            // comes.
    151            node.as_element()
    152                .unwrap()
    153                .store_children_to_process(children_to_process);
    154        }
    155    }
    156 
    157    /// Style invalidations happen when traversing from a parent to its children.
    158    /// However, this mechanism can't handle style invalidations on the root. As
    159    /// such, we have a pre-traversal step to handle that part and determine whether
    160    /// a full traversal is needed.
    161    fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {
    162        use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;
    163 
    164        let traversal_flags = shared_context.traversal_flags;
    165 
    166        let mut data = root.mutate_data();
    167        let mut data = data.as_mut().map(|d| &mut **d);
    168 
    169        if let Some(ref mut data) = data {
    170            if !traversal_flags.for_animation_only() {
    171                // Invalidate our style, and that of our siblings and
    172                // descendants as needed.
    173                let invalidation_result = data.invalidate_style_if_needed(
    174                    root,
    175                    shared_context,
    176                    None,
    177                    &mut SelectorCaches::default(),
    178                );
    179 
    180                if invalidation_result.has_invalidated_siblings() {
    181                    let actual_root = root.as_node().parent_element_or_host().expect(
    182                        "How in the world can you invalidate \
    183                         siblings without a parent?",
    184                    );
    185                    propagate_dirty_bit_up_to(actual_root, root);
    186                    return PreTraverseToken(Some(actual_root));
    187                }
    188            }
    189        }
    190 
    191        let should_traverse =
    192            Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));
    193 
    194        // If we're not going to traverse at all, we may need to clear some state
    195        // off the root (which would normally be done at the end of recalc_style_at).
    196        if !should_traverse && data.is_some() {
    197            clear_state_after_traversing(root, data.unwrap(), traversal_flags);
    198        }
    199 
    200        PreTraverseToken(if should_traverse { Some(root) } else { None })
    201    }
    202 
    203    /// Returns true if traversal should visit a text node. The style system
    204    /// never processes text nodes, but Servo overrides this to visit them for
    205    /// flow construction when necessary.
    206    fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {
    207        debug_assert!(node.is_text_node());
    208        false
    209    }
    210 
    211    /// Returns true if traversal is needed for the given element and subtree.
    212    fn element_needs_traversal(
    213        el: E,
    214        traversal_flags: TraversalFlags,
    215        data: Option<&ElementData>,
    216    ) -> bool {
    217        debug!(
    218            "element_needs_traversal({:?}, {:?}, {:?})",
    219            el, traversal_flags, data
    220        );
    221 
    222        // Non-incremental layout visits every node.
    223        if is_servo_nonincremental_layout() {
    224            return true;
    225        }
    226 
    227        // Unwrap the data.
    228        let data = match data {
    229            Some(d) if d.has_styles() => d,
    230            _ => return true,
    231        };
    232 
    233        if traversal_flags.for_animation_only() {
    234            // In case of animation-only traversal we need to traverse the element if the element
    235            // has animation only dirty descendants bit, or animation-only restyle hint.
    236            return el.has_animation_only_dirty_descendants()
    237                || data.hint.has_animation_hint_or_recascade();
    238        }
    239 
    240        // If the dirty descendants bit is set, we need to traverse no matter
    241        // what. Skip examining the ElementData.
    242        if el.has_dirty_descendants() {
    243            return true;
    244        }
    245 
    246        // If we have a restyle hint or need to recascade, we need to visit the
    247        // element.
    248        //
    249        // Note that this is different than checking has_current_styles_for_traversal(),
    250        // since that can return true even if we have a restyle hint indicating
    251        // that the element's descendants (but not necessarily the element) need
    252        // restyling.
    253        if !data.hint.is_empty() {
    254            return true;
    255        }
    256 
    257        // Servo uses the post-order traversal for flow construction, so we need
    258        // to traverse any element with damage so that we can perform fixup /
    259        // reconstruction on our way back up the tree.
    260        if cfg!(feature = "servo") && !data.damage.is_empty() {
    261            return true;
    262        }
    263 
    264        trace!("{:?} doesn't need traversal", el);
    265        false
    266    }
    267 
    268    /// Return the shared style context common to all worker threads.
    269    fn shared_context(&self) -> &SharedStyleContext<'_>;
    270 }
    271 
    272 /// Manually resolve style by sequentially walking up the parent chain to the
    273 /// first styled Element, ignoring pending restyles. The resolved style is made
    274 /// available via a callback, and can be dropped by the time this function
    275 /// returns in the display:none subtree case.
    276 pub fn resolve_style<E>(
    277    context: &mut StyleContext<E>,
    278    element: E,
    279    rule_inclusion: RuleInclusion,
    280    pseudo: Option<&PseudoElement>,
    281    mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
    282 ) -> ElementStyles
    283 where
    284    E: TElement,
    285 {
    286    debug_assert!(
    287        rule_inclusion == RuleInclusion::DefaultOnly
    288            || pseudo.map_or(false, |p| p.is_before_or_after())
    289            || element.borrow_data().map_or(true, |d| !d.has_styles()),
    290        "Why are we here?"
    291    );
    292    debug_assert!(
    293        rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
    294        "can't use the cache for default styles only"
    295    );
    296 
    297    let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
    298 
    299    // Clear the bloom filter, just in case the caller is reusing TLS.
    300    context.thread_local.bloom_filter.clear();
    301 
    302    let mut style = None;
    303    let mut ancestor = element.traversal_parent();
    304    while let Some(current) = ancestor {
    305        if rule_inclusion == RuleInclusion::All {
    306            if let Some(data) = current.borrow_data() {
    307                if let Some(ancestor_style) = data.styles.get_primary() {
    308                    style = Some(ancestor_style.clone());
    309                    break;
    310                }
    311            }
    312        }
    313        if let Some(ref mut cache) = undisplayed_style_cache {
    314            if let Some(s) = cache.get(&current.opaque()) {
    315                style = Some(s.clone());
    316                break;
    317            }
    318        }
    319        ancestors_requiring_style_resolution.push(current);
    320        ancestor = current.traversal_parent();
    321    }
    322 
    323    if let Some(ancestor) = ancestor {
    324        context.thread_local.bloom_filter.rebuild(ancestor);
    325        context.thread_local.bloom_filter.push(ancestor);
    326    }
    327 
    328    let mut layout_parent_style = style.clone();
    329    while let Some(style) = layout_parent_style.take() {
    330        if !style.is_display_contents() {
    331            layout_parent_style = Some(style);
    332            break;
    333        }
    334 
    335        ancestor = ancestor.unwrap().traversal_parent();
    336        layout_parent_style =
    337            ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
    338    }
    339 
    340    for ancestor in ancestors_requiring_style_resolution.iter().rev() {
    341        context.thread_local.bloom_filter.assert_complete(*ancestor);
    342 
    343        // Actually `PseudoElementResolution` doesn't really matter here.
    344        // (but it does matter below!).
    345        let primary_style = StyleResolverForElement::new(
    346            *ancestor,
    347            context,
    348            rule_inclusion,
    349            PseudoElementResolution::IfApplicable,
    350        )
    351        .resolve_primary_style(
    352            style.as_deref(),
    353            layout_parent_style.as_deref(),
    354            selectors::matching::IncludeStartingStyle::No,
    355        );
    356 
    357        let is_display_contents = primary_style.style().is_display_contents();
    358 
    359        style = Some(primary_style.style.0);
    360        if !is_display_contents {
    361            layout_parent_style = style.clone();
    362        }
    363 
    364        if let Some(ref mut cache) = undisplayed_style_cache {
    365            cache.insert(ancestor.opaque(), style.clone().unwrap());
    366        }
    367        context.thread_local.bloom_filter.push(*ancestor);
    368    }
    369 
    370    context.thread_local.bloom_filter.assert_complete(element);
    371    let styles: ElementStyles = StyleResolverForElement::new(
    372        element,
    373        context,
    374        rule_inclusion,
    375        PseudoElementResolution::Force,
    376    )
    377    .resolve_style(style.as_deref(), layout_parent_style.as_deref())
    378    .into();
    379 
    380    if let Some(ref mut cache) = undisplayed_style_cache {
    381        cache.insert(element.opaque(), styles.primary().clone());
    382    }
    383 
    384    styles
    385 }
    386 
    387 /// Calculates the style for a single node.
    388 #[inline]
    389 #[allow(unsafe_code)]
    390 pub fn recalc_style_at<E, D, F>(
    391    _traversal: &D,
    392    traversal_data: &PerLevelTraversalData,
    393    context: &mut StyleContext<E>,
    394    element: E,
    395    data: &mut ElementData,
    396    note_child: F,
    397 ) where
    398    E: TElement,
    399    D: DomTraversal<E>,
    400    F: FnMut(E::ConcreteNode),
    401 {
    402    use std::cmp;
    403 
    404    let flags = context.shared.traversal_flags;
    405    let is_initial_style = !data.has_styles();
    406 
    407    context.thread_local.statistics.elements_traversed += 1;
    408    debug_assert!(
    409        flags.intersects(TraversalFlags::AnimationOnly)
    410            || is_initial_style
    411            || !element.has_snapshot()
    412            || element.handled_snapshot(),
    413        "Should've handled snapshots here already"
    414    );
    415 
    416    let restyle_kind = data.restyle_kind(&context.shared);
    417    debug!(
    418        "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
    419        element,
    420        restyle_kind,
    421        element.has_dirty_descendants(),
    422        data
    423    );
    424 
    425    let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
    426 
    427    // Compute style for this element if necessary.
    428    if let Some(restyle_kind) = restyle_kind {
    429        child_restyle_requirement =
    430            compute_style(traversal_data, context, element, data, restyle_kind);
    431 
    432        if !element.matches_user_and_content_rules() {
    433            // We must always cascade native anonymous subtrees, since they
    434            // may have pseudo-elements underneath that would inherit from the
    435            // closest non-NAC ancestor instead of us.
    436            child_restyle_requirement = cmp::max(
    437                child_restyle_requirement,
    438                ChildRestyleRequirement::MustCascadeChildren,
    439            );
    440        }
    441 
    442        // If we're restyling this element to display:none, throw away all style
    443        // data in the subtree, notify the caller to early-return.
    444        if data.styles.is_display_none() {
    445            debug!(
    446                "{:?} style is display:none - clearing data from descendants.",
    447                element
    448            );
    449            unsafe {
    450                clear_descendant_data(element);
    451            }
    452        }
    453 
    454        // Inform any paint worklets of changed style, to speculatively
    455        // evaluate the worklet code. In the case that the size hasn't changed,
    456        // this will result in increased concurrency between script and layout.
    457        notify_paint_worklet(context, data);
    458    } else {
    459        debug_assert!(data.has_styles());
    460        data.set_traversed_without_styling();
    461    }
    462 
    463    // Now that matching and cascading is done, clear the bits corresponding to
    464    // those operations and compute the propagated restyle hint (unless we're
    465    // not processing invalidations, in which case don't need to propagate it
    466    // and must avoid clearing it).
    467    debug_assert!(
    468        flags.for_animation_only() || !data.hint.has_animation_hint(),
    469        "animation restyle hint should be handled during \
    470         animation-only restyles"
    471    );
    472    let mut propagated_hint = data.hint.propagate(&flags);
    473    trace!(
    474        "propagated_hint={:?}, restyle_requirement={:?}, \
    475         is_display_none={:?}, implementing_pseudo={:?}",
    476        propagated_hint,
    477        child_restyle_requirement,
    478        data.styles.is_display_none(),
    479        element.implemented_pseudo_element()
    480    );
    481 
    482    // Integrate the child cascade requirement into the propagated hint.
    483    match child_restyle_requirement {
    484        ChildRestyleRequirement::CanSkipCascade => {},
    485        ChildRestyleRequirement::MustCascadeDescendants => {
    486            propagated_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
    487        },
    488        ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => {
    489            propagated_hint |= RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE;
    490        },
    491        ChildRestyleRequirement::MustCascadeChildren => {
    492            propagated_hint |= RestyleHint::RECASCADE_SELF;
    493        },
    494        ChildRestyleRequirement::MustMatchDescendants => {
    495            propagated_hint |= RestyleHint::restyle_subtree();
    496        },
    497    }
    498 
    499    let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
    500        element.has_animation_only_dirty_descendants()
    501    } else {
    502        element.has_dirty_descendants()
    503    };
    504 
    505    // Before examining each child individually, try to prove that our children
    506    // don't need style processing. They need processing if any of the following
    507    // conditions hold:
    508    //
    509    //  * We have the dirty descendants bit.
    510    //  * We're propagating a restyle hint.
    511    //  * This is a servo non-incremental traversal.
    512    //
    513    // We only do this if we're not a display: none root, since in that case
    514    // it's useless to style children.
    515    let mut traverse_children = has_dirty_descendants_for_this_restyle
    516        || !propagated_hint.is_empty()
    517        || is_servo_nonincremental_layout();
    518 
    519    traverse_children = traverse_children && !data.styles.is_display_none();
    520 
    521    // Examine our children, and enqueue the appropriate ones for traversal.
    522    if traverse_children {
    523        note_children::<E, D, F>(
    524            context,
    525            element,
    526            data,
    527            propagated_hint,
    528            is_initial_style,
    529            note_child,
    530        );
    531    }
    532 
    533    // FIXME(bholley): Make these assertions pass for servo.
    534    if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
    535        debug_assert!(!element.has_dirty_descendants());
    536        debug_assert!(!element.has_animation_only_dirty_descendants());
    537    }
    538 
    539    clear_state_after_traversing(element, data, flags);
    540 }
    541 
    542 fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
    543 where
    544    E: TElement,
    545 {
    546    if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
    547        debug_assert!(flags.for_animation_only());
    548        data.clear_restyle_flags_and_damage();
    549        unsafe {
    550            element.unset_animation_only_dirty_descendants();
    551        }
    552    }
    553 }
    554 
    555 fn compute_style<E>(
    556    traversal_data: &PerLevelTraversalData,
    557    context: &mut StyleContext<E>,
    558    element: E,
    559    data: &mut ElementData,
    560    kind: RestyleKind,
    561 ) -> ChildRestyleRequirement
    562 where
    563    E: TElement,
    564 {
    565    use crate::data::RestyleKind::*;
    566 
    567    context.thread_local.statistics.elements_styled += 1;
    568    debug!("compute_style: {:?} (kind={:?})", element, kind);
    569 
    570    if data.has_styles() {
    571        data.set_restyled();
    572    }
    573 
    574    let mut important_rules_changed = false;
    575    let new_styles = match kind {
    576        MatchAndCascade => {
    577            debug_assert!(
    578                !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
    579                "MatchAndCascade shouldn't normally be processed during animation-only traversal"
    580            );
    581            // Ensure the bloom filter is up to date.
    582            context
    583                .thread_local
    584                .bloom_filter
    585                .insert_parents_recovering(element, traversal_data.current_dom_depth);
    586 
    587            context.thread_local.bloom_filter.assert_complete(element);
    588            debug_assert_eq!(
    589                context.thread_local.bloom_filter.matching_depth(),
    590                traversal_data.current_dom_depth
    591            );
    592 
    593            // This is only relevant for animations as of right now.
    594            important_rules_changed = true;
    595 
    596            let mut target = StyleSharingTarget::new(element);
    597 
    598            // Now that our bloom filter is set up, try the style sharing
    599            // cache.
    600            match target.share_style_if_possible(context) {
    601                Some(shared_styles) => {
    602                    context.thread_local.statistics.styles_shared += 1;
    603                    shared_styles
    604                },
    605                None => {
    606                    context.thread_local.statistics.elements_matched += 1;
    607                    // Perform the matching and cascading.
    608                    let new_styles = {
    609                        let mut resolver = StyleResolverForElement::new(
    610                            element,
    611                            context,
    612                            RuleInclusion::All,
    613                            PseudoElementResolution::IfApplicable,
    614                        );
    615 
    616                        resolver.resolve_style_with_default_parents()
    617                    };
    618 
    619                    context.thread_local.sharing_cache.insert_if_possible(
    620                        &element,
    621                        &new_styles.primary,
    622                        Some(&mut target),
    623                        traversal_data.current_dom_depth,
    624                        &context.shared,
    625                    );
    626 
    627                    new_styles
    628                },
    629            }
    630        },
    631        CascadeWithReplacements(flags) => {
    632            // Skipping full matching, load cascade inputs from previous values.
    633            let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
    634            important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
    635 
    636            let mut resolver = StyleResolverForElement::new(
    637                element,
    638                context,
    639                RuleInclusion::All,
    640                PseudoElementResolution::IfApplicable,
    641            );
    642 
    643            resolver
    644                .cascade_styles_with_default_parents(cascade_inputs, data.may_have_starting_style())
    645        },
    646        CascadeOnly => {
    647            // Skipping full matching, load cascade inputs from previous values.
    648            let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
    649 
    650            let new_styles = {
    651                let mut resolver = StyleResolverForElement::new(
    652                    element,
    653                    context,
    654                    RuleInclusion::All,
    655                    PseudoElementResolution::IfApplicable,
    656                );
    657 
    658                resolver.cascade_styles_with_default_parents(
    659                    cascade_inputs,
    660                    data.may_have_starting_style(),
    661                )
    662            };
    663 
    664            // Insert into the cache, but only if this style isn't reused from a
    665            // sibling or cousin. Otherwise, recascading a bunch of identical
    666            // elements would unnecessarily flood the cache with identical entries.
    667            //
    668            // This is analogous to the obvious "don't insert an element that just
    669            // got a hit in the style sharing cache" behavior in the MatchAndCascade
    670            // handling above.
    671            //
    672            // Note that, for the MatchAndCascade path, we still insert elements that
    673            // shared styles via the rule node, because we know that there's something
    674            // different about them that caused them to miss the sharing cache before
    675            // selector matching. If we didn't, we would still end up with the same
    676            // number of eventual styles, but would potentially miss out on various
    677            // opportunities for skipping selector matching, which could hurt
    678            // performance.
    679            if !new_styles.primary.reused_via_rule_node {
    680                context.thread_local.sharing_cache.insert_if_possible(
    681                    &element,
    682                    &new_styles.primary,
    683                    None,
    684                    traversal_data.current_dom_depth,
    685                    &context.shared,
    686                );
    687            }
    688 
    689            new_styles
    690        },
    691    };
    692 
    693    element.finish_restyle(context, data, new_styles, important_rules_changed)
    694 }
    695 
    696 #[cfg(feature = "servo")]
    697 fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
    698 where
    699    E: TElement,
    700 {
    701    use crate::values::generics::image::Image;
    702    use style_traits::ToCss;
    703 
    704    // We speculatively evaluate any paint worklets during styling.
    705    // This allows us to run paint worklets in parallel with style and layout.
    706    // Note that this is wasted effort if the size of the node has
    707    // changed, but in may cases it won't have.
    708    if let Some(ref values) = data.styles.primary {
    709        for image in &values.get_background().background_image.0 {
    710            let (name, arguments) = match *image {
    711                Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
    712                _ => continue,
    713            };
    714            let painter = match context.shared.registered_speculative_painters.get(name) {
    715                Some(painter) => painter,
    716                None => continue,
    717            };
    718            let properties = painter
    719                .properties()
    720                .iter()
    721                .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
    722                .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
    723                .collect();
    724            let arguments = arguments
    725                .iter()
    726                .map(|argument| argument.to_css_string())
    727                .collect();
    728            debug!("Notifying paint worklet {}.", painter.name());
    729            painter.speculatively_draw_a_paint_image(properties, arguments);
    730        }
    731    }
    732 }
    733 
    734 #[cfg(not(feature = "servo"))]
    735 fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
    736 where
    737    E: TElement,
    738 {
    739    // The CSS paint API is Servo-only at the moment
    740 }
    741 
    742 fn note_children<E, D, F>(
    743    context: &mut StyleContext<E>,
    744    element: E,
    745    data: &ElementData,
    746    propagated_hint: RestyleHint,
    747    is_initial_style: bool,
    748    mut note_child: F,
    749 ) where
    750    E: TElement,
    751    D: DomTraversal<E>,
    752    F: FnMut(E::ConcreteNode),
    753 {
    754    trace!("note_children: {:?}", element);
    755    let flags = context.shared.traversal_flags;
    756 
    757    // Loop over all the traversal children.
    758    for child_node in element.traversal_children() {
    759        let child = match child_node.as_element() {
    760            Some(el) => el,
    761            None => {
    762                if is_servo_nonincremental_layout()
    763                    || D::text_node_needs_traversal(child_node, data)
    764                {
    765                    note_child(child_node);
    766                }
    767                continue;
    768            },
    769        };
    770 
    771        let mut child_data = child.mutate_data();
    772        let mut child_data = child_data.as_mut().map(|d| &mut **d);
    773        trace!(
    774            " > {:?} -> {:?} + {:?}, pseudo: {:?}",
    775            child,
    776            child_data.as_ref().map(|d| d.hint),
    777            propagated_hint,
    778            child.implemented_pseudo_element()
    779        );
    780 
    781        if let Some(ref mut child_data) = child_data {
    782            child_data.hint.insert(propagated_hint);
    783 
    784            // Handle element snapshots and invalidation of descendants and siblings
    785            // as needed.
    786            //
    787            // NB: This will be a no-op if there's no snapshot.
    788            child_data.invalidate_style_if_needed(
    789                child,
    790                &context.shared,
    791                Some(&context.thread_local.stack_limit_checker),
    792                &mut context.thread_local.selector_caches,
    793            );
    794        }
    795 
    796        if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
    797            note_child(child_node);
    798 
    799            // Set the dirty descendants bit on the parent as needed, so that we
    800            // can find elements during the post-traversal.
    801            //
    802            // Note that these bits may be cleared again at the bottom of
    803            // recalc_style_at if requested by the caller.
    804            if !is_initial_style {
    805                if flags.for_animation_only() {
    806                    unsafe {
    807                        element.set_animation_only_dirty_descendants();
    808                    }
    809                } else {
    810                    unsafe {
    811                        element.set_dirty_descendants();
    812                    }
    813                }
    814            }
    815        }
    816    }
    817 }
    818 
    819 /// Clear style data for all the subtree under `root` (but not for root itself).
    820 ///
    821 /// We use a list to avoid unbounded recursion, which we need to avoid in the
    822 /// parallel traversal because the rayon stacks are small.
    823 pub unsafe fn clear_descendant_data<E>(root: E)
    824 where
    825    E: TElement,
    826 {
    827    let mut parents = SmallVec::<[E; 32]>::new();
    828    parents.push(root);
    829    while let Some(p) = parents.pop() {
    830        for kid in p.traversal_children() {
    831            if let Some(kid) = kid.as_element() {
    832                // We maintain an invariant that, if an element has data, all its
    833                // ancestors have data as well.
    834                //
    835                // By consequence, any element without data has no descendants with
    836                // data.
    837                if kid.has_data() {
    838                    kid.clear_data();
    839                    parents.push(kid);
    840                }
    841            }
    842        }
    843    }
    844 
    845    // Make sure not to clear NODE_NEEDS_FRAME on the root.
    846    root.clear_descendant_bits();
    847 }