Bug 1387956 - Overhaul ComputedValues measurement, and add style structs measurement. r?bholley. draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 11 Aug 2017 16:37:33 +1000
changeset 644773 5d0d64cc122d84c03ed5598b7c21fe0a6f82dd59
parent 644688 f50b8a44bc048f4693a5269ddc7ba8185645f85c
child 725712 be9b3307abaa6125b6bdd7e71edacd73fc40388e
push id73548
push usernnethercote@mozilla.com
push dateFri, 11 Aug 2017 10:33:36 +0000
reviewersbholley
bugs1387956
milestone57.0a1
Bug 1387956 - Overhaul ComputedValues measurement, and add style structs measurement. r?bholley. This patch moves measurement of ComputedValues objects from Rust to C++. Measurement now happens (a) via DOM elements and (b) remaining elements via the frame tree. Likewise for the style structs hanging off ComputedValues objects. Here is an example of the output. > ├──27,600,448 B (26.49%) -- active/window(https://en.wikipedia.org/wiki/Barack_Obama) > │ ├──12,772,544 B (12.26%) -- layout > │ │ ├───4,483,744 B (04.30%) -- frames > │ │ │ ├──1,653,552 B (01.59%) ── nsInlineFrame > │ │ │ ├──1,415,760 B (01.36%) ── nsTextFrame > │ │ │ ├────431,376 B (00.41%) ── nsBlockFrame > │ │ │ ├────340,560 B (00.33%) ── nsHTMLScrollFrame > │ │ │ ├────302,544 B (00.29%) ── nsContinuingTextFrame > │ │ │ ├────156,408 B (00.15%) ── nsBulletFrame > │ │ │ ├─────73,024 B (00.07%) ── nsPlaceholderFrame > │ │ │ ├─────27,656 B (00.03%) ── sundries > │ │ │ ├─────23,520 B (00.02%) ── nsTableCellFrame > │ │ │ ├─────16,704 B (00.02%) ── nsImageFrame > │ │ │ ├─────15,488 B (00.01%) ── nsTableRowFrame > │ │ │ ├─────13,776 B (00.01%) ── nsTableColFrame > │ │ │ └─────13,376 B (00.01%) ── nsTableFrame > │ │ ├───3,412,192 B (03.28%) -- servo-style-structs > │ │ │ ├──1,288,224 B (01.24%) ── Display > │ │ │ ├────742,400 B (00.71%) ── Position > │ │ │ ├────308,736 B (00.30%) ── Font > │ │ │ ├────226,512 B (00.22%) ── Background > │ │ │ ├────218,304 B (00.21%) ── TextReset > │ │ │ ├────214,896 B (00.21%) ── Text > │ │ │ ├────130,560 B (00.13%) ── Border > │ │ │ ├─────81,408 B (00.08%) ── UIReset > │ │ │ ├─────61,440 B (00.06%) ── Padding > │ │ │ ├─────38,176 B (00.04%) ── UserInterface > │ │ │ ├─────29,232 B (00.03%) ── Margin > │ │ │ ├─────21,824 B (00.02%) ── sundries > │ │ │ ├─────20,080 B (00.02%) ── Color > │ │ │ ├─────20,080 B (00.02%) ── Column > │ │ │ └─────10,320 B (00.01%) ── Effects > │ │ ├───2,227,680 B (02.14%) -- computed-values > │ │ │ ├──1,182,928 B (01.14%) ── non-dom > │ │ │ └──1,044,752 B (01.00%) ── dom > │ │ ├───1,500,016 B (01.44%) ── text-runs > │ │ ├─────492,640 B (00.47%) ── line-boxes > │ │ ├─────326,688 B (00.31%) ── frame-properties > │ │ ├─────301,760 B (00.29%) ── pres-shell > │ │ ├──────27,648 B (00.03%) ── pres-contexts > │ │ └─────────176 B (00.00%) ── style-sets The 'servo-style-structs' and 'computed-values' sub-trees are new. (Prior to this patch, ComputedValues under DOM elements were tallied under the the 'dom/element-nodes' sub-tree, and ComputedValues not under DOM element were ignored.) 'servo-style-structs/sundries' aggregates all the style structs that are smaller than 8 KiB. Other notable things done by the patch are as follows. - It significantly changes the signatures of the methods measuring nsINode and its subclasses, in order to handle the tallying of style structs separately from element-nodes. Likewise for nsIFrame. - It renames the 'layout/style-structs' sub-tree as 'layout/gecko-style-structs', to clearly distinguish it from the new 'layout/servo-style-structs' sub-tree. - It adds some FFI functions to access various Rust-side data structures from C++ code. - There is a nasty hack used twice to measure Arcs, by stepping backwards from an interior pointer to a base pointer. It works, but I want to replace it with something better eventually. The "XXX WARNING" comments have details. - It makes DMD print a line to the console if it sees a pointer it doesn't recognise. This is useful for detecting when we are measuring an interior pointer instead of a base pointer, which is bad but easy to do when Arcs are involved. - It removes the Rust code for measuring CVs, because it's now all done on the C++ side. * * * [mq]: realign MozReview-Commit-ID: BKebACLKtCi
dom/base/Element.cpp
dom/base/Element.h
dom/base/FragmentOrElement.cpp
dom/base/FragmentOrElement.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsGenericDOMDataNode.cpp
dom/base/nsGenericDOMDataNode.h
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/base/nsWindowMemoryReporter.cpp
dom/base/nsWindowSizes.h
dom/html/HTMLAnchorElement.cpp
dom/html/HTMLAnchorElement.h
dom/html/HTMLAreaElement.cpp
dom/html/HTMLAreaElement.h
dom/html/HTMLLinkElement.cpp
dom/html/HTMLLinkElement.h
dom/svg/SVGPathElement.cpp
dom/svg/SVGPathElement.h
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/PresShell.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/style/ServoBindingList.h
layout/style/ServoBindings.cpp
layout/style/ServoStyleContext.h
layout/style/ServoTypes.h
layout/style/nsCSSPseudoElements.h
memory/replace/dmd/DMD.cpp
servo/components/style/data.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/properties/gecko.mako.rs
servo/ports/geckolib/glue.rs
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4138,23 +4138,44 @@ Element::SetCustomElementData(CustomElem
 {
   nsExtendedDOMSlots *slots = ExtendedDOMSlots();
   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
   slots->mCustomElementData = aData;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
 
-size_t
-Element::SizeOfExcludingThis(SizeOfState& aState) const
+void
+Element::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
+                                size_t* aNodeSize) const
 {
-  size_t n = FragmentOrElement::SizeOfExcludingThis(aState);
-
-  // Measure mServoData. We use ServoElementMallocSizeOf rather than
-  // |aState.mMallocSizeOf| to distinguish in DMD's output the memory
-  // measured within Servo code.
-  if (mServoData.Get()) {
-    n += Servo_Element_SizeOfExcludingThis(ServoElementMallocSizeOf,
-                                           &aState.mSeenPtrs, this);
+  FragmentOrElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+
+  if (HasServoData()) {
+    // Measure mServoData, excluding the ComputedValues. This measurement
+    // counts towards the element's size. We use ServoElementMallocSizeOf
+    // rather thang |aState.mMallocSizeOf| to better distinguish in DMD's
+    // output the memory measured within Servo code.
+    *aNodeSize +=
+      Servo_Element_SizeOfExcludingThisAndCVs(ServoElementMallocSizeOf,
+                                              &aState.mSeenPtrs, this);
+
+    // Now measure just the ComputedValues (and style structs) under
+    // mServoData. This counts towards the relevant fields in |aSizes|.
+    RefPtr<ServoStyleContext> sc;
+    if (Servo_Element_HasPrimaryComputedValues(this)) {
+      sc = Servo_Element_GetPrimaryComputedValues(this).Consume();
+      if (!aState.HaveSeenPtr(sc.get())) {
+        sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
+      }
+
+      for (size_t i = 0; i < nsCSSPseudoElements::kEagerPseudoCount; i++) {
+        if (Servo_Element_HasPseudoComputedValues(this, i)) {
+          sc = Servo_Element_GetPseudoComputedValues(this, i).Consume();
+          if (!aState.HaveSeenPtr(sc.get())) {
+            sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
+          }
+        }
+      }
+    }
   }
-  return n;
 }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -200,17 +200,17 @@ public:
   {
     NS_ASSERTION(!HasServoData(), "expected ServoData to be cleared earlier");
   }
 
 #endif // MOZILLA_INTERNAL_API
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)
 
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
 
   /**
    * Method to get the full state of this element.  See mozilla/EventStates.h
    * for the possible bits that could be set here.
    */
   EventStates State() const
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -2497,29 +2497,28 @@ FragmentOrElement::FireNodeRemovedForChi
   nsCOMPtr<nsINode> child;
   for (child = GetFirstChild();
        child && child->GetParentNode() == this;
        child = child->GetNextSibling()) {
     nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
   }
 }
 
-size_t
-FragmentOrElement::SizeOfExcludingThis(SizeOfState& aState) const
+void
+FragmentOrElement::AddSizeOfExcludingThis(SizeOfState& aState,
+                                          nsStyleSizes& aSizes,
+                                          size_t* aNodeSize) const
 {
-  size_t n = 0;
-  n += nsIContent::SizeOfExcludingThis(aState);
-  n += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
+  nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
 
   nsDOMSlots* slots = GetExistingDOMSlots();
   if (slots) {
-    n += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
+    *aNodeSize += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
   }
-
-  return n;
 }
 
 void
 FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)
 {
   if (aInStyleScope && IsElementInStyleScope()) {
     return;
   }
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -113,17 +113,17 @@ class ShadowRoot;
 class FragmentOrElement : public nsIContent
 {
 public:
   explicit FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   explicit FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   // nsINode interface methods
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
   virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12362,36 +12362,35 @@ nsDocument::GetVisibilityState(nsAString
 {
   const EnumEntry& entry =
     VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
   aState.AssignASCII(entry.value, entry.length);
   return NS_OK;
 }
 
 /* virtual */ void
-nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
-{
-  aWindowSizes.mDOMOtherSize +=
-    nsINode::SizeOfExcludingThis(aWindowSizes.mState);
+nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const
+{
+  nsINode::AddSizeOfExcludingThis(aSizes.mState, aSizes.mStyleSizes,
+                                  &aSizes.mDOMOtherSize);
 
   if (mPresShell) {
-    mPresShell->AddSizeOfIncludingThis(aWindowSizes);
-  }
-
-  aWindowSizes.mPropertyTablesSize +=
-    mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
+    mPresShell->AddSizeOfIncludingThis(aSizes);
+  }
+
+  aSizes.mPropertyTablesSize +=
+    mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
   for (uint32_t i = 0, count = mExtraPropertyTables.Length();
        i < count; ++i) {
-    aWindowSizes.mPropertyTablesSize +=
-      mExtraPropertyTables[i]->SizeOfIncludingThis(
-        aWindowSizes.mState.mMallocSizeOf);
+    aSizes.mPropertyTablesSize +=
+      mExtraPropertyTables[i]->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   }
 
   if (EventListenerManager* elm = GetExistingListenerManager()) {
-    aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
+    aSizes.mDOMEventListenersCount += elm->ListenerCount();
   }
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
 
 void
@@ -12412,63 +12411,73 @@ SizeOfOwnedSheetArrayExcludingThis(const
       // Avoid over-reporting shared sheets.
       continue;
     }
     n += sheet->SizeOfIncludingThis(aMallocSizeOf);
   }
   return n;
 }
 
-size_t
-nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
-{
-  // This SizeOfExcludingThis() overrides the one from nsINode.  But
+void
+nsDocument::AddSizeOfExcludingThis(SizeOfState& aState,
+                                   nsStyleSizes& aSizes,
+                                   size_t* aNodeSize) const
+{
+  // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
   // nsDocuments can only appear at the top of the DOM tree, and we use the
   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
   // be called.
   MOZ_CRASH();
 }
 
 void
 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
 {
-  nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
-
   for (nsIContent* node = nsINode::GetFirstChild();
        node;
        node = node->GetNextNode(this))
   {
-    size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes.mState);
-    size_t* p;
-
+    size_t nodeSize = 0;
+    node->AddSizeOfIncludingThis(aWindowSizes.mState, aWindowSizes.mStyleSizes,
+                                 &nodeSize);
+
+    // This is where we transfer the nodeSize obtained from
+    // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
     switch (node->NodeType()) {
     case nsIDOMNode::ELEMENT_NODE:
-      p = &aWindowSizes.mDOMElementNodesSize;
+      aWindowSizes.mDOMElementNodesSize += nodeSize;
       break;
     case nsIDOMNode::TEXT_NODE:
-      p = &aWindowSizes.mDOMTextNodesSize;
+      aWindowSizes.mDOMTextNodesSize += nodeSize;
       break;
     case nsIDOMNode::CDATA_SECTION_NODE:
-      p = &aWindowSizes.mDOMCDATANodesSize;
+      aWindowSizes.mDOMCDATANodesSize += nodeSize;
       break;
     case nsIDOMNode::COMMENT_NODE:
-      p = &aWindowSizes.mDOMCommentNodesSize;
+      aWindowSizes.mDOMCommentNodesSize += nodeSize;
       break;
     default:
-      p = &aWindowSizes.mDOMOtherSize;
+      aWindowSizes.mDOMOtherSize += nodeSize;
       break;
     }
 
-    *p += nodeSize;
-
     if (EventListenerManager* elm = node->GetExistingListenerManager()) {
       aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
     }
   }
 
+  // IMPORTANT: for our ComputedValues measurements, we want to measure
+  // ComputedValues accessible from DOM elements before ComputedValues not
+  // accessible from DOM elements (i.e. accessible only from the frame tree).
+  //
+  // Therefore, the measurement of the nsIDocument superclass must happen after
+  // the measurement of DOM nodes (above), because nsIDocument contains the
+  // PresShell, which contains the frame tree.
+  nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
+
   aWindowSizes.mStyleSheetsSize +=
     SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
                                        aWindowSizes.mState.mMallocSizeOf);
   // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
   // (the nsLayoutStyleSheetCache singleton does).
   aWindowSizes.mStyleSheetsSize +=
     mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
       aWindowSizes.mState.mMallocSizeOf);
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -362,17 +362,17 @@ class nsDocument : public nsIDocument,
 
 public:
   typedef mozilla::dom::Element Element;
   using nsIDocument::GetElementsByTagName;
   typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup) override;
   virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
                           nsIPrincipal* aPrincipal) override;
 
   already_AddRefed<nsIPrincipal> MaybeDowngradePrincipal(nsIPrincipal* aPrincipal);
 
   // StartDocumentLoad is pure virtual so that subclasses must override it.
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -1104,16 +1104,17 @@ nsGenericDOMDataNode::IsAttributeMapped(
 nsChangeHint
 nsGenericDOMDataNode::GetAttributeChangeHint(const nsIAtom* aAttribute,
                                              int32_t aModType) const
 {
   NS_NOTREACHED("Shouldn't be calling this!");
   return nsChangeHint(0);
 }
 
-size_t
-nsGenericDOMDataNode::SizeOfExcludingThis(SizeOfState& aState) const
+void
+nsGenericDOMDataNode::AddSizeOfExcludingThis(SizeOfState& aState,
+                                             nsStyleSizes& aSizes,
+                                             size_t* aNodeSize) const
 {
-  size_t n = nsIContent::SizeOfExcludingThis(aState);
-  n += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
-  return n;
+  nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
 }
 
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -62,17 +62,17 @@ ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIF
 
 #undef DATA_NODE_FLAG_BIT
 
 class nsGenericDOMDataNode : public nsIContent
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
   virtual void GetNodeValueInternal(nsAString& aNodeValue) override;
   virtual void SetNodeValueInternal(const nsAString& aNodeValue,
                                     mozilla::ErrorResult& aError) override;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2574,34 +2574,33 @@ nsINode::GetAccessibleNode()
 #ifdef ACCESSIBILITY
   RefPtr<AccessibleNode> anode = new AccessibleNode(this);
   return anode.forget();
 #endif
 
   return nullptr;
 }
 
