aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/data.rs29
-rw-r--r--components/style/matching.rs460
-rw-r--r--components/style/rule_tree/mod.rs13
-rw-r--r--components/style/traversal.rs45
4 files changed, 246 insertions, 301 deletions
diff --git a/components/style/data.rs b/components/style/data.rs
index a6bcc344208..ddf9370418a 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -76,13 +76,6 @@ 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`.
@@ -97,19 +90,6 @@ 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 {
@@ -467,7 +447,14 @@ impl ElementData {
/// Gets a mutable reference to the element styles. Panic if the element has
/// never been styled.
pub fn styles_mut(&mut self) -> &mut ElementStyles {
- self.styles.as_mut().expect("Caling styles_mut() on unstyled ElementData")
+ self.styles.as_mut().expect("Calling styles_mut() on unstyled ElementData")
+ }
+
+ /// Borrows both styles and restyle mutably at the same time.
+ pub fn styles_and_restyle_mut(&mut self) -> (&mut ElementStyles,
+ Option<&mut RestyleData>) {
+ (self.styles.as_mut().unwrap(),
+ self.restyle.as_mut().map(|r| &mut **r))
}
/// Sets the computed element styles.
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 08d87f27b02..35daa395d27 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -13,7 +13,7 @@ use atomic_refcell::AtomicRefMut;
use cache::LRUCache;
use cascade_info::CascadeInfo;
use context::{SequentialTask, SharedStyleContext, StyleContext};
-use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoStyles};
+use data::{ComputedStyle, ElementData, ElementStyles};
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;
@@ -22,11 +22,11 @@ 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, AFFECTED_BY_STYLE_ATTRIBUTE};
use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use servo_config::opts;
use sink::ForgetfulSink;
-use std::collections::HashMap;
+use std::collections::hash_map::Entry;
use std::slice::IterMut;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
@@ -61,22 +61,21 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
flags
}
-/// The results of selector matching on an element.
+/// The results returned from running selector matching on an element.
pub struct MatchResults {
- /// The rule node reference that represents the rules matched by the
- /// element.
- pub primary: StrongRuleNode,
/// A set of style relations (different hints about what rules matched or
- /// could have matched).
- pub relations: StyleRelations,
- /// The results of selector-matching the pseudo-elements.
- pub per_pseudo: PseudoRuleNodes,
+ /// could have matched). This is necessary if the style will be shared.
+ /// If None, the style will not be shared.
+ pub primary_relations: Option<StyleRelations>,
+ /// Whether the rule nodes changed during selector matching.
+ pub rule_nodes_changed: bool,
}
impl MatchResults {
/// Returns true if the primary rule node is shareable with other nodes.
pub fn primary_is_shareable(&self) -> bool {
- relations_are_shareable(&self.relations)
+ self.primary_relations.as_ref()
+ .map_or(false, relations_are_shareable)
}
}
@@ -434,7 +433,7 @@ pub enum StyleSharingResult {
StyleWasShared(usize),
}
-/// Callers need to pass several boolean flags to cascade_node_pseudo_element.
+/// Callers need to pass several boolean flags to cascade_primary_or_pseudo.
/// We encapsulate them in this struct to avoid mixing them up.
///
/// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
@@ -444,18 +443,12 @@ struct CascadeBooleans {
}
trait PrivateMatchMethods: TElement {
- /// Actually cascades style for a node or a pseudo-element of a node.
- ///
- /// Note that animations only apply to nodes or ::before or ::after
- /// pseudo-elements.
- fn cascade_node_pseudo_element<'a>(&self,
- context: &StyleContext<Self>,
- parent_style: Option<&Arc<ComputedValues>>,
- old_style: Option<&Arc<ComputedValues>>,
- rule_node: &StrongRuleNode,
- possibly_expired_animations: &[PropertyAnimation],
- booleans: CascadeBooleans)
- -> Arc<ComputedValues> {
+ fn cascade_internal(&self,
+ context: &StyleContext<Self>,
+ primary_style: &ComputedStyle,
+ pseudo_style: &mut Option<(&PseudoElement, &mut ComputedStyle)>,
+ booleans: &CascadeBooleans)
+ -> Arc<ComputedValues> {
let shared_context = context.shared;
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
@@ -466,53 +459,118 @@ trait PrivateMatchMethods: TElement {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
}
- let this_style = match parent_style {
- Some(ref parent_style) => {
- cascade(shared_context.viewport_size,
- rule_node,
- Some(&***parent_style),
- &shared_context.default_computed_values,
- Some(&mut cascade_info),
- shared_context.error_reporter.clone(),
- cascade_flags)
- }
- None => {
- cascade(shared_context.viewport_size,
- rule_node,
- None,
- &shared_context.default_computed_values,
- Some(&mut cascade_info),
- shared_context.error_reporter.clone(),
- cascade_flags)
+ // Grab the rule node.
+ let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules;
+
+ // Grab the inherited values.
+ let parent_el;
+ let parent_data;
+ let inherited_values_ = if pseudo_style.is_none() {
+ parent_el = self.parent_element();
+ parent_data = parent_el.as_ref().and_then(|x| x.borrow_data());
+ let parent_values = parent_data.as_ref().map(|d| {
+ // Sometimes Gecko eagerly styles things without processing
+ // pending restyles first. In general we'd like to avoid this,
+ // but there can be good reasons (for example, needing to
+ // construct a frame for some small piece of newly-added
+ // content in order to do something specific with that frame,
+ // but not wanting to flush all of layout).
+ debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
+ d.styles().primary.values()
+ });
+
+ // Propagate the "can be fragmented" bit. It would be nice to
+ // encapsulate this better.
+ if let Some(ref p) = parent_values {
+ let can_be_fragmented =
+ p.is_multicol() || parent_el.unwrap().as_node().can_be_fragmented();
+ unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
}
- };
- cascade_info.finish(&self.as_node());
- let mut this_style = Arc::new(this_style);
+ parent_values
+ } else {
+ Some(primary_style.values())
+ };
+ let inherited_values = inherited_values_.map(|x| &**x);
+
+ // Invoke the cascade algorithm.
+ let values =
+ Arc::new(cascade(shared_context.viewport_size,
+ rule_node,
+ inherited_values,
+ &shared_context.default_computed_values,
+ Some(&mut cascade_info),
+ shared_context.error_reporter.clone(),
+ cascade_flags));
+ cascade_info.finish(&self.as_node());
+ values
+ }
+
+ /// Computes values and damage for the primary or pseudo style of an element,
+ /// setting them on the ElementData.
+ fn cascade_primary_or_pseudo<'a>(&self,
+ context: &StyleContext<Self>,
+ data: &mut ElementData,
+ pseudo: Option<&PseudoElement>,
+ possibly_expired_animations: &mut Vec<PropertyAnimation>,
+ booleans: CascadeBooleans) {
+ // Collect some values.
+ let shared_context = context.shared;
+ let (mut styles, restyle) = data.styles_and_restyle_mut();
+ let mut primary_style = &mut styles.primary;
+ let pseudos = &mut styles.pseudos;
+ let mut pseudo_style = pseudo.map(|p| (p, pseudos.get_mut(p).unwrap()));
+ let mut old_values =
+ pseudo_style.as_mut().map_or_else(|| primary_style.values.take(), |p| p.1.values.take());
+
+ // Compute the new values.
+ let mut new_values = self.cascade_internal(context, primary_style,
+ &mut pseudo_style, &booleans);
+
+ // Handle animations.
if booleans.animate {
+ if let Some(ref mut old) = old_values {
+ self.update_animations_for_cascade(shared_context, old,
+ possibly_expired_animations);
+ }
+
let new_animations_sender = &context.thread_local.new_animations_sender;
let this_opaque = self.as_node().opaque();
// Trigger any present animations if necessary.
animation::maybe_start_animations(&shared_context,
new_animations_sender,
- this_opaque, &this_style);
+ this_opaque, &new_values);
- // Trigger transitions if necessary. This will reset `this_style` back
+ // Trigger transitions if necessary. This will reset `new_values` back
// to its old value if it did trigger a transition.
- if let Some(ref style) = old_style {
+ if let Some(ref values) = old_values {
animation::start_transitions_if_applicable(
new_animations_sender,
this_opaque,
self.as_node().to_unsafe(),
- &**style,
- &mut this_style,
+ &**values,
+ &mut new_values,
&shared_context.timer,
&possibly_expired_animations);
}
}
- this_style
+ // Accumulate restyle damage unless we've already maxed it out.
+ if let Some(old) = old_values {
+ let restyle = restyle.expect("Old values but no restyle?");
+ if restyle.damage != RestyleDamage::rebuild_and_reflow() {
+ let d = self.compute_restyle_damage(Some(&old), &new_values, pseudo);
+ restyle.damage |= d;
+ }
+ }
+
+ // Set the new computed values.
+ if let Some((_, ref mut style)) = pseudo_style {
+ style.values = Some(new_values);
+ } else {
+ primary_style.values = Some(new_values);
+ }
}
fn update_animations_for_cascade(&self,
@@ -576,9 +634,10 @@ impl<E: TElement> PrivateMatchMethods for E {}
/// The public API that elements expose for selector matching.
pub trait MatchMethods : TElement {
- /// Runs selector matching of this element, and returns the result.
+ /// Runs selector matching to (re)compute rule nodes for this element.
fn match_element(&self,
- context: &mut StyleContext<Self>)
+ context: &mut StyleContext<Self>,
+ data: &mut ElementData)
-> MatchResults
{
let mut applicable_declarations =
@@ -588,6 +647,7 @@ pub trait MatchMethods : TElement {
let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None);
let mut flags = ElementSelectorFlags::empty();
+ let mut rule_nodes_changed = false;
// Compute the primary rule node.
let mut primary_relations =
@@ -599,10 +659,18 @@ pub trait MatchMethods : TElement {
&mut applicable_declarations,
&mut flags);
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
+ if !data.has_styles() {
+ data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
+ rule_nodes_changed = true;
+ } else if data.styles().primary.rules != primary_rule_node {
+ data.styles_mut().primary.rules = primary_rule_node;
+ rule_nodes_changed = true;
+ }
- // Compute the pseudo rule nodes.
- let mut per_pseudo: PseudoRuleNodes = HashMap::with_hasher(Default::default());
+ // Compute rule nodes for eagerly-cascaded pseudo-elements.
+ let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
+ let mut per_pseudo = &mut data.styles_mut().pseudos;
debug_assert!(applicable_declarations.is_empty());
let pseudo_animation_rules = self.get_animation_rules(Some(&pseudo));
stylist.push_applicable_declarations(self,
@@ -613,13 +681,35 @@ pub trait MatchMethods : TElement {
&mut flags);
if !applicable_declarations.is_empty() {
- let rule_node = compute_rule_node(context, &mut applicable_declarations);
- per_pseudo.insert(pseudo, rule_node);
+ let new_rules = compute_rule_node(context, &mut applicable_declarations);
+ match per_pseudo.entry(pseudo) {
+ Entry::Occupied(mut e) => {
+ if e.get().rules != new_rules {
+ e.get_mut().rules = new_rules;
+ rule_nodes_changed = true;
+ }
+ },
+ Entry::Vacant(e) => {
+ e.insert(ComputedStyle::new_partial(new_rules));
+ matches_different_pseudos = true;
+ }
+ }
+ } else if per_pseudo.remove(&pseudo).is_some() {
+ matches_different_pseudos = true;
}
});
+ if matches_different_pseudos {
+ rule_nodes_changed = true;
+ if let Some(r) = data.get_restyle_mut() {
+ // Any changes to the matched pseudo-elements trigger
+ // reconstruction.
+ r.damage |= RestyleDamage::rebuild_and_reflow();
+ }
+ }
+
// If we have any pseudo elements, indicate so in the primary StyleRelations.
- if !per_pseudo.is_empty() {
+ if !data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
@@ -640,63 +730,45 @@ pub trait MatchMethods : TElement {
}
MatchResults {
- primary: primary_rule_node,
- relations: primary_relations,
- per_pseudo: per_pseudo,
- }
- }
-
- /// 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(),
+ primary_relations: Some(primary_relations),
+ rule_nodes_changed: rule_nodes_changed,
}
-
}
/// Updates the rule nodes without re-running selector matching, using just
- /// the rule tree.
+ /// the rule tree. Returns true if the rule nodes changed.
fn cascade_with_replacements(&self,
hint: RestyleHint,
context: &StyleContext<Self>,
data: &mut AtomicRefMut<ElementData>)
- -> MatchResults {
- let mut rule_node = data.styles().primary.rules.clone();
+ -> bool {
+ let primary_rules = &mut data.styles_mut().primary.rules;
+ let mut rule_node_changed = false;
if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute();
- rule_node = context.shared.stylist.rule_tree
+ let new_node = context.shared.stylist.rule_tree
.update_rule_at_level(CascadeLevel::StyleAttributeNormal,
style_attribute,
- rule_node);
+ primary_rules);
+ if let Some(n) = new_node {
+ *primary_rules = n;
+ rule_node_changed = true;
+ }
- rule_node = context.shared.stylist.rule_tree
+ let new_node = context.shared.stylist.rule_tree
.update_rule_at_level(CascadeLevel::StyleAttributeImportant,
style_attribute,
- rule_node);
+ primary_rules);
+ if let Some(n) = new_node {
+ *primary_rules = n;
+ rule_node_changed = true;
+ }
}
- 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(),
- }
+ // The per-pseudo rule nodes never change in this path.
+ rule_node_changed
}
/// Attempts to share a style with another node. This method is unsafe
@@ -867,165 +939,53 @@ pub trait MatchMethods : TElement {
}
}
- /// Given the results of selector matching, run the CSS cascade and style
- /// the node, potentially starting any new transitions or animations.
- fn cascade_node(&self,
- context: &StyleContext<Self>,
- mut data: &mut AtomicRefMut<ElementData>,
- parent: Option<Self>,
- primary_rule_node: StrongRuleNode,
- pseudo_rule_nodes: PseudoRuleNodes,
- primary_is_shareable: bool)
+ /// Run the CSS cascade and compute values for the element, potentially
+ /// starting any new transitions or animations.
+ fn cascade_element(&self,
+ context: &StyleContext<Self>,
+ mut data: &mut AtomicRefMut<ElementData>,
+ primary_is_shareable: bool)
{
- // Get our parent's style.
- let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap());
- let parent_style = parent_data.as_ref().map(|d| {
- // Sometimes Gecko eagerly styles things without processing pending
- // restyles first. In general we'd like to avoid this, but there can
- // be good reasons (for example, needing to construct a frame for
- // some small piece of newly-added content in order to do something
- // specific with that frame, but not wanting to flush all of
- // layout).
- debug_assert!(cfg!(feature = "gecko") || d.has_current_styles());
- d.styles().primary.values()
- });
-
- let mut new_styles;
let mut possibly_expired_animations = vec![];
- let damage = {
- debug_assert!(!data.has_current_styles());
- let (old_primary, old_pseudos) = match data.get_styles_mut() {
- None => (None, None),
- Some(previous) => {
- // Update animations before the cascade. This may modify the
- // value of the old primary style.
- self.update_animations_for_cascade(&context.shared,
- previous.primary.values_mut(),
- &mut possibly_expired_animations);
- (Some(previous.primary.values()), Some(&mut previous.pseudos))
- }
- };
-
- let new_style =
- self.cascade_node_pseudo_element(context,
- parent_style,
- old_primary,
- &primary_rule_node,
- &possibly_expired_animations,
- CascadeBooleans {
- shareable: primary_is_shareable,
- animate: true,
- });
-
- let primary = ComputedStyle::new(primary_rule_node, new_style);
- new_styles = ElementStyles::new(primary);
-
- let damage =
- self.compute_damage_and_cascade_pseudos(old_primary,
- old_pseudos,
- &new_styles.primary.values(),
- &mut new_styles.pseudos,
- context,
- pseudo_rule_nodes,
- &mut possibly_expired_animations);
-
- unsafe {
- self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
- p.as_node().can_be_fragmented() ||
- parent_style.unwrap().is_multicol()
- }));
+ // Cascade the primary style.
+ self.cascade_primary_or_pseudo(context, data, None,
+ &mut possibly_expired_animations,
+ CascadeBooleans {
+ shareable: primary_is_shareable,
+ animate: true,
+ });
+
+ // Check whether the primary style is display:none.
+ let display_none = data.styles().primary.values().get_box().clone_display() ==
+ display::T::none;
+
+ // Cascade each pseudo-element.
+ //
+ // Note that we've already set up the map of matching pseudo-elements
+ // in match_element (and handled the damage implications of changing
+ // which pseudos match), so now we can just iterate the map. This does
+ // mean collecting the keys, so that the borrow checker will let us pass
+ // the mutable |data| to the inner cascade function.
+ let matched_pseudos: Vec<PseudoElement> =
+ data.styles().pseudos.keys().cloned().collect();
+ for pseudo in matched_pseudos {
+ // If the new primary style is display:none, we don't need pseudo
+ // styles, but we still need to clear any stale values.
+ if display_none {
+ data.styles_mut().pseudos.get_mut(&pseudo).unwrap().values = None;
+ continue;
}
- damage
- };
-
- if data.has_styles() {
- data.restyle_mut().damage |= damage;
+ // Only ::before and ::after are animatable.
+ let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
+ self.cascade_primary_or_pseudo(context, data, Some(&pseudo),
+ &mut possibly_expired_animations,
+ CascadeBooleans {
+ shareable: false,
+ animate: animate,
+ });
}
- data.set_styles(new_styles);
- }
-
- /// Given the old and new styling results, compute the final restyle damage.
- fn compute_damage_and_cascade_pseudos(
- &self,
- old_primary: Option<&Arc<ComputedValues>>,
- mut old_pseudos: Option<&mut PseudoStyles>,
- new_primary: &Arc<ComputedValues>,
- new_pseudos: &mut PseudoStyles,
- context: &StyleContext<Self>,
- mut pseudo_rule_nodes: PseudoRuleNodes,
- possibly_expired_animations: &mut Vec<PropertyAnimation>)
- -> RestyleDamage
- {
- // Compute the damage and sum up the damage related to pseudo-elements.
- let mut damage =
- self.compute_restyle_damage(old_primary, new_primary, None);
-
- // If the new style is display:none, we don't need pseudo-elements styles.
- if new_primary.get_box().clone_display() == display::T::none {
- return damage;
- }
-
- let rebuild_and_reflow = RestyleDamage::rebuild_and_reflow();
-
- debug_assert!(new_pseudos.is_empty());
- <Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- let maybe_rule_node = pseudo_rule_nodes.remove(&pseudo);
-
- // Grab the old pseudo style for analysis.
- let mut maybe_old_pseudo_style =
- old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
-
- 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 {
- if let Some(ref mut old_pseudo_style) = maybe_old_pseudo_style {
- // Update animations before the cascade. This may modify
- // the value of old_pseudo_style.
- self.update_animations_for_cascade(&context.shared,
- old_pseudo_style.values_mut(),
- possibly_expired_animations);
- }
- }
-
- let new_pseudo_values =
- self.cascade_node_pseudo_element(context,
- Some(new_primary),
- maybe_old_pseudo_style.as_ref()
- .map(|s| s.values()),
- &new_rule_node,
- &possibly_expired_animations,
- CascadeBooleans {
- shareable: false,
- animate: animate,
- });
-
- // Compute restyle damage unless we've already maxed it out.
- if damage != rebuild_and_reflow {
- damage = damage | match maybe_old_pseudo_style {
- None => rebuild_and_reflow,
- Some(ref old) => self.compute_restyle_damage(Some(old.values()),
- &new_pseudo_values,
- Some(&pseudo)),
- };
- }
-
- // Insert the new entry into the map.
- let new_pseudo_style = ComputedStyle::new(new_rule_node, new_pseudo_values);
- let existing = new_pseudos.insert(pseudo, new_pseudo_style);
- debug_assert!(existing.is_none());
- } else {
- if maybe_old_pseudo_style.is_some() {
- damage = rebuild_and_reflow;
- }
- }
- });
-
- damage
}
}
diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs
index c93ff94a841..77cf7d17697 100644
--- a/components/style/rule_tree/mod.rs
+++ b/components/style/rule_tree/mod.rs
@@ -183,12 +183,13 @@ impl RuleTree {
/// Replaces a rule in a given level (if present) for another rule.
///
- /// Returns the resulting node that represents the new path.
+ /// Returns the resulting node that represents the new path, or None if
+ /// the old path is still valid.
pub fn update_rule_at_level(&self,
level: CascadeLevel,
pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
- path: StrongRuleNode)
- -> StrongRuleNode {
+ path: &StrongRuleNode)
+ -> Option<StrongRuleNode> {
debug_assert!(level.is_unique_per_element());
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of
// the refcount churn.
@@ -231,7 +232,7 @@ impl RuleTree {
if is_here_already {
debug!("Picking the fast path in rule replacement");
- return path;
+ return None;
}
}
current = current.parent().unwrap().clone();
@@ -262,7 +263,7 @@ impl RuleTree {
// Now the rule is in the relevant place, push the children as
// necessary.
- self.insert_ordered_rules_from(current, children.into_iter().rev())
+ Some(self.insert_ordered_rules_from(current, children.into_iter().rev()))
}
}
@@ -501,7 +502,7 @@ struct WeakRuleNode {
}
/// A strong reference to a rule node.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
pub struct StrongRuleNode {
ptr: *mut RuleNode,
}
diff --git a/components/style/traversal.rs b/components/style/traversal.rs
index 141637b8471..f49a2042ec7 100644
--- a/components/style/traversal.rs
+++ b/components/style/traversal.rs
@@ -10,7 +10,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint};
use dom::{NodeInfo, TElement, TNode};
-use matching::MatchMethods;
+use matching::{MatchMethods, MatchResults};
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
use selector_parser::RestyleDamage;
use servo_config::opts;
@@ -339,12 +339,9 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>, element: E, ensur
}
// Compute our style.
- let match_results = element.match_element(context);
- let shareable = match_results.primary_is_shareable();
- element.cascade_node(context, &mut data, element.parent_element(),
- match_results.primary,
- match_results.per_pseudo,
- shareable);
+ let match_results = element.match_element(context, &mut data);
+ element.cascade_element(context, &mut data,
+ match_results.primary_is_shareable());
// Conservatively mark us as having dirty descendants, since there might
// be other unstyled siblings we miss when walking straight up the parent
@@ -504,8 +501,6 @@ fn compute_style<E, D>(_traversal: &D,
}
}
- // TODO(emilio): Make cascade_input less expensive to compute in the cases
- // we don't need to run selector matching.
let match_results = match kind {
MatchAndCascade => {
// Ensure the bloom filter is up to date.
@@ -522,38 +517,40 @@ fn compute_style<E, D>(_traversal: &D,
context.thread_local.bloom_filter.assert_complete(element);
- // Perform the CSS selector matching.
- context.thread_local.statistics.elements_matched += 1;
- element.match_element(context)
+ // Perform CSS selector matching.
+ context.thread_local.statistics.elements_matched += 1;
+ element.match_element(context, &mut data)
}
CascadeWithReplacements(hint) => {
- element.cascade_with_replacements(hint, context, &mut data)
+ let rule_nodes_changed =
+ element.cascade_with_replacements(hint, context, &mut data);
+ MatchResults {
+ primary_relations: None,
+ rule_nodes_changed: rule_nodes_changed,
+ }
}
CascadeOnly => {
- // TODO(emilio): Stop doing this work, and teach cascade_node about
- // the current style instead.
- element.match_results_from_current_style(&*data)
+ MatchResults {
+ primary_relations: None,
+ rule_nodes_changed: false,
+ }
}
};
- // Perform the CSS cascade.
+ // Cascade properties and compute values.
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);
+ element.cascade_element(context, &mut data, shareable);
}
+ // If the style is shareable, add it to the LRU cache.
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);
+ match_results.primary_relations.unwrap());
}
}