Bug 898333, patch 2: Change RestyleManager::Restyle's tree traversal to reach next-continuations (and same-display block-in-inline siblings) from their prev-continuation rather than from their parent. r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Wed, 25 Sep 2013 12:28:07 -0700
changeset 148703 ed9c22ef51e0478b4d2c2b9e2e3b9b9210fb3f47
parent 148702 b680053ea755d57715e2b5e65b94e70bcc406bb0
child 148704 cab84ad2c07c54ed42e6c4eed0d60742c03798c5
push id25352
push userkwierso@gmail.com
push dateThu, 26 Sep 2013 03:27:24 +0000
treeherdermozilla-central@94548c13fd47 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs898333, 828312, 918064
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 898333, patch 2: Change RestyleManager::Restyle's tree traversal to reach next-continuations (and same-display block-in-inline siblings) from their prev-continuation rather than from their parent. r=bzbarsky This patch fundamentally makes three changes: (1) Change the way RestyleSelf is used so that it is called in a loop, over all of the same-style continuations and block-in-inline siblings. (I had a note here reminding myself: TODO: Don't set hints for descendants! but I no longer remember what it meant.) (2) Change the traversal of children to traverse all of the children of the items traversed in (1). (3) When traversing children, skip children that are continuations, since we already reached them in (1) and (2). A later patch will change the RestyleSelf loop to copy for the later continuations rather than repeating the work. This will mean reverting many of the RestyleSelf changes contained here. Some of the pieces marked temporary will be removed in bug 828312 patch 11, and the rest should hopefully be removed in bug 918064.
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1698,16 +1698,19 @@ ElementForStyleContext(nsIContent* aPare
     nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
     return block->GetContent()->AsElement();
   }
 
   nsIContent* content = aParentContent ? aParentContent : aFrame->GetContent();
   return content->AsElement();
 }
 
