Bug 1290335: Hoist frame-construction logic in RestyleManager to static members in RestyleManagerBase. draft
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Mon, 01 Aug 2016 13:31:57 -0700
changeset 396030 6d7c1a0f064761a8dc1d0c600270bce4eac0f9db
parent 396029 dd88b1a74ac93bd70107b10552e8e400264e0c41
child 396031 64bbcea8a3848f8398fd3269aae79f7ac183f5de
push id24900
push userbmo:ealvarez@mozilla.com
push dateWed, 03 Aug 2016 08:06:14 +0000
bugs1290335
milestone51.0a1
Bug 1290335: Hoist frame-construction logic in RestyleManager to static members in RestyleManagerBase. MozReview-Commit-ID: BqywJXZ0CYU
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/RestyleManagerBase.cpp
layout/base/RestyleManagerBase.h
layout/base/RestyleManagerHandle.h
layout/base/RestyleManagerHandleInlines.h
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
layout/base/nsCSSFrameConstructor.h
layout/base/nsCSSRendering.cpp
layout/base/nsPresShell.cpp
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -106,934 +106,16 @@ RestyleManager::RestyleManager(nsPresCon
 }
 
 void
 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
   mOverflowChangedTracker.RemoveFrame(aFrame);
 }
 
-#ifdef DEBUG
-  // To ensure that the functions below are only called within
-  // |ApplyRenderingChangeToTree|.
-static bool gInApplyRenderingChangeToTree = false;
-#endif
-
-static inline nsIFrame*
-GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
-{
-  NS_ASSERTION(!aFrame->GetPrevContinuation(),
-               "must start with the first continuation");
-  // Might we have ib-split siblings?
-  if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-    // nothing more to do here
-    return nullptr;
-  }
-
-  return static_cast<nsIFrame*>
-    (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
-}
-
-static nsIFrame*
-GetNearestAncestorFrame(nsIContent* aContent)
-{
-  nsIFrame* ancestorFrame = nullptr;
-  for (nsIContent* ancestor = aContent->GetParent();
-       ancestor && !ancestorFrame;
-       ancestor = ancestor->GetParent()) {
-    ancestorFrame = ancestor->GetPrimaryFrame();
-  }
-  return ancestorFrame;
-}
-
-static void
-DoApplyRenderingChangeToTree(nsIFrame* aFrame,
-                             nsChangeHint aChange);
-
-/**
- * Sync views on aFrame and all of aFrame's descendants (following placeholders),
- * if aChange has nsChangeHint_SyncFrameView.
- * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
- * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
- * aFrame should be some combination of nsChangeHint_SyncFrameView,
- * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
- * nsChangeHint_SchedulePaint, nothing else.
-*/
-static void
-SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
-                                  nsChangeHint aChange)
-{
-  NS_PRECONDITION(gInApplyRenderingChangeToTree,
-                  "should only be called within ApplyRenderingChangeToTree");
-  NS_ASSERTION(nsChangeHint_size_t(aChange) ==
-                          (aChange & (nsChangeHint_RepaintFrame |
-                                      nsChangeHint_SyncFrameView |
-                                      nsChangeHint_UpdateOpacityLayer |
-                                      nsChangeHint_SchedulePaint)),
-               "Invalid change flag");
-
-  nsView* view = aFrame->GetView();
-  if (view) {
-    if (aChange & nsChangeHint_SyncFrameView) {
-      nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
-                                                aFrame, nullptr, view);
-    }
-  }
-
-  nsIFrame::ChildListIterator lists(aFrame);
-  for (; !lists.IsDone(); lists.Next()) {
-    for (nsIFrame* child : lists.CurrentList()) {
-      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
-        // only do frames that don't have placeholders
-        if (nsGkAtoms::placeholderFrame == child->GetType()) {
-          // do the out-of-flow frame and its continuations
-          nsIFrame* outOfFlowFrame =
-            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
-          DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
-        } else if (lists.CurrentID() == nsIFrame::kPopupList) {
-          DoApplyRenderingChangeToTree(child, aChange);
-        } else {  // regular frame
-          SyncViewsAndInvalidateDescendants(child, aChange);
-        }
-      }
-    }
-  }
-}
-
-/**
- * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
- * frames of the SVG frame concerned. This helper function is used to find that
- * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
- * that we iterate over the intended children, since sometimes we end up
- * handling that hint while processing hints for one of the SVG frame's
- * ancestor frames.
- *
- * The reason that we sometimes end up trying to process the hint for an
- * ancestor of the SVG frame that the hint is intended for is due to the way we
- * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
- * the restyled element's principle frame to one of its ancestor frames based
- * on what nsCSSRendering::FindBackground returns, since the background style
- * may have been propagated up to an ancestor frame. Processing hints using an
- * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
- * a special case since it is intended to update the children of a specific
- * frame.
- */
-static nsIFrame*
-GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
-{
-  if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
-    // This happens if the root-<svg> is fixed positioned, in which case we
-    // can't use aFrame->GetContent() to find the primary frame, since
-    // GetContent() returns nullptr for ViewportFrame.
-    aFrame = aFrame->PrincipalChildList().FirstChild();
-  }
-  // For an nsHTMLScrollFrame, this will get the SVG frame that has the
-  // children-only transforms:
-  aFrame = aFrame->GetContent()->GetPrimaryFrame();
-  if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
-    aFrame = aFrame->PrincipalChildList().FirstChild();
-    MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
-               "Where is the nsSVGOuterSVGFrame's anon child??");
-  }
-  MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG |
-                                   nsIFrame::eSVGContainer),
-             "Children-only transforms only expected on SVG frames");
-  return aFrame;
-}
-
-static void
-DoApplyRenderingChangeToTree(nsIFrame* aFrame,
-                             nsChangeHint aChange)
-{
-  NS_PRECONDITION(gInApplyRenderingChangeToTree,
-                  "should only be called within ApplyRenderingChangeToTree");
-
-  for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
-    // Invalidate and sync views on all descendant frames, following placeholders.
-    // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
-    // there can't be any out-of-flows or popups that need to be transformed;
-    // all out-of-flow descendants of the transformed element must also be
-    // descendants of the transformed frame.
-    SyncViewsAndInvalidateDescendants(aFrame,
-      nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
-                              nsChangeHint_SyncFrameView |
-                              nsChangeHint_UpdateOpacityLayer |
-                              nsChangeHint_SchedulePaint)));
-    // This must be set to true if the rendering change needs to
-    // invalidate content.  If it's false, a composite-only paint
-    // (empty transaction) will be scheduled.
-    bool needInvalidatingPaint = false;
-
-    // if frame has view, will already be invalidated
-    if (aChange & nsChangeHint_RepaintFrame) {
-      // Note that this whole block will be skipped when painting is suppressed
-      // (due to our caller ApplyRendingChangeToTree() discarding the
-      // nsChangeHint_RepaintFrame hint).  If you add handling for any other
-      // hints within this block, be sure that they too should be ignored when
-      // painting is suppressed.
-      needInvalidatingPaint = true;
-      aFrame->InvalidateFrameSubtree();
-      if ((aChange & nsChangeHint_UpdateEffects) &&
-          aFrame->IsFrameOfType(nsIFrame::eSVG) &&
-          !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
-        // Need to update our overflow rects:
-        nsSVGUtils::ScheduleReflowSVG(aFrame);
-      }
-    }
-    if (aChange & nsChangeHint_UpdateTextPath) {
-      if (aFrame->IsSVGText()) {
-        // Invalidate and reflow the entire SVGTextFrame:
-        NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
-                     "expected frame for a <textPath> element");
-        nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
-                                                      aFrame,
-                                                      nsGkAtoms::svgTextFrame);
-        NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
-        static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
-      } else {
-        MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
-      }
-    }
-    if (aChange & nsChangeHint_UpdateOpacityLayer) {
-      // FIXME/bug 796697: we can get away with empty transactions for
-      // opacity updates in many cases.
-      needInvalidatingPaint = true;
-
-      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
-      if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
-        // SVG effects paints the opacity without using
-        // nsDisplayOpacity. We need to invalidate manually.
-        aFrame->InvalidateFrameSubtree();
-      }
-    }
-    if ((aChange & nsChangeHint_UpdateTransformLayer) &&
-        aFrame->IsTransformed()) {
-      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
-      // If we're not already going to do an invalidating paint, see
-      // if we can get away with only updating the transform on a
-      // layer for this frame, and not scheduling an invalidating
-      // paint.
-      if (!needInvalidatingPaint) {
-        Layer* layer;
-        needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
-
-        if (!needInvalidatingPaint) {
-          // Since we're not going to paint, we need to resend animation
-          // data to the layer.
-          MOZ_ASSERT(layer, "this can't happen if there's no layer");
-          nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
-            nullptr, nullptr, aFrame, eCSSProperty_transform);
-        }
-      }
-    }
-    if (aChange & nsChangeHint_ChildrenOnlyTransform) {
-      needInvalidatingPaint = true;
-      nsIFrame* childFrame =
-        GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
-      for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
-        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
-      }
-    }
-    if (aChange & nsChangeHint_SchedulePaint) {
-      needInvalidatingPaint = true;
-    }
-    aFrame->SchedulePaint(needInvalidatingPaint ?
-                          nsIFrame::PAINT_DEFAULT :
-                          nsIFrame::PAINT_COMPOSITE_ONLY);
-  }
-}
-
-static void
-ApplyRenderingChangeToTree(nsPresContext* aPresContext,
-                           nsIFrame* aFrame,
-                           nsChangeHint aChange)
-{
-  // We check StyleDisplay()->HasTransformStyle() in addition to checking
-  // IsTransformed() since we can get here for some frames that don't support
-  // CSS transforms.
-  NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
-               aFrame->IsTransformed() ||
-               aFrame->StyleDisplay()->HasTransformStyle(),
-               "Unexpected UpdateTransformLayer hint");
-
-  nsIPresShell* shell = aPresContext->PresShell();
-  if (shell->IsPaintingSuppressed()) {
-    // Don't allow synchronous rendering changes when painting is turned off.
-    aChange &= ~nsChangeHint_RepaintFrame;
-    if (!aChange) {
-      return;
-    }
-  }
-
-  // Trigger rendering updates by damaging this frame and any
-  // continuations of this frame.
-#ifdef DEBUG
-  gInApplyRenderingChangeToTree = true;
-#endif
-  if (aChange & nsChangeHint_RepaintFrame) {
-    // If the frame's background is propagated to an ancestor, walk up to
-    // that ancestor and apply the RepaintFrame change hint to it.
-    nsStyleContext* bgSC;
-    nsIFrame* propagatedFrame = aFrame;
-    while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
-      propagatedFrame = propagatedFrame->GetParent();
-      NS_ASSERTION(aFrame, "root frame must paint");
-    }
-
-    if (propagatedFrame != aFrame) {
-      DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
-      aChange &= ~nsChangeHint_RepaintFrame;
-      if (!aChange) {
-        return;
-      }
-    }
-  }
-  DoApplyRenderingChangeToTree(aFrame, aChange);
-#ifdef DEBUG
-  gInApplyRenderingChangeToTree = false;
-#endif
-}
-
-bool
-RestyleManager::RecomputePosition(nsIFrame* aFrame)
-{
-  // Don't process position changes on table frames, since we already handle
-  // the dynamic position change on the table wrapper frame, and the
-  // reflow-based fallback code path also ignores positions on inner table
-  // frames.
-  if (aFrame->GetType() == nsGkAtoms::tableFrame) {
-    return true;
-  }
-
-  const nsStyleDisplay* display = aFrame->StyleDisplay();
-  // Changes to the offsets of a non-positioned element can safely be ignored.
-  if (display->mPosition == NS_STYLE_POSITION_STATIC) {
-    return true;
-  }
-
-  // Don't process position changes on frames which have views or the ones which
-  // have a view somewhere in their descendants, because the corresponding view
-  // needs to be repositioned properly as well.
-  if (aFrame->HasView() ||
-      (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
-    StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
-    return false;
-  }
-
-  aFrame->SchedulePaint();
-
-  // For relative positioning, we can simply update the frame rect
-  if (display->IsRelativelyPositionedStyle()) {
-    // Move the frame
-    if (display->mPosition == NS_STYLE_POSITION_STICKY) {
-      if (display->IsInnerTableStyle()) {
-        // We don't currently support sticky positioning of inner table
-        // elements (bug 975644). Bail.
-        //
-        // When this is fixed, remove the null-check for the computed
-        // offsets in nsTableRowFrame::ReflowChildren.
-        return true;
-      }
-
-      // Update sticky positioning for an entire element at once, starting with
-      // the first continuation or ib-split sibling.
-      // It's rare that the frame we already have isn't already the first
-      // continuation or ib-split sibling, but it can happen when styles differ
-      // across continuations such as ::first-line or ::first-letter, and in
-      // those cases we will generally (but maybe not always) do the work twice.
-      nsIFrame* firstContinuation =
-        nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-
-      StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
-      StickyScrollContainer* ssc =
-        StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
-      if (ssc) {
-        ssc->PositionContinuations(firstContinuation);
-      }
-    } else {
-      MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
-                 "Unexpected type of positioning");
-      for (nsIFrame* cont = aFrame; cont;
-           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-        nsIFrame* cb = cont->GetContainingBlock();
-        nsMargin newOffsets;
-        WritingMode wm = cb->GetWritingMode();
-        const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
-
-        ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
-        NS_ASSERTION(newOffsets.left == -newOffsets.right &&
-                     newOffsets.top == -newOffsets.bottom,
-                     "ComputeRelativeOffsets should return valid results");
-
-        // ReflowInput::ApplyRelativePositioning would work here, but
-        // since we've already checked mPosition and aren't changing the frame's
-        // normal position, go ahead and add the offsets directly.
-        cont->SetPosition(cont->GetNormalPosition() +
-                          nsPoint(newOffsets.left, newOffsets.top));
-      }
-    }
-
-    return true;
-  }
-
-  // For the absolute positioning case, set up a fake HTML reflow state for
-  // the frame, and then get the offsets and size from it. If the frame's size
-  // doesn't need to change, we can simply update the frame position. Otherwise
-  // we fall back to a reflow.
-  nsRenderingContext rc(
-    aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
-
-  // Construct a bogus parent reflow state so that there's a usable
-  // containing block reflow state.
-  nsIFrame* parentFrame = aFrame->GetParent();
-  WritingMode parentWM = parentFrame->GetWritingMode();
-  WritingMode frameWM = aFrame->GetWritingMode();
-  LogicalSize parentSize = parentFrame->GetLogicalSize();
-
-  nsFrameState savedState = parentFrame->GetStateBits();
-  ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame,
-                                      &rc, parentSize);
-  parentFrame->RemoveStateBits(~nsFrameState(0));
-  parentFrame->AddStateBits(savedState);
-
-  // The bogus parent state here was created with no parent state of its own,
-  // and therefore it won't have an mCBReflowInput set up.
-  // But we may need one (for InitCBReflowInput in a child state), so let's
-  // try to create one here for the cases where it will be needed.
-  Maybe<ReflowInput> cbReflowInput;
-  nsIFrame* cbFrame = parentFrame->GetContainingBlock();
-  if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
-                  parentFrame->GetType() == nsGkAtoms::tableFrame)) {
-    LogicalSize cbSize = cbFrame->GetLogicalSize();
-    cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize);
-    cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
-    cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
-    cbReflowInput->ComputedPhysicalBorderPadding() =
-      cbFrame->GetUsedBorderAndPadding();
-    parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
-  }
-
-  NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
-                   parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
-                   "parentSize should be valid");
-  parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
-  parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
-  parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
-
-  parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
-  parentReflowInput.ComputedPhysicalBorderPadding() =
-    parentFrame->GetUsedBorderAndPadding();
-  LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
-  availSize.BSize(frameWM) = NS_INTRINSICSIZE;
-
-  ViewportFrame* viewport = do_QueryFrame(parentFrame);
-  nsSize cbSize = viewport ?
-    viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
-    : aFrame->GetContainingBlock()->GetSize();
-  const nsMargin& parentBorder =
-    parentReflowInput.mStyleBorder->GetComputedBorder();
-  cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
-  LogicalSize lcbSize(frameWM, cbSize);
-  ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput,
-                                aFrame, availSize, &lcbSize);
-  nsSize computedSize(reflowInput.ComputedWidth(), reflowInput.ComputedHeight());
-  computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
-  if (computedSize.height != NS_INTRINSICSIZE) {
-    computedSize.height += reflowInput.ComputedPhysicalBorderPadding().TopBottom();
-  }
-  nsSize size = aFrame->GetSize();
-  // The RecomputePosition hint is not used if any offset changed between auto
-  // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
-  // element height will be its intrinsic height, and since 'top' and 'bottom''s
-  // auto-ness hasn't changed, the old height must also be its intrinsic
-  // height, which we can assume hasn't changed (or reflow would have
-  // been triggered).
-  if (computedSize.width == size.width &&
-      (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
-    // If we're solving for 'left' or 'top', then compute it here, in order to
-    // match the reflow code path.
-    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
-      reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
-                                          reflowInput.ComputedPhysicalOffsets().right -
-                                          reflowInput.ComputedPhysicalMargin().right -
-                                          size.width -
-                                          reflowInput.ComputedPhysicalMargin().left;
-    }
-
-    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
-      reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
-                                         reflowInput.ComputedPhysicalOffsets().bottom -
-                                         reflowInput.ComputedPhysicalMargin().bottom -
-                                         size.height -
-                                         reflowInput.ComputedPhysicalMargin().top;
-    }
-
-    // Move the frame
-    nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
-                reflowInput.ComputedPhysicalMargin().left,
-                parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
-                reflowInput.ComputedPhysicalMargin().top);
-    aFrame->SetPosition(pos);
-
-    return true;
-  }
-
-  // Fall back to a reflow
-  StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
-  return false;
-}
-
-static bool
-HasBoxAncestor(nsIFrame* aFrame)
-{
-  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
-    if (f->IsXULBoxFrame()) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void
-RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
-{
-  nsIPresShell::IntrinsicDirty dirtyType;
-  if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
-    NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
-                 "Please read the comments in nsChangeHint.h");
-    NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
-                 "ClearDescendantIntrinsics requires NeedDirtyReflow");
-    dirtyType = nsIPresShell::eStyleChange;
-  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
-             aFrame->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
-    dirtyType = nsIPresShell::eStyleChange;
-  } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
-    dirtyType = nsIPresShell::eTreeChange;
-  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
-             HasBoxAncestor(aFrame)) {
-    // The frame's computed BSize is changing, and we have a box ancestor
-    // whose cached intrinsic height may need to be updated.
-    dirtyType = nsIPresShell::eTreeChange;
-  } else {
-    dirtyType = nsIPresShell::eResize;
-  }
-
-  nsFrameState dirtyBits;
-  if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
-    dirtyBits = nsFrameState(0);
-  } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
-             dirtyType == nsIPresShell::eStyleChange) {
-    dirtyBits = NS_FRAME_IS_DIRTY;
-  } else {
-    dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
-  }
-
-  // If we're not going to clear any intrinsic sizes on the frames, and
-  // there are no dirty bits to set, then there's nothing to do.
-  if (dirtyType == nsIPresShell::eResize && !dirtyBits)
-    return;
-
-  nsIPresShell::ReflowRootHandling rootHandling;
-  if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
-    rootHandling = nsIPresShell::ePositionOrSizeChange;
-  } else {
-    rootHandling = nsIPresShell::eNoPositionOrSizeChange;
-  }
-
-  do {
-    PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
-                                                rootHandling);
-    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
-  } while (aFrame);
-}
-
-void
-RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
-{
-  if (aFrame->FrameMaintainsOverflow()) {
-    mOverflowChangedTracker.AddFrame(aFrame,
-                                     OverflowChangedTracker::CHILDREN_CHANGED);
-  }
-  nsIFrame::ChildListIterator lists(aFrame);
-  for (; !lists.IsDone(); lists.Next()) {
-    for (nsIFrame* child : lists.CurrentList()) {
-      AddSubtreeToOverflowTracker(child);
-    }
-  }
-}
-
-NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
-
-/**
- * Return true if aFrame's subtree has placeholders for out-of-flow content
- * whose 'position' style's bit in aPositionMask is set.
- */
-static bool
-FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
-{
-  const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
-                                    nsIFrame::kFixedList);
-  for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
-    if (!skip.Contains(lists.CurrentID())) {
-      for (nsIFrame* f : lists.CurrentList()) {
-        if (f->GetType() == nsGkAtoms::placeholderFrame) {
-          nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
-          // If SVG text frames could appear here, they could confuse us since
-          // they ignore their position style ... but they can't.
-          NS_ASSERTION(!outOfFlow->IsSVGText(),
-                       "SVG text frames can't be out of flow");
-          if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
-            return true;
-          }
-        }
-        if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
-          return true;
-        }
-      }
-    }
-  }
-  return false;
-}
-
-static bool
-NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
-{
-  static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
-                NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
-  static_assert(0 <= NS_STYLE_POSITION_FIXED &&
-                NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
-
-  uint32_t positionMask;
-  // Don't call aFrame->IsPositioned here, since that returns true if
-  // the frame already has a transform, and we want to ignore that here
-  if (aFrame->IsAbsolutelyPositioned() ||
-      aFrame->IsRelativelyPositioned()) {
-    // This frame is a container for abs-pos descendants whether or not it
-    // has a transform.
-    // So abs-pos descendants are no problem; we only need to reframe if
-    // we have fixed-pos descendants.
-    positionMask = 1 << NS_STYLE_POSITION_FIXED;
-  } else {
-    // This frame may not be a container for abs-pos descendants already.
-    // So reframe if we have abs-pos or fixed-pos descendants.
-    positionMask = (1 << NS_STYLE_POSITION_FIXED) |
-        (1 << NS_STYLE_POSITION_ABSOLUTE);
-  }
-  for (nsIFrame* f = aFrame; f;
-       f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
-    if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-nsresult
-RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
-{
-  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
-               "Someone forgot a script blocker");
-  int32_t count = aChangeList.Count();
-  if (!count)
-    return NS_OK;
-
-  PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
-    js::ProfileEntry::Category::CSS);
-
-  // Make sure to not rebuild quote or counter lists while we're
-  // processing restyles
-  FrameConstructor()->BeginUpdate();
-
-  FramePropertyTable* propTable = PresContext()->PropertyTable();
-
-  // Mark frames so that we skip frames that die along the way, bug 123049.
-  // A frame can be in the list multiple times with different hints. Further
-  // optmization is possible if nsStyleChangeList::AppendChange could coalesce
-  int32_t index = count;
-
-  while (0 <= --index) {
-    const nsStyleChangeData* changeData;
-    aChangeList.ChangeAt(index, &changeData);
-    if (changeData->mFrame) {
-      propTable->Set(changeData->mFrame, ChangeListProperty(), true);
-    }
-  }
-
-  index = count;
-
-  bool didUpdateCursor = false;
-
-  while (0 <= --index) {
-    nsIFrame* frame;
-    nsIContent* content;
-    bool didReflowThisFrame = false;
-    nsChangeHint hint;
-    aChangeList.ChangeAt(index, frame, content, hint);
-
-    NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
-                 (hint & nsChangeHint_NeedReflow),
-                 "Reflow hint bits set without actually asking for a reflow");
-
-    // skip any frame that has been destroyed due to a ripple effect
-    if (frame && !propTable->Get(frame, ChangeListProperty())) {
-      continue;
-    }
-
-    if (frame && frame->GetContent() != content) {
-      // XXXbz this is due to image maps messing with the primary frame of
-      // <area>s.  See bug 135040.  Remove this block once that's fixed.
-      frame = nullptr;
-      if (!(hint & nsChangeHint_ReconstructFrame)) {
-        continue;
-      }
-    }
-
-    if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
-        !(hint & nsChangeHint_ReconstructFrame)) {
-      if (NeedToReframeForAddingOrRemovingTransform(frame) ||
-          frame->GetType() == nsGkAtoms::fieldSetFrame ||
-          frame->GetContentInsertionFrame() != frame) {
-        // The frame has positioned children that need to be reparented, or
-        // it can't easily be converted to/from being an abs-pos container correctly.
-        hint |= nsChangeHint_ReconstructFrame;
-      } else {
-        for (nsIFrame* cont = frame; cont;
-             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-          // Normally frame construction would set state bits as needed,
-          // but we're not going to reconstruct the frame so we need to set them.
-          // It's because we need to set this state on each affected frame
-          // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
-          // to ancestors (i.e. it can't be an inherited change hint).
-          if (cont->IsAbsPosContaininingBlock()) {
-            if (cont->StyleDisplay()->HasTransform(cont)) {
-              cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
-            }
-            if (!cont->IsAbsoluteContainer() &&
-                (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
-              cont->MarkAsAbsoluteContainingBlock();
-            }
-          } else {
-            // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
-            // transformed by other means. It's OK to have the bit even if it's
-            // not needed.
-            if (cont->IsAbsoluteContainer()) {
-              cont->MarkAsNotAbsoluteContainingBlock();
-            }
-          }
-        }
-      }
-    }
-    if (hint & nsChangeHint_ReconstructFrame) {
-      // If we ever start passing true here, be careful of restyles
-      // that involve a reframe and animations.  In particular, if the
-      // restyle we're processing here is an animation restyle, but
-      // the style resolution we will do for the frame construction
-      // happens async when we're not in an animation restyle already,
-      // problems could arise.
-      // We could also have problems with triggering of CSS transitions
-      // on elements whose frames are reconstructed, since we depend on
-      // the reconstruction happening synchronously.
-      FrameConstructor()->RecreateFramesForContent(content, false,
-        nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
-    } else {
-      NS_ASSERTION(frame, "This shouldn't happen");
-
-      if (!frame->FrameMaintainsOverflow()) {
-        // frame does not maintain overflow rects, so avoid calling
-        // FinishAndStoreOverflow on it:
-        hint &= ~(nsChangeHint_UpdateOverflow |
-                  nsChangeHint_ChildrenOnlyTransform |
-                  nsChangeHint_UpdatePostTransformOverflow |
-                  nsChangeHint_UpdateParentOverflow);
-      }
-
-      if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
-        // Frame can not be transformed, and thus a change in transform will
-        // have no effect and we should not use the
-        // nsChangeHint_UpdatePostTransformOverflow hint.
-        hint &= ~nsChangeHint_UpdatePostTransformOverflow;
-      }
-
-      if (hint & nsChangeHint_UpdateEffects) {
-        for (nsIFrame* cont = frame; cont;
-             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-          nsSVGEffects::UpdateEffects(cont);
-        }
-      }
-      if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
-          ((hint & nsChangeHint_UpdateOpacityLayer) &&
-           frame->IsFrameOfType(nsIFrame::eSVG) &&
-           !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
-        nsSVGEffects::InvalidateRenderingObservers(frame);
-      }
-      if (hint & nsChangeHint_NeedReflow) {
-        StyleChangeReflow(frame, hint);
-        didReflowThisFrame = true;
-      }
-
-      if ((hint & nsChangeHint_UpdateUsesOpacity) &&
-          frame->IsFrameOfType(nsIFrame::eTablePart)) {
-        NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
-                     "should only return UpdateUsesOpacity hint "
-                     "when also returning UpdateOpacityLayer hint");
-        // When an internal table part (including cells) changes between
-        // having opacity 1 and non-1, it changes whether its
-        // backgrounds (and those of table parts inside of it) are
-        // painted as part of the table's nsDisplayTableBorderBackground
-        // display item, or part of its own display item.  That requires
-        // invalidation, so change UpdateOpacityLayer to RepaintFrame.
-        hint &= ~nsChangeHint_UpdateOpacityLayer;
-        hint |= nsChangeHint_RepaintFrame;
-      }
-
-      if (hint & nsChangeHint_UpdateBackgroundPosition) {
-        // For most frame types, DLBI can detect background position changes,
-        // so we only need to schedule a paint.
-        hint |= nsChangeHint_SchedulePaint;
-        if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
-            frame->IsFrameOfType(nsIFrame::eMathML)) {
-          // Table parts and MathML frames don't build display items for their
-          // backgrounds, so DLBI can't detect background-position changes for
-          // these frames. Repaint the whole frame.
-          hint |= nsChangeHint_RepaintFrame;
-        }
-      }
-
-      if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
-                  nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
-                  nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
-        ApplyRenderingChangeToTree(PresContext(), frame, hint);
-      }
-      if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
-        ActiveLayerTracker::NotifyOffsetRestyle(frame);
-        // It is possible for this to fall back to a reflow
-        if (!RecomputePosition(frame)) {
-          didReflowThisFrame = true;
-        }
-      }
-      NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
-                   (hint & nsChangeHint_UpdateOverflow),
-                   "nsChangeHint_UpdateOverflow should be passed too");
-      if (!didReflowThisFrame &&
-          (hint & (nsChangeHint_UpdateOverflow |
-                   nsChangeHint_UpdatePostTransformOverflow |
-                   nsChangeHint_UpdateParentOverflow |
-                   nsChangeHint_UpdateSubtreeOverflow))) {
-        if (hint & nsChangeHint_UpdateSubtreeOverflow) {
-          for (nsIFrame* cont = frame; cont; cont =
-                 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-            AddSubtreeToOverflowTracker(cont);
-          }
-          // The work we just did in AddSubtreeToOverflowTracker
-          // subsumes some of the other hints:
-          hint &= ~(nsChangeHint_UpdateOverflow |
-                    nsChangeHint_UpdatePostTransformOverflow);
-        }
-        if (hint & nsChangeHint_ChildrenOnlyTransform) {
-          // The overflow areas of the child frames need to be updated:
-          nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
-          nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
-          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
-                       "SVG frames should not have continuations "
-                       "or ib-split siblings");
-          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
-                       "SVG frames should not have continuations "
-                       "or ib-split siblings");
-          for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
-            MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
-                       "Not expecting non-SVG children");
-            // If |childFrame| is dirty or has dirty children, we don't bother
-            // updating overflows since that will happen when it's reflowed.
-            if (!(childFrame->GetStateBits() &
-                  (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
-              mOverflowChangedTracker.AddFrame(childFrame,
-                           OverflowChangedTracker::CHILDREN_CHANGED);
-            }
-            NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
-                         "SVG frames should not have continuations "
-                         "or ib-split siblings");
-            NS_ASSERTION(childFrame->GetParent() == hintFrame,
-                         "SVG child frame not expected to have different parent");
-          }
-        }
-        // If |frame| is dirty or has dirty children, we don't bother updating
-        // overflows since that will happen when it's reflowed.
-        if (!(frame->GetStateBits() &
-              (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
-          if (hint & (nsChangeHint_UpdateOverflow |
-                      nsChangeHint_UpdatePostTransformOverflow)) {
-            OverflowChangedTracker::ChangeKind changeKind;
-            // If we have both nsChangeHint_UpdateOverflow and
-            // nsChangeHint_UpdatePostTransformOverflow,
-            // CHILDREN_CHANGED is selected as it is
-            // strictly stronger.
-            if (hint & nsChangeHint_UpdateOverflow) {
-              changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
-            } else {
-              changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
-            }
-            for (nsIFrame* cont = frame; cont; cont =
-                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-              mOverflowChangedTracker.AddFrame(cont, changeKind);
-            }
-          }
-          // UpdateParentOverflow hints need to be processed in addition
-          // to the above, since if the processing of the above hints
-          // yields no change, the update will not propagate to the
-          // parent.
-          if (hint & nsChangeHint_UpdateParentOverflow) {
-            MOZ_ASSERT(frame->GetParent(),
-                       "shouldn't get style hints for the root frame");
-            for (nsIFrame* cont = frame; cont; cont =
-                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-              mOverflowChangedTracker.AddFrame(cont->GetParent(),
-                                   OverflowChangedTracker::CHILDREN_CHANGED);
-            }
-          }
-        }
-      }
-      if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
-        PresContext()->PresShell()->SynthesizeMouseMove(false);
-        didUpdateCursor = true;
-      }
-    }
-  }
-
-  FrameConstructor()->EndUpdate();
-
-  // cleanup references and verify the style tree.  Note that the latter needs
-  // to happen once we've processed the whole list, since until then the tree
-  // is not in fact in a consistent state.
-  index = count;
-  while (0 <= --index) {
-    const nsStyleChangeData* changeData;
-    aChangeList.ChangeAt(index, &changeData);
-    if (changeData->mFrame) {
-      propTable->Delete(changeData->mFrame, ChangeListProperty());
-    }
-
-#ifdef DEBUG
-    // reget frame from content since it may have been regenerated...
-    if (changeData->mContent) {
-      nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
-      if (frame) {
-        DebugVerifyStyleTree(frame);
-      }
-    } else if (!changeData->mFrame ||
-               changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
-      NS_WARNING("Unable to test style tree integrity -- no content node "
-                 "(and not a viewport frame)");
-    }
-#endif
-  }
-
-  aChangeList.Clear();
-  return NS_OK;
-}
-
 void
 RestyleManager::RestyleElement(Element*               aElement,
                                nsIFrame*              aPrimaryFrame,
                                nsChangeHint           aMinHint,
                                RestyleTracker&        aRestyleTracker,
                                nsRestyleHint          aRestyleHint,
                                const RestyleHintData& aRestyleHintData)
 {
@@ -1907,176 +989,16 @@ RestyleManager::PostRebuildAllStyleDataE
   mDoRebuildAllStyleData = true;
   mRebuildAllExtraHint |= aExtraHint;
   mRebuildAllRestyleHint |= aRestyleHint;
 
   // Get a restyle event posted if necessary
   PostRestyleEventInternal(false);
 }
 
