Track which frames were float continuations that need to be pulled to the next block rather than figuring it out again when it's time to pull them. (This allows first-in-flows to be float continuations.) (Bug 563584, patch 12) r=roc
authorL. David Baron <dbaron@dbaron.org>
Thu, 05 Aug 2010 21:59:19 -0700
changeset 48989 c3f25dd3c232f3aaa97b71d96f20e8493bfbec3e
parent 48988 2abf47cd611e624da4685299fd76d47af0ba4655
child 48990 69b9b34abe5825d176c04be037bd0dcb80770cd9
push id14884
push userdbaron@mozilla.com
push dateFri, 06 Aug 2010 05:01:26 +0000
treeherderautoland@8ab7ef79b673 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs563584
milestone2.0b4pre
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
Track which frames were float continuations that need to be pulled to the next block rather than figuring it out again when it's time to pull them. (This allows first-in-flows to be float continuations.) (Bug 563584, patch 12) r=roc
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowState.h
layout/generic/nsIFrame.h
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1009,18 +1009,19 @@ nsBlockFrame::Reflow(nsPresContext*     
   rv = ReflowDirtyLines(state);
   NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
   if (NS_FAILED(rv)) return rv;
 
   NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
   NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
 
   // Put continued floats at the end of mFloats
-  if (state.mFloatContinuations.NotEmpty())
+  if (state.mFloatContinuations.NotEmpty()) {
     mFloats.AppendFrames(nsnull, state.mFloatContinuations);
+  }
 
   // If we end in a BR with clear and affected floats continue,
   // we need to continue, too.
   if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
       NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
       state.mFloatManager->ClearContinues(FindTrailingClear())) {
     NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
   }
@@ -1763,17 +1764,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockRe
     // dirty
   nscoord deltaY = 0;
 
     // whether we did NOT reflow the previous line and thus we need to
     // recompute the carried out margin before the line if we want to
     // reflow it or if its previous margin is dirty
   PRBool needToRecoverState = PR_FALSE;
     // Float continuations were reflowed in ReflowFloatContinuations
-  PRBool reflowedFloat = mFloats.NotEmpty() && mFloats.FirstChild()->GetPrevInFlow();
+  PRBool reflowedFloat = mFloats.NotEmpty() &&
+    (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION);
   PRBool lastLineMovedUp = PR_FALSE;
   // We save up information about BR-clearance here
   PRUint8 inlineFloatBreakType = aState.mFloatBreakType;
 
   line_iterator line = begin_lines(), line_end = end_lines();
 
   // Reflow the lines that are already ours
   for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
@@ -3889,18 +3891,26 @@ nsBlockFrame::CreateContinuationFor(nsBl
   return NS_OK;
 }
 
 nsresult
 nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
                          nsIFrame*           aFloat,
                          nsReflowStatus      aFloatStatus)
 {
-  nsIFrame* nextInFlow = nsnull;
-  if (!aFloat->GetNextInFlow()) {
+  nsIFrame* nextInFlow = aFloat->GetNextInFlow();
+  if (nextInFlow) {
+    nsContainerFrame *oldParent =
+      static_cast<nsContainerFrame*>(nextInFlow->GetParent());
+    nsresult rv = oldParent->StealFrame(aState.mPresContext, nextInFlow);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
+    if (oldParent != this) {
+      ReparentFrame(nextInFlow, oldParent, this);
+    }
+  } else {
     nsresult rv = aState.mPresContext->PresShell()->FrameConstructor()->
       CreateContinuingFrame(aState.mPresContext, aFloat, this, &nextInFlow);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
     aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
 
   // Float continuations can only trigger overflow
@@ -3911,38 +3921,39 @@ nsBlockFrame::SplitFloat(nsBlockReflowSt
   if (aFloat->GetStyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
     aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
   } else {
     NS_ABORT_IF_FALSE(aFloat->GetStyleDisplay()->mFloats ==
                         NS_STYLE_FLOAT_RIGHT, "unexpected float side");
     aState.mFloatManager->SetSplitRightFloatAcrossBreak();
   }
 
-  if (nextInFlow) {
-    // Next in flow was created above.
-    aState.AppendFloatContinuation(nextInFlow);
-  }
+  aState.AppendFloatContinuation(nextInFlow);
   return NS_OK;
 }
 
 static nsFloatCache*
 GetLastFloat(nsLineBox* aLine)
 {
   nsFloatCache* fc = aLine->GetFirstFloat();
   while (fc && fc->Next()) {
     fc = fc->Next();
   }
   return fc;
 }
 
 static PRBool
 CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
 {
-  if (!aFC || aFC->mFloat->GetPrevInFlow())
+  if (!aFC)
     return PR_TRUE;
+  NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
+               "float in a line should never be a continuation");
+  NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION),
+               "float in a line should never be a pushed float");
   nsIFrame* ph = aBlock->PresContext()->FrameManager()->
                    GetPlaceholderFrameFor(aFC->mFloat->GetFirstInFlow());
   for (nsIFrame* f = ph; f; f = f->GetParent()) {
     if (f->GetParent() == aBlock)
       return aLine->Contains(f);
   }
   NS_ASSERTION(PR_FALSE, "aBlock is not an ancestor of aFrame!");
   return PR_TRUE;
@@ -4430,63 +4441,66 @@ nsBlockFrame::DrainOverflowLines(nsBlock
     }
     delete ourOverflowLines;
   }
 
   return PR_TRUE;
 }
 
 // This function assumes our prev-in-flow has completed reflow and its
