Match eager pseudos after the primary cascade.

This is necessary in order to make the computation of eager pseudos depend on
the primary ComputedValues, which we want to do for ::first-letter/::first-line.

This also fixes a bug where the behavior of EagerPseudoStyles::is_empty was reversed
in both the implementation and the callsite.

MozReview-Commit-ID: EXBxclyHWXu
This commit is contained in:
Bobby Holley 2017-04-05 19:17:59 -07:00
parent 19743a67ba
commit 8acb4ed87c
3 changed files with 132 additions and 83 deletions

View file

@ -80,7 +80,7 @@ pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
impl EagerPseudoStyles { impl EagerPseudoStyles {
/// Returns whether there are any pseudo styles. /// Returns whether there are any pseudo styles.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_some() self.0.is_none()
} }
/// Returns a reference to the style for a given eager pseudo, if it exists. /// Returns a reference to the style for a given eager pseudo, if it exists.

View file

@ -30,7 +30,7 @@ use stylist::ApplicableDeclarationBlock;
/// Determines the amount of relations where we're going to share style. /// Determines the amount of relations where we're going to share style.
#[inline] #[inline]
pub fn relations_are_shareable(relations: &StyleRelations) -> bool { fn relations_are_shareable(relations: &StyleRelations) -> bool {
use selectors::matching::*; use selectors::matching::*;
!relations.intersects(AFFECTED_BY_ID_SELECTOR | !relations.intersects(AFFECTED_BY_ID_SELECTOR |
AFFECTED_BY_PSEUDO_ELEMENTS | AFFECTED_BY_STATE | AFFECTED_BY_PSEUDO_ELEMENTS | AFFECTED_BY_STATE |
@ -775,13 +775,71 @@ fn compute_rule_node<E: TElement>(rule_tree: &RuleTree,
impl<E: TElement> PrivateMatchMethods for E {} impl<E: TElement> PrivateMatchMethods for E {}
/// Controls whether the style sharing cache is used.
#[derive(Clone, Copy, PartialEq)]
pub enum StyleSharingBehavior {
/// Style sharing allowed.
Allow,
/// Style sharing disallowed.
Disallow,
}
/// The public API that elements expose for selector matching. /// The public API that elements expose for selector matching.
pub trait MatchMethods : TElement { pub trait MatchMethods : TElement {
/// Runs selector matching to (re)compute rule nodes for this element. /// Performs selector matching and property cascading on an element and its eager pseudos.
fn match_element(&self, fn match_and_cascade(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
sharing: StyleSharingBehavior)
{
// Perform selector matching for the primary style.
let mut primary_relations = StyleRelations::empty();
let _rule_node_changed = self.match_primary(context, data, &mut primary_relations);
// Cascade properties and compute primary values.
let mut expired = vec![];
self.cascade_primary(context, data, &mut expired);
// Match and cascade eager pseudo-elements.
if !data.styles().is_display_none() {
let _pseudo_rule_nodes_changed =
self.match_pseudos(context, data);
self.cascade_pseudos(context, data, &mut expired);
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
if !data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// If the style is shareable, add it to the LRU cache.
if sharing == StyleSharingBehavior::Allow && relations_are_shareable(&primary_relations) {
context.thread_local
.style_sharing_candidate_cache
.insert_if_possible(self,
data.styles().primary.values(),
primary_relations);
}
}
/// Performs the cascade, without matching.
fn cascade_primary_and_pseudos(&self,
context: &mut StyleContext<Self>,
mut data: &mut ElementData)
{
let mut possibly_expired_animations = vec![];
self.cascade_primary(context, &mut data, &mut possibly_expired_animations);
self.cascade_pseudos(context, &mut data, &mut possibly_expired_animations);
}
/// Runs selector matching to (re)compute the primary rule node for this element.
///
/// Returns whether the primary rule node changed.
fn match_primary(&self,
context: &mut StyleContext<Self>, context: &mut StyleContext<Self>,
data: &mut ElementData) data: &mut ElementData,
-> (StyleRelations, bool) relations: &mut StyleRelations)
-> bool
{ {
let mut applicable_declarations = let mut applicable_declarations =
Vec::<ApplicableDeclarationBlock>::with_capacity(16); Vec::<ApplicableDeclarationBlock>::with_capacity(16);
@ -790,6 +848,49 @@ pub trait MatchMethods : TElement {
let style_attribute = self.style_attribute(); let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None); let animation_rules = self.get_animation_rules(None);
let mut rule_nodes_changed = false; let mut rule_nodes_changed = false;
let bloom = context.thread_local.bloom_filter.filter();
let tasks = &mut context.thread_local.tasks;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
self.apply_selector_flags(tasks, element, flags);
};
// Compute the primary rule node.
*relations = stylist.push_applicable_declarations(self,
Some(bloom),
style_attribute,
animation_rules,
None,
&context.shared.guards,
&mut applicable_declarations,
&mut set_selector_flags);
let primary_rule_node =
compute_rule_node::<Self>(&stylist.rule_tree, &mut applicable_declarations);
if !data.has_styles() {
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
rule_nodes_changed = true;
} else if data.styles().primary.rules != primary_rule_node {
data.styles_mut().primary.rules = primary_rule_node;
rule_nodes_changed = true;
}
rule_nodes_changed
}
/// Runs selector matching to (re)compute eager pseudo-element rule nodes for this
/// element.
///
/// Returns whether any of the pseudo rule nodes changed (including, but not
/// limited to, cases where we match different pseudos altogether).
fn match_pseudos(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData)
-> bool
{
let mut applicable_declarations =
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
let mut rule_nodes_changed = false;
let tasks = &mut context.thread_local.tasks; let tasks = &mut context.thread_local.tasks;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
@ -798,31 +899,11 @@ pub trait MatchMethods : TElement {
// Borrow the stuff we need here so the borrow checker doesn't get mad // Borrow the stuff we need here so the borrow checker doesn't get mad
// at us later in the closure. // at us later in the closure.
let stylist = &context.shared.stylist;
let guards = &context.shared.guards; let guards = &context.shared.guards;
let rule_tree = &context.shared.stylist.rule_tree; let rule_tree = &stylist.rule_tree;
let bloom_filter = context.thread_local.bloom_filter.filter(); let bloom_filter = context.thread_local.bloom_filter.filter();
// Compute the primary rule node.
let mut primary_relations =
stylist.push_applicable_declarations(self,
Some(bloom_filter),
style_attribute,
animation_rules,
None,
guards,
&mut applicable_declarations,
&mut set_selector_flags);
let primary_rule_node =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
if !data.has_styles() {
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
rule_nodes_changed = true;
} else if data.styles().primary.rules != primary_rule_node {
data.styles_mut().primary.rules = primary_rule_node;
rule_nodes_changed = true;
}
// Compute rule nodes for eagerly-cascaded pseudo-elements. // Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false; let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| { SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
@ -864,12 +945,7 @@ pub trait MatchMethods : TElement {
} }
} }
// If we have any pseudo elements, indicate so in the primary StyleRelations. rule_nodes_changed
if data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
(primary_relations, rule_nodes_changed)
} }
/// Applies selector flags to an element, deferring mutations of the parent /// Applies selector flags to an element, deferring mutations of the parent
@ -1139,44 +1215,33 @@ pub trait MatchMethods : TElement {
} }
} }
/// Run the CSS cascade and compute values for the element, potentially /// Performs the cascade for the element's primary style.
/// starting any new transitions or animations. fn cascade_primary(&self,
fn cascade_element(&self,
context: &mut StyleContext<Self>, context: &mut StyleContext<Self>,
mut data: &mut AtomicRefMut<ElementData>) mut data: &mut ElementData,
possibly_expired_animations: &mut Vec<PropertyAnimation>)
{ {
let mut possibly_expired_animations = vec![]; self.cascade_primary_or_pseudo(context, &mut data, None,
possibly_expired_animations, /* animate = */ true);
}
// Cascade the primary style. /// Performs the cascade for the element's eager pseudos.
self.cascade_primary_or_pseudo(context, data, None, fn cascade_pseudos(&self,
&mut possibly_expired_animations, context: &mut StyleContext<Self>,
/* animate = */ true); mut data: &mut ElementData,
possibly_expired_animations: &mut Vec<PropertyAnimation>)
// Check whether the primary style is display:none. {
let display_none = data.styles().primary.values().get_box().clone_display() ==
display::T::none;
// Cascade each pseudo-element.
//
// Note that we've already set up the map of matching pseudo-elements // Note that we've already set up the map of matching pseudo-elements
// in match_element (and handled the damage implications of changing // in match_pseudos (and handled the damage implications of changing
// which pseudos match), so now we can just iterate what we have. This // which pseudos match), so now we can just iterate what we have. This
// does mean collecting owned pseudos, so that the borrow checker will // does mean collecting owned pseudos, so that the borrow checker will
// let us pass the mutable |data| to the inner cascade function. // let us pass the mutable |data| to the cascade function.
let matched_pseudos = data.styles().pseudos.keys(); let matched_pseudos = data.styles().pseudos.keys();
for pseudo in matched_pseudos { for pseudo in matched_pseudos {
// If the new primary style is display:none, we don't need pseudo
// styles, but we still need to clear any stale values.
if display_none {
data.styles_mut().pseudos.get_mut(&pseudo).unwrap().values = None;
continue;
}
// Only ::before and ::after are animatable. // Only ::before and ::after are animatable.
let animate = pseudo.is_before_or_after(); let animate = pseudo.is_before_or_after();
self.cascade_primary_or_pseudo(context, data, Some(&pseudo), self.cascade_primary_or_pseudo(context, data, Some(&pseudo),
&mut possibly_expired_animations, possibly_expired_animations, animate);
animate);
} }
} }

