Bug 1373018 - Part 5: stylo: Move child/sibling pointers to GeckoStyleContext; r=bholley
authorManish Goregaokar <manishearth@gmail.com>
Sat, 10 Jun 2017 22:27:45 -0700
changeset 364277 5492eb087406719b10f510b9b74093c460ee0622
parent 364276 df118b3de43b4fa7f69d0520077c49fdcf44b6b6
child 364278 3dcb1623e11548d9f4abc60de307d0de51ae52d6
push id32036
push userarchaeopteryx@coole-files.de
push dateFri, 16 Jun 2017 07:47:22 +0000
treeherdermozilla-central@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 5: stylo: Move child/sibling pointers to GeckoStyleContext; r=bholley MozReview-Commit-ID: Gay6RwpkNcu
layout/base/GeckoRestyleManager.cpp
layout/style/GeckoStyleContext.cpp
layout/style/GeckoStyleContext.h
layout/style/moz.build
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
layout/style/nsStyleContextInlines.h
layout/style/nsStyleSet.cpp
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -8,26 +8,28 @@
  * changes need to happen, scheduling them, and doing them.
  */
 
 #include "mozilla/GeckoRestyleManager.h"
 
 #include <algorithm> // For std::max
 #include "gfxContext.h"
 #include "mozilla/EffectSet.h"
+#include "mozilla/GeckoStyleContext.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/ViewportFrame.h"
 #include "mozilla/css/StyleRule.h" // For nsCSSSelector
 #include "nsLayoutUtils.h"
 #include "AnimationCommon.h" // For GetLayerAnimationInfo
 #include "FrameLayerBuilder.h"
 #include "GeckoProfiler.h"
 #include "nsAutoPtr.h"
 #include "nsStyleChangeList.h"
 #include "nsRuleProcessorData.h"
