Bug 1373018 - Part 7: stylo: Move nsStyleContext::mSource into subclasses; r=bholley
authorManish Goregaokar <manishearth@gmail.com>
Sat, 10 Jun 2017 22:27:45 -0700
changeset 364279 305613bee6fb16a5f33077cc52807dcc5713f9d9
parent 364278 3dcb1623e11548d9f4abc60de307d0de51ae52d6
child 364280 02462591f243f41dc94a71e4e1b0c30e06016e46
push id44850
push usermanishearth@gmail.com
push dateFri, 16 Jun 2017 04:29:37 +0000
treeherderautoland@64a2ba65f0d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1373018
milestone56.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
Bug 1373018 - Part 7: stylo: Move nsStyleContext::mSource into subclasses; r=bholley MozReview-Commit-ID: AspYUJ7lGqD
layout/style/GeckoStyleContext.cpp
layout/style/GeckoStyleContext.h
layout/style/ServoStyleContext.cpp
layout/style/ServoStyleContext.h
layout/style/StyleAnimationValue.cpp
layout/style/StyleContextSource.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
layout/style/nsStyleContextInlines.h
--- a/layout/style/GeckoStyleContext.cpp
+++ b/layout/style/GeckoStyleContext.cpp
@@ -17,37 +17,37 @@
 
 using namespace mozilla;
 
 GeckoStyleContext::GeckoStyleContext(nsStyleContext* aParent,
                                      nsIAtom* aPseudoTag,
                                      CSSPseudoElementType aPseudoType,
                                      already_AddRefed<nsRuleNode> aRuleNode,
                                      bool aSkipParentDisplayBasedStyleFixup)
-  : nsStyleContext(aParent, OwningStyleContextSource(Move(aRuleNode)),
-                   aPseudoTag, aPseudoType)
+  : nsStyleContext(aParent, aPseudoTag, aPseudoType)
   , mChild(nullptr)
   , mEmptyChild(nullptr)
+  , mRuleNode(Move(aRuleNode))
 {
   mBits |= NS_STYLE_CONTEXT_IS_GECKO;
 
   if (aParent) {
 #ifdef DEBUG
-    nsRuleNode *r1 = mParent->RuleNode(), *r2 = mSource.AsGeckoRuleNode();
+    nsRuleNode *r1 = mParent->RuleNode(), *r2 = mRuleNode;
     while (r1->GetParent())
       r1 = r1->GetParent();
     while (r2->GetParent())
       r2 = r2->GetParent();
     NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
 #endif
   } else {
     PresContext()->PresShell()->StyleSet()->RootStyleContextAdded();
   }
 
-  mSource.AsGeckoRuleNode()->SetUsedDirectly(); // before ApplyStyleFixups()!
+  mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()!
   // FinishConstruction() calls AddChild which needs these
   // to be initialized!
   mNextSibling = this;
   mPrevSibling = this;
 
   FinishConstruction();
   ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
 }
