servo: Merge #18384 - style: Don't waste a whole selector map for each class / id in the document (from emilio:invalidation-map-bloat); r=bholley
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 05 Sep 2017 12:51:17 -0500
changeset 428503 49d106783eecf3140a81575e807e5f6325d6bba6
parent 428447 08d59706aac1fc2adaa4839e9db8c04e00bc121b
child 428504 a068e2e9e539434ed6e2b0ab50c983495bd78c68
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs18384, 18375
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18384 - style: Don't waste a whole selector map for each class / id in the document (from emilio:invalidation-map-bloat); r=bholley On top of #18375, only last commit needs review. Source-Repo: https://github.com/servo/servo Source-Revision: 4721ef81fd7fb6ee15546a6abe140a66ce0280e8
servo/components/style/gecko/data.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/gecko/generated/structs_debug.rs
servo/components/style/gecko/generated/structs_release.rs
servo/components/style/invalidation/element/invalidation_map.rs
servo/components/style/invalidation/element/invalidator.rs
servo/components/style/invalidation/media_queries.rs
servo/components/style/rule_tree/mod.rs
servo/components/style/selector_map.rs
servo/components/style/selector_parser.rs
servo/components/style/stylesheets/keyframes_rule.rs
servo/components/style/stylesheets/memory.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -11,17 +11,18 @@ use gecko_bindings::structs::{RawGeckoPr
 use gecko_bindings::structs::{StyleSheetInfo, ServoStyleSheetInner};
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use servo_arc::Arc;
 use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
-use stylesheets::{MallocSizeOfFn, PerOrigin, StylesheetContents, StylesheetInDocument};
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfFn, PerOrigin, StylesheetContents};
+use stylesheets::StylesheetInDocument;
 use stylist::{ExtraStyleData, Stylist};
 
 /// Little wrapper to a Gecko style sheet.
 #[derive(Debug, Eq, PartialEq)]
 pub struct GeckoStyleSheet(*const ServoStyleSheet);
 
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
@@ -182,20 +183,27 @@ impl PerDocumentStyleDataImpl {
     }
     /// Returns whether visited styles are enabled.
     pub fn visited_styles_enabled(&self) -> bool {
         self.visited_links_enabled() && !self.is_private_browsing_enabled()
     }
 
     /// Measures heap usage.
     pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
                                        sizes: &mut ServoStyleSetSizes) {
-        self.stylist.malloc_add_size_of_children(malloc_size_of, sizes);
+        self.stylist.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of, sizes);
 
-        // We may measure more fields in the future if DMD says it's worth it.
+        let data = &self.extra_style_data;
+        sizes.mStylistOther +=
+            data.user_agent.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
+        sizes.mStylistOther +=
+            data.user.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
+        sizes.mStylistOther +=
+            data.author.malloc_size_of_children(malloc_size_of, malloc_enclosing_size_of);
     }
 }
 
 unsafe impl HasFFI for PerDocumentStyleData {
     type FFIType = RawServoStyleSet;
 }
 unsafe impl HasSimpleFFI for PerDocumentStyleData {}
 unsafe impl HasBoxFFI for PerDocumentStyleData {}
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -2058,16 +2058,18 @@ extern "C" {
                                                  parent_style:
                                                      ServoStyleContextBorrowedOrNull,
                                                  declarations:
                                                      RawServoDeclarationBlockBorrowed)
      -> ServoStyleContextStrong;
 }
 extern "C" {
     pub fn Servo_StyleSet_AddSizeOfExcludingThis(malloc_size_of: MallocSizeOf,
+                                                 malloc_enclosing_size_of:
+                                                     MallocSizeOf,
                                                  sizes:
                                                      *mut ServoStyleSetSizes,
                                                  set:
                                                      RawServoStyleSetBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleContext_AddRef(ctx: ServoStyleContextBorrowed);
 }
--- a/servo/components/style/gecko/generated/structs_debug.rs
+++ b/servo/components/style/gecko/generated/structs_debug.rs
@@ -5560,37 +5560,77 @@ pub mod root {
                         const _ as usize } , 0usize , concat ! (
                         "Alignment of field: " , stringify ! ( GeckoEffects )
                         , "::" , stringify ! ( gecko ) ));
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
         pub struct ServoStyleSetSizes {
             pub mStylistRuleTree: usize,
+            pub mStylistPrecomputedPseudos: usize,
+            pub mStylistElementAndPseudosMaps: usize,
+            pub mStylistInvalidationMap: usize,
+            pub mStylistRevalidationSelectors: usize,
+            pub mStylistOther: usize,
             pub mOther: usize,
         }
         #[test]
         fn bindgen_test_layout_ServoStyleSetSizes() {
-            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 16usize ,
+            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 56usize ,
                        concat ! (
                        "Size of: " , stringify ! ( ServoStyleSetSizes ) ));
             assert_eq! (::std::mem::align_of::<ServoStyleSetSizes>() , 8usize
                         , concat ! (
                         "Alignment of " , stringify ! ( ServoStyleSetSizes )
                         ));
             assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) .
                         mStylistRuleTree as * const _ as usize } , 0usize ,
                         concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! (
                         mStylistRuleTree ) ));
             assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistPrecomputedPseudos as * const _ as usize } ,