-#ifdef DEBUG
-static void
-DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
-{
-  if (aFrame) {
-    fputs("frame: ", stdout);
-    nsAutoString  name;
-    aFrame->GetFrameName(name);
-    fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
-    fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
-  }
-  if (aContext) {
-    fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
-
-    nsIAtom* pseudoTag = aContext->GetPseudo();
-    if (pseudoTag) {
-      nsAutoString  buffer;
-      pseudoTag->ToString(buffer);
-      fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
-      fputs(" ", stdout);
-    }
-    fputs("{}\n", stdout);
-  }
-}
-
-static void
-VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
-{
-  nsStyleContext* top1 = aContext1;
-  nsStyleContext* top2 = aContext2;
-  nsStyleContext* parent;
-  for (;;) {
-    parent = top1->GetParent();
-    if (!parent)
-      break;
-    top1 = parent;
-  }
-  for (;;) {
-    parent = top2->GetParent();
-    if (!parent)
-      break;
-    top2 = parent;
-  }
-  NS_ASSERTION(top1 == top2,
-               "Style contexts are not in the same style context tree");
-}
-
-static void
-VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
-                    nsStyleContext* aParentContext)
-{
-  // get the contexts not provided
-  if (!aContext) {
-    aContext = aFrame->StyleContext();
-  }
-
-  if (!aParentContext) {
-    nsIFrame* providerFrame;
-    aParentContext = aFrame->GetParentStyleContext(&providerFrame);
-    // aParentContext could still be null
-  }
-
-  NS_ASSERTION(aContext, "Failure to get required contexts");
-  nsStyleContext* actualParentContext = aContext->GetParent();
-
-  if (aParentContext) {
-    if (aParentContext != actualParentContext) {
-      DumpContext(aFrame, aContext);
-      if (aContext == aParentContext) {
-        NS_ERROR("Using parent's style context");
-      }
-      else {
-        NS_ERROR("Wrong parent style context");
-        fputs("Wrong parent style context: ", stdout);
-        DumpContext(nullptr, actualParentContext);
-        fputs("should be using: ", stdout);
-        DumpContext(nullptr, aParentContext);
-        VerifySameTree(actualParentContext, aParentContext);
-        fputs("\n", stdout);
-      }
-    }
-
-  }
-  else {
-    if (actualParentContext) {
-      NS_ERROR("Have parent context and shouldn't");
-      DumpContext(aFrame, aContext);
-      fputs("Has parent context: ", stdout);
-      DumpContext(nullptr, actualParentContext);
-      fputs("Should be null\n\n", stdout);
-    }
-  }
-
-  nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
-  // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
-  // as the parent or it has a different rulenode from aContext _and_ has
-  // aContext->GetParent() as the parent.
-  if (childStyleIfVisited &&
-      !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
-         childStyleIfVisited->GetParent() == aContext->GetParent()) ||
-        childStyleIfVisited->GetParent() ==
-          aContext->GetParent()->GetStyleIfVisited())) {
-    NS_ERROR("Visited style has wrong parent");
-    DumpContext(aFrame, aContext);
-    fputs("\n", stdout);
-  }
-}
-
-static void
-VerifyStyleTree(nsIFrame* aFrame)
-{
-  nsStyleContext*  context = aFrame->StyleContext();
-  VerifyContextParent(aFrame, context, nullptr);
-
-  nsIFrame::ChildListIterator lists(aFrame);
-  for (; !lists.IsDone(); lists.Next()) {
-    for (nsIFrame* child : lists.CurrentList()) {
-      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
-        // only do frames that are in flow
-        if (nsGkAtoms::placeholderFrame == child->GetType()) {
-          // placeholder: first recurse and verify the out of flow frame,
-          // then verify the placeholder's context
-          nsIFrame* outOfFlowFrame =
-            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
-
-          // recurse to out of flow frame, letting the parent context get resolved
-          do {
-            VerifyStyleTree(outOfFlowFrame);
-          } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
-
-          // verify placeholder using the parent frame's context as
-          // parent context
-          VerifyContextParent(child, nullptr, nullptr);
-        }
-        else { // regular frame
-          VerifyStyleTree(child);
-        }
-      }
-    }
-  }
-
-  // do additional contexts
-  int32_t contextIndex = 0;
-  for (nsStyleContext* extraContext;
-       (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
-       ++contextIndex) {
-    VerifyContextParent(aFrame, extraContext, context);
-  }
-}
-
-void
-RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
-{
-  if (aFrame) {
-    VerifyStyleTree(aFrame);
-  }
-}
-
-#endif // DEBUG
-
 // aContent must be the content for the frame in question, which may be
 // :before/:after content
 /* static */ bool
 RestyleManager::TryStartingTransition(nsPresContext* aPresContext,
                                       nsIContent* aContent,
                                       nsStyleContext* aOldStyleContext,
                                       RefPtr<nsStyleContext>*
                                         aNewStyleContext /* inout */)