@@ -66,17 +66,23 @@ GeckoStyleContext::operator new(size_t s
 
 void
 GeckoStyleContext::AddChild(GeckoStyleContext* aChild)
 {
   NS_ASSERTION(aChild->mPrevSibling == aChild &&
                aChild->mNextSibling == aChild,
                "child already in a child list");
 
-  GeckoStyleContext **listPtr = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
+  GeckoStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
+  if (const nsRuleNode* source = aChild->mRuleNode) {
+    if (source->IsRoot()) {
+      listPtr = &mEmptyChild;
+    }
+  }
+
   // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
   // etc. don't alias with what ever listPtr points at.
   GeckoStyleContext *list = *listPtr;
 
   // Insert at the beginning of the list.  See also FindChildWithRules.
   if (list) {
     // Link into existing elements, if there are any.
     aChild->mNextSibling = list;
@@ -87,17 +93,18 @@ GeckoStyleContext::AddChild(GeckoStyleCo
   (*listPtr) = aChild;
 }
 
 void
 GeckoStyleContext::RemoveChild(GeckoStyleContext* aChild)
 {
   NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
 
-  GeckoStyleContext **list = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
+  MOZ_ASSERT(aChild->mRuleNode, "child context should have rule node");
+  GeckoStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
 
   if (aChild->mPrevSibling != aChild) { // has siblings
     if ((*list) == aChild) {
       (*list) = (*list)->mNextSibling;
     }
   }
   else {
     NS_ASSERTION((*list) == aChild, "bad sibling pointers");
@@ -184,40 +191,40 @@ GeckoStyleContext::DoClearCachedInherite
 
   if (aStructs == 0) {
     return;
   }
 
   ClearCachedInheritedStyleDataOnDescendants(aStructs);
 }
 
-
 already_AddRefed<GeckoStyleContext>
 GeckoStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
-                                   NonOwningStyleContextSource aSource,
-                                   NonOwningStyleContextSource aSourceIfVisited,
+                                   nsRuleNode* aSource,
+                                   nsRuleNode* aSourceIfVisited,
                                    bool aRelevantLinkVisited)
 {
   uint32_t threshold = 10; // The # of siblings we're willing to examine
                            // before just giving this whole thing up.
 
   RefPtr<GeckoStyleContext> result;
-  GeckoStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
+  MOZ_ASSERT(aSource);
+  GeckoStyleContext *list = aSource->IsRoot() ? mEmptyChild : mChild;
 
   if (list) {
     GeckoStyleContext *child = list;
     do {
-      if (child->mSource.AsRaw() == aSource &&
+      if (child->StyleSource() == aSource &&
           child->mPseudoTag == aPseudoTag &&
           !child->IsStyleIfVisited() &&
           child->RelevantLinkVisited() == aRelevantLinkVisited) {
         bool match = false;
-        if (!aSourceIfVisited.IsNull()) {
+        if (aSourceIfVisited) {
           match = child->GetStyleIfVisited() &&
-                  child->GetStyleIfVisited()->AsGecko()->mSource.AsRaw() == aSourceIfVisited;
+                  child->GetStyleIfVisited()->RuleNode() == aSourceIfVisited;
         } else {
           match = !child->GetStyleIfVisited();
         }
         if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
           result = child;
           break;
         }
       }
@@ -343,16 +350,17 @@ GeckoStyleContext::SetIneligibleForShari
     GeckoStyleContext* child = mEmptyChild;
     do {
       child->SetIneligibleForSharing();
       child = child->mNextSibling;
     } while (mEmptyChild != child);
   }
 }
 
+#ifdef RESTYLE_LOGGING
 void
 GeckoStyleContext::LogChildStyleContextTree(uint32_t aStructs) const
 {
   if (nullptr != mChild) {
     GeckoStyleContext* child = mChild;
     do {
       child->LogStyleContextTree(false, aStructs);
       child = child->mNextSibling;
@@ -361,16 +369,17 @@ GeckoStyleContext::LogChildStyleContextT
   if (nullptr != mEmptyChild) {
     GeckoStyleContext* child = mEmptyChild;
     do {
       child->LogStyleContextTree(false, aStructs);
       child = child->mNextSibling;
     } while (mEmptyChild != child);
   }
 }
+#endif
 
 static bool
 ShouldSuppressLineBreak(const nsStyleContext* aContext,
                         const nsStyleDisplay* aDisplay,
                         const nsStyleContext* aParentContext,
                         const nsStyleDisplay* aParentDisplay)
 {
   // The display change should only occur for "in-flow" children
@@ -506,19 +515,16 @@ GeckoStyleContext::AssertChildStructsNot
     } while (child != mEmptyChild);
   }
 }
 #endif
 
 void
 GeckoStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)
 {
-  MOZ_ASSERT(!mSource.IsServoComputedValues(),
-             "Can't do Gecko style fixups on Servo values");
-
 #define GET_UNIQUE_STYLE_DATA(name_) \
   static_cast<nsStyle##name_*>(GetUniqueStyleData(eStyleStruct_##name_))
 
   // CSS Inline Layout Level 3 - 3.5 Sizing Initial Letters:
   // For an N-line drop initial in a Western script, the cap-height of the
   // letter needs to be (N – 1) times the line-height, plus the cap-height
   // of the surrounding text.
   if (mPseudoTag == nsCSSPseudoElements::firstLetter) {
@@ -732,19 +738,19 @@ GeckoStyleContext::ApplyStyleFixups(bool
    * If a box has a different block flow direction than its containing block:
    *   * If the box has a specified display of inline, its display computes
    *     to inline-block. [CSS21]
    *   ...etc.
    */
   if (disp->mDisplay == mozilla::StyleDisplay::Inline &&
       !nsCSSAnonBoxes::IsNonElement(mPseudoTag) &&
       mParent) {
-    auto cbContext = mParent;
+    auto cbContext = GetParent();
     while (cbContext->StyleDisplay()->mDisplay == mozilla::StyleDisplay::Contents) {
-      cbContext = cbContext->mParent;
+      cbContext = cbContext->GetParent();
     }
     MOZ_ASSERT(cbContext, "the root context can't have display:contents");
     // We don't need the full mozilla::WritingMode value (incorporating dir
     // and text-orientation) here; just the writing-mode property is enough.
     if (StyleVisibility()->mWritingMode !=
           cbContext->StyleVisibility()->mWritingMode) {
       nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
       disp = mutable_display;
@@ -757,9 +763,9 @@ GeckoStyleContext::ApplyStyleFixups(bool
   StyleUserInterface();
 #undef GET_UNIQUE_STYLE_DATA
 }
 
 bool
 GeckoStyleContext::HasNoChildren() const
 {
   return (nullptr == mChild) && (nullptr == mEmptyChild);
-}
\ No newline at end of file
+}
--- a/layout/style/GeckoStyleContext.h
+++ b/layout/style/GeckoStyleContext.h
@@ -17,73 +17,91 @@ public:
                     nsIAtom* aPseudoTag,
                     CSSPseudoElementType aPseudoType,
                     already_AddRefed<nsRuleNode> aRuleNode,
                     bool aSkipParentDisplayBasedStyleFixup);
 
   void* operator new(size_t sz, nsPresContext* aPresContext);
 
   nsPresContext* PresContext() const {
-    return mSource.AsGeckoRuleNode()->PresContext();
+    return RuleNode()->PresContext();
   }
 
   void AddChild(GeckoStyleContext* aChild);
   void RemoveChild(GeckoStyleContext* aChild);
 
   void* GetUniqueStyleData(const nsStyleStructID& aSID);
   void* CreateEmptyStyleData(const nsStyleStructID& aSID);
 
 
   /**
    * Sets the NS_STYLE_INELIGIBLE_FOR_SHARING bit on this style context
    * and its descendants.  If it finds a descendant that has the bit
    * already set, assumes that it can skip that subtree.
    */
   void SetIneligibleForSharing();
-  void LogChildStyleContextTree(uint32_t aStructs) const;
   /**
    * On each descendant of this style context, clears out any cached inherited
    * structs indicated in aStructs.
    */
   void ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
   // Find, if it already exists *and is easily findable* (i.e., near the
   // start of the child list), a style context whose:
   //  * GetPseudo() matches aPseudoTag
-  //  * mSource matches aSource
+  //  * mRuleNode matches aSource
   //  * !!GetStyleIfVisited() == !!aSourceIfVisited, and, if they're
-  //    non-null, GetStyleIfVisited()->mSource == aSourceIfVisited
+  //    non-null, GetStyleIfVisited()->mRuleNode == aSourceIfVisited
   //  * RelevantLinkVisited() == aRelevantLinkVisited
   already_AddRefed<GeckoStyleContext>
   FindChildWithRules(const nsIAtom* aPseudoTag,
-                     mozilla::NonOwningStyleContextSource aSource,
-                     mozilla::NonOwningStyleContextSource aSourceIfVisited,
+                     nsRuleNode* aSource,
+                     nsRuleNode* aSourceIfVisited,
                      bool aRelevantLinkVisited);
 
 #ifdef DEBUG
   void AssertChildStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
                                           int32_t aLevels) const;
   void ListDescendants(FILE* out, int32_t aIndent);
+
+#endif
+
+#ifdef RESTYLE_LOGGING
+  void LogChildStyleContextTree(uint32_t aStructs) const;
 #endif
 
   // Only called for Gecko-backed nsStyleContexts.
   void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup);
 
   bool HasNoChildren() const;
 
+  NonOwningStyleContextSource StyleSource() const {
+    return NonOwningStyleContextSource(mRuleNode);
+  }
+
+  nsRuleNode* RuleNode() const {
+    MOZ_ASSERT(mRuleNode);
+    return mRuleNode;
+  }
+
+  ~GeckoStyleContext() {
+    Destructor();
+  }
+
 private:
   // Helper for ClearCachedInheritedStyleDataOnDescendants.
   void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
 
   // Children are kept in two circularly-linked lists.  The list anchor
   // is not part of the list (null for empty), and we point to the first
   // child.
   // mEmptyChild for children whose rule node is the root rule node, and
   // mChild for other children.  The order of children is not
   // meaningful.
   GeckoStyleContext* mChild;
   GeckoStyleContext* mEmptyChild;
   GeckoStyleContext* mPrevSibling;
   GeckoStyleContext* mNextSibling;
+  RefPtr<nsRuleNode> mRuleNode;
 };
 
 }
 
 #endif // mozilla_GeckoStyleContext_h
--- a/layout/style/ServoStyleContext.cpp
+++ b/layout/style/ServoStyleContext.cpp
@@ -13,18 +13,18 @@
 
 using namespace mozilla;
 
 ServoStyleContext::ServoStyleContext(nsStyleContext* aParent,
                                nsPresContext* aPresContext,
                                nsIAtom* aPseudoTag,
                                CSSPseudoElementType aPseudoType,
                                already_AddRefed<ServoComputedValues> aComputedValues)
-  : nsStyleContext(aParent, OwningStyleContextSource(Move(aComputedValues)),
-                   aPseudoTag, aPseudoType)
+  : nsStyleContext(aParent, aPseudoTag, aPseudoType),
+  mSource(Move(aComputedValues))
 {
   mPresContext = aPresContext;
 
   FinishConstruction();
 
   // No need to call ApplyStyleFixups here, since fixups are handled by Servo when
   // producing the ServoComputedValues.
 }
--- a/layout/style/ServoStyleContext.h
+++ b/layout/style/ServoStyleContext.h
@@ -18,15 +18,25 @@ public:
                     nsIAtom* aPseudoTag,
                     CSSPseudoElementType aPseudoType,
                     already_AddRefed<ServoComputedValues> aComputedValues);
 
   nsPresContext* PresContext() const {
     return mPresContext;
   }
 
+  NonOwningStyleContextSource StyleSource() const {
+    return NonOwningStyleContextSource(mSource);
+  }
+  ServoComputedValues* ComputedValues() const {
+    return mSource;
+  }
+  ~ServoStyleContext() {
+    Destructor();
+  }
 private:
   nsPresContext* mPresContext;
+  RefPtr<ServoComputedValues> mSource;
 };
 
 }
 
 #endif // mozilla_ServoStyleContext_h
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -33,16 +33,17 @@
 #include "mozilla/KeyframeUtils.h" // KeyframeUtils::ParseProperty
 #include "mozilla/Likely.h"
 #include "mozilla/ServoBindings.h" // RawServoDeclarationBlock
 #include "gfxMatrix.h"
 #include "gfxQuaternion.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "gfx2DGlue.h"
