tor-browser

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

matching.rs (44093B)


      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 //! High-level interface to CSS selector matching.
      6 
      7 #![allow(unsafe_code)]
      8 #![deny(missing_docs)]
      9 
     10 use crate::computed_value_flags::ComputedValueFlags;
     11 #[cfg(feature = "servo")]
     12 use crate::context::CascadeInputs;
     13 use crate::context::{ElementCascadeInputs, QuirksMode};
     14 use crate::context::{SharedStyleContext, StyleContext};
     15 use crate::data::{ElementData, ElementStyles};
     16 use crate::dom::TElement;
     17 #[cfg(feature = "servo")]
     18 use crate::dom::TNode;
     19 use crate::invalidation::element::restyle_hints::RestyleHint;
     20 use crate::properties::longhands::display::computed_value::T as Display;
     21 use crate::properties::ComputedValues;
     22 use crate::properties::PropertyDeclarationBlock;
     23 use crate::rule_tree::{CascadeLevel, StrongRuleNode};
     24 use crate::selector_parser::{PseudoElement, RestyleDamage};
     25 use crate::shared_lock::Locked;
     26 use crate::style_resolver::StyleResolverForElement;
     27 use crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles};
     28 use crate::stylesheets::layer_rule::LayerOrder;
     29 use crate::stylist::RuleInclusion;
     30 use crate::traversal_flags::TraversalFlags;
     31 use servo_arc::{Arc, ArcBorrow};
     32 
     33 /// Represents the result of comparing an element's old and new style.
     34 #[derive(Debug)]
     35 pub struct StyleDifference {
     36    /// The resulting damage.
     37    pub damage: RestyleDamage,
     38 
     39    /// Whether any styles changed.
     40    pub change: StyleChange,
     41 }
     42 
     43 /// Represents whether or not the style of an element has changed.
     44 #[derive(Clone, Copy, Debug)]
     45 pub enum StyleChange {
     46    /// The style hasn't changed.
     47    Unchanged,
     48    /// The style has changed.
     49    Changed {
     50        /// Whether only reset structs changed.
     51        reset_only: bool,
     52    },
     53 }
     54 
     55 /// Whether or not newly computed values for an element need to be cascaded to
     56 /// children (or children might need to be re-matched, e.g., for container
     57 /// queries).
     58 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
     59 pub enum ChildRestyleRequirement {
     60    /// Old and new computed values were the same, or we otherwise know that
     61    /// we won't bother recomputing style for children, so we can skip cascading
     62    /// the new values into child elements.
     63    CanSkipCascade = 0,
     64    /// The same as `MustCascadeChildren`, but we only need to actually
     65    /// recascade if the child inherits any explicit reset style.
     66    MustCascadeChildrenIfInheritResetStyle = 1,
     67    /// Old and new computed values were different, so we must cascade the
     68    /// new values to children.
     69    MustCascadeChildren = 2,
     70    /// The same as `MustCascadeChildren`, but for the entire subtree.  This is
     71    /// used to handle root font-size updates needing to recascade the whole
     72    /// document.
     73    MustCascadeDescendants = 3,
     74    /// We need to re-match the whole subttree. This is used to handle container
     75    /// query relative unit changes for example. Container query size changes
     76    /// also trigger re-match, but after layout.
     77    MustMatchDescendants = 4,
     78 }
     79 
     80 /// Determines which styles are being cascaded currently.
     81 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     82 enum CascadeVisitedMode {
     83    /// Cascade the regular, unvisited styles.
     84    Unvisited,
     85    /// Cascade the styles used when an element's relevant link is visited.  A
     86    /// "relevant link" is the element being matched if it is a link or the
     87    /// nearest ancestor link.
     88    Visited,
     89 }
     90 
     91 trait PrivateMatchMethods: TElement {
     92    fn replace_single_rule_node(
     93        context: &SharedStyleContext,
     94        level: CascadeLevel,
     95        layer_order: LayerOrder,
     96        pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
     97        path: &mut StrongRuleNode,
     98    ) -> bool {
     99        let stylist = &context.stylist;
    100        let guards = &context.guards;
    101 
    102        let mut important_rules_changed = false;
    103        let new_node = stylist.rule_tree().update_rule_at_level(
    104            level,
    105            layer_order,
    106            pdb,
    107            path,
    108            guards,
    109            &mut important_rules_changed,
    110        );
    111        if let Some(n) = new_node {
    112            *path = n;
    113        }
    114        important_rules_changed
    115    }
    116 
    117    /// Updates the rule nodes without re-running selector matching, using just
    118    /// the rule tree, for a specific visited mode.
    119    ///
    120    /// Returns true if an !important rule was replaced.
    121    fn replace_rules_internal(
    122        &self,
    123        replacements: RestyleHint,
    124        context: &mut StyleContext<Self>,
    125        cascade_visited: CascadeVisitedMode,
    126        cascade_inputs: &mut ElementCascadeInputs,
    127    ) -> bool {
    128        debug_assert!(
    129            replacements.intersects(RestyleHint::replacements())
    130                && (replacements & !RestyleHint::replacements()).is_empty()
    131        );
    132 
    133        let primary_rules = match cascade_visited {
    134            CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
    135            CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
    136        };
    137 
    138        let primary_rules = match primary_rules {
    139            Some(r) => r,
    140            None => return false,
    141        };
    142 
    143        if !context.shared.traversal_flags.for_animation_only() {
    144            let mut result = false;
    145            if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
    146                let style_attribute = self.style_attribute();
    147                result |= Self::replace_single_rule_node(
    148                    context.shared,
    149                    CascadeLevel::same_tree_author_normal(),
    150                    LayerOrder::style_attribute(),
    151                    style_attribute,
    152                    primary_rules,
    153                );
    154                result |= Self::replace_single_rule_node(
    155                    context.shared,
    156                    CascadeLevel::same_tree_author_important(),
    157                    LayerOrder::style_attribute(),
    158                    style_attribute,
    159                    primary_rules,
    160                );
    161                // FIXME(emilio): Still a hack!
    162                self.unset_dirty_style_attribute();
    163            }
    164            return result;
    165        }
    166 
    167        // Animation restyle hints are processed prior to other restyle
    168        // hints in the animation-only traversal.
    169        //
    170        // Non-animation restyle hints will be processed in a subsequent
    171        // normal traversal.
    172        if replacements.intersects(RestyleHint::for_animations()) {
    173            debug_assert!(context.shared.traversal_flags.for_animation_only());
    174 
    175            if replacements.contains(RestyleHint::RESTYLE_SMIL) {
    176                Self::replace_single_rule_node(
    177                    context.shared,
    178                    CascadeLevel::SMILOverride,
    179                    LayerOrder::root(),
    180                    self.smil_override(),
    181                    primary_rules,
    182                );
    183            }
    184 
    185            if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
    186                Self::replace_single_rule_node(
    187                    context.shared,
    188                    CascadeLevel::Transitions,
    189                    LayerOrder::root(),
    190                    self.transition_rule(&context.shared)
    191                        .as_ref()
    192                        .map(|a| a.borrow_arc()),
    193                    primary_rules,
    194                );
    195            }
    196 
    197            if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
    198                Self::replace_single_rule_node(
    199                    context.shared,
    200                    CascadeLevel::Animations,
    201                    LayerOrder::root(),
    202                    self.animation_rule(&context.shared)
    203                        .as_ref()
    204                        .map(|a| a.borrow_arc()),
    205                    primary_rules,
    206                );
    207            }
    208        }
    209 
    210        false
    211    }
    212 
    213    /// If there is no transition rule in the ComputedValues, it returns None.
    214    fn after_change_style(
    215        &self,
    216        context: &mut StyleContext<Self>,
    217        primary_style: &Arc<ComputedValues>,
    218    ) -> Option<Arc<ComputedValues>> {
    219        // Actually `PseudoElementResolution` doesn't really matter.
    220        StyleResolverForElement::new(
    221            *self,
    222            context,
    223            RuleInclusion::All,
    224            PseudoElementResolution::IfApplicable,
    225        )
    226        .after_change_style(primary_style)
    227    }
    228 
    229    fn needs_animations_update(
    230        &self,
    231        context: &mut StyleContext<Self>,
    232        old_style: Option<&ComputedValues>,
    233        new_style: &ComputedValues,
    234        pseudo_element: Option<PseudoElement>,
    235    ) -> bool {
    236        let new_ui_style = new_style.get_ui();
    237        let new_style_specifies_animations = new_ui_style.specifies_animations();
    238 
    239        let has_animations = self.has_css_animations(&context.shared, pseudo_element);
    240        if !new_style_specifies_animations && !has_animations {
    241            return false;
    242        }
    243 
    244        let old_style = match old_style {
    245            Some(old) => old,
    246            // If we have no old style but have animations, we may be a
    247            // pseudo-element which was re-created without style changes.
    248            //
    249            // This can happen when we reframe the pseudo-element without
    250            // restyling it (due to content insertion on a flex container or
    251            // such, for example). See bug 1564366.
    252            //
    253            // FIXME(emilio): The really right fix for this is keeping the
    254            // pseudo-element itself around on reframes, but that's a bit
    255            // harder. If we do that we can probably remove quite a lot of the
    256            // EffectSet complexity though, since right now it's stored on the
    257            // parent element for pseudo-elements given we need to keep it
    258            // around...
    259            None => {
    260                return new_style_specifies_animations || new_style.is_pseudo_style();
    261            },
    262        };
    263 
    264        let old_ui_style = old_style.get_ui();
    265 
    266        let keyframes_could_have_changed = context
    267            .shared
    268            .traversal_flags
    269            .contains(TraversalFlags::ForCSSRuleChanges);
    270 
    271        // If the traversal is triggered due to changes in CSS rules changes, we
    272        // need to try to update all CSS animations on the element if the
    273        // element has or will have CSS animation style regardless of whether
    274        // the animation is running or not.
    275        //
    276        // TODO: We should check which @keyframes were added/changed/deleted and
    277        // update only animations corresponding to those @keyframes.
    278        if keyframes_could_have_changed {
    279            return true;
    280        }
    281 
    282        // If the animations changed, well...
    283        if !old_ui_style.animations_equals(new_ui_style) {
    284            return true;
    285        }
    286 
    287        let old_display = old_style.clone_display();
    288        let new_display = new_style.clone_display();
    289 
    290        // If we were display: none, we may need to trigger animations.
    291        if old_display == Display::None && new_display != Display::None {
    292            return new_style_specifies_animations;
    293        }
    294 
    295        // If we are becoming display: none, we may need to stop animations.
    296        if old_display != Display::None && new_display == Display::None {
    297            return has_animations;
    298        }
    299 
    300        // We might need to update animations if writing-mode or direction
    301        // changed, and any of the animations contained logical properties.
    302        //
    303        // We may want to be more granular, but it's probably not worth it.
    304        if new_style.writing_mode != old_style.writing_mode {
    305            return has_animations;
    306        }
    307 
    308        false
    309    }
    310 
    311    fn might_need_transitions_update(
    312        &self,
    313        context: &StyleContext<Self>,
    314        old_style: Option<&ComputedValues>,
    315        new_style: &ComputedValues,
    316        pseudo_element: Option<PseudoElement>,
    317    ) -> bool {
    318        let old_style = match old_style {
    319            Some(v) => v,
    320            None => return false,
    321        };
    322 
    323        if !self.has_css_transitions(context.shared, pseudo_element)
    324            && !new_style.get_ui().specifies_transitions()
    325        {
    326            return false;
    327        }
    328 
    329        if old_style.clone_display().is_none() {
    330            return false;
    331        }
    332 
    333        return true;
    334    }
    335 
    336    #[cfg(feature = "gecko")]
    337    fn maybe_resolve_starting_style(
    338        &self,
    339        context: &mut StyleContext<Self>,
    340        old_values: Option<&Arc<ComputedValues>>,
    341        new_styles: &ResolvedElementStyles,
    342    ) -> Option<Arc<ComputedValues>> {
    343        // For both cases:
    344        // 1. If we didn't see any starting-style rules for this given element during full matching.
    345        // 2. If there is no transitions specified.
    346        // We don't have to resolve starting style.
    347        if !new_styles.may_have_starting_style()
    348            || !new_styles.primary_style().get_ui().specifies_transitions()
    349        {
    350            return None;
    351        }
    352 
    353        // We resolve starting style only if we don't have before-change-style, or we change from
    354        // display:none.
    355        if old_values.is_some()
    356            && !new_styles
    357                .primary_style()
    358                .is_display_property_changed_from_none(old_values.map(|s| &**s))
    359        {
    360            return None;
    361        }
    362 
    363        // Note: Basically, we have to remove transition rules because the starting style for an
    364        // element is the after-change style with @starting-style rules applied in addition.
    365        // However, we expect there is no transition rules for this element when calling this
    366        // function because we do this only when we don't have before-change style or we change
    367        // from display:none. In these cases, it's unlikely to have running transitions on this
    368        // element.
    369        let mut resolver = StyleResolverForElement::new(
    370            *self,
    371            context,
    372            RuleInclusion::All,
    373            PseudoElementResolution::IfApplicable,
    374        );
    375 
    376        let starting_style = resolver.resolve_starting_style().style;
    377        if starting_style.style().clone_display().is_none() {
    378            return None;
    379        }
    380 
    381        Some(starting_style.0)
    382    }
    383 
    384    /// Handle CSS Transitions. Returns None if we don't need to update transitions. And it returns
    385    /// the before-change style per CSS Transitions spec.
    386    ///
    387    /// Note: The before-change style could be the computed values of all properties on the element
    388    /// as of the previous style change event, or the starting style if we don't have the valid
    389    /// before-change style there.
    390    #[cfg(feature = "gecko")]
    391    fn process_transitions(
    392        &self,
    393        context: &mut StyleContext<Self>,
    394        old_values: Option<&Arc<ComputedValues>>,
    395        new_styles: &mut ResolvedElementStyles,
    396    ) -> Option<Arc<ComputedValues>> {
    397        let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles);
    398        let before_change_or_starting = if starting_values.is_some() {
    399            starting_values.as_ref()
    400        } else {
    401            old_values
    402        };
    403        let new_values = new_styles.primary_style_mut();
    404 
    405        if !self.might_need_transitions_update(
    406            context,
    407            before_change_or_starting.map(|s| &**s),
    408            new_values,
    409            /* pseudo_element = */ None,
    410        ) {
    411            return None;
    412        }
    413 
    414        let after_change_style =
    415            if self.has_css_transitions(context.shared, /* pseudo_element = */ None) {
    416                self.after_change_style(context, new_values)
    417            } else {
    418                None
    419            };
    420 
    421        // In order to avoid creating a SequentialTask for transitions which
    422        // may not be updated, we check it per property to make sure Gecko
    423        // side will really update transition.
    424        if !self.needs_transitions_update(
    425            before_change_or_starting.unwrap(),
    426            after_change_style.as_ref().unwrap_or(&new_values),
    427        ) {
    428            return None;
    429        }
    430 
    431        if let Some(values_without_transitions) = after_change_style {
    432            *new_values = values_without_transitions;
    433        }
    434 
    435        // Move the new-created starting style, or clone the old values.
    436        if starting_values.is_some() {
    437            starting_values
    438        } else {
    439            old_values.cloned()
    440        }
    441    }
    442 
    443    #[cfg(feature = "gecko")]
    444    fn process_animations(
    445        &self,
    446        context: &mut StyleContext<Self>,
    447        old_styles: &mut ElementStyles,
    448        new_styles: &mut ResolvedElementStyles,
    449        important_rules_changed: bool,
    450    ) {
    451        use crate::context::UpdateAnimationsTasks;
    452 
    453        let old_values = &old_styles.primary;
    454        if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {
    455            return;
    456        }
    457 
    458        // Bug 868975: These steps should examine and update the visited styles
    459        // in addition to the unvisited styles.
    460 
    461        let mut tasks = UpdateAnimationsTasks::empty();
    462 
    463        if old_values.as_deref().map_or_else(
    464            || {
    465                new_styles
    466                    .primary_style()
    467                    .get_ui()
    468                    .specifies_scroll_timelines()
    469            },
    470            |old| {
    471                !old.get_ui()
    472                    .scroll_timelines_equals(new_styles.primary_style().get_ui())
    473            },
    474        ) {
    475            tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
    476        }
    477 
    478        if old_values.as_deref().map_or_else(
    479            || {
    480                new_styles
    481                    .primary_style()
    482                    .get_ui()
    483                    .specifies_view_timelines()
    484            },
    485            |old| {
    486                !old.get_ui()
    487                    .view_timelines_equals(new_styles.primary_style().get_ui())
    488            },
    489        ) {
    490            tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
    491        }
    492 
    493        if self.needs_animations_update(
    494            context,
    495            old_values.as_deref(),
    496            new_styles.primary_style(),
    497            /* pseudo_element = */ None,
    498        ) {
    499            tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
    500        }
    501 
    502        let before_change_style =
    503            self.process_transitions(context, old_values.as_ref(), new_styles);
    504        if before_change_style.is_some() {
    505            tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
    506        }
    507 
    508        if self.has_animations(&context.shared) {
    509            tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
    510            if important_rules_changed {
    511                tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
    512            }
    513            if new_styles
    514                .primary_style()
    515                .is_display_property_changed_from_none(old_values.as_deref())
    516            {
    517                tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
    518            }
    519        }
    520 
    521        if !tasks.is_empty() {
    522            let task = crate::context::SequentialTask::update_animations(
    523                *self,
    524                before_change_style,
    525                tasks,
    526            );
    527            context.thread_local.tasks.push(task);
    528        }
    529    }
    530 
    531    #[cfg(feature = "servo")]
    532    fn process_animations(
    533        &self,
    534        context: &mut StyleContext<Self>,
    535        old_styles: &mut ElementStyles,
    536        new_resolved_styles: &mut ResolvedElementStyles,
    537        _important_rules_changed: bool,
    538    ) {
    539        use crate::animation::AnimationSetKey;
    540        use crate::dom::TDocument;
    541 
    542        let style_changed = self.process_animations_for_style(
    543            context,
    544            &mut old_styles.primary,
    545            new_resolved_styles.primary_style_mut(),
    546            /* pseudo_element = */ None,
    547        );
    548 
    549        // If we have modified animation or transitions, we recascade style for this node.
    550        if style_changed {
    551            let primary_style = new_resolved_styles.primary_style();
    552            let mut rule_node = primary_style.rules().clone();
    553            let declarations = context.shared.animations.get_all_declarations(
    554                &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
    555                context.shared.current_time_for_animations,
    556                self.as_node().owner_doc().shared_lock(),
    557            );
    558            Self::replace_single_rule_node(
    559                &context.shared,
    560                CascadeLevel::Transitions,
    561                LayerOrder::root(),
    562                declarations.transitions.as_ref().map(|a| a.borrow_arc()),
    563                &mut rule_node,
    564            );
    565            Self::replace_single_rule_node(
    566                &context.shared,
    567                CascadeLevel::Animations,
    568                LayerOrder::root(),
    569                declarations.animations.as_ref().map(|a| a.borrow_arc()),
    570                &mut rule_node,
    571            );
    572 
    573            if rule_node != *primary_style.rules() {
    574                let inputs = CascadeInputs {
    575                    rules: Some(rule_node),
    576                    visited_rules: primary_style.visited_rules().cloned(),
    577                    flags: primary_style.flags.for_cascade_inputs(),
    578                };
    579 
    580                new_resolved_styles.primary.style = StyleResolverForElement::new(
    581                    *self,
    582                    context,
    583                    RuleInclusion::All,
    584                    PseudoElementResolution::IfApplicable,
    585                )
    586                .cascade_style_and_visited_with_default_parents(inputs);
    587            }
    588        }
    589 
    590        self.process_animations_for_pseudo(
    591            context,
    592            old_styles,
    593            new_resolved_styles,
    594            PseudoElement::Before,
    595        );
    596        self.process_animations_for_pseudo(
    597            context,
    598            old_styles,
    599            new_resolved_styles,
    600            PseudoElement::After,
    601        );
    602    }
    603 
    604    #[cfg(feature = "servo")]
    605    fn process_animations_for_pseudo(
    606        &self,
    607        context: &mut StyleContext<Self>,
    608        old_styles: &ElementStyles,
    609        new_resolved_styles: &mut ResolvedElementStyles,
    610        pseudo_element: PseudoElement,
    611    ) {
    612        use crate::animation::AnimationSetKey;
    613        use crate::dom::TDocument;
    614 
    615        let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
    616        let style = match new_resolved_styles.pseudos.get(&pseudo_element) {
    617            Some(style) => Arc::clone(style),
    618            None => {
    619                context
    620                    .shared
    621                    .animations
    622                    .cancel_all_animations_for_key(&key);
    623                return;
    624            },
    625        };
    626 
    627        let old_style = old_styles.pseudos.get(&pseudo_element).cloned();
    628        self.process_animations_for_style(
    629            context,
    630            &old_style,
    631            &style,
    632            Some(pseudo_element.clone()),
    633        );
    634 
    635        let declarations = context.shared.animations.get_all_declarations(
    636            &key,
    637            context.shared.current_time_for_animations,
    638            self.as_node().owner_doc().shared_lock(),
    639        );
    640        if declarations.is_empty() {
    641            return;
    642        }
    643 
    644        let mut rule_node = style.rules().clone();
    645        Self::replace_single_rule_node(
    646            &context.shared,
    647            CascadeLevel::Transitions,
    648            LayerOrder::root(),
    649            declarations.transitions.as_ref().map(|a| a.borrow_arc()),
    650            &mut rule_node,
    651        );
    652        Self::replace_single_rule_node(
    653            &context.shared,
    654            CascadeLevel::Animations,
    655            LayerOrder::root(),
    656            declarations.animations.as_ref().map(|a| a.borrow_arc()),
    657            &mut rule_node,
    658        );
    659        if rule_node == *style.rules() {
    660            return;
    661        }
    662 
    663        let inputs = CascadeInputs {
    664            rules: Some(rule_node),
    665            visited_rules: style.visited_rules().cloned(),
    666            flags: style.flags.for_cascade_inputs(),
    667        };
    668 
    669        let new_style = StyleResolverForElement::new(
    670            *self,
    671            context,
    672            RuleInclusion::All,
    673            PseudoElementResolution::IfApplicable,
    674        )
    675        .cascade_style_and_visited_for_pseudo_with_default_parents(
    676            inputs,
    677            &pseudo_element,
    678            &new_resolved_styles.primary,
    679        );
    680 
    681        new_resolved_styles
    682            .pseudos
    683            .set(&pseudo_element, new_style.0);
    684    }
    685 
    686    #[cfg(feature = "servo")]
    687    fn process_animations_for_style(
    688        &self,
    689        context: &mut StyleContext<Self>,
    690        old_values: &Option<Arc<ComputedValues>>,
    691        new_values: &Arc<ComputedValues>,
    692        pseudo_element: Option<PseudoElement>,
    693    ) -> bool {
    694        use crate::animation::{AnimationSetKey, AnimationState};
    695 
    696        // We need to call this before accessing the `ElementAnimationSet` from the
    697        // map because this call will do a RwLock::read().
    698        let needs_animations_update = self.needs_animations_update(
    699            context,
    700            old_values.as_deref(),
    701            new_values,
    702            pseudo_element,
    703        );
    704 
    705        let might_need_transitions_update = self.might_need_transitions_update(
    706            context,
    707            old_values.as_deref(),
    708            new_values,
    709            pseudo_element,
    710        );
    711 
    712        let mut after_change_style = None;
    713        if might_need_transitions_update {
    714            after_change_style = self.after_change_style(context, new_values);
    715        }
    716 
    717        let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
    718        let shared_context = context.shared;
    719        let mut animation_set = shared_context
    720            .animations
    721            .sets
    722            .write()
    723            .remove(&key)
    724            .unwrap_or_default();
    725 
    726        // Starting animations is expensive, because we have to recalculate the style
    727        // for all the keyframes. We only want to do this if we think that there's a
    728        // chance that the animations really changed.
    729        if needs_animations_update {
    730            let mut resolver = StyleResolverForElement::new(
    731                *self,
    732                context,
    733                RuleInclusion::All,
    734                PseudoElementResolution::IfApplicable,
    735            );
    736 
    737            animation_set.update_animations_for_new_style::<Self>(
    738                *self,
    739                &shared_context,
    740                &new_values,
    741                &mut resolver,
    742            );
    743        }
    744 
    745        animation_set.update_transitions_for_new_style(
    746            might_need_transitions_update,
    747            &shared_context,
    748            old_values.as_ref(),
    749            after_change_style.as_ref().unwrap_or(new_values),
    750        );
    751 
    752        // This should change the computed values in the style, so we don't need
    753        // to mark this set as dirty.
    754        animation_set
    755            .transitions
    756            .retain(|transition| transition.state != AnimationState::Finished);
    757 
    758        animation_set
    759            .animations
    760            .retain(|animation| animation.state != AnimationState::Finished);
    761 
    762        // If the ElementAnimationSet is empty, and don't store it in order to
    763        // save memory and to avoid extra processing later.
    764        let changed_animations = animation_set.dirty;
    765        if !animation_set.is_empty() {
    766            animation_set.dirty = false;
    767            shared_context
    768                .animations
    769                .sets
    770                .write()
    771                .insert(key, animation_set);
    772        }
    773 
    774        changed_animations
    775    }
    776 
    777    /// Computes and applies non-redundant damage.
    778    fn accumulate_damage_for(
    779        &self,
    780        shared_context: &SharedStyleContext,
    781        damage: &mut RestyleDamage,
    782        old_values: &ComputedValues,
    783        new_values: &ComputedValues,
    784        pseudo: Option<&PseudoElement>,
    785    ) -> ChildRestyleRequirement {
    786        debug!("accumulate_damage_for: {:?}", self);
    787        debug_assert!(!shared_context
    788            .traversal_flags
    789            .contains(TraversalFlags::FinalAnimationTraversal));
    790 
    791        let difference = self.compute_style_difference(old_values, new_values, pseudo);
    792 
    793        *damage |= difference.damage;
    794 
    795        debug!(" > style difference: {:?}", difference);
    796 
    797        // We need to cascade the children in order to ensure the correct
    798        // propagation of inherited computed value flags.
    799        if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
    800            debug!(
    801                " > flags changed: {:?} != {:?}",
    802                old_values.flags, new_values.flags
    803            );
    804            return ChildRestyleRequirement::MustCascadeChildren;
    805        }
    806 
    807        if old_values.effective_zoom != new_values.effective_zoom {
    808            // Zoom changes need to get propagated to children.
    809            debug!(
    810                " > zoom changed: {:?} != {:?}",
    811                old_values.effective_zoom, new_values.effective_zoom
    812            );
    813            return ChildRestyleRequirement::MustCascadeChildren;
    814        }
    815 
    816        match difference.change {
    817            StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade,
    818            StyleChange::Changed { reset_only } => {
    819                // If inherited properties changed, the best we can do is
    820                // cascade the children.
    821                if !reset_only {
    822                    return ChildRestyleRequirement::MustCascadeChildren;
    823                }
    824            },
    825        }
    826 
    827        let old_display = old_values.clone_display();
    828        let new_display = new_values.clone_display();
    829 
    830        if old_display != new_display {
    831            // If we used to be a display: none element, and no longer are, our
    832            // children need to be restyled because they're unstyled.
    833            if old_display == Display::None {
    834                return ChildRestyleRequirement::MustCascadeChildren;
    835            }
    836            // Blockification of children may depend on our display value,
    837            // so we need to actually do the recascade. We could potentially
    838            // do better, but it doesn't seem worth it.
    839            if old_display.is_item_container() != new_display.is_item_container() {
    840                return ChildRestyleRequirement::MustCascadeChildren;
    841            }
    842            // We may also need to blockify and un-blockify descendants if our
    843            // display goes from / to display: contents, since the "layout
    844            // parent style" changes.
    845            if old_display.is_contents() || new_display.is_contents() {
    846                return ChildRestyleRequirement::MustCascadeChildren;
    847            }
    848            // Line break suppression may also be affected if the display
    849            // type changes from ruby to non-ruby.
    850            #[cfg(feature = "gecko")]
    851            {
    852                if old_display.is_ruby_type() != new_display.is_ruby_type() {
    853                    return ChildRestyleRequirement::MustCascadeChildren;
    854                }
    855            }
    856        }
    857 
    858        // Children with justify-items: auto may depend on our
    859        // justify-items property value.
    860        //
    861        // Similarly, we could potentially do better, but this really
    862        // seems not common enough to care about.
    863        #[cfg(feature = "gecko")]
    864        {
    865            use crate::values::specified::align::AlignFlags;
    866 
    867            let old_justify_items = old_values.get_position().clone_justify_items();
    868            let new_justify_items = new_values.get_position().clone_justify_items();
    869 
    870            let was_legacy_justify_items = old_justify_items.computed.contains(AlignFlags::LEGACY);
    871 
    872            let is_legacy_justify_items = new_justify_items.computed.contains(AlignFlags::LEGACY);
    873 
    874            if is_legacy_justify_items != was_legacy_justify_items {
    875                return ChildRestyleRequirement::MustCascadeChildren;
    876            }
    877 
    878            if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed
    879            {
    880                return ChildRestyleRequirement::MustCascadeChildren;
    881            }
    882        }
    883 
    884        #[cfg(feature = "servo")]
    885        {
    886            // We may need to set or propagate the CAN_BE_FRAGMENTED bit
    887            // on our children.
    888            if old_values.is_multicol() != new_values.is_multicol() {
    889                return ChildRestyleRequirement::MustCascadeChildren;
    890            }
    891        }
    892 
    893        // We could prove that, if our children don't inherit reset
    894        // properties, we can stop the cascade.
    895        ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle
    896    }
    897 }
    898 
    899 impl<E: TElement> PrivateMatchMethods for E {}
    900 
    901 /// The public API that elements expose for selector matching.
    902 pub trait MatchMethods: TElement {
    903    /// Returns the closest parent element that doesn't have a display: contents
    904    /// style (and thus generates a box).
    905    ///
    906    /// This is needed to correctly handle blockification of flex and grid
    907    /// items.
    908    ///
    909    /// Returns itself if the element has no parent. In practice this doesn't
    910    /// happen because the root element is blockified per spec, but it could
    911    /// happen if we decide to not blockify for roots of disconnected subtrees,
    912    /// which is a kind of dubious behavior.
    913    fn layout_parent(&self) -> Self {
    914        let mut current = self.clone();
    915        loop {
    916            current = match current.traversal_parent() {
    917                Some(el) => el,
    918                None => return current,
    919            };
    920 
    921            let is_display_contents = current
    922                .borrow_data()
    923                .unwrap()
    924                .styles
    925                .primary()
    926                .is_display_contents();
    927 
    928            if !is_display_contents {
    929                return current;
    930            }
    931        }
    932    }
    933 
    934    /// Updates the styles with the new ones, diffs them, and stores the restyle
    935    /// damage.
    936    fn finish_restyle(
    937        &self,
    938        context: &mut StyleContext<Self>,
    939        data: &mut ElementData,
    940        mut new_styles: ResolvedElementStyles,
    941        important_rules_changed: bool,
    942    ) -> ChildRestyleRequirement {
    943        use std::cmp;
    944 
    945        self.process_animations(
    946            context,
    947            &mut data.styles,
    948            &mut new_styles,
    949            important_rules_changed,
    950        );
    951 
    952        // First of all, update the styles.
    953        let old_styles = data.set_styles(new_styles);
    954 
    955        let new_primary_style = data.styles.primary.as_ref().unwrap();
    956 
    957        let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
    958        let is_root = new_primary_style
    959            .flags
    960            .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
    961        let is_container = !new_primary_style
    962            .get_box()
    963            .clone_container_type()
    964            .is_normal();
    965        if is_root || is_container {
    966            let device = context.shared.stylist.device();
    967            let old_style = old_styles.primary.as_ref();
    968            let new_font_size = new_primary_style.get_font().clone_font_size();
    969            let old_font_size = old_style.map(|s| s.get_font().clone_font_size());
    970 
    971            // For line-height, we want the fully resolved value, as `normal` also depends on other
    972            // font properties.
    973            let new_line_height = device
    974                .calc_line_height(
    975                    &new_primary_style.get_font(),
    976                    new_primary_style.writing_mode,
    977                    None,
    978                )
    979                .0;
    980            let old_line_height = old_style.map(|s| {
    981                device
    982                    .calc_line_height(&s.get_font(), s.writing_mode, None)
    983                    .0
    984            });
    985 
    986            // Update root font-relative units. If any of these unit values changed
    987            // since last time, ensure that we recascade the entire tree.
    988            if is_root {
    989                debug_assert!(self.owner_doc_matches_for_testing(device));
    990                device.set_root_style(new_primary_style);
    991 
    992                // Update root font size for rem units
    993                if old_font_size != Some(new_font_size) {
    994                    let size = new_font_size.computed_size();
    995                    device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
    996                    if device.used_root_font_size() {
    997                        restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
    998                    }
    999                }
   1000 
   1001                // Update root line height for rlh units
   1002                if old_line_height != Some(new_line_height) {
   1003                    device.set_root_line_height(
   1004                        new_primary_style
   1005                            .effective_zoom
   1006                            .unzoom(new_line_height.px()),
   1007                    );
   1008                    if device.used_root_line_height() {
   1009                        restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
   1010                    }
   1011                }
   1012 
   1013                // Update root font metrics for rcap, rch, rex, ric units. Since querying
   1014                // font metrics can be an expensive call, they are only updated if these
   1015                // units are used in the document.
   1016                if device.used_root_font_metrics() && device.update_root_font_metrics() {
   1017                    restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
   1018                }
   1019            }
   1020 
   1021            if is_container
   1022                && (old_font_size.is_some_and(|old| old != new_font_size)
   1023                    || old_line_height.is_some_and(|old| old != new_line_height))
   1024            {
   1025                // TODO(emilio): Maybe only do this if we were matched
   1026                // against relative font sizes?
   1027                // Also, maybe we should do this as well for font-family /
   1028                // etc changes (for ex/ch/ic units to work correctly)? We
   1029                // should probably do the optimization mentioned above if
   1030                // so.
   1031                restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
   1032            }
   1033        }
   1034 
   1035        if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
   1036            if self.is_html_document_body_element() {
   1037                // NOTE(emilio): We _could_ handle dynamic changes to it if it
   1038                // changes and before we reach our children the cascade stops,
   1039                // but we don't track right now whether we use the document body
   1040                // color, and nobody else handles that properly anyway.
   1041                let device = context.shared.stylist.device();
   1042 
   1043                // Needed for the "inherit from body" quirk.
   1044                let text_color = new_primary_style.get_inherited_text().clone_color();
   1045                device.set_body_text_color(text_color);
   1046            }
   1047        }
   1048 
   1049        // Don't accumulate damage if we're in the final animation traversal.
   1050        if context
   1051            .shared
   1052            .traversal_flags
   1053            .contains(TraversalFlags::FinalAnimationTraversal)
   1054        {
   1055            return ChildRestyleRequirement::MustCascadeChildren;
   1056        }
   1057 
   1058        // Also, don't do anything if there was no style.
   1059        let old_primary_style = match old_styles.primary {
   1060            Some(s) => s,
   1061            None => return ChildRestyleRequirement::MustCascadeChildren,
   1062        };
   1063 
   1064        let old_container_type = old_primary_style.clone_container_type();
   1065        let new_container_type = new_primary_style.clone_container_type();
   1066        if old_container_type != new_container_type && !new_container_type.is_size_container_type()
   1067        {
   1068            // Stopped being a size container. Re-evaluate container queries and units on all our descendants.
   1069            // Changes into and between different size containment is handled in `UpdateContainerQueryStyles`.
   1070            restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
   1071        } else if old_container_type.is_size_container_type()
   1072            && !old_primary_style.is_display_contents()
   1073            && new_primary_style.is_display_contents()
   1074        {
   1075            // Also re-evaluate when a container gets 'display: contents', since size queries will now evaluate to unknown.
   1076            // Other displays like 'inline' will keep generating a box, so they are handled in `UpdateContainerQueryStyles`.
   1077            restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
   1078        }
   1079 
   1080        restyle_requirement = cmp::max(
   1081            restyle_requirement,
   1082            self.accumulate_damage_for(
   1083                context.shared,
   1084                &mut data.damage,
   1085                &old_primary_style,
   1086                new_primary_style,
   1087                None,
   1088            ),
   1089        );
   1090 
   1091        if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
   1092            // This is the common case; no need to examine pseudos here.
   1093            return restyle_requirement;
   1094        }
   1095 
   1096        let pseudo_styles = old_styles
   1097            .pseudos
   1098            .as_array()
   1099            .iter()
   1100            .zip(data.styles.pseudos.as_array().iter());
   1101 
   1102        for (i, (old, new)) in pseudo_styles.enumerate() {
   1103            match (old, new) {
   1104                (&Some(ref old), &Some(ref new)) => {
   1105                    self.accumulate_damage_for(
   1106                        context.shared,
   1107                        &mut data.damage,
   1108                        old,
   1109                        new,
   1110                        Some(&PseudoElement::from_eager_index(i)),
   1111                    );
   1112                },
   1113                (&None, &None) => {},
   1114                _ => {
   1115                    // It's possible that we're switching from not having
   1116                    // ::before/::after at all to having styles for them but not
   1117                    // actually having a useful pseudo-element.  Check for that
   1118                    // case.
   1119                    let pseudo = PseudoElement::from_eager_index(i);
   1120                    let new_pseudo_should_exist =
   1121                        new.as_ref().map_or(false, |s| pseudo.should_exist(s));
   1122                    let old_pseudo_should_exist =
   1123                        old.as_ref().map_or(false, |s| pseudo.should_exist(s));
   1124                    if new_pseudo_should_exist != old_pseudo_should_exist {
   1125                        data.damage |= RestyleDamage::reconstruct();
   1126                        return restyle_requirement;
   1127                    }
   1128                },
   1129            }
   1130        }
   1131 
   1132        restyle_requirement
   1133    }
   1134 
   1135    /// Updates the rule nodes without re-running selector matching, using just
   1136    /// the rule tree.
   1137    ///
   1138    /// Returns true if an !important rule was replaced.
   1139    fn replace_rules(
   1140        &self,
   1141        replacements: RestyleHint,
   1142        context: &mut StyleContext<Self>,
   1143        cascade_inputs: &mut ElementCascadeInputs,
   1144    ) -> bool {
   1145        let mut result = false;
   1146        result |= self.replace_rules_internal(
   1147            replacements,
   1148            context,
   1149            CascadeVisitedMode::Unvisited,
   1150            cascade_inputs,
   1151        );
   1152        result |= self.replace_rules_internal(
   1153            replacements,
   1154            context,
   1155            CascadeVisitedMode::Visited,
   1156            cascade_inputs,
   1157        );
   1158        result
   1159    }
   1160 
   1161    /// Given the old and new style of this element, and whether it's a
   1162    /// pseudo-element, compute the restyle damage used to determine which
   1163    /// kind of layout or painting operations we'll need.
   1164    fn compute_style_difference(
   1165        &self,
   1166        old_values: &ComputedValues,
   1167        new_values: &ComputedValues,
   1168        pseudo: Option<&PseudoElement>,
   1169    ) -> StyleDifference {
   1170        debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
   1171        #[cfg(feature = "gecko")]
   1172        {
   1173            RestyleDamage::compute_style_difference(old_values, new_values)
   1174        }
   1175        #[cfg(feature = "servo")]
   1176        {
   1177            RestyleDamage::compute_style_difference::<Self>(old_values, new_values)
   1178        }
   1179    }
   1180 }
   1181 
   1182 impl<E: TElement> MatchMethods for E {}