+#include "nsStyleContextInlines.h"
 #include "nsStyleSet.h"
 #include "nsStyleUtil.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsSVGEffects.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRendering.h"
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
@@ -3438,17 +3440,17 @@ ElementRestyler::SendAccessibilityNotifi
 
 static void
 ClearCachedInheritedStyleDataOnDescendants(
     nsTArray<ElementRestyler::ContextToClear>& aContextsToClear)
 {
   for (size_t i = 0; i < aContextsToClear.Length(); i++) {
     auto& entry = aContextsToClear[i];
     if (!entry.mStyleContext->HasSingleReference()) {
-      entry.mStyleContext->ClearCachedInheritedStyleDataOnDescendants(
+      entry.mStyleContext->AsGecko()->ClearCachedInheritedStyleDataOnDescendants(
           entry.mStructs);
     }
     entry.mStyleContext = nullptr;
   }
 }
 
 void
 GeckoRestyleManager::ComputeAndProcessStyleChange(
--- a/layout/style/GeckoStyleContext.cpp
+++ b/layout/style/GeckoStyleContext.cpp
@@ -4,26 +4,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/GeckoStyleContext.h"
 
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"
 #include "nsPresContext.h"
 #include "nsRuleNode.h"
+#include "nsStyleContextInlines.h"
 
 using namespace mozilla;
 
 GeckoStyleContext::GeckoStyleContext(nsStyleContext* aParent,
                                      nsIAtom* aPseudoTag,
                                      CSSPseudoElementType aPseudoType,
                                      already_AddRefed<nsRuleNode> aRuleNode,
                                      bool aSkipParentDisplayBasedStyleFixup)
   : nsStyleContext(aParent, OwningStyleContextSource(Move(aRuleNode)),
                    aPseudoTag, aPseudoType)
+  , mChild(nullptr)
+  , mEmptyChild(nullptr)
 {
   mBits |= NS_STYLE_CONTEXT_IS_GECKO;
 
   if (aParent) {
 #ifdef DEBUG
     nsRuleNode *r1 = mParent->RuleNode(), *r2 = mSource.AsGeckoRuleNode();
     while (r1->GetParent())
       r1 = r1->GetParent();
@@ -31,22 +34,205 @@ GeckoStyleContext::GeckoStyleContext(nsS
       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() calls AddChild which needs these
+  // to be initialized!
+  mNextSibling = this;
+  mPrevSibling = this;
+
   FinishConstruction();
   ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
 }
 
 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
 // (which comes from the presShell) to perform the allocation.
 void*
 GeckoStyleContext::operator new(size_t sz, nsPresContext* aPresContext)
 {
   MOZ_ASSERT(sz == sizeof(GeckoStyleContext));
   // Check the recycle list first.
   return aPresContext->PresShell()->
     AllocateByObjectID(eArenaObjectID_GeckoStyleContext, sz);
 }
+
+
+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;
+  // 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;
+    aChild->mPrevSibling = list->mPrevSibling;
+    list->mPrevSibling->mNextSibling = aChild;
+    list->mPrevSibling = aChild;
+  }
+  (*listPtr) = aChild;
+}
+
+void
+GeckoStyleContext::RemoveChild(GeckoStyleContext* aChild)
+{
+  NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
+
+  GeckoStyleContext **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");
+    (*list) = nullptr;
+  }
+
+  aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
+  aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
+  aChild->mNextSibling = aChild;
+  aChild->mPrevSibling = aChild;
+}
+
+#ifdef DEBUG
+void
+GeckoStyleContext::ListDescendants(FILE* out, int32_t aIndent)
+{
+  if (nullptr != mChild) {
+    GeckoStyleContext* child = mChild;
+    do {
+      child->List(out, aIndent + 1, true);
+      child = child->mNextSibling;
+    } while (mChild != child);
+  }
+  if (nullptr != mEmptyChild) {
+    GeckoStyleContext* child = mEmptyChild;
+    do {
+      child->List(out, aIndent + 1, true);
+      child = child->mNextSibling;
+    } while (mEmptyChild != child);
+  }
+}
+#endif
+
+void
+GeckoStyleContext::ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
+{
+  if (mChild) {
+    GeckoStyleContext* child = mChild;
+    do {
+      child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
+      child = child->mNextSibling;
+    } while (mChild != child);
+  }
+  if (mEmptyChild) {
+    GeckoStyleContext* child = mEmptyChild;
+    do {
+      child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
+      child = child->mNextSibling;
+    } while (mEmptyChild != child);
+  }
+}
+
+void
+GeckoStyleContext::DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
+{
+  NS_ASSERTION(mFrameRefCnt == 0, "frame still referencing style context");
+  for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
+       i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
+       i = nsStyleStructID(i + 1)) {
+    uint32_t bit = nsCachedStyleData::GetBitForSID(i);
+    if (aStructs & bit) {
+      if (!(mBits & bit) && mCachedInheritedData.mStyleStructs[i]) {
+        aStructs &= ~bit;
+      } else {
+        mCachedInheritedData.mStyleStructs[i] = nullptr;
+      }
+    }
+  }
+
+  if (mCachedResetData) {
+    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) {
+        if (!(mBits & bit) && mCachedResetData->mStyleStructs[i]) {
+          aStructs &= ~bit;
+        } else {
+          mCachedResetData->mStyleStructs[i] = nullptr;
+        }
+      }
+    }
+  }
+
+  if (aStructs == 0) {
+    return;
+  }
+
+  ClearCachedInheritedStyleDataOnDescendants(aStructs);
+}
+
+
+already_AddRefed<GeckoStyleContext>
+GeckoStyleContext::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<GeckoStyleContext> result;
+  GeckoStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
+
+  if (list) {
+    GeckoStyleContext *child = list;
+    do {
+      if (child->mSource.AsRaw() == aSource &&
+          child->mPseudoTag == aPseudoTag &&
+          !child->IsStyleIfVisited() &&
+          child->RelevantLinkVisited() == aRelevantLinkVisited) {
+        bool match = false;
+        if (!aSourceIfVisited.IsNull()) {
+          match = child->GetStyleIfVisited() &&
+                  child->GetStyleIfVisited()->AsGecko()->mSource.AsRaw() == aSourceIfVisited;
+        } else {
+          match = !child->GetStyleIfVisited();
+        }
+        if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
+          result = child;
+          break;
+        }
+      }
+      child = child->mNextSibling;
+      threshold--;
+      if (threshold == 0)
+        break;
+    } while (child != list);
+  }
+
+  if (result) {
+    if (result != list) {
+      // Move result to the front of the list.
+      RemoveChild(result);
+      AddChild(result);
+    }
+    result->mBits |= NS_STYLE_IS_SHARED;
+  }
+
+  return result.forget();
+}
+
--- a/layout/style/GeckoStyleContext.h
+++ b/layout/style/GeckoStyleContext.h
@@ -19,13 +19,54 @@ public:
                     already_AddRefed<nsRuleNode> aRuleNode,
                     bool aSkipParentDisplayBasedStyleFixup);
 
   void* operator new(size_t sz, nsPresContext* aPresContext);
 
   nsPresContext* PresContext() const {
     return mSource.AsGeckoRuleNode()->PresContext();
   }
