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(RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS)
64     }
65 
66     /// Returns whether we need to restyle this element.
has_non_animation_invalidations(&self) -> bool67     pub fn has_non_animation_invalidations(&self) -> bool {
68         self.intersects(
69             RestyleHint::RESTYLE_SELF |
70                 RestyleHint::RECASCADE_SELF |
71                 (Self::replacements() & !Self::for_animations()),
72         )
73     }
74 
75     /// Propagates this restyle hint to a child element.
propagate(&mut self, traversal_flags: &TraversalFlags) -> Self76     pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
77         use std::mem;
78 
79         // In the middle of an animation only restyle, we don't need to
80         // propagate any restyle hints, and we need to remove ourselves.
81         if traversal_flags.for_animation_only() {
82             self.remove_animation_hints();
83             return Self::empty();
84         }
85 
86         debug_assert!(
87             !self.has_animation_hint(),
88             "There should not be any animation restyle hints \
89              during normal traversal"
90         );
91 
92         // Else we should clear ourselves, and return the propagated hint.
93         mem::replace(self, Self::empty()).propagate_for_non_animation_restyle()
94     }
95 
96     /// Returns a new `CascadeHint` appropriate for children of the current
97     /// element.
propagate_for_non_animation_restyle(&self) -> Self98     fn propagate_for_non_animation_restyle(&self) -> Self {
99         if self.contains(RestyleHint::RESTYLE_DESCENDANTS) {
100             return Self::restyle_subtree();
101         }
102         if self.contains(RestyleHint::RECASCADE_DESCENDANTS) {
103             return Self::recascade_subtree();
104         }
105         Self::empty()
106     }
107 
108     /// Returns a hint that contains all the replacement hints.
replacements() -> Self109     pub fn replacements() -> Self {
110         RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
111     }
112 
113     /// The replacements for the animation cascade levels.
114     #[inline]
for_animations() -> Self115     pub fn for_animations() -> Self {
116         RestyleHint::RESTYLE_SMIL |
117             RestyleHint::RESTYLE_CSS_ANIMATIONS |
118             RestyleHint::RESTYLE_CSS_TRANSITIONS
119     }
120 
121     /// Returns whether the hint specifies that the currently element must be
122     /// recascaded.
has_recascade_self(&self) -> bool123     pub fn has_recascade_self(&self) -> bool {
124         self.contains(RestyleHint::RECASCADE_SELF)
125     }
126 
127     /// Returns whether the hint specifies that an animation cascade level must
128     /// be replaced.
129     #[inline]
has_animation_hint(&self) -> bool130     pub fn has_animation_hint(&self) -> bool {
131         self.intersects(Self::for_animations())
132     }
133 
134     /// Returns whether the hint specifies that an animation cascade level must
135     /// be replaced.
136     #[inline]
has_animation_hint_or_recascade(&self) -> bool137     pub fn has_animation_hint_or_recascade(&self) -> bool {
138         self.intersects(Self::for_animations() | RestyleHint::RECASCADE_SELF)
139     }
140 
141     /// Returns whether the hint specifies some restyle work other than an
142     /// animation cascade level replacement.
143     #[inline]
has_non_animation_hint(&self) -> bool144     pub fn has_non_animation_hint(&self) -> bool {
145         !(*self & !Self::for_animations()).is_empty()
146     }
147 
148     /// Returns whether the hint specifies that selector matching must be re-run
149     /// for the element.
150     #[inline]
match_self(&self) -> bool151     pub fn match_self(&self) -> bool {
152         self.intersects(RestyleHint::RESTYLE_SELF)
153     }
154 
155     /// Returns whether the hint specifies that some cascade levels must be
156     /// replaced.
157     #[inline]
has_replacements(&self) -> bool158     pub fn has_replacements(&self) -> bool {
159         self.intersects(Self::replacements())
160     }
161 
162     /// Removes all of the animation-related hints.
163     #[inline]
remove_animation_hints(&mut self)164     pub fn remove_animation_hints(&mut self) {
165         self.remove(Self::for_animations());
166 
167         // While RECASCADE_SELF is not animation-specific, we only ever add and
168         // process it during traversal.  If we are here, removing animation
169         // hints, then we are in an animation-only traversal, and we know that
170         // any RECASCADE_SELF flag must have been set due to changes in
171         // inherited values after restyling for animations, and thus we want to
172         // remove it so that we don't later try to restyle the element during a
173         // normal restyle.  (We could have separate RECASCADE_SELF_NORMAL and
174         // RECASCADE_SELF_ANIMATIONS flags to make it clear, but this isn't
175         // currently necessary.)
176         self.remove(RestyleHint::RECASCADE_SELF);
177     }
178 }
179 
180 impl Default for RestyleHint {
default() -> Self181     fn default() -> Self {
182         Self::empty()
183     }
184 }
185 
186 #[cfg(feature = "servo")]
187 malloc_size_of_is_0!(RestyleHint);
188