Bug 1373018 - Part 6: stylo: Move most Gecko-specific methods into GeckoStyleContext; r=bholley
authorManish Goregaokar <manishearth@gmail.com>
Sat, 10 Jun 2017 22:27:45 -0700
changeset 412984 3dcb1623e11548d9f4abc60de307d0de51ae52d6
parent 412983 5492eb087406719b10f510b9b74093c460ee0622
child 412985 305613bee6fb16a5f33077cc52807dcc5713f9d9
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [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 6: stylo: Move most Gecko-specific methods into GeckoStyleContext; r=bholley MozReview-Commit-ID: KzMAbuY8nll
layout/base/GeckoRestyleManager.cpp
layout/style/GeckoStyleContext.cpp
layout/style/GeckoStyleContext.h
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -1764,17 +1764,17 @@ ElementRestyler::Restyle(nsRestyleHint a
   // If we are restyling this frame with eRestyle_Self or weaker hints,
   // we restyle children with nsRestyleHint(0).  But we pass the
   // eRestyle_ForceDescendants flag down too.
   nsRestyleHint childRestyleHint =
     nsRestyleHint(aRestyleHint & (eRestyle_SomeDescendants |
                                   eRestyle_Subtree |
                                   eRestyle_ForceDescendants));
 
-  RefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
+  RefPtr<GeckoStyleContext> oldContext = mFrame->StyleContext()->AsGecko();
 
   nsTArray<SwapInstruction> swaps;
 
   // TEMPORARY (until bug 918064):  Call RestyleSelf for each
   // continuation or block-in-inline sibling.
 
   // We must make a single decision on how to process this frame and
   // its descendants, yet RestyleSelf might return different RestyleResult
--- a/layout/style/GeckoStyleContext.cpp
+++ b/layout/style/GeckoStyleContext.cpp
@@ -5,16 +5,20 @@
 
 #include "mozilla/GeckoStyleContext.h"
 
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"
 #include "nsPresContext.h"
 #include "nsRuleNode.h"
 #include "nsStyleContextInlines.h"
+#include "nsIFrame.h"
+#include "nsLayoutUtils.h"
+#include "mozilla/ReflowInput.h"
+#include "RubyUtils.h"
 
 using namespace mozilla;
 
 GeckoStyleContext::GeckoStyleContext(nsStyleContext* aParent,
                                      nsIAtom* aPseudoTag,
                                      CSSPseudoElementType aPseudoType,
                                      already_AddRefed<nsRuleNode> aRuleNode,
                                      bool aSkipParentDisplayBasedStyleFixup)
@@ -231,8 +235,531 @@ GeckoStyleContext::FindChildWithRules(co
       AddChild(result);
     }
     result->mBits |= NS_STYLE_IS_SHARED;
   }
 
   return result.forget();
 }
 