+
+  void AddChild(GeckoStyleContext* aChild);
+  void RemoveChild(GeckoStyleContext* aChild);
+  /**
+   * 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
+  //  * !!GetStyleIfVisited() == !!aSourceIfVisited, and, if they're
+  //    non-null, GetStyleIfVisited()->mSource == aSourceIfVisited
+  //  * RelevantLinkVisited() == aRelevantLinkVisited
+  already_AddRefed<GeckoStyleContext>
+  FindChildWithRules(const nsIAtom* aPseudoTag,
+                     mozilla::NonOwningStyleContextSource aSource,
+                     mozilla::NonOwningStyleContextSource aSourceIfVisited,
+                     bool aRelevantLinkVisited);
+
+#ifdef DEBUG
+  void ListDescendants(FILE* out, int32_t aIndent);
+#endif
+
+private:
+  // Helper for ClearCachedInheritedStyleDataOnDescendants.
+  void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
+
+
+public:
+  // 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;
 };
 
 }
 
 #endif // mozilla_GeckoStyleContext_h
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -64,16 +64,17 @@ EXPORTS += [
     'nsMediaList.h',
     'nsRuleData.h',
     'nsRuleNode.h',
     'nsRuleProcessorData.h',
     'nsRuleWalker.h',
     'nsStyleAutoArray.h',
     'nsStyleConsts.h',
     'nsStyleContext.h',
+    'nsStyleContextInlines.h',
     'nsStyleCoord.h',
     'nsStyleSet.h',
     'nsStyleStruct.h',
     'nsStyleStructFwd.h',
     'nsStyleStructInlines.h',
     'nsStyleTransformMatrix.h',
     'nsStyleUtil.h',
 ]
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1,18 +1,16 @@
 /* -*- 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/. */
 
 /* the interface (to internal code) for retrieving computed style data */
 
 #include "nsStyleContext.h"
-#include "nsStyleContextInlines.h"
-
 #include "CSSVariableImageTable.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsFontMetrics.h"
 #include "nsStyleConsts.h"
@@ -32,16 +30,17 @@
 #include "nsPrintfCString.h"
 #include "RubyUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/GeckoStyleContext.h"
 #include "mozilla/ServoStyleContext.h"
+#include "nsStyleContextInlines.h"
 
 #include "mozilla/ReflowInput.h"
 #include "nsLayoutUtils.h"
 #include "nsCoord.h"
 
 // Ensure the binding function declarations in nsStyleContext.h matches
 // those in ServoBindings.h.
 #include "mozilla/ServoBindings.h"
@@ -82,18 +81,16 @@ const uint32_t nsStyleContext::sDependen
 static bool sExpensiveStyleStructAssertionsEnabled;
 #endif
 
 nsStyleContext::nsStyleContext(nsStyleContext* aParent,
                                OwningStyleContextSource&& aSource,
                                nsIAtom* aPseudoTag,
                                CSSPseudoElementType aPseudoType)
   : mParent(aParent)
-  , mChild(nullptr)
-  , mEmptyChild(nullptr)
   , 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)
@@ -112,33 +109,34 @@ nsStyleContext::FinishConstruction()
   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);
   }
 
   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()
 {
-  NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children");
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    NS_ASSERTION((nullptr == gecko->mChild) && (nullptr == gecko->mEmptyChild),
+                 "destructing context with children");
+  }
   MOZ_ASSERT(!mSource.IsServoComputedValues() || !mCachedResetData);
 
 #ifdef DEBUG
   if (mSource.IsServoComputedValues()) {
     MOZ_ASSERT(!mCachedResetData,
                "Servo shouldn't cache reset structs in nsStyleContext");
     for (const auto* data : mCachedInheritedData.mStyleStructs) {
       MOZ_ASSERT(!data,
@@ -250,76 +248,49 @@ nsStyleContext::AssertStructsNotUsedElse
 #include "nsStyleStructList.h"
 
 #undef STYLE_STRUCT_INHERITED
 #undef STYLE_STRUCT_RESET
       }
     }
   }
 
