aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-02-02 03:00:22 -0800
committerGitHub <noreply@github.com>2017-02-02 03:00:22 -0800
commitfb7f65fc5711f41b991b72ed97d7286dd16301ed (patch)
tree4e53a5644ec98930df37c2e640bc9bd1a21935f0
parent8aa23b46db300210b67c16190c794bc9ee216f3f (diff)
parent2594cb9c33d93b1e63ed49816797887c287340db (diff)
downloadservo-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.rs103
-rw-r--r--components/style/matching.rs72
-rw-r--r--components/style/restyle_hints.rs8
-rw-r--r--components/style/rule_tree/mod.rs131
-rw-r--r--components/style/traversal.rs117
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);
}
}