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