diff options
Diffstat (limited to 'servo/components/style/invalidation/element/restyle_hints.rs')
-rw-r--r-- | servo/components/style/invalidation/element/restyle_hints.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/servo/components/style/invalidation/element/restyle_hints.rs b/servo/components/style/invalidation/element/restyle_hints.rs new file mode 100644 index 0000000000..fe89636e88 --- /dev/null +++ b/servo/components/style/invalidation/element/restyle_hints.rs @@ -0,0 +1,191 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Restyle hints: an optimization to avoid unnecessarily matching selectors. + +use crate::traversal_flags::TraversalFlags; + +bitflags! { + /// The kind of restyle we need to do for a given element. + #[repr(C)] + #[derive(Clone, Copy, Debug)] + pub struct RestyleHint: u16 { + /// Do a selector match of the element. + const RESTYLE_SELF = 1 << 0; + + /// Do a selector match of the element's pseudo-elements. Always to be combined with + /// RESTYLE_SELF. + const RESTYLE_PSEUDOS = 1 << 1; + + /// Do a selector match if the element is a pseudo-element. + const RESTYLE_SELF_IF_PSEUDO = 1 << 2; + + /// Do a selector match of the element's descendants. + const RESTYLE_DESCENDANTS = 1 << 3; + + /// Recascade the current element. + const RECASCADE_SELF = 1 << 4; + + /// Recascade the current element if it inherits any reset style. + const RECASCADE_SELF_IF_INHERIT_RESET_STYLE = 1 << 5; + + /// Recascade all descendant elements. + const RECASCADE_DESCENDANTS = 1 << 6; + + /// Replace the style data coming from CSS transitions without updating + /// any other style data. This hint is only processed in animation-only + /// traversal which is prior to normal traversal. + const RESTYLE_CSS_TRANSITIONS = 1 << 7; + + /// Replace the style data coming from CSS animations without updating + /// any other style data. This hint is only processed in animation-only + /// traversal which is prior to normal traversal. + const RESTYLE_CSS_ANIMATIONS = 1 << 8; + + /// Don't re-run selector-matching on the element, only the style + /// attribute has changed, and this change didn't have any other + /// dependencies. + const RESTYLE_STYLE_ATTRIBUTE = 1 << 9; + + /// Replace the style data coming from SMIL animations without updating + /// any other style data. This hint is only processed in animation-only + /// traversal which is prior to normal traversal. + const RESTYLE_SMIL = 1 << 10; + } +} + +impl RestyleHint { + /// Creates a new `RestyleHint` indicating that the current element and all + /// its descendants must be fully restyled. + pub fn restyle_subtree() -> Self { + RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS + } + + /// Creates a new `RestyleHint` indicating that the current element and all + /// its descendants must be recascaded. + pub fn recascade_subtree() -> Self { + RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS + } + + /// Returns whether this hint invalidates the element and all its + /// descendants. + pub fn contains_subtree(&self) -> bool { + self.contains(Self::restyle_subtree()) + } + + /// Returns whether we'll recascade all of the descendants. + pub fn will_recascade_subtree(&self) -> bool { + self.contains_subtree() || self.contains(Self::recascade_subtree()) + } + + /// Returns whether we need to restyle this element. + pub fn has_non_animation_invalidations(&self) -> bool { + !(*self & !Self::for_animations()).is_empty() + } + + /// Propagates this restyle hint to a child element. + pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self { + use std::mem; + + // In the middle of an animation only restyle, we don't need to + // propagate any restyle hints, and we need to remove ourselves. + if traversal_flags.for_animation_only() { + self.remove_animation_hints(); + return Self::empty(); + } + + debug_assert!( + !self.has_animation_hint(), + "There should not be any animation restyle hints \ + during normal traversal" + ); + + // Else we should clear ourselves, and return the propagated hint. + mem::replace(self, Self::empty()).propagate_for_non_animation_restyle() + } + + /// Returns a new `RestyleHint` appropriate for children of the current element. + fn propagate_for_non_animation_restyle(&self) -> Self { + if self.contains(RestyleHint::RESTYLE_DESCENDANTS) { + return Self::restyle_subtree(); + } + let mut result = Self::empty(); + if self.contains(RestyleHint::RESTYLE_PSEUDOS) { + result |= Self::RESTYLE_SELF_IF_PSEUDO; + } + if self.contains(RestyleHint::RECASCADE_DESCENDANTS) { + result |= Self::recascade_subtree(); + } + result + } + + /// Returns a hint that contains all the replacement hints. + pub fn replacements() -> Self { + RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations() + } + + /// The replacements for the animation cascade levels. + #[inline] + pub fn for_animations() -> Self { + RestyleHint::RESTYLE_SMIL | + RestyleHint::RESTYLE_CSS_ANIMATIONS | + RestyleHint::RESTYLE_CSS_TRANSITIONS + } + + /// Returns whether the hint specifies that an animation cascade level must + /// be replaced. + #[inline] + pub fn has_animation_hint(&self) -> bool { + self.intersects(Self::for_animations()) + } + + /// Returns whether the hint specifies that an animation cascade level must + /// be replaced. + #[inline] + pub fn has_animation_hint_or_recascade(&self) -> bool { + self.intersects( + Self::for_animations() | + Self::RECASCADE_SELF | + Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE, + ) + } + + /// Returns whether the hint specifies some restyle work other than an + /// animation cascade level replacement. + #[inline] + pub fn has_non_animation_hint(&self) -> bool { + !(*self & !Self::for_animations()).is_empty() + } + + /// Returns whether the hint specifies that some cascade levels must be + /// replaced. + #[inline] + pub fn has_replacements(&self) -> bool { + self.intersects(Self::replacements()) + } + + /// Removes all of the animation-related hints. + #[inline] + pub fn remove_animation_hints(&mut self) { + self.remove(Self::for_animations()); + + // While RECASCADE_SELF is not animation-specific, we only ever add and process it during + // traversal. If we are here, removing animation hints, then we are in an animation-only + // traversal, and we know that any RECASCADE_SELF flag must have been set due to changes in + // inherited values after restyling for animations, and thus we want to remove it so that + // we don't later try to restyle the element during a normal restyle. + // (We could have separate RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to + // make it clear, but this isn't currently necessary.) + self.remove(Self::RECASCADE_SELF | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE); + } +} + +impl Default for RestyleHint { + fn default() -> Self { + Self::empty() + } +} + +#[cfg(feature = "servo")] +malloc_size_of_is_0!(RestyleHint); |