195 lines
7.1 KiB
Rust
195 lines
7.1 KiB
Rust
/* 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.
|
|
#[inline]
|
|
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.
|
|
#[inline]
|
|
pub fn recascade_subtree() -> Self {
|
|
RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS
|
|
}
|
|
|
|
/// Returns whether this hint invalidates the element and all its
|
|
/// descendants.
|
|
#[inline]
|
|
pub fn contains_subtree(&self) -> bool {
|
|
self.contains(Self::restyle_subtree())
|
|
}
|
|
|
|
/// Returns whether we'll recascade all of the descendants.
|
|
#[inline]
|
|
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);
|