+
+
+// This is an evil evil function, since it forces you to alloc your own separate copy of
+// style data!  Do not use this function unless you absolutely have to!  You should avoid
+// this at all costs! -dwh
+void*
+GeckoStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
+{
+  // 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 &&
+      !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
+      GetCachedStyleData(aSID))
+    return const_cast<void*>(current);
+
+  void* result;
+  nsPresContext *presContext = PresContext();
+  switch (aSID) {
+
+#define UNIQUE_CASE(c_)                                                       \
+  case eStyleStruct_##c_:                                                     \
+    result = new (presContext) nsStyle##c_(                                   \
+      * static_cast<const nsStyle##c_ *>(current));                           \
+    break;
+
+  UNIQUE_CASE(Font)
+  UNIQUE_CASE(Display)
+  UNIQUE_CASE(Text)
+  UNIQUE_CASE(TextReset)
+  UNIQUE_CASE(Visibility)
+
+#undef UNIQUE_CASE
+
+  default:
+    NS_ERROR("Struct type not supported.  Please find another way to do this if you can!");
+    return nullptr;
+  }
+
+  SetStyle(aSID, result);
+  mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
+
+  return result;
+}
+
+
+// This is an evil function, but less evil than GetUniqueStyleData. It
+// creates an empty style struct for this nsStyleContext.
+void*
+GeckoStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID)
+{
+  MOZ_ASSERT(!mChild && !mEmptyChild &&
+             !(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_: \
+      result = new (presContext) nsStyle##c_(presContext); \
+      break;
+
+  UNIQUE_CASE(Border)
+  UNIQUE_CASE(Padding)
+
+#undef UNIQUE_CASE
+
+  default:
+    NS_ERROR("Struct type not supported.");
+    return nullptr;
+  }
+
+  // The new struct is owned by this style context, but that we don't
+  // need to clear the bit in mBits because we've asserted that at the
+  // top of this function.
+  SetStyle(aSID, result);
+  return result;
+}
+
+
+void
+GeckoStyleContext::SetIneligibleForSharing()
+{
+  if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
+    return;
+  }
+  mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
+  if (mChild) {
+    GeckoStyleContext* child = mChild;
+    do {
+      child->SetIneligibleForSharing();
+      child = child->mNextSibling;
+    } while (mChild != child);
+  }
+  if (mEmptyChild) {
+    GeckoStyleContext* child = mEmptyChild;
+    do {
+      child->SetIneligibleForSharing();
+      child = child->mNextSibling;
+    } while (mEmptyChild != child);
+  }
+}
+
+void
+GeckoStyleContext::LogChildStyleContextTree(uint32_t aStructs) const
+{
+  if (nullptr != mChild) {
+    GeckoStyleContext* child = mChild;
+    do {
+      child->LogStyleContextTree(false, aStructs);
+      child = child->mNextSibling;
+    } while (mChild != child);
+  }
+  if (nullptr != mEmptyChild) {
+    GeckoStyleContext* child = mEmptyChild;
+    do {
+      child->LogStyleContextTree(false, aStructs);
+      child = child->mNextSibling;
+    } while (mEmptyChild != child);
+  }
+}
+
+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
+  if (aDisplay->IsOutOfFlowStyle()) {
+    return false;
+  }
+  // Display value of any anonymous box should not be touched. In most
+  // cases, anonymous boxes are actually not in ruby frame, but instead,
+  // some other frame with a ruby display value. Non-element pseudos
+  // which represents text frames, as well as ruby pseudos are excluded
+  // because we still want to set the flag for them.
+  if ((aContext->GetPseudoType() == CSSPseudoElementType::InheritingAnonBox ||
+       aContext->GetPseudoType() == CSSPseudoElementType::NonInheritingAnonBox) &&
+      !nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) &&
+      !RubyUtils::IsRubyPseudo(aContext->GetPseudo())) {
+    return false;
+  }
+  if (aParentContext->ShouldSuppressLineBreak()) {
+    // Line break suppressing bit is propagated to any children of
+    // line participants, which include inline, contents, and inline
+    // ruby boxes.
+    if (aParentDisplay->mDisplay == mozilla::StyleDisplay::Inline ||
+        aParentDisplay->mDisplay == mozilla::StyleDisplay::Contents ||
+        aParentDisplay->mDisplay == mozilla::StyleDisplay::Ruby ||
+        aParentDisplay->mDisplay == mozilla::StyleDisplay::RubyBaseContainer) {
+      return true;
+    }
+  }
+  // Any descendant of ruby level containers is non-breakable, but
+  // the level containers themselves are breakable. We have to check
+  // the container display type against all ruby display type here
+  // because any of the ruby boxes could be anonymous.
+  // Note that, when certain HTML tags, e.g. form controls, have ruby
+  // level container display type, they could also escape from this flag
+  // while they shouldn't. However, it is generally fine since they
+  // won't usually break the assertion that there is no line break
+  // inside ruby, because:
+  // 1. their display types, the ruby level container types, are inline-
+  //    outside, which means they won't cause any forced line break; and
+  // 2. they never start an inline span, which means their children, if
+  //    any, won't be able to break the line its ruby ancestor lays; and
+  // 3. their parent frame is always a ruby content frame (due to
+  //    anonymous ruby box generation), which makes line layout suppress
+  //    any optional line break around this frame.
+  // However, there is one special case which is BR tag, because it
+  // directly affects the line layout. This case is handled by the BR
+  // frame which checks the flag of its parent frame instead of itself.
+  if ((aParentDisplay->IsRubyDisplayType() &&
+       aDisplay->mDisplay != mozilla::StyleDisplay::RubyBaseContainer &&
+       aDisplay->mDisplay != mozilla::StyleDisplay::RubyTextContainer) ||
+      // Since ruby base and ruby text may exist themselves without any
+      // non-anonymous frame outside, we should also check them.
+      aDisplay->mDisplay == mozilla::StyleDisplay::RubyBase ||
+      aDisplay->mDisplay == mozilla::StyleDisplay::RubyText) {
+    return true;
+  }
+  return false;
+}
+
+void
+nsStyleContext::SetStyleBits()
+{
+  // Here we set up various style bits for both the Gecko and Servo paths.
+  // _Only_ change the bits here.  For fixups of the computed values, you can
+  // add to ApplyStyleFixups in Gecko and StyleAdjuster as part of Servo's
+  // cascade.
+
+  // See if we have any text decorations.
+  // First see if our parent has text decorations.  If our parent does, then we inherit the bit.
+  if (mParent && mParent->HasTextDecorationLines()) {
+    AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
+  } else {
+    // We might have defined a decoration.
+    if (StyleTextReset()->HasTextDecorationLines()) {
+      AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
+    }
+  }
+
+  if ((mParent && mParent->HasPseudoElementData()) || IsPseudoElement()) {
+    AddStyleBit(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA);
+  }
+
+  // Set the NS_STYLE_IN_DISPLAY_NONE_SUBTREE bit
+  const nsStyleDisplay* disp = StyleDisplay();
+  if ((mParent && mParent->IsInDisplayNoneSubtree()) ||
+      disp->mDisplay == mozilla::StyleDisplay::None) {
+    AddStyleBit(NS_STYLE_IN_DISPLAY_NONE_SUBTREE);
+  }
+
+  // Mark text combined for text-combine-upright, as needed.
+  if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
+      mParent->StyleVisibility()->mWritingMode !=
+        NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
+      mParent->StyleText()->mTextCombineUpright ==
+        NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
+    AddStyleBit(NS_STYLE_IS_TEXT_COMBINED);
+  }
+}
+
+// Flex & grid containers blockify their children.
+//  "The display value of a flex item is blockified"
+//    https://drafts.csswg.org/css-flexbox-1/#flex-items
+//  "The display value of a grid item is blockified"
+//    https://drafts.csswg.org/css-grid/#grid-items
+static bool
+ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp)
+{
+  auto displayVal = aStyleDisp->mDisplay;
+  return mozilla::StyleDisplay::Flex == displayVal ||
+    mozilla::StyleDisplay::InlineFlex == displayVal ||
+    mozilla::StyleDisplay::Grid == displayVal ||
+    mozilla::StyleDisplay::InlineGrid == displayVal;
+}
+
+#ifdef DEBUG
+void
+GeckoStyleContext::AssertChildStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
+                                                      int32_t aLevels) const
+{
+  if (mChild) {
+    const GeckoStyleContext* child = mChild;
+    do {
+      child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+      child = child->mNextSibling;
+    } while (child != mChild);
+  }
+
+  if (mEmptyChild) {
+    const GeckoStyleContext* child = mEmptyChild;
+    do {
+      child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
+      child = child->mNextSibling;
+    } 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) {
+    const nsStyleTextReset* textReset = StyleTextReset();
+    if (textReset->mInitialLetterSize != 0.0f) {
+      nsStyleContext* containerSC = mParent;
+      const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
+      while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
+        if (!containerSC->GetParent()) {
+          break;
+        }
+        containerSC = containerSC->GetParent();
+        containerDisp = containerSC->StyleDisplay();
+      }
+      nscoord containerLH =
+        ReflowInput::CalcLineHeight(nullptr, containerSC, NS_AUTOHEIGHT, 1.0f);
+      RefPtr<nsFontMetrics> containerFM =
+        nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
+      MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
+      nscoord containerCH = containerFM->CapHeight();
+      RefPtr<nsFontMetrics> firstLetterFM =
+        nsLayoutUtils::GetFontMetricsForStyleContext(this);
+      MOZ_ASSERT(firstLetterFM, "Should have fontMetrics!!");
+      nscoord firstLetterCH = firstLetterFM->CapHeight();
+      nsStyleFont* mutableStyleFont = GET_UNIQUE_STYLE_DATA(Font);
+      float invCapHeightRatio =
+        mutableStyleFont->mFont.size / NSCoordToFloat(firstLetterCH);
+      mutableStyleFont->mFont.size =
+        NSToCoordRound(((textReset->mInitialLetterSize - 1) * containerLH +
+                        containerCH) *
+                       invCapHeightRatio);
+    }
+  }
+
+  // Change writing mode of text frame for text-combine-upright. We use
+  // style structs of the parent to avoid triggering computation before
+  // we change the writing mode.
+  // It is safe to look at the parent's style because we are looking at
+  // inherited properties, and ::-moz-text never matches any rules.
+  if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
+      mParent->StyleVisibility()->mWritingMode !=
+        NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
+      mParent->StyleText()->mTextCombineUpright ==
+        NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
+    MOZ_ASSERT(!PeekStyleVisibility(), "If StyleVisibility was already "
+               "computed, some properties may have been computed "
+               "incorrectly based on the old writing mode value");
+    nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility);
+    mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
+  }
+
+  // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
+  // (PageContentFrame/CanvasFrame etc will inherit 'direction')
+  if (mPseudoTag == nsCSSAnonBoxes::viewport) {
+    nsPresContext* presContext = PresContext();
+    mozilla::dom::Element* docElement = presContext->Document()->GetRootElement();
+    if (docElement) {
+      RefPtr<nsStyleContext> rootStyle =
+        presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, nullptr);
+      auto dir = rootStyle->StyleVisibility()->mDirection;
+      if (dir != StyleVisibility()->mDirection) {
+        nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
+        uniqueVisibility->mDirection = dir;
+      }
+    }
+  }
+
+  // Correct tables.
+  const nsStyleDisplay* disp = StyleDisplay();
+  if (disp->mDisplay == mozilla::StyleDisplay::Table) {
+    // -moz-center and -moz-right are used for HTML's alignment
+    // This is covering the <div align="right"><table>...</table></div> case.
+    // In this case, we don't want to inherit the text alignment into the table.
+    const nsStyleText* text = StyleText();
+
+    if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
+        text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
+        text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
+    {
+      nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
+      uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
+    }
+  }
+
+  // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
+  // the root element.  We can't implement them in nsRuleNode because we
+  // don't want to store all display structs that aren't 'block',
+  // 'inline', or 'table' in the style context tree on the off chance
+  // that the root element has its style reresolved later.  So do them
+  // here if needed, by changing the style data, so that other code
+  // doesn't get confused by looking at the style data.
+  if (!mParent &&
+      // We don't want to blockify various anon boxes that just happen to not
+      // inherit from anything.  So restrict blockification only to actual
+      // elements, the viewport (which should be block anyway, but in SVG
+      // document's isn't because we lazy-load ua.css there), and the ::backdrop
+      // pseudo-element.  This last is explicitly allowed to have any specified
+      // display type in the spec, but computes to a blockified display type per
+      // various provisions of
+      // https://fullscreen.spec.whatwg.org/#new-stacking-layer
+      (!mPseudoTag ||
+       mPseudoTag == nsCSSAnonBoxes::viewport ||
+       mPseudoTag == nsCSSPseudoElements::backdrop)) {
+    auto displayVal = disp->mDisplay;
+    if (displayVal != mozilla::StyleDisplay::Contents) {
+      nsRuleNode::EnsureBlockDisplay(displayVal, true);
+    } else {
+      // http://dev.w3.org/csswg/css-display/#transformations
+      // "... a display-outside of 'contents' computes to block-level
+      //  on the root element."
+      displayVal = mozilla::StyleDisplay::Block;
+    }
+    if (displayVal != disp->mDisplay) {
+      nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+      disp = mutable_display;
+
+      // If we're in this code, then mOriginalDisplay doesn't matter
+      // for purposes of the cascade (because this nsStyleDisplay
+      // isn't living in the ruletree anyway), and for determining
+      // hypothetical boxes it's better to have mOriginalDisplay
+      // matching mDisplay here.
+      mutable_display->mOriginalDisplay = mutable_display->mDisplay =
+        displayVal;
+    }
+  }
+
+  // Adjust the "display" values of flex and grid items (but not for raw text
+  // or placeholders). CSS3 Flexbox section 4 says:
+  //   # The computed 'display' of a flex item is determined
+  //   # by applying the table in CSS 2.1 Chapter 9.7.
+  // ...which converts inline-level elements to their block-level equivalents.
+  // Any block-level element directly contained by elements with ruby display
+  // values are converted to their inline-level equivalents.
+  if (!aSkipParentDisplayBasedStyleFixup && mParent) {
+    // Skip display:contents ancestors to reach the potential container.
+    // (If there are only display:contents ancestors between this node and
+    // a flex/grid container ancestor, then this node is a flex/grid item, since
+    // its parent *in the frame tree* will be the flex/grid container. So we treat
+    // it like a flex/grid item here.)
+    nsStyleContext* containerContext = mParent;
+    const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
+    while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
+      if (!containerContext->GetParent()) {
+        break;
+      }
+      containerContext = containerContext->GetParent();
+      containerDisp = containerContext->StyleDisplay();
+    }
+    if (ShouldBlockifyChildren(containerDisp) &&
+        !nsCSSAnonBoxes::IsNonElement(GetPseudo())) {
+      // NOTE: Technically, we shouldn't modify the 'display' value of
+      // positioned elements, since they aren't flex/grid items. However,
+      // we don't need to worry about checking for that, because if we're
+      // positioned, we'll have already been through a call to
+      // EnsureBlockDisplay() in nsRuleNode, so this call here won't change
+      // anything. So we're OK.
+      auto displayVal = disp->mDisplay;
+      nsRuleNode::EnsureBlockDisplay(displayVal);
+      if (displayVal != disp->mDisplay) {
+        NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
+                     "We shouldn't be changing the display value of "
+                     "positioned content (and we should have already "
+                     "converted its display value to be block-level...)");
+        nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+        disp = mutable_display;
+        mutable_display->mDisplay = displayVal;
+      }
+    }
+  }
+
+  // Note: This must come after the blockification above, otherwise we fail
+  // the grid-item-blockifying-001.html reftest.
+  if (mParent && ::ShouldSuppressLineBreak(this, disp, mParent,
+                                           mParent->StyleDisplay())) {
+    mBits |= NS_STYLE_SUPPRESS_LINEBREAK;
+    auto displayVal = disp->mDisplay;
+    nsRuleNode::EnsureInlineDisplay(displayVal);
+    if (displayVal != disp->mDisplay) {
+      nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
+      disp = mutable_display;
+      mutable_display->mDisplay = displayVal;
+    }
+  }
+  // Suppress border/padding of ruby level containers
+  if (disp->mDisplay == mozilla::StyleDisplay::RubyBaseContainer ||
+      disp->mDisplay == mozilla::StyleDisplay::RubyTextContainer) {
+    CreateEmptyStyleData(eStyleStruct_Border);
+    CreateEmptyStyleData(eStyleStruct_Padding);
+  }
+  if (disp->IsRubyDisplayType()) {
+    // Per CSS Ruby spec section Bidi Reordering, for all ruby boxes,
+    // the 'normal' and 'embed' values of 'unicode-bidi' should compute to
+    // 'isolate', and 'bidi-override' should compute to 'isolate-override'.
+    const nsStyleTextReset* textReset = StyleTextReset();
+    uint8_t unicodeBidi = textReset->mUnicodeBidi;
+    if (unicodeBidi == NS_STYLE_UNICODE_BIDI_NORMAL ||
+        unicodeBidi == NS_STYLE_UNICODE_BIDI_EMBED) {
+      unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE;
+    } else if (unicodeBidi == NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
+      unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE;
+    }
+    if (unicodeBidi != textReset->mUnicodeBidi) {
+      nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset);
+      mutableTextReset->mUnicodeBidi = unicodeBidi;
+    }
+  }
+
+  /*
+   * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow:
+   *
+   * 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;
+    while (cbContext->StyleDisplay()->mDisplay == mozilla::StyleDisplay::Contents) {
+      cbContext = cbContext->mParent;
+    }
+    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;
+      mutable_display->mOriginalDisplay = mutable_display->mDisplay =
+        mozilla::StyleDisplay::InlineBlock;
+    }
+  }
+
+  // Compute User Interface style, to trigger loads of cursors
+  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
@@ -22,16 +22,28 @@ public:
   void* operator new(size_t sz, nsPresContext* aPresContext);
 
   nsPresContext* PresContext() const {
     return mSource.AsGeckoRuleNode()->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
@@ -41,25 +53,30 @@ public:
   //  * RelevantLinkVisited() == aRelevantLinkVisited
   already_AddRefed<GeckoStyleContext>
   FindChildWithRules(const nsIAtom* aPseudoTag,
                      mozilla::NonOwningStyleContextSource aSource,
                      mozilla::NonOwningStyleContextSource aSourceIfVisited,
                      bool aRelevantLinkVisited);
 
 #ifdef DEBUG
+  void AssertChildStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
+                                          int32_t aLevels) const;
   void ListDescendants(FILE* out, int32_t aIndent);
 #endif
 
+  // Only called for Gecko-backed nsStyleContexts.
+  void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup);
+
+  bool HasNoChildren() const;
+
 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;
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -124,18 +124,17 @@ nsStyleContext::FinishConstruction()
   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()
 {
   if (const GeckoStyleContext* gecko = GetAsGecko()) {
-    NS_ASSERTION((nullptr == gecko->mChild) && (nullptr == gecko->mEmptyChild),
-                 "destructing context with children");
+    NS_ASSERTION(gecko->HasNoChildren(), "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) {
@@ -249,31 +248,17 @@ nsStyleContext::AssertStructsNotUsedElse
 
 #undef STYLE_STRUCT_INHERITED
 #undef STYLE_STRUCT_RESET
       }
     }
   }
 
   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 (gecko->mEmptyChild) {
-      const GeckoStyleContext* child = gecko->mEmptyChild;
-      do {
-        child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
-        child = child->mNextSibling;
-      } while (child != gecko->mEmptyChild);
-    }
+    gecko->AssertChildStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
   }
 }
 #endif
 
 
 void nsStyleContext::AddChild(nsStyleContext* aChild)
 {
   if (GeckoStyleContext* gecko = GetAsGecko()) {
@@ -377,107 +362,16 @@ const void* nsStyleContext::StyleData(ns
     // potentially giving mCachedInheritedData the same treatment.
     //
     // Note that there is a similar comment in the struct getters in nsStyleContext.h.
     SetStyle(aSID, const_cast<void*>(newData));
   }
   return newData;
 }
 
-// This is an evil evil function, since it forces you to alloc your own separate copy of
-// style data!  Do not use this function unless you absolutely have to!  You should avoid
-// this at all costs! -dwh
-void*
-nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
-{
-  MOZ_ASSERT(!mSource.IsServoComputedValues(),
-             "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);
-  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) {
-
-#define UNIQUE_CASE(c_)                                                       \
-  case eStyleStruct_##c_:                                                     \
-    result = new (presContext) nsStyle##c_(                                   \
-      * static_cast<const nsStyle##c_ *>(current));                           \
-    break;
-
-  UNIQUE_CASE(Font)
-  UNIQUE_CASE(Display)
-  UNIQUE_CASE(Text)
-  UNIQUE_CASE(TextReset)
-  UNIQUE_CASE(Visibility)
-
-#undef UNIQUE_CASE
-
-  default:
-    NS_ERROR("Struct type not supported.  Please find another way to do this if you can!");
-    return nullptr;
-  }
-
-  SetStyle(aSID, result);
-  mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
-
-  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)
-{
-  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_: \
-      result = new (presContext) nsStyle##c_(presContext); \
-      break;
-
-  UNIQUE_CASE(Border)
-  UNIQUE_CASE(Padding)
-
-#undef UNIQUE_CASE
-
-  default:
-    NS_ERROR("Struct type not supported.");
-    return nullptr;
-  }
-
-  // The new struct is owned by this style context, but that we don't
-  // need to clear the bit in mBits because we've asserted that at the
-  // top of this function.
-  SetStyle(aSID, result);
-  return result;
-}
-
 void
 nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
 {
   MOZ_ASSERT(!mSource.IsServoComputedValues(),
              "Servo shouldn't cache style structs in the style context!");
   // This method should only be called from nsRuleNode!  It is not a public
   // method!
 
@@ -496,384 +390,16 @@ nsStyleContext::SetStyle(nsStyleStructID
   } else {
     dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
   }
   NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
                "Going to leak style data");
   *dataSlot = aStruct;
 }
 
-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
-  if (aDisplay->IsOutOfFlowStyle()) {
-    return false;
-  }
-  // Display value of any anonymous box should not be touched. In most
-  // cases, anonymous boxes are actually not in ruby frame, but instead,
-  // some other frame with a ruby display value. Non-element pseudos
-  // which represents text frames, as well as ruby pseudos are excluded
-  // because we still want to set the flag for them.
-  if ((aContext->GetPseudoType() == CSSPseudoElementType::InheritingAnonBox ||
-       aContext->GetPseudoType() == CSSPseudoElementType::NonInheritingAnonBox) &&
-      !nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) &&
-      !RubyUtils::IsRubyPseudo(aContext->GetPseudo())) {
-    return false;
-  }
-  if (aParentContext->ShouldSuppressLineBreak()) {
-    // Line break suppressing bit is propagated to any children of
-    // line participants, which include inline, contents, and inline
-    // ruby boxes.
-    if (aParentDisplay->mDisplay == mozilla::StyleDisplay::Inline ||
-        aParentDisplay->mDisplay == mozilla::StyleDisplay::Contents ||
-        aParentDisplay->mDisplay == mozilla::StyleDisplay::Ruby ||
-        aParentDisplay->mDisplay == mozilla::StyleDisplay::RubyBaseContainer) {
-      return true;
-    }
-  }
-  // Any descendant of ruby level containers is non-breakable, but
-  // the level containers themselves are breakable. We have to check
-  // the container display type against all ruby display type here
-  // because any of the ruby boxes could be anonymous.
-  // Note that, when certain HTML tags, e.g. form controls, have ruby
-  // level container display type, they could also escape from this flag
-  // while they shouldn't. However, it is generally fine since they
-  // won't usually break the assertion that there is no line break
-  // inside ruby, because:
-  // 1. their display types, the ruby level container types, are inline-
-  //    outside, which means they won't cause any forced line break; and
-  // 2. they never start an inline span, which means their children, if
-  //    any, won't be able to break the line its ruby ancestor lays; and
-  // 3. their parent frame is always a ruby content frame (due to
-  //    anonymous ruby box generation), which makes line layout suppress
-  //    any optional line break around this frame.
-  // However, there is one special case which is BR tag, because it
-  // directly affects the line layout. This case is handled by the BR
-  // frame which checks the flag of its parent frame instead of itself.
-  if ((aParentDisplay->IsRubyDisplayType() &&
-       aDisplay->mDisplay != mozilla::StyleDisplay::RubyBaseContainer &&
-       aDisplay->mDisplay != mozilla::StyleDisplay::RubyTextContainer) ||
-      // Since ruby base and ruby text may exist themselves without any
-      // non-anonymous frame outside, we should also check them.
-      aDisplay->mDisplay == mozilla::StyleDisplay::RubyBase ||
-      aDisplay->mDisplay == mozilla::StyleDisplay::RubyText) {
-    return true;
-  }
-  return false;
-}
-
-// Flex & grid containers blockify their children.
-//  "The display value of a flex item is blockified"
-//    https://drafts.csswg.org/css-flexbox-1/#flex-items
-//  "The display value of a grid item is blockified"
-//    https://drafts.csswg.org/css-grid/#grid-items
-static bool
-ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp)
-{
-  auto displayVal = aStyleDisp->mDisplay;
-  return mozilla::StyleDisplay::Flex == displayVal ||
-    mozilla::StyleDisplay::InlineFlex == displayVal ||
-    mozilla::StyleDisplay::Grid == displayVal ||
-    mozilla::StyleDisplay::InlineGrid == displayVal;
-}
-
-void
-nsStyleContext::SetStyleBits()
-{
-  // Here we set up various style bits for both the Gecko and Servo paths.
-  // _Only_ change the bits here.  For fixups of the computed values, you can
-  // add to ApplyStyleFixups in Gecko and StyleAdjuster as part of Servo's
-  // cascade.
-
-  // See if we have any text decorations.
-  // First see if our parent has text decorations.  If our parent does, then we inherit the bit.
-  if (mParent && mParent->HasTextDecorationLines()) {
-    AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
-  } else {
-    // We might have defined a decoration.
-    if (StyleTextReset()->HasTextDecorationLines()) {
-      AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
-    }
-  }
-
-  if ((mParent && mParent->HasPseudoElementData()) || IsPseudoElement()) {
-    AddStyleBit(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA);
-  }
-
-  // Set the NS_STYLE_IN_DISPLAY_NONE_SUBTREE bit
-  const nsStyleDisplay* disp = StyleDisplay();
-  if ((mParent && mParent->IsInDisplayNoneSubtree()) ||
-      disp->mDisplay == mozilla::StyleDisplay::None) {
-    AddStyleBit(NS_STYLE_IN_DISPLAY_NONE_SUBTREE);
-  }
-
-  // Mark text combined for text-combine-upright, as needed.
-  if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
-      mParent->StyleVisibility()->mWritingMode !=
-        NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
-      mParent->StyleText()->mTextCombineUpright ==
-        NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
-    AddStyleBit(NS_STYLE_IS_TEXT_COMBINED);
-  }
-}
-
-void
-nsStyleContext::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) {
-    const nsStyleTextReset* textReset = StyleTextReset();
-    if (textReset->mInitialLetterSize != 0.0f) {
-      nsStyleContext* containerSC = mParent;
-      const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
-      while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
-        if (!containerSC->GetParent()) {
-          break;
-        }
-        containerSC = containerSC->GetParent();
-        containerDisp = containerSC->StyleDisplay();
-      }
-      nscoord containerLH =
-        ReflowInput::CalcLineHeight(nullptr, containerSC, NS_AUTOHEIGHT, 1.0f);
-      RefPtr<nsFontMetrics> containerFM =
-        nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
-      MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
-      nscoord containerCH = containerFM->CapHeight();
-      RefPtr<nsFontMetrics> firstLetterFM =
-        nsLayoutUtils::GetFontMetricsForStyleContext(this);
-      MOZ_ASSERT(firstLetterFM, "Should have fontMetrics!!");
-      nscoord firstLetterCH = firstLetterFM->CapHeight();
-      nsStyleFont* mutableStyleFont = GET_UNIQUE_STYLE_DATA(Font);
-      float invCapHeightRatio =
-        mutableStyleFont->mFont.size / NSCoordToFloat(firstLetterCH);
-      mutableStyleFont->mFont.size =
-        NSToCoordRound(((textReset->mInitialLetterSize - 1) * containerLH +
-                        containerCH) *
-                       invCapHeightRatio);
-    }
-  }
-
-  // Change writing mode of text frame for text-combine-upright. We use
-  // style structs of the parent to avoid triggering computation before
-  // we change the writing mode.
-  // It is safe to look at the parent's style because we are looking at
-  // inherited properties, and ::-moz-text never matches any rules.
-  if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
-      mParent->StyleVisibility()->mWritingMode !=
-        NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
-      mParent->StyleText()->mTextCombineUpright ==
-        NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
-    MOZ_ASSERT(!PeekStyleVisibility(), "If StyleVisibility was already "
-               "computed, some properties may have been computed "
-               "incorrectly based on the old writing mode value");
-    nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility);
-    mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
-  }
-
-  // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
-  // (PageContentFrame/CanvasFrame etc will inherit 'direction')
-  if (mPseudoTag == nsCSSAnonBoxes::viewport) {
-    nsPresContext* presContext = PresContext();
-    mozilla::dom::Element* docElement = presContext->Document()->GetRootElement();
-    if (docElement) {
-      RefPtr<nsStyleContext> rootStyle =
-        presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, nullptr);
-      auto dir = rootStyle->StyleVisibility()->mDirection;
-      if (dir != StyleVisibility()->mDirection) {
-        nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
-        uniqueVisibility->mDirection = dir;
-      }
-    }
-  }
-
-  // Correct tables.
-  const nsStyleDisplay* disp = StyleDisplay();
-  if (disp->mDisplay == mozilla::StyleDisplay::Table) {
-    // -moz-center and -moz-right are used for HTML's alignment
-    // This is covering the <div align="right"><table>...</table></div> case.
-    // In this case, we don't want to inherit the text alignment into the table.
-    const nsStyleText* text = StyleText();
-
-    if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
-        text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
-        text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
-    {
-      nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
-      uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
-    }
-  }
-
-  // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
-  // the root element.  We can't implement them in nsRuleNode because we
-  // don't want to store all display structs that aren't 'block',
-  // 'inline', or 'table' in the style context tree on the off chance
-  // that the root element has its style reresolved later.  So do them
-  // here if needed, by changing the style data, so that other code
-  // doesn't get confused by looking at the style data.
-  if (!mParent &&
-      // We don't want to blockify various anon boxes that just happen to not
-      // inherit from anything.  So restrict blockification only to actual
-      // elements, the viewport (which should be block anyway, but in SVG
-      // document's isn't because we lazy-load ua.css there), and the ::backdrop
-      // pseudo-element.  This last is explicitly allowed to have any specified
-      // display type in the spec, but computes to a blockified display type per
-      // various provisions of
-      // https://fullscreen.spec.whatwg.org/#new-stacking-layer
-      (!mPseudoTag ||
-       mPseudoTag == nsCSSAnonBoxes::viewport ||
-       mPseudoTag == nsCSSPseudoElements::backdrop)) {
-    auto displayVal = disp->mDisplay;
-    if (displayVal != mozilla::StyleDisplay::Contents) {
-      nsRuleNode::EnsureBlockDisplay(displayVal, true);
-    } else {
-      // http://dev.w3.org/csswg/css-display/#transformations
-      // "... a display-outside of 'contents' computes to block-level
-      //  on the root element."
-      displayVal = mozilla::StyleDisplay::Block;
-    }
-    if (displayVal != disp->mDisplay) {
-      nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
-      disp = mutable_display;
-
-      // If we're in this code, then mOriginalDisplay doesn't matter
-      // for purposes of the cascade (because this nsStyleDisplay
-      // isn't living in the ruletree anyway), and for determining
-      // hypothetical boxes it's better to have mOriginalDisplay
-      // matching mDisplay here.
-      mutable_display->mOriginalDisplay = mutable_display->mDisplay =
-        displayVal;
-    }
-  }
-
-  // Adjust the "display" values of flex and grid items (but not for raw text
-  // or placeholders). CSS3 Flexbox section 4 says:
-  //   # The computed 'display' of a flex item is determined
-  //   # by applying the table in CSS 2.1 Chapter 9.7.
-  // ...which converts inline-level elements to their block-level equivalents.
-  // Any block-level element directly contained by elements with ruby display
-  // values are converted to their inline-level equivalents.
-  if (!aSkipParentDisplayBasedStyleFixup && mParent) {
-    // Skip display:contents ancestors to reach the potential container.
-    // (If there are only display:contents ancestors between this node and
-    // a flex/grid container ancestor, then this node is a flex/grid item, since
-    // its parent *in the frame tree* will be the flex/grid container. So we treat
-    // it like a flex/grid item here.)
-    nsStyleContext* containerContext = mParent;
-    const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
-    while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
-      if (!containerContext->GetParent()) {
-        break;
-      }
-      containerContext = containerContext->GetParent();
-      containerDisp = containerContext->StyleDisplay();
-    }
-    if (ShouldBlockifyChildren(containerDisp) &&
-        !nsCSSAnonBoxes::IsNonElement(GetPseudo())) {
-      // NOTE: Technically, we shouldn't modify the 'display' value of
-      // positioned elements, since they aren't flex/grid items. However,
-      // we don't need to worry about checking for that, because if we're
-      // positioned, we'll have already been through a call to
-      // EnsureBlockDisplay() in nsRuleNode, so this call here won't change
-      // anything. So we're OK.
-      auto displayVal = disp->mDisplay;
-      nsRuleNode::EnsureBlockDisplay(displayVal);
-      if (displayVal != disp->mDisplay) {
-        NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
-                     "We shouldn't be changing the display value of "
-                     "positioned content (and we should have already "
-                     "converted its display value to be block-level...)");
-        nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
-        disp = mutable_display;
-        mutable_display->mDisplay = displayVal;
-      }
-    }
-  }
-
-  // Note: This must come after the blockification above, otherwise we fail
-  // the grid-item-blockifying-001.html reftest.
-  if (mParent && ::ShouldSuppressLineBreak(this, disp, mParent,
-                                           mParent->StyleDisplay())) {
-    mBits |= NS_STYLE_SUPPRESS_LINEBREAK;
-    auto displayVal = disp->mDisplay;
-    nsRuleNode::EnsureInlineDisplay(displayVal);
-    if (displayVal != disp->mDisplay) {
-      nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
-      disp = mutable_display;
-      mutable_display->mDisplay = displayVal;
-    }
-  }
-  // Suppress border/padding of ruby level containers
-  if (disp->mDisplay == mozilla::StyleDisplay::RubyBaseContainer ||
-      disp->mDisplay == mozilla::StyleDisplay::RubyTextContainer) {
-    CreateEmptyStyleData(eStyleStruct_Border);
-    CreateEmptyStyleData(eStyleStruct_Padding);
-  }
-  if (disp->IsRubyDisplayType()) {
-    // Per CSS Ruby spec section Bidi Reordering, for all ruby boxes,
-    // the 'normal' and 'embed' values of 'unicode-bidi' should compute to
-    // 'isolate', and 'bidi-override' should compute to 'isolate-override'.
-    const nsStyleTextReset* textReset = StyleTextReset();
-    uint8_t unicodeBidi = textReset->mUnicodeBidi;
-    if (unicodeBidi == NS_STYLE_UNICODE_BIDI_NORMAL ||
-        unicodeBidi == NS_STYLE_UNICODE_BIDI_EMBED) {
-      unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE;
-    } else if (unicodeBidi == NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
-      unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE;
-    }
-    if (unicodeBidi != textReset->mUnicodeBidi) {
-      nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset);
-      mutableTextReset->mUnicodeBidi = unicodeBidi;
-    }
-  }
-
-  /*
-   * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow:
-   *
-   * 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;
-    while (cbContext->StyleDisplay()->mDisplay == mozilla::StyleDisplay::Contents) {
-      cbContext = cbContext->mParent;
-    }
-    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;
-      mutable_display->mOriginalDisplay = mutable_display->mDisplay =
-        mozilla::StyleDisplay::InlineBlock;
-    }
-  }
-
-  // Compute User Interface style, to trigger loads of cursors
-  StyleUserInterface();
-#undef GET_UNIQUE_STYLE_DATA
-}
-
 template<class StyleContextLike>
 nsChangeHint
 nsStyleContext::CalcStyleDifferenceInternal(StyleContextLike* aNewContext,
                                             uint32_t* aEqualStructs,
                                             uint32_t* aSamePointerStructs)
 {
   PROFILER_LABEL("nsStyleContext", "CalcStyleDifference",
     js::ProfileEntry::Category::CSS);
@@ -1447,41 +973,16 @@ nsStyleContext::SwapStyleData(nsStyleCon
         thisData = nullptr;
       }
     } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
       std::swap(thisData, otherData);
     }
   }
 }
 
-void
-nsStyleContext::SetIneligibleForSharing()
-{
-  if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
-    return;
-  }
-  mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
-  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;
   for (nsStyleStructID i = nsStyleStructID(0);
        i < nsStyleStructID_Length;
        i = nsStyleStructID(i + 1)) {
@@ -1549,30 +1050,17 @@ 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 (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);
-    }
+    gecko->LogChildStyleContextTree(aStructs);
   }
 }
 #endif
 
 #ifdef DEBUG
 /* static */ void
 nsStyleContext::Initialize()
 {
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -442,24 +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);
 
-
-  /**
-   * 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();
-
 #ifdef DEBUG
   void List(FILE* out, int32_t aIndent, bool aListDescendants = true);
   static const char* StructName(nsStyleStructID aSID);
   static bool LookupStruct(const nsACString& aName, nsStyleStructID& aResult);
 #endif
 
 #ifdef RESTYLE_LOGGING
   nsCString GetCachedStyleDataAsString(uint32_t aStructs);
@@ -503,24 +495,18 @@ public: // temporary
 
   // 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();
 
-  // Only called for Gecko-backed nsStyleContexts.
-  void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup);
-
   const void* StyleStructFromServoComputedValues(nsStyleStructID aSID) {
     switch (aSID) {
 #define STYLE_STRUCT(name_, checkdata_cb_)                                    \
       case eStyleStruct_##name_:                                              \
         return Servo_GetStyle##name_(mSource.AsServoComputedValues());
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
       default: