diff --git a/components/style/matching.rs b/components/style/matching.rs index 97faf4d0a6a..ca799af62c0 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -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(element: & flags } -pub struct ApplicableDeclarations { - pub normal: Vec, - pub per_pseudo: HashMap, - BuildHasherDefault<::fnv::FnvHasher>>, - - /// Whether the `normal` declarations are shareable with other nodes. - pub normal_shareable: bool, +type PseudoRuleNodes = HashMap>; +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>, old_style: Option<&Arc>, - applicable_declarations: &mut Vec, + rule_node: &StrongRuleNode, booleans: CascadeBooleans) - -> (Arc, StrongRuleNode) + -> Arc 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) + -> 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 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 = 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, parent: Option, - 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, 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()); ::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 = ::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, diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 4dff1d0c967..2b26fe61143 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -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.