diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2017-06-13 11:03:06 +0200 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2017-06-13 13:26:41 +0200 |
commit | cb06375fe2d445a09c45865fcb0fe6585a545461 (patch) | |
tree | 1078c7344fa5ab1bb3fac2b64f73ed7fe2026dc3 /components/style/invalidation/element/restyle_hints.rs | |
parent | fd10729941a8a21938d88770250dc4b7cb6a48af (diff) | |
download | servo-cb06375fe2d445a09c45865fcb0fe6585a545461.tar.gz servo-cb06375fe2d445a09c45865fcb0fe6585a545461.zip |
style: Implement a more fine-grained invalidation method.
This commit also removes the old restyle_hints module and splits it into
multiple modules under components/style/invalidation/element/.
The basic approach is to walk down the tree using compound selectors as needed,
in order to do as little selector-matching as possible.
Bug: 1368240
MozReview-Commit-ID: 2YO8fKFygZI
Diffstat (limited to 'components/style/invalidation/element/restyle_hints.rs')
-rw-r--r-- | components/style/invalidation/element/restyle_hints.rs | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs new file mode 100644 index 00000000000..786c267f203 --- /dev/null +++ b/components/style/invalidation/element/restyle_hints.rs @@ -0,0 +1,212 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! Restyle hints: an optimization to avoid unnecessarily matching selectors. + +#[cfg(feature = "gecko")] +use gecko_bindings::structs::nsRestyleHint; + +bitflags! { + /// The kind of restyle we need to do for a given element. + pub flags 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 { + RESTYLE_SELF | RESTYLE_DESCENDANTS + } + + /// Creates a new `RestyleHint` indicating that the current element and all + /// its descendants must be recascaded. + pub fn recascade_subtree() -> Self { + RECASCADE_SELF | RECASCADE_DESCENDANTS + } + + /// Returns a new `CascadeHint` appropriate for children of the current + /// element. + pub fn propagate_for_non_animation_restyle(&self) -> Self { + if self.contains(RESTYLE_DESCENDANTS) { + return Self::restyle_subtree() + } + if self.contains(RECASCADE_DESCENDANTS) { + return Self::recascade_subtree() + } + Self::empty() + } + + /// Creates a new `RestyleHint` that indicates the element must be + /// recascaded. + pub fn recascade_self() -> Self { + RECASCADE_SELF + } + + /// Returns a hint that contains all the replacement hints. + pub fn replacements() -> Self { + RESTYLE_STYLE_ATTRIBUTE | Self::for_animations() + } + + /// The replacements for the animation cascade levels. + #[inline] + pub fn for_animations() -> Self { + RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS + } + + /// Returns whether the hint specifies that some work must be performed on + /// the current element. + #[inline] + pub fn affects_self(&self) -> bool { + self.intersects(RESTYLE_SELF | RECASCADE_SELF | Self::replacements()) + } + + /// Returns whether the hint specifies that the currently element must be + /// recascaded. + pub fn has_recascade_self(&self) -> bool { + self.contains(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 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(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(RECASCADE_SELF); + } +} + +#[cfg(feature = "gecko")] +impl From<nsRestyleHint> for RestyleHint { + fn from(raw: nsRestyleHint) -> Self { + use gecko_bindings::structs::nsRestyleHint_eRestyle_ForceDescendants as eRestyle_ForceDescendants; + use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings; + use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self; + use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants; + use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree; + + let mut hint = RestyleHint::empty(); + + debug_assert!(raw.0 & eRestyle_LaterSiblings.0 == 0, + "Handle later siblings manually if necessary plz."); + + if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 { + hint.insert(RESTYLE_SELF); + } + + if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 { + hint.insert(RESTYLE_DESCENDANTS); + } + + if (raw.0 & eRestyle_ForceDescendants.0) != 0 { + hint.insert(RECASCADE_DESCENDANTS); + } + + hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8)); + + hint + } +} + +#[cfg(feature = "servo")] +impl ::heapsize::HeapSizeOf for RestyleHint { + fn heap_size_of_children(&self) -> usize { 0 } +} + +/// Asserts that all replacement hints have a matching nsRestyleHint value. +#[cfg(feature = "gecko")] +#[inline] +pub fn assert_restyle_hints_match() { + use gecko_bindings::structs; + + macro_rules! check_restyle_hints { + ( $( $a:ident => $b:ident ),*, ) => { + if cfg!(debug_assertions) { + let mut replacements = RestyleHint::replacements(); + $( + assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b)); + replacements.remove($b); + )* + assert_eq!(replacements, RestyleHint::empty(), + "all RestyleHint replacement bits should have an \ + assertion"); + } + } + } + + check_restyle_hints! { + nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS, + nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS, + nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE, + nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL, + } +} |