+                        8usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistPrecomputedPseudos ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistElementAndPseudosMaps as * const _ as usize }
+                        , 16usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistElementAndPseudosMaps ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistInvalidationMap as * const _ as usize } ,
+                        24usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistInvalidationMap ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistRevalidationSelectors as * const _ as usize }
+                        , 32usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistRevalidationSelectors ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistOther as * const _ as usize } , 40usize ,
+                        concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistOther ) ));
+            assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) . mOther
-                        as * const _ as usize } , 8usize , concat ! (
+                        as * const _ as usize } , 48usize , concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! ( mOther )
                         ));
         }
         impl Clone for ServoStyleSetSizes {
             fn clone(&self) -> Self { *self }
         }
         #[repr(u8)]
--- a/servo/components/style/gecko/generated/structs_release.rs
+++ b/servo/components/style/gecko/generated/structs_release.rs
@@ -5448,37 +5448,77 @@ pub mod root {
                         const _ as usize } , 0usize , concat ! (
                         "Alignment of field: " , stringify ! ( GeckoEffects )
                         , "::" , stringify ! ( gecko ) ));
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
         pub struct ServoStyleSetSizes {
             pub mStylistRuleTree: usize,
+            pub mStylistPrecomputedPseudos: usize,
+            pub mStylistElementAndPseudosMaps: usize,
+            pub mStylistInvalidationMap: usize,
+            pub mStylistRevalidationSelectors: usize,
+            pub mStylistOther: usize,
             pub mOther: usize,
         }
         #[test]
         fn bindgen_test_layout_ServoStyleSetSizes() {
-            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 16usize ,
+            assert_eq!(::std::mem::size_of::<ServoStyleSetSizes>() , 56usize ,
                        concat ! (
                        "Size of: " , stringify ! ( ServoStyleSetSizes ) ));
             assert_eq! (::std::mem::align_of::<ServoStyleSetSizes>() , 8usize
                         , concat ! (
                         "Alignment of " , stringify ! ( ServoStyleSetSizes )
                         ));
             assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) .
                         mStylistRuleTree as * const _ as usize } , 0usize ,
                         concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! (
                         mStylistRuleTree ) ));
             assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistPrecomputedPseudos as * const _ as usize } ,
+                        8usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistPrecomputedPseudos ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistElementAndPseudosMaps as * const _ as usize }
+                        , 16usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistElementAndPseudosMaps ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistInvalidationMap as * const _ as usize } ,
+                        24usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistInvalidationMap ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistRevalidationSelectors as * const _ as usize }
+                        , 32usize , concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistRevalidationSelectors ) ));
+            assert_eq! (unsafe {
+                        & ( * ( 0 as * const ServoStyleSetSizes ) ) .
+                        mStylistOther as * const _ as usize } , 40usize ,
+                        concat ! (
+                        "Alignment of field: " , stringify ! (
+                        ServoStyleSetSizes ) , "::" , stringify ! (
+                        mStylistOther ) ));
+            assert_eq! (unsafe {
                         & ( * ( 0 as * const ServoStyleSetSizes ) ) . mOther
-                        as * const _ as usize } , 8usize , concat ! (
+                        as * const _ as usize } , 48usize , concat ! (
                         "Alignment of field: " , stringify ! (
                         ServoStyleSetSizes ) , "::" , stringify ! ( mOther )
                         ));
         }
         impl Clone for ServoStyleSetSizes {
             fn clone(&self) -> Self { *self }
         }
         #[repr(u8)]
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -9,16 +9,18 @@ use context::QuirksMode;
 use element_state::ElementState;
 use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry};
 use selector_parser::SelectorImpl;
 use selectors::attr::NamespaceConstraint;
 use selectors::parser::{Combinator, Component};
 use selectors::parser::{Selector, SelectorIter, SelectorMethods};
 use selectors::visitor::SelectorVisitor;
 use smallvec::SmallVec;
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfHash};
 
 #[cfg(feature = "gecko")]
 /// Gets the element state relevant to the given `:dir` pseudo-class selector.
 pub fn dir_selector_to_state(s: &[u16]) -> ElementState {
     use element_state::{IN_LTR_STATE, IN_RTL_STATE};
 
     // Jump through some hoops to deal with our Box<[u16]> thing.
     const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0];
@@ -132,20 +134,20 @@ impl SelectorMapEntry for StateDependenc
 /// In particular, we want to lookup as few things as possible to get the fewer
 /// selectors the better, so this looks up by id, class, or looks at the list of
 /// state/other attribute affecting selectors.
 #[derive(Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct InvalidationMap {
     /// A map from a given class name to all the selectors with that class
     /// selector.
-    pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+    pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map from a given id to all the selectors with that ID in the
     /// stylesheets currently applying to the document.
