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