Skip to main content

style/invalidation/element/
restyle_hints.rs

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//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
6
7use crate::traversal_flags::TraversalFlags;
8
9bitflags! {
10    /// The kind of restyle we need to do for a given element.
11    #[repr(C)]
12    #[derive(Clone, Copy, Debug)]
13    pub struct RestyleHint: u16 {
14        /// Do a selector match of the element.
15        const RESTYLE_SELF = 1 << 0;
16
17        /// Do a selector match of the element's pseudo-elements. Always to be combined with
18        /// RESTYLE_SELF.
19        const RESTYLE_PSEUDOS = 1 << 1;
20
21        /// Do a selector match if the element is a pseudo-element.
22        const RESTYLE_SELF_IF_PSEUDO = 1 << 2;
23
24        /// Do a selector match of the element's descendants.
25        const RESTYLE_DESCENDANTS = 1 << 3;
26
27        /// Recascade the current element.
28        const RECASCADE_SELF = 1 << 4;
29
30        /// Recascade the current element if it inherits any reset style.
31        const RECASCADE_SELF_IF_INHERIT_RESET_STYLE = 1 << 5;
32
33        /// Recascade all descendant elements.
34        const RECASCADE_DESCENDANTS = 1 << 6;
35
36        /// Replace the style data coming from CSS transitions without updating
37        /// any other style data. This hint is only processed in animation-only
38        /// traversal which is prior to normal traversal.
39        const RESTYLE_CSS_TRANSITIONS = 1 << 7;
40
41        /// Replace the style data coming from CSS animations without updating
42        /// any other style data. This hint is only processed in animation-only
43        /// traversal which is prior to normal traversal.
44        const RESTYLE_CSS_ANIMATIONS = 1 << 8;
45
46        /// Don't re-run selector-matching on the element, only the style
47        /// attribute has changed, and this change didn't have any other
48        /// dependencies.
49        const RESTYLE_STYLE_ATTRIBUTE = 1 << 9;
50
51        /// Replace the style data coming from SMIL animations without updating
52        /// any other style data. This hint is only processed in animation-only
53        /// traversal which is prior to normal traversal.
54        const RESTYLE_SMIL = 1 << 10;
55
56        /// Match self if this element is dependent on a style query.
57        const RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES = 1 << 11;
58
59        /// Match self or descendants if dependent on a named style query.
60        const RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER = 1 << 12;
61
62        /// Do a selector match of the element if it depends on an ancestor's font
63        /// or this element's writing mode. Used to handle the invalidation cases
64        /// for style container queries in which relative font metrics change or
65        /// when a writing-mode-dependant unit needs to be updated for a style
66        /// container query.
67        const RESTYLE_IF_AFFECTED_BY_WM_OR_ANCESTOR_FONT = 1 << 13;
68
69        /// We don't need to match this element but do a selector match of it's
70        /// children if they are affected by style container queries.
71        const RESTYLE_CHILD_IF_AFFECTED_BY_STYLE_QUERIES = 1 <<14;
72    }
73}
74
75impl RestyleHint {
76    /// Creates a new `RestyleHint` indicating that the current element and all
77    /// its descendants must be fully restyled.
78    #[inline]
79    pub fn restyle_subtree() -> Self {
80        RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS
81    }
82
83    /// Creates a new `RestyleHint` indicating that the current element and all
84    /// its descendants must be recascaded.
85    #[inline]
86    pub fn recascade_subtree() -> Self {
87        RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS
88    }
89
90    /// Returns whether this hint invalidates the element and all its
91    /// descendants.
92    #[inline]
93    pub fn contains_subtree(&self) -> bool {
94        self.contains(Self::restyle_subtree())
95    }
96
97    /// Returns whether we'll recascade all of the descendants.
98    #[inline]
99    pub fn will_recascade_subtree(&self) -> bool {
100        self.contains_subtree() || self.contains(Self::recascade_subtree())
101    }
102
103    /// Returns whether we need to restyle this element.
104    pub fn has_non_animation_invalidations(&self) -> bool {
105        !(*self & !Self::for_animations()).is_empty()
106    }
107
108    /// Propagates this restyle hint to a child element.
109    pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
110        // In the middle of an animation only restyle, we don't need to
111        // propagate any restyle hints, and we need to remove ourselves.
112        if traversal_flags.for_animation_only() {
113            self.remove_animation_hints();
114            return Self::empty();
115        }
116
117        debug_assert!(
118            !self.has_animation_hint(),
119            "There should not be any animation restyle hints \
120             during normal traversal"
121        );
122
123        // Else we should clear ourselves, and return the propagated hint.
124        std::mem::take(self).propagate_for_non_animation_restyle()
125    }
126
127    /// Returns a new `RestyleHint` appropriate for children of the current element.
128    fn propagate_for_non_animation_restyle(&self) -> Self {
129        if self.contains(RestyleHint::RESTYLE_DESCENDANTS) {
130            return Self::restyle_subtree();
131        }
132        let mut result = Self::empty();
133        if self.contains(Self::RESTYLE_PSEUDOS) {
134            result |= Self::RESTYLE_SELF_IF_PSEUDO;
135        }
136        if self.contains(Self::RECASCADE_DESCENDANTS) {
137            result |= Self::recascade_subtree();
138        }
139        if self.contains(Self::RESTYLE_IF_AFFECTED_BY_WM_OR_ANCESTOR_FONT) {
140            result |= Self::RESTYLE_IF_AFFECTED_BY_WM_OR_ANCESTOR_FONT;
141        }
142        if self.contains(Self::RESTYLE_CHILD_IF_AFFECTED_BY_STYLE_QUERIES) {
143            result |= Self::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES;
144        }
145        if self.contains(Self::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER) {
146            // We may need to restyle further down the tree if rules are
147            // declared for a named container.
148            // e.g @container my-name {#b {...}}
149            // and <div id=a> <div> <div id=b> </div> </div> </div>
150            // If a toggles `container-name: my-name` the rules for #b
151            // also invalidate. This is why we need one hint for unnamed
152            // container and one for named containers.
153            result |= Self::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER;
154        }
155        result
156    }
157
158    /// Returns a hint that contains all the replacement hints.
159    pub fn replacements() -> Self {
160        RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
161    }
162
163    /// The replacements for the animation cascade levels.
164    #[inline]
165    pub fn for_animations() -> Self {
166        RestyleHint::RESTYLE_SMIL
167            | RestyleHint::RESTYLE_CSS_ANIMATIONS
168            | RestyleHint::RESTYLE_CSS_TRANSITIONS
169    }
170
171    /// Returns whether the hint specifies that an animation cascade level must
172    /// be replaced.
173    #[inline]
174    pub fn has_animation_hint(&self) -> bool {
175        self.intersects(Self::for_animations())
176    }
177
178    /// Returns whether the hint specifies that an animation cascade level must
179    /// be replaced.
180    #[inline]
181    pub fn has_animation_hint_or_recascade(&self) -> bool {
182        self.intersects(
183            Self::for_animations()
184                | Self::RECASCADE_SELF
185                | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE,
186        )
187    }
188
189    /// Returns whether the hint specifies some restyle work other than an
190    /// animation cascade level replacement.
191    #[inline]
192    pub fn has_non_animation_hint(&self) -> bool {
193        !(*self & !Self::for_animations()).is_empty()
194    }
195
196    /// Returns whether the hint specifies that some cascade levels must be
197    /// replaced.
198    #[inline]
199    pub fn has_replacements(&self) -> bool {
200        self.intersects(Self::replacements())
201    }
202
203    /// Removes all of the animation-related hints.
204    #[inline]
205    pub fn remove_animation_hints(&mut self) {
206        self.remove(Self::for_animations());
207
208        // While RECASCADE_SELF is not animation-specific, we only ever add and process it during
209        // traversal.  If we are here, removing animation hints, then we are in an animation-only
210        // traversal, and we know that any RECASCADE_SELF flag must have been set due to changes in
211        // inherited values after restyling for animations, and thus we want to remove it so that
212        // we don't later try to restyle the element during a normal restyle.
213        // (We could have separate RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to
214        // make it clear, but this isn't currently necessary.)
215        self.remove(Self::RECASCADE_SELF | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE);
216    }
217}
218
219impl Default for RestyleHint {
220    fn default() -> Self {
221        Self::empty()
222    }
223}
224
225#[cfg(feature = "servo")]
226malloc_size_of::malloc_size_of_is_0!(RestyleHint);