-size_t
-nsINode::SizeOfExcludingThis(SizeOfState& aState) const
+void
+nsINode::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
+                                size_t* aNodeSize) const
 {
-  size_t n = 0;
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
-    n += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
+    *aNodeSize += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
   }
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mNodeInfo
   // - mSlots
   //
   // The following members are not measured:
   // - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
   //   non-owning
-  return n;
 }
 
 #define EVENT(name_, id_, type_, struct_)                                    \
   EventHandlerNonNull* nsINode::GetOn##name_() {                             \
     EventListenerManager *elm = GetExistingListenerManager();                \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())   \
                : nullptr;                                                    \
   }                                                                          \
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -12,19 +12,19 @@
 #include "nsCOMPtr.h"               // for member, local
 #include "nsGkAtoms.h"              // for nsGkAtoms::baseURIProperty
 #include "nsIDOMNode.h"
 #include "mozilla/dom/NodeInfo.h"            // member (in nsCOMPtr)
 #include "nsIVariant.h"             // for use in GetUserData()
 #include "nsNodeInfoManager.h"      // for use in NodePrincipal()
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
+#include "nsWindowSizes.h"          // for nsStyleSizes
 #include "mozilla/ErrorResult.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/SizeOfState.h"    // for SizeOfState
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsTHashtable.h"
 #include <iosfwd>
 
 // Including 'windows.h' will #define GetClassInfo to something else.
