servo: Merge #16289 - Cascade the primary style before matching pseudos (from bholley:primary_before_pseudos); r=emilio
authorBobby Holley <bobbyholley@gmail.com>
Thu, 06 Apr 2017 16:32:14 -0500
changeset 558277 b21110aa79591ea6556ca0c9781675ec039e6599
parent 558276 68a13da9b6f255df1edde21d0ffb4669988a7cec
child 558278 f0792bb960b6e16be5d854366ba81ec2a604052c
push id52860
push userbmo:walkingice0204@gmail.com
push dateFri, 07 Apr 2017 13:29:26 +0000
reviewersemilio
milestone55.0a1
servo: Merge #16289 - Cascade the primary style before matching pseudos (from bholley:primary_before_pseudos); r=emilio https://bugzilla.mozilla.org/show_bug.cgi?id=1353960 Source-Repo: https://github.com/servo/servo Source-Revision: 3beaa8d2e98bd077ba8badf4a2e3bc3ddc51ed0e
servo/components/style/data.rs
servo/components/style/matching.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/traversal.rs
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -75,17 +75,17 @@ impl fmt::Debug for ComputedStyle {
 
 /// A list of styles for eagerly-cascaded pseudo-elements. Lazily-allocated.
 #[derive(Clone, Debug)]
 pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
 
 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.
     pub fn get(&self, pseudo: &PseudoElement) -> Option<&ComputedStyle> {
         debug_assert!(pseudo.is_eager());
         self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
     }
 
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -10,17 +10,17 @@
 use {Atom, LocalName};
 use animation::{self, Animation, PropertyAnimation};
 use atomic_refcell::AtomicRefMut;
 use cache::{LRUCache, LRUCacheMutIterator};
 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};
 use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
 use selectors::bloom::BloomFilter;
 use selectors::matching::{ElementSelectorFlags, StyleRelations};
 use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
 use servo_config::opts;
@@ -53,34 +53,16 @@ fn create_common_style_affecting_attribu
                     flags.insert(flag)
                 }
             }
         }
     }
     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<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 {
-        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,
 /// and once here, the style information of the element is immutable, so it's
 /// safe to access.
 ///
 /// TODO: We can stick a lot more info here.
 #[derive(Debug)]
@@ -425,25 +407,16 @@ impl<E: TElement> StyleSharingCandidateC
 pub enum StyleSharingResult {
     /// We didn't find anybody to share the style with.
     CannotShare,
     /// The node's style can be shared. The integer specifies the index in the
     /// LRU cache that was hit and the damage that was done.
     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).
     ///
     /// This is needed to correctly handle blockification of flex and grid
     /// items.
     ///
     /// Returns itself if the element has no parent. In practice this doesn't
@@ -538,54 +511,50 @@ trait PrivateMatchMethods: TElement {
 
         cascade_info.finish(&self.as_node());
         values
     }
 
     fn cascade_internal(&self,
                         context: &StyleContext<Self>,
                         primary_style: &ComputedStyle,
-                        pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>,
-                        booleans: &CascadeBooleans)
+                        pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>)
                         -> Arc<ComputedValues> {
         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)
         }
 
         // Grab the rule node.
         let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules;
         self.cascade_with_rules(context.shared, rule_node, primary_style, cascade_flags, pseudo_style.is_some())
     }
 
     /// 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: &mut StyleContext<Self>,
                                      data: &mut ElementData,
                                      pseudo: Option<&PseudoElement>,
                                      possibly_expired_animations: &mut Vec<PropertyAnimation>,
-                                     booleans: CascadeBooleans) {
+                                     animate: bool) {
         // Collect some values.
         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,
-                                                   &pseudo_style, &booleans);
+                                                   &pseudo_style);
 
         // Handle animations.