-  if (mChild) {
-    const nsStyleContext* child = mChild;
-    do {
-      child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
-      child = child->mNextSibling;
-    } while (child != mChild);
-  }
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    if (gecko->mChild) {
+      const GeckoStyleContext* child = gecko->mChild;
+      do {
+        child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+        child = child->mNextSibling;
+      } while (child != gecko->mChild);
+    }
 
-  if (mEmptyChild) {
-    const nsStyleContext* child = mEmptyChild;
-    do {
-      child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
-      child = child->mNextSibling;
-    } while (child != mEmptyChild);
+    if (gecko->mEmptyChild) {
+      const GeckoStyleContext* child = gecko->mEmptyChild;
+      do {
+        child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+        child = child->mNextSibling;
+      } while (child != gecko->mEmptyChild);
+    }
   }
 }
 #endif
 
+
 void nsStyleContext::AddChild(nsStyleContext* aChild)
 {
-  NS_ASSERTION(aChild->mPrevSibling == aChild &&
-               aChild->mNextSibling == aChild,
-               "child already in a child list");
-
-  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;
-    aChild->mPrevSibling = list->mPrevSibling;
-    list->mPrevSibling->mNextSibling = aChild;
-    list->mPrevSibling = aChild;
+  if (GeckoStyleContext* gecko = GetAsGecko()) {
+    gecko->AddChild(aChild->AsGecko());
   }
-  (*listPtr) = aChild;
 }
 
 void nsStyleContext::RemoveChild(nsStyleContext* aChild)
 {
-  NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
-
-  nsStyleContext **list = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild;
-
-  if (aChild->mPrevSibling != aChild) { // has siblings
-    if ((*list) == aChild) {
-      (*list) = (*list)->mNextSibling;
-    }
+  if (GeckoStyleContext* gecko = GetAsGecko()) {
+    gecko->RemoveChild(aChild->AsGecko());
   }
-  else {
-    NS_ASSERTION((*list) == aChild, "bad sibling pointers");
-    (*list) = nullptr;
-  }
-
-  aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
-  aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
-  aChild->mNextSibling = aChild;
-  aChild->mPrevSibling = aChild;
 }
 
 void
 nsStyleContext::MoveTo(nsStyleContext* aNewParent)
 {
   MOZ_ASSERT(aNewParent != mParent);
 
   // This function shouldn't be getting called if the parents have different
@@ -358,66 +329,16 @@ 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,
-                                   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 = aSource.MatchesNoRules() ? mEmptyChild : mChild;
-
-  if (list) {
-    nsStyleContext *child = list;
-    do {
-      if (child->mSource.AsRaw() == aSource &&
-          child->mPseudoTag == aPseudoTag &&
-          !child->IsStyleIfVisited() &&
-          child->RelevantLinkVisited() == aRelevantLinkVisited) {
-        bool match = false;
-        if (!aSourceIfVisited.IsNull()) {
-          match = child->GetStyleIfVisited() &&
-                  child->GetStyleIfVisited()->mSource.AsRaw() == aSourceIfVisited;
-        } else {
-          match = !child->GetStyleIfVisited();
-        }
-        if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
-          result = child;
-          break;
-        }
-      }
-      child = child->mNextSibling;
-      threshold--;
-      if (threshold == 0)
-        break;
-    } while (child != list);
-  }
-
-  if (result) {
-    if (result != list) {
-      // Move result to the front of the list.
-      RemoveChild(result);
-      AddChild(result);
-    }
-    result->mBits |= NS_STYLE_IS_SHARED;
-  }
-
-  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 style source will take care of it for us.
   const void* newData;
   if (mSource.IsGeckoRuleNode()) {
@@ -471,17 +392,22 @@ nsStyleContext::GetUniqueStyleData(const
              "Can't COW-mutate servo values from Gecko!");
 
   // If we already own the struct and no kids could depend on it, then
   // just return it.  (We leak in this case if there are kids -- and this
   // function really shouldn't be called for style contexts that could
   // have kids depending on the data.  ClearStyleData would be OK, but
   // this test for no mChild or mEmptyChild doesn't catch that case.)
   const void *current = StyleData(aSID);
-  if (!mChild && !mEmptyChild &&
+  GeckoStyleContext *child = nullptr, *emptyChild = nullptr;
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    child = gecko->mChild;
+    emptyChild = gecko->mEmptyChild;
+  }
+  if (!child && !emptyChild &&
       !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
       GetCachedStyleData(aSID))
     return const_cast<void*>(current);
 
   void* result;
   nsPresContext *presContext = PresContext();
   switch (aSID) {
 
@@ -510,18 +436,20 @@ nsStyleContext::GetUniqueStyleData(const
   return result;
 }
 
 // This is an evil function, but less evil than GetUniqueStyleData. It
 // creates an empty style struct for this nsStyleContext.
 void*
 nsStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID)
 {
-  MOZ_ASSERT(!mChild && !mEmptyChild &&
-             !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    MOZ_ASSERT(!gecko->mChild && !gecko->mEmptyChild, "This style should not have been computed");
+  }
+  MOZ_ASSERT(!(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
              !GetCachedStyleData(aSID),
              "This style should not have been computed");
 
   void* result;
   nsPresContext* presContext = PresContext();
   switch (aSID) {
 #define UNIQUE_CASE(c_) \
     case eStyleStruct_##c_: \
@@ -1300,29 +1228,18 @@ void nsStyleContext::List(FILE* out, int
     }
     fprintf_stderr(out, "%s}\n", str.get());
   }
   else {
     fprintf_stderr(out, "%s{}\n", str.get());
   }
 
   if (aListDescendants) {
-    if (nullptr != mChild) {
-      nsStyleContext* child = mChild;
-      do {
-        child->List(out, aIndent + 1, aListDescendants);
-        child = child->mNextSibling;
-      } while (mChild != child);
-    }
-    if (nullptr != mEmptyChild) {
-      nsStyleContext* child = mEmptyChild;
-      do {
-        child->List(out, aIndent + 1, aListDescendants);
-        child = child->mNextSibling;
-      } while (mEmptyChild != child);
+    if (GeckoStyleContext* gecko = GetAsGecko()) {
+      gecko->ListDescendants(out, aIndent);
     }
   }
 }
 #endif
 
 // 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
@@ -1531,93 +1448,37 @@ nsStyleContext::SwapStyleData(nsStyleCon
       }
     } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
       std::swap(thisData, otherData);
     }
   }
 }
 
 void