View file

@ -10,8 +10,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint}; use data::{ElementData, ElementStyles, StoredRestyleHint};
use dom::{DirtyDescendants, NodeInfo, TElement, TNode}; use dom::{DirtyDescendants, NodeInfo, TElement, TNode};
use matching::MatchMethods; use matching::{MatchMethods, StyleSharingBehavior};
use matching::relations_are_shareable;
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF}; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
use selector_parser::RestyleDamage; use selector_parser::RestyleDamage;
use servo_config::opts; use servo_config::opts;
@ -396,8 +395,7 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
} }
// Compute our style. // Compute our style.
element.match_element(context, &mut data); element.match_and_cascade(context, &mut data, StyleSharingBehavior::Disallow);
element.cascade_element(context, &mut data);
// Conservatively mark us as having dirty descendants, since there might // Conservatively mark us as having dirty descendants, since there might
// be other unstyled siblings we miss when walking straight up the parent // be other unstyled siblings we miss when walking straight up the parent
@ -602,32 +600,18 @@ fn compute_style<E, D>(_traversal: &D,
traversal_data.current_dom_depth = Some(dom_depth); traversal_data.current_dom_depth = Some(dom_depth);
context.thread_local.bloom_filter.assert_complete(element); context.thread_local.bloom_filter.assert_complete(element);
// Perform CSS selector matching.
context.thread_local.statistics.elements_matched += 1; context.thread_local.statistics.elements_matched += 1;
let (primary_relations, _rule_nodes_changed) =
element.match_element(context, &mut data);
// Cascade properties and compute values. // Perform the matching and cascading.
element.cascade_element(context, &mut data); element.match_and_cascade(context, &mut data, StyleSharingBehavior::Allow);
// If the style is shareable, add it to the LRU cache.
if relations_are_shareable(&primary_relations) {
context.thread_local
.style_sharing_candidate_cache
.insert_if_possible(&element,
data.styles().primary.values(),
primary_relations);
}
} }
CascadeWithReplacements(hint) => { CascadeWithReplacements(hint) => {
let _rule_nodes_changed = let _rule_nodes_changed =
element.cascade_with_replacements(hint, context, &mut data); element.cascade_with_replacements(hint, context, &mut data);
element.cascade_element(context, &mut data); element.cascade_primary_and_pseudos(context, &mut data);
} }
CascadeOnly => { CascadeOnly => {
element.cascade_element(context, &mut data); element.cascade_primary_and_pseudos(context, &mut data);
} }
}; };
} }