-        if booleans.animate {
+        if animate {
             self.process_animations(context,
                                     &mut old_values,
                                     &mut new_values,
                                     pseudo,
                                     possibly_expired_animations);
         }
 
         // Accumulate restyle damage.
@@ -801,107 +770,145 @@ fn compute_rule_node<E: TElement>(rule_t
 {
     let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level));
     let rule_node = rule_tree.insert_ordered_rules(rules);
     rule_node
 }
 
 impl<E: TElement> 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<Self>,
+                         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<Self>,
+                                   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<Self>,
-                     data: &mut ElementData)
-                     -> MatchResults
+                     data: &mut ElementData,
+                     relations: &mut StyleRelations)
+                     -> bool
     {
         let mut applicable_declarations =
             Vec::<ApplicableDeclarationBlock>::with_capacity(16);
 
         let stylist = &context.shared.stylist;
         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();
 
-        // 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
-        // at us later in the closure.
-        let guards = &context.shared.guards;
-        let rule_tree = &context.shared.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);
+        *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::<Self>(rule_tree, &mut applicable_declarations);
+            compute_rule_node::<Self>(&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<Self>,
+                     data: &mut ElementData)
+                     -> bool
+    {
+        let mut applicable_declarations =
+            Vec::<ApplicableDeclarationBlock>::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| {
+            self.apply_selector_flags(tasks, element, flags);
+        };
+
+        // 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 = &stylist.rule_tree;
+        let bloom_filter = context.thread_local.bloom_filter.filter();
+
         // Compute rule nodes for eagerly-cascaded pseudo-elements.
         let mut matches_different_pseudos = false;
         SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
             let mut pseudos = &mut data.styles_mut().pseudos;
             debug_assert!(applicable_declarations.is_empty());
             let pseudo_animation_rules = if pseudo.is_before_or_after() {
                 self.get_animation_rules(Some(&pseudo))
             } else {
@@ -933,24 +940,69 @@ pub trait MatchMethods : TElement {
             rule_nodes_changed = true;
             if let Some(r) = data.get_restyle_mut() {
                 // Any changes to the matched pseudo-elements trigger
                 // reconstruction.
                 r.damage |= RestyleDamage::reconstruct();
             }
         }
 
-        // If we have any pseudo elements, indicate so in the primary StyleRelations.
-        if data.styles().pseudos.is_empty() {
-            primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
+        rule_nodes_changed
+    }
+
+    /// 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<SequentialTask<Self>>,
+                            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);
+                }
+            }
         }
-
-        MatchResults {
-            primary_relations: Some(primary_relations),
-            rule_nodes_changed: rule_nodes_changed,
+        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,
                                  hint: RestyleHint,
                                  context: &StyleContext<Self>,
@@ -1158,61 +1210,43 @@ pub trait MatchMethods : TElement {
                 } else {
                     // Something else. Be conservative for now.
                     RestyleDamage::reconstruct()
                 }
             }
         }
     }
 