-    pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+    pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
     /// A map of all the state dependencies.
     pub state_affecting_selectors: SelectorMap<StateDependency>,
     /// A map of other attribute affecting selectors.
     pub other_attribute_affecting_selectors: SelectorMap<Dependency>,
     /// Whether there are attribute rules of the form `[class~="foo"]` that may
     /// match. In that case, we need to look at
     /// `other_attribute_affecting_selectors` too even if only the `class` has
     /// changed.
@@ -238,31 +240,31 @@ impl InvalidationMap {
             }
 
             self.has_id_attribute_selectors |= compound_visitor.has_id_attribute_selectors;
             self.has_class_attribute_selectors |= compound_visitor.has_class_attribute_selectors;
 
             for class in compound_visitor.classes {
                 self.class_to_selector
                     .entry(class, quirks_mode)
-                    .or_insert_with(SelectorMap::new)
-                    .insert(Dependency {
+                    .or_insert_with(SmallVec::new)
+                    .push(Dependency {
                         selector: selector.clone(),
                         selector_offset: sequence_start,
-                    }, quirks_mode);
+                    })
             }
 
             for id in compound_visitor.ids {
                 self.id_to_selector
                     .entry(id, quirks_mode)
-                    .or_insert_with(SelectorMap::new)
-                    .insert(Dependency {
+                    .or_insert_with(SmallVec::new)
+                    .push(Dependency {
                         selector: selector.clone(),
                         selector_offset: sequence_start,
-                    }, quirks_mode);
+                    })
             }
 
             if !compound_visitor.state.is_empty() {
                 self.state_affecting_selectors
                     .insert(StateDependency {
                         dep: Dependency {
                             selector: selector.clone(),
                             selector_offset: sequence_start,
@@ -282,16 +284,34 @@ impl InvalidationMap {
             combinator = iter.next_sequence();
             if combinator.is_none() {
                 break;
             }
 
             index += 1; // Account for the combinator.
         }
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // Currently we measure the HashMap storage, but not things pointed to
+        // by keys and values.
+        let mut n = 0;
+
+        n += self.class_to_selector.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.id_to_selector.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+
+        n += self.state_affecting_selectors.malloc_size_of_children(malloc_enclosing_size_of);
+
+        n += self.other_attribute_affecting_selectors.malloc_size_of_children(
+            malloc_enclosing_size_of);
+        n
+    }
 }
 
 /// A struct that collects invalidations for a given compound selector.
 struct CompoundSelectorDependencyCollector {
     /// The state this compound selector is affected by.
     state: ElementState,
 
     /// The classes this compound selector is affected by.
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -18,16 +18,22 @@ use selector_parser::{SelectorImpl, Snap
 use selectors::attr::CaseSensitivity;
 use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode};
 use selectors::matching::{matches_selector, matches_compound_selector};
 use selectors::matching::CompoundSelectorMatchingResult;
 use selectors::parser::{Combinator, Component, Selector};
 use smallvec::SmallVec;
 use std::fmt;
 
+#[derive(Debug, PartialEq)]
+enum VisitedDependent {
+    Yes,
+    No,
+}
+
 /// The struct that takes care of encapsulating all the logic on where and how
 /// element styles need to be invalidated.
 pub struct TreeStyleInvalidator<'a, 'b: 'a, E>
     where E: TElement,
 {
     element: E,
     // TODO(emilio): It's tempting enough to just avoid running invalidation for
     // elements without data.
@@ -787,30 +793,36 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
     fn collect_dependencies_in_invalidation_map(
         &mut self,
         map: &InvalidationMap,
     ) {
         let quirks_mode = self.shared_context.quirks_mode();
         let removed_id = self.removed_id;
         if let Some(ref id) = removed_id {
             if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         let added_id = self.added_id;
         if let Some(ref id) = added_id {
             if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
             if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
-                self.collect_dependencies_in_map(deps)
+                for dep in deps {
+                    self.scan_dependency(dep, VisitedDependent::No);
+                }
             }
         }
 
         let should_examine_attribute_selector_map =
             self.snapshot.other_attr_changed() ||
             (self.snapshot.class_changed() && map.has_class_attribute_selectors) ||
             (self.snapshot.id_changed() && map.has_id_attribute_selectors);
 
@@ -834,20 +846,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         map: &SelectorMap<Dependency>,
     ) {
         map.lookup_with_additional(
             self.lookup_element,
             self.shared_context.quirks_mode(),
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
-                self.scan_dependency(
-                    dependency,
-                    /* is_visited_dependent = */ false
-                );
+                self.scan_dependency(dependency, VisitedDependent::No);
                 true
             },
         );
     }
 
     fn collect_state_dependencies(
         &mut self,
         map: &SelectorMap<StateDependency>,
@@ -857,31 +866,34 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
             self.lookup_element,
             self.shared_context.quirks_mode(),
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
                 if !dependency.state.intersects(state_changes) {
                     return true;
                 }
-                self.scan_dependency(
-                    &dependency.dep,
-                    dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE)
-                );
+                let visited_dependent =
+                    if dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE) {
+                        VisitedDependent::Yes
+                    } else {
+                        VisitedDependent::No
+                    };
+                self.scan_dependency(&dependency.dep, visited_dependent);
                 true
             },
         );
     }
 
     fn scan_dependency(
         &mut self,
         dependency: &Dependency,
-        is_visited_dependent: bool
+        is_visited_dependent: VisitedDependent,
     ) {
-        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {})",
+        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {:?})",
                self.element,
                dependency,
                is_visited_dependent);
 
         if !self.dependency_may_be_relevant(dependency) {
             return;
         }
 