+/**
+ * FIXME: Temporary.  Should merge with following function.
+ */
 static nsIFrame*
 GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
 {
   // Account for {ib} splits when looking for "prevContinuation".  In
   // particular, for the first-continuation of a part of an {ib} split we
   // want to use the special prevsibling of the special prevsibling of
   // aFrame, which should have the same style context as aFrame itself.
   // In particular, if aFrame is the first continuation of an inline part
@@ -1723,19 +1726,94 @@ GetPrevContinuationWithPossiblySameStyle
     // property directly
     prevContinuation = static_cast<nsIFrame*>(
       aFrame->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
     if (prevContinuation) {
       prevContinuation = static_cast<nsIFrame*>(
         prevContinuation->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
     }
   }
+
+  NS_ASSERTION(!prevContinuation ||
+               prevContinuation->GetContent() == aFrame->GetContent(),
+               "unexpected content mismatch");
+
   return prevContinuation;
 }
 
+/**
+ * Get the previous continuation or similar special sibling (assuming
+ * block/inline alternation), conditionally on it having the same style.
+ * This assumes that we're not between resolving the two (i.e., that
+ * they're both already resolved.
+ */
+static nsIFrame*
+GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
+{
+  nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
+  if (!prevContinuation) {
+    return nullptr;
+  }
+
+  nsStyleContext* prevStyle = prevContinuation->StyleContext();
+  nsStyleContext* selfStyle = aFrame->StyleContext();
+  if (prevStyle != selfStyle) {
+    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 special 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)
+{
+  // See GetPrevContinuationWithSameStyle about {ib} splits.
+
+  nsIFrame *nextContinuation = aFrame->GetNextContinuation();
+  if (!nextContinuation && (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
+    // We're the last continuation, so we have to hop back to the first
+    // before getting the frame property
+    nextContinuation = static_cast<nsIFrame*>(aFrame->FirstContinuation()->
+      Properties().Get(nsIFrame::IBSplitSpecialSibling()));
+    if (nextContinuation) {
+      nextContinuation = static_cast<nsIFrame*>(
+        nextContinuation->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
+    }
+  }
+
+  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;
+  }
+  return nextContinuation;
+}
+
 nsresult
 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
 {
   if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
     // Also reparent the out-of-flow and all its continuations.
     nsIFrame* outOfFlow =
       nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
     NS_ASSERTION(outOfFlow, "no out-of-flow frame");
@@ -2007,16 +2085,17 @@ ElementRestyler::ElementRestyler(ParentC
   , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
 #endif
 {
 }
 
 void
 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
                                nsStyleContext* aNewContext,
+                               nsIFrame* aContinuation, // TEMPORARY (until bug 828312 patch 11)
                                nsChangeHint aChangeToAssume)
 {
   // Check some invariants about replacing one style context with another.
   NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
                "old and new style contexts should have the same pseudo");
   NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
                "old and new style contexts should have the same pseudo");
 
@@ -2032,26 +2111,30 @@ ElementRestyler::CaptureChange(nsStyleCo
   if ((ourChange & nsChangeHint_UpdateEffects) &&
       mContent && !mContent->IsElement()) {
     ourChange = NS_SubtractHint(ourChange, nsChangeHint_UpdateEffects);
   }
 
   NS_UpdateHint(ourChange, aChangeToAssume);
   if (NS_UpdateHint(mHintsHandled, ourChange)) {
     if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
-      mChangeList->AppendChange(mFrame, mContent, ourChange);
+      mChangeList->AppendChange(aContinuation, mContent, ourChange);
     }
   }
   NS_UpdateHint(mHintsNotHandledForDescendants,
                 NS_HintsNotHandledForDescendantsIn(ourChange));
 }
 
 /**
- * Recompute style for mFrame and accumulate changes into mChangeList
- * given that mHintsHandled is already accumulated for an ancestor.
+ * Recompute style for mFrame (which should not have a prev continuation
+ * with the same style), all of its next continuations with the same
+ * style, and all special siblings of the same type (either block or
+ * inline, skipping the intermediates of the other type) and accumulate
+ * changes into mChangeList given that mHintsHandled is already accumulated
+ * for an ancestor.
  * mParentContent is the content node used to resolve the parent style
  * context.  This means that, for pseudo-elements, it is the content
  * that should be used for selector matching (rather than the fake
  * content node attached to the frame).
  */
 void
 ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
 {
@@ -2062,16 +2145,19 @@ ElementRestyler::Restyle(nsRestyleHint a
   // |mFrame->GetContent()|, make more sense.  However, we can't,
   // because of frame trees like the one in
   // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 .  Once we
   // fix bug 242277 we should be able to make this make more sense.
   NS_ASSERTION(mFrame->GetContent() || !mParentContent ||
                !mParentContent->GetParent(),
                "frame must have content (unless at the top of the tree)");
 
+  NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
+               "should not be trying to restyle this frame separately");
+
   if (mContent && mContent->IsElement()) {
     mContent->OwnerDoc()->FlushPendingLinkUpdates();
     RestyleTracker::RestyleData restyleData;
     if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
       if (NS_UpdateHint(mHintsHandled, restyleData.mChangeHint)) {
         mChangeList->AppendChange(mFrame, mContent, restyleData.mChangeHint);
       }
       aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
@@ -2079,209 +2165,174 @@ ElementRestyler::Restyle(nsRestyleHint a
   }
 
   nsRestyleHint childRestyleHint = aRestyleHint;
 
   if (childRestyleHint == eRestyle_Self) {
     childRestyleHint = nsRestyleHint(0);
   }
 
-  RestyleSelf(aRestyleHint);
+  {
+    nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
+
+    // TEMPORARY (until bug 918064):  Call RestyleSelf for each
+    // continuation or block-in-inline sibling.
+    nsChangeHint hintsHandled = mHintsHandled;
+
+    for (nsIFrame* f = mFrame; f;
+         f = GetNextContinuationWithSameStyle(f, oldContext)) {
+      // restore for each continuation, since we need the change hint
+      // posted for each continuation and failing to restore would
+      // suppress that.
+      mHintsHandled = hintsHandled;
+
+      RestyleSelf(f, aRestyleHint);
+    }
+  }
 
   RestyleChildren(childRestyleHint);
 }
 
 void
-ElementRestyler::RestyleSelf(nsRestyleHint aRestyleHint)
+ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
 {
   // XXXldb get new context from prev-in-flow if possible, to avoid
   // duplication.  (Or should we just let |GetContext| handle that?)
   // Getting the hint would be nice too, but that's harder.
 
   // XXXbryner we may be able to avoid some of the refcounting goop here.
   // We do need a reference to oldContext for the lifetime of this function, and it's possible
   // that the frame has the last reference to it, so AddRef it here.
 
   nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
-  nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
+  nsRefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
   nsStyleSet* styleSet = mPresContext->StyleSet();
 
 #ifdef ACCESSIBILITY
   mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
     oldContext->StyleVisibility()->IsVisible() : false;
 #endif
 
   nsIAtom* const pseudoTag = oldContext->GetPseudo();
   const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
 
   nsStyleContext* parentContext;
   // Get the frame providing the parent style context.  If it is a
   // child, then resolve the provider first.
-  nsIFrame* providerFrame = mFrame->GetParentStyleContextFrame();
-  bool isChild = providerFrame && providerFrame->GetParent() == mFrame;
+  nsIFrame* providerFrame = aSelf->GetParentStyleContextFrame();
+  bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
   if (!isChild) {
     if (providerFrame)
       parentContext = providerFrame->StyleContext();
     else
       parentContext = nullptr;
   }
   else {
-    MOZ_ASSERT(providerFrame->GetContent() == mFrame->GetContent(),
+    MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
                "Postcondition for GetParentStyleContextFrame() violated. "
                "That means we need to add the current element to the "
                "ancestor filter.");
 
-    // resolve the provider here (before mFrame below).
+    // resolve the provider here (before aSelf below).
 
     // assumeDifferenceHint forces the parent's change to be also
     // applied to this frame, no matter what
     // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
     // can't be trusted because it assumes any changes to the parent
     // style context provider will be automatically propagated to
     // the frame(s) with child style contexts.
 
     ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
                                      *this, providerFrame);
     providerRestyler.Restyle(aRestyleHint);
     assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
 
     // The provider's new context becomes the parent context of
-    // mFrame's context.
+    // aSelf's context.
     parentContext = providerFrame->StyleContext();
     // Set |mResolvedChild| so we don't bother resolving the
     // provider again.
     mResolvedChild = providerFrame;
   }
 
-  if (providerFrame != mFrame->GetParent()) {
+  if (providerFrame != aSelf->GetParent()) {
     // We don't actually know what the parent style context's
     // non-inherited hints were, so assume the worst.
     mParentFrameHintsNotHandledForDescendants =
       nsChangeHint_Hints_NotHandledForDescendants;
   }
 
-#ifdef DEBUG
-  {
-    // Check that our assumption that continuations of the same
-    // pseudo-type and with the same style context parent have the
-    // same style context is valid before the reresolution.  (We need
-    // to check the pseudo-type and style context parent because of
-    // :first-letter and :first-line, where we create styled and
-    // unstyled letter/line frames distinguished by pseudo-type, and
-    // then need to distinguish their descendants based on having
-    // different parents.)
-    nsIFrame *nextContinuation = mFrame->GetNextContinuation();
-    if (nextContinuation) {
-      nsStyleContext *nextContinuationContext =
-        nextContinuation->StyleContext();
-      NS_ASSERTION(oldContext == nextContinuationContext ||
-                   oldContext->GetPseudo() !=
-                     nextContinuationContext->GetPseudo() ||
-                   oldContext->GetParent() !=
-                     nextContinuationContext->GetParent(),
-                   "continuations should have the same style context");
-    }
-    // And assert the same thing for {ib} splits.  See the comments in
-    // GetPrevContinuationWithPossiblySameStyle for an explanation of
-    // why we step two forward in the special sibling chain.
-    if ((mFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) &&
-        !mFrame->GetPrevContinuation()) {
-      nsIFrame *nextIBSibling = static_cast<nsIFrame*>(
-        mFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
-      if (nextIBSibling) {
-        nextIBSibling = static_cast<nsIFrame*>(
-          nextIBSibling->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
-      }
-      if (nextIBSibling) {
-        nsStyleContext *nextIBSiblingContext =
-          nextIBSibling->StyleContext();
-        NS_ASSERTION(oldContext == nextIBSiblingContext ||
-                     oldContext->GetPseudo() !=
-                       nextIBSiblingContext->GetPseudo() ||
-                     oldContext->GetParent() !=
-                       nextIBSiblingContext->GetParent(),
-                     "continuations should have the same style context");
-      }
-    }
-  }
-#endif
-
   // do primary context
   nsRefPtr<nsStyleContext> newContext;
   nsIFrame *prevContinuation =
-    GetPrevContinuationWithPossiblySameStyle(mFrame);
+    GetPrevContinuationWithPossiblySameStyle(aSelf);
   nsStyleContext *prevContinuationContext;
   bool copyFromContinuation =
     prevContinuation &&
     (prevContinuationContext = prevContinuation->StyleContext())
       ->GetPseudo() == oldContext->GetPseudo() &&
      prevContinuationContext->GetParent() == parentContext;
   if (copyFromContinuation) {
     // Just use the style context from the frame's previous
-    // continuation (see assertion about mFrame->GetNextContinuation()
-    // above, which we would have previously hit for mFrame's previous
-    // continuation).
+    // continuation.
     newContext = prevContinuationContext;
   }
   else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
-    NS_ASSERTION(mFrame->GetContent(),
+    NS_ASSERTION(aSelf->GetContent(),
                  "non pseudo-element frame without content node");
     newContext = styleSet->ResolveStyleForNonElement(parentContext);
   }
   else if (!aRestyleHint && !prevContinuation) {
     // Unfortunately, if prevContinuation is non-null then we may have
     // already stolen the restyle tracker entry for this element while
     // processing prevContinuation.  So we don't know whether aRestyleHint
     // should really be 0 here or whether it should be eRestyle_Self.  Be
     // pessimistic and force an actual reresolve in that situation.  The good
     // news is that in the common case when prevContinuation is non-null we
     // just used prevContinuationContext anyway and aren't reaching this code
     // to start with.
     newContext =
       styleSet->ReparentStyleContext(oldContext, parentContext,
                                      ElementForStyleContext(mParentContent,
-                                                            mFrame,
-                                                            pseudoType));
+                                                            aSelf, pseudoType));
   } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
     newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
                                                     parentContext);
   }
   else {
-    Element* element = ElementForStyleContext(mParentContent,
-                                              mFrame,
-                                              pseudoType);
+    Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
     if (pseudoTag) {
       if (pseudoTag == nsCSSPseudoElements::before ||
           pseudoTag == nsCSSPseudoElements::after) {
         // XXX what other pseudos do we need to treat like this?
         newContext = styleSet->ProbePseudoElementStyle(element,
                                                        pseudoType,
                                                        parentContext,
                                                        mTreeMatchContext);
         if (!newContext) {
           // This pseudo should no longer exist; gotta reframe
           NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
-          mChangeList->AppendChange(mFrame, element,
+          mChangeList->AppendChange(aSelf, element,
                                     nsChangeHint_ReconstructFrame);
           // We're reframing anyway; just keep the same context
           newContext = oldContext;
         }
       } else {
         // Don't expect XUL tree stuff here, since it needs a comparator and
         // all.
         NS_ASSERTION(pseudoType <
                        nsCSSPseudoElements::ePseudo_PseudoElementCount,
                      "Unexpected pseudo type");
         newContext = styleSet->ResolvePseudoElementStyle(element,
                                                          pseudoType,
                                                          parentContext);
       }
     }
     else {
-      NS_ASSERTION(mFrame->GetContent(),
+      NS_ASSERTION(aSelf->GetContent(),
                    "non pseudo-element frame without content node");
       // Skip flex-item style fixup for anonymous subtrees:
       TreeMatchContext::AutoFlexItemStyleFixupSkipper
         flexFixupSkipper(mTreeMatchContext,
                          element->IsRootOfNativeAnonymousSubtree());
       newContext = styleSet->ResolveStyleFor(element, parentContext,
                                              mTreeMatchContext);
     }
@@ -2300,34 +2351,34 @@ ElementRestyler::RestyleSelf(nsRestyleHi
       // style contexts around.  However, we need to start from the
       // same root.
       newContext = oldContext;
     }
   }
 
   if (newContext != oldContext) {
     if (!copyFromContinuation) {
-      TryStartingTransition(mPresContext, mFrame->GetContent(),
+      TryStartingTransition(mPresContext, aSelf->GetContent(),
                             oldContext, &newContext);
     }
 
-    CaptureChange(oldContext, newContext, assumeDifferenceHint);
+    CaptureChange(oldContext, newContext, aSelf, assumeDifferenceHint);
     if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
       // if frame gets regenerated, let it keep old context
-      mFrame->SetStyleContext(newContext);
+      aSelf->SetStyleContext(newContext);
     }
   }
   oldContext = nullptr;
 
   // do additional contexts
   // XXXbz might be able to avoid selector matching here in some
   // cases; won't worry about it for now.
   int32_t contextIndex = 0;
   for (nsStyleContext* oldExtraContext;
-       (oldExtraContext = mFrame->GetAdditionalStyleContext(contextIndex));
+       (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
        ++contextIndex) {
     nsRefPtr<nsStyleContext> newExtraContext;
     nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
     const nsCSSPseudoElements::Type extraPseudoType =
       oldExtraContext->GetPseudoType();
     NS_ASSERTION(extraPseudoTag &&
                  extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
                  "extra style context is not pseudo element");
@@ -2344,19 +2395,20 @@ ElementRestyler::RestyleSelf(nsRestyleHi
       newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
                                                             extraPseudoType,
                                                             newContext);
     }
 
     MOZ_ASSERT(newExtraContext);
 
     if (oldExtraContext != newExtraContext) {
-      CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint);
+      CaptureChange(oldExtraContext, newExtraContext, aSelf,
+                    assumeDifferenceHint);
       if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
-        mFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
+        aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
       }
     }
   }
 }
 
 void
 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
 {
@@ -2378,29 +2430,34 @@ ElementRestyler::RestyleChildren(nsResty
   // on a frame change.  The act of reconstructing frames will force
   // new style contexts to be resolved on all of this frame's
   // descendants anyway, so we want to avoid wasting time processing
   // style contexts that we're just going to throw away anyway. - dwh
   // It's also important to check mHintsHandled since reresolving the
   // 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();
 
-    RestyleContentChildren(aChildRestyleHint);
+    for (nsIFrame* f = mFrame; f;
+         f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
+      lastContinuation = f;
+      RestyleContentChildren(f, aChildRestyleHint);
+    }
 
     SendAccessibilityNotifications();
   }
 
   // Check whether we might need to create a new ::after frame.
   // See comments above regarding :before.
   if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
       aChildRestyleHint) {
-    RestyleAfterPseudo();
+    RestyleAfterPseudo(lastContinuation);
   }
 }
 
 void
 ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
 {
   // When the root element is display:none, we still construct *some*
   // frames that have the root element as their mContent, down to the
@@ -2509,41 +2566,45 @@ ElementRestyler::RestyleBeforePseudo()
         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
         mChangeList->AppendChange(mFrame, mContent,
                                   nsChangeHint_ReconstructFrame);
       }
     }
   }
 }
 
+/**
+ * aFrame is the last continuation or block-in-inline sibling that this
+ * ElementRestyler is restyling.
+ */
 void
-ElementRestyler::RestyleAfterPseudo()
+ElementRestyler::RestyleAfterPseudo(nsIFrame* aFrame)
 {
   // Make sure not to do this for pseudo-frames or frames that
   // can't have generated content.
-  if (!mFrame->StyleContext()->GetPseudo() &&
-      ((mFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
+  if (!aFrame->StyleContext()->GetPseudo() &&
+      ((aFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
        // Our content insertion frame might have gotten flagged
-       (mFrame->GetContentInsertionFrame()->GetStateBits() &
+       (aFrame->GetContentInsertionFrame()->GetStateBits() &
         NS_FRAME_MAY_HAVE_GENERATED_CONTENT))) {
     // Check for new :after content, but only if the frame is the
     // last continuation.
-    nsIFrame* nextContinuation = mFrame->GetNextContinuation();
+    nsIFrame* nextContinuation = aFrame->GetNextContinuation();
 
     if (!nextContinuation) {
       // Getting the :after frame is more expensive than getting the pseudo
       // context, so get the pseudo context first.
-      if (nsLayoutUtils::HasPseudoStyle(mFrame->GetContent(),
-                                        mFrame->StyleContext(),
+      if (nsLayoutUtils::HasPseudoStyle(aFrame->GetContent(),
+                                        aFrame->StyleContext(),
                                         nsCSSPseudoElements::ePseudo_after,
                                         mPresContext) &&
-          !nsLayoutUtils::GetAfterFrame(mFrame)) {
+          !nsLayoutUtils::GetAfterFrame(aFrame)) {
         // have to create the new :after frame
         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
-        mChangeList->AppendChange(mFrame, mContent,
+        mChangeList->AppendChange(aFrame, mContent,
                                   nsChangeHint_ReconstructFrame);
       }
     }
   }
 }
 
 void
 ElementRestyler::InitializeAccessibilityNotifications()
@@ -2581,29 +2642,33 @@ ElementRestyler::InitializeAccessibility
       mVisibleKidsOfHiddenElement.AppendElement(mFrame->GetContent());
       mKidsDesiredA11yNotifications = eSkipNotifications;
     }
   }
 #endif
 }
 
 void
-ElementRestyler::RestyleContentChildren(nsRestyleHint aChildRestyleHint)
+ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
+                                        nsRestyleHint aChildRestyleHint)
 {
-  nsIFrame::ChildListIterator lists(mFrame);
+  nsIFrame::ChildListIterator lists(aParent);
   for (TreeMatchContext::AutoAncestorPusher
          pushAncestor(!lists.IsDone(),
                       mTreeMatchContext,
                       mContent && mContent->IsElement()
                         ? mContent->AsElement() : nullptr);
        !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
-      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+      // 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)) {
         // Get the parent of the child frame's content and check if it
         // is a XBL children element. Push the children element as an
         // ancestor here because it does not have a frame and would not
         // otherwise be pushed as an ancestor.
 
         // Check if the frame has a content because |child| may be a
         // nsPageFrame that does not have a content.
         nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
@@ -2630,16 +2695,23 @@ ElementRestyler::RestyleContentChildren(
           // the <body> would miss reflowing fixed-pos nodes that also need
           // reflow).  In the cases when the out-of-flow _is_ a geometric
           // descendant of a frame we already have a reflow hint for,
           // reflow coalescing should keep us from doing the work twice.
 
           // |nsFrame::GetParentStyleContextFrame| checks being out
           // of flow so that this works correctly.
           do {
+            if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
+              // Later continuations are likely restyled as a result of
+              // the restyling of the previous continuation.
+              // (Currently that's always true, but it's likely to
+              // change if we implement overflow:fragments or similar.)
+              continue;
+            }
             ElementRestyler oofRestyler(*this, outOfFlowFrame,
                                         FOR_OUT_OF_FLOW_CHILD);
             oofRestyler.Restyle(aChildRestyleHint);
           } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
 
           // reresolve placeholder's context under the same parent
           // as the out-of-flow frame
           ElementRestyler phRestyler(*this, child, 0);
@@ -2703,62 +2775,62 @@ RestyleManager::ComputeStyleChangeFor(ns
   PROFILER_LABEL("CSS", "ComputeStyleChangeFor");
 
   nsIContent *content = aFrame->GetContent();
   if (aMinChange) {
     aChangeList->AppendChange(aFrame, content, aMinChange);
   }
 
   nsIFrame* frame = aFrame;
-  nsIFrame* frame2 = aFrame;
 
   NS_ASSERTION(!frame->GetPrevContinuation(), "must start with the first in flow");
 
-  // We want to start with this frame and walk all its next-in-flows,
-  // as well as all its special siblings and their next-in-flows,
-  // reresolving style on all the frames we encounter in this walk.
+  // We need to handle aFrame and all of its continuations and special
+  // siblings and their continuations.  ReResolveStyleContext loops over
+  // the continuations, and also loops over the similar-type sequences
+  // in block-in-inline splits (i.e., either all the blocks or all the
+  // inlines), so here we only have to advance one step to the other set
+  // of special siblings (i.e., switch from blocks to inlines, or
+  // vice-versa).
 
   FramePropertyTable* propTable = mPresContext->PropertyTable();
 
   TreeMatchContext treeMatchContext(true,
                                     nsRuleWalker::eRelevantLinkUnvisited,
                                     mPresContext->Document());
   nsIContent *parent = content ? content->GetParent() : nullptr;
   Element *parentElement =
     parent && parent->IsElement() ? parent->AsElement() : nullptr;
   treeMatchContext.InitAncestors(parentElement);
   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
-  do {
-    // Outer loop over special siblings
-    do {
-      // Inner loop over next-in-flows of the current frame
-      ElementRestyler restyler(mPresContext, frame, aChangeList,
-                               aMinChange, aRestyleTracker,
-                               treeMatchContext,
-                               visibleKidsOfHiddenElement);
+  for (int ibSet = 0; ibSet < 2; ++ibSet) {
+    // loop over the two sets (blocks, inlines) of special siblings
+    ElementRestyler restyler(mPresContext, frame, aChangeList,
+                             aMinChange, aRestyleTracker,
+                             treeMatchContext,
+                             visibleKidsOfHiddenElement);
 
-      restyler.Restyle(aRestyleDescendants ? eRestyle_Subtree : eRestyle_Self);
+    restyler.Restyle(aRestyleDescendants ? eRestyle_Subtree : eRestyle_Self);
 
-      if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
-        // If it's going to cause a framechange, then don't bother
-        // with the continuations or special siblings since they'll be
-        // clobbered by the frame reconstruct anyway.
-        NS_ASSERTION(!frame->GetPrevContinuation(),
-                     "continuing frame had more severe impact than first-in-flow");
-        return;
-      }
-
-      frame = frame->GetNextContinuation();
-    } while (frame);
+    if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
+      // If it's going to cause a framechange, then don't bother
+      // with the continuations or special siblings since they'll be
+      // clobbered by the frame reconstruct anyway.
+      NS_ASSERTION(!frame->GetPrevContinuation(),
+                   "continuing frame had more severe impact than first-in-flow");
+      return;
+    }
 
     // Might we have special siblings?
-    if (!(frame2->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
+    if (!(frame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
       // nothing more to do here
       return;
     }
 
-    frame2 = static_cast<nsIFrame*>
-      (propTable->Get(frame2, nsIFrame::IBSplitSpecialSibling()));
-    frame = frame2;
-  } while (frame2);
+    frame = static_cast<nsIFrame*>
+      (propTable->Get(frame, nsIFrame::IBSplitSpecialSibling()));
+    if (!frame) {
+      return;
+    }
+  }
 }
 
 } // namespace mozilla
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -317,39 +317,41 @@ public:
    * hints have been handled for this frame.
    */
   nsChangeHint HintsHandledForFrame() { return mHintsHandled; }
 
 private:
   /**
    * First half of Restyle().
    */
-  void RestyleSelf(nsRestyleHint aRestyleHint);
+  void RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint);
 
   /**
    * Restyle the children of this frame (and, in turn, their children).
    *
    * Second half of Restyle().
    */
   void RestyleChildren(nsRestyleHint aChildRestyleHint);
 
   /**
    * Helper for RestyleSelf().
    */
   void CaptureChange(nsStyleContext* aOldContext,
                      nsStyleContext* aNewContext,
+                     nsIFrame* aContinuation, // TEMPORARY (until bug 828312 patch 11)
                      nsChangeHint aChangeToAssume);
 
   /**
    * Helpers for RestyleChildren().
    */
   void RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint);
   void RestyleBeforePseudo();
-  void RestyleAfterPseudo();
-  void RestyleContentChildren(nsRestyleHint aChildRestyleHint);
+  void RestyleAfterPseudo(nsIFrame* aFrame);
+  void RestyleContentChildren(nsIFrame* aParent,
+                              nsRestyleHint aChildRestyleHint);
   void InitializeAccessibilityNotifications();
   void SendAccessibilityNotifications();
 
   enum DesiredA11yNotifications {
     eSkipNotifications,
     eSendAllNotifications,
     eNotifyIfShown
   };