-// mFloats contains at most two frames that belong to the same flow chain,
-// the second one being a last-in-flow continuation intended for this block.
+// mFloats may contain frames at the end of its float list, marked with
+// NS_FRAME_IS_FLOAT_CONTINUATION, that should be pulled to this block.
 void
 nsBlockFrame::DrainFloatContinuations(nsBlockReflowState& aState)
 {
   // Cache any continuations of our own floats that we're still holding onto
   // so they're out of the way. This should only happen if we're re-Reflow'd
   // before our next-in-flow gets a chance to pull these continuations.
-  nsFrameList floatContinuations;
-  nsPresContext* presContext = PresContext();
-  for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
-    nsIFrame* nif = f->GetNextInFlow();
-    if (!nif) continue;
-    if (nif->GetParent() == this) {
-      NS_ASSERTION(!nif->GetNextInFlow(),
-                   "Unexpected next-in-flow for float continuation");
-      StealFrame(presContext, nif);
-      floatContinuations.AppendFrame(nsnull, nif);
-    }
-  }
-  if (floatContinuations.NotEmpty()) {
+  // However, if it's a "continuation" that's not actually a continuation,
+  // put it back on the floats list.
+  // FIXME: This is not compatible with doing float breaking in dynamic
+  // situations, since in those situations we could have current
+  // continuations at the end of our float list that were actually
+  // continuations from a previous frame to this one.  (However, it's
+  // not clear to me that we really need this code in the first place;
+  // the best solution might just be to remove it.)
+  nsIFrame *f = mFloats.LastChild();
+  if (f && (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION)) {
+    do {
+      f = f->GetPrevSibling();
+    } while (f && (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION));
     aState.SetupFloatContinuationList();
-    aState.mFloatContinuations.AppendFrames(nsnull, floatContinuations);
-  }
-
-  // Walk our prev-in-flow's floats and prepend their continuations to our
-  // floats list. This pulls any continuations we need to take from our
-  // prev-in-flow and makes sure our continuations of its floats are in the
-  // proper order.
+    // RemoveFramesAfter(nsnull) removes the whole list
+    nsFrameList floatContinuations = mFloats.RemoveFramesAfter(f);
+    while (floatContinuations.NotEmpty()) {
+      nsIFrame *f = floatContinuations.RemoveFirstChild();
+      if (f->GetPrevContinuation()) {
+        aState.mFloatContinuations.AppendFrame(nsnull, f);
+      } else {
+        f->RemoveStateBits(NS_FRAME_IS_FLOAT_CONTINUATION);
+        mFloats.AppendFrame(nsnull, f);
+      }
+    }
+  }
+
+  // Take any continuations we need to take from our prev-in-flow.
   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
   if (!prevBlock)
     return;
