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