Bug 765621 - Drain the parent frame's overflow list before attempting to insert/append new child frames. r=bz a=akeybl
authorMats Palmgren <matspal@gmail.com>
Tue, 18 Sep 2012 01:05:05 +0200
changeset 106919 592da114f13829036a6bf67310665d9e330b46c3
parent 106918 c039da3793f755b7c5f881623acb69e931a1367b
child 106920 3acb1af786e00567be1163656e426a91bb4b1e4c
push id2132
push usermpalmgren@mozilla.com
push dateFri, 21 Sep 2012 17:34:45 +0000
treeherdermozilla-aurora@592da114f138 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, akeybl
bugs765621
milestone17.0a2
Bug 765621 - Drain the parent frame's overflow list before attempting to insert/append new child frames. r=bz a=akeybl
layout/base/nsCSSFrameConstructor.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsIFrame.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5683,16 +5683,19 @@ AdjustAppendParentForAfterContent(nsPres
                                   nsIFrame** aAfterFrame)
 {
   // See if the parent has an :after pseudo-element.  Check for the presence
   // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
   nsStyleContext* parentStyle = aParentFrame->GetStyleContext();
   if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
                                     nsCSSPseudoElements::ePseudo_after,
                                     aPresContext)) {
+    // Ensure that the :after frame is on the principal child list.
+    aParentFrame->DrainSelfOverflowList();
+
     nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
     if (afterFrame) {
       *aAfterFrame = afterFrame;
       return afterFrame->GetParent();
     }
   }
 
   *aAfterFrame = nullptr;
@@ -5725,19 +5728,24 @@ AdjustAppendParentForAfterContent(nsPres
  * it takes a parent frame (must not be null) and its :after frame (may be
  * null).
  */
 static nsIFrame*
 FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
 {
   if (aAfterFrame) {
     NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
+    NS_ASSERTION(aAfterFrame->GetPrevSibling() ||
+                 aParentFrame->GetFirstPrincipalChild() == aAfterFrame,
+                 ":after frame must be on the principal child list here");
     return aAfterFrame->GetPrevSibling();
   }
 
+  aParentFrame->DrainSelfOverflowList();
+
   return aParentFrame->GetLastChild(kPrincipalList);
 }
 
 /**
  * This function will get the next sibling for a frame insert operation given
  * the parent and previous sibling.  aPrevSibling may be null.
  */
 static nsIFrame*
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -4465,85 +4465,76 @@ nsBlockFrame::PushLines(nsBlockReflowSta
 // the invariant that the property is never set if the list is empty.
 
 bool
 nsBlockFrame::DrainOverflowLines()
 {
 #ifdef DEBUG
   VerifyOverflowSituation();
 #endif
-  FrameLines* overflowLines = nullptr;
-  FrameLines* ourOverflowLines = nullptr;
-
-  // First grab the prev-in-flows overflow lines
-  nsBlockFrame* prevBlock = (nsBlockFrame*) GetPrevInFlow();
+
+  // Steal the prev-in-flow's overflow lines and prepend them.
+  bool didFindOverflow = false;
+  nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
   if (prevBlock) {
     prevBlock->ClearLineCursor();
-    overflowLines = prevBlock->RemoveOverflowLines();
+    FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
     if (overflowLines) {
-      NS_ASSERTION(!overflowLines->mLines.empty(),
-                   "overflow lines should never be set and empty");
       // Make all the frames on the overflow line list mine.
       ReparentFrames(overflowLines->mFrames, prevBlock, this);
 
       // Make the overflow out-of-flow frames mine too.
       nsAutoOOFFrameList oofs(prevBlock);
       if (oofs.mList.NotEmpty()) {
         ReparentFrames(oofs.mList, prevBlock, this);
         mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
       }
-    }
-    
-    // The lines on the overflow list have already been marked dirty and their
-    // previous margins marked dirty also.
-  }
-
-  // Don't need to reparent frames in our own overflow lines/oofs, because they're
+
+      if (!mLines.empty()) {
+        // Remember to recompute the margins on the first line. This will
+        // also recompute the correct deltaY if necessary.
+        mLines.front()->MarkPreviousMarginDirty();
+      }
+      // The overflow lines have already been marked dirty and their previous
+      // margins marked dirty also.
+      
+      // Prepend the overflow frames/lines to our principal list.
+      mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
+      mLines.splice(mLines.begin(), overflowLines->mLines);
+      NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
+      delete overflowLines;
+      didFindOverflow = true;
+    }
+  }
+
+  // Now append our own overflow lines.
+  return DrainSelfOverflowList() || didFindOverflow;
+}
+
+bool
+nsBlockFrame::DrainSelfOverflowList()
+{
+  // No need to reparent frames in our own overflow lines/oofs, because they're
   // already ours. But we should put overflow floats back in mFloats.
-  ourOverflowLines = RemoveOverflowLines();
+  FrameLines* ourOverflowLines = RemoveOverflowLines();
   if (ourOverflowLines) {
     nsAutoOOFFrameList oofs(this);
     if (oofs.mList.NotEmpty()) {
-      // The overflow floats go after our regular floats
+      // The overflow floats go after our regular floats.
       mFloats.AppendFrames(nullptr, oofs.mList);
     }
-  }
-
-  if (!overflowLines && !ourOverflowLines) {
-    // nothing to do; always the case for non-constrained-height reflows
+  } else {
     return false;
   }
 
-  // Now join the line lists into mLines
-  if (overflowLines) {
-    if (!overflowLines->mLines.empty()) {
-      // Join the line lists
-      if (!mLines.empty()) {
-          // Remember to recompute the margins on the first line. This will
-          // also recompute the correct deltaY if necessary.
-          mLines.front()->MarkPreviousMarginDirty();
-      }
-      
-      // Join the sibling lists together
-      mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
-
-      // Place overflow lines at the front of our line list
-      mLines.splice(mLines.begin(), overflowLines->mLines);
-      NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
-    }
-    delete overflowLines;
-  }
-  if (ourOverflowLines) {
-    if (!ourOverflowLines->mLines.empty()) {
-      mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
-      mLines.splice(mLines.end(), ourOverflowLines->mLines);
-    }
-    delete ourOverflowLines;
-  }
-
+  if (!ourOverflowLines->mLines.empty()) {
+    mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
+    mLines.splice(mLines.end(), ourOverflowLines->mLines);
+  }
+  delete ourOverflowLines;
   return true;
 }
 
 // This function assumes our prev-in-flow has completed reflow and its
 // mFloats may contain frames at the end of its float list, marked with
 // NS_FRAME_IS_PUSHED_FLOAT, that should be pulled to this block.
 void
 nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState)
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -263,16 +263,22 @@ public:
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   NS_IMETHOD AttributeChanged(int32_t         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               int32_t         aModType);
 