@@ -938,17 +950,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         // The matching process only considers the relevant link state and
         // visited handling mode when deciding if visited matches.  Instead, we
         // are rematching here in case there is some :visited selector whose
         // matching result changed for some other state or attribute change of
         // this element (for example, for things like [foo]:visited).
         //
         // NOTE: This thing is actually untested because testing it is flaky,
         // see the tests that were added and then backed out in bug 1328509.
-        if is_visited_dependent && now_context.relevant_link_found {
+        if is_visited_dependent == VisitedDependent::Yes && now_context.relevant_link_found {
             then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
             let matched_then =
                 matches_selector(&dependency.selector,
                                  dependency.selector_offset,
                                  None,
                                  &self.wrapper,
                                  &mut then_context,
                                  &mut |_, _| {});
--- a/servo/components/style/invalidation/media_queries.rs
+++ b/servo/components/style/invalidation/media_queries.rs
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Code related to the invalidation of media-query-affected rules.
 
 use context::QuirksMode;
 use fnv::FnvHashSet;
 use media_queries::Device;
 use shared_lock::SharedRwLockReadGuard;
-use stylesheets::{DocumentRule, ImportRule, MediaRule,  SupportsRule};
-use stylesheets::{NestedRuleIterationCondition, Stylesheet};
+use stylesheets::{DocumentRule, ImportRule, MallocEnclosingSizeOfFn, MallocSizeOfHash, MediaRule};
+use stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule};
 
 /// A key for a given media query result.
 ///
 /// NOTE: It happens to be the case that all the media lists we care about
 /// happen to have a stable address, so we can just use an opaque pointer to
 /// represent them.
 ///
 /// Also, note that right now when a rule or stylesheet is removed, we do a full
@@ -83,16 +83,22 @@ impl EffectiveMediaQueryResults {
     /// Notices that an effective item has been seen, and caches it as matching.
     pub fn saw_effective<T>(&mut self, item: &T)
         where T: ToMediaListKey,
     {
         // NOTE(emilio): We can't assert that we don't cache the same item twice
         // because of stylesheet reusing... shrug.
         self.set.insert(item.to_media_list_key());
     }
+
+    /// Measure heap usage.
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        self.set.malloc_shallow_size_of_hash(malloc_enclosing_size_of)
+    }
 }
 
 /// A filter that filters over effective rules, but allowing all potentially
 /// effective `@media` rules.
 pub struct PotentiallyEffectiveMediaRules;
 
 impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
     fn process_import(
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -791,17 +791,17 @@ impl RuleNode {
                 None
             } else {
                 Some(WeakRuleNode::from_ptr(first_child))
             }
         }
     }
 
     fn malloc_size_of_including_self(&self, malloc_size_of: MallocSizeOfFn) -> usize {
-        let mut n = unsafe { malloc_size_of(self as *const _ as *const _) };
+        let mut n = unsafe { (malloc_size_of.0)(self as *const _ as *const _) };
         for child in self.iter_children() {
             n += unsafe { (*child.ptr()).malloc_size_of_including_self(malloc_size_of) };
         }
         n
     }
 }
 
 #[derive(Clone)]
--- a/servo/components/style/selector_map.rs
+++ b/servo/components/style/selector_map.rs
@@ -14,16 +14,18 @@ use hash::map as hash_map;
 use pdqsort::sort_by;
 use precomputed_hash::PrecomputedHash;
 use rule_tree::CascadeLevel;
 use selector_parser::SelectorImpl;
 use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlags};
 use selectors::parser::{Component, Combinator, SelectorIter};
 use smallvec::{SmallVec, VecLike};
 use std::hash::{BuildHasherDefault, Hash, Hasher};
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOfHash};
 use stylist::Rule;
 
 /// A hasher implementation that doesn't hash anything, because it expects its
 /// input to be a suitable u32 hash.
 pub struct PrecomputedHasher {
     hash: Option<u32>,
 }
 
