Bug 1290335: Hoist GetNextContinuationWithSameStyle to RestyleManagerBase. draft
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Mon, 01 Aug 2016 13:40:27 -0700
changeset 396032 acf7c285b201a3e3d3ead2970d459258fcd7fa91
parent 396031 64bbcea8a3848f8398fd3269aae79f7ac183f5de
child 396033 25658fd5956ff44c4d02dc8db61cdb3a870d33a8
child 396041 f42f9c04298be7d958cd32e79d7588b6e68832ef
push id24900
push userbmo:ealvarez@mozilla.com
push dateWed, 03 Aug 2016 08:06:14 +0000
bugs1290335
milestone51.0a1
Bug 1290335: Hoist GetNextContinuationWithSameStyle to RestyleManagerBase. MozReview-Commit-ID: 3Jz9Od5xav0
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/RestyleManagerBase.cpp
layout/base/RestyleManagerBase.h
layout/base/RestyleTracker.cpp
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1163,66 +1163,16 @@ GetPrevContinuationWithSameStyle(nsIFram
     NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
                  prevStyle->GetParent() != selfStyle->GetParent(),
                  "continuations should have the same style context");
     prevContinuation = nullptr;
   }
   return prevContinuation;
 }
 
-/**
- * Get the next continuation or similar ib-split sibling (assuming
- * block/inline alternation), conditionally on it having the same style.
- *
- * Since this is used when deciding to copy the new style context, it
- * takes as an argument the old style context to check if the style is
- * the same.  When it is used in other contexts (i.e., where the next
- * continuation would already have the new style context), the current
- * style context should be passed.
- */
-static nsIFrame*
-GetNextContinuationWithSameStyle(nsIFrame* aFrame,
-                                 nsStyleContext* aOldStyleContext,
-                                 bool* aHaveMoreContinuations = nullptr)
-{
-  // See GetPrevContinuationWithSameStyle about {ib} splits.
-
-  nsIFrame *nextContinuation = aFrame->GetNextContinuation();
-  if (!nextContinuation &&
-      (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-    // We're the last continuation, so we have to hop back to the first
-    // before getting the frame property
-    nextContinuation = aFrame->FirstContinuation()->
-      Properties().Get(nsIFrame::IBSplitSibling());
-    if (nextContinuation) {
-      nextContinuation =
-        nextContinuation->Properties().Get(nsIFrame::IBSplitSibling());
-    }
-  }
-
-  if (!nextContinuation) {
-    return nullptr;
-  }
-
-  NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
-               "unexpected content mismatch");
-
-  nsStyleContext* nextStyle = nextContinuation->StyleContext();
-  if (nextStyle != aOldStyleContext) {
-    NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
-                 aOldStyleContext->GetParent() != nextStyle->GetParent(),
-                 "continuations should have the same style context");
-    nextContinuation = nullptr;
-    if (aHaveMoreContinuations) {
-      *aHaveMoreContinuations = true;
-    }
-  }
-  return nextContinuation;
-}
-
 nsresult
 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
 {
   nsIAtom* frameType = aFrame->GetType();
   if (frameType == nsGkAtoms::placeholderFrame) {
     // Also reparent the out-of-flow and all its continuations.
     nsIFrame* outOfFlow =
       nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
@@ -1778,17 +1728,17 @@ ElementRestyler::ConditionallyRestyleCon
   MOZ_ASSERT(aFrame->GetContent()->IsElement());
   MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());
 
   if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) {
     aRestyleRoot = aFrame->GetContent()->AsElement();
   }
 
   for (nsIFrame* f = aFrame; f;
-       f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
+       f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
     nsIFrame::ChildListIterator lists(f);
     for (; !lists.IsDone(); lists.Next()) {
       for (nsIFrame* child : lists.CurrentList()) {
         // Out-of-flows are reached through their placeholders.  Continuations
         // and block-in-inline splits are reached through those chains.
         if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
             !GetPrevContinuationWithSameStyle(child)) {
           // only do frames that are in flow
@@ -2059,17 +2009,17 @@ ElementRestyler::MoveStyleContextsForChi
   nsTArray<nsStyleContext*> contextsToMove;
 
   MOZ_ASSERT(!MustReframeForBeforePseudo(),
              "shouldn't need to reframe ::before as we would have had "
              "eRestyle_Subtree and wouldn't get in here");
 
   DebugOnly<nsIFrame*> lastContinuation;
   for (nsIFrame* f = mFrame; f;
-       f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
+       f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
     lastContinuation = f;
     if (!MoveStyleContextsForContentChildren(f, aOldContext, contextsToMove)) {
       return false;
     }
   }
 
   MOZ_ASSERT(!MustReframeForAfterPseudo(lastContinuation),
              "shouldn't need to reframe ::after as we would have had "
@@ -2214,17 +2164,19 @@ ElementRestyler::Restyle(nsRestyleHint a
 
     if (thisResult > result) {
       // We take the highest RestyleResult value when working out what to do
       // with this frame and its descendants.  Higher RestyleResult values
       // represent a superset of the work done by lower values.
       result = thisResult;
     }
 
-    f = GetNextContinuationWithSameStyle(f, oldContext, &haveMoreContinuations);
+    f = RestyleManager::GetNextContinuationWithSameStyle(f,
+                                                         oldContext,
+                                                         &haveMoreContinuations);
   }
 
   // Some changes to animations don't affect the computed style and yet still
   // require the layer to be updated. For example, pausing an animation via
   // the Web Animations API won't affect an element's style but still
   // requires us to pull the animation off the layer.
   //
   // Although we only expect this code path to be called when computed style
@@ -3295,17 +3247,17 @@ ElementRestyler::RestyleChildren(nsResty
   // kids would use mFrame->StyleContext(), which is out of date if
   // mHintsHandled has a ReconstructFrame hint; doing this could trigger
   // assertions about mismatched rule trees.
   nsIFrame* lastContinuation;
   if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
     InitializeAccessibilityNotifications(mFrame->StyleContext());
 
     for (nsIFrame* f = mFrame; f;
-         f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
+         f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
       lastContinuation = f;
       RestyleContentChildren(f, aChildRestyleHint);
     }
 
     SendAccessibilityNotifications();
   }
 
   // Check whether we might need to create a new ::after frame.
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -483,21 +483,16 @@ private:
                       nsChangeHint    aMinHint,
                       RestyleTracker& aRestyleTracker,
                       nsRestyleHint   aRestyleHint,
                       const RestyleHintData& aRestyleHintData);
 
   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);
-
   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
@@ -52,43 +52,44 @@ RestyleManagerBase::ContentStateChangedI
         aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
                                          NS_EVENT_STATE_USERDISABLED |
                                          NS_EVENT_STATE_SUPPRESSED |
                                          NS_EVENT_STATE_LOADING)) {
       *aOutChangeHint = nsChangeHint_ReconstructFrame;
     } else {
       uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
       if (app) {
-        nsITheme *theme = PresContext()->GetTheme();
-        if (theme && theme->ThemeSupportsWidget(PresContext(),
-                                                primaryFrame, app)) {
+        nsITheme* theme = PresContext()->GetTheme();
+        if (theme &&
+            theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
           bool repaint = false;
-          theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr);
+          theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
+                                    nullptr);
           if (repaint) {
             *aOutChangeHint |= nsChangeHint_RepaintFrame;
           }
         }
       }
     }
 
     pseudoType = primaryFrame->StyleContext()->GetPseudoType();
 
     primaryFrame->ContentStatesChanged(aStateMask);
   }
 
   if (pseudoType >= CSSPseudoElementType::Count) {
     *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
   } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