+  /**
+   * Move any frames on our overflow list to the end of our principal list.
+   * @return true if there were any overflow frames
+   */
+  virtual bool DrainSelfOverflowList();
+
   virtual nsresult StealFrame(nsPresContext* aPresContext,
                               nsIFrame*      aChild,
                               bool           aForceNormal = false);
 
   virtual void DeleteNextInFlowChild(nsPresContext* aPresContext,
                                      nsIFrame*      aNextInFlow,
                                      bool           aDeletingEmptyFrames);
 
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -1534,24 +1534,30 @@ nsContainerFrame::MoveOverflowToChildLis
       nsContainerFrame::ReparentFrameViewList(aPresContext,
                                               *prevOverflowFrames,
                                               prevInFlow, this);
       mFrames.AppendFrames(this, *prevOverflowFrames);
       result = true;
     }
   }
 
-  // It's also possible that we have an overflow list for ourselves
+  // It's also possible that we have an overflow list for ourselves.
+  return DrainSelfOverflowList() || result;
+}
+
+bool
+nsContainerFrame::DrainSelfOverflowList()
+{
   nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
   if (overflowFrames) {
     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     mFrames.AppendFrames(nullptr, *overflowFrames);
-    result = true;
+    return true;
   }
-  return result;
+  return false;
 }
 
 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsPresContext*    aPresContext,
                                                              nsContainerFrame* aFrame,
                                                              bool              aWalkOOFFrames,
                                                              bool              aSkipOverflowContainerChildren)
   : mOverflowContList(nullptr),
     mPrevOverflowCont(nullptr),
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -298,16 +298,22 @@ public:
    */
   nsresult ReflowOverflowContainerChildren(nsPresContext*           aPresContext,
                                            const nsHTMLReflowState& aReflowState,
                                            nsOverflowAreas&         aOverflowRects,
                                            uint32_t                 aFlags,
                                            nsReflowStatus&          aStatus);
 
   /**
+   * Move any frames on our overflow list to the end of our principal list.
+   * @return true if there were any overflow frames
+   */
+  virtual bool DrainSelfOverflowList();
+
+  /**
    * Removes aChild without destroying it and without requesting reflow.
    * Continuations are not affected. Checks the primary and overflow
    * or overflow containers and excess overflow containers lists, depending
    * on whether the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. Does not
    * check any other auxiliary lists.
    * Returns NS_ERROR_UNEXPECTED if we failed to remove aChild.
    * Returns other error codes if we failed to put back a proptable list.
    * If aForceNormal is true, only checks the primary and overflow lists
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -689,16 +689,22 @@ public:
 
   /**
    * Get the frame that should be the parent for the frames of child elements
    * May return nullptr during reflow
    */
   virtual nsIFrame* GetContentInsertionFrame() { return this; }
 
   /**
+   * Move any frames on our overflow list to the end of our principal list.
+   * @return true if there were any overflow frames
+   */
+  virtual bool DrainSelfOverflowList() { return false; }
+
+  /**
    * Get the frame that should be scrolled if the content associated
    * with this frame is targeted for scrolling. For frames implementing
    * nsIScrollableFrame this will return the frame itself. For frames
    * like nsTextControlFrame that contain a scrollframe, will return
    * that scrollframe.
    */
   virtual nsIScrollableFrame* GetScrollTargetFrame() { return nullptr; }