-nsStyleContext::ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
-{
-  if (mChild) {
-    nsStyleContext* child = mChild;
-    do {
-      child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
-      child = child->mNextSibling;
-    } while (mChild != child);
-  }
-  if (mEmptyChild) {
-    nsStyleContext* child = mEmptyChild;
-    do {
-      child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
-      child = child->mNextSibling;
-    } while (mEmptyChild != child);
-  }
-}
-
-void
-nsStyleContext::DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
-{
-  NS_ASSERTION(mFrameRefCnt == 0, "frame still referencing style context");
-  for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
-       i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
-       i = nsStyleStructID(i + 1)) {
-    uint32_t bit = nsCachedStyleData::GetBitForSID(i);
-    if (aStructs & bit) {
-      if (!(mBits & bit) && mCachedInheritedData.mStyleStructs[i]) {
-        aStructs &= ~bit;
-      } else {
-        mCachedInheritedData.mStyleStructs[i] = nullptr;
-      }
-    }
-  }
-
-  if (mCachedResetData) {
-    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) {
-        if (!(mBits & bit) && mCachedResetData->mStyleStructs[i]) {
-          aStructs &= ~bit;
-        } else {
-          mCachedResetData->mStyleStructs[i] = nullptr;
-        }
-      }
-    }
-  }
-
-  if (aStructs == 0) {
-    return;
-  }
-
-  ClearCachedInheritedStyleDataOnDescendants(aStructs);
-}
-
-void
 nsStyleContext::SetIneligibleForSharing()
 {
   if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
     return;
   }
   mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