@@ -2484,17 +1406,17 @@ RestyleManager::ReparentStyleContext(nsI
             NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
                          "Our frame tree is likely to be bogus!");
           }
 
           aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
         }
       }
 #ifdef DEBUG
-      VerifyStyleTree(aFrame);
+      DebugVerifyStyleTree(aFrame);
 #endif
     }
   }
 
   return NS_OK;
 }
 
 ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
@@ -4493,17 +3415,17 @@ ElementRestyler::ComputeStyleChangeFor(n
     content ? content->GetParentElementCrossingShadowRoot() : nullptr;
   treeMatchContext.InitAncestors(parent);
   nsTArray<nsCSSSelector*> selectorsForDescendants;
   selectorsForDescendants.AppendElements(
       aRestyleHintData.mSelectorsForDescendants);
   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
   nsIFrame* nextIBSibling;
   for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
-    nextIBSibling = GetNextBlockInInlineSibling(propTable, ibSibling);
+    nextIBSibling = RestyleManager::GetNextBlockInInlineSibling(propTable, ibSibling);
 
     if (nextIBSibling) {
       // Don't allow some ib-split siblings to be processed with
       // RestyleResult::eStopWithStyleChange and others not.
       aRestyleHint |= eRestyle_Force;
     }
 
     // Outer loop over ib-split siblings
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -32,17 +32,20 @@ namespace mozilla {
 
 namespace dom {
   class Element;
 } // namespace dom
 
 class RestyleManager final : public RestyleManagerBase
 {
 public:
+  typedef RestyleManagerBase base_type;
+
   friend class RestyleTracker;
+  friend class ElementRestyler;
 
   explicit RestyleManager(nsPresContext* aPresContext);
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~RestyleManager()
   {
     MOZ_ASSERT(!mReframingStyleContexts,
@@ -132,29 +135,26 @@ private:
                                     Element*               aElement,
                                     nsChangeHint           aMinChange,
                                     RestyleTracker&        aRestyleTracker,
                                     nsRestyleHint          aRestyleHint,
                                     const RestyleHintData& aRestyleHintData);
 
 public:
 
-#ifdef DEBUG
-  /**
-   * DEBUG ONLY method to verify integrity of style tree versus frame tree
-   */
-  void DebugVerifyStyleTree(nsIFrame* aFrame);
-#endif
-
   // Note: It's the caller's responsibility to make sure to wrap a
   // ProcessRestyledFrames call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
-  nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
+  nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList) {
+    return base_type::ProcessRestyledFrames(aChangeList,
+                                            *PresContext(),
+                                            mOverflowChangedTracker);
+  }
 
   /**
    * In order to start CSS transitions on elements that are being
    * reframed, we need to stash their style contexts somewhere during
    * the reframing process.
    *
    * In all cases, the content node in the hash table is the real
    * content node, not the anonymous content node we create for ::before
@@ -488,21 +488,16 @@ private:
   void StartRebuildAllStyleData(RestyleTracker& aRestyleTracker);
   void FinishRebuildAllStyleData();
 
   void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
 
   // Recursively add all the given frame and all children to the tracker.
   void AddSubtreeToOverflowTracker(nsIFrame* aFrame);
 
-  // Returns true if this function managed to successfully move a frame, and
-  // false if it could not process the position change, and a reflow should
-  // be performed instead.
-  bool RecomputePosition(nsIFrame* aFrame);
-
   bool ShouldStartRebuildAllFor(RestyleTracker& aRestyleTracker) {
     // When we process our primary restyle tracker and we have a pending
     // rebuild-all, we need to process it.
     return mDoRebuildAllStyleData &&
            &aRestyleTracker == &mPendingRestyles;
   }
 
   void ProcessRestyles(RestyleTracker& aRestyleTracker) {
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -129,10 +129,1095 @@ RestyleManagerBase::RestyleHintToString(
   } else {
     if (!any) {
       result.AppendLiteral("0");
     }
   }
   return result;
 }
 
+/**
+ * Frame construction helpers follow.
+ */
+#ifdef DEBUG
+static bool gInApplyRenderingChangeToTree = false;
+#endif
+
+#ifdef DEBUG
+static void
+DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
+{
+  if (aFrame) {
+    fputs("frame: ", stdout);
+    nsAutoString  name;
+    aFrame->GetFrameName(name);
+    fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
+    fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
+  }
+  if (aContext) {
+    fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
+
+    nsIAtom* pseudoTag = aContext->GetPseudo();
+    if (pseudoTag) {
+      nsAutoString  buffer;
+      pseudoTag->ToString(buffer);
+      fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
+      fputs(" ", stdout);
+    }
+    fputs("{}\n", stdout);
+  }
+}
+
+static void
+VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
+{
+  nsStyleContext* top1 = aContext1;
+  nsStyleContext* top2 = aContext2;
+  nsStyleContext* parent;
+  for (;;) {
+    parent = top1->GetParent();
+    if (!parent)
+      break;
+    top1 = parent;
+  }
+  for (;;) {
+    parent = top2->GetParent();
+    if (!parent)
+      break;
+    top2 = parent;
+  }
+  NS_ASSERTION(top1 == top2,
+               "Style contexts are not in the same style context tree");
+}
+
+static void
+VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
+                    nsStyleContext* aParentContext)
+{
+  // get the contexts not provided
+  if (!aContext) {
+    aContext = aFrame->StyleContext();
+  }
+
+  if (!aParentContext) {
+    nsIFrame* providerFrame;
+    aParentContext = aFrame->GetParentStyleContext(&providerFrame);
+    // aParentContext could still be null
+  }
+
+  NS_ASSERTION(aContext, "Failure to get required contexts");
+  nsStyleContext* actualParentContext = aContext->GetParent();
+
+  if (aParentContext) {
+    if (aParentContext != actualParentContext) {
+      DumpContext(aFrame, aContext);
+      if (aContext == aParentContext) {
+        NS_ERROR("Using parent's style context");
+      }
+      else {
+        NS_ERROR("Wrong parent style context");
+        fputs("Wrong parent style context: ", stdout);
+        DumpContext(nullptr, actualParentContext);
+        fputs("should be using: ", stdout);
+        DumpContext(nullptr, aParentContext);
+        VerifySameTree(actualParentContext, aParentContext);
+        fputs("\n", stdout);
+      }
+    }
+
+  }
+  else {
+    if (actualParentContext) {
+      NS_ERROR("Have parent context and shouldn't");
+      DumpContext(aFrame, aContext);
+      fputs("Has parent context: ", stdout);
+      DumpContext(nullptr, actualParentContext);
+      fputs("Should be null\n\n", stdout);
+    }
+  }
+
+  nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
+  // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
+  // as the parent or it has a different rulenode from aContext _and_ has
+  // aContext->GetParent() as the parent.
+  if (childStyleIfVisited &&
+      !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
+         childStyleIfVisited->GetParent() == aContext->GetParent()) ||
+        childStyleIfVisited->GetParent() ==
+          aContext->GetParent()->GetStyleIfVisited())) {
+    NS_ERROR("Visited style has wrong parent");
+    DumpContext(aFrame, aContext);
+    fputs("\n", stdout);
+  }
+}
+
+static void
+VerifyStyleTree(nsIFrame* aFrame)
+{
+  nsStyleContext*  context = aFrame->StyleContext();
+  VerifyContextParent(aFrame, context, nullptr);
+
+  nsIFrame::ChildListIterator lists(aFrame);
+  for (; !lists.IsDone(); lists.Next()) {
+    for (nsIFrame* child : lists.CurrentList()) {
+      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+        // only do frames that are in flow
+        if (nsGkAtoms::placeholderFrame == child->GetType()) {
+          // placeholder: first recurse and verify the out of flow frame,
+          // then verify the placeholder's context
+          nsIFrame* outOfFlowFrame =
+            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
+
+          // recurse to out of flow frame, letting the parent context get resolved
+          do {
+            VerifyStyleTree(outOfFlowFrame);
+          } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
+
+          // verify placeholder using the parent frame's context as
+          // parent context
+          VerifyContextParent(child, nullptr, nullptr);
+        } else { // regular frame
+          VerifyStyleTree(child);
+        }
+      }
+    }
+  }
+
+  // do additional contexts
+  int32_t contextIndex = 0;
+  for (nsStyleContext* extraContext;
+       (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
+       ++contextIndex) {
+    VerifyContextParent(aFrame, extraContext, context);
+  }
+}
+
+void
+RestyleManagerBase::DebugVerifyStyleTree(nsIFrame* aFrame)
+{
+  if (aFrame) {
+    VerifyStyleTree(aFrame);
+  }
+}
+
+#endif // DEBUG
+
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
+
+/**
+ * Sync views on aFrame and all of aFrame's descendants (following placeholders),
+ * if aChange has nsChangeHint_SyncFrameView.
+ * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
+ * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
+ * aFrame should be some combination of nsChangeHint_SyncFrameView,
+ * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
+ * nsChangeHint_SchedulePaint, nothing else.
+*/
+static void
+SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange);
+
+static void
+StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
+
+/**
+ * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
+ * frames of the SVG frame concerned. This helper function is used to find that
+ * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
+ * that we iterate over the intended children, since sometimes we end up
+ * handling that hint while processing hints for one of the SVG frame's
+ * ancestor frames.
+ *
+ * The reason that we sometimes end up trying to process the hint for an
+ * ancestor of the SVG frame that the hint is intended for is due to the way we
+ * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
+ * the restyled element's principle frame to one of its ancestor frames based
+ * on what nsCSSRendering::FindBackground returns, since the background style
+ * may have been propagated up to an ancestor frame. Processing hints using an
+ * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
+ * a special case since it is intended to update the children of a specific
+ * frame.
+ */
+static nsIFrame*
+GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
+{
+  if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
+    // This happens if the root-<svg> is fixed positioned, in which case we
+    // can't use aFrame->GetContent() to find the primary frame, since
+    // GetContent() returns nullptr for ViewportFrame.
+    aFrame = aFrame->PrincipalChildList().FirstChild();
+  }
+  // For an nsHTMLScrollFrame, this will get the SVG frame that has the
+  // children-only transforms:
+  aFrame = aFrame->GetContent()->GetPrimaryFrame();
+  if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
+    aFrame = aFrame->PrincipalChildList().FirstChild();
+    MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
+               "Where is the nsSVGOuterSVGFrame's anon child??");
+  }
+  MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG |
+                                   nsIFrame::eSVGContainer),
+             "Children-only transforms only expected on SVG frames");
+  return aFrame;
+}
+
+
+// Returns true if this function managed to successfully move a frame, and
+// false if it could not process the position change, and a reflow should
+// be performed instead.
+bool
+RecomputePosition(nsIFrame* aFrame)
+{
+  // Don't process position changes on table frames, since we already handle
+  // the dynamic position change on the table wrapper frame, and the
+  // reflow-based fallback code path also ignores positions on inner table
+  // frames.
+  if (aFrame->GetType() == nsGkAtoms::tableFrame) {
+    return true;
+  }
+
+  const nsStyleDisplay* display = aFrame->StyleDisplay();
+  // Changes to the offsets of a non-positioned element can safely be ignored.
+  if (display->mPosition == NS_STYLE_POSITION_STATIC) {
+    return true;
+  }
+
+  // Don't process position changes on frames which have views or the ones which
+  // have a view somewhere in their descendants, because the corresponding view
+  // needs to be repositioned properly as well.
+  if (aFrame->HasView() ||
+      (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+    StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
+    return false;
+  }
+
+  aFrame->SchedulePaint();
+
+  // For relative positioning, we can simply update the frame rect
+  if (display->IsRelativelyPositionedStyle()) {
+    // Move the frame
+    if (display->mPosition == NS_STYLE_POSITION_STICKY) {
+      if (display->IsInnerTableStyle()) {
+        // We don't currently support sticky positioning of inner table
+        // elements (bug 975644). Bail.
+        //
+        // When this is fixed, remove the null-check for the computed
+        // offsets in nsTableRowFrame::ReflowChildren.
+        return true;
+      }
+
+      // Update sticky positioning for an entire element at once, starting with
+      // the first continuation or ib-split sibling.
+      // It's rare that the frame we already have isn't already the first
+      // continuation or ib-split sibling, but it can happen when styles differ
+      // across continuations such as ::first-line or ::first-letter, and in
+      // those cases we will generally (but maybe not always) do the work twice.
+      nsIFrame* firstContinuation =
+        nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
+
+      StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
+      StickyScrollContainer* ssc =
+        StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
+      if (ssc) {
+        ssc->PositionContinuations(firstContinuation);
+      }
+    } else {
+      MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
+                 "Unexpected type of positioning");
+      for (nsIFrame* cont = aFrame; cont;
+           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+        nsIFrame* cb = cont->GetContainingBlock();
+        nsMargin newOffsets;
+        WritingMode wm = cb->GetWritingMode();
+        const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
+
+        ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
+        NS_ASSERTION(newOffsets.left == -newOffsets.right &&
+                     newOffsets.top == -newOffsets.bottom,
+                     "ComputeRelativeOffsets should return valid results");
+
+        // ReflowInput::ApplyRelativePositioning would work here, but
+        // since we've already checked mPosition and aren't changing the frame's
+        // normal position, go ahead and add the offsets directly.
+        cont->SetPosition(cont->GetNormalPosition() +
+                          nsPoint(newOffsets.left, newOffsets.top));
+      }
+    }
+
+    return true;
+  }
+
+  // For the absolute positioning case, set up a fake HTML reflow state for
+  // the frame, and then get the offsets and size from it. If the frame's size
+  // doesn't need to change, we can simply update the frame position. Otherwise
+  // we fall back to a reflow.
+  nsRenderingContext rc(
+    aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
+
+  // Construct a bogus parent reflow state so that there's a usable
+  // containing block reflow state.
+  nsIFrame* parentFrame = aFrame->GetParent();
+  WritingMode parentWM = parentFrame->GetWritingMode();
+  WritingMode frameWM = aFrame->GetWritingMode();
+  LogicalSize parentSize = parentFrame->GetLogicalSize();
+
+  nsFrameState savedState = parentFrame->GetStateBits();
+  ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame,
+                                      &rc, parentSize);
+  parentFrame->RemoveStateBits(~nsFrameState(0));
+  parentFrame->AddStateBits(savedState);
+
+  // The bogus parent state here was created with no parent state of its own,
+  // and therefore it won't have an mCBReflowInput set up.
+  // But we may need one (for InitCBReflowInput in a child state), so let's
+  // try to create one here for the cases where it will be needed.
+  Maybe<ReflowInput> cbReflowInput;
+  nsIFrame* cbFrame = parentFrame->GetContainingBlock();
+  if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
+                  parentFrame->GetType() == nsGkAtoms::tableFrame)) {
+    LogicalSize cbSize = cbFrame->GetLogicalSize();
+    cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize);
+    cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
+    cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
+    cbReflowInput->ComputedPhysicalBorderPadding() =
+      cbFrame->GetUsedBorderAndPadding();
+    parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
+  }
+
+  NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
+                   parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
+                   "parentSize should be valid");
+  parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
+  parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
+  parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
+
+  parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
+  parentReflowInput.ComputedPhysicalBorderPadding() =
+    parentFrame->GetUsedBorderAndPadding();
+  LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
+  availSize.BSize(frameWM) = NS_INTRINSICSIZE;
+
+  ViewportFrame* viewport = do_QueryFrame(parentFrame);
+  nsSize cbSize = viewport ?
+    viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
+    : aFrame->GetContainingBlock()->GetSize();
+  const nsMargin& parentBorder =
+    parentReflowInput.mStyleBorder->GetComputedBorder();
+  cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
+  LogicalSize lcbSize(frameWM, cbSize);
+  ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput,
+                                aFrame, availSize, &lcbSize);
+  nsSize computedSize(reflowInput.ComputedWidth(), reflowInput.ComputedHeight());
+  computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
+  if (computedSize.height != NS_INTRINSICSIZE) {
+    computedSize.height += reflowInput.ComputedPhysicalBorderPadding().TopBottom();
+  }
+  nsSize size = aFrame->GetSize();
+  // The RecomputePosition hint is not used if any offset changed between auto
+  // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
+  // element height will be its intrinsic height, and since 'top' and 'bottom''s
+  // auto-ness hasn't changed, the old height must also be its intrinsic
+  // height, which we can assume hasn't changed (or reflow would have
+  // been triggered).
+  if (computedSize.width == size.width &&
+      (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
+    // If we're solving for 'left' or 'top', then compute it here, in order to
+    // match the reflow code path.
+    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
+      reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
+                                          reflowInput.ComputedPhysicalOffsets().right -
+                                          reflowInput.ComputedPhysicalMargin().right -
+                                          size.width -
+                                          reflowInput.ComputedPhysicalMargin().left;
+    }
+
+    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
+      reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
+                                         reflowInput.ComputedPhysicalOffsets().bottom -
+                                         reflowInput.ComputedPhysicalMargin().bottom -
+                                         size.height -
+                                         reflowInput.ComputedPhysicalMargin().top;
+    }
+
+    // Move the frame
+    nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
+                reflowInput.ComputedPhysicalMargin().left,
+                parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
+                reflowInput.ComputedPhysicalMargin().top);
+    aFrame->SetPosition(pos);
+
+    return true;
+  }
+
+  // Fall back to a reflow
+  StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
+  return false;
+}
+
+static bool
+HasBoxAncestor(nsIFrame* aFrame)
+{
+  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
+    if (f->IsXULBoxFrame()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Return true if aFrame's subtree has placeholders for out-of-flow content
+ * whose 'position' style's bit in aPositionMask is set.
+ */
+static bool
+FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
+{
+  const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
+                                    nsIFrame::kFixedList);
+  for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
+    if (!skip.Contains(lists.CurrentID())) {
+      for (nsIFrame* f : lists.CurrentList()) {
+        if (f->GetType() == nsGkAtoms::placeholderFrame) {
+          nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
+          // If SVG text frames could appear here, they could confuse us since
+          // they ignore their position style ... but they can't.
+          NS_ASSERTION(!outOfFlow->IsSVGText(),
+                       "SVG text frames can't be out of flow");
+          if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
+            return true;
+          }
+        }
+        if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+static bool
+NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
+{
+  static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
+                NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
+  static_assert(0 <= NS_STYLE_POSITION_FIXED &&
+                NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
+
+  uint32_t positionMask;
+  // Don't call aFrame->IsPositioned here, since that returns true if
+  // the frame already has a transform, and we want to ignore that here
+  if (aFrame->IsAbsolutelyPositioned() ||
+      aFrame->IsRelativelyPositioned()) {
+    // This frame is a container for abs-pos descendants whether or not it
+    // has a transform.
+    // So abs-pos descendants are no problem; we only need to reframe if
+    // we have fixed-pos descendants.
+    positionMask = 1 << NS_STYLE_POSITION_FIXED;
+  } else {
+    // This frame may not be a container for abs-pos descendants already.
+    // So reframe if we have abs-pos or fixed-pos descendants.
+    positionMask = (1 << NS_STYLE_POSITION_FIXED) |
+        (1 << NS_STYLE_POSITION_ABSOLUTE);
+  }
+  for (nsIFrame* f = aFrame; f;
+       f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
+    if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/* static */ nsIFrame*
+RestyleManagerBase::GetNearestAncestorFrame(nsIContent* aContent)
+{
+  nsIFrame* ancestorFrame = nullptr;
+  for (nsIContent* ancestor = aContent->GetParent();
+       ancestor && !ancestorFrame;
+       ancestor = ancestor->GetParent()) {
+    ancestorFrame = ancestor->GetPrimaryFrame();
+  }
+  return ancestorFrame;
+}
+
+/* static */ nsIFrame*
+RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
+{
+  NS_ASSERTION(!aFrame->GetPrevContinuation(),
+               "must start with the first continuation");
+  // Might we have ib-split siblings?
+  if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
+    // nothing more to do here
+    return nullptr;
+  }
+
+  return static_cast<nsIFrame*>
+    (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
+}
+
+static void
+DoApplyRenderingChangeToTree(nsIFrame* aFrame,
+                             nsChangeHint aChange)
+{
+  NS_PRECONDITION(gInApplyRenderingChangeToTree,
+                  "should only be called within ApplyRenderingChangeToTree");
+
+  for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
+    // Invalidate and sync views on all descendant frames, following placeholders.
+    // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
+    // there can't be any out-of-flows or popups that need to be transformed;
+    // all out-of-flow descendants of the transformed element must also be
+    // descendants of the transformed frame.
+    SyncViewsAndInvalidateDescendants(aFrame,
+      nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
+                              nsChangeHint_SyncFrameView |
+                              nsChangeHint_UpdateOpacityLayer |
+                              nsChangeHint_SchedulePaint)));
+    // This must be set to true if the rendering change needs to
+    // invalidate content.  If it's false, a composite-only paint
+    // (empty transaction) will be scheduled.
+    bool needInvalidatingPaint = false;
+
+    // if frame has view, will already be invalidated
+    if (aChange & nsChangeHint_RepaintFrame) {
+      // Note that this whole block will be skipped when painting is suppressed
+      // (due to our caller ApplyRendingChangeToTree() discarding the
+      // nsChangeHint_RepaintFrame hint).  If you add handling for any other
+      // hints within this block, be sure that they too should be ignored when
+      // painting is suppressed.
+      needInvalidatingPaint = true;
+      aFrame->InvalidateFrameSubtree();
+      if ((aChange & nsChangeHint_UpdateEffects) &&
+          aFrame->IsFrameOfType(nsIFrame::eSVG) &&
+          !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
+        // Need to update our overflow rects:
+        nsSVGUtils::ScheduleReflowSVG(aFrame);
+      }
+    }
+    if (aChange & nsChangeHint_UpdateTextPath) {
+      if (aFrame->IsSVGText()) {
+        // Invalidate and reflow the entire SVGTextFrame:
+        NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
+                     "expected frame for a <textPath> element");
+        nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
+                                                      aFrame,
+                                                      nsGkAtoms::svgTextFrame);
+        NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
+        static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
+      } else {
+        MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
+      }
+    }
+    if (aChange & nsChangeHint_UpdateOpacityLayer) {
+      // FIXME/bug 796697: we can get away with empty transactions for
+      // opacity updates in many cases.
+      needInvalidatingPaint = true;
+
+      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
+      if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
+        // SVG effects paints the opacity without using
+        // nsDisplayOpacity. We need to invalidate manually.
+        aFrame->InvalidateFrameSubtree();
+      }
+    }
+    if ((aChange & nsChangeHint_UpdateTransformLayer) &&
+        aFrame->IsTransformed()) {
+      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
+      // If we're not already going to do an invalidating paint, see
+      // if we can get away with only updating the transform on a
+      // layer for this frame, and not scheduling an invalidating
+      // paint.
+      if (!needInvalidatingPaint) {
+        Layer* layer;
+        needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
+
+        if (!needInvalidatingPaint) {
+          // Since we're not going to paint, we need to resend animation
+          // data to the layer.
+          MOZ_ASSERT(layer, "this can't happen if there's no layer");
+          nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
+            nullptr, nullptr, aFrame, eCSSProperty_transform);
+        }
+      }
+    }
+    if (aChange & nsChangeHint_ChildrenOnlyTransform) {
+      needInvalidatingPaint = true;
+      nsIFrame* childFrame =
+        GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
+      for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
+      }
+    }
+    if (aChange & nsChangeHint_SchedulePaint) {
+      needInvalidatingPaint = true;
+    }
+    aFrame->SchedulePaint(needInvalidatingPaint ?
+                          nsIFrame::PAINT_DEFAULT :
+                          nsIFrame::PAINT_COMPOSITE_ONLY);
+  }
+}
+
+/* static */ void
+SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
+                                  nsChangeHint aChange)
+{
+  NS_ASSERTION(nsChangeHint_size_t(aChange) ==
+                          (aChange & (nsChangeHint_RepaintFrame |
+                                      nsChangeHint_SyncFrameView |
+                                      nsChangeHint_UpdateOpacityLayer |
+                                      nsChangeHint_SchedulePaint)),
+               "Invalid change flag");
+
+  nsView* view = aFrame->GetView();
+  if (view) {
+    if (aChange & nsChangeHint_SyncFrameView) {
+      nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
+                                                aFrame, nullptr, view);
+    }
+  }
+
+  nsIFrame::ChildListIterator lists(aFrame);
+  for (; !lists.IsDone(); lists.Next()) {
+    for (nsIFrame* child : lists.CurrentList()) {
+      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+        // only do frames that don't have placeholders
+        if (nsGkAtoms::placeholderFrame == child->GetType()) {
+          // do the out-of-flow frame and its continuations
+          nsIFrame* outOfFlowFrame =
+            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
+          DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
+        } else if (lists.CurrentID() == nsIFrame::kPopupList) {
+          DoApplyRenderingChangeToTree(child, aChange);
+        } else {  // regular frame
+          SyncViewsAndInvalidateDescendants(child, aChange);
+        }
+      }
+    }
+  }
+}
+
+void
+ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
+                           nsIFrame* aFrame,
+                           nsChangeHint aChange)
+{
+  // We check StyleDisplay()->HasTransformStyle() in addition to checking
+  // IsTransformed() since we can get here for some frames that don't support
+  // CSS transforms.
+  NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
+               aFrame->IsTransformed() ||
+               aFrame->StyleDisplay()->HasTransformStyle(),
+               "Unexpected UpdateTransformLayer hint");
+
+  if (aPresShell->IsPaintingSuppressed()) {
+    // Don't allow synchronous rendering changes when painting is turned off.
+    aChange &= ~nsChangeHint_RepaintFrame;
+    if (!aChange) {
+      return;
+    }
+  }
+
+  // Trigger rendering updates by damaging this frame and any
+  // continuations of this frame.
+#ifdef DEBUG
+  gInApplyRenderingChangeToTree = true;
+#endif
+  if (aChange & nsChangeHint_RepaintFrame) {
+    // If the frame's background is propagated to an ancestor, walk up to
+    // that ancestor and apply the RepaintFrame change hint to it.
+    nsStyleContext* bgSC;
+    nsIFrame* propagatedFrame = aFrame;
+    while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
+      propagatedFrame = propagatedFrame->GetParent();
+      NS_ASSERTION(aFrame, "root frame must paint");
+    }
+
+    if (propagatedFrame != aFrame) {
+      DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
+      aChange &= ~nsChangeHint_RepaintFrame;
+      if (!aChange) {
+        return;
+      }
+    }
+  }
+  DoApplyRenderingChangeToTree(aFrame, aChange);
+#ifdef DEBUG
+  gInApplyRenderingChangeToTree = false;
+#endif
+}
+
+static void
+AddSubtreeToOverflowTracker(nsIFrame* aFrame,
+                            OverflowChangedTracker& aOverflowChangedTracker)
+{
+  if (aFrame->FrameMaintainsOverflow()) {
+    aOverflowChangedTracker.AddFrame(aFrame,
+                                     OverflowChangedTracker::CHILDREN_CHANGED);
+  }
+  nsIFrame::ChildListIterator lists(aFrame);
+  for (; !lists.IsDone(); lists.Next()) {
+    for (nsIFrame* child : lists.CurrentList()) {
+      AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
+    }
+  }
+}
+
+static void
+StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
+{
+  nsIPresShell::IntrinsicDirty dirtyType;
+  if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
+    NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
+                 "Please read the comments in nsChangeHint.h");
+    NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
+                 "ClearDescendantIntrinsics requires NeedDirtyReflow");
+    dirtyType = nsIPresShell::eStyleChange;
+  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
+             aFrame->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+    dirtyType = nsIPresShell::eStyleChange;
+  } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
+    dirtyType = nsIPresShell::eTreeChange;
+  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
+             HasBoxAncestor(aFrame)) {
+    // The frame's computed BSize is changing, and we have a box ancestor
+    // whose cached intrinsic height may need to be updated.
+    dirtyType = nsIPresShell::eTreeChange;
+  } else {
+    dirtyType = nsIPresShell::eResize;
+  }
+
+  nsFrameState dirtyBits;
+  if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+    dirtyBits = nsFrameState(0);
+  } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
+             dirtyType == nsIPresShell::eStyleChange) {
+    dirtyBits = NS_FRAME_IS_DIRTY;
+  } else {
+    dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
+  }
+
+  // If we're not going to clear any intrinsic sizes on the frames, and
+  // there are no dirty bits to set, then there's nothing to do.
+  if (dirtyType == nsIPresShell::eResize && !dirtyBits)
+    return;
+
+  nsIPresShell::ReflowRootHandling rootHandling;
+  if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
+    rootHandling = nsIPresShell::ePositionOrSizeChange;
+  } else {
+    rootHandling = nsIPresShell::eNoPositionOrSizeChange;
+  }
+
+  do {
+    aFrame->PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
+                                                         rootHandling);
+    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
+  } while (aFrame);
+}
+
+/* static */ nsresult
+RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
+                                          nsPresContext& aPresContext,
+                                          OverflowChangedTracker& aOverflowChangedTracker)
+{
+  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
+               "Someone forgot a script blocker");
+  int32_t count = aChangeList.Count();
+  if (!count)
+    return NS_OK;
+
+  PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
+    js::ProfileEntry::Category::CSS);
+
+  FramePropertyTable* propTable = aPresContext.PropertyTable();
+  nsCSSFrameConstructor* frameConstructor = aPresContext.FrameConstructor();
+
+  // Make sure to not rebuild quote or counter lists while we're
+  // processing restyles
+  frameConstructor->BeginUpdate();
+
+  // Mark frames so that we skip frames that die along the way, bug 123049.
+  // A frame can be in the list multiple times with different hints. Further
+  // optmization is possible if nsStyleChangeList::AppendChange could coalesce
+  int32_t index = count;
+
+  while (0 <= --index) {
+    const nsStyleChangeData* changeData;
+    aChangeList.ChangeAt(index, &changeData);
+    if (changeData->mFrame) {
+      propTable->Set(changeData->mFrame, ChangeListProperty(), true);
+    }
+  }
+
+  index = count;
+
+  bool didUpdateCursor = false;
+
+  while (0 <= --index) {
+    nsIFrame* frame;
+    nsIContent* content;
+    bool didReflowThisFrame = false;
+    nsChangeHint hint;
+    aChangeList.ChangeAt(index, frame, content, hint);
+
+    NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
+                 (hint & nsChangeHint_NeedReflow),
+                 "Reflow hint bits set without actually asking for a reflow");
+
+    // skip any frame that has been destroyed due to a ripple effect
+    if (frame && !propTable->Get(frame, ChangeListProperty())) {
+      continue;
+    }
+
+    if (frame && frame->GetContent() != content) {
+      // XXXbz this is due to image maps messing with the primary frame of
+      // <area>s.  See bug 135040.  Remove this block once that's fixed.
+      frame = nullptr;
+      if (!(hint & nsChangeHint_ReconstructFrame)) {
+        continue;
+      }
+    }
+
+    if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
+        !(hint & nsChangeHint_ReconstructFrame)) {
+      if (NeedToReframeForAddingOrRemovingTransform(frame) ||
+          frame->GetType() == nsGkAtoms::fieldSetFrame ||
+          frame->GetContentInsertionFrame() != frame) {
+        // The frame has positioned children that need to be reparented, or
+        // it can't easily be converted to/from being an abs-pos container correctly.
+        hint |= nsChangeHint_ReconstructFrame;
+      } else {
+        for (nsIFrame* cont = frame; cont;
+             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+          // Normally frame construction would set state bits as needed,
+          // but we're not going to reconstruct the frame so we need to set them.
+          // It's because we need to set this state on each affected frame
+          // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
+          // to ancestors (i.e. it can't be an inherited change hint).
+          if (cont->IsAbsPosContaininingBlock()) {
+            if (cont->StyleDisplay()->HasTransform(cont)) {
+              cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+            }
+            if (!cont->IsAbsoluteContainer() &&
+                (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
+              cont->MarkAsAbsoluteContainingBlock();
+            }
+          } else {
+            // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
+            // transformed by other means. It's OK to have the bit even if it's
+            // not needed.
+            if (cont->IsAbsoluteContainer()) {
+              cont->MarkAsNotAbsoluteContainingBlock();
+            }
+          }
+        }
+      }
+    }
+    if (hint & nsChangeHint_ReconstructFrame) {
+      // If we ever start passing true here, be careful of restyles
+      // that involve a reframe and animations.  In particular, if the
+      // restyle we're processing here is an animation restyle, but
+      // the style resolution we will do for the frame construction
+      // happens async when we're not in an animation restyle already,
+      // problems could arise.
+      // We could also have problems with triggering of CSS transitions
+      // on elements whose frames are reconstructed, since we depend on
+      // the reconstruction happening synchronously.
+      frameConstructor->RecreateFramesForContent(content, false,
+        nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
+    } else {
+      NS_ASSERTION(frame, "This shouldn't happen");
+
+      if (!frame->FrameMaintainsOverflow()) {
+        // frame does not maintain overflow rects, so avoid calling
+        // FinishAndStoreOverflow on it:
+        hint &= ~(nsChangeHint_UpdateOverflow |
+                  nsChangeHint_ChildrenOnlyTransform |
+                  nsChangeHint_UpdatePostTransformOverflow |
+                  nsChangeHint_UpdateParentOverflow);
+      }
+
+      if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
+        // Frame can not be transformed, and thus a change in transform will
+        // have no effect and we should not use the
+        // nsChangeHint_UpdatePostTransformOverflow hint.
+        hint &= ~nsChangeHint_UpdatePostTransformOverflow;
+      }
+
+      if (hint & nsChangeHint_UpdateEffects) {
+        for (nsIFrame* cont = frame; cont;
+             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+          nsSVGEffects::UpdateEffects(cont);
+        }
+      }
+      if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
+          ((hint & nsChangeHint_UpdateOpacityLayer) &&
+           frame->IsFrameOfType(nsIFrame::eSVG) &&
+           !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
+        nsSVGEffects::InvalidateRenderingObservers(frame);
+      }
+      if (hint & nsChangeHint_NeedReflow) {
+        StyleChangeReflow(frame, hint);
+        didReflowThisFrame = true;
+      }
+
+      if ((hint & nsChangeHint_UpdateUsesOpacity) &&
+          frame->IsFrameOfType(nsIFrame::eTablePart)) {
+        NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
+                     "should only return UpdateUsesOpacity hint "
+                     "when also returning UpdateOpacityLayer hint");
+        // When an internal table part (including cells) changes between
+        // having opacity 1 and non-1, it changes whether its
+        // backgrounds (and those of table parts inside of it) are
+        // painted as part of the table's nsDisplayTableBorderBackground
+        // display item, or part of its own display item.  That requires
+        // invalidation, so change UpdateOpacityLayer to RepaintFrame.
+        hint &= ~nsChangeHint_UpdateOpacityLayer;
+        hint |= nsChangeHint_RepaintFrame;
+      }
+
+      if (hint & nsChangeHint_UpdateBackgroundPosition) {
+        // For most frame types, DLBI can detect background position changes,
+        // so we only need to schedule a paint.
+        hint |= nsChangeHint_SchedulePaint;
+        if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
+            frame->IsFrameOfType(nsIFrame::eMathML)) {
+          // Table parts and MathML frames don't build display items for their
+          // backgrounds, so DLBI can't detect background-position changes for
+          // these frames. Repaint the whole frame.
+          hint |= nsChangeHint_RepaintFrame;
+        }
+      }
+
+      if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
+                  nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
+                  nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
+        ApplyRenderingChangeToTree(aPresContext.PresShell(), frame, hint);
+      }
+      if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
+        ActiveLayerTracker::NotifyOffsetRestyle(frame);
+        // It is possible for this to fall back to a reflow
+        if (!RecomputePosition(frame)) {
+          didReflowThisFrame = true;
+        }
+      }
+      NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
+                   (hint & nsChangeHint_UpdateOverflow),
+                   "nsChangeHint_UpdateOverflow should be passed too");
+      if (!didReflowThisFrame &&
+          (hint & (nsChangeHint_UpdateOverflow |
+                   nsChangeHint_UpdatePostTransformOverflow |
+                   nsChangeHint_UpdateParentOverflow |
+                   nsChangeHint_UpdateSubtreeOverflow))) {
+        if (hint & nsChangeHint_UpdateSubtreeOverflow) {
+          for (nsIFrame* cont = frame; cont; cont =
+                 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+            AddSubtreeToOverflowTracker(cont, aOverflowChangedTracker);
+          }
+          // The work we just did in AddSubtreeToOverflowTracker
+          // subsumes some of the other hints:
+          hint &= ~(nsChangeHint_UpdateOverflow |
+                    nsChangeHint_UpdatePostTransformOverflow);
+        }
+        if (hint & nsChangeHint_ChildrenOnlyTransform) {
+          // The overflow areas of the child frames need to be updated:
+          nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
+          nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
+          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
+                       "SVG frames should not have continuations "
+                       "or ib-split siblings");
+          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
+                       "SVG frames should not have continuations "
+                       "or ib-split siblings");
+          for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+            MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
+                       "Not expecting non-SVG children");
+            // If |childFrame| is dirty or has dirty children, we don't bother
+            // updating overflows since that will happen when it's reflowed.
+            if (!(childFrame->GetStateBits() &
+                  (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
+              aOverflowChangedTracker.AddFrame(childFrame,
+                                        OverflowChangedTracker::CHILDREN_CHANGED);
+            }
+            NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
+                         "SVG frames should not have continuations "
+                         "or ib-split siblings");
+            NS_ASSERTION(childFrame->GetParent() == hintFrame,
+                         "SVG child frame not expected to have different parent");
+          }
+        }
+        // If |frame| is dirty or has dirty children, we don't bother updating
+        // overflows since that will happen when it's reflowed.
+        if (!(frame->GetStateBits() &
+              (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
+          if (hint & (nsChangeHint_UpdateOverflow |
+                      nsChangeHint_UpdatePostTransformOverflow)) {
+            OverflowChangedTracker::ChangeKind changeKind;
+            // If we have both nsChangeHint_UpdateOverflow and
+            // nsChangeHint_UpdatePostTransformOverflow,
+            // CHILDREN_CHANGED is selected as it is
+            // strictly stronger.
+            if (hint & nsChangeHint_UpdateOverflow) {
+              changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
+            } else {
+              changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
+            }
+            for (nsIFrame* cont = frame; cont; cont =
+                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+              aOverflowChangedTracker.AddFrame(cont, changeKind);
+            }
+          }
+          // UpdateParentOverflow hints need to be processed in addition
+          // to the above, since if the processing of the above hints
+          // yields no change, the update will not propagate to the
+          // parent.
+          if (hint & nsChangeHint_UpdateParentOverflow) {
+            MOZ_ASSERT(frame->GetParent(),
+                       "shouldn't get style hints for the root frame");
+            for (nsIFrame* cont = frame; cont; cont =
+                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+              aOverflowChangedTracker.AddFrame(cont->GetParent(),
+                                   OverflowChangedTracker::CHILDREN_CHANGED);
+            }
+          }
+        }
+      }
+      if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
+        aPresContext.PresShell()->SynthesizeMouseMove(false);
+        didUpdateCursor = true;
+      }
+    }
+  }
+
+  frameConstructor->EndUpdate();
+
+  // cleanup references and verify the style tree.  Note that the latter needs
+  // to happen once we've processed the whole list, since until then the tree
+  // is not in fact in a consistent state.
+  index = count;
+  while (0 <= --index) {
+    const nsStyleChangeData* changeData;
+    aChangeList.ChangeAt(index, &changeData);
+    if (changeData->mFrame) {
+      propTable->Delete(changeData->mFrame, ChangeListProperty());
+    }
+
+#ifdef DEBUG
+    // reget frame from content since it may have been regenerated...
+    if (changeData->mContent) {
+      nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
+      if (frame) {
+        DebugVerifyStyleTree(frame);
+      }
+    } else if (!changeData->mFrame ||
+               changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
+      NS_WARNING("Unable to test style tree integrity -- no content node "
+                 "(and not a viewport frame)");
+    }
+#endif
+  }
+
+  aChangeList.Clear();
+  return NS_OK;
+}
 
 } // namespace mozilla
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -4,18 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_RestyleManagerBase_h
 #define mozilla_RestyleManagerBase_h
 
 #include "nsChangeHint.h"
 