-  for (nsIFrame* pf = prevBlock->mFloats.FirstChild(); pf; pf = pf->GetNextSibling()) {
-    nsIFrame* nif = pf->GetNextInFlow();
-    if (!nif)
-      continue;
-    nsContainerFrame* nifParent = static_cast<nsContainerFrame*>(nif->GetParent());
-    nifParent->StealFrame(presContext, nif);
-    if (nif->GetParent() != this) {
-      NS_ASSERTION(!nif->GetNextInFlow(),
-                   "Unexpected next-in-flow for float continuation");
-      ReparentFrame(nif, nifParent, this);
-    }
-    floatContinuations.AppendFrame(this, nif);
-  }
-  if (floatContinuations.NotEmpty())
+  f = prevBlock->mFloats.LastChild();
+  if (f && (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION)) {
+    do {
+      ReparentFrame(f, prevBlock, this);
+      f = f->GetPrevSibling();
+    } while (f && (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION));
+
+    // RemoveFramesAfter(nsnull) removes the whole list
+    nsFrameList floatContinuations = prevBlock->mFloats.RemoveFramesAfter(f);
     mFloats.InsertFrames(nsnull, nsnull, floatContinuations);
+  }
 
 #ifdef DEBUG
   for (nsIFrame* f = mFloats.FirstChild(); f ; f = f->GetNextSibling()) {
     for (nsIFrame* c = f->GetFirstInFlow(); c ; c = c->GetNextInFlow()) {
       NS_ASSERTION(c == f || c->GetParent() != this || !mFloats.ContainsFrame(c),
                    "Two floats with same parent in same floats list, expect weird errors.");
     }
   }
