Skip to content

Commit

Permalink
Separate rule node computation from cascade.
Browse files Browse the repository at this point in the history
  • Loading branch information
bholley committed Nov 11, 2016
1 parent 75d3524 commit 5741b81
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 92 deletions.
141 changes: 71 additions & 70 deletions components/style/matching.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -421,15 +406,15 @@ 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(),
cascade_flags)
}
None => {
cascade(shared_context.viewport_size,
&rule_node,
rule_node,
None,
Some(&mut cascade_info),
shared_context.error_reporter.clone(),
Expand Down Expand Up @@ -461,7 +446,7 @@ trait PrivateMatchMethods: TElement {
}
}

(this_style, rule_node)
this_style
}

fn update_animations_for_cascade(&self,
Expand Down Expand Up @@ -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);
});

let has_pseudos =
applicable_declarations.per_pseudo.values().any(|v| !v.is_empty());
if !applicable_declarations.is_empty() {
let rule_node = compute_rule_node(context, &mut applicable_declarations);
per_pseudo.insert(pseudo, rule_node);
}
});

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
Expand Down Expand Up @@ -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.
Expand All @@ -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) => {
Expand All @@ -735,25 +738,25 @@ 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,
old_pseudos,
&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() ||
Expand All @@ -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>
{
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down
37 changes: 15 additions & 22 deletions components/style/traversal.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 5741b81

Please sign in to comment.