@@ -259,23 +259,23 @@ private:
   // This is the value sGeneration had when the guard was constructed.
   uint64_t mStartingGeneration;
 
   // This value is incremented on every mutation, for the life of the process.
   static uint64_t sGeneration;
 };
 
 // This should be used for any nsINode sub-class that has fields of its own
-// that it needs to measure;  any sub-class that doesn't use it will inherit
-// SizeOfExcludingThis from its super-class.  SizeOfIncludingThis() need not be
-// defined, it is inherited from nsINode.
-// This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
-#define NS_DECL_SIZEOF_EXCLUDING_THIS \
-  virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) \
-    const override;
+// that it needs to measure; any sub-class that doesn't use it will inherit
+// AddSizeOfExcludingThis from its super-class. AddSizeOfIncludingThis() need
+// not be defined, it is inherited from nsINode.
+#define NS_DECL_ADDSIZEOFEXCLUDINGTHIS \
+  virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState, \
+                                      nsStyleSizes& aSizes, \
+                                      size_t* aNodeSize) const override;
 
 // Categories of node properties
 // 0 is global.
 #define DOM_USER_DATA         1
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
 { 0x70ba4547, 0x7699, 0x44fc, \
@@ -300,16 +300,20 @@ public:
   typedef mozilla::dom::CallerType CallerType;
   typedef mozilla::ErrorResult ErrorResult;
 
   template<class T>
   using Sequence = mozilla::dom::Sequence<T>;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
 
+  // The |aNodeSize| outparam on this function is where the actual node size
+  // value is put. It gets added to the appropriate value within |aSizes| by
+  // nsDocument::DocAddSizeOfExcludingThis().
+  //
   // Among the sub-classes that inherit (directly or indirectly) from nsINode,
   // measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - nsGenericHTMLElement:  mForm, mFieldSet
   // - nsGenericHTMLFrameElement: mFrameLoader (bug 672539)
   // - HTMLBodyElement:       mContentStyleRule
   // - HTMLDataListElement:   mOptions
   // - HTMLFieldSetElement:   mElements, mDependentElements, mFirstLegend
@@ -323,25 +327,30 @@ public:
   // - nsHTMLSelectElement:   mOptions, mRestoreState
   // - nsHTMLTableElement:    mTBodies, mRows, mTableInheritedAttributes
   // - nsHTMLTableSectionElement: mRows
   // - nsHTMLTextAreaElement: mControllers, mState
   //
   // The following members don't need to be measured:
   // - nsIContent: mPrimaryFrame, because it's non-owning and measured elsewhere
   //
-  virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) const;
+  virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState,
+                                      nsStyleSizes& aSizes,
+                                      size_t* aNodeSize) const;
 
   // SizeOfIncludingThis doesn't need to be overridden by sub-classes because
   // sub-classes of nsINode are guaranteed to be laid out in memory in such a
   // way that |this| points to the start of the allocated object, even in
   // methods of nsINode's sub-classes, so aState.mMallocSizeOf(this) is always
   // safe to call no matter which object it was invoked on.