-  if (mChild) {
-    nsStyleContext* child = mChild;
-    do {
-      child->SetIneligibleForSharing();
-      child = child->mNextSibling;
-    } while (mChild != child);
-  }
-  if (mEmptyChild) {
-    nsStyleContext* child = mEmptyChild;
-    do {
-      child->SetIneligibleForSharing();
-      child = child->mNextSibling;
-    } while (mEmptyChild != child);
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    if (gecko->mChild) {
+      GeckoStyleContext* child = gecko->mChild;
+      do {
+        child->SetIneligibleForSharing();
+        child = child->mNextSibling;
+      } while (gecko->mChild != child);
+    }
+    if (gecko->mEmptyChild) {
+      GeckoStyleContext* child = gecko->mEmptyChild;
+      do {
+        child->SetIneligibleForSharing();
+        child = child->mNextSibling;
+      } while (gecko->mEmptyChild != child);
+    }
   }
 }
 
 #ifdef RESTYLE_LOGGING
 nsCString
 nsStyleContext::GetCachedStyleDataAsString(uint32_t aStructs)
 {
   nsCString structs;
@@ -1687,29 +1548,31 @@ nsStyleContext::LogStyleContextTree(bool
   }
 
   LOG_RESTYLE("%p(%d) %s%s%s%s",
               this, mRefCnt,
               structs.get(), pseudo.get(), flags.get(), parent.get());
 
   LOG_RESTYLE_INDENT();
 
-  if (nullptr != mChild) {
-    nsStyleContext* child = mChild;
-    do {
-      child->LogStyleContextTree(false, aStructs);
-      child = child->mNextSibling;
-    } while (mChild != child);
-  }
-  if (nullptr != mEmptyChild) {
-    nsStyleContext* child = mEmptyChild;
-    do {
-      child->LogStyleContextTree(false, aStructs);
-      child = child->mNextSibling;
-    } while (mEmptyChild != child);
+  if (const GeckoStyleContext* gecko = GetAsGecko()) {
+    if (nullptr != gecko->mChild) {
+      GeckoStyleContext* child = gecko->mChild;
+      do {
+        child->LogStyleContextTree(false, aStructs);
+        child = child->mNextSibling;
+      } while (gecko->mChild != child);
+    }
+    if (nullptr != gecko->mEmptyChild) {
+      GeckoStyleContext* child = gecko->mEmptyChild;
+      do {
+        child->LogStyleContextTree(false, aStructs);
+        child = child->mNextSibling;
+      } while (gecko->mEmptyChild != child);
+    }
   }
 }
 #endif
 
 #ifdef DEBUG
 /* static */ void
 nsStyleContext::Initialize()
 {
@@ -1719,8 +1582,9 @@ nsStyleContext::Initialize()
 }
 #endif
 
 nsPresContext*
 nsStyleContext::PresContext() const
 {
     MOZ_STYLO_FORWARD(PresContext, ())
 }
+
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -149,29 +149,16 @@ public:
   bool IsAnonBox() const {
     return
       GetPseudoType() == mozilla::CSSPseudoElementType::InheritingAnonBox ||
       GetPseudoType() == mozilla::CSSPseudoElementType::NonInheritingAnonBox;
   }
   bool IsPseudoElement() const { return mPseudoTag && !IsAnonBox(); }
 
 
-  // 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
-  //  * !!GetStyleIfVisited() == !!aSourceIfVisited, and, if they're
-  //    non-null, GetStyleIfVisited()->mSource == aSourceIfVisited
-  //  * RelevantLinkVisited() == aRelevantLinkVisited
-  already_AddRefed<nsStyleContext>
-  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); }
 
   // Whether any line break inside should be suppressed? If this returns
@@ -455,21 +442,16 @@ public:
    * inheriting from the old ancestor.  This is not normally a problem, as
    * this style context will usually be destroyed by being released at the
    * end of ElementRestyler::Restyle; but for style contexts held on to outside
    * of the frame, we need to clear out the cached pointer so that if we need
    * it again we'll re-fetch it from the new ancestor.
    */
   void SwapStyleData(nsStyleContext* aNewContext, uint32_t aStructs);
 
