Bug 1260310 - Generalize nsStyleContext to support resolving styles from either nsRuleNode or ServoComputedValues. r=heycam
authorBobby Holley <bobbyholley@gmail.com>
Fri, 18 Mar 2016 16:56:47 -0700
changeset 291014 d926ec33189d35357876a4e7be7150ab8adec7dd
parent 291013 06a8c115f8fa8a253b867bf798ac376a168418b5
child 291015 de0c81292e18eb939061eb17515ed92fbbfa9e10
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1260310
milestone48.0a1
Bug 1260310 - Generalize nsStyleContext to support resolving styles from either nsRuleNode or ServoComputedValues. r=heycam
layout/style/ServoBindingHelpers.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/StyleContextSource.h
layout/style/moz.build
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
--- a/layout/style/ServoBindingHelpers.h
+++ b/layout/style/ServoBindingHelpers.h
@@ -12,20 +12,31 @@
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
 template<>
 struct RefPtrTraits<RawServoStyleSheet>
 {
   static void AddRef(RawServoStyleSheet* aPtr) {
-    MOZ_CRASH("stylo: not implemented");
+    Servo_AddRefStyleSheet(aPtr);
   }
   static void Release(RawServoStyleSheet* aPtr) {
-    Servo_ReleaseStylesheet(aPtr);
+    Servo_ReleaseStyleSheet(aPtr);
+  }
+};
+
+template<>
+struct RefPtrTraits<ServoComputedValues>
+{
+  static void AddRef(ServoComputedValues* aPtr) {
+    Servo_AddRefComputedValues(aPtr);
+  }
+  static void Release(ServoComputedValues* aPtr) {
+    Servo_ReleaseComputedValues(aPtr);
   }
 };
 
 template<>
 class DefaultDelete<RawServoStyleSet>
 {
 public:
   void operator()(RawServoStyleSet* aPtr) const
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -160,17 +160,24 @@ Servo_DropNodeData(ServoNodeData* data)
 RawServoStyleSheet*
 Servo_StylesheetFromUTF8Bytes(const uint8_t* bytes, uint32_t length)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_StylesheetFromUTF8Bytes in a "
             "non-MOZ_STYLO build");
 }
 
 void
-Servo_ReleaseStylesheet(RawServoStyleSheet* sheet)
+Servo_AddRefStyleSheet(RawServoStyleSheet* sheet)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_AddRefStylesheet in a "
+            "non-MOZ_STYLO build");
+}
+
+void
+Servo_ReleaseStyleSheet(RawServoStyleSheet* sheet)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_ReleaseStylesheet in a "
             "non-MOZ_STYLO build");
 }
 
 void
 Servo_AppendStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set)
 {
@@ -207,14 +214,28 @@ Servo_InitStyleSet()
 void
 Servo_DropStyleSet(RawServoStyleSet* set)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_DropStyleSet in a "
             "non-MOZ_STYLO build");
 }
 
 void
+Servo_AddRefComputedValues(ServoComputedValues*)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_AddRefComputedValues in a "
+            "non-MOZ_STYLO build");
+}
+
+void
+Servo_ReleaseComputedValues(ServoComputedValues*)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_ReleaseComputedValues in a "
+            "non-MOZ_STYLO build");
+}
+
+void
 Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_RestyleDocument in a "
             "non-MOZ_STYLO build");
 }
 #endif
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -20,16 +20,17 @@
 class nsINode;
 typedef nsINode RawGeckoNode;
 namespace mozilla { namespace dom { class Element; } }
 using mozilla::dom::Element;
 typedef mozilla::dom::Element RawGeckoElement;
 class nsIDocument;
 typedef nsIDocument RawGeckoDocument;
 struct ServoNodeData;