@@ -139,16 +141,32 @@ impl<T: 'static> SelectorMap<T> {
     pub fn is_empty(&self) -> bool {
         self.count == 0
     }
 
     /// Returns the number of entries.
     pub fn len(&self) -> usize {
         self.count
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // Currently we measure the HashMap storage, but not things pointed to
+        // by keys and values.
+        let mut n = 0;
+        n += self.id_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.class_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n += self.local_name_hash.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+
+        // We may measure other fields in the future if DMD says it's worth it.
+
+        n
+    }
 }
 
 impl SelectorMap<Rule> {
     /// Append to `rule_list` all Rules in `self` that match element.
     ///
     /// Extract matching rules as per element's ID, classes, tag name, etc..
     /// Sort the Rules at the end to maintain cascading order.
     pub fn get_all_matching_rules<E, V, F>(&self,
@@ -497,8 +515,19 @@ impl<V: 'static> MaybeCaseInsensitiveHas
     pub fn get(&self, key: &Atom, quirks_mode: QuirksMode) -> Option<&V> {
         if quirks_mode == QuirksMode::Quirks {
             self.0.get(&key.to_ascii_lowercase())
         } else {
             self.0.get(key)
         }
     }
 }
+
+#[cfg(feature = "gecko")]
+impl<K, V> MallocSizeOfHash for MaybeCaseInsensitiveHashMap<K, V>
+    where K: PrecomputedHash + Eq + Hash
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        self.0.malloc_shallow_size_of_hash(malloc_enclosing_size_of)
+    }
+}
+
--- a/servo/components/style/selector_parser.rs
+++ b/servo/components/style/selector_parser.rs
@@ -183,9 +183,14 @@ impl<T> PerPseudoElementMap<T> {
             Some(i) => i,
             None => return Err(()),
         };
         if self.entries[index].is_none() {
             self.entries[index] = Some(f());
         }
         Ok(self.entries[index].as_mut().unwrap())
     }
+
+    /// Get an iterator for the entries.
+    pub fn iter(&self) -> ::std::slice::Iter<Option<T>> {
+        self.entries.iter()
+    }
 }
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -14,17 +14,17 @@ use properties::LonghandIdSet;
 use properties::animated_properties::AnimatableLonghand;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
 use selectors::parser::SelectorParseError;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
 use std::fmt;
 use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError};
 use style_traits::PropertyDeclarationParseError;
-use stylesheets::{CssRuleType, StylesheetContents};
+use stylesheets::{CssRuleType, MallocSizeOfFn, MallocSizeOfVec, StylesheetContents};
 use stylesheets::rule_parser::{VendorPrefix, get_location_with_offset};
 use values::{KeyframesName, serialize_percentage};
 
 /// A [`@keyframes`][keyframes] rule.
 ///
 /// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
 #[derive(Debug)]
 pub struct KeyframesRule {
@@ -437,16 +437,24 @@ impl KeyframesAnimation {
         if result.steps.last().unwrap().start_percentage.0 != 1. {
             result.steps.push(KeyframesStep::new(KeyframePercentage::new(1.),
                                                  KeyframesStepValue::ComputedValues,
                                                  guard));
         }
 
         result
     }
+
+    /// Measure heap usage.
+    pub fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        let mut n = 0;
+        n += self.steps.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.properties_changed.malloc_shallow_size_of_vec(malloc_size_of);
+        n
+    }
 }
 
 /// Parses a keyframes list, like:
 /// 0%, 50% {
 ///     width: 50%;
 /// }
 ///
 /// 40%, 60%, 100% {
--- a/servo/components/style/stylesheets/memory.rs
+++ b/servo/components/style/stylesheets/memory.rs
@@ -4,51 +4,68 @@
 
 //! Memory reporting for the style system when running inside of Gecko.
 
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings::Gecko_HaveSeenPtr;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::SeenPtrs;
 #[cfg(feature = "gecko")]
+use hash::HashMap;
+#[cfg(feature = "gecko")]
 use servo_arc::Arc;
 use shared_lock::SharedRwLockReadGuard;
+use std::collections::HashSet;
+use std::hash::{BuildHasher, Hash};
 use std::os::raw::c_void;
 
 /// Like gecko_bindings::structs::MallocSizeOf, but without the Option<>
 /// wrapper.
 ///
-/// Note that functions of this type should not be called via
-/// do_malloc_size_of(), rather than directly.
-pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
+/// Note that functions of this type should be called via do_malloc_size_of(),
+/// rather than directly.
+#[derive(Clone, Copy)]
+pub struct MallocSizeOfFn(pub unsafe extern "C" fn(ptr: *const c_void) -> usize);
+
+/// Like MallocSizeOfFn, but can take an interior pointer.
+#[derive(Clone, Copy)]
+pub struct MallocEnclosingSizeOfFn(pub unsafe extern "C" fn(ptr: *const c_void) -> usize);
 
 /// Servo-side counterpart to mozilla::SizeOfState. The only difference is that
 /// this struct doesn't contain the SeenPtrs table, just a pointer to it.
 #[cfg(feature = "gecko")]
 pub struct SizeOfState {
     /// Function that measures the size of heap blocks.
     pub malloc_size_of: MallocSizeOfFn,
     /// Table recording heap blocks that have already been measured.
     pub seen_ptrs: *mut SeenPtrs,
 }
 
-/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
+/// Check if an allocation is empty.
 pub unsafe fn is_empty<T>(ptr: *const T) -> bool {
     return ptr as usize <= ::std::mem::align_of::<T>();
 }
 
 /// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
 pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
     if is_empty(ptr) {
         0
     } else {
-        malloc_size_of(ptr as *const c_void)
+        (malloc_size_of.0)(ptr as *const c_void)
     }
 }
 
