From f9de1dedd870b538531ce9c1015517fc3aae1d15 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 5 Apr 2017 17:52:17 -0700 Subject: [PATCH 1/4] Factor out the application of selector flags into a helper. MozReview-Commit-ID: 2FZxnBxvaOb --- components/style/matching.rs | 98 ++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/components/style/matching.rs b/components/style/matching.rs index 4bc5a183167..9a8a45fe2e0 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -822,52 +822,9 @@ pub trait MatchMethods : TElement { let animation_rules = self.get_animation_rules(None); let mut rule_nodes_changed = false; - // TODO(emilio): This is somewhat inefficient, because of a variety of - // reasons: - // - // * It doesn't coalesce flags. - // * It doesn't look at flags already sent in a task for the main - // thread to process. - // * It doesn't take advantage of us knowing that the traversal is - // sequential. - // - // I suspect (need to measure!) that we don't use to set flags on - // a lot of different elements, but we could end up posting the same - // flag over and over with this approach. - // - // If the number of elements is low, perhaps a small cache with the - // flags already sent would be appropriate. - // - // The sequential task business for this is kind of sad :(. - // - // Anyway, let's do the obvious thing for now. let tasks = &mut context.thread_local.tasks; let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { - // Apply the selector flags. - let self_flags = flags.for_self(); - if !self_flags.is_empty() { - if element == self { - unsafe { element.set_selector_flags(self_flags); } - } else { - if !element.has_selector_flags(self_flags) { - let task = - SequentialTask::set_selector_flags(element.clone(), - self_flags); - tasks.push(task); - } - } - } - let parent_flags = flags.for_parent(); - if !parent_flags.is_empty() { - if let Some(p) = element.parent_element() { - // Avoid the overhead of the SequentialTask if the flags are - // already set. - if !p.has_selector_flags(parent_flags) { - let task = SequentialTask::set_selector_flags(p, parent_flags); - tasks.push(task); - } - } - } + self.apply_selector_flags(tasks, element, flags); }; // Borrow the stuff we need here so the borrow checker doesn't get mad @@ -949,6 +906,59 @@ pub trait MatchMethods : TElement { } } + /// Applies selector flags to an element, deferring mutations of the parent + /// until after the traversal. + /// + /// TODO(emilio): This is somewhat inefficient, because of a variety of + /// reasons: + /// + /// * It doesn't coalesce flags. + /// * It doesn't look at flags already sent in a task for the main + /// thread to process. + /// * It doesn't take advantage of us knowing that the traversal is + /// sequential. + /// + /// I suspect (need to measure!) that we don't use to set flags on + /// a lot of different elements, but we could end up posting the same + /// flag over and over with this approach. + /// + /// If the number of elements is low, perhaps a small cache with the + /// flags already sent would be appropriate. + /// + /// The sequential task business for this is kind of sad :(. + /// + /// Anyway, let's do the obvious thing for now. + fn apply_selector_flags(&self, + tasks: &mut Vec>, + element: &Self, + flags: ElementSelectorFlags) { + // Apply the selector flags. + let self_flags = flags.for_self(); + if !self_flags.is_empty() { + if element == self { + unsafe { element.set_selector_flags(self_flags); } + } else { + if !element.has_selector_flags(self_flags) { + let task = + SequentialTask::set_selector_flags(element.clone(), + self_flags); + tasks.push(task); + } + } + } + let parent_flags = flags.for_parent(); + if !parent_flags.is_empty() { + if let Some(p) = element.parent_element() { + // Avoid the overhead of the SequentialTask if the flags are + // already set. + if !p.has_selector_flags(parent_flags) { + let task = SequentialTask::set_selector_flags(p, parent_flags); + tasks.push(task); + } + } + } + } + /// Updates the rule nodes without re-running selector matching, using just /// the rule tree. Returns true if the rule nodes changed. fn cascade_with_replacements(&self, From 42f5aea76a58b2885dfdf3a59067bc87bb7c784a Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 5 Apr 2017 18:30:46 -0700 Subject: [PATCH 2/4] Remove shareable boolean from ComputedValues and simplify code. This code is all vestigial at this point, presumably after a refactoring. MozReview-Commit-ID: CV0lKMStq13 --- components/style/matching.rs | 36 +++++-------------- components/style/properties/gecko.mako.rs | 5 --- .../style/properties/properties.mako.rs | 13 ++----- components/style/traversal.rs | 12 +++---- 4 files changed, 14 insertions(+), 52 deletions(-) diff --git a/components/style/matching.rs b/components/style/matching.rs index 9a8a45fe2e0..ed77ae54557 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -15,7 +15,7 @@ use cascade_info::CascadeInfo; use context::{SequentialTask, SharedStyleContext, StyleContext}; use data::{ComputedStyle, ElementData, ElementStyles, RestyleData}; use dom::{AnimationRules, SendElement, TElement, TNode}; -use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; +use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::longhands::display::computed_value as display; use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RestyleHint}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode}; @@ -430,15 +430,6 @@ pub enum StyleSharingResult { StyleWasShared(usize), } -/// 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? -struct CascadeBooleans { - shareable: bool, - animate: bool, -} - trait PrivateMatchMethods: TElement { /// Returns the closest parent element that doesn't have a display: contents /// style (and thus generates a box). @@ -543,13 +534,9 @@ trait PrivateMatchMethods: TElement { fn cascade_internal(&self, context: &StyleContext, primary_style: &ComputedStyle, - pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>, - booleans: &CascadeBooleans) + pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>) -> Arc { let mut cascade_flags = CascadeFlags::empty(); - if booleans.shareable { - cascade_flags.insert(SHAREABLE) - } if self.skip_root_and_item_based_display_fixup() { cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) } @@ -566,7 +553,7 @@ trait PrivateMatchMethods: TElement { data: &mut ElementData, pseudo: Option<&PseudoElement>, possibly_expired_animations: &mut Vec, - booleans: CascadeBooleans) { + animate: bool) { // Collect some values. let (mut styles, restyle) = data.styles_and_restyle_mut(); let mut primary_style = &mut styles.primary; @@ -577,10 +564,10 @@ trait PrivateMatchMethods: TElement { // Compute the new values. let mut new_values = self.cascade_internal(context, primary_style, - &pseudo_style, &booleans); + &pseudo_style); // Handle animations. - if booleans.animate { + if animate { self.process_animations(context, &mut old_values, &mut new_values, @@ -1177,18 +1164,14 @@ pub trait MatchMethods : TElement { /// starting any new transitions or animations. fn cascade_element(&self, context: &mut StyleContext, - mut data: &mut AtomicRefMut, - primary_is_shareable: bool) + mut data: &mut AtomicRefMut) { let mut possibly_expired_animations = vec![]; // Cascade the primary style. self.cascade_primary_or_pseudo(context, data, None, &mut possibly_expired_animations, - CascadeBooleans { - shareable: primary_is_shareable, - animate: true, - }); + /* animate = */ true); // Check whether the primary style is display:none. let display_none = data.styles().primary.values().get_box().clone_display() == @@ -1214,10 +1197,7 @@ pub trait MatchMethods : TElement { let animate = pseudo.is_before_or_after(); self.cascade_primary_or_pseudo(context, data, Some(&pseudo), &mut possibly_expired_animations, - CascadeBooleans { - shareable: false, - animate: animate, - }); + animate); } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 2d55fd3c475..2d8424ff2e2 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -77,7 +77,6 @@ pub struct ComputedValues { % endfor custom_properties: Option>, - shareable: bool, pub writing_mode: WritingMode, pub root_font_size: Au, pub font_size_keyword: Option, @@ -87,7 +86,6 @@ impl ComputedValues { pub fn inherit_from(parent: &Self, default: &Self) -> Arc { Arc::new(ComputedValues { custom_properties: parent.custom_properties.clone(), - shareable: parent.shareable, writing_mode: parent.writing_mode, root_font_size: parent.root_font_size, font_size_keyword: parent.font_size_keyword, @@ -102,7 +100,6 @@ impl ComputedValues { } pub fn new(custom_properties: Option>, - shareable: bool, writing_mode: WritingMode, root_font_size: Au, font_size_keyword: Option, @@ -112,7 +109,6 @@ impl ComputedValues { ) -> Self { ComputedValues { custom_properties: custom_properties, - shareable: shareable, writing_mode: writing_mode, root_font_size: root_font_size, font_size_keyword: font_size_keyword, @@ -125,7 +121,6 @@ impl ComputedValues { pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc { Arc::new(ComputedValues { custom_properties: None, - shareable: true, writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious root_font_size: longhands::font_size::get_initial_value(), // FIXME(bz): Also seems dubious? font_size_keyword: Some(Default::default()), diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index d00d4b38119..88121aa32f3 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1542,7 +1542,6 @@ pub struct ComputedValues { ${style_struct.ident}: Arc, % endfor custom_properties: Option>, - shareable: bool, /// The writing mode of this computed values struct. pub writing_mode: WritingMode, /// The root element's computed font size. @@ -1555,7 +1554,6 @@ pub struct ComputedValues { impl ComputedValues { /// Construct a `ComputedValues` instance. pub fn new(custom_properties: Option>, - shareable: bool, writing_mode: WritingMode, root_font_size: Au, font_size_keyword: Option, @@ -1565,7 +1563,6 @@ impl ComputedValues { ) -> Self { ComputedValues { custom_properties: custom_properties, - shareable: shareable, writing_mode: writing_mode, root_font_size: root_font_size, font_size_keyword: font_size_keyword, @@ -1889,7 +1886,6 @@ mod lazy_static_module { }), % endfor custom_properties: None, - shareable: true, writing_mode: WritingMode::empty(), root_font_size: longhands::font_size::get_initial_value(), font_size_keyword: Some(Default::default()), @@ -1918,15 +1914,12 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [ bitflags! { /// A set of flags to tweak the behavior of the `cascade` function. pub flags CascadeFlags: u8 { - /// Whether the `ComputedValues` structure to be constructed should be - /// considered shareable. - const SHAREABLE = 0x01, /// Whether to inherit all styles from the parent. If this flag is not /// present, non-inherited styles are reset to their initial values. - const INHERIT_ALL = 0x02, + const INHERIT_ALL = 0x01, /// Whether to skip any root element and flex/grid item display style /// fixup. - const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 0x04, + const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 0x02, } } @@ -2033,7 +2026,6 @@ pub fn apply_declarations<'a, F, I>(device: &Device, let starting_style = if !flags.contains(INHERIT_ALL) { ComputedValues::new(custom_properties, - flags.contains(SHAREABLE), WritingMode::empty(), inherited_style.root_font_size, inherited_style.font_size_keyword, @@ -2047,7 +2039,6 @@ pub fn apply_declarations<'a, F, I>(device: &Device, ) } else { ComputedValues::new(custom_properties, - flags.contains(SHAREABLE), WritingMode::empty(), inherited_style.root_font_size, inherited_style.font_size_keyword, diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 9712b3d84af..a26404792d5 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -395,9 +395,8 @@ fn resolve_style_internal(context: &mut StyleContext, } // Compute our style. - let match_results = element.match_element(context, &mut data); - element.cascade_element(context, &mut data, - match_results.primary_is_shareable()); + element.match_element(context, &mut data); + element.cascade_element(context, &mut data); // Conservatively mark us as having dirty descendants, since there might // be other unstyled siblings we miss when walking straight up the parent @@ -625,13 +624,10 @@ fn compute_style(_traversal: &D, }; // Cascade properties and compute values. - let shareable = match_results.primary_is_shareable(); - unsafe { - element.cascade_element(context, &mut data, shareable); - } + element.cascade_element(context, &mut data); // If the style is shareable, add it to the LRU cache. - if shareable { + if match_results.primary_is_shareable() { context.thread_local .style_sharing_candidate_cache .insert_if_possible(&element, From 19743a67ba8f8f48bbc26fb4a809473a0474d32b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 5 Apr 2017 18:55:09 -0700 Subject: [PATCH 3/4] Rearrange compute_style and eliminate MatchResults. This simplifies things by avoiding the computation of MatchResults when we don't need them. We continue to compute rule_nodes_changed, even though we don't use it for the moment, since we will need it soon for various incremental restyle optimizations. MozReview-Commit-ID: 4qsUYaD5Bs2 --- components/style/matching.rs | 27 +++------------------ components/style/traversal.rs | 44 ++++++++++++++++------------------- 2 files changed, 23 insertions(+), 48 deletions(-) diff --git a/components/style/matching.rs b/components/style/matching.rs index ed77ae54557..a6dd30c40e5 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -30,7 +30,7 @@ use stylist::ApplicableDeclarationBlock; /// Determines the amount of relations where we're going to share style. #[inline] -fn relations_are_shareable(relations: &StyleRelations) -> bool { +pub fn relations_are_shareable(relations: &StyleRelations) -> bool { use selectors::matching::*; !relations.intersects(AFFECTED_BY_ID_SELECTOR | AFFECTED_BY_PSEUDO_ELEMENTS | AFFECTED_BY_STATE | @@ -58,24 +58,6 @@ fn create_common_style_affecting_attributes_from_element(element: & flags } -/// The results returned from running selector matching on an element. -pub struct MatchResults { - /// A set of style relations (different hints about what rules matched or - /// could have matched). This is necessary if the style will be shared. - /// If None, the style will not be shared. - pub primary_relations: Option, - /// 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 { - self.primary_relations.as_ref() - .map_or(false, relations_are_shareable) - } -} - /// Information regarding a style sharing candidate. /// /// Note that this information is stored in TLS and cleared after the traversal, @@ -799,7 +781,7 @@ pub trait MatchMethods : TElement { fn match_element(&self, context: &mut StyleContext, data: &mut ElementData) - -> MatchResults + -> (StyleRelations, bool) { let mut applicable_declarations = Vec::::with_capacity(16); @@ -887,10 +869,7 @@ pub trait MatchMethods : TElement { primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS; } - MatchResults { - primary_relations: Some(primary_relations), - rule_nodes_changed: rule_nodes_changed, - } + (primary_relations, rule_nodes_changed) } /// Applies selector flags to an element, deferring mutations of the parent diff --git a/components/style/traversal.rs b/components/style/traversal.rs index a26404792d5..09792333d8d 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -10,7 +10,8 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use data::{ElementData, ElementStyles, StoredRestyleHint}; use dom::{DirtyDescendants, NodeInfo, TElement, TNode}; -use matching::{MatchMethods, MatchResults}; +use matching::MatchMethods; +use matching::relations_are_shareable; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF}; use selector_parser::RestyleDamage; use servo_config::opts; @@ -586,7 +587,7 @@ fn compute_style(_traversal: &D, } } - let match_results = match kind { + match kind { MatchAndCascade => { // Ensure the bloom filter is up to date. let dom_depth = @@ -605,35 +606,30 @@ fn compute_style(_traversal: &D, // Perform CSS selector matching. context.thread_local.statistics.elements_matched += 1; - element.match_element(context, &mut data) + let (primary_relations, _rule_nodes_changed) = + element.match_element(context, &mut data); + + // Cascade properties and compute values. + element.cascade_element(context, &mut data); + + // If the style is shareable, add it to the LRU cache. + if relations_are_shareable(&primary_relations) { + context.thread_local + .style_sharing_candidate_cache + .insert_if_possible(&element, + data.styles().primary.values(), + primary_relations); + } } CascadeWithReplacements(hint) => { - let rule_nodes_changed = + let _rule_nodes_changed = element.cascade_with_replacements(hint, context, &mut data); - MatchResults { - primary_relations: None, - rule_nodes_changed: rule_nodes_changed, - } + element.cascade_element(context, &mut data); } CascadeOnly => { - MatchResults { - primary_relations: None, - rule_nodes_changed: false, - } + element.cascade_element(context, &mut data); } }; - - // Cascade properties and compute values. - element.cascade_element(context, &mut data); - - // If the style is shareable, add it to the LRU cache. - if match_results.primary_is_shareable() { - context.thread_local - .style_sharing_candidate_cache - .insert_if_possible(&element, - data.styles().primary.values(), - match_results.primary_relations.unwrap()); - } } fn preprocess_children(traversal: &D, From 8acb4ed87c2763d7f87cbca5d20b6ed237358600 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 5 Apr 2017 19:17:59 -0700 Subject: [PATCH 4/4] Match eager pseudos after the primary cascade. This is necessary in order to make the computation of eager pseudos depend on the primary ComputedValues, which we want to do for ::first-letter/::first-line. This also fixes a bug where the behavior of EagerPseudoStyles::is_empty was reversed in both the implementation and the callsite. MozReview-Commit-ID: EXBxclyHWXu --- components/style/data.rs | 2 +- components/style/matching.rs | 185 +++++++++++++++++++++++----------- components/style/traversal.rs | 28 ++--- 3 files changed, 132 insertions(+), 83 deletions(-) diff --git a/components/style/data.rs b/components/style/data.rs index 9eb831e07a1..46c1c621825 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -80,7 +80,7 @@ pub struct EagerPseudoStyles(Option]>>); impl EagerPseudoStyles { /// Returns whether there are any pseudo styles. pub fn is_empty(&self) -> bool { - self.0.is_some() + self.0.is_none() } /// Returns a reference to the style for a given eager pseudo, if it exists. diff --git a/components/style/matching.rs b/components/style/matching.rs index a6dd30c40e5..49c521e87fd 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -30,7 +30,7 @@ use stylist::ApplicableDeclarationBlock; /// Determines the amount of relations where we're going to share style. #[inline] -pub fn relations_are_shareable(relations: &StyleRelations) -> bool { +fn relations_are_shareable(relations: &StyleRelations) -> bool { use selectors::matching::*; !relations.intersects(AFFECTED_BY_ID_SELECTOR | AFFECTED_BY_PSEUDO_ELEMENTS | AFFECTED_BY_STATE | @@ -775,13 +775,71 @@ fn compute_rule_node(rule_tree: &RuleTree, impl PrivateMatchMethods for E {} +/// Controls whether the style sharing cache is used. +#[derive(Clone, Copy, PartialEq)] +pub enum StyleSharingBehavior { + /// Style sharing allowed. + Allow, + /// Style sharing disallowed. + Disallow, +} + /// The public API that elements expose for selector matching. pub trait MatchMethods : TElement { - /// Runs selector matching to (re)compute rule nodes for this element. - fn match_element(&self, + /// Performs selector matching and property cascading on an element and its eager pseudos. + fn match_and_cascade(&self, + context: &mut StyleContext, + data: &mut ElementData, + sharing: StyleSharingBehavior) + { + // Perform selector matching for the primary style. + let mut primary_relations = StyleRelations::empty(); + let _rule_node_changed = self.match_primary(context, data, &mut primary_relations); + + // Cascade properties and compute primary values. + let mut expired = vec![]; + self.cascade_primary(context, data, &mut expired); + + // Match and cascade eager pseudo-elements. + if !data.styles().is_display_none() { + let _pseudo_rule_nodes_changed = + self.match_pseudos(context, data); + self.cascade_pseudos(context, data, &mut expired); + } + + // If we have any pseudo elements, indicate so in the primary StyleRelations. + if !data.styles().pseudos.is_empty() { + primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS; + } + + // If the style is shareable, add it to the LRU cache. + if sharing == StyleSharingBehavior::Allow && relations_are_shareable(&primary_relations) { + context.thread_local + .style_sharing_candidate_cache + .insert_if_possible(self, + data.styles().primary.values(), + primary_relations); + } + } + + /// Performs the cascade, without matching. + fn cascade_primary_and_pseudos(&self, + context: &mut StyleContext, + mut data: &mut ElementData) + { + let mut possibly_expired_animations = vec![]; + self.cascade_primary(context, &mut data, &mut possibly_expired_animations); + self.cascade_pseudos(context, &mut data, &mut possibly_expired_animations); + } + + /// Runs selector matching to (re)compute the primary rule node for this element. + /// + /// Returns whether the primary rule node changed. + fn match_primary(&self, context: &mut StyleContext, - data: &mut ElementData) - -> (StyleRelations, bool) + data: &mut ElementData, + relations: &mut StyleRelations) + -> bool { let mut applicable_declarations = Vec::::with_capacity(16); @@ -790,6 +848,49 @@ pub trait MatchMethods : TElement { let style_attribute = self.style_attribute(); let animation_rules = self.get_animation_rules(None); let mut rule_nodes_changed = false; + let bloom = context.thread_local.bloom_filter.filter(); + + let tasks = &mut context.thread_local.tasks; + let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { + self.apply_selector_flags(tasks, element, flags); + }; + + // Compute the primary rule node. + *relations = stylist.push_applicable_declarations(self, + Some(bloom), + style_attribute, + animation_rules, + None, + &context.shared.guards, + &mut applicable_declarations, + &mut set_selector_flags); + + let primary_rule_node = + compute_rule_node::(&stylist.rule_tree, &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; + } + + rule_nodes_changed + } + + /// Runs selector matching to (re)compute eager pseudo-element rule nodes for this + /// element. + /// + /// Returns whether any of the pseudo rule nodes changed (including, but not + /// limited to, cases where we match different pseudos altogether). + fn match_pseudos(&self, + context: &mut StyleContext, + data: &mut ElementData) + -> bool + { + let mut applicable_declarations = + Vec::::with_capacity(16); + let mut rule_nodes_changed = false; let tasks = &mut context.thread_local.tasks; let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { @@ -798,31 +899,11 @@ pub trait MatchMethods : TElement { // Borrow the stuff we need here so the borrow checker doesn't get mad // at us later in the closure. + let stylist = &context.shared.stylist; let guards = &context.shared.guards; - let rule_tree = &context.shared.stylist.rule_tree; + let rule_tree = &stylist.rule_tree; let bloom_filter = context.thread_local.bloom_filter.filter(); - // Compute the primary rule node. - let mut primary_relations = - stylist.push_applicable_declarations(self, - Some(bloom_filter), - style_attribute, - animation_rules, - None, - guards, - &mut applicable_declarations, - &mut set_selector_flags); - - let primary_rule_node = - compute_rule_node::(rule_tree, &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 rule nodes for eagerly-cascaded pseudo-elements. let mut matches_different_pseudos = false; SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| { @@ -864,12 +945,7 @@ pub trait MatchMethods : TElement { } } - // If we have any pseudo elements, indicate so in the primary StyleRelations. - if data.styles().pseudos.is_empty() { - primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS; - } - - (primary_relations, rule_nodes_changed) + rule_nodes_changed } /// Applies selector flags to an element, deferring mutations of the parent @@ -1139,44 +1215,33 @@ pub trait MatchMethods : TElement { } } - /// Run the CSS cascade and compute values for the element, potentially - /// starting any new transitions or animations. - fn cascade_element(&self, + /// Performs the cascade for the element's primary style. + fn cascade_primary(&self, context: &mut StyleContext, - mut data: &mut AtomicRefMut) + mut data: &mut ElementData, + possibly_expired_animations: &mut Vec) { - let mut possibly_expired_animations = vec![]; + self.cascade_primary_or_pseudo(context, &mut data, None, + possibly_expired_animations, /* animate = */ true); + } - // Cascade the primary style. - self.cascade_primary_or_pseudo(context, data, None, - &mut possibly_expired_animations, - /* 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. - // + /// Performs the cascade for the element's eager pseudos. + fn cascade_pseudos(&self, + context: &mut StyleContext, + mut data: &mut ElementData, + possibly_expired_animations: &mut Vec) + { // Note that we've already set up the map of matching pseudo-elements - // in match_element (and handled the damage implications of changing + // in match_pseudos (and handled the damage implications of changing // which pseudos match), so now we can just iterate what we have. This // does mean collecting owned pseudos, so that the borrow checker will - // let us pass the mutable |data| to the inner cascade function. + // let us pass the mutable |data| to the cascade function. let matched_pseudos = data.styles().pseudos.keys(); 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; - } - // Only ::before and ::after are animatable. let animate = pseudo.is_before_or_after(); self.cascade_primary_or_pseudo(context, data, Some(&pseudo), - &mut possibly_expired_animations, - animate); + possibly_expired_animations, animate); } } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 09792333d8d..f221a4c4780 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -10,8 +10,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use data::{ElementData, ElementStyles, StoredRestyleHint}; use dom::{DirtyDescendants, NodeInfo, TElement, TNode}; -use matching::MatchMethods; -use matching::relations_are_shareable; +use matching::{MatchMethods, StyleSharingBehavior}; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF}; use selector_parser::RestyleDamage; use servo_config::opts; @@ -396,8 +395,7 @@ fn resolve_style_internal(context: &mut StyleContext, } // Compute our style. - element.match_element(context, &mut data); - element.cascade_element(context, &mut data); + element.match_and_cascade(context, &mut data, StyleSharingBehavior::Disallow); // Conservatively mark us as having dirty descendants, since there might // be other unstyled siblings we miss when walking straight up the parent @@ -602,32 +600,18 @@ fn compute_style(_traversal: &D, traversal_data.current_dom_depth = Some(dom_depth); context.thread_local.bloom_filter.assert_complete(element); - - - // Perform CSS selector matching. context.thread_local.statistics.elements_matched += 1; - let (primary_relations, _rule_nodes_changed) = - element.match_element(context, &mut data); - // Cascade properties and compute values. - element.cascade_element(context, &mut data); - - // If the style is shareable, add it to the LRU cache. - if relations_are_shareable(&primary_relations) { - context.thread_local - .style_sharing_candidate_cache - .insert_if_possible(&element, - data.styles().primary.values(), - primary_relations); - } + // Perform the matching and cascading. + element.match_and_cascade(context, &mut data, StyleSharingBehavior::Allow); } CascadeWithReplacements(hint) => { let _rule_nodes_changed = element.cascade_with_replacements(hint, context, &mut data); - element.cascade_element(context, &mut data); + element.cascade_primary_and_pseudos(context, &mut data); } CascadeOnly => { - element.cascade_element(context, &mut data); + element.cascade_primary_and_pseudos(context, &mut data); } }; }