+struct ServoComputedValues;
 struct RawServoStyleSheet;
 struct RawServoStyleSet;
 
 extern "C" {
 
 // DOM Traversal.
 uint32_t Gecko_ChildrenCount(RawGeckoNode* node);
 bool Gecko_NodeIsElement(RawGeckoNode* node);
@@ -59,22 +60,27 @@ ServoNodeData* Gecko_GetNodeData(RawGeck
 void Gecko_SetNodeData(RawGeckoNode* node, ServoNodeData* data);
 void Servo_DropNodeData(ServoNodeData* data);
 
 // Styleset and Stylesheet management.
 //
 // TODO: Make these return already_AddRefed and UniquePtr when the binding
 // generator is smart enough to handle them.
 RawServoStyleSheet* Servo_StylesheetFromUTF8Bytes(const uint8_t* bytes, uint32_t length);
-void Servo_ReleaseStylesheet(RawServoStyleSheet* sheet);
+void Servo_AddRefStyleSheet(RawServoStyleSheet* sheet);
+void Servo_ReleaseStyleSheet(RawServoStyleSheet* sheet);
 void Servo_AppendStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set);
 void Servo_PrependStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set);
 void Servo_RemoveStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set);
 bool Servo_StyleSheetHasRules(RawServoStyleSheet* sheet);
 RawServoStyleSet* Servo_InitStyleSet();
 void Servo_DropStyleSet(RawServoStyleSet* set);
 
+// Computed style data.
+void Servo_AddRefComputedValues(ServoComputedValues*);
+void Servo_ReleaseComputedValues(ServoComputedValues*);
+
 // Servo API.
 void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set);
 
 } // extern "C"
 
 #endif // mozilla_ServoBindings_h
new file mode 100644
--- /dev/null
+++ b/layout/style/StyleContextSource.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_StyleContextSource_h
+#define mozilla_StyleContextSource_h
+
+#include "mozilla/ServoBindingHelpers.h"
+#include "nsRuleNode.h"
+
+namespace mozilla {
+
+// Tagged union between Gecko Rule Nodes and Servo Computed Values.
+//
+// The rule node is the node in the lexicographic tree of rule nodes
+// (the "rule tree") that indicates which style rules are used to
+// compute the style data, and in what cascading order.  The least
+// specific rule matched is the one whose rule node is a child of the
+// root of the rule tree, and the most specific rule matched is the
+// |mRule| member of the rule node.
+//
+// In the Servo case, we hold an atomically-refcounted handle to a
+// Servo ComputedValues struct, which is more or less the Servo equivalent
+// of an nsStyleContext.
+
+// Underlying pointer without any strong ownership semantics.
+struct NonOwningStyleContextSource
+{
+  MOZ_IMPLICIT NonOwningStyleContextSource(nsRuleNode* aRuleNode)
+    : mBits(reinterpret_cast<uintptr_t>(aRuleNode)) {}
+  explicit NonOwningStyleContextSource(ServoComputedValues* aComputedValues)
+    : mBits(reinterpret_cast<uintptr_t>(aComputedValues) | 1) {}
+
+  bool operator==(const NonOwningStyleContextSource& aOther) const {
+    MOZ_ASSERT(IsServoComputedValues() == aOther.IsServoComputedValues(),
+               "Comparing Servo to Gecko - probably a bug");
+    return mBits == aOther.mBits;
+  }
+  bool operator!=(const NonOwningStyleContextSource& aOther) const {
+    return !(*this == aOther);
+  }
+
+  // We intentionally avoid exposing IsGeckoRuleNode() here, because that would
+  // encourage callers to do:
+  //
+  // if (source.IsGeckoRuleNode()) {
+  //   // Code that we would run unconditionally if it weren't for Servo.
+  // }
+  //
+  // We want these branches to compile away when MOZ_STYLO is disabled, but that
+  // won't happen if there's an implicit null-check.
+  bool IsNull() const { return !mBits; }
+  bool IsGeckoRuleNodeOrNull() const { return !IsServoComputedValues(); }
+  bool IsServoComputedValues() const {
+#ifdef MOZ_STYLO
+    return mBits & 1;
+#else
+    return false;
+#endif
+  }
+
+  nsRuleNode* AsGeckoRuleNode() const {
+    MOZ_ASSERT(IsGeckoRuleNodeOrNull() && !IsNull());
+    return reinterpret_cast<nsRuleNode*>(mBits);
+  }
+
+  ServoComputedValues* AsServoComputedValues() const {
+    MOZ_ASSERT(IsServoComputedValues());
+    return reinterpret_cast<ServoComputedValues*>(mBits & ~1);
+  }
+
+  bool MatchesNoRules() const {
+    if (IsGeckoRuleNodeOrNull()) {
+      return AsGeckoRuleNode()->IsRoot();
+    } else {
+      MOZ_CRASH("stylo: not implemented");
+    }
+  }
+
+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_ASSERT(!mRaw.IsNull()); };
+  explicit OwningStyleContextSource(already_AddRefed<ServoComputedValues> aComputedValues)
+    : mRaw(aComputedValues.take()) { MOZ_ASSERT(!mRaw.IsNull()); }
+  OwningStyleContextSource(OwningStyleContextSource&& aOther)
+    : mRaw(aOther.mRaw) { aOther.mRaw = nullptr; }
+
+  OwningStyleContextSource& operator=(OwningStyleContextSource&) = delete;
+  OwningStyleContextSource(OwningStyleContextSource&) = delete;
+
+  ~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 mRaw.AsServoComputedValues();
+  }
+
+  bool MatchesNoRules() const { return mRaw.MatchesNoRules(); }
+
+private:
+  NonOwningStyleContextSource mRaw;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StyleContextSource_h
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -92,16 +92,17 @@ EXPORTS.mozilla += [
     'RuleProcessorCache.h',
     'ServoBindingHelpers.h',
     'ServoBindings.h',
     'ServoStyleSet.h',
     'ServoStyleSheet.h',
     'SheetType.h',
     'StyleAnimationValue.h',
     'StyleBackendType.h',
+    'StyleContextSource.h',
     'StyleSetHandle.h',
     'StyleSetHandleInlines.h',
     'StyleSheet.h',
     'StyleSheetHandle.h',
     'StyleSheetHandleInlines.h',
     'StyleSheetInfo.h',
     'StyleSheetInlines.h',
 ]
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -67,83 +67,116 @@ 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,
-                               nsRuleNode* aRuleNode,
-                               bool aSkipParentDisplayBasedStyleFixup)
+                               CSSPseudoElementType aPseudoType)
   : mParent(aParent)
   , mChild(nullptr)
   , mEmptyChild(nullptr)
   , mPseudoTag(aPseudoTag)