+class nsStyleChangeList;
+
 namespace mozilla {
 
+class OverflowChangedTracker;
 class ServoRestyleManager;
 class RestyleManager;
 
 /**
  * Class for sharing data and logic common to both RestyleManager and
  * ServoRestyleManager.
  */
 class RestyleManagerBase
@@ -39,16 +42,23 @@ public:
   void SetObservingRefreshDriver(bool aObserving) {
       mObservingRefreshDriver = aObserving;
   }
 
   void Disconnect() { mPresContext = nullptr; }
 
   static nsCString RestyleHintToString(nsRestyleHint aHint);
 
+#ifdef DEBUG
+  /**
+   * DEBUG ONLY method to verify integrity of style tree versus frame tree
+   */
+  static void DebugVerifyStyleTree(nsIFrame* aFrame);
+#endif
+
 protected:
   void ContentStateChangedInternal(Element* aElement,
                                    EventStates aStateMask,
                                    nsChangeHint* aOutChangeHint,
                                    nsRestyleHint* aOutRestyleHint);
 
   bool IsDisconnected() { return mPresContext == nullptr; }
 
@@ -87,13 +97,29 @@ protected:
   }
 
 private:
   nsPresContext* mPresContext; // weak, can be null after Disconnect().
   uint32_t mRestyleGeneration;
   uint32_t mHoverGeneration;
   // True if we're already waiting for a refresh notification.
   bool mObservingRefreshDriver;
