diff options
Diffstat (limited to 'components/style/rule_tree/mod.rs')
-rw-r--r-- | components/style/rule_tree/mod.rs | 131 |
1 files changed, 119 insertions, 12 deletions
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. |