diff options
-rw-r--r-- | components/style/data.rs | 29 | ||||
-rw-r--r-- | components/style/matching.rs | 460 | ||||
-rw-r--r-- | components/style/rule_tree/mod.rs | 13 | ||||
-rw-r--r-- | components/style/traversal.rs | 45 |
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()); } } |