diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-02-02 03:00:22 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-02 03:00:22 -0800 |
commit | fb7f65fc5711f41b991b72ed97d7286dd16301ed (patch) | |
tree | 4e53a5644ec98930df37c2e640bc9bd1a21935f0 | |
parent | 8aa23b46db300210b67c16190c794bc9ee216f3f (diff) | |
parent | 2594cb9c33d93b1e63ed49816797887c287340db (diff) | |
download | servo-fb7f65fc5711f41b991b72ed97d7286dd16301ed.tar.gz servo-fb7f65fc5711f41b991b72ed97d7286dd16301ed.zip |
Auto merge of #15317 - emilio:style-attr-restyle, r=bholley
style: Avoid selector-matching when only the style attribute is changed.
r? @bholley
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15317)
<!-- Reviewable:end -->
-rw-r--r-- | components/style/data.rs | 103 | ||||
-rw-r--r-- | components/style/matching.rs | 72 | ||||
-rw-r--r-- | components/style/restyle_hints.rs | 8 | ||||
-rw-r--r-- | components/style/rule_tree/mod.rs | 131 | ||||
-rw-r--r-- | components/style/traversal.rs | 117 |
5 files changed, 337 insertions, 94 deletions
diff --git a/components/style/data.rs b/components/style/data.rs index 0c097b103a1..c2807a2036b 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -9,7 +9,7 @@ use dom::TElement; use properties::ComputedValues; use properties::longhands::display::computed_value as display; -use restyle_hints::{RESTYLE_LATER_SIBLINGS, RestyleHint}; +use restyle_hints::{RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use rule_tree::StrongRuleNode; use selector_parser::{PseudoElement, RestyleDamage, Snapshot}; use std::collections::HashMap; @@ -44,8 +44,8 @@ impl ComputedStyle { } } -// We manually implement Debug for ComputedStyle so tht we can avoid the verbose -// stringification of ComputedValues for normal logging. +// We manually implement Debug for ComputedStyle so that we can avoid the +// verbose stringification of ComputedValues for normal logging. impl fmt::Debug for ComputedStyle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ComputedStyle {{ rules: {:?}, values: {{..}} }}", self.rules) @@ -55,6 +55,13 @@ impl fmt::Debug for ComputedStyle { type PseudoStylesInner = HashMap<PseudoElement, ComputedStyle, BuildHasherDefault<::fnv::FnvHasher>>; +/// The rule nodes for each of the pseudo-elements of an element. +/// +/// TODO(emilio): Probably shouldn't be a `HashMap` by default, but a smaller +/// array. +pub type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode, + BuildHasherDefault<::fnv::FnvHasher>>; + /// A set of styles for a given element's pseudo-elements. /// /// This is a map from pseudo-element to `ComputedStyle`. @@ -69,6 +76,19 @@ impl PseudoStyles { pub fn empty() -> Self { PseudoStyles(HashMap::with_hasher(Default::default())) } + + /// Gets the rules that the different pseudo-elements matched. + /// + /// FIXME(emilio): We could in theory avoid creating these when we have + /// support for just re-cascading an element. Then the input to + /// `cascade_node` could be `MatchResults` or just `UseExistingStyle`. + pub fn get_rules(&self) -> PseudoRuleNodes { + let mut rules = HashMap::with_hasher(Default::default()); + for (pseudo, style) in &self.0 { + rules.insert(pseudo.clone(), style.rules.clone()); + } + rules + } } impl Deref for PseudoStyles { @@ -144,8 +164,11 @@ impl DescendantRestyleHint { /// to provide more type safety while propagating restyle hints down the tree. #[derive(Clone, Debug)] pub struct StoredRestyleHint { - /// Whether this element should be restyled during the traversal. - pub restyle_self: bool, + /// Whether this element should be restyled during the traversal, and how. + /// + /// This hint is stripped down, and only contains hints that are a subset of + /// RestyleHint::for_single_element(). + pub self_: RestyleHint, /// Whether the descendants of this element need to be restyled. pub descendants: DescendantRestyleHint, } @@ -154,7 +177,11 @@ impl StoredRestyleHint { /// Propagates this restyle hint to a child element. pub fn propagate(&self) -> Self { StoredRestyleHint { - restyle_self: self.descendants != DescendantRestyleHint::Empty, + self_: if self.descendants == DescendantRestyleHint::Empty { + RestyleHint::empty() + } else { + RESTYLE_SELF + }, descendants: self.descendants.propagate(), } } @@ -162,7 +189,7 @@ impl StoredRestyleHint { /// Creates an empty `StoredRestyleHint`. pub fn empty() -> Self { StoredRestyleHint { - restyle_self: false, + self_: RestyleHint::empty(), descendants: DescendantRestyleHint::Empty, } } @@ -171,29 +198,32 @@ impl StoredRestyleHint { /// including the element. pub fn subtree() -> Self { StoredRestyleHint { - restyle_self: true, + self_: RESTYLE_SELF, descendants: DescendantRestyleHint::Descendants, } } + /// Returns true if the hint indicates that our style may be invalidated. + pub fn has_self_invalidations(&self) -> bool { + !self.self_.is_empty() + } + /// Whether the restyle hint is empty (nothing requires to be restyled). pub fn is_empty(&self) -> bool { - !self.restyle_self && self.descendants == DescendantRestyleHint::Empty + !self.has_self_invalidations() && + self.descendants == DescendantRestyleHint::Empty } /// Insert another restyle hint, effectively resulting in the union of both. pub fn insert(&mut self, other: &Self) { - self.restyle_self = self.restyle_self || other.restyle_self; + self.self_ |= other.self_; self.descendants = self.descendants.union(other.descendants); } } impl Default for StoredRestyleHint { fn default() -> Self { - StoredRestyleHint { - restyle_self: false, - descendants: DescendantRestyleHint::Empty, - } + StoredRestyleHint::empty() } } @@ -203,7 +233,7 @@ impl From<RestyleHint> for StoredRestyleHint { use self::DescendantRestyleHint::*; debug_assert!(!hint.contains(RESTYLE_LATER_SIBLINGS), "Caller should apply sibling hints"); StoredRestyleHint { - restyle_self: hint.contains(RESTYLE_SELF) || hint.contains(RESTYLE_STYLE_ATTRIBUTE), + self_: hint & RestyleHint::for_self(), descendants: if hint.contains(RESTYLE_DESCENDANTS) { Descendants } else { Empty }, } } @@ -319,7 +349,9 @@ impl RestyleData { /// Returns true if this RestyleData might invalidate the current style. pub fn has_invalidations(&self) -> bool { - self.hint.restyle_self || self.recascade || self.snapshot.is_some() + self.hint.has_self_invalidations() || + self.recascade || + self.snapshot.is_some() } } @@ -338,6 +370,19 @@ pub struct ElementData { restyle: Option<Box<RestyleData>>, } +/// The kind of restyle that a single element should do. +pub enum RestyleKind { + /// We need to run selector matching plus re-cascade, that is, a full + /// restyle. + MatchAndCascade, + /// We need to recascade with some replacement rule, such as the style + /// attribute, or animation rules. + CascadeWithReplacements(RestyleHint), + /// We only need to recascade, for example, because only inherited + /// properties in the parent changed. + CascadeOnly, +} + impl ElementData { /// Trivially construct an ElementData. pub fn new(existing: Option<ElementStyles>) -> Self { @@ -356,7 +401,31 @@ impl ElementData { /// invalidation. pub fn has_current_styles(&self) -> bool { self.has_styles() && - self.restyle.as_ref().map_or(true, |r| !r.has_invalidations()) + self.restyle.as_ref().map_or(true, |r| !r.has_invalidations()) + } + + /// Returns the kind of restyling that we're going to need to do on this + /// element, based of the stored restyle hint. + pub fn restyle_kind(&self) -> RestyleKind { + debug_assert!(!self.has_current_styles(), "Should've stopped earlier"); + if !self.has_styles() { + return RestyleKind::MatchAndCascade; + } + + debug_assert!(self.restyle.is_some()); + let restyle_data = self.restyle.as_ref().unwrap(); + let hint = restyle_data.hint.self_; + if hint.contains(RESTYLE_SELF) { + return RestyleKind::MatchAndCascade; + } + + if !hint.is_empty() { + return RestyleKind::CascadeWithReplacements(hint); + } + + debug_assert!(restyle_data.recascade, + "We definitely need to do something!"); + return RestyleKind::CascadeOnly; } /// Gets the element styles, if any. diff --git a/components/style/matching.rs b/components/style/matching.rs index 51f05b8bdec..bacb0905389 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -13,19 +13,19 @@ use atomic_refcell::AtomicRefMut; use cache::LRUCache; use cascade_info::CascadeInfo; use context::{SharedStyleContext, StyleContext}; -use data::{ComputedStyle, ElementData, ElementStyles, PseudoStyles}; +use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoStyles}; use dom::{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 rule_tree::StrongRuleNode; +use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint}; +use rule_tree::{CascadeLevel, StrongRuleNode}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selectors::MatchAttr; use selectors::bloom::BloomFilter; -use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, MatchingReason, StyleRelations}; +use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE, MatchingReason, StyleRelations}; use servo_config::opts; use sink::ForgetfulSink; use std::collections::HashMap; -use std::hash::BuildHasherDefault; use std::slice::IterMut; use std::sync::Arc; use stylist::ApplicableDeclarationBlock; @@ -60,13 +60,6 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: & flags } -/// The rule nodes for each of the pseudo-elements of an element. -/// -/// TODO(emilio): Probably shouldn't be a `HashMap` by default, but a smaller -/// array. -type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode, - BuildHasherDefault<::fnv::FnvHasher>>; - /// The results of selector matching on an element. pub struct MatchResults { /// The rule node reference that represents the rules matched by the @@ -635,6 +628,59 @@ pub trait MatchMethods : TElement { } } + /// Get the appropriate MatchResults from the current styles, to perform a + /// recascade. + /// + /// TODO(emilio): Stop using `MachResults`, use an enum, or something. + fn match_results_from_current_style(&self, data: &ElementData) -> MatchResults { + let rule_node = data.styles().primary.rules.clone(); + MatchResults { + primary: rule_node, + // FIXME(emilio): Same concern as below. + relations: StyleRelations::empty(), + // The per-pseudo rule-nodes haven't changed, but still need to be + // recascaded. + per_pseudo: data.styles().pseudos.get_rules(), + } + + } + + /// Updates the rule nodes without re-running selector matching, using just + /// the rule tree. + fn cascade_with_replacements(&self, + hint: RestyleHint, + context: &StyleContext<Self>, + data: &mut AtomicRefMut<ElementData>) + -> MatchResults { + let mut rule_node = data.styles().primary.rules.clone(); + + if hint.contains(RESTYLE_STYLE_ATTRIBUTE) { + let style_attribute = self.style_attribute(); + + rule_node = context.shared.stylist.rule_tree + .update_rule_at_level(CascadeLevel::StyleAttributeNormal, + style_attribute, + rule_node); + + rule_node = context.shared.stylist.rule_tree + .update_rule_at_level(CascadeLevel::StyleAttributeImportant, + style_attribute, + rule_node); + } + + MatchResults { + primary: rule_node, + // FIXME(emilio): This is ok, for now, we shouldn't need to fake + // this. + relations: AFFECTED_BY_STYLE_ATTRIBUTE, + // The per-pseudo rule-nodes haven't changed, but still need to be + // recomputed. + // + // TODO(emilio): We could probably optimize this quite a bit. + per_pseudo: data.styles().pseudos.get_rules(), + } + } + /// Attempts to share a style with another node. This method is unsafe /// because it depends on the `style_sharing_candidate_cache` having only /// live nodes in it, and we have no way to guarantee that at the type @@ -649,6 +695,10 @@ pub trait MatchMethods : TElement { return StyleSharingResult::CannotShare } + if self.parent_element().is_none() { + return StyleSharingResult::CannotShare + } + if self.style_attribute().is_some() { return StyleSharingResult::CannotShare } diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 8528ef116b6..cb0d889e7e9 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -50,6 +50,14 @@ bitflags! { } } +impl RestyleHint { + /// The subset hints that affect the styling of a single element during the + /// traversal. + pub fn for_self() -> Self { + RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE + } +} + #[cfg(feature = "gecko")] impl From<nsRestyleHint> for RestyleHint { fn from(raw: nsRestyleHint) -> Self { diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index ee5ac080b49..c93ff94a841 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -153,9 +153,20 @@ impl RuleTree { pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode where I: Iterator<Item=(StyleSource, CascadeLevel)>, { - let mut current = self.root.clone(); + self.insert_ordered_rules_from(self.root.clone(), iter) + } + + fn insert_ordered_rules_from<'a, I>(&self, + from: StrongRuleNode, + iter: I) -> StrongRuleNode + where I: Iterator<Item=(StyleSource, CascadeLevel)>, + { + let mut current = from; + let mut last_level = current.get().level; for (source, level) in iter { + debug_assert!(last_level <= level, "Not really ordered"); current = current.ensure_child(self.root.downgrade(), source, level); + last_level = level; } current } @@ -169,6 +180,90 @@ impl RuleTree { pub unsafe fn maybe_gc(&self) { self.root.maybe_gc(); } + + /// Replaces a rule in a given level (if present) for another rule. + /// + /// Returns the resulting node that represents the new path. + pub fn update_rule_at_level(&self, + level: CascadeLevel, + pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>, + path: StrongRuleNode) + -> StrongRuleNode { + debug_assert!(level.is_unique_per_element()); + // TODO(emilio): Being smarter with lifetimes we could avoid a bit of + // the refcount churn. + let mut current = path.clone(); + + // First walk up until the first less-or-equally specific rule. + let mut children = vec![]; + while current.get().level > level { + children.push((current.get().source.clone().unwrap(), current.get().level)); + current = current.parent().unwrap().clone(); + } + + // Then remove the one at the level we want to replace, if any. + // + // NOTE: Here we assume that only one rule can be at the level we're + // replacing. + // + // This is certainly true for HTML style attribute rules, animations and + // transitions, but could not be so for SMIL animations, which we'd need + // to special-case (isn't hard, it's just about removing the `if` and + // special cases, and replacing them for a `while` loop, avoiding the + // optimizations). + if current.get().level == level { + if let Some(pdb) = pdb { + // If the only rule at the level we're replacing is exactly the + // same as `pdb`, we're done, and `path` is still valid. + // + // TODO(emilio): Another potential optimization is the one where + // we can just replace the rule at that level for `pdb`, and + // then we don't need to re-create the children, and `path` is + // also equally valid. This is less likely, and would require an + // in-place mutation of the source, which is, at best, fiddly, + // so let's skip it for now. + let is_here_already = match current.get().source.as_ref() { + Some(&StyleSource::Declarations(ref already_here)) => { + arc_ptr_eq(pdb, already_here) + }, + _ => unreachable!("Replacing non-declarations style?"), + }; + + if is_here_already { + debug!("Picking the fast path in rule replacement"); + return path; + } + } + current = current.parent().unwrap().clone(); + } + debug_assert!(current.get().level != level, + "Multiple rules should've been replaced?"); + + // Insert the rule if it's relevant at this level in the cascade. + // + // These optimizations are likely to be important, because the levels + // where replacements apply (style and animations) tend to trigger + // pretty bad styling cases already. + if let Some(pdb) = pdb { + if level.is_important() { + if pdb.read().any_important() { + current = current.ensure_child(self.root.downgrade(), + StyleSource::Declarations(pdb.clone()), + level); + } + } else { + if pdb.read().any_normal() { + current = current.ensure_child(self.root.downgrade(), + StyleSource::Declarations(pdb.clone()), + level); + } + } + } + + // Now the rule is in the relevant place, push the children as + // necessary. + self.insert_ordered_rules_from(current, children.into_iter().rev()) + } } /// The number of RuleNodes added to the free list before we will consider @@ -183,7 +278,7 @@ const RULE_TREE_GC_INTERVAL: usize = 300; /// /// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin #[repr(u8)] -#[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[derive(Eq, PartialEq, Copy, Clone, Debug, PartialOrd)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum CascadeLevel { /// Normal User-Agent rules. @@ -211,6 +306,18 @@ pub enum CascadeLevel { } impl CascadeLevel { + /// Returns whether this cascade level is unique per element, in which case + /// we can replace the path in the cascade without fear. + pub fn is_unique_per_element(&self) -> bool { + match *self { + CascadeLevel::Transitions | + CascadeLevel::Animations | + CascadeLevel::StyleAttributeNormal | + CascadeLevel::StyleAttributeImportant => true, + _ => false, + } + } + /// Returns whether this cascade level represents important rules of some /// sort. #[inline] @@ -248,7 +355,7 @@ struct RuleNode { source: Option<StyleSource>, /// The cascade level this rule is positioned at. - cascade_level: CascadeLevel, + level: CascadeLevel, refcount: AtomicUsize, first_child: AtomicPtr<RuleNode>, @@ -274,13 +381,13 @@ impl RuleNode { fn new(root: WeakRuleNode, parent: StrongRuleNode, source: StyleSource, - cascade_level: CascadeLevel) -> Self { + level: CascadeLevel) -> Self { debug_assert!(root.upgrade().parent().is_none()); RuleNode { root: Some(root), parent: Some(parent), source: Some(source), - cascade_level: cascade_level, + level: level, refcount: AtomicUsize::new(1), first_child: AtomicPtr::new(ptr::null_mut()), next_sibling: AtomicPtr::new(ptr::null_mut()), @@ -295,7 +402,7 @@ impl RuleNode { root: None, parent: None, source: None, - cascade_level: CascadeLevel::UANormal, + level: CascadeLevel::UANormal, refcount: AtomicUsize::new(1), first_child: AtomicPtr::new(ptr::null_mut()), next_sibling: AtomicPtr::new(ptr::null_mut()), @@ -444,10 +551,10 @@ impl StrongRuleNode { fn ensure_child(&self, root: WeakRuleNode, source: StyleSource, - cascade_level: CascadeLevel) -> StrongRuleNode { + level: CascadeLevel) -> StrongRuleNode { let mut last = None; for child in self.get().iter_children() { - if child .get().cascade_level == cascade_level && + if child .get().level == level && child.get().source.as_ref().unwrap().ptr_equals(&source) { return child; } @@ -455,9 +562,9 @@ impl StrongRuleNode { } let mut node = Box::new(RuleNode::new(root, - self.clone(), - source.clone(), - cascade_level)); + self.clone(), + source.clone(), + level)); let new_ptr: *mut RuleNode = &mut *node; loop { @@ -521,7 +628,7 @@ impl StrongRuleNode { /// Get the importance that this rule node represents. pub fn importance(&self) -> Importance { - self.get().cascade_level.importance() + self.get().level.importance() } /// Get an iterator for this rule node and its ancestors. diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 47d33dd7269..a438211495a 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -8,7 +8,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; -use data::{ElementData, ElementStyles, StoredRestyleHint}; +use data::{ElementData, ElementStyles, RestyleKind, StoredRestyleHint}; use dom::{NodeInfo, TElement, TNode}; use matching::{MatchMethods, StyleSharingResult}; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF}; @@ -453,9 +453,6 @@ pub fn recalc_style_at<E, D>(traversal: &D, // Computes style, returning true if the inherited styles changed for this // element. -// -// FIXME(bholley): This should differentiate between matching and cascading, -// since we have separate bits for each now. fn compute_style<E, D>(_traversal: &D, traversal_data: &mut PerLevelTraversalData, context: &mut StyleContext<E>, @@ -466,63 +463,75 @@ fn compute_style<E, D>(_traversal: &D, { context.thread_local.statistics.elements_styled += 1; let shared_context = context.shared; - // Ensure the bloom filter is up to date. - let dom_depth = context.thread_local.bloom_filter - .insert_parents_recovering(element, traversal_data.current_dom_depth); - // Update the dom depth with the up-to-date dom depth. - // - // Note that this is always the same than the pre-existing depth, but it can - // change from unknown to known at this step. - traversal_data.current_dom_depth = Some(dom_depth); - - context.thread_local.bloom_filter.assert_complete(element); - - // Check to see whether we can share a style with someone. - let sharing_result = if element.parent_element().is_none() { - StyleSharingResult::CannotShare - } else { - unsafe { element.share_style_if_possible(&mut context.thread_local.style_sharing_candidate_cache, - shared_context, &mut data) } - }; + // TODO(emilio): Make cascade_input less expensive to compute in the cases + // we don't need to run selector matching. + let cascade_input = match data.restyle_kind() { + RestyleKind::MatchAndCascade => { + // Check to see whether we can share a style with someone. + let sharing_result = unsafe { + element.share_style_if_possible(&mut context.thread_local.style_sharing_candidate_cache, + shared_context, + &mut data) + }; - // Otherwise, match and cascade selectors. - match sharing_result { - StyleSharingResult::CannotShare => { - let match_results; - let shareable_element = { - // Perform the CSS selector matching. - context.thread_local.statistics.elements_matched += 1; - let filter = context.thread_local.bloom_filter.filter(); - match_results = element.match_element(context, Some(filter)); - if match_results.primary_is_shareable() { - Some(element) - } else { + match sharing_result { + StyleSharingResult::StyleWasShared(index) => { + context.thread_local.statistics.styles_shared += 1; + context.thread_local.style_sharing_candidate_cache.touch(index); None } - }; - let relations = match_results.relations; - - // Perform the CSS cascade. - unsafe { - let shareable = match_results.primary_is_shareable(); - element.cascade_node(context, &mut data, - element.parent_element(), - match_results.primary, - match_results.per_pseudo, - shareable); - } + StyleSharingResult::CannotShare => { + // Ensure the bloom filter is up to date. + let dom_depth = + context.thread_local.bloom_filter + .insert_parents_recovering(element, + traversal_data.current_dom_depth); + + // Update the dom depth with the up-to-date dom depth. + // + // Note that this is always the same than the pre-existing depth, + // but it can change from unknown to known at this step. + traversal_data.current_dom_depth = Some(dom_depth); - // Add ourselves to the LRU cache. - if let Some(element) = shareable_element { - context.thread_local - .style_sharing_candidate_cache - .insert_if_possible(&element, &data.styles().primary.values, relations); + context.thread_local.bloom_filter.assert_complete(element); + + // Perform the CSS selector matching. + context.thread_local.statistics.elements_matched += 1; + + let filter = context.thread_local.bloom_filter.filter(); + Some(element.match_element(context, Some(filter))) + } } } - StyleSharingResult::StyleWasShared(index) => { - context.thread_local.statistics.styles_shared += 1; - context.thread_local.style_sharing_candidate_cache.touch(index); + RestyleKind::CascadeWithReplacements(hint) => { + Some(element.cascade_with_replacements(hint, context, &mut data)) + } + RestyleKind::CascadeOnly => { + // TODO(emilio): Stop doing this work, and teach cascade_node about + // the current style instead. + Some(element.match_results_from_current_style(&*data)) + } + }; + + if let Some(match_results) = cascade_input { + // Perform the CSS cascade. + let shareable = match_results.primary_is_shareable(); + unsafe { + element.cascade_node(context, &mut data, + element.parent_element(), + match_results.primary, + match_results.per_pseudo, + shareable); + } + + if shareable { + // Add ourselves to the LRU cache. + context.thread_local + .style_sharing_candidate_cache + .insert_if_possible(&element, + &data.styles().primary.values, + match_results.relations); } } |