+#include "nsStyleContextInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::gfx;
 using nsStyleTransformMatrix::Decompose2DMatrix;
 using nsStyleTransformMatrix::Decompose3DMatrix;
 using nsStyleTransformMatrix::ShearType;
 
--- a/layout/style/StyleContextSource.h
+++ b/layout/style/StyleContextSource.h
@@ -65,97 +65,15 @@ struct NonOwningStyleContextSource
     return reinterpret_cast<nsRuleNode*>(mBits);
   }
 
   const ServoComputedValues* AsServoComputedValues() const {
     MOZ_ASSERT(IsServoComputedValues());
     return reinterpret_cast<ServoComputedValues*>(mBits & ~1);
   }
 
-  bool MatchesNoRules() const {
-    if (IsGeckoRuleNodeOrNull()) {
-      return AsGeckoRuleNode()->IsRoot();
-    }
-
-    // Just assume a Servo-backed StyleContextSource always matches some rules.
-    //
-    // MatchesNoRules is used to ensure style contexts that match no rules
-    // go into a separate mEmptyChild list on their parent.  This is only used
-    // as an optimization so that calling FindChildWithRules for style context
-    // sharing is faster for text nodes (which match no rules, and are common).
-    // Since Servo will handle sharing for us, there's no need to split children
-    // into two lists.
-    return false;
-  }
-
 private:
   uintptr_t mBits;
 };
 
