Bug 1345950: stylo: Fix slow selector flags. r=bholley

MozReview-Commit-ID: QwuSxulNG0
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-03-21 12:58:28 +01:00
parent f90f7d6824
commit a9b133bcbb
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
9 changed files with 288 additions and 189 deletions

View file

@ -18,7 +18,7 @@ use dom::{AnimationRules, SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
use rule_tree::{CascadeLevel, StrongRuleNode};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
@ -750,12 +750,12 @@ trait PrivateMatchMethods: TElement {
}
}
fn compute_rule_node<E: TElement>(context: &StyleContext<E>,
fn compute_rule_node<E: TElement>(rule_tree: &RuleTree,
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
-> StrongRuleNode
{
let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level));
let rule_node = context.shared.stylist.rule_tree.insert_ordered_rules(rules);
let rule_node = rule_tree.insert_ordered_rules(rules);
rule_node
}
@ -775,20 +775,75 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None);
let mut flags = ElementSelectorFlags::empty();
let mut rule_nodes_changed = false;
// TODO(emilio): This is somewhat inefficient, because of a variety of
// reasons:
//
// * It doesn't coalesce flags.
// * It doesn't look at flags already sent in a task for the main
// thread to process.
// * It doesn't take advantage of us knowing that the traversal is
// sequential.
//
// I suspect (need to measure!) that we don't use to set flags on
// a lot of different elements, but we could end up posting the same
// flag over and over with this approach.
//
// If the number of elements is low, perhaps a small cache with the
// flags already sent would be appropriate.
//
// The sequential task business for this is kind of sad :(.
//
// Anyway, let's do the obvious thing for now.
let tasks = &mut context.thread_local.tasks;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
if element == self {
unsafe { element.set_selector_flags(self_flags); }
} else {
if !element.has_selector_flags(self_flags) {
let task =
SequentialTask::set_selector_flags(element.clone(),
self_flags);
tasks.push(task);
}
}
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are
// already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
tasks.push(task);
}
}
}
};
// Borrow the stuff we need here so the borrow checker doesn't get mad
// at us later in the closure.
let guards = &context.shared.guards;
let rule_tree = &context.shared.stylist.rule_tree;
let bloom_filter = context.thread_local.bloom_filter.filter();
// Compute the primary rule node.
let mut primary_relations =
stylist.push_applicable_declarations(self,
Some(context.thread_local.bloom_filter.filter()),
Some(bloom_filter),
style_attribute,
animation_rules,
None,
&context.shared.guards,
guards,
&mut applicable_declarations,
&mut flags);
let primary_rule_node = compute_rule_node(context, &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;
@ -808,15 +863,16 @@ pub trait MatchMethods : TElement {
AnimationRules(None, None)
};
stylist.push_applicable_declarations(self,
Some(context.thread_local.bloom_filter.filter()),
Some(bloom_filter),
None, pseudo_animation_rules,
Some(&pseudo),
&context.shared.guards,
&guards,
&mut applicable_declarations,
&mut flags);
&mut set_selector_flags);
if !applicable_declarations.is_empty() {
let new_rules = compute_rule_node(context, &mut applicable_declarations);
let new_rules =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
match per_pseudo.entry(pseudo) {
Entry::Occupied(mut e) => {
if e.get().rules != new_rules {
@ -848,22 +904,6 @@ pub trait MatchMethods : TElement {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { self.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = self.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
context.thread_local.tasks.push(task);
}
}
}
MatchResults {
primary_relations: Some(primary_relations),
rule_nodes_changed: rule_nodes_changed,