Bug 1367904 - Part 6: stylo: Move most Gecko-specific methods into GeckoStyleContext; r?bholley draft
authorManish Goregaokar <manishearth@gmail.com>
Sat, 10 Jun 2017 22:27:45 -0700
changeset 593882 f1323c3f74b66267fd93dd23e5976fa1c56ff8e8
parent 593881 3e2d689a0a696b1f850bb74bd3f44b1d88ddd9d7
child 593883 5ab45d956f5eb226a676075380361a5d4eca0120
push id63848
push userbmo:manishearth@gmail.com
push dateWed, 14 Jun 2017 08:07:45 +0000
reviewersbholley
bugs1367904
milestone55.0a1
Bug 1367904 - 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
@@ -1772,17 +1772,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)
@@ -230,8 +234,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
@@ -40,21 +52,32 @@ public:
   //    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 AssertChildStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
+                                          int32_t aLevels) const;
+#endif
+
+  // Only called for Gecko-backed nsStyleContexts.
+  void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup);
+
+  void ListDescendants(FILE* out, int32_t aIndent);
+
+  bool HasNoChildren() const;
+
 private:
   // Helper for ClearCachedInheritedStyleDataOnDescendants.
   void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
-public:
-  void ListDescendants(FILE* out, int32_t aIndent);
+
   // 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);
@@ -1448,41 +974,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)) {
@@ -1550,30 +1051,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: