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
This commit is contained in:
Emilio Cobos Álvarez 2017-06-13 11:03:06 +02:00
parent fd10729941
commit cb06375fe2
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
21 changed files with 1673 additions and 1486 deletions

View file

@ -7,9 +7,9 @@
use arrayvec::ArrayVec;
use context::SharedStyleContext;
use dom::TElement;
use invalidation::element::restyle_hints::RestyleHint;
use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
use properties::longhands::display::computed_value as display;
use restyle_hints::{CascadeHint, HintComputationContext, RestyleReplacements, RestyleHint};
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
use selectors::matching::VisitedHandlingMode;
@ -345,8 +345,10 @@ impl ElementStyles {
///
/// We wrap it in a newtype to force the encapsulation of the complexity of
/// handling the correct invalidations in this file.
#[derive(Clone, Debug)]
pub struct StoredRestyleHint(RestyleHint);
///
/// TODO(emilio): This will probably be a non-issue in a bit.
#[derive(Clone, Copy, Debug)]
pub struct StoredRestyleHint(pub RestyleHint);
impl StoredRestyleHint {
/// Propagates this restyle hint to a child element.
@ -378,14 +380,7 @@ impl StoredRestyleHint {
/// Creates a restyle hint that forces the whole subtree to be restyled,
/// including the element.
pub fn subtree() -> Self {
StoredRestyleHint(RestyleHint::subtree())
}
/// Creates a restyle hint that forces the element and all its later
/// siblings to have their whole subtrees restyled, including the elements
/// themselves.
pub fn subtree_and_later_siblings() -> Self {
StoredRestyleHint(RestyleHint::subtree_and_later_siblings())
StoredRestyleHint(RestyleHint::restyle_subtree())
}
/// Creates a restyle hint that indicates the element must be recascaded.
@ -398,12 +393,6 @@ impl StoredRestyleHint {
self.0.affects_self()
}
/// Returns true if the hint indicates that our sibling's style may be
/// invalidated.
pub fn has_sibling_invalidations(&self) -> bool {
self.0.affects_later_siblings()
}
/// Whether the restyle hint is empty (nothing requires to be restyled).
pub fn is_empty(&self) -> bool {
self.0.is_empty()
@ -416,12 +405,7 @@ impl StoredRestyleHint {
/// Contains whether the whole subtree is invalid.
pub fn contains_subtree(&self) -> bool {
self.0.contains(&RestyleHint::subtree())
}
/// Insert another restyle hint, effectively resulting in the union of both.
pub fn insert_from(&mut self, other: &Self) {
self.0.insert_from(&other.0)
self.0.contains(RestyleHint::restyle_subtree())
}
/// Returns true if the hint has animation-only restyle.
@ -434,16 +418,11 @@ impl StoredRestyleHint {
pub fn has_recascade_self(&self) -> bool {
self.0.has_recascade_self()
}
/// Insert the specified `CascadeHint`.
pub fn insert_cascade_hint(&mut self, cascade_hint: CascadeHint) {
self.0.insert_cascade_hint(cascade_hint);
}
}
impl Default for StoredRestyleHint {
fn default() -> Self {
StoredRestyleHint::empty()
Self::empty()
}
}
@ -483,11 +462,6 @@ impl RestyleData {
self.hint.has_self_invalidations()
}
/// Returns true if this RestyleData might invalidate sibling styles.
pub fn has_sibling_invalidations(&self) -> bool {
self.hint.has_sibling_invalidations()
}
/// Returns damage handled.
#[cfg(feature = "gecko")]
pub fn damage_handled(&self) -> RestyleDamage {
@ -533,66 +507,41 @@ pub enum RestyleKind {
MatchAndCascade,
/// We need to recascade with some replacement rule, such as the style
/// attribute, or animation rules.
CascadeWithReplacements(RestyleReplacements),
CascadeWithReplacements(RestyleHint),
/// We only need to recascade, for example, because only inherited
/// properties in the parent changed.
CascadeOnly,
}
impl ElementData {
/// Computes the final restyle hint for this element, potentially allocating
/// a `RestyleData` if we need to.
///
/// This expands the snapshot (if any) into a restyle hint, and handles
/// explicit sibling restyle hints from the stored restyle hint.
///
/// Returns true if later siblings must be restyled.
pub fn compute_final_hint<'a, E: TElement>(
/// Invalidates style for this element, its descendants, and later siblings,
/// based on the snapshot of the element that we took when attributes or
/// state changed.
pub fn invalidate_style_if_needed<'a, E: TElement>(
&mut self,
element: E,
shared_context: &SharedStyleContext,
hint_context: HintComputationContext<'a, E>)
-> bool
shared_context: &SharedStyleContext)
{
debug!("compute_final_hint: {:?}, {:?}",
element,
shared_context.traversal_flags);
use invalidation::element::invalidator::TreeStyleInvalidator;
let mut hint = match self.get_restyle() {
Some(r) => r.hint.0.clone(),
None => RestyleHint::empty(),
};
debug!("compute_final_hint: {:?}, has_snapshot: {}, handled_snapshot: {}, \
pseudo: {:?}",
debug!("invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
handled_snapshot: {}, pseudo: {:?}",
element,
shared_context.traversal_flags,
element.has_snapshot(),
element.handled_snapshot(),
element.implemented_pseudo_element());
if element.has_snapshot() && !element.handled_snapshot() {
let snapshot_hint =
shared_context.stylist.compute_restyle_hint(&element,
shared_context,
hint_context);
hint.insert(snapshot_hint);
let invalidator = TreeStyleInvalidator::new(
element,
Some(self),
shared_context,
);
invalidator.invalidate();
unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot());
}
let empty_hint = hint.is_empty();
// If the hint includes a directive for later siblings, strip it out and
// notify the caller to modify the base hint for future siblings.
let later_siblings = hint.remove_later_siblings_hint();
// Insert the hint, overriding the previous hint. This effectively takes
// care of removing the later siblings restyle hint.
if !empty_hint {
self.ensure_restyle().hint = hint.into();
}
later_siblings
}
@ -626,13 +575,13 @@ impl ElementData {
debug_assert!(self.restyle.is_some());
let restyle_data = self.restyle.as_ref().unwrap();
let hint = &restyle_data.hint.0;
let hint = restyle_data.hint.0;
if hint.match_self() {
return RestyleKind::MatchAndCascade;
}
if hint.has_replacements() {
return RestyleKind::CascadeWithReplacements(hint.replacements);
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements());
}
debug_assert!(hint.has_recascade_self(), "We definitely need to do something!");