-// Higher-level struct that owns a strong reference to the source. The source
-// is never null.
-struct OwningStyleContextSource
-{
-  explicit OwningStyleContextSource(already_AddRefed<nsRuleNode> aRuleNode)
-    : mRaw(aRuleNode.take())
-  {
-    MOZ_COUNT_CTOR(OwningStyleContextSource);
-    MOZ_ASSERT(!mRaw.IsNull());
-  };
-
-  explicit OwningStyleContextSource(already_AddRefed<ServoComputedValues> aComputedValues)
-    : mRaw(aComputedValues.take())
-  {
-    MOZ_COUNT_CTOR(OwningStyleContextSource);
-    MOZ_ASSERT(!mRaw.IsNull());
-  }
-
-  OwningStyleContextSource(OwningStyleContextSource&& aOther)
-    : mRaw(aOther.mRaw)
-  {
-    MOZ_COUNT_CTOR(OwningStyleContextSource);
-    aOther.mRaw = nullptr;
-  }
-
-  OwningStyleContextSource& operator=(OwningStyleContextSource&) = delete;
-  OwningStyleContextSource(OwningStyleContextSource&) = delete;
-
-  ~OwningStyleContextSource() {
-    MOZ_COUNT_DTOR(OwningStyleContextSource);
-    if (mRaw.IsNull()) {
-      // We must have invoked the move constructor.
-    } else if (IsGeckoRuleNode()) {
-      RefPtr<nsRuleNode> releaseme = dont_AddRef(AsGeckoRuleNode());
-    } else {
-      MOZ_ASSERT(IsServoComputedValues());
-      RefPtr<ServoComputedValues> releaseme =
-        dont_AddRef(AsServoComputedValues());
-    }
-  }
-
-  bool operator==(const OwningStyleContextSource& aOther) const {
-    return mRaw == aOther.mRaw;
-  }
-  bool operator!=(const OwningStyleContextSource& aOther) const {
-    return !(*this == aOther);
-  }
-  bool IsNull() const { return mRaw.IsNull(); }
-  bool IsGeckoRuleNode() const {
-    MOZ_ASSERT(!mRaw.IsNull());
-    return mRaw.IsGeckoRuleNodeOrNull();
-  }
-  bool IsServoComputedValues() const { return mRaw.IsServoComputedValues(); }
-
-  NonOwningStyleContextSource AsRaw() const { return mRaw; }
-  nsRuleNode* AsGeckoRuleNode() const { return mRaw.AsGeckoRuleNode(); }
-  ServoComputedValues* AsServoComputedValues() const {
-    return const_cast<ServoComputedValues*>(mRaw.AsServoComputedValues());
-  }
-
-  bool MatchesNoRules() const { return mRaw.MatchesNoRules(); }
-
-private:
-  NonOwningStyleContextSource mRaw;
-};
-
 } // namespace mozilla
 
 #endif // mozilla_StyleContextSource_h
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -46,16 +46,17 @@
 #include "nsDisplayList.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsStyleTransformMatrix.h"
 #include "mozilla/dom/Element.h"
 #include "prtime.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/AppUnits.h"
 #include <algorithm>
+#include "nsStyleContextInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
 #define DEBUG_ComputedDOMStyle
 #endif
 
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -77,22 +77,20 @@ const uint32_t nsStyleContext::sDependen
 #undef STYLE_STRUCT_END
 };
 
 // Whether to perform expensive assertions in the nsStyleContext destructor.
 static bool sExpensiveStyleStructAssertionsEnabled;
 #endif
 
 nsStyleContext::nsStyleContext(nsStyleContext* aParent,
-                               OwningStyleContextSource&& aSource,
                                nsIAtom* aPseudoTag,
                                CSSPseudoElementType aPseudoType)
   : mParent(aParent)
   , mPseudoTag(aPseudoTag)