+/// Call malloc_enclosing_size_of on ptr, which must not be empty.
+pub unsafe fn do_malloc_enclosing_size_of<T>(
+    malloc_enclosing_size_of: MallocEnclosingSizeOfFn, ptr: *const T) -> usize
+{
+    assert!(!is_empty(ptr));
+    (malloc_enclosing_size_of.0)(ptr as *const c_void)
+}
+
 /// Trait for measuring the size of heap data structures.
 pub trait MallocSizeOf {
     /// Measure the size of any heap-allocated structures that hang off this
     /// value, but not the space taken up by the value itself.
     fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize;
 }
 
 /// Like MallocSizeOf, but takes a SizeOfState which allows it to measure
@@ -87,17 +104,17 @@ impl<T: MallocSizeOf> MallocSizeOf for V
 }
 
 #[cfg(feature = "gecko")]
 impl<T: MallocSizeOfWithRepeats> MallocSizeOfWithRepeats for Arc<T> {
     fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
         let mut n = 0;
         let heap_ptr = self.heap_ptr();
         if unsafe { !is_empty(heap_ptr) && !Gecko_HaveSeenPtr(state.seen_ptrs, heap_ptr) } {
-            n += unsafe { (state.malloc_size_of)(heap_ptr) };
+            n += unsafe { (state.malloc_size_of.0)(heap_ptr) };
             n += (**self).malloc_size_of_children(state);
         }
         n
     }
 }
 
 impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
     fn malloc_size_of_children(
@@ -105,8 +122,83 @@ impl<T: MallocSizeOfWithGuard> MallocSiz
         guard: &SharedRwLockReadGuard,
         malloc_size_of: MallocSizeOfFn,
     ) -> usize {
         self.iter().fold(
             unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
             |n, elem| n + elem.malloc_size_of_children(guard, malloc_size_of))
     }
 }
+
+/// Trait for measuring the heap usage of a Box<T>.
+pub trait MallocSizeOfBox {
+    /// Measure shallowly the size of the memory used by the T -- anything
+    /// pointed to by the T must be measured separately.
+    fn malloc_shallow_size_of_box(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+impl<T> MallocSizeOfBox for Box<T> {
+    fn malloc_shallow_size_of_box(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        unsafe { do_malloc_size_of(malloc_size_of, &**self as *const T) }
+    }
+}
+
+/// Trait for measuring the heap usage of a vector.
+pub trait MallocSizeOfVec {
+    /// Measure shallowly the size of the memory used by the Vec's elements --
+    /// anything pointed to by the elements must be measured separately, using
+    /// iteration.
+    fn malloc_shallow_size_of_vec(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+impl<T> MallocSizeOfVec for Vec<T> {
+    fn malloc_shallow_size_of_vec(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+        unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) }
+    }
+}
+
+/// Trait for measuring the heap usage of a hash table.
+pub trait MallocSizeOfHash {
+    /// Measure shallowly the size of the memory used within a hash table --
+    /// anything pointer to by the keys and values must be measured separately,
+    /// using iteration.
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize;
+}
+
+impl<T, S> MallocSizeOfHash for HashSet<T, S>
+    where T: Eq + Hash,
+          S: BuildHasher
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // The first value from the iterator gives us an interior pointer.
+        // malloc_enclosing_size_of() then gives us the storage size. This
+        // assumes that the HashSet's contents (values and hashes) are all
+        // stored in a single contiguous heap allocation.
+        let mut n = 0;
+        for v in self.iter() {
+            n += unsafe { do_malloc_enclosing_size_of(malloc_enclosing_size_of, v as *const T) };
+            break;
+        }
+        n
+    }
+}
+
+#[cfg(feature = "gecko")]
+impl<K, V, S> MallocSizeOfHash for HashMap<K, V, S>
+    where K: Eq + Hash,
+          S: BuildHasher
+{
+    fn malloc_shallow_size_of_hash(&self, malloc_enclosing_size_of: MallocEnclosingSizeOfFn)
+                                   -> usize {
+        // The first value from the iterator gives us an interior pointer.
+        // malloc_enclosing_size_of() then gives us the storage size. This
+        // assumes that the HashMap's contents (keys, values, and hashes) are
+        // all stored in a single contiguous heap allocation.
+        let mut n = 0;
+        for v in self.values() {
+            n += unsafe { do_malloc_enclosing_size_of(malloc_enclosing_size_of, v as *const V) };
+            break;
+        }
+        n
+    }
+}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -35,17 +35,18 @@ use style_traits::PARSING_MODE_DEFAULT;
 pub use self::counter_style_rule::CounterStyleRule;
 pub use self::document_rule::DocumentRule;
 pub use self::font_face_rule::FontFaceRule;
 pub use self::font_feature_values_rule::FontFeatureValuesRule;
 pub use self::import_rule::ImportRule;
 pub use self::keyframes_rule::KeyframesRule;
 pub use self::loader::StylesheetLoader;
 pub use self::media_rule::MediaRule;
-pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
+pub use self::memory::{MallocEnclosingSizeOfFn, MallocSizeOf, MallocSizeOfBox, MallocSizeOfFn};
+pub use self::memory::{MallocSizeOfHash, MallocSizeOfVec, MallocSizeOfWithGuard};
 #[cfg(feature = "gecko")]
 pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState};
 pub use self::namespace_rule::NamespaceRule;
 pub use self::origin::{Origin, OriginSet, PerOrigin, PerOriginIter};
 pub use self::page_rule::PageRule;
 pub use self::rule_parser::{State, TopLevelRuleParser};
 pub use self::rule_list::{CssRules, CssRulesHelpers};
 pub use self::rules_iterator::{AllRules, EffectiveRules, NestedRuleIterationCondition, RulesIterator};
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -38,17 +38,19 @@ use smallvec::VecLike;
 use std::fmt::Debug;
 use std::ops;
 use style_traits::viewport::ViewportConstraints;
 use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetIterator, StylesheetFlusher};
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule};
 use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
 #[cfg(feature = "gecko")]
-use stylesheets::{MallocSizeOf, MallocSizeOfFn};
+use stylesheets::{MallocEnclosingSizeOfFn, MallocSizeOf, MallocSizeOfBox, MallocSizeOfFn};
+#[cfg(feature = "gecko")]
+use stylesheets::{MallocSizeOfHash, MallocSizeOfVec};
 use stylesheets::StyleRule;
 use stylesheets::StylesheetInDocument;
 use stylesheets::UserAgentStylesheets;
 use stylesheets::keyframes_rule::KeyframesAnimation;
 use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
 
 pub use ::fnv::FnvHashMap;
@@ -355,16 +357,35 @@ impl DocumentCascadeData {
                         .borrow_mut_for_origin(&origin)
                         .add_counter_style(guard, rule);
                 }
                 // We don't care about any other rule.
                 _ => {}
             }
         }
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
+                                       sizes: &mut ServoStyleSetSizes) {
+        self.per_origin.user_agent.malloc_add_size_of_children(malloc_size_of,
+                                                               malloc_enclosing_size_of, sizes);
+        self.per_origin.user.malloc_add_size_of_children(malloc_size_of,
+                                                         malloc_enclosing_size_of, sizes);
+        self.per_origin.author.malloc_add_size_of_children(malloc_size_of,
+                                                           malloc_enclosing_size_of, sizes);
+
+        for elem in self.precomputed_pseudo_element_decls.iter() {
+            if let Some(ref elem) = *elem {
+                sizes.mStylistPrecomputedPseudos += elem.malloc_shallow_size_of_vec(malloc_size_of);
+            }
+        }
+    }
 }
 
 /// A wrapper over a StylesheetSet that can be `Sync`, since it's only used and
 /// exposed via mutable methods in the `Stylist`.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 struct StylistStylesheetSet(StylesheetSet<StylistSheet>);
 // Read above to see why this is fine.
 unsafe impl Sync for StylistStylesheetSet {}
@@ -1560,19 +1581,23 @@ impl Stylist {
     /// Accessor for a shared reference to the rule tree.
     pub fn rule_tree(&self) -> &RuleTree {
         &self.rule_tree
     }
 
     /// Measures heap usage.
     #[cfg(feature = "gecko")]
     pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
                                        sizes: &mut ServoStyleSetSizes) {
-        // XXX: need to measure other fields
+        self.cascade_data.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of,
+                                                      sizes);
         sizes.mStylistRuleTree += self.rule_tree.malloc_size_of_children(malloc_size_of);