-    /// 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<Self>,
-                       mut data: &mut AtomicRefMut<ElementData>,
-                       primary_is_shareable: bool)
+                       mut data: &mut ElementData,
+                       possibly_expired_animations: &mut Vec<PropertyAnimation>)
     {
-        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,
-                                       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.
-        //
+    /// Performs the cascade for the element's eager pseudos.
+    fn cascade_pseudos(&self,
+                       context: &mut StyleContext<Self>,
+                       mut data: &mut ElementData,
+                       possibly_expired_animations: &mut Vec<PropertyAnimation>)
+    {
         // 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,
-                                           CascadeBooleans {
-                                               shareable: false,
-                                               animate: animate,
-                                           });
+                                           possibly_expired_animations, animate);
         }
     }
 
     /// Returns computed values without animation and transition rules.
     fn get_base_style(&self,
                       shared_context: &SharedStyleContext,
                       primary_style: &ComputedStyle,
                       pseudo_style: &Option<(&PseudoElement, &ComputedStyle)>)
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -72,65 +72,60 @@ pub mod style_structs {
 
 #[derive(Clone, Debug)]
 pub struct ComputedValues {
     % for style_struct in data.style_structs:
     ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
     % endfor
 
     custom_properties: Option<Arc<ComputedValuesMap>>,
-    shareable: bool,
     pub writing_mode: WritingMode,
     pub root_font_size: Au,
     pub font_size_keyword: Option<longhands::font_size::KeywordSize>,
 }
 
 impl ComputedValues {
     pub fn inherit_from(parent: &Self, default: &Self) -> Arc<Self> {
         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,
             % for style_struct in data.style_structs:
             % if style_struct.inherited:
             ${style_struct.ident}: parent.${style_struct.ident}.clone(),
             % else:
             ${style_struct.ident}: default.${style_struct.ident}.clone(),
             % endif
             % endfor
         })
     }
 
     pub fn new(custom_properties: Option<Arc<ComputedValuesMap>>,
-           shareable: bool,
            writing_mode: WritingMode,
            root_font_size: Au,
            font_size_keyword: Option<longhands::font_size::KeywordSize>,
             % for style_struct in data.style_structs:
            ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
             % endfor
     ) -> Self {
         ComputedValues {
             custom_properties: custom_properties,
-            shareable: shareable,
             writing_mode: writing_mode,
             root_font_size: root_font_size,
             font_size_keyword: font_size_keyword,
             % for style_struct in data.style_structs:
             ${style_struct.ident}: ${style_struct.ident},
             % endfor
         }
     }
 
     pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
         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()),
             % for style_struct in data.style_structs:
                 ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context),
             % endfor
         })
     }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1537,40 +1537,37 @@ pub type ServoComputedValues = ComputedV
 /// When needed, the structs may be copied in order to get mutated.
 #[cfg(feature = "servo")]
 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
 pub struct ComputedValues {
     % for style_struct in data.active_style_structs():
         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
     % endfor
     custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
-    shareable: bool,
     /// The writing mode of this computed values struct.
     pub writing_mode: WritingMode,
     /// The root element's computed font size.
     pub root_font_size: Au,
     /// The keyword behind the current font-size property, if any
     pub font_size_keyword: Option<longhands::font_size::KeywordSize>,
 }
 
 #[cfg(feature = "servo")]
 impl ComputedValues {
     /// Construct a `ComputedValues` instance.
     pub fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
-               shareable: bool,
                writing_mode: WritingMode,
                root_font_size: Au,
                font_size_keyword: Option<longhands::font_size::KeywordSize>,
             % for style_struct in data.active_style_structs():
                ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
             % endfor
     ) -> Self {
         ComputedValues {
             custom_properties: custom_properties,
-            shareable: shareable,
             writing_mode: writing_mode,
             root_font_size: root_font_size,
             font_size_keyword: font_size_keyword,
         % for style_struct in data.active_style_structs():
             ${style_struct.ident}: ${style_struct.ident},
         % endfor
         }
     }
@@ -1884,17 +1881,16 @@ mod lazy_static_module {
                         ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
                     % endfor
                     % if style_struct.name == "Font":
                         hash: 0,
                     % endif
                 }),
             % 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()),
         };
     }
 }
 
 /// A per-longhand function that performs the CSS cascade for that longhand.
@@ -1913,25 +1909,22 @@ static CASCADE_PROPERTY: [CascadePropert
     % for property in data.longhands:
         longhands::${property.ident}::cascade_property,
     % endfor
 ];
 
 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,
     }
 }
 
 /// Performs the CSS cascade, computing new styles for an element from its parent style.
 ///
 /// The arguments are:
 ///
 ///   * `device`: Used to get the initial viewport and other external state.
