Bug 1373018 - Part 7: stylo: Move nsStyleContext::mSource into subclasses; r?bholley draft
authorManish Goregaokar <manishearth@gmail.com>
Sat, 10 Jun 2017 22:27:45 -0700
changeset 595122 364de39d4df995f8c9418129e4d28d5c77b68bd5
parent 595121 df1d11ad343e98862c6c8758559a9c95e1bb3b66
child 595123 6e9cce6ce87ab235bfb284dfc75d61f74edc8802
push id64258
push userbmo:manishearth@gmail.com
push dateFri, 16 Jun 2017 03:04:15 +0000
reviewersbholley
bugs1373018
milestone56.0a1
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