-                                                                  pseudoType)) {
+               pseudoType)) {
     // If aElement is a pseudo-element, we want to check to see whether there
     // are any state-dependent rules applying to that pseudo.
-    Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
-                                               pseudoType);
-    *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement,
-                                                       aStateMask);
+    Element* ancestor =
+      ElementForStyleContext(nullptr, primaryFrame, pseudoType);
+    *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
+                                                        aElement, aStateMask);
   } else {
     *aOutRestyleHint = nsRestyleHint(0);
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
     IncrementHoverGeneration();
   }
 
@@ -142,27 +143,27 @@ static bool gInApplyRenderingChangeToTre
 #endif
 
 #ifdef DEBUG
 static void
 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
 {
   if (aFrame) {
     fputs("frame: ", stdout);
-    nsAutoString  name;
+    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;
+      nsAutoString buffer;
       pseudoTag->ToString(buffer);
       fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
       fputs(" ", stdout);
     }
     fputs("{}\n", stdout);
   }
 }
 
@@ -206,30 +207,28 @@ VerifyContextParent(nsIFrame* aFrame, ns
   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 {
+      } 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 {
+  } 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);
     }
   }
@@ -247,17 +246,17 @@ VerifyContextParent(nsIFrame* aFrame, ns
     DumpContext(aFrame, aContext);
     fputs("\n", stdout);
   }
 }
 
 static void
 VerifyStyleTree(nsIFrame* aFrame)
 {
-  nsStyleContext*  context = aFrame->StyleContext();
+  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()) {
@@ -295,33 +294,31 @@ RestyleManagerBase::DebugVerifyStyleTree
 {
   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 SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
+                                              nsChangeHint aChange);
 
-static void
-StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
+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.
@@ -348,23 +345,21 @@ GetFrameForChildrenOnlyTransformHint(nsI
   // 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),
+  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
@@ -410,17 +405,18 @@ RecomputePosition(nsIFrame* aFrame)
       // 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);
+        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)) {
@@ -455,18 +451,18 @@ RecomputePosition(nsIFrame* aFrame)
   // 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);
+  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;
@@ -498,22 +494,24 @@ RecomputePosition(nsIFrame* aFrame)
   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());
+  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();
+    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).
@@ -563,25 +561,27 @@ HasBoxAncestor(nsIFrame* aFrame)
   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)
+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);
+          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;
           }
         }