+
+        // We may measure other fields in the future if DMD says it's worth it.
     }
 }
 
 /// This struct holds data which users of Stylist may want to extract
 /// from stylesheets which can be done at the same time as updating.
 #[derive(Default)]
 pub struct ExtraStyleData {
     /// A list of effective font-face rules and their origin.
@@ -1615,16 +1640,27 @@ impl ExtraStyleData {
     fn clear(&mut self) {
         #[cfg(feature = "gecko")]
         {
             self.font_faces.clear();
             self.font_feature_values.clear();
             self.counter_styles.clear();
         }
     }
+
+    /// Measure heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                   malloc_enclosing_size_of: MallocEnclosingSizeOfFn) -> usize {
+        let mut n = 0;
+        n += self.font_faces.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.font_feature_values.malloc_shallow_size_of_vec(malloc_size_of);
+        n += self.counter_styles.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        n
+    }
 }
 
 /// SelectorMapEntry implementation for use in our revalidation selector map.
 #[derive(Clone, Debug)]
 struct RevalidationSelectorAndHashes {
     selector: Selector<SelectorImpl>,
     selector_offset: usize,
     hashes: AncestorHashes,
@@ -1908,16 +1944,48 @@ impl CascadeData {
         self.effective_media_query_results.clear();
         self.invalidation_map.clear();
         self.attribute_dependencies.clear();
         self.style_attribute_dependency = false;
         self.state_dependencies = ElementState::empty();
         self.mapped_ids.clear();
         self.selectors_for_cache_revalidation.clear();
     }
+
+    /// Measures heap usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_add_size_of_children(&self, malloc_size_of: MallocSizeOfFn,
+                                       malloc_enclosing_size_of: MallocEnclosingSizeOfFn,
+                                       sizes: &mut ServoStyleSetSizes) {
+        sizes.mStylistElementAndPseudosMaps +=
+            self.element_map.malloc_size_of_children(malloc_enclosing_size_of);
+
+        for elem in self.pseudos_map.iter() {
+            if let Some(ref elem) = *elem {
+                sizes.mStylistElementAndPseudosMaps +=
+                    elem.malloc_shallow_size_of_box(malloc_size_of) +
+                    elem.malloc_size_of_children(malloc_enclosing_size_of)
+            }
+        }
+
+        sizes.mStylistOther +=
+            self.animations.malloc_shallow_size_of_hash(malloc_enclosing_size_of);
+        for val in self.animations.values() {
+            sizes.mStylistOther += val.malloc_size_of_children(malloc_size_of);
+        }
+
+        sizes.mStylistInvalidationMap +=
+            self.invalidation_map.malloc_size_of_children(malloc_enclosing_size_of);
+
+        sizes.mStylistRevalidationSelectors +=
+            self.selectors_for_cache_revalidation.malloc_size_of_children(malloc_enclosing_size_of);
+
+        sizes.mStylistOther +=
+            self.effective_media_query_results.malloc_size_of_children(malloc_enclosing_size_of);
+    }
 }
 
 impl Default for CascadeData {
     fn default() -> Self {
         CascadeData::new()
     }
 }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -113,18 +113,19 @@ use style::properties::animated_properti
 use style::properties::animated_properties::compare_property_priority;
 use style::properties::parse_one_declaration_into;
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
-use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard};
-use style::stylesheets::{MediaRule, NamespaceRule, Origin, OriginSet, PageRule, SizeOfState, StyleRule};
+use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocEnclosingSizeOfFn};
+use style::stylesheets::{MallocSizeOfFn, MallocSizeOfWithGuard, MediaRule};
+use style::stylesheets::{NamespaceRule, Origin, OriginSet, PageRule, SizeOfState, StyleRule};
 use style::stylesheets::{StylesheetContents, SupportsRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::RuleInclusion;
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
@@ -770,17 +771,17 @@ pub extern "C" fn Servo_StyleWorkerThrea
 pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) {
     unsafe { GeckoElement(element).clear_data() };
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: GeckoMallocSizeOf,
                                                           seen_ptrs: *mut SeenPtrs,
                                                           element: RawGeckoElementBorrowed) -> usize {
-    let malloc_size_of = malloc_size_of.unwrap();
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
     let element = GeckoElement(element);
     let borrow = element.borrow_data();
     if let Some(data) = borrow {
         let mut state = SizeOfState {
             malloc_size_of: malloc_size_of,
             seen_ptrs: seen_ptrs,
         };
         (*data).malloc_size_of_children_excluding_cvs(&mut state)
@@ -1078,19 +1079,18 @@ pub extern "C" fn Servo_StyleSheet_Clone
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
     malloc_size_of: GeckoMallocSizeOf,
     sheet: RawServoStyleSheetContentsBorrowed
 ) -> usize {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
-    let malloc_size_of = malloc_size_of.unwrap();
-    StylesheetContents::as_arc(&sheet)
-        .malloc_size_of_children(&guard, malloc_size_of)
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
+    StylesheetContents::as_arc(&sheet).malloc_size_of_children(&guard, malloc_size_of)
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_GetOrigin(
     sheet: RawServoStyleSheetContentsBorrowed
 ) -> u8 {
     let origin = match StylesheetContents::as_arc(&sheet).origin {
         Origin::UserAgent => OriginFlags_UserAgent,
@@ -3605,23 +3605,25 @@ pub extern "C" fn Servo_StyleSet_Resolve
         parent_style,
         declarations.clone_arc(),
     ).into()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
     malloc_size_of: GeckoMallocSizeOf,
+    malloc_enclosing_size_of: GeckoMallocSizeOf,
     sizes: *mut ServoStyleSetSizes,
     raw_data: RawServoStyleSetBorrowed
 ) {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
-    let malloc_size_of = malloc_size_of.unwrap();
+    let malloc_size_of = MallocSizeOfFn(malloc_size_of.unwrap());
+    let malloc_enclosing_size_of = MallocEnclosingSizeOfFn(malloc_enclosing_size_of.unwrap());
     let sizes = unsafe { sizes.as_mut() }.unwrap();
-    data.malloc_add_size_of_children(malloc_size_of, sizes);
+    data.malloc_add_size_of_children(malloc_size_of, malloc_enclosing_size_of, sizes);
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
     raw_data: RawServoStyleSetBorrowed,
     element: RawGeckoElementBorrowed,
     local_name: *mut nsIAtom,
 ) -> bool {