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 
7 use crate::traversal_flags::TraversalFlags;
8 
9 bitflags! {
10     /// The kind of restyle we need to do for a given element.
11     #[repr(C)]
12     pub struct RestyleHint: u8 {
13         /// Do a selector match of the element.
14         const RESTYLE_SELF = 1 << 0;
15 
16         /// Do a selector match of the element's descendants.
17         const RESTYLE_DESCENDANTS = 1 << 1;
18 
19         /// Recascade the current element.
20         const RECASCADE_SELF = 1 << 2;
21 
22         /// Recascade all descendant elements.
23         const RECASCADE_DESCENDANTS = 1 << 3;
24 
25         /// Replace the style data coming from CSS transitions without updating
26         /// any other style data. This hint is only processed in animation-only
27         /// traversal which is prior to normal traversal.
28         const RESTYLE_CSS_TRANSITIONS = 1 << 4;
29 
30         /// Replace the style data coming from CSS animations without updating
31         /// any other style data. This hint is only processed in animation-only
32         /// traversal which is prior to normal traversal.
33         const RESTYLE_CSS_ANIMATIONS = 1 << 5;
34 
35         /// Don't re-run selector-matching on the element, only the style
36         /// attribute has changed, and this change didn't have any other
37         /// dependencies.
38         const RESTYLE_STYLE_ATTRIBUTE = 1 << 6;
39 
40         /// Replace the style data coming from SMIL animations without updating
41         /// any other style data. This hint is only processed in animation-only
42         /// traversal which is prior to normal traversal.
43         const RESTYLE_SMIL = 1 << 7;
44     }
45 }
46 
47 impl RestyleHint {
48     /// Creates a new `RestyleHint` indicating that the current element and all
49     /// its descendants must be fully restyled.
restyle_subtree() -> Self50     pub fn restyle_subtree() -> Self {
51         RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS
52     }
53 
54     /// Creates a new `RestyleHint` indicating that the current element and all
55     /// its descendants must be recascaded.
recascade_subtree() -> Self56     pub fn recascade_subtree() -> Self {
57         RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS
58     }
59 
60     /// Returns whether this hint invalidates the element and all its
61     /// descendants.
contains_subtree(&self) -> bool62     pub fn contains_subtree(&self) -> bool {
63         self.contains(Self::restyle_subtree())
64     }
65 
66     /// Returns whether we'll recascade all of the descendants.
will_recascade_subtree(&self) -> bool67     pub fn will_recascade_subtree(&self) -> bool {
68         self.contains_subtree() || self.contains(Self::recascade_subtree())
69     }
70 
71     /// Returns whether we need to restyle this element.
has_non_animation_invalidations(&self) -> bool72     pub fn has_non_animation_invalidations(&self) -> bool {
73         self.intersects(
74             RestyleHint::RESTYLE_SELF |
75                 RestyleHint::RECASCADE_SELF |
76                 (Self::replacements() & !Self::for_animations()),
77         )
78     }
79 
80     /// Propagates this restyle hint to a child element.
propagate(&mut self, traversal_flags: &TraversalFlags) -> Self81     pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
82         use std::mem;
83 
84         // In the middle of an animation only restyle, we don't need to
85         // propagate any restyle hints, and we need to remove ourselves.
86         if traversal_flags.for_animation_only() {
87             self.remove_animation_hints();
88             return Self::empty();
89         }
90 
91         debug_assert!(
92             !self.has_animation_hint(),
93             "There should not be any animation restyle hints \
94              during normal traversal"
95         );
96 
97         // Else we should clear ourselves, and return the propagated hint.
98         mem::replace(self, Self::empty()).propagate_for_non_animation_restyle()
99     }
100 
101     /// Returns a new `CascadeHint` appropriate for children of the current
102     /// element.
propagate_for_non_animation_restyle(&self) -> Self103     fn propagate_for_non_animation_restyle(&self) -> Self {
104         if self.contains(RestyleHint::RESTYLE_DESCENDANTS) {
105             return Self::restyle_subtree();
106         }
107         if self.contains(RestyleHint::RECASCADE_DESCENDANTS) {
108             return Self::recascade_subtree();
109         }
110         Self::empty()
111     }
112 
113     /// Returns a hint that contains all the replacement hints.
replacements() -> Self114     pub fn replacements() -> Self {
115         RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
116     }
117 
118     /// The replacements for the animation cascade levels.
119     #[inline]
for_animations() -> Self120     pub fn for_animations() -> Self {
121         RestyleHint::RESTYLE_SMIL |
122             RestyleHint::RESTYLE_CSS_ANIMATIONS |
123             RestyleHint::RESTYLE_CSS_TRANSITIONS
124     }
125 
126     /// Returns whether the hint specifies that the currently element must be
127     /// recascaded.
has_recascade_self(&self) -> bool128     pub fn has_recascade_self(&self) -> bool {
129         self.contains(RestyleHint::RECASCADE_SELF)
130     }
131 
132     /// Returns whether the hint specifies that an animation cascade level must
133     /// be replaced.
134     #[inline]
has_animation_hint(&self) -> bool135     pub fn has_animation_hint(&self) -> bool {
136         self.intersects(Self::for_animations())
137     }
138 
139     /// Returns whether the hint specifies that an animation cascade level must
140     /// be replaced.
141     #[inline]
has_animation_hint_or_recascade(&self) -> bool142     pub fn has_animation_hint_or_recascade(&self) -> bool {
143         self.intersects(Self::for_animations() | RestyleHint::RECASCADE_SELF)
144     }
145 
146     /// Returns whether the hint specifies some restyle work other than an
147     /// animation cascade level replacement.
148     #[inline]
has_non_animation_hint(&self) -> bool149     pub fn has_non_animation_hint(&self) -> bool {
150         !(*self & !Self::for_animations()).is_empty()
151     }
152 
153     /// Returns whether the hint specifies that selector matching must be re-run
154     /// for the element.
155     #[inline]
match_self(&self) -> bool156     pub fn match_self(&self) -> bool {
157         self.intersects(RestyleHint::RESTYLE_SELF)
158     }
159 
160     /// Returns whether the hint specifies that some cascade levels must be
161     /// replaced.
162     #[inline]
has_replacements(&self) -> bool163     pub fn has_replacements(&self) -> bool {
164         self.intersects(Self::replacements())
165     }
166 
167     /// Removes all of the animation-related hints.
168     #[inline]
remove_animation_hints(&mut self)169     pub fn remove_animation_hints(&mut self) {
170         self.remove(Self::for_animations());
171 
172         // While RECASCADE_SELF is not animation-specific, we only ever add and
173         // process it during traversal.  If we are here, removing animation
174         // hints, then we are in an animation-only traversal, and we know that
175         // any RECASCADE_SELF flag must have been set due to changes in
176         // inherited values after restyling for animations, and thus we want to
177         // remove it so that we don't later try to restyle the element during a
178         // normal restyle.  (We could have separate RECASCADE_SELF_NORMAL and
179         // RECASCADE_SELF_ANIMATIONS flags to make it clear, but this isn't
180         // currently necessary.)
181         self.remove(RestyleHint::RECASCADE_SELF);
182     }
183 }
184 
185 impl Default for RestyleHint {
default() -> Self186     fn default() -> Self {
187         Self::empty()
188     }
189 }
190 
191 #[cfg(feature = "servo")]
192 malloc_size_of_is_0!(RestyleHint);
193