@@ -600,28 +600,27 @@ NeedToReframeForAddingOrRemovingTransfor
   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()) {
+  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);
+    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;
@@ -635,17 +634,18 @@ RestyleManagerBase::GetNearestAncestorFr
        ancestor && !ancestorFrame;
        ancestor = ancestor->GetParent()) {
     ancestorFrame = ancestor->GetPrimaryFrame();
   }
   return ancestorFrame;
 }
 
 /* static */ nsIFrame*
-RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
+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;
   }
@@ -693,19 +693,18 @@ DoApplyRenderingChangeToTree(nsIFrame* a
         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);
+        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
@@ -729,70 +728,71 @@ DoApplyRenderingChangeToTree(nsIFrame* a
       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);
+          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);
+    aFrame->SchedulePaint(needInvalidatingPaint
+                            ? nsIFrame::PAINT_DEFAULT
+                            : nsIFrame::PAINT_COMPOSITE_ONLY);
   }
 }
 
 /* static */ void
-SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
-                                  nsChangeHint aChange)
+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);
+      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
+        } else { // regular frame
           SyncViewsAndInvalidateDescendants(child, aChange);
         }
       }
     }
   }
 }
 
 void
@@ -811,18 +811,18 @@ ApplyRenderingChangeToTree(nsIPresShell*
   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.
+// 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;
@@ -867,17 +867,18 @@ StyleChangeReflow(nsIFrame* aFrame, nsCh
   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)) {
+             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;
@@ -903,35 +904,75 @@ StyleChangeReflow(nsIFrame* aFrame, nsCh
   nsIPresShell::ReflowRootHandling rootHandling;
   if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
     rootHandling = nsIPresShell::ePositionOrSizeChange;
   } else {
     rootHandling = nsIPresShell::eNoPositionOrSizeChange;
   }
 
   do {
-    aFrame->PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
-                                                         rootHandling);
+    aFrame->PresContext()->PresShell()->FrameNeedsReflow(
+      aFrame, dirtyType, dirtyBits, rootHandling);
     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   } while (aFrame);
 }
 
+/* static */ nsIFrame*
+RestyleManagerBase::GetNextContinuationWithSameStyle(
+  nsIFrame* aFrame, nsStyleContext* aOldStyleContext,
+  bool* aHaveMoreContinuations)
+{
+  // See GetPrevContinuationWithSameStyle about {ib} splits.
+
+  nsIFrame* nextContinuation = aFrame->GetNextContinuation();
+  if (!nextContinuation &&
+      (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
+    // We're the last continuation, so we have to hop back to the first
+    // before getting the frame property
+    nextContinuation =
+      aFrame->FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
+    if (nextContinuation) {
+      nextContinuation =
+        nextContinuation->Properties().Get(nsIFrame::IBSplitSibling());
+    }
+  }
+
+  if (!nextContinuation) {
+    return nullptr;
+  }
+
+  NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
+               "unexpected content mismatch");
+
+  nsStyleContext* nextStyle = nextContinuation->StyleContext();
+  if (nextStyle != aOldStyleContext) {
+    NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
+                 aOldStyleContext->GetParent() != nextStyle->GetParent(),
+                 "continuations should have the same style context");
+    nextContinuation = nullptr;
+    if (aHaveMoreContinuations) {
+      *aHaveMoreContinuations = true;
+    }
+  }
+  return nextContinuation;
+}
+
 /* static */ nsresult
-RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
-                                          nsPresContext& aPresContext,
-                                          OverflowChangedTracker& aOverflowChangedTracker)
+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);
+                 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();
 
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -99,27 +99,42 @@ 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.
+   * These are protected static methods that help with the change hint
+   * processing 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);
+
+  /**
+   * Get the next continuation or similar ib-split sibling (assuming
+   * block/inline alternation), conditionally on it having the same style.
+   *
+   * Since this is used when deciding to copy the new style context, it
+   * takes as an argument the old style context to check if the style is
+   * the same.  When it is used in other contexts (i.e., where the next
+   * continuation would already have the new style context), the current
+   * style context should be passed.
+   */
+  static nsIFrame*
+  GetNextContinuationWithSameStyle(nsIFrame* aFrame,
+                                   nsStyleContext* aOldStyleContext,
+                                   bool* aHaveMoreContinuations = nullptr);
 };
 
 } // namespace mozilla
 
 #endif
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -163,17 +163,18 @@ RestyleTracker::DoProcessRestyles()
 
     // loop so that we process any restyle events generated by processing
     while (mPendingRestyles.Count()) {
       if (mHaveLaterSiblingRestyles) {
         // Convert them to individual restyles on all the later siblings
         AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
         for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
           auto element = static_cast<dom::Element*>(iter.Key());
-          MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here");
+          MOZ_ASSERT(!element->IsStyledByServo(),
+                     "Should not have Servo-styled elements here");
           // Only collect the entries that actually need restyling by us (and
           // haven't, for example, already been restyled).
           // It's important to not mess with the flags on entries not in our
           // document.
           if (element->GetComposedDoc() == Document() &&
               element->HasFlag(RestyleBit()) &&
               (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) {
             laterSiblingArr.AppendElement(element);