-  , mRuleNode(aRuleNode)
+  , mSource(Move(aSource))
+#ifdef MOZ_STYLO
+  , mPresContext(nullptr)
+#endif
   , mCachedResetData(nullptr)
   , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
   , mRefCnt(0)
 #ifdef DEBUG
   , mFrameRefCnt(0)
   , mComputingStruct(nsStyleStructID_None)
 #endif
+{}
+
+nsStyleContext::nsStyleContext(nsStyleContext* aParent,
+                               nsIAtom* aPseudoTag,
+                               CSSPseudoElementType aPseudoType,
+                               already_AddRefed<nsRuleNode> aRuleNode,
+                               bool aSkipParentDisplayBasedStyleFixup)
+  : nsStyleContext(aParent, OwningStyleContextSource(Move(aRuleNode)),
+                   aPseudoTag, aPseudoType)
+{
+#ifdef MOZ_STYLO
+  mPresContext = mSource.AsGeckoRuleNode()->PresContext();
+#endif
+
+  if (aParent) {
+#ifdef DEBUG
+    nsRuleNode *r1 = mParent->RuleNode(), *r2 = mSource.AsGeckoRuleNode();
+    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()!
+  FinishConstruction(aSkipParentDisplayBasedStyleFixup);
+}
+
+nsStyleContext::nsStyleContext(nsStyleContext* aParent,
+                               nsPresContext* aPresContext,
+                               nsIAtom* aPseudoTag,
+                               CSSPseudoElementType aPseudoType,
+                               already_AddRefed<ServoComputedValues> aComputedValues,
+                               bool aSkipParentDisplayBasedStyleFixup)
+  : nsStyleContext(aParent, OwningStyleContextSource(Move(aComputedValues)),
+                   aPseudoTag, aPseudoType)
+{
+#ifdef MOZ_STYLO
+  mPresContext = aPresContext;
+#endif
+
+  FinishConstruction(aSkipParentDisplayBasedStyleFixup);
+}
+
+void
+nsStyleContext::FinishConstruction(bool aSkipParentDisplayBasedStyleFixup)
 {
   // 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(aRuleNode);
+  MOZ_ASSERT(!mSource.IsNull());
 
 #ifdef DEBUG
   static_assert(MOZ_ARRAY_LENGTH(nsStyleContext::sDependencyTable)
                   == nsStyleStructID_Length,
                 "Number of items in dependency table doesn't match IDs");
 #endif
 
   mNextSibling = this;
   mPrevSibling = this;
   if (mParent) {
     mParent->AddChild(this);
-#ifdef DEBUG
-    nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode;
-    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 {
-    mRuleNode->PresContext()->PresShell()->StyleSet()->RootStyleContextAdded();
   }
 
-  mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()!
   ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
 
   #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
   NS_ASSERTION(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()
 {
   NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children");
 
-  nsPresContext *presContext = mRuleNode->PresContext();
-  StyleSetHandle styleSet = presContext->PresShell()->StyleSet();
-  NS_ASSERTION(!styleSet->IsGecko() ||
-               styleSet->AsGecko()->GetRuleTree() == mRuleNode->RuleTree() ||
-               styleSet->AsGecko()->IsInRuleTreeReconstruct(),
-               "destroying style context from old rule tree too late");
-
 #ifdef DEBUG
   if (sExpensiveStyleStructAssertionsEnabled) {
     // Assert that the style structs we are about to destroy are not referenced
     // anywhere else in the style context tree.  These checks are expensive,
     // which is why they are not enabled by default.
     nsStyleContext* root = this;
     while (root->mParent) {
       root = root->mParent;
@@ -152,20 +185,27 @@ nsStyleContext::~nsStyleContext()
                                         std::numeric_limits<int32_t>::max());
   } else {
     // In DEBUG builds when the pref is not enabled, we perform a more limited
     // check just of the children of this style context.
     AssertStructsNotUsedElsewhere(this, 2);
   }
 #endif
 
+  nsPresContext *presContext = PresContext();
+  DebugOnly<nsStyleSet*> geckoStyleSet = presContext->PresShell()->StyleSet()->GetAsGecko();
+  MOZ_ASSERT(!geckoStyleSet ||
+             geckoStyleSet->GetRuleTree() == mSource.AsGeckoRuleNode()->RuleTree() ||
+             geckoStyleSet->IsInRuleTreeReconstruct(),
+             "destroying style context from old rule tree too late");
+
   if (mParent) {
     mParent->RemoveChild(this);
   } else {
-    styleSet->RootStyleContextRemoved();
+    presContext->StyleSet()->RootStyleContextRemoved();
   }
 
   // Free up our data structs.
   mCachedInheritedData.DestroyStructs(mBits, presContext);
   if (mCachedResetData) {
     mCachedResetData->Destroy(mBits, presContext);
   }
 
@@ -257,17 +297,17 @@ nsStyleContext::AssertStructsNotUsedElse
 #endif
 
 void nsStyleContext::AddChild(nsStyleContext* aChild)
 {
   NS_ASSERTION(aChild->mPrevSibling == aChild &&
                aChild->mNextSibling == aChild,
                "child already in a child list");
 
-  nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
+  nsStyleContext **listPtr = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
   // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
   // etc. don't alias with what ever listPtr points at.
   nsStyleContext *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;
@@ -277,17 +317,17 @@ void nsStyleContext::AddChild(nsStyleCon
   }
   (*listPtr) = aChild;
 }
 
 void nsStyleContext::RemoveChild(nsStyleContext* aChild)
 {
   NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
 
-  nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
+  nsStyleContext **list = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
 
   if (aChild->mPrevSibling != aChild) { // has siblings
     if ((*list) == aChild) {
       (*list) = (*list)->mNextSibling;
     }
   } 
   else {
     NS_ASSERTION((*list) == aChild, "bad sibling pointers");
@@ -336,38 +376,38 @@ nsStyleContext::MoveTo(nsStyleContext* a
   if (mStyleIfVisited) {
     mStyleIfVisited->mParent->RemoveChild(mStyleIfVisited);
     mStyleIfVisited->mParent = aNewParent;
     mStyleIfVisited->mParent->AddChild(mStyleIfVisited);
   }
 }
 
 already_AddRefed<nsStyleContext>
-nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, 
-                                   nsRuleNode* aRuleNode,
-                                   nsRuleNode* aRulesIfVisited,
+nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
+                                   NonOwningStyleContextSource aSource,
+                                   NonOwningStyleContextSource aSourceIfVisited,
                                    bool aRelevantLinkVisited)
 {
   uint32_t threshold = 10; // The # of siblings we're willing to examine
                            // before just giving this whole thing up.
 
   RefPtr<nsStyleContext> result;
-  nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild;
+  nsStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
 
   if (list) {
     nsStyleContext *child = list;
     do {
-      if (child->mRuleNode == aRuleNode &&
+      if (child->mSource.AsRaw() == aSource &&
           child->mPseudoTag == aPseudoTag &&
           !child->IsStyleIfVisited() &&
           child->RelevantLinkVisited() == aRelevantLinkVisited) {
         bool match = false;
-        if (aRulesIfVisited) {
+        if (!aSourceIfVisited.IsNull()) {
           match = child->GetStyleIfVisited() &&
-                  child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited;
+                  child->GetStyleIfVisited()->mSource.AsRaw() == aSourceIfVisited;
         } else {
           match = !child->GetStyleIfVisited();
         }
         if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
           result = child;
           break;
         }
       }
@@ -390,18 +430,20 @@ nsStyleContext::FindChildWithRules(const
   return result.forget();
 }
 
 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 rule node will take care of it for us.
-  const void* newData = mRuleNode->GetStyleData(aSID, this, true);
+  // Our style source will take care of it for us.
+  const void* newData = mSource.IsGeckoRuleNode()
+                          ? mSource.AsGeckoRuleNode()->GetStyleData(aSID, this, true)
+                          : StyleStructFromServoComputedValues(aSID);
   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);
   }
   return newData;
 }
 
@@ -495,17 +537,17 @@ nsStyleContext::SetStyle(nsStyleStructID
 
   // NOTE:  nsCachedStyleData::GetStyleData works roughly the same way.
   // See the comments there (in nsRuleNode.h) for more details about
   // what this is doing and why.
 
   void** dataSlot;
   if (nsCachedStyleData::IsReset(aSID)) {
     if (!mCachedResetData) {
-      mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData;
+      mCachedResetData = new (PresContext()) nsResetStyleData;
     }
     dataSlot = &mCachedResetData->mStyleStructs[aSID];
   } else {
     dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
   }
   NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
                "Going to leak style data");
   *dataSlot = aStruct;
@@ -816,36 +858,36 @@ nsStyleContext::CalcStyleDifference(nsSt
   // context that are filled in on the old context, so that if we get
   // two style changes in succession, the second of which causes a real
   // style change, the PeekStyleData doesn't return null (implying that
   // nobody ever looked at that struct's data).  In other words, we
   // can't skip later structs if we get a big change up front, because
   // we could later get a small change in one of those structs that we
   // don't want to miss.
 
-  // If our rule nodes are the same, then any differences in style data
+  // If our sources are the same, then any differences in style data
   // are already accounted for by differences on ancestors.  We know
   // this because CalcStyleDifference is always called on two style
   // contexts that point to the same element, so we know that our
   // position in the style context tree is the same and our position in
-  // the rule node tree is also the same.
+  // the rule node tree (if applicable) is also the same.
   // However, if there were noninherited style change hints on the
   // parent, we might produce these same noninherited hints on this
   // style context's frame due to 'inherit' values, so we do need to
   // compare.
   // (Things like 'em' units are handled by the change hint produced
   // by font-size changing, so we don't need to worry about them like
   // we worry about 'inherit' values.)
-  bool compare = mRuleNode != aOther->mRuleNode;
+  bool compare = mSource != aOther->mSource;
 
   DebugOnly<uint32_t> structsFound = 0;
 
   // If we had any change in variable values, then we'll need to examine
   // all of the other style structs too, even if the new style context has
-  // the same rule node as the old one.
+  // the same source as the old one.
   const nsStyleVariables* thisVariables = PeekStyleVariables();
   if (thisVariables) {
     structsFound |= NS_STYLE_INHERIT_BIT(Variables);
     const nsStyleVariables* otherVariables = aOther->StyleVariables();
     if (thisVariables->mVariables == otherVariables->mVariables) {
       *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
     } else {
       compare = true;
@@ -1125,20 +1167,22 @@ void nsStyleContext::List(FILE* out, int
                              (void*)this, mRefCnt, (void *)mParent));
   if (mPseudoTag) {
     nsAutoString  buffer;
     mPseudoTag->ToString(buffer);
     AppendUTF16toUTF8(buffer, str);
     str.Append(' ');
   }
 
-  if (mRuleNode) {
+  if (mSource.IsServoComputedValues()) {
+    fprintf_stderr(out, "%s{ServoComputedValues}\n", str.get());
+  } else if (mSource.IsGeckoRuleNode()) {
     fprintf_stderr(out, "%s{\n", str.get());
     str.Truncate();
-    nsRuleNode* ruleNode = mRuleNode;
+    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; ) {
@@ -1179,18 +1223,18 @@ nsStyleContext::operator new(size_t sz, 
     AllocateByObjectID(eArenaObjectID_nsStyleContext, sz);
 }
 
 // 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()
 {
-  // Get the pres context from our rule node.
-  RefPtr<nsPresContext> presContext = mRuleNode->PresContext();
+  // Get the pres context.
+  RefPtr<nsPresContext> presContext = PresContext();
 
   // Call our destructor.
   this->~nsStyleContext();
 
   // Don't let the memory be freed, since it will be recycled
   // instead. Don't call the global operator delete.
   presContext->PresShell()->
     FreeByObjectID(eArenaObjectID_nsStyleContext, this);
@@ -1198,27 +1242,43 @@ nsStyleContext::Destroy()
 
 already_AddRefed<nsStyleContext>
 NS_NewStyleContext(nsStyleContext* aParentContext,
                    nsIAtom* aPseudoTag,
                    CSSPseudoElementType aPseudoType,
                    nsRuleNode* aRuleNode,
                    bool aSkipParentDisplayBasedStyleFixup)
 {
+  RefPtr<nsRuleNode> node = aRuleNode;
   RefPtr<nsStyleContext> context =
     new (aRuleNode->PresContext())
-    nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode,
+    nsStyleContext(aParentContext, aPseudoTag, aPseudoType, node.forget(),
                    aSkipParentDisplayBasedStyleFixup);
   return context.forget();
 }
 
+already_AddRefed<nsStyleContext>
+NS_NewStyleContext(nsStyleContext* aParentContext,
+                   nsPresContext* aPresContext,
+                   nsIAtom* aPseudoTag,
+                   CSSPseudoElementType aPseudoType,
+                   already_AddRefed<ServoComputedValues> aComputedValues,
+                   bool aSkipParentDisplayBasedStyleFixup)
+{
+  RefPtr<nsStyleContext> context =
+    new (aPresContext)
+    nsStyleContext(aParentContext, aPresContext, aPseudoTag, aPseudoType,
+                   Move(aComputedValues), aSkipParentDisplayBasedStyleFixup);
+  return context.forget();
+}
+
 nsIPresShell*
 nsStyleContext::Arena()
 {
-  return mRuleNode->PresContext()->PresShell();
+  return PresContext()->PresShell();
 }
 
 static inline void
 ExtractAnimationValue(nsCSSProperty aProperty,
                       nsStyleContext* aStyleContext,
                       StyleAnimationValue& aResult)
 {
   DebugOnly<bool> success =
@@ -1381,21 +1441,20 @@ nsStyleContext::SwapStyleData(nsStyleCon
   for (nsStyleStructID i = nsStyleStructID_Reset_Start;
        i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
        i = nsStyleStructID(i + 1)) {
     uint32_t bit = nsCachedStyleData::GetBitForSID(i);
     if (!(aStructs & bit)) {
       continue;
     }
     if (!mCachedResetData) {
-      mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData;
+      mCachedResetData = new (PresContext()) nsResetStyleData;
     }
     if (!aNewContext->mCachedResetData) {
-      aNewContext->mCachedResetData =
-        new (mRuleNode->PresContext()) nsResetStyleData;
+      aNewContext->mCachedResetData = new (PresContext()) nsResetStyleData;
     }
     void*& thisData = mCachedResetData->mStyleStructs[i];
     void*& otherData = aNewContext->mCachedResetData->mStyleStructs[i];
     if (mBits & bit) {
       if (thisData == otherData) {
         thisData = nullptr;
       }
     } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -3,19 +3,20 @@
  * 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/. */
 
 /* the interface (to internal code) for retrieving computed style data */
 
 #ifndef _nsStyleContext_h_
 #define _nsStyleContext_h_
 
+#include "mozilla/Assertions.h"
 #include "mozilla/RestyleLogging.h"
-#include "mozilla/Assertions.h"
-#include "nsRuleNode.h"
+#include "mozilla/StyleContextSource.h"
+#include "nsStyleSet.h"
 
 class nsIAtom;
 class nsPresContext;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
 } // namespace mozilla
 
@@ -63,17 +64,26 @@ public:
    *                 If set, this flag indicates that we should skip
    *                 the chunk of ApplyStyleFixups() that applies to
    *                 special cases where a child element's style may
    *                 need to be modified based on its parent's display
    *                 value.
    */
   nsStyleContext(nsStyleContext* aParent, nsIAtom* aPseudoTag,
                  mozilla::CSSPseudoElementType aPseudoType,
-                 nsRuleNode* aRuleNode,
+                 already_AddRefed<nsRuleNode> aRuleNode,
+                 bool aSkipParentDisplayBasedStyleFixup);
+
+  // Version of the above that takes a ServoComputedValues instead of a Gecko
+  // nsRuleNode.
+  nsStyleContext(nsStyleContext* aParent,
+                 nsPresContext* aPresContext,
+                 nsIAtom* aPseudoTag,
+                 mozilla::CSSPseudoElementType aPseudoType,
+                 already_AddRefed<ServoComputedValues> aComputedValues,
                  bool aSkipParentDisplayBasedStyleFixup);
 
   void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW;
   void Destroy();
 
   // These two methods are for use by ArenaRefPtr.
   static mozilla::ArenaObjectID ArenaObjectID()
   {
@@ -128,36 +138,43 @@ public:
 
   bool HasSingleReference() const {
     NS_ASSERTION(mRefCnt != 0,
                  "do not call HasSingleReference on a newly created "
                  "nsStyleContext with no references yet");
     return mRefCnt == 1;
   }
 
-  nsPresContext* PresContext() const { return mRuleNode->PresContext(); }
+  nsPresContext* PresContext() const {
+#ifdef MOZ_STYLO
+    return mPresContext;
+#else
+    return mSource.AsGeckoRuleNode()->PresContext();
+#endif
+  }
 
   nsStyleContext* GetParent() const { return mParent; }
 
   nsIAtom* GetPseudo() const { return mPseudoTag; }
   mozilla::CSSPseudoElementType GetPseudoType() const {
     return static_cast<mozilla::CSSPseudoElementType>(
              mBits >> NS_STYLE_CONTEXT_TYPE_SHIFT);
   }
 
   // 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
-  //  * RuleNode() matches aRules
-  //  * !GetStyleIfVisited() == !aRulesIfVisited, and, if they're
-  //    non-null, GetStyleIfVisited()->RuleNode() == aRulesIfVisited
+  //  * mSource matches aSource
+  //  * !!GetStyleIfVisited() == !!aSourceIfVisited, and, if they're
+  //    non-null, GetStyleIfVisited()->mSource == aSourceIfVisited
   //  * RelevantLinkVisited() == aRelevantLinkVisited
   already_AddRefed<nsStyleContext>
-  FindChildWithRules(const nsIAtom* aPseudoTag, nsRuleNode* aRules,
-                     nsRuleNode* aRulesIfVisited,
+  FindChildWithRules(const nsIAtom* aPseudoTag,
+                     mozilla::NonOwningStyleContextSource aSource,
+                     mozilla::NonOwningStyleContextSource aSourceIfVisited,
                      bool aRelevantLinkVisited);
 
   // Does this style context or any of its ancestors have text
   // decoration lines?
   // Differs from nsStyleTextReset::HasTextDecorationLines, which tests
   // only the data for a single context.
   bool HasTextDecorationLines() const
     { return !!(mBits & NS_STYLE_HAS_TEXT_DECORATION_LINES); }
@@ -268,17 +285,21 @@ 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() { return mRuleNode; }
+  nsRuleNode* RuleNode() {
+    MOZ_RELEASE_ASSERT(mSource.IsGeckoRuleNode());
+    return mSource.AsGeckoRuleNode();
+  }
+
   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
    * the rule tree or the style context tree), so it should not be
@@ -453,24 +474,37 @@ public:
     }
     return cachedData;
   }
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~nsStyleContext();
 
+  // Delegated Helper constructor.
+  nsStyleContext(nsStyleContext* aParent,
+                 mozilla::OwningStyleContextSource&& aSource,
+                 nsIAtom* aPseudoTag,
+                 mozilla::CSSPseudoElementType aPseudoType);
+
+  // Helper post-contruct hook.
+  void FinishConstruction(bool aSkipParentDisplayBasedStyleFixup);
+
   void AddChild(nsStyleContext* aChild);
   void RemoveChild(nsStyleContext* aChild);
 
   void* GetUniqueStyleData(const nsStyleStructID& aSID);
   void* CreateEmptyStyleData(const nsStyleStructID& aSID);
 
   void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup);
 
+  const void* StyleStructFromServoComputedValues(nsStyleStructID aSID) {
+    MOZ_CRASH("stylo: not implemented");
+  }
+
 #ifdef DEBUG
   struct AutoCheckDependency {
 
     nsStyleContext* mStyleContext;
     nsStyleStructID mOuterSID;
 
     AutoCheckDependency(nsStyleContext* aContext, nsStyleStructID aInnerSID)
       : mStyleContext(aContext)
@@ -507,17 +541,17 @@ private:
       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 =                                  \
-        mRuleNode->GetStyle##name_<aComputeData>(this, mBits);          \
+        mSource.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;                                                   \
     }
   #define STYLE_STRUCT_RESET(name_, checkdata_cb_)                      \
     template<bool aComputeData>                                         \
@@ -526,17 +560,17 @@ private:
         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 mRuleNode->GetStyle##name_<aComputeData>(this);            \
+      return mSource.AsGeckoRuleNode()->GetStyle##name_<aComputeData>(this); \
     }
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT_RESET
   #undef STYLE_STRUCT_INHERITED
 
   // Helper for ClearCachedInheritedStyleDataOnDescendants.
   void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
 
@@ -571,39 +605,42 @@ private:
   // 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 rule node is the node in the lexicographic tree of rule nodes
-  // (the "rule tree") that indicates which style rules are used to
-  // compute the style data, and in what cascading order.  The least
-  // specific rule matched is the one whose rule node is a child of the
-  // root of the rule tree, and the most specific rule matched is the
-  // |mRule| member of |mRuleNode|.
-  const RefPtr<nsRuleNode> mRuleNode;
+  // 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;
+
+#ifdef MOZ_STYLO
+  // In Gecko, we can get this off the rule node. We make this conditional
+  // on stylo builds to avoid the memory bloat on release.
+  nsPresContext* mPresContext;
+#endif
 
   // 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 mRuleNode rather than by this style context.
+  // 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
   // sometimes allocate the mCachedResetData.
   nsResetStyleData*       mCachedResetData; // Cached reset style data.
   nsInheritedStyleData    mCachedInheritedData; // Cached inherited style data
 
   // mBits stores a number of things:
   //  - It records (using the style struct bits) which structs are
-  //    inherited from the parent context or owned by mRuleNode (i.e.,
+  //    inherited from the parent context or owned by the rule node (i.e.,
   //    not owned by the style context).
   //  - It also stores the additional bits listed at the top of
   //    nsStyleStruct.h.
   uint64_t                mBits;
 
   uint32_t                mRefCnt;
 
 #ifdef DEBUG
@@ -624,9 +661,18 @@ private:
 };
 
 already_AddRefed<nsStyleContext>
 NS_NewStyleContext(nsStyleContext* aParentContext,
                    nsIAtom* aPseudoTag,
                    mozilla::CSSPseudoElementType aPseudoType,
                    nsRuleNode* aRuleNode,
                    bool aSkipParentDisplayBasedStyleFixup);
+
+already_AddRefed<nsStyleContext>
+NS_NewStyleContext(nsStyleContext* aParentContext,
+                   nsPresContext* aPresContext,
+                   nsIAtom* aPseudoTag,
+                   mozilla::CSSPseudoElementType aPseudoType,
+                   already_AddRefed<ServoComputedValues> aComputedValues,
+                   bool aSkipParentDisplayBasedStyleFixup);
+
 #endif