@@ -5714,17 +5728,18 @@ nsBlockFrame::FindTrailingClear()
 }
 
 nsresult
 nsBlockFrame::ReflowFloatContinuations(nsBlockReflowState& aState,
                                        nsRect&             aBounds,
                                        nsReflowStatus&     aStatus)
 {
   nsresult rv = NS_OK;
-  for (nsIFrame* f = mFloats.FirstChild(); f && f->GetPrevInFlow();
+  for (nsIFrame* f = mFloats.FirstChild();
+       f && (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION);
        f = f->GetNextSibling()) {
     if (NS_SUBTREE_DIRTY(f) || aState.mReflowState.ShouldReflowAllKids()) {
       // Cache old bounds
       nsRect oldRect = f->GetRect();
       nsRect oldOverflow = f->GetOverflowRect();
 
       // Reflow
       nsReflowStatus fStatus = NS_FRAME_COMPLETE;
@@ -6039,17 +6054,17 @@ nsBlockFrame::BuildDisplayList(nsDisplay
   }
 #endif
 
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   if (GetPrevInFlow()) {
     DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
     for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
-      if (f->GetPrevInFlow())
+      if (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION)
          BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
     }
   }
 
   aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
   aBuilder->MarkFramesForDisplayList(this, mAbsoluteContainer.GetChildList(),
                                      aDirtyRect);
 
@@ -6659,29 +6674,40 @@ void nsBlockFrame::CollectFloats(nsIFram
                                  PRBool aFromOverflow, PRBool aCollectSiblings) {
   while (aFrame) {
     // Don't descend into float containing blocks.
     if (!aFrame->IsFloatContainingBlock()) {
       nsIFrame *outOfFlowFrame =
         aFrame->GetType() == nsGkAtoms::placeholderFrame ?
           nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nsnull;
       if (outOfFlowFrame) {
-        // Make sure that its parent is us. Otherwise we don't want
-        // to mess around with it because it belongs to someone
-        // else. I think this could happen if the overflow lines
-        // contain a block descendant which owns its own floats.
-        NS_ASSERTION(outOfFlowFrame->GetParent() == this,
-                     "Out of flow frame doesn't have the expected parent");
-        if (aFromOverflow) {
-          nsAutoOOFFrameList oofs(this);
-          oofs.mList.RemoveFrame(outOfFlowFrame);
+        if (outOfFlowFrame->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION) {
+          if (outOfFlowFrame->GetParent() == this) {
+            nsFrameList* list = GetPropTableFrames(PresContext(),
+                                                   FloatContinuationProperty());
+            if (!list || !list->RemoveFrameIfPresent(outOfFlowFrame)) {
+              mFloats.RemoveFrame(outOfFlowFrame);
+            }
+            aList.AppendFrame(nsnull, outOfFlowFrame);
+          }
         } else {
-          mFloats.RemoveFrame(outOfFlowFrame);
+          // Make sure that its parent is us. Otherwise we don't want
+          // to mess around with it because it belongs to someone
+          // else. I think this could happen if the overflow lines
+          // contain a block descendant which owns its own floats.
+          NS_ASSERTION(outOfFlowFrame->GetParent() == this,
+                       "Out of flow frame doesn't have the expected parent");
+          if (aFromOverflow) {
+            nsAutoOOFFrameList oofs(this);
+            oofs.mList.RemoveFrame(outOfFlowFrame);
+          } else {
+            mFloats.RemoveFrame(outOfFlowFrame);
+          }
+          aList.AppendFrame(nsnull, outOfFlowFrame);
         }
-        aList.AppendFrame(nsnull, outOfFlowFrame);
       }
 
       CollectFloats(aFrame->GetFirstChild(nsnull), 
                     aList, aFromOverflow, PR_TRUE);
       // Note: Even though we're calling CollectFloats on aFrame's overflow
       // list, we'll pass down aFromOverflow unchanged because we're still
       // traversing the regular-children subtree of the 'this' frame.
       CollectFloats(aFrame->GetFirstChild(nsGkAtoms::overflowList), 
@@ -6717,17 +6743,17 @@ nsBlockFrame::CheckFloats(nsBlockReflowS
       anyLineDirty = PR_TRUE;
     }
   }
   
   nsAutoTArray<nsIFrame*, 8> storedFloats;
   PRBool equal = PR_TRUE;
   PRUint32 i = 0;
   for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
-    if (f->GetPrevInFlow())
+    if (f->GetStateBits() & NS_FRAME_IS_FLOAT_CONTINUATION)
       continue;
     storedFloats.AppendElement(f);
     if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
       equal = PR_FALSE;
     }
     ++i;
   }
 
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -92,17 +92,27 @@ class nsIntervalSet;
  * flow frames whose placeholders are in the overflow list.
  * -- A given piece of content has at most one placeholder
  * frame in a block's normal child list.
  * -- While a block is being reflowed, it may have a FloatContinuationProperty
  * frame property that points to an nsFrameList in its
  * nsBlockReflowState. This list contains continuations for
  * floats whose prev-in-flow is in the block's regular float
  * list. The list is always empty/nonexistent after the
- * block has been reflowed.
+ * block has been reflowed.  (However, after it has been
+ * reflowed and before the continuations are moved to the
+ * next block, they are temporarily at the end of the
+ * block's float list.  FIXME: This temporary storage
+ * situation is not compatible with doing float breaking in
+ * dynamic cases, since we can't distinguish unflushed
+ * temporary storage (floats being transferred from the
+ * frame) in a case where we need a second reflow from
+ * frames previously transferred to the frame; fixing this
+ * would require an additional frame list for this temporary
+ * storage.)
  * -- In all these frame lists, if there are two frames for
  * the same content appearing in the list, then the frames
  * appear with the prev-in-flow before the next-in-flow.
  * -- While reflowing a block, its overflow line list
  * will usually be empty but in some cases will have lines
  * (while we reflow the block at its shrink-wrap width).
  * In this case any new overflowing content must be
  * prepended to the overflow lines.
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -228,16 +228,17 @@ public:
   // they move to the end of the mFloats list.
   nsFrameList mFloatContinuations;
   // This method makes sure float continuations are accessible to
   // StealFrame. Call it before adding any frames to mFloatContinuations.
   void SetupFloatContinuationList();
   // Use this method to append to mFloatContinuations.
   void AppendFloatContinuation(nsIFrame* aFloatCont) {
     SetupFloatContinuationList();
+    aFloatCont->AddStateBits(NS_FRAME_IS_FLOAT_CONTINUATION);
     mFloatContinuations.AppendFrame(mBlock, aFloatCont);
   }
 
   // Track child overflow continuations.
   nsOverflowContinuationTracker* mOverflowTracker;
 
   //----------------------------------------
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -251,16 +251,22 @@ typedef PRUint64 nsFrameState;
 
 // If this bit is set, then reflow may be dispatched from the current
 // frame instead of the root frame.
 #define NS_FRAME_REFLOW_ROOT                        NS_FRAME_STATE_BIT(19)
 
 // Bits 20-31 of the frame state are reserved for implementations.
 #define NS_FRAME_IMPL_RESERVED                      nsFrameState(0xFFF00000)
 
+// This bit is set on floats whose parent does not contain their
+// placeholder.  This can happen for two reasons:  (1) the float was
+// split, and this piece is the continuation, or (2) the entire float
+// didn't fit on the page.
+#define NS_FRAME_IS_FLOAT_CONTINUATION              NS_FRAME_STATE_BIT(32)
+
 // The lower 20 bits and upper 32 bits of the frame state are reserved
 // by this API.
 #define NS_FRAME_RESERVED                           ~NS_FRAME_IMPL_RESERVED
 
 // Box layout bits
 #define NS_STATE_IS_HORIZONTAL                      NS_FRAME_STATE_BIT(22)
 #define NS_STATE_IS_DIRECTION_NORMAL                NS_FRAME_STATE_BIT(31)