-  , mSource(Move(aSource))
   , mCachedResetData(nullptr)
   , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
   , mRefCnt(0)
 #ifdef DEBUG
   , mFrameRefCnt(0)
   , mComputingStruct(nsStyleStructID_None)
 #endif
 {}
@@ -101,17 +99,17 @@ void
 nsStyleContext::FinishConstruction()
 {
   // This check has to be done "backward", because if it were written the
   // more natural way it wouldn't fail even when it needed to.
   static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
                  static_cast<CSSPseudoElementTypeBase>(
                    CSSPseudoElementType::MAX),
                 "pseudo element bits no longer fit in a uint64_t");
-  MOZ_ASSERT(!mSource.IsNull());
+  MOZ_ASSERT(!StyleSource().IsNull());
 
 #ifdef DEBUG
   static_assert(MOZ_ARRAY_LENGTH(nsStyleContext::sDependencyTable)
                   == nsStyleStructID_Length,
                 "Number of items in dependency table doesn't match IDs");
 #endif
 
   if (mParent) {
@@ -121,25 +119,26 @@ nsStyleContext::FinishConstruction()
   SetStyleBits();
 
   #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
   static_assert(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
                 "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
   #undef eStyleStruct_LastItem
 }
 
-nsStyleContext::~nsStyleContext()
+void
+nsStyleContext::Destructor()
 {
   if (const GeckoStyleContext* gecko = GetAsGecko()) {
     NS_ASSERTION(gecko->HasNoChildren(), "destructing context with children");
   }
-  MOZ_ASSERT(!mSource.IsServoComputedValues() || !mCachedResetData);
+  MOZ_ASSERT(!IsServo() || !mCachedResetData);
 
 #ifdef DEBUG
-  if (mSource.IsServoComputedValues()) {
+  if (IsServo()) {
     MOZ_ASSERT(!mCachedResetData,
                "Servo shouldn't cache reset structs in nsStyleContext");
     for (const auto* data : mCachedInheritedData.mStyleStructs) {
       MOZ_ASSERT(!data,
                  "Servo shouldn't cache inherit structs in nsStyleContext");
     }
   }
 
@@ -158,17 +157,17 @@ nsStyleContext::~nsStyleContext()
     // check just of the children of this style context.
     AssertStructsNotUsedElsewhere(this, 2);
   }
 #endif
 
   nsPresContext *presContext = PresContext();
   DebugOnly<nsStyleSet*> geckoStyleSet = presContext->PresShell()->StyleSet()->GetAsGecko();
   NS_ASSERTION(!geckoStyleSet ||
-               geckoStyleSet->GetRuleTree() == mSource.AsGeckoRuleNode()->RuleTree() ||
+               geckoStyleSet->GetRuleTree() == AsGecko()->RuleNode()->RuleTree() ||
                geckoStyleSet->IsInRuleTreeReconstruct(),
                "destroying style context from old rule tree too late");
 
   if (mParent) {
     mParent->RemoveChild(this);
   } else {
     presContext->StyleSet()->RootStyleContextRemoved();
   }
@@ -321,18 +320,18 @@ nsStyleContext::MoveTo(nsStyleContext* a
 
 const void* nsStyleContext::StyleData(nsStyleStructID aSID)
 {
   const void* cachedData = GetCachedStyleData(aSID);
   if (cachedData)
     return cachedData; // We have computed data stored on this node in the context tree.
   // Our style source will take care of it for us.
   const void* newData;
-  if (mSource.IsGeckoRuleNode()) {
-    newData = mSource.AsGeckoRuleNode()->GetStyleData(aSID, this, true);
+  if (IsGecko()) {
+    newData = AsGecko()->RuleNode()->GetStyleData(aSID, this, true);
     if (!nsCachedStyleData::IsReset(aSID)) {
       // always cache inherited data on the style context; the rule
       // node set the bit in mBits for us if needed.
       mCachedInheritedData.mStyleStructs[aSID] = const_cast<void*>(newData);
     }
   } else {
     newData = StyleStructFromServoComputedValues(aSID);
 
@@ -365,17 +364,17 @@ const void* nsStyleContext::StyleData(ns
     SetStyle(aSID, const_cast<void*>(newData));
   }
   return newData;
 }
 
 void
 nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
 {
-  MOZ_ASSERT(!mSource.IsServoComputedValues(),
+  MOZ_ASSERT(!IsServo(),
              "Servo shouldn't cache style structs in the style context!");
   // This method should only be called from nsRuleNode!  It is not a public
   // method!
 
   NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
 
   // NOTE:  nsCachedStyleData::GetStyleData works roughly the same way.
   // See the comments there (in nsRuleNode.h) for more details about
@@ -438,29 +437,29 @@ nsStyleContext::CalcStyleDifferenceInter
 
   DebugOnly<int> styleStructCount = 1;  // count Variables already
 
   // Servo's optimization to stop the cascade when there are no style changes
   // that children need to be recascade for relies on comparing all of the
   // structs, not just those that are returned from PeekStyleData, although
   // if PeekStyleData does return null we still don't want to accumulate
   // any change hints for those structs.
-  bool checkUnrequestedServoStructs = mSource.IsServoComputedValues();
+  bool checkUnrequestedServoStructs = IsServo();
 
 #define EXPAND(...) __VA_ARGS__
 #define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)                  \
   PR_BEGIN_MACRO                                                              \
     const nsStyle##struct_* this##struct_ = PeekStyle##struct_();             \
     bool unrequestedStruct;                                                   \
     if (this##struct_) {                                                      \
       unrequestedStruct = false;                                              \
       structsFound |= NS_STYLE_INHERIT_BIT(struct_);                          \
     } else if (checkUnrequestedServoStructs) {                                \
       this##struct_ =                                                         \
-        Servo_GetStyle##struct_(mSource.AsServoComputedValues());             \
+        Servo_GetStyle##struct_(AsServo()->ComputedValues());                 \
       unrequestedStruct = true;                                               \
     } else {                                                                  \
       unrequestedStruct = false;                                              \
     }                                                                         \
     if (this##struct_) {                                                      \
       const nsStyle##struct_* other##struct_ = aNewContext->Style##struct_(); \
       if (this##struct_ == other##struct_) {                                  \
         /* The very same struct, so we know that there will be no */          \
@@ -703,17 +702,17 @@ nsStyleContext::EnsureSameStructsCached(
 #define STYLE_STRUCT(name_, checkdata_cb_)                                    \
   if (aOldContext->PeekStyle##name_()) {                                      \
     Style##name_();                                                           \
   }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 #ifdef DEBUG
-  if (mSource.IsServoComputedValues()) {
+  if (IsServo()) {
     auto oldMask = aOldContext->mBits & NS_STYLE_INHERIT_MASK;
     auto newMask = mBits & NS_STYLE_INHERIT_MASK;
     MOZ_ASSERT((oldMask & newMask) == oldMask,
                "Should have at least as many structs computed as the "
                "old context!");
   }
 #endif
 }
@@ -731,22 +730,21 @@ void nsStyleContext::List(FILE* out, int
                              (void*)this, mRefCnt, (void *)mParent));
   if (mPseudoTag) {
     nsAutoString  buffer;
     mPseudoTag->ToString(buffer);
     AppendUTF16toUTF8(buffer, str);
     str.Append(' ');
   }
 
-  if (mSource.IsServoComputedValues()) {
+  if (IsServo()) {
     fprintf_stderr(out, "%s{ServoComputedValues}\n", str.get());
-  } else if (mSource.IsGeckoRuleNode()) {
+  } else if (nsRuleNode* ruleNode = AsGecko()->RuleNode()) {
     fprintf_stderr(out, "%s{\n", str.get());
     str.Truncate();
-    nsRuleNode* ruleNode = mSource.AsGeckoRuleNode();
     while (ruleNode) {
       nsIStyleRule *styleRule = ruleNode->GetRule();
       if (styleRule) {
         styleRule->List(out, aIndent + 1);
       }
       ruleNode = ruleNode->GetParent();
     }
     for (ix = aIndent; --ix >= 0; ) {
@@ -761,16 +759,40 @@ void nsStyleContext::List(FILE* out, int
   if (aListDescendants) {
     if (GeckoStyleContext* gecko = GetAsGecko()) {
       gecko->ListDescendants(out, aIndent);
     }
   }
 }
 #endif
 
+NonOwningStyleContextSource
+nsStyleContext::StyleSource() const
+{
+  MOZ_STYLO_FORWARD(StyleSource, ())
+}
+
+#define STYLE_STRUCT(name_, checkdata_cb_)                      \
+const nsStyle##name_ *                                          \
+nsStyleContext::Style##name_() {                                \
+  return DoGetStyle##name_<true>();                             \
+}                                                               \
+const nsStyle##name_ *                                          \
+nsStyleContext::ThreadsafeStyle##name_() {                      \
+  if (mozilla::ServoStyleSet::IsInServoTraversal()) {           \
+    return Servo_GetStyle##name_(AsServo()->ComputedValues());  \
+  }                                                             \
+  return Style##name_();                                        \
+}                                                               \
+const nsStyle##name_ * nsStyleContext::PeekStyle##name_() {     \
+  return DoGetStyle##name_<false>();                            \
+}
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+
 // Overridden to prevent the global delete from being called, since the memory
 // came out of an nsIArena instead of the global delete operator's heap.
 void
 nsStyleContext::Destroy()
 {
   if (IsGecko()) {
     // Get the pres context.
     RefPtr<nsPresContext> presContext = PresContext();
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -126,17 +126,17 @@ public:
                  "do not call HasSingleReference on a newly created "
                  "nsStyleContext with no references yet");
     return mRefCnt == 1;
   }
 
   nsPresContext* PresContext() const;
 
   nsStyleContext* GetParent() const {
-    MOZ_ASSERT(mSource.IsGeckoRuleNode(),
+    MOZ_ASSERT(IsGecko(),
                "This should be used only in Gecko-backed style system!");
     return mParent;
   }
 
   nsStyleContext* GetParentAllowServo() const {
     return mParent;
   }
 
@@ -274,20 +274,17 @@ public:
    * given style struct and it does NOT own that struct.  This can
    * happen because it was inherited from the parent style context, or
    * because it was stored conditionally on the rule node.
    */
   bool HasCachedDependentStyleData(nsStyleStructID aSID) {
     return mBits & nsCachedStyleData::GetBitForSID(aSID);
   }
 
-  nsRuleNode* RuleNode() {
-    MOZ_RELEASE_ASSERT(mSource.IsGeckoRuleNode());
-    return mSource.AsGeckoRuleNode();
-  }
+  inline nsRuleNode* RuleNode();
 
   void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; }
 
   /*
    * Get the style data for a style struct.  This is the most important
    * member function of nsStyleContext.  It fills in a const pointer
    * to a style data struct that is appropriate for the style context's
    * frame.  This struct may be shared with other contexts (either in
@@ -306,52 +303,43 @@ public:
 
   /**
    * Define typesafe getter functions for each style struct by
    * preprocessing the list of style structs.  These functions are the
    * preferred way to get style data.  The macro creates functions like:
    *   const nsStyleBorder* StyleBorder();
    *   const nsStyleColor* StyleColor();
    */
-  #define STYLE_STRUCT(name_, checkdata_cb_)                   \
-    const nsStyle##name_ * Style##name_() MOZ_NONNULL_RETURN { \
-      return DoGetStyle##name_<true>();                        \
-    }
+  #define STYLE_STRUCT(name_, checkdata_cb_) \
+    const nsStyle##name_ * Style##name_() MOZ_NONNULL_RETURN;
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
   /**
    * Equivalent to StyleFoo(), except that we skip the cache write during the
    * servo traversal. This can cause incorrect behavior if used improperly,
    * since we won't record that layout potentially depends on the values in
    * this style struct. Use with care.
    */
 
-  #define STYLE_STRUCT(name_, checkdata_cb_)                                  \
-    const nsStyle##name_ * ThreadsafeStyle##name_() {                         \
-      if (mozilla::ServoStyleSet::IsInServoTraversal()) {                     \
-        return Servo_GetStyle##name_(mSource.AsServoComputedValues());        \
-      }                                                                       \
-      return Style##name_();                                                  \
-    }
+  #define STYLE_STRUCT(name_, checkdata_cb_) \
+    const nsStyle##name_ * ThreadsafeStyle##name_();
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
 
   /**
    * PeekStyle* is like Style* but doesn't trigger style
    * computation if the data is not cached on either the style context
    * or the rule node.
    *
    * Perhaps this shouldn't be a public nsStyleContext API.
    */
-  #define STYLE_STRUCT(name_, checkdata_cb_)              \
-    const nsStyle##name_ * PeekStyle##name_() {           \
-      return DoGetStyle##name_<false>();                  \
-    }
+  #define STYLE_STRUCT(name_, checkdata_cb_)  \
+    const nsStyle##name_ * PeekStyle##name_();
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
   /**
    * Compute the style changes needed during restyling when this style
    * context is being replaced by aNewContext.  (This is nonsymmetric since
    * we optimize by skipping comparison for styles that have never been
    * requested.)
@@ -476,42 +464,45 @@ public:
         cachedData = nullptr;
       }
     } else {
       cachedData = mCachedInheritedData.mStyleStructs[aSID];
     }
     return cachedData;
   }
 
-  mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); }
+  mozilla::NonOwningStyleContextSource StyleSource() const;
 
-public: // temporary
-  // Private destructor, to discourage deletion outside of Release():
-  ~nsStyleContext();
-
+protected:
+  // protected destructor to discourage deletion outside of Release()
+  ~nsStyleContext() {}
+  // Where the actual destructor lives
+  // We use this instead of a real destructor because we need
+  // this to be called *before* the subclass fields are destroyed
+  // by the subclass destructor
+  void Destructor();
   // Delegated Helper constructor.
   nsStyleContext(nsStyleContext* aParent,
-                 mozilla::OwningStyleContextSource&& aSource,
                  nsIAtom* aPseudoTag,
                  mozilla::CSSPseudoElementType aPseudoType);
 
   // Helper post-contruct hook.
   void FinishConstruction();
 
   // Only does stuff in Gecko mode
   void AddChild(nsStyleContext* aChild);
   void RemoveChild(nsStyleContext* aChild);
 
   void SetStyleBits();
 
   const void* StyleStructFromServoComputedValues(nsStyleStructID aSID) {
     switch (aSID) {
 #define STYLE_STRUCT(name_, checkdata_cb_)                                    \
       case eStyleStruct_##name_:                                              \
-        return Servo_GetStyle##name_(mSource.AsServoComputedValues());
+        return Servo_GetStyle##name_(StyleSource().AsServoComputedValues());
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
       default:
         MOZ_ASSERT_UNREACHABLE("unexpected nsStyleStructID value");
         return nullptr;
     }
   }
 
@@ -543,31 +534,31 @@ public: // temporary
 #else
 #define AUTO_CHECK_DEPENDENCY(sid_)
 #endif
 
   // Helper functions for GetStyle* and PeekStyle*
   #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)                  \
     template<bool aComputeData>                                         \
     const nsStyle##name_ * DoGetStyle##name_() {                        \
-      if (mSource.IsGeckoRuleNode()) {                                  \
+      if (IsGecko()) {                                                  \
         const nsStyle##name_ * cachedData =                             \
           static_cast<nsStyle##name_*>(                                 \
             mCachedInheritedData.mStyleStructs[eStyleStruct_##name_]);  \
         if (cachedData) /* Have it cached already, yay */               \
           return cachedData;                                            \
         if (!aComputeData) {                                            \
           /* We always cache inherited structs on the context when we */\
           /* compute them. */                                           \
           return nullptr;                                               \
         }                                                               \
         /* Have the rulenode deal */                                    \
         AUTO_CHECK_DEPENDENCY(eStyleStruct_##name_);                    \
         const nsStyle##name_ * newData =                                \
-          mSource.AsGeckoRuleNode()->                                   \
+          StyleSource().AsGeckoRuleNode()->                             \
             GetStyle##name_<aComputeData>(this, mBits);                 \
         /* always cache inherited data on the style context; the rule */\
         /* node set the bit in mBits for us if needed. */               \
         mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] =      \
           const_cast<nsStyle##name_ *>(newData);                        \
         return newData;                                                 \
       }                                                                 \
       /**                                                               \
@@ -599,50 +590,50 @@ public: // temporary
       }                                                                 \
                                                                         \
       const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_));\
       if (!aComputeData && needToCompute) {                             \
         return nullptr;                                                 \
       }                                                                 \
                                                                         \
       const nsStyle##name_* data =                                      \
-        Servo_GetStyle##name_(mSource.AsServoComputedValues());         \
+        Servo_GetStyle##name_(StyleSource().AsServoComputedValues());   \
       /* perform any remaining main thread work on the struct */        \
       if (needToCompute) {                                              \
         MOZ_ASSERT(NS_IsMainThread());                                  \
         MOZ_ASSERT(!mozilla::ServoStyleSet::IsInServoTraversal());      \
         const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext());  \
         /* the Servo-backed StyleContextSource owns the struct */       \
         AddStyleBit(NS_STYLE_INHERIT_BIT(name_));                       \
       }                                                                 \
       return data;                                                      \
     }
 
   #define STYLE_STRUCT_RESET(name_, checkdata_cb_)                      \
     template<bool aComputeData>                                         \
     const nsStyle##name_ * DoGetStyle##name_() {                        \
-      if (mSource.IsGeckoRuleNode()) {                                  \
+      if (IsGecko()) {                                                  \
         if (mCachedResetData) {                                         \
           const nsStyle##name_ * cachedData =                           \
             static_cast<nsStyle##name_*>(                               \
               mCachedResetData->mStyleStructs[eStyleStruct_##name_]);   \
           if (cachedData) /* Have it cached already, yay */             \
             return cachedData;                                          \
         }                                                               \
         /* Have the rulenode deal */                                    \
         AUTO_CHECK_DEPENDENCY(eStyleStruct_##name_);                    \
-        return mSource.AsGeckoRuleNode()->                              \
+        return StyleSource().AsGeckoRuleNode()->                        \
           GetStyle##name_<aComputeData>(this);                          \
       }                                                                 \
       const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_));\
       if (!aComputeData && needToCompute) {                             \
         return nullptr;                                                 \
       }                                                                 \
       const nsStyle##name_* data =                                      \
-        Servo_GetStyle##name_(mSource.AsServoComputedValues());         \
+        Servo_GetStyle##name_(StyleSource().AsServoComputedValues());   \
       /* perform any remaining main thread work on the struct */        \
       if (needToCompute) {                                              \
         const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext());  \
         /* the Servo-backed StyleContextSource owns the struct */       \
         AddStyleBit(NS_STYLE_INHERIT_BIT(name_));                       \
       }                                                                 \
       return data;                                                      \
     }
@@ -670,21 +661,16 @@ public: // temporary
   // background-color, and border-*-color if the nearest ancestor link
   // element is visited (see RelevantLinkVisited()).
   RefPtr<nsStyleContext> mStyleIfVisited;
 
   // If this style context is for a pseudo-element or anonymous box,
   // the relevant atom.
   nsCOMPtr<nsIAtom> mPseudoTag;
 
-  // The source for our style data, either a Gecko nsRuleNode or a Servo
-  // ComputedValues struct. This never changes after construction, except
-  // when it's released and nulled out during teardown.
-  const mozilla::OwningStyleContextSource mSource;
-
   // mCachedInheritedData and mCachedResetData point to both structs that
   // are owned by this style context and structs that are owned by one of
   // this style context's ancestors (which are indirectly owned since this
   // style context owns a reference to its parent).  If the bit in |mBits|
   // is set for a struct, that means that the pointer for that struct is
   // owned by an ancestor or by the rule node rather than by this style context.
   // Since style contexts typically have some inherited data but only sometimes
   // have reset data, we always allocate the mCachedInheritedData, but only
--- a/layout/style/nsStyleContextInlines.h
+++ b/layout/style/nsStyleContextInlines.h
@@ -18,9 +18,17 @@
 #include "mozilla/ServoStyleContext.h"
 #include "mozilla/GeckoStyleContext.h"
 #include "mozilla/ServoUtils.h"
 
 using namespace mozilla;
 
 MOZ_DEFINE_STYLO_METHODS(nsStyleContext, GeckoStyleContext, ServoStyleContext);
 
+nsRuleNode*
+nsStyleContext::RuleNode()
+{
+    MOZ_RELEASE_ASSERT(IsGecko());
+    return AsGecko()->RuleNode();
+}
+
+
 #endif // nsStyleContextInlines_h