-  /**
-   * On each descendant of this style context, clears out any cached inherited
-   * structs indicated in aStructs.
-   */
-  void ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
 
   /**
    * 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();
 
@@ -504,29 +486,30 @@ public:
     } else {
       cachedData = mCachedInheritedData.mStyleStructs[aSID];
     }
     return cachedData;
   }
 
   mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); }
 
-protected:
+public: // temporary
   // 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();
 
+  // Only does stuff in Gecko mode
   void AddChild(nsStyleContext* aChild);
   void RemoveChild(nsStyleContext* aChild);
 
   void* GetUniqueStyleData(const nsStyleStructID& aSID);
   void* CreateEmptyStyleData(const nsStyleStructID& aSID);
 
   void SetStyleBits();
 
@@ -676,46 +659,32 @@ protected:
         AddStyleBit(NS_STYLE_INHERIT_BIT(name_));                       \
       }                                                                 \
       return data;                                                      \
     }
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT_RESET
   #undef STYLE_STRUCT_INHERITED
 
-  // Helper for ClearCachedInheritedStyleDataOnDescendants.
-  void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
-
 #ifdef DEBUG
   void AssertStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
                                      int32_t aLevels) const;
 #endif
 
 #ifdef RESTYLE_LOGGING
   void LogStyleContextTree(bool aFirst, uint32_t aStructs);
 
   // This only gets called under call trees where we've already checked
   // that PresContext()->RestyleManager()->ShouldLogRestyle() returned true.
   // It exists here just to satisfy LOG_RESTYLE's expectations.
   bool ShouldLogRestyle() { return true; }
 #endif
 
   RefPtr<nsStyleContext> mParent;
 
-  // 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.
-  nsStyleContext* mChild;
-  nsStyleContext* mEmptyChild;
-  nsStyleContext* mPrevSibling;
-  nsStyleContext* mNextSibling;
-
   // Style to be used instead for the R, G, and B components of color,
   // 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;
--- a/layout/style/nsStyleContextInlines.h
+++ b/layout/style/nsStyleContextInlines.h
@@ -6,21 +6,21 @@
 
 /*
  * Inlined methods for nsStyleContext. Will just redirect to
  * GeckoStyleContext methods when compiled without stylo, but will do
  * virtual dispatch (by checking which kind of container it is)
  * in stylo mode.
  */
 
-#ifndef mozilla_nsStyleContextInlines_h
-#define mozilla_nsStyleContextInlines_h
+#ifndef nsStyleContextInlines_h
+#define nsStyleContextInlines_h
 
 #include "nsStyleContext.h"
 #include "mozilla/ServoStyleContext.h"
 #include "mozilla/GeckoStyleContext.h"
 #include "mozilla/ServoUtils.h"
 
 using namespace mozilla;
 
 MOZ_DEFINE_STYLO_METHODS(nsStyleContext, GeckoStyleContext, ServoStyleContext);
 
-#endif // mozilla_nsStyleContextInlines_h
+#endif // nsStyleContextInlines_h
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -38,16 +38,17 @@
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsCSSRules.h"
 #include "nsPrintfCString.h"
 #include "nsIFrame.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
 #include "nsQueryObject.h"
+#include "nsStyleContextInlines.h"
 
 #include <inttypes.h>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(nsEmptyStyleRule, nsIStyleRule)
 
@@ -921,17 +922,17 @@ nsStyleSet::GetContext(nsStyleContext* a
   }
 
   bool relevantLinkVisited = (aFlags & eIsLink) ?
     (aFlags & eIsVisitedLink) :
     (aParentContext && aParentContext->RelevantLinkVisited());
 
   RefPtr<nsStyleContext> result;
   if (aParentContext)
-    result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode,
+    result = aParentContext->AsGecko()->FindChildWithRules(aPseudoTag, aRuleNode,
                                                 aVisitedRuleNode,
                                                 relevantLinkVisited);
 
   if (!result) {
     // |aVisitedRuleNode| may have a ref-count of zero since we are yet
     // to create the style context that will hold an owning reference to it.
     // As a result, we need to make sure it stays alive until that point
     // in case something in the first call to NS_NewStyleContext triggers a