/* 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)] pub struct RestyleHint: u8 { /// Do a selector match of the element. const RESTYLE_SELF = 1 << 0; /// Do a selector match of the element's descendants. const RESTYLE_DESCENDANTS = 1 << 1; /// Recascade the current element. const RECASCADE_SELF = 1 << 2; /// Recascade all descendant elements. const RECASCADE_DESCENDANTS = 1 << 3; /// 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 << 4; /// 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 << 5; /// 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 << 6; /// 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 << 7; } } 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.intersects( RestyleHint::RESTYLE_SELF | RestyleHint::RECASCADE_SELF | (Self::replacements() & !Self::for_animations()), ) } /// 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 `CascadeHint` 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(); } if self.contains(RestyleHint::RECASCADE_DESCENDANTS) { return Self::recascade_subtree(); } Self::empty() } /// 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 the currently element must be /// recascaded. pub fn has_recascade_self(&self) -> bool { self.contains(RestyleHint::RECASCADE_SELF) } /// 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() | RestyleHint::RECASCADE_SELF) } /// 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 selector matching must be re-run /// for the element. #[inline] pub fn match_self(&self) -> bool { self.intersects(RestyleHint::RESTYLE_SELF) } /// 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(RestyleHint::RECASCADE_SELF); } } impl Default for RestyleHint { fn default() -> Self { Self::empty() } } #[cfg(feature = "servo")] malloc_size_of_is_0!(RestyleHint);