servo: Merge #14175 - Separate selector matching from property cascading (from bholley:separate_cascade); r=emilio

This builds on @emilio's rule tree work. The goal is to raise the boundary between rule node computation and property cascading higher up the callstack, so that the new traversal architecture can decide to do one but not the other.

Source-Repo: https://github.com/servo/servo
Source-Revision: 3b2e3dcfb98464d24ad721a03009a1d31f54b31d
This commit is contained in:
Bobby Holley 2016-11-11 16:35:34 -06:00
parent 2bfe265228
commit 7fa6f2be7f
2 changed files with 86 additions and 92 deletions

View File

@ -18,7 +18,7 @@ use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
use properties::longhands::display::computed_value as display;
use rule_tree::StrongRuleNode;
use selector_impl::{PseudoElement, RestyleDamage, TheSelectorImpl};
use selector_matching::{ApplicableDeclarationBlock, Stylist};
use selector_matching::ApplicableDeclarationBlock;
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, MatchingReason, StyleRelations};
@ -50,29 +50,19 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
flags
}
pub struct ApplicableDeclarations {
pub normal: Vec<ApplicableDeclarationBlock>,
pub per_pseudo: HashMap<PseudoElement,
Vec<ApplicableDeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
/// Whether the `normal` declarations are shareable with other nodes.
pub normal_shareable: bool,
type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode,
BuildHasherDefault<::fnv::FnvHasher>>;
pub struct MatchResults {
pub primary: StrongRuleNode,
pub relations: StyleRelations,
pub per_pseudo: PseudoRuleNodes,
}
impl ApplicableDeclarations {
pub fn new() -> Self {
let mut applicable_declarations = ApplicableDeclarations {
normal: Vec::with_capacity(16),
per_pseudo: HashMap::with_hasher(Default::default()),
normal_shareable: false,
};
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
applicable_declarations.per_pseudo.insert(pseudo, vec![]);
});
applicable_declarations
impl MatchResults {
/// Returns true if the primary rule node is shareable with other nodes.
pub fn primary_is_shareable(&self) -> bool {
use traversal::relations_are_shareable;
relations_are_shareable(&self.relations)
}
}
@ -401,17 +391,12 @@ trait PrivateMatchMethods: TElement {
context: &Ctx,
parent_style: Option<&Arc<ComputedValues>>,
old_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>,
rule_node: &StrongRuleNode,
booleans: CascadeBooleans)
-> (Arc<ComputedValues>, StrongRuleNode)
-> Arc<ComputedValues>
where Ctx: StyleContext<'a>
{
let shared_context = context.shared_context();
let rule_node =
shared_context.stylist.rule_tree
.insert_ordered_rules(
applicable_declarations.drain(..).map(|d| (d.source, d.importance)));
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
if booleans.shareable {
@ -421,7 +406,7 @@ trait PrivateMatchMethods: TElement {
let this_style = match parent_style {
Some(ref parent_style) => {
cascade(shared_context.viewport_size,
&rule_node,
rule_node,
Some(&***parent_style),
Some(&mut cascade_info),
shared_context.error_reporter.clone(),
@ -429,7 +414,7 @@ trait PrivateMatchMethods: TElement {
}
None => {
cascade(shared_context.viewport_size,
&rule_node,
rule_node,
None,
Some(&mut cascade_info),
shared_context.error_reporter.clone(),
@ -461,7 +446,7 @@ trait PrivateMatchMethods: TElement {
}
}
(this_style, rule_node)
this_style
}
fn update_animations_for_cascade(&self,
@ -516,45 +501,63 @@ trait PrivateMatchMethods: TElement {
}
}
fn compute_rule_node<'a, Ctx>(context: &Ctx,
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
-> StrongRuleNode
where Ctx: StyleContext<'a>
{
let shared_context = context.shared_context();
let rules = applicable_declarations.drain(..).map(|d| (d.source, d.importance));
let rule_node = shared_context.stylist.rule_tree.insert_ordered_rules(rules);
rule_node
}
impl<E: TElement> PrivateMatchMethods for E {}
pub trait MatchMethods : TElement {
fn match_element(&self,
stylist: &Stylist,
parent_bf: Option<&BloomFilter>,
mut applicable_declarations: &mut ApplicableDeclarations)
-> StyleRelations {
use traversal::relations_are_shareable;
fn match_element<'a, Ctx>(&self, context: &Ctx, parent_bf: Option<&BloomFilter>)
-> MatchResults
where Ctx: StyleContext<'a>
{
let mut applicable_declarations: Vec<ApplicableDeclarationBlock> = Vec::with_capacity(16);
let stylist = &context.shared_context().stylist;
let style_attribute = self.style_attribute();
let mut relations =
// Compute the primary rule node.
let mut primary_relations =
stylist.push_applicable_declarations(self,
parent_bf,
style_attribute,
None,
&mut applicable_declarations.normal,
&mut applicable_declarations,
MatchingReason::ForStyling);
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
applicable_declarations.normal_shareable = relations_are_shareable(&relations);
// Compute the pseudo rule nodes.
let mut per_pseudo: PseudoRuleNodes = HashMap::with_hasher(Default::default());
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
stylist.push_applicable_declarations(self,
parent_bf,
None,
debug_assert!(applicable_declarations.is_empty());
stylist.push_applicable_declarations(self, parent_bf, None,
Some(&pseudo.clone()),
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]),
&mut applicable_declarations,
MatchingReason::ForStyling);
if !applicable_declarations.is_empty() {
let rule_node = compute_rule_node(context, &mut applicable_declarations);
per_pseudo.insert(pseudo, rule_node);
}
});
let has_pseudos =
applicable_declarations.per_pseudo.values().any(|v| !v.is_empty());
if has_pseudos {
relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
// If we have any pseudo elements, indicate so in the primary StyleRelations.
if !per_pseudo.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
relations
MatchResults {
primary: primary_rule_node,
relations: primary_relations,
per_pseudo: per_pseudo,
}
}
/// Attempts to share a style with another node. This method is unsafe because it depends on
@ -712,7 +715,9 @@ pub trait MatchMethods : TElement {
context: &Ctx,
mut data: AtomicRefMut<ElementData>,
parent: Option<Self>,
mut applicable_declarations: ApplicableDeclarations)
primary_rule_node: StrongRuleNode,
pseudo_rule_nodes: PseudoRuleNodes,
primary_is_shareable: bool)
where Ctx: StyleContext<'a>
{
// Get our parent's style.
@ -722,8 +727,6 @@ pub trait MatchMethods : TElement {
let mut new_styles;
let damage = {
let shareable = applicable_declarations.normal_shareable;
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
None => (None, None),
Some(previous) => {
@ -735,17 +738,17 @@ pub trait MatchMethods : TElement {
}
};
let (new_style, rule_node) =
let new_style =
self.cascade_node_pseudo_element(context,
parent_style,
old_primary,
&mut applicable_declarations.normal,
&primary_rule_node,
CascadeBooleans {
shareable: shareable,
shareable: primary_is_shareable,
animate: true,
});
new_styles = ElementStyles::new(new_style, rule_node);
new_styles = ElementStyles::new(new_style, primary_rule_node);
let damage =
self.compute_damage_and_cascade_pseudos(old_primary,
@ -753,7 +756,7 @@ pub trait MatchMethods : TElement {
&new_styles.primary,
&mut new_styles.pseudos,
context,
&mut applicable_declarations);
pseudo_rule_nodes);
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
p.as_node().can_be_fragmented() ||
@ -775,7 +778,7 @@ pub trait MatchMethods : TElement {
new_primary: &Arc<ComputedValues>,
new_pseudos: &mut PseudoStyles,
context: &Ctx,
applicable_declarations: &mut ApplicableDeclarations)
mut pseudo_rule_nodes: PseudoRuleNodes)
-> RestyleDamage
where Ctx: StyleContext<'a>
{
@ -817,17 +820,15 @@ pub trait MatchMethods : TElement {
debug_assert!(new_pseudos.is_empty());
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
let mut applicable_declarations_for_this_pseudo =
applicable_declarations.per_pseudo.get_mut(&pseudo).unwrap();
let has_declarations =
!applicable_declarations_for_this_pseudo.is_empty();
let maybe_rule_node = pseudo_rule_nodes.remove(&pseudo);
// Grab the old pseudo style for analysis.
let mut maybe_old_pseudo_style_and_rule_node =
old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
if has_declarations {
if maybe_rule_node.is_some() {
let new_rule_node = maybe_rule_node.unwrap();
// We have declarations, so we need to cascade. Compute parameters.
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
if animate {
@ -839,10 +840,10 @@ pub trait MatchMethods : TElement {
}
}
let (new_pseudo_style, new_rule_node) =
let new_pseudo_style =
self.cascade_node_pseudo_element(context, Some(new_primary),
maybe_old_pseudo_style_and_rule_node.as_ref().map(|s| &s.0),
&mut applicable_declarations_for_this_pseudo,
&new_rule_node,
CascadeBooleans {
shareable: false,
animate: animate,

View File

@ -8,7 +8,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
use data::ElementData;
use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode};
use matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult};
use matching::{MatchMethods, StyleSharingResult};
use selectors::bloom::BloomFilter;
use selectors::matching::StyleRelations;
use std::cell::RefCell;
@ -285,16 +285,14 @@ fn ensure_element_styled_internal<'a, E, C>(element: E,
// Note that we could add the bloom filter's complexity here, but that's
// probably not necessary since we're likely to be matching only a few
// nodes, at best.
let mut applicable_declarations = ApplicableDeclarations::new();
let data = prepare_for_styling(element, element.get_data().unwrap());
let stylist = &context.shared_context().stylist;
element.match_element(&**stylist,
None,
&mut applicable_declarations);
let match_results = element.match_element(context, None);
unsafe {
element.cascade_node(context, data, parent, applicable_declarations);
let shareable = match_results.primary_is_shareable();
element.cascade_node(context, data, parent,
match_results.primary,
match_results.per_pseudo,
shareable);
}
}
@ -330,34 +328,29 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
// Otherwise, match and cascade selectors.
match sharing_result {
StyleSharingResult::CannotShare => {
let mut applicable_declarations = ApplicableDeclarations::new();
let relations;
let match_results;
let shareable_element = {
if opts::get().style_sharing_stats {
STYLE_SHARING_CACHE_MISSES.fetch_add(1, Ordering::Relaxed);
}
// Perform the CSS selector matching.
let stylist = &context.shared_context().stylist;
relations = element.match_element(&**stylist,
Some(&*bf),
&mut applicable_declarations);
debug!("Result of selector matching: {:?}", relations);
if relations_are_shareable(&relations) {
match_results = element.match_element(context, Some(&*bf));
if match_results.primary_is_shareable() {
Some(element)
} else {
None
}
};
let relations = match_results.relations;
// Perform the CSS cascade.
unsafe {
let shareable = match_results.primary_is_shareable();
element.cascade_node(context, data, element.parent_element(),
applicable_declarations);
match_results.primary,
match_results.per_pseudo,
shareable);
}
// Add ourselves to the LRU cache.