+
+  /**
+   * These are protected static methods that help with the frame construction
+   * bits of the restyle managers.
+   */
+protected:
+  static nsIFrame*
+  GetNearestAncestorFrame(nsIContent* aContent);
+
+  static nsIFrame*
+  GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame);
+
+  static nsresult
+  ProcessRestyledFrames(nsStyleChangeList& aChangeList,
+                        nsPresContext& aPresContext,
+                        OverflowChangedTracker& aOverflowChangedTracker);
 };
 
 } // namespace mozilla
 
 #endif
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -20,16 +20,17 @@ class ServoRestyleManager;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
+class nsStyleChangeList;
 
 namespace mozilla {
 
 #define SERVO_BIT 0x1
 
 /**
  * Smart pointer class that can hold a pointer to either a RestyleManager
  * or a ServoRestyleManager.
@@ -132,16 +133,18 @@ public:
                                  nsIAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aOldValue);
     inline nsresult ReparentStyleContext(nsIFrame* aFrame);
     inline bool HasPendingRestyles();
     inline uint64_t GetRestyleGeneration() const;
     inline uint32_t GetHoverGeneration() const;
     inline void SetObservingRefreshDriver(bool aObserving);
+    inline nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
+    inline void FlushOverflowChangedTracker();
 
   private:
     // Stores a pointer to an RestyleManager or a ServoRestyleManager.  The least
     // significant bit is 0 for the former, 1 for the latter.  This is
     // valid as the least significant bit will never be used for a pointer
     // value on platforms we care about.
     uintptr_t mValue;
   };
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -69,16 +69,28 @@ RestyleManagerHandle::Ptr::PostRebuildAl
 }
 
 void
 RestyleManagerHandle::Ptr::ProcessPendingRestyles()
 {
   FORWARD(ProcessPendingRestyles, ());
 }
 
+nsresult
+RestyleManagerHandle::Ptr::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+{
+  FORWARD(ProcessRestyledFrames, (aChangeList));
+}
+
+void
+RestyleManagerHandle::Ptr::FlushOverflowChangedTracker()
+{
+  FORWARD(FlushOverflowChangedTracker, ());
+}
+
 void
 RestyleManagerHandle::Ptr::RestyleForInsertOrChange(dom::Element* aContainer,
                                                     nsIContent* aChild)
 {
   FORWARD(RestyleForInsertOrChange, (aContainer, aChild));
 }
 
 void
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -306,9 +306,23 @@ ServoRestyleManager::ReparentStyleContex
 ServoElementSnapshot*
 ServoRestyleManager::SnapshotForElement(Element* aElement)
 {
   // NB: aElement is the argument for the construction of the snapshot in the
   // not found case.
   return mModifiedElements.LookupOrAdd(aElement, aElement);
 }
 
+nsresult
+ServoRestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+{
+  MOZ_CRASH("stylo: ServoRestyleManager::ProcessRestyledFrames not implemented "
+            "for Servo-backed style system");
+}
+
+void
+ServoRestyleManager::FlushOverflowChangedTracker()
+{
+  MOZ_CRASH("stylo: ServoRestyleManager::FlushOverflowChangedTracker "
+            "not implemented for Servo-backed style system");
+}
+
 } // namespace mozilla
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -20,16 +20,17 @@ namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
+class nsStyleChangeList;
 
 namespace mozilla {
 
 /**
  * Restyle manager for a Servo-backed style system.
  */
 class ServoRestyleManager : public RestyleManagerBase
 {
@@ -64,16 +65,18 @@ public:
                            const nsAttrValue* aNewValue);
 
   // XXXbholley: We should assert that the element is already snapshotted.
   void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
                         nsIAtom* aAttribute, int32_t aModType,
                         const nsAttrValue* aOldValue) {}
 
   nsresult ReparentStyleContext(nsIFrame* aFrame);
+  nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
+  void FlushOverflowChangedTracker();
 
   bool HasPendingRestyles() { return !mModifiedElements.IsEmpty(); }
 
 protected:
   ~ServoRestyleManager() {}
 
 private:
   ServoElementSnapshot* SnapshotForElement(Element* aElement);
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -7,16 +7,17 @@
  * construction of a frame tree that is nearly isomorphic to the content
  * tree and updating of that tree in response to dynamic changes
  */
 
 #ifndef nsCSSFrameConstructor_h___
 #define nsCSSFrameConstructor_h___
 
 #include "mozilla/Attributes.h"
+#include "mozilla/RestyleManagerBase.h"
 #include "mozilla/RestyleManagerHandle.h"
 
 #include "nsCOMPtr.h"
 #include "nsILayoutHistoryState.h"
 #include "nsQuoteList.h"
 #include "nsCounterManager.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsFrameManager.h"
@@ -48,16 +49,17 @@ class FlattenedChildIterator;
 
 class nsCSSFrameConstructor : public nsFrameManager
 {
 public:
   typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
   typedef mozilla::dom::Element Element;
 
   friend class mozilla::RestyleManager;
+  friend class mozilla::RestyleManagerBase;
 
   nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor(void) {
     NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
   }
 
   // get the alternate text for a content node
   static void GetAlternateTextFor(nsIContent*    aContent,
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -52,16 +52,17 @@
 #include "GeckoProfiler.h"
 #include "nsCSSRenderingBorders.h"
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
 #include "gfxGradientCache.h"
 #include "nsInlineFrame.h"
+#include "nsRubyTextContainerFrame.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using mozilla::CSSSizeOrRatio;
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2894,22 +2894,18 @@ PresShell::RecreateFramesFor(nsIContent*
   nsAutoScriptBlocker scriptBlocker;
 
   nsStyleChangeList changeList;
   changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
 
   // Mark ourselves as not safe to flush while we're doing frame construction.
   ++mChangeNestCount;
   RestyleManagerHandle restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->IsServo()) {
-    MOZ_CRASH("stylo: PresShell::RecreateFramesFor not implemented for Servo-"
-              "backed style system");
-  }
-  nsresult rv = restyleManager->AsGecko()->ProcessRestyledFrames(changeList);
-  restyleManager->AsGecko()->FlushOverflowChangedTracker();
+  nsresult rv = restyleManager->ProcessRestyledFrames(changeList);
+  restyleManager->FlushOverflowChangedTracker();
   --mChangeNestCount;
 
   return rv;
 }
 
 void
 nsIPresShell::PostRecreateFramesFor(Element* aElement)
 {