-  virtual size_t SizeOfIncludingThis(mozilla::SizeOfState& aState) const {
-    return aState.mMallocSizeOf(this) + SizeOfExcludingThis(aState);
+  virtual void AddSizeOfIncludingThis(mozilla::SizeOfState& aState,
+                                      nsStyleSizes& aSizes,
+                                      size_t* aNodeSize) const {
+    *aNodeSize += aState.mMallocSizeOf(this);
+    AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
   }
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
   friend class nsAttrAndChildArray;
 
 #ifdef MOZILLA_INTERNAL_API
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -379,17 +379,17 @@ CollectWindowReports(nsGlobalWindow *aWi
   aWindowTotalSizes->mArenaSizes.mRuleNodes
     += windowSizes.mArenaSizes.mRuleNodes;
 
   REPORT_SIZE("/layout/style-contexts", windowSizes.mArenaSizes.mStyleContexts,
               "Memory used by style contexts within a window.");
   aWindowTotalSizes->mArenaSizes.mStyleContexts
     += windowSizes.mArenaSizes.mStyleContexts;
 
-  REPORT_SIZE("/layout/style-structs", windowSizes.mArenaSizes.mStyleStructs,
+  REPORT_SIZE("/layout/gecko-style-structs", windowSizes.mArenaSizes.mStyleStructs,
               "Memory used by style structs within a window.");
   aWindowTotalSizes->mArenaSizes.mStyleStructs
     += windowSizes.mArenaSizes.mStyleStructs;
 
   REPORT_SIZE("/layout/style-sets", windowSizes.mLayoutStyleSetsSize,
               "Memory used by style sets within a window.");
   aWindowTotalSizes->mLayoutStyleSetsSize += windowSizes.mLayoutStyleSetsSize;
 
@@ -424,41 +424,83 @@ CollectWindowReports(nsGlobalWindow *aWi
 
   // There are many different kinds of frames, but it is very likely
   // that only a few matter.  Implement a cutoff so we don't bloat
   // about:memory with many uninteresting entries.
   const size_t FRAME_SUNDRIES_THRESHOLD =
     js::MemoryReportingSundriesThreshold();
 
   size_t frameSundriesSize = 0;
-#define FRAME_ID(classname, ...)                                        \
-  {                                                                     \
-    size_t frameSize                                                    \
-      = windowSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname);        \
-    if (frameSize < FRAME_SUNDRIES_THRESHOLD) {                         \
-      frameSundriesSize += frameSize;                                   \
-    } else {                                                            \
-      REPORT_SIZE("/layout/frames/" # classname, frameSize,             \
-                  "Memory used by frames of "                           \
-                  "type " #classname " within a window.");              \
-    }                                                                   \
-    aWindowTotalSizes->mArenaSizes.NS_ARENA_SIZES_FIELD(classname)      \
-      += frameSize;                                                     \
+#define FRAME_ID(classname, ...) \
+  { \
+    size_t size = windowSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname); \
+    if (size < FRAME_SUNDRIES_THRESHOLD) { \
+      frameSundriesSize += size; \
+    } else { \
+      REPORT_SIZE("/layout/frames/" # classname, size, \
+                  "Memory used by frames of " \
+                  "type " #classname " within a window."); \
+    } \
+    aWindowTotalSizes->mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += size; \
   }
 #define ABSTRACT_FRAME_ID(...)
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
 
   if (frameSundriesSize > 0) {
     REPORT_SIZE("/layout/frames/sundries", frameSundriesSize,
                 "The sum of all memory used by frames which were too small "
                 "to be shown individually.");
   }
 
+  // There are many different kinds of style structs, but it is likely that
+  // only a few matter. Implement a cutoff so we don't bloat about:memory with
+  // many uninteresting entries.
+  const size_t STYLE_SUNDRIES_THRESHOLD =
+    js::MemoryReportingSundriesThreshold();
+
+  size_t styleSundriesSize = 0;
+#define STYLE_STRUCT(name_, cb_) \
+  { \
+    size_t size = windowSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
+    if (size < STYLE_SUNDRIES_THRESHOLD) { \
+      styleSundriesSize += size; \
+    } else { \
+      REPORT_SIZE("/layout/servo-style-structs/" # name_, size, \
+                  "Memory used by the " #name_ " Servo style structs " \
+                  "within a window."); \
+    } \
+    aWindowTotalSizes->mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += size; \
+  }
+#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+
+  if (styleSundriesSize > 0) {
+    REPORT_SIZE("/layout/servo-style-structs/sundries", styleSundriesSize,
+                "The sum of all memory used by Servo style structs which were "
+                "too small to be shown individually.");
+  }
+
+  REPORT_SIZE("/layout/computed-values/dom",
+              windowSizes.mStyleSizes.mComputedValuesDom,
+              "Memory used by ComputedValues objects accessible from DOM "
+              "elements.");
+  aWindowTotalSizes->mStyleSizes.mComputedValuesDom +=
+    windowSizes.mStyleSizes.mComputedValuesDom;
+
+  REPORT_SIZE("/layout/computed-values/non-dom",
+              windowSizes.mStyleSizes.mComputedValuesNonDom,
+              "Memory used by ComputedValues objects not accessible from DOM "
+              "elements.");
+  aWindowTotalSizes->mStyleSizes.mComputedValuesNonDom +=
+    windowSizes.mStyleSizes.mComputedValuesNonDom;
+
 #undef REPORT_SIZE
 #undef REPORT_COUNT
 }
 
 typedef nsTArray< RefPtr<nsGlobalWindow> > WindowArray;
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
@@ -570,19 +612,19 @@ nsWindowMemoryReporter::CollectReports(n
   REPORT("window-objects/layout/rule-nodes",
          windowTotalSizes.mArenaSizes.mRuleNodes,
          "This is the sum of all windows' 'layout/rule-nodes' numbers.");
 
   REPORT("window-objects/layout/style-contexts",
          windowTotalSizes.mArenaSizes.mStyleContexts,
          "This is the sum of all windows' 'layout/style-contexts' numbers.");
 
-  REPORT("window-objects/layout/style-structs",
+  REPORT("window-objects/layout/gecko-style-structs",
          windowTotalSizes.mArenaSizes.mStyleStructs,
-         "This is the sum of all windows' 'layout/style-structs' numbers.");
+         "This is the sum of all windows' 'layout/gecko-style-structs' numbers.");
 
   REPORT("window-objects/layout/style-sets", windowTotalSizes.mLayoutStyleSetsSize,
          "This is the sum of all windows' 'layout/style-sets' numbers.");
 
   REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRunsSize,
          "This is the sum of all windows' 'layout/text-runs' numbers.");
 
   REPORT("window-objects/layout/pres-contexts", windowTotalSizes.mLayoutPresContextSize,
@@ -598,16 +640,34 @@ nsWindowMemoryReporter::CollectReports(n
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
 
   REPORT("window-objects/layout/frames", frameTotal,
          "Memory used for layout frames within windows. "
          "This is the sum of all windows' 'layout/frames/' numbers.");
 
+  size_t styleTotal = 0;
+#define STYLE_STRUCT(name_, cb_) \
+  styleTotal += windowTotalSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_);
+#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+
+  REPORT("window-objects/layout/servo-style-structs", styleTotal,
+         "Memory used for style structs within windows. This is the sum of "
+         "all windows' 'layout/servo-style-structs/' numbers.");
+
+  REPORT("window-objects/layout/computed-values",
+         windowTotalSizes.mStyleSizes.mComputedValuesDom +
+         windowTotalSizes.mStyleSizes.mComputedValuesNonDom,
+         "This is the sum of all windows' 'layout/computed-values/' "
+         "numbers.");
+
 #undef REPORT
 
   return NS_OK;
 }
 
 uint32_t
 nsWindowMemoryReporter::GetGhostTimeout()
 {
--- a/dom/base/nsWindowSizes.h
+++ b/dom/base/nsWindowSizes.h
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWindowSizes_h
 #define nsWindowSizes_h
 
+#include "mozilla/SizeOfState.h"
+
 class nsTabSizes {
 public:
   enum Kind {
       DOM,        // DOM stuff.
       Style,      // Style stuff.
       Other       // Everything else.
   };
 
@@ -27,76 +29,148 @@ public:
     }
   }
 
   size_t mDom;
   size_t mStyle;
   size_t mOther;
 };
 
+#define ZERO_SIZE(kind, mSize)         mSize(0),
+#define ADD_TO_TAB_SIZES(kind, mSize)  aSizes->add(nsTabSizes::kind, mSize);
+#define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
+#define DECL_SIZE(kind, mSize)         size_t mSize;
+
 #define NS_ARENA_SIZES_FIELD(classname) mArena##classname
 
 struct nsArenaSizes {
 #define FOR_EACH_SIZE(macro) \
   macro(Other, mLineBoxes) \
   macro(Style, mRuleNodes) \
   macro(Style, mStyleContexts) \
   macro(Style, mStyleStructs)
 
   nsArenaSizes()
     :
-      #define ZERO_SIZE(kind, mSize) mSize(0),
       FOR_EACH_SIZE(ZERO_SIZE)
-      #undef ZERO_SIZE
-      #define FRAME_ID(classname, ...) NS_ARENA_SIZES_FIELD(classname)(),
+
+      #define FRAME_ID(classname, ...) \
+        NS_ARENA_SIZES_FIELD(classname)(0),
       #define ABSTRACT_FRAME_ID(...)
       #include "nsFrameIdList.h"
       #undef FRAME_ID
       #undef ABSTRACT_FRAME_ID
+
       dummy()
   {}
 
-  void addToTabSizes(nsTabSizes *sizes) const
+  void addToTabSizes(nsTabSizes* aSizes) const
   {
-    #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
-    #undef ADD_TO_TAB_SIZES
+
     #define FRAME_ID(classname, ...) \
-      sizes->add(nsTabSizes::Other, NS_ARENA_SIZES_FIELD(classname));
+      aSizes->add(nsTabSizes::Other, NS_ARENA_SIZES_FIELD(classname));
     #define ABSTRACT_FRAME_ID(...)
     #include "nsFrameIdList.h"
     #undef FRAME_ID
     #undef ABSTRACT_FRAME_ID
   }
 
   size_t getTotalSize() const
   {
     size_t total = 0;
-    #define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
+
     FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
-    #undef ADD_TO_TOTAL_SIZE
+
     #define FRAME_ID(classname, ...) \
       total += NS_ARENA_SIZES_FIELD(classname);
     #define ABSTRACT_FRAME_ID(...)
     #include "nsFrameIdList.h"
     #undef FRAME_ID
     #undef ABSTRACT_FRAME_ID
+
     return total;
   }
 
-  #define DECL_SIZE(kind, mSize) size_t mSize;
   FOR_EACH_SIZE(DECL_SIZE)
-  #undef DECL_SIZE
-  #define FRAME_ID(classname, ...) size_t NS_ARENA_SIZES_FIELD(classname);
+
+  #define FRAME_ID(classname, ...) \
+    size_t NS_ARENA_SIZES_FIELD(classname);
   #define ABSTRACT_FRAME_ID(...)
   #include "nsFrameIdList.h"
   #undef FRAME_ID
   #undef ABSTRACT_FRAME_ID
-  int dummy;  // present just to absorb the trailing comma from FRAME_ID in the
-              // constructor
+
+  // Present just to absorb the trailing comma in the constructor.
+  int dummy;
+
+#undef FOR_EACH_SIZE
+};
+
+#define NS_STYLE_SIZES_FIELD(name_) mStyle##name_
+
+struct nsStyleSizes
+{
+#define FOR_EACH_SIZE(macro) \
+  macro(Style, mComputedValuesDom) \
+  macro(Style, mComputedValuesNonDom)
+
+  nsStyleSizes()
+    :
+      FOR_EACH_SIZE(ZERO_SIZE)
+
+      #define STYLE_STRUCT(name_, cb_) \
+        NS_STYLE_SIZES_FIELD(name_)(0),
+      #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+      #include "nsStyleStructList.h"
+      #undef STYLE_STRUCT
+      #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+
+      dummy()
+  {}
+
+  void addToTabSizes(nsTabSizes* aSizes) const
+  {
+    FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+
+    #define STYLE_STRUCT(name_, cb_) \
+      aSizes->add(nsTabSizes::Style, NS_STYLE_SIZES_FIELD(name_));
+    #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+    #include "nsStyleStructList.h"
+    #undef STYLE_STRUCT
+    #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+  }
+
+  size_t getTotalSize() const
+  {
+    size_t total = 0;
+
+    FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
+
+    #define STYLE_STRUCT(name_, cb_) \
+      total += NS_STYLE_SIZES_FIELD(name_);
+    #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+    #include "nsStyleStructList.h"
+    #undef STYLE_STRUCT
+    #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+
+    return total;
+  }
+
+  FOR_EACH_SIZE(DECL_SIZE)
+
+  #define STYLE_STRUCT(name_, cb_) \
+    size_t NS_STYLE_SIZES_FIELD(name_);
+  #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+  #include "nsStyleStructList.h"
+  #undef STYLE_STRUCT
+  #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+
+  // Present just to absorb the trailing comma in the constructor.
+  int dummy;
 
 #undef FOR_EACH_SIZE
 };
 
 class nsWindowSizes
 {
 #define FOR_EACH_SIZE(macro) \
   macro(DOM,   mDOMElementNodesSize) \
@@ -113,48 +187,55 @@ class nsWindowSizes
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Other, mPropertyTablesSize) \
 
 public:
   explicit nsWindowSizes(mozilla::SizeOfState& aState)
     :
-      #define ZERO_SIZE(kind, mSize)  mSize(0),
       FOR_EACH_SIZE(ZERO_SIZE)
-      #undef ZERO_SIZE
       mDOMEventTargetsCount(0),
       mDOMEventListenersCount(0),
       mArenaSizes(),
+      mStyleSizes(),
       mState(aState)
   {}
 
-  void addToTabSizes(nsTabSizes *sizes) const {
-    #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
+  void addToTabSizes(nsTabSizes* aSizes) const {
     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
-    #undef ADD_TO_TAB_SIZES
-    mArenaSizes.addToTabSizes(sizes);
+    mArenaSizes.addToTabSizes(aSizes);
+    mStyleSizes.addToTabSizes(aSizes);
   }
 
   size_t getTotalSize() const
   {
     size_t total = 0;
-    #define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
+
     FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
-    #undef ADD_TO_TOTAL_SIZE
     total += mArenaSizes.getTotalSize();
+    total += mStyleSizes.getTotalSize();
+
     return total;
   }
 
-  #define DECL_SIZE(kind, mSize) size_t mSize;
   FOR_EACH_SIZE(DECL_SIZE);
-  #undef DECL_SIZE
 
   uint32_t mDOMEventTargetsCount;
   uint32_t mDOMEventListenersCount;
 
   nsArenaSizes mArenaSizes;
+
+  // This is Stylo-only because in Gecko these style structs are stored in the
+  // nsPresArena, and so are measured as part of that.
+  nsStyleSizes mStyleSizes;
+
   mozilla::SizeOfState& mState;
 
 #undef FOR_EACH_SIZE
 };
 
+#undef ZERO_SIZE
+#undef ADD_TO_TAB_SIZES
+#undef ADD_TO_TOTAL_SIZE
+#undef DECL_SIZE
+
 #endif // nsWindowSizes_h
--- a/dom/html/HTMLAnchorElement.cpp
+++ b/dom/html/HTMLAnchorElement.cpp
@@ -400,17 +400,19 @@ HTMLAnchorElement::AfterSetAttr(int32_t 
 }
 
 EventStates
 HTMLAnchorElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
-size_t
-HTMLAnchorElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
+void
+HTMLAnchorElement::AddSizeOfExcludingThis(SizeOfState& aState,
+                                          nsStyleSizes& aSizes,
+                                          size_t* aNodeSize) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
-         Link::SizeOfExcludingThis(aState);
+  nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += Link::SizeOfExcludingThis(aState);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLAnchorElement.h
+++ b/dom/html/HTMLAnchorElement.h
@@ -43,18 +43,17 @@ public:
   virtual bool Draggable() const override;
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
   // nsIDOMHTMLAnchorElement
   NS_DECL_NSIDOMHTMLANCHORELEMENT
 
-  // DOM memory reporter participant
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
   virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override;
 
--- a/dom/html/HTMLAreaElement.cpp
+++ b/dom/html/HTMLAreaElement.cpp
@@ -214,21 +214,23 @@ HTMLAreaElement::GetHrefURI() const
 }
 
 EventStates
 HTMLAreaElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
-size_t
-HTMLAreaElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
+void
+HTMLAreaElement::AddSizeOfExcludingThis(SizeOfState& aState,
+                                        nsStyleSizes& aSizes,
+                                        size_t* aNodeSize) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
-         Link::SizeOfExcludingThis(aState);
+  nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += Link::SizeOfExcludingThis(aState);
 }
 
 JSObject*
 HTMLAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLAreaElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLAreaElement.h
+++ b/dom/html/HTMLAreaElement.h
@@ -31,18 +31,17 @@ public:
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // CC
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAreaElement,
                                            nsGenericHTMLElement)
 
-  // DOM memory reporter participant
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   virtual int32_t TabIndexDefault() override;
 
   // nsIDOMHTMLAreaElement
   NS_DECL_NSIDOMHTMLAREAELEMENT
 
   virtual nsresult GetEventTargetParent(
                      EventChainPreVisitor& aVisitor) override;
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -503,21 +503,23 @@ HTMLLinkElement::GetCORSMode() const
 }
 
 EventStates
 HTMLLinkElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
-size_t
-HTMLLinkElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
+void
+HTMLLinkElement::AddSizeOfExcludingThis(SizeOfState& aState,
+                                        nsStyleSizes& aSizes,
+                                        size_t* aNodeSize) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
-         Link::SizeOfExcludingThis(aState);
+  nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += Link::SizeOfExcludingThis(aState);
 }
 
 JSObject*
 HTMLLinkElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLLinkElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -31,18 +31,17 @@ public:
 
   // CC
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLLinkElement,
                                            nsGenericHTMLElement)
 
   // nsIDOMHTMLLinkElement
   NS_DECL_NSIDOMHTMLLINKELEMENT
 
-  // DOM memory reporter participant
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   void LinkAdded();
   void LinkRemoved();
 
   // nsIDOMEventTarget
   virtual nsresult GetEventTargetParent(
                      EventChainPreVisitor& aVisitor) override;
   virtual nsresult PostHandleEvent(
--- a/dom/svg/SVGPathElement.cpp
+++ b/dom/svg/SVGPathElement.cpp
@@ -40,21 +40,23 @@ SVGPathElement::WrapNode(JSContext *aCx,
 SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGPathElementBase(aNodeInfo)
 {
 }
 
 //----------------------------------------------------------------------
 // memory reporting methods
 
-size_t
-SVGPathElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
+void
+SVGPathElement::AddSizeOfExcludingThis(SizeOfState& aState,
+                                       nsStyleSizes& aSizes,
+                                       size_t* aNodeSize) const
 {
-  return SVGPathElementBase::SizeOfExcludingThis(aState) +
-         mD.SizeOfExcludingThis(aState.mMallocSizeOf);
+  SVGPathElementBase::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
+  *aNodeSize += mD.SizeOfExcludingThis(aState.mMallocSizeOf);
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
 
 uint32_t
--- a/dom/svg/SVGPathElement.h
+++ b/dom/svg/SVGPathElement.h
@@ -30,18 +30,17 @@ class SVGPathElement final : public SVGP
 
 protected:
   friend nsresult (::NS_NewSVGPathElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
   explicit SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
 public:
-  // DOM memory reporter participant
-  NS_DECL_SIZEOF_EXCLUDING_THIS
+  NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
 
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const override;
 
   // SVGGeometryElement methods:
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2116,26 +2116,16 @@ class JSMainRuntimeCompartmentsReporter 
 };
 
 NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
 
 MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
 
 namespace xpc {
 
-static size_t
-SizeOfTreeIncludingThis(nsINode* tree, SizeOfState& aState)
-{
-    size_t n = tree->SizeOfIncludingThis(aState);
-    for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
-        n += child->SizeOfIncludingThis(aState);
-
-    return n;
-}
-
 class OrphanReporter : public JS::ObjectPrivateVisitor
 {
   public:
     explicit OrphanReporter(GetISupportsFun aGetISupports)
       : JS::ObjectPrivateVisitor(aGetISupports)
       , mState(OrphanMallocSizeOf)
     {}
 
@@ -2149,22 +2139,37 @@ class OrphanReporter : public JS::Object
         if (node && !node->IsInUncomposedDoc() &&
             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
         {
             // This is an orphan node.  If we haven't already handled the
             // sub-tree that this node belongs to, measure the sub-tree's size
             // and then record its root so we don't measure it again.
             nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
             if (orphanTree && !mState.HaveSeenPtr(orphanTree.get())) {
-                n += SizeOfTreeIncludingThis(orphanTree, mState);
+                n += SizeOfTreeIncludingThis(orphanTree);
             }
         }
         return n;
     }
 
+    size_t SizeOfTreeIncludingThis(nsINode* tree)
+    {
+        size_t nodeSize = 0;
+        nsStyleSizes sizes;
+        tree->AddSizeOfIncludingThis(mState, sizes, &nodeSize);
+        for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
+            child->AddSizeOfIncludingThis(mState, sizes, &nodeSize);
+
+        // We combine the node size with nsStyleSizes here. It's not ideal, but
+        // it's hard to get the style structs measurements out to
+        // nsWindowMemoryReporter, and the number of orphan DOM nodes is
+        // usually small.
+        return nodeSize + sizes.getTotalSize();
+    }
+
   private:
     SizeOfState mState;
 };
 
 #ifdef DEBUG
 static bool
 StartsWithExplicit(nsACString& s)
 {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -11040,18 +11040,17 @@ PresShell::AddSizeOfIncludingThis(nsWind
 
   aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
 
   aSizes.mLayoutPresContextSize +=
     mPresContext->SizeOfIncludingThis(mallocSizeOf);
 
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   if (rootFrame) {
-    aSizes.mLayoutFramePropertiesSize +=
-      rootFrame->SizeOfFramePropertiesForTree(mallocSizeOf);
+    rootFrame->AddSizeOfExcludingThisForTree(aSizes);
   }
 }
 
 size_t
 PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const
 {
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   if (!rootFrame) {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10683,32 +10683,39 @@ nsFrame::HasCSSAnimations()
 bool
 nsFrame::HasCSSTransitions()
 {
   auto collection =
     AnimationCollection<CSSTransition>::GetAnimationCollection(this);
   return collection && collection->mAnimations.Length() > 0;
 }
 
-size_t
-nsIFrame::SizeOfFramePropertiesForTree(MallocSizeOf aMallocSizeOf) const
-{
-  size_t result = 0;
-
-  result += mProperties.SizeOfExcludingThis(aMallocSizeOf);
+void
+nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const
+{
+  aSizes.mLayoutFramePropertiesSize +=
+    mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
+
+  // We don't do this for Gecko because this stuff is stored in the nsPresArena
+  // and so measured elsewhere.
+  if (mStyleContext->IsServo()) {
+    ServoStyleContext* sc = mStyleContext->AsServo();
+    if (!aSizes.mState.HaveSeenPtr(sc)) {
+      sc->AddSizeOfIncludingThis(aSizes.mState, aSizes.mStyleSizes,
+                                 /* isDOM = */ false);
+    }
+  }
 
   FrameChildListIterator iter(this);
   while (!iter.IsDone()) {
     for (const nsIFrame* f : iter.CurrentList()) {
-      result += f->SizeOfFramePropertiesForTree(aMallocSizeOf);
+      f->AddSizeOfExcludingThisForTree(aSizes);
     }
     iter.Next();
   }
-
-  return result;
 }
 
 // Box layout debugging
 #ifdef DEBUG_REFLOW
 int32_t gIndent2 = 0;
 
 void
 nsAdaptorAddIndents()
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -83,16 +83,17 @@ class gfxSkipChars;
 class gfxSkipCharsIterator;
 class gfxContext;
 class nsLineList_iterator;
 class nsAbsoluteContainingBlock;
 class nsIContent;
 class nsContainerFrame;
 class nsPlaceholderFrame;
 class nsStyleChangeList;
+class nsWindowSizes;
 
 struct nsPeekOffsetStruct;
 struct nsPoint;
 struct nsRect;
 struct nsSize;
 struct nsMargin;
 struct CharacterDataChangeInfo;
 
@@ -3531,18 +3532,20 @@ public:
     mProperties.Delete(aProperty, this);
   }
 
   void DeleteAllProperties()
   {
     mProperties.DeleteAll(this);
   }
 
-  // Reports size of the FrameProperties for this frame and its descendants
-  size_t SizeOfFramePropertiesForTree(mozilla::MallocSizeOf aMallocSizeOf) const;
+  // nsIFrames themselves are in the nsPresArena, and so are not measured here.
+  // Instead, this measures heap-allocated things hanging off the nsIFrame, and
+  // likewise for its descendants.
+  void AddSizeOfExcludingThisForTree(nsWindowSizes& aWindowSizes) const;
 
   /**
    * Return true if and only if this frame obeys visibility:hidden.
    * if it does not, then nsContainerFrame will hide its view even though
    * this means children can't be made visible again.
    */
   virtual bool SupportsVisibilityHidden() { return true; }
 
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -15,18 +15,29 @@
  *
  * Users of this list should define a macro
  * SERVO_BINDING_FUNC(name_, return_, ...)
  * before including this file.
  */
 
 // Element data
 SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
-SERVO_BINDING_FUNC(Servo_Element_SizeOfExcludingThis, size_t, mozilla::MallocSizeOf,
-                   mozilla::SeenPtrs* seen_ptrs, RawGeckoElementBorrowed node)
+SERVO_BINDING_FUNC(Servo_Element_SizeOfExcludingThisAndCVs, size_t,
+                   mozilla::MallocSizeOf, mozilla::SeenPtrs* seen_ptrs,
+                   RawGeckoElementBorrowed node)
+SERVO_BINDING_FUNC(Servo_Element_HasPrimaryComputedValues, bool,
+                   RawGeckoElementBorrowed node)
+SERVO_BINDING_FUNC(Servo_Element_GetPrimaryComputedValues,
+                   ServoStyleContextStrong,
+                   RawGeckoElementBorrowed node)
+SERVO_BINDING_FUNC(Servo_Element_HasPseudoComputedValues, bool,
+                   RawGeckoElementBorrowed node, size_t index)
+SERVO_BINDING_FUNC(Servo_Element_GetPseudoComputedValues,
+                   ServoStyleContextStrong,
+                   RawGeckoElementBorrowed node, size_t index)
 
 // Styleset and Stylesheet management
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetContentsStrong,
                    mozilla::css::Loader* loader,
                    mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
                    mozilla::css::SheetParsingMode parsing_mode,
                    RawGeckoURLExtraData* extra_data,
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -230,16 +230,56 @@ ServoComputedData::ServoComputedData(
 
 const nsStyleVariables*
 ServoComputedData::GetStyleVariables() const
 {
   MOZ_CRASH("ServoComputedData::GetStyleVariables should never need to be "
             "called");
 }
 
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleStructsMallocSizeOf)
+
+void
+ServoComputedData::AddSizeOfExcludingThis(SizeOfState& aState,
+                                          nsStyleSizes& aSizes) const
+{
+  // XXX WARNING: GetStyleFoo() returns an nsStyleFoo pointer. This nsStyleFoo
+  // sits within a servo_arc::Arc, i.e. it is preceded by a word-sized
+  // refcount. So this pointer is an interior pointer. To get the start address
+  // of the heap block we move the pointer back by one word. For this to work,
+  // two things must be true.
+  //
+  // - The layout of servo_arc::Arc must stay the same.
+  //
+  // - The alignment of each nsStyleFoo must not be greater than the size of a
+  //   word (otherwise padding might be inserted between the refcount and the
+  //   struct in the servo_arc::Arc).
+  //
+  // In the long run a better solution here is for mozjemalloc to provide a
+  // function that converts an interior pointer to a start pointer (bug
+  // 1389305), but that's not available right now.
+  //
+  // Also, we use ServoStyleStructsMallocSizeOf rather than
+  // |aState.mMallocSizeOf| to better distinguish in DMD's output the memory
+  // measured here.
+#define STYLE_STRUCT(name_, cb_) \
+  static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \
+                "alignment will break AddSizeOfExcludingThis()"); \
+  const char* p##name_ = reinterpret_cast<const char*>(GetStyle##name_()); \
+  p##name_ -= sizeof(size_t); \
+  if (!aState.HaveSeenPtr(p##name_)) { \
+    aSizes.NS_STYLE_SIZES_FIELD(name_) += \
+      ServoStyleStructsMallocSizeOf(p##name_); \
+  }
+  #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
+}
+
 void
 Gecko_ServoStyleContext_Destroy(ServoStyleContext* aContext)
 {
   aContext->~ServoStyleContext();
 }
 
 void
 Gecko_ConstructStyleChildrenIterator(
--- a/layout/style/ServoStyleContext.h
+++ b/layout/style/ServoStyleContext.h
@@ -2,24 +2,29 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoStyleContext_h
 #define mozilla_ServoStyleContext_h
 
+#include "nsIMemoryReporter.h"
 #include "nsStyleContext.h"
+#include "nsWindowSizes.h"
+#include <algorithm>
 
 namespace mozilla {
 
 namespace dom {
 class Element;
 } // namespace dom
 
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoComputedValuesMallocSizeOf)
+
 class ServoStyleContext final : public nsStyleContext
 {
 public:
   ServoStyleContext(nsPresContext* aPresContext,
                     nsIAtom* aPseudoTag,
                     CSSPseudoElementType aPseudoType,
                     ServoComputedDataForgotten aComputedValues);
 
@@ -90,16 +95,39 @@ public:
   }
 
   /**
    * Makes this context match |aOther| in terms of which style structs have
    * been resolved.
    */
   inline void ResolveSameStructsAs(const ServoStyleContext* aOther);
 
+  void AddSizeOfIncludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
+                              bool aIsDOM) const
+  {
+    // XXX WARNING: similar to ServoComputedData::AddSizeOfExcludingThis(),
+    // but here we need to step back 4 or 8 bytes to get past the servo_arc::Arc
+    // refcount to the base pointer.
+    static_assert(alignof(ServoStyleContext) == 4 ||
+                  alignof(ServoStyleContext) == 8,
+                  "alignment will break AddSizeOfExcludingThis()");
+    const char* p = reinterpret_cast<const char*>(this);
+    p -= std::max(sizeof(size_t), alignof(ServoStyleContext));
+
+    // We use ServoComputedValuesMallocSizeOf rather than
+    // |aState.mMallocSizeOf| to better distinguish in DMD's output the memory
+    // measured here.
+    if (aIsDOM) {
+      aSizes.mComputedValuesDom += ServoComputedValuesMallocSizeOf(p);
+    } else {
+      aSizes.mComputedValuesNonDom += ServoComputedValuesMallocSizeOf(p);
+    }
+    mSource.AddSizeOfExcludingThis(aState, aSizes);
+  }
+
 private:
   nsPresContext* mPresContext;
   ServoComputedData mSource;
 
   // A linked-list cache of inheriting anon boxes inheriting from this style _if
   // the style isn't an inheriting anon-box_.
   //
   // Otherwise it represents the next entry in the cache of the parent style
--- a/layout/style/ServoTypes.h
+++ b/layout/style/ServoTypes.h
@@ -13,19 +13,22 @@
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 /*
  * Type definitions used to interact with Servo. This gets included by nsINode,
  * so don't add significant include dependencies to this file.
  */
 
+struct nsStyleSizes;
 struct ServoNodeData;
 namespace mozilla {
 
+class SizeOfState;
+
 /*
  * Replaced types. These get mapped to associated Servo types in bindgen.
  */
 
 template<typename T>
 struct ServoUnsafeCell {
   T value;
 
@@ -217,16 +220,19 @@ public:
   mozilla::ServoRawOffsetArc<mozilla::Gecko##name_> name_; \
   inline const nsStyle##name_* GetStyle##name_() const;
   #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
   const nsStyleVariables* GetStyleVariables() const;
 
+  void AddSizeOfExcludingThis(mozilla::SizeOfState& aState,
+                              nsStyleSizes& aSizes) const;
+
 private:
   mozilla::ServoCustomPropertiesMap custom_properties;
   mozilla::ServoWritingMode writing_mode;
   mozilla::ServoComputedValueFlags flags;
   /// The rule node representing the ordered list of rules matched for this
   /// node.  Can be None for default values and text nodes.  This is
   /// essentially an optimization to avoid referencing the root rule node.
   mozilla::ServoRuleNode rules;
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -87,16 +87,19 @@ public:
 
   static bool IsCSS2PseudoElement(nsIAtom *aAtom);
 
   static bool IsEagerlyCascadedInServo(const Type aType)
   {
     return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_CSS2);
   }
 
+  // This must match EAGER_PSEUDO_COUNT in Rust code.
+  static const size_t kEagerPseudoCount = 4;
+
 #define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \
   static nsICSSPseudoElement* _name;
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 
   static Type GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState);
 
   // Get the atom for a given Type.  aType must be < CSSPseudoElementType::Count
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -1672,17 +1672,19 @@ ReportHelper(const void* aPtr, bool aRep
   AutoBlockIntercepts block(t);
   AutoLockState lock;
 
   if (LiveBlockTable::Ptr p = gLiveBlockTable->lookup(aPtr)) {
     p->Report(t, aReportedOnAlloc);
   } else {
     // We have no record of the block. It must be a bogus pointer. This should
     // be extremely rare because Report() is almost always called in
-    // conjunction with a malloc_size_of-style function.
+    // conjunction with a malloc_size_of-style function. Print a message so
+    // that we get some feedback.
+    StatusMsg("Unknown pointer %p\n", aPtr);
   }
 }
 
 void
 DMDFuncs::Report(const void* aPtr)
 {
   ReportHelper(aPtr, /* onAlloc */ false);
 }
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -11,17 +11,17 @@ use properties::ComputedValues;
 use properties::longhands::display::computed_value as display;
 use rule_tree::StrongRuleNode;
 use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
 use servo_arc::Arc;
 use shared_lock::StylesheetGuards;
 use std::fmt;
 use std::ops::{Deref, DerefMut};
 #[cfg(feature = "gecko")]
-use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
+use stylesheets::SizeOfState;
 
 bitflags! {
     flags RestyleFlags: u8 {
         /// Whether the styles changed for this restyle.
         const WAS_RESTYLED = 1 << 0,
         /// Whether the last traversal of this element did not do
         /// any style computation. This is not true during the initial
         /// styling pass, nor is it true when we restyle (in which case
@@ -256,42 +256,39 @@ impl ElementStyles {
     pub fn primary(&self) -> &Arc<ComputedValues> {
         self.primary.as_ref().unwrap()
     }
 
     /// Whether this element `display` value is `none`.
     pub fn is_display_none(&self) -> bool {
         self.primary().get_box().clone_display() == display::T::none
     }
+
+    #[cfg(feature = "gecko")]
+    fn malloc_size_of_children_excluding_cvs(&self, _state: &mut SizeOfState) -> usize {
+        // As the method name suggests, we don't measures the ComputedValues
+        // here, because they are measured on the C++ side.
+
+        // XXX: measure the EagerPseudoArray itself, but not the ComputedValues
+        // within it.
+
+        0
+    }
 }
 
 // We manually implement Debug for ElementStyles so that we can avoid the
 // verbose stringification of every property in the ComputedValues. We
 // substitute the rule node instead.
 impl fmt::Debug for ElementStyles {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "ElementStyles {{ primary: {:?}, pseudos: {:?} }}",
                self.primary.as_ref().map(|x| &x.rules), self.pseudos)
     }
 }
 
-#[cfg(feature = "gecko")]
-impl MallocSizeOfWithRepeats for ElementStyles {
-    fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
-        let mut n = 0;
-        if let Some(ref primary) = self.primary {
-            n += primary.malloc_size_of_children(state)
-        };
-
-        // We may measure more fields in the future if DMD says it's worth it.
-
-        n
-    }
-}
-
 /// Style system data associated with an Element.
 ///
 /// In Gecko, this hangs directly off the Element. Servo, this is embedded
 /// inside of layout data, which itself hangs directly off the Element. In
 /// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
 #[derive(Debug, Default)]
 pub struct ElementData {
     /// The styles for the element and its pseudo-elements.
@@ -431,20 +428,19 @@ impl ElementData {
     pub fn clear_restyle_state(&mut self) {
         self.restyle.clear_restyle_state();
     }
 
     /// Drops restyle flags and damage from the element.
     pub fn clear_restyle_flags_and_damage(&mut self) {
         self.restyle.clear_restyle_flags_and_damage();
     }
-}
 
-#[cfg(feature = "gecko")]
-impl MallocSizeOfWithRepeats for ElementData {
-    fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
-        let n = self.styles.malloc_size_of_children(state);
+    /// Measures memory usage.
+    #[cfg(feature = "gecko")]
+    pub fn malloc_size_of_children_excluding_cvs(&self, state: &mut SizeOfState) -> usize {
+        let n = self.styles.malloc_size_of_children_excluding_cvs(state);
 
         // We may measure more fields in the future if DMD says it's worth it.
 
         n
     }
 }
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -1906,22 +1906,37 @@ extern "C" {
 }
 extern "C" {
     pub fn Gecko_SetJemallocThreadLocalArena(enabled: bool);
 }
 extern "C" {
     pub fn Servo_Element_ClearData(node: RawGeckoElementBorrowed);
 }
 extern "C" {
-    pub fn Servo_Element_SizeOfExcludingThis(arg1: MallocSizeOf,
-                                             seen_ptrs: *mut SeenPtrs,
-                                             node: RawGeckoElementBorrowed)
+    pub fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: MallocSizeOf,
+                                                   seen_ptrs: *mut SeenPtrs,
+                                                   node: RawGeckoElementBorrowed)
      -> usize;
 }
 extern "C" {
+    pub fn Servo_Element_HasPrimaryComputedValues(element: RawGeckoElementBorrowed) -> bool;
+}
+extern "C" {
+    pub fn Servo_Element_GetPrimaryComputedValues(element: RawGeckoElementBorrowed)
+     -> ServoStyleContextStrong;
+}
+extern "C" {
+    pub fn Servo_Element_HasPseudoComputedValues(element: RawGeckoElementBorrowed, index: usize)
+     -> bool;
+}
+extern "C" {
+    pub fn Servo_Element_GetPseudoComputedValues(element: RawGeckoElementBorrowed, index: usize)
+     -> ServoStyleContextStrong;
+}
+extern "C" {
     pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader,
                                           gecko_stylesheet:
                                               *mut ServoStyleSheet,
                                           data: *const nsACString,
                                           parsing_mode: SheetParsingMode,
                                           extra_data:
                                               *mut RawGeckoURLExtraData,
                                           line_number_offset: u32,
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -55,17 +55,16 @@ use properties::animated_properties::Tra
 use properties::computed_value_flags::ComputedValueFlags;
 use properties::{longhands, FontComputationData, Importance, LonghandId};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
 use rule_tree::StrongRuleNode;
 use selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
-use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
 use values::{self, Auto, CustomIdent, Either, KeyframesName};
 use values::computed::{NonNegativeAu, ToComputedValue, Percentage};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
@@ -364,28 +363,16 @@ impl ComputedValuesInner {
                 % endif
             % endfor
             PropertyDeclarationId::Custom(_name) => unimplemented!(),
             _ => unimplemented!()
         }
     }
 }
 
-impl MallocSizeOfWithRepeats for ComputedValues {
-    fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
-        let mut n = 0;
-        if let Some(ref raw_offset_arc) = *self.get_raw_visited_style() {
-            n += raw_offset_arc.with_arc(|a: &Arc<ComputedValues>| {
-                a.malloc_size_of_children(state)
-            })
-        }
-        n
-    }
-}
-
 <%def name="declare_style_struct(style_struct)">
 pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name};
 impl ${style_struct.gecko_struct_name} {
     pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} {
         &self.gecko
     }
     pub fn gecko_mut(&mut self) -> &mut ${style_struct.gecko_ffi_name} {
         &mut self.gecko
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -105,19 +105,18 @@ use style::properties::parse_one_declara
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 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::{MallocSizeOfWithRepeats, MediaRule};
-use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule};
-use style::stylesheets::StylesheetContents;
+use style::stylesheets::{MediaRule, NamespaceRule, Origin, 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, TraversalDriver};
 use style::traversal::resolve_style;
@@ -754,34 +753,71 @@ pub extern "C" fn Servo_StyleWorkerThrea
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) {
     unsafe { GeckoElement(element).clear_data() };
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_Element_SizeOfExcludingThis(malloc_size_of: MallocSizeOf,
-                                                    seen_ptrs: *mut SeenPtrs,
-                                                    element: RawGeckoElementBorrowed) -> usize {
+pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: MallocSizeOf,
+                                                          seen_ptrs: *mut SeenPtrs,
+                                                          element: RawGeckoElementBorrowed) -> usize {
     let malloc_size_of = 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(&mut state)
+        (*data).malloc_size_of_children_excluding_cvs(&mut state)
     } else {
         0
     }
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_Element_HasPrimaryComputedValues(element: RawGeckoElementBorrowed) -> bool
+{
+    let element = GeckoElement(element);
+    let data = element.borrow_data().expect("Looking for CVs on unstyled element");
+    data.has_styles()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_GetPrimaryComputedValues(element: RawGeckoElementBorrowed)
+                                                         -> ServoStyleContextStrong
+{
+    let element = GeckoElement(element);
+    let data = element.borrow_data().expect("Getting CVs on unstyled element");
+    assert!(data.has_styles(), "Getting CVs on unstyled element");
+    data.styles.primary().clone().into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_HasPseudoComputedValues(element: RawGeckoElementBorrowed,
+                                                        index: usize) -> bool
+{
+    let element = GeckoElement(element);
+    let data = element.borrow_data().expect("Looking for CVs on unstyled element");
+    data.styles.pseudos.as_array()[index].is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_GetPseudoComputedValues(element: RawGeckoElementBorrowed,
+                                                        index: usize) -> ServoStyleContextStrong
+{
+    let element = GeckoElement(element);
+    let data = element.borrow_data().expect("Getting CVs on unstyled element");
+    data.styles.pseudos.as_array()[index].as_ref().expect("Getting CVs on unstyled element")
+        .clone().into()
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetContentsStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let origin = match mode {
         SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
         SheetParsingMode::eUserSheetFeatures => Origin::User,
         SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
         SheetParsingMode::eSafeAgentSheetFeatures => Origin::UserAgent,
     };