@@ -2028,31 +2021,29 @@ pub fn apply_declarations<'a, F, I>(devi
     }
 
     let custom_properties =
         ::custom_properties::finish_cascade(
             custom_properties, &inherited_custom_properties);
 
     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,
                             % for style_struct in data.active_style_structs():
                                 % if style_struct.inherited:
                                     inherited_style.clone_${style_struct.name_lower}(),
                                 % else:
                                     default_style.clone_${style_struct.name_lower}(),
                                 % endif
                             % endfor
                             )
     } else {
         ComputedValues::new(custom_properties,
-                            flags.contains(SHAREABLE),
                             WritingMode::empty(),
                             inherited_style.root_font_size,
                             inherited_style.font_size_keyword,
                             % for style_struct in data.active_style_structs():
                                 inherited_style.clone_${style_struct.name_lower}(),
                             % endfor
                             )
     };
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -5,17 +5,17 @@
 //! Traversing the DOM tree; the bloom filter.
 
 #![deny(missing_docs)]
 
 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, StyleSharingBehavior};
 use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
 use selector_parser::RestyleDamage;
 use servo_config::opts;
 use std::borrow::BorrowMut;
 use stylist::Stylist;
 
 /// A per-traversal-level chunk of data. This is sent down by the traversal, and
 /// currently only holds the dom depth for the bloom filter.
@@ -390,19 +390,17 @@ fn resolve_style_internal<E, F>(context:
         if context.thread_local.bloom_filter.is_empty() {
             context.thread_local.bloom_filter.rebuild(element);
         } else {
             context.thread_local.bloom_filter.push(parent.unwrap());
             context.thread_local.bloom_filter.assert_complete(element);
         }
 
         // 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_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
         // chain.
         unsafe { element.note_descendants::<DirtyDescendants>() };
     }
 
     // If we're display:none and none of our ancestors are, we're the root
@@ -582,67 +580,45 @@ fn compute_style<E, D>(_traversal: &D,
         };
         if let StyleWasShared(index) = sharing_result {
             context.thread_local.statistics.styles_shared += 1;
             context.thread_local.style_sharing_candidate_cache.touch(index);
             return;
         }
     }
 
-    let match_results = match kind {
+    match kind {
         MatchAndCascade => {
             // Ensure the bloom filter is up to date.
             let dom_depth =
                 context.thread_local.bloom_filter
                        .insert_parents_recovering(element,
                                                   traversal_data.current_dom_depth);
 
             // Update the dom depth with the up-to-date dom depth.
             //
             // Note that this is always the same than the pre-existing depth,
             // but it can change from unknown to known at this step.
             traversal_data.current_dom_depth = Some(dom_depth);
 
             context.thread_local.bloom_filter.assert_complete(element);
-
+            context.thread_local.statistics.elements_matched += 1;
 
-            // Perform CSS selector matching.
-            context.thread_local.statistics.elements_matched += 1;
-            element.match_element(context, &mut data)
+            // Perform the matching and cascading.
+            element.match_and_cascade(context, &mut data, StyleSharingBehavior::Allow);
         }
         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_primary_and_pseudos(context, &mut data);
         }
         CascadeOnly => {
-            MatchResults {
-                primary_relations: None,
-                rule_nodes_changed: false,
-            }
+            element.cascade_primary_and_pseudos(context, &mut data);
         }
     };
-
-    // Cascade properties and compute values.
-    let shareable = match_results.primary_is_shareable();
-    unsafe {
-        element.cascade_element(context, &mut data, shareable);
-    }
-
-    // If the style is shareable, add it to the LRU cache.
-    if shareable {
-        context.thread_local
-               .style_sharing_candidate_cache
-               .insert_if_possible(&element,
-                                   data.styles().primary.values(),
-                                   match_results.primary_relations.unwrap());
-    }
 }
 
 fn preprocess_children<E, D>(traversal: &D,
                              element: E,
                              mut propagated_hint: StoredRestyleHint,
                              damage_handled: RestyleDamage,
                              parent_inherited_style_changed: bool)
     where E: TElement,