Switch away from nsBlockReflowState::mAvailSpaceRect for reflow of child floats. (Bug 25888) r+sr=roc
authorL. David Baron <dbaron@dbaron.org>
Wed, 08 Apr 2009 13:52:36 -0700
changeset 27101 698d52722aebbb2023cedc4b41e44500bb016cdf
parent 27100 df0130484852d12339e976cbfc916e91df69bd15
child 27102 800e279fe24dd4f9d6faecd9e9018e9df121d989
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs25888
milestone1.9.2a1pre
Switch away from nsBlockReflowState::mAvailSpaceRect for reflow of child floats. (Bug 25888) r+sr=roc
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowState.cpp
layout/generic/nsBlockReflowState.h
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -5641,34 +5641,35 @@ nsBlockFrame::DeleteNextInFlowChild(nsPr
         aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Float support
 
 nsRect
-nsBlockFrame::ComputeFloatAvailableSpace(nsBlockReflowState& aState,
-                                         nsIFrame* aFloatFrame)
+nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState,
+                                        const nsRect& aFloatAvailableSpace,
+                                        nsIFrame* aFloatFrame)
 {
   // Compute the available width. By default, assume the width of the
   // containing block.
   nscoord availWidth;
   const nsStyleDisplay* floatDisplay = aFloatFrame->GetStyleDisplay();
 
   if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
       eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
     availWidth = aState.mContentArea.width;
   }
   else {
     // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
     // give tables only the available space
     // if they can shrink we may not be constrained to place
     // them in the next line
-    availWidth = aState.mAvailSpaceRect.width;
+    availWidth = aFloatAvailableSpace.width;
     // round down to twips per pixel so that we fit
     // needed when prev. float has procentage width
     // (maybe is a table flaw that makes table chose to round up
     // but I don't want to change that, too risky)
     nscoord twp = nsPresContext::CSSPixelsToAppUnits(1);
     availWidth -=  availWidth % twp;
   }
 
@@ -5691,49 +5692,53 @@ nsBlockFrame::ComputeFloatAvailableSpace
 
   return nsRect(aState.BorderPadding().left,
                 aState.BorderPadding().top,
                 availWidth, availHeight);
 }
 
 nscoord
 nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState,
+                                const nsRect&       aFloatAvailableSpace,
                                 nsPlaceholderFrame* aPlaceholder)
 {
   // Reflow the float.
   nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
 
-  nsRect availSpace = ComputeFloatAvailableSpace(aState, floatFrame);
+  nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
+                                                floatFrame);
 
   nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
                             floatFrame, 
                             nsSize(availSpace.width, availSpace.height));
   return floatRS.ComputedWidth() + floatRS.mComputedBorderPadding.LeftRight() +
     floatRS.mComputedMargin.LeftRight();
 }
 
 nsresult
 nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
+                          const nsRect&       aFloatAvailableSpace,
                           nsPlaceholderFrame* aPlaceholder,
                           nsMargin&           aFloatMargin,
                           nsReflowStatus&     aReflowStatus)
 {
   // Reflow the float.
   nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
   aReflowStatus = NS_FRAME_COMPLETE;
 
 #ifdef NOISY_FLOAT
   printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
           aPlaceholder->GetOutOfFlowFrame(), this, 
-          aState.mAvailSpaceRect.x, aState.mAvailSpaceRect.y, 
-          aState.mAvailSpaceRect.width, aState.mAvailSpaceRect.height
+          aFloatAvailableSpace.x, aFloatAvailableSpace.y, 
+          aFloatAvailableSpace.width, aFloatAvailableSpace.height
   );
 #endif
 
-  nsRect availSpace = ComputeFloatAvailableSpace(aState, floatFrame);
+  nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
+                                                floatFrame);
 
   nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
                             floatFrame, 
                             nsSize(availSpace.width, availSpace.height));
 
   // Setup a block reflow state to reflow the float.
   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
 
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -524,24 +524,27 @@ protected:
 
   nsresult ReflowInlineFrame(nsBlockReflowState& aState,
                              nsLineLayout& aLineLayout,
                              line_iterator aLine,
                              nsIFrame* aFrame,
                              LineReflowStatus* aLineReflowStatus);
 
   // Compute the available width for a float. 
-  nsRect ComputeFloatAvailableSpace(nsBlockReflowState& aState,
-                                    nsIFrame*           aFloatFrame);
+  nsRect AdjustFloatAvailableSpace(nsBlockReflowState& aState,
+                                   const nsRect&       aFloatAvailableSpace,
+                                   nsIFrame*           aFloatFrame);
   // Computes the border-box width of the float
   nscoord ComputeFloatWidth(nsBlockReflowState& aState,
+                            const nsRect&       aFloatAvailableSpace,
                             nsPlaceholderFrame* aPlaceholder);
   // An incomplete aReflowStatus indicates the float should be split
   // but only if the available height is constrained.
   nsresult ReflowFloat(nsBlockReflowState& aState,
+                       const nsRect&       aFloatAvailableSpace,
                        nsPlaceholderFrame* aPlaceholder,
                        nsMargin&           aFloatMargin,
                        nsReflowStatus&     aReflowStatus);
 
   //----------------------------------------
   // Methods for pushing/pulling lines/frames
 
   virtual nsresult CreateContinuationFor(nsBlockReflowState& aState,
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -561,37 +561,40 @@ nsBlockReflowState::AddFloat(nsLineLayou
 {
   NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr");
 
   aReflowStatus = NS_FRAME_COMPLETE;
   // Allocate a nsFloatCache for the float
   nsFloatCache* fc = mFloatCacheFreeList.Alloc();
   fc->mPlaceholder = aPlaceholder;
 
+  // Because we are in the middle of reflowing a placeholder frame
+  // within a line (and possibly nested in an inline frame or two
+  // that's a child of our block) we need to restore the space
+  // manager's translation to the space that the block resides in
+  // before placing the float.
+  nscoord ox, oy;
+  mFloatManager->GetTranslation(ox, oy);
+  nscoord dx = ox - mFloatManagerX;
+  nscoord dy = oy - mFloatManagerY;
+  mFloatManager->Translate(-dx, -dy);
+
   PRBool placed;
 
   // Now place the float immediately if possible. Otherwise stash it
   // away in mPendingFloats and place it later.
   // If one or more floats has already been pushed to the next line,
   // don't let this one go on the current line, since that would violate
   // float ordering.
+  nsRect floatAvailableSpace;
+  GetFloatAvailableSpace(floatAvailableSpace);
   if (mBelowCurrentLineFloats.IsEmpty() &&
       (aLineLayout.LineIsEmpty() ||
-       mBlock->ComputeFloatWidth(*this, aPlaceholder) <= aAvailableWidth)) {
-    // Because we are in the middle of reflowing a placeholder frame
-    // within a line (and possibly nested in an inline frame or two
-    // that's a child of our block) we need to restore the space
-    // manager's translation to the space that the block resides in
-    // before placing the float.
-    nscoord ox, oy;
-    mFloatManager->GetTranslation(ox, oy);
-    nscoord dx = ox - mFloatManagerX;
-    nscoord dy = oy - mFloatManagerY;
-    mFloatManager->Translate(-dx, -dy);
-
+       mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aPlaceholder) <=
+         aAvailableWidth)) {
     // And then place it
     PRBool isLeftFloat;
     // force it to fit if we're at the top of the block and we can't
     // break before this
     PRBool forceFit = IsAdjacentWithTop() && !aLineLayout.LineIsBreakable();
     placed = FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus, forceFit);
     NS_ASSERTION(placed || !forceFit,
                  "If we asked for force-fit, it should have been placed");
@@ -619,19 +622,16 @@ nsBlockReflowState::AddFloat(nsLineLayou
         aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
       } else {
         // Make sure we propagate the truncated status; this signals the
         // block to push the line to the next page.
         aReflowStatus |= NS_FRAME_TRUNCATED;
       }
       delete fc;
     }
-
-    // Restore coordinate system
-    mFloatManager->Translate(dx, dy);
   }
   else {
     // Always claim to be placed; we don't know whether we fit yet, so we
     // deal with this in PlaceBelowCurrentLineFloats
     placed = PR_TRUE;
     // This float will be placed after the line is done (it is a
     // below-current-line float).
     mBelowCurrentLineFloats.Append(fc);
@@ -643,62 +643,67 @@ nsBlockReflowState::AddFloat(nsLineLayou
       // Note that we could have unconstrained height and yet have
       // a next-in-flow placeholder --- for example columns can switch
       // from constrained height to unconstrained height.
       if (aPlaceholder->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE) {
         aReflowStatus = NS_FRAME_NOT_COMPLETE;
       }
     }
   }
+
+  // Restore coordinate system
+  mFloatManager->Translate(dx, dy);
+
   return placed;
 }
 
 PRBool
-nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize,
-                                  PRUint8 aFloats, PRBool aForceFit)
+nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize, PRUint8 aFloats,
+                                  const nsRect& aFloatAvailableSpace,
+                                  PRBool aBandHasFloats, PRBool aForceFit)
 {
   // If the current Y coordinate is not impacted by any floats
   // then by definition the float fits.
   PRBool result = PR_TRUE;
-  if (mBandHasFloats) {
+  if (aBandHasFloats) {
     // XXX We should allow overflow by up to half a pixel here (bug 21193).
-    if (mAvailSpaceRect.width < aFloatSize.width) {
+    if (aFloatAvailableSpace.width < aFloatSize.width) {
       // The available width is too narrow (and its been impacted by a
       // prior float)
       result = PR_FALSE;
     }
   }
 
   if (!result)
     return result;
 
   // At this point we know that there is enough horizontal space for
   // the float (somewhere). Lets see if there is enough vertical
   // space.
-  if (NSCoordGreaterThan(aFloatSize.height, mAvailSpaceRect.height)) {
+  if (NSCoordGreaterThan(aFloatSize.height, aFloatAvailableSpace.height)) {
     // The available height is too short. However, its possible that
     // there is enough open space below which is not impacted by a
     // float.
     //
     // Compute the X coordinate for the float based on its float
     // type, assuming its placed on the current line. This is
     // where the float will be placed horizontally if it can go
     // here.
     nscoord xa;
     if (NS_STYLE_FLOAT_LEFT == aFloats) {
-      xa = mAvailSpaceRect.x;
+      xa = aFloatAvailableSpace.x;
     }
     else {
-      xa = mAvailSpaceRect.XMost() - aFloatSize.width;
+      xa = aFloatAvailableSpace.XMost() - aFloatSize.width;
 
       // In case the float is too big, don't go past the left edge
       // XXXldb This seems wrong, but we might want to fix bug 6976
       // first.
-      if (xa < mAvailSpaceRect.x) {
-        xa = mAvailSpaceRect.x;
+      if (xa < aFloatAvailableSpace.x) {
+        xa = aFloatAvailableSpace.x;
       }
     }
     nscoord xb = xa + aFloatSize.width;
 
     // Calculate the top and bottom y coordinates, again assuming
     // that the float is placed on the current line.
     const nsMargin& borderPadding = BorderPadding();
     nscoord ya = mY - borderPadding.top;
@@ -708,47 +713,48 @@ nsBlockReflowState::CanPlaceFloat(const 
       // containing block is the content edge of the block box, this
       // means the margin edge of the float can't be higher than the
       // content edge of the block that contains it.)
       ya = 0;
     }
     nscoord yb = ya + aFloatSize.height;
 
     nscoord saveY = mY;
+    nsRect floatAvailableSpace(aFloatAvailableSpace);
     for (;;) {
       // Get the available space at the new Y coordinate
-      if (mAvailSpaceRect.height <= 0) {
+      if (floatAvailableSpace.height <= 0) {
         // there is no more available space. We lose.
         result = PR_FALSE;
         break;
       }
 
-      mY += mAvailSpaceRect.height;
-      GetAvailableSpace(mY, aForceFit);
+      mY += floatAvailableSpace.height;
+      PRBool bandHasFloats =
+        GetFloatAvailableSpace(mY, aForceFit, floatAvailableSpace);
 
-      if (mBandHasFloats) {
-        if ((xa < mAvailSpaceRect.x) || (xb > mAvailSpaceRect.XMost())) {
+      if (bandHasFloats) {
+        if (xa < floatAvailableSpace.x ||
+            xb > floatAvailableSpace.XMost()) {
           // The float can't go here.
           result = PR_FALSE;
           break;
         }
       }
 
       // See if there is now enough height for the float.
-      if (yb <= mY + mAvailSpaceRect.height) {
+      if (yb <= mY + floatAvailableSpace.height) {
         // Winner. The bottom Y coordinate of the float is in
         // this band.
         break;
       }
     }
 
-    // Restore Y coordinate and available space information
-    // regardless of the outcome.
+    // Restore Y coordinate
     mY = saveY;
-    GetAvailableSpace(mY, aForceFit);
   }
 
   return result;
 }
 
 PRBool
 nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache*   aFloatCache,
                                       PRBool*         aIsLeftFloat,
@@ -779,24 +785,27 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // See if the float should clear any preceding floats...
   // XXX We need to mark this float somehow so that it gets reflowed
   // when floats are inserted before it.
   if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
     // XXXldb Does this handle vertical margins correctly?
     mY = ClearFloats(mY, floatDisplay->mBreakType);
   }
     // Get the band of available space
-  GetAvailableSpace(mY, aForceFit);
+  nsRect floatAvailableSpace;
+  PRBool bandHasFloats =
+    GetFloatAvailableSpace(mY, aForceFit, floatAvailableSpace);
 
   NS_ASSERTION(floatFrame->GetParent() == mBlock,
                "Float frame has wrong parent");
 
   // Reflow the float
   nsMargin floatMargin;
-  mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus);
+  mBlock->ReflowFloat(*this, floatAvailableSpace, placeholder,
+                      floatMargin, aReflowStatus);
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsRect region = floatFrame->GetRect();
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
     printf("flowed float: ");
     nsFrame::ListTag(stdout, floatFrame);
     printf(" (%d,%d,%d,%d)\n",
@@ -812,29 +821,31 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // block if possible (CSS2 spec section 9.5.1, see the rule list).
   NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) ||
 	       (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats),
 	       "invalid float type");
 
   // Can the float fit here?
   PRBool keepFloatOnSameLine = PR_FALSE;
 
-  while (!CanPlaceFloat(floatSize, floatDisplay->mFloats, aForceFit)) {
-    if (mAvailSpaceRect.height <= 0) {
+  while (!CanPlaceFloat(floatSize, floatDisplay->mFloats, floatAvailableSpace,
+                        bandHasFloats, aForceFit)) {
+    if (floatAvailableSpace.height <= 0) {
       // No space, nowhere to put anything.
       mY = saveY;
       return PR_FALSE;
     }
 
     // Nope. try to advance to the next band.
     if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
           eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
 
-      mY += mAvailSpaceRect.height;
-      GetAvailableSpace(mY, aForceFit);
+      mY += floatAvailableSpace.height;
+      bandHasFloats =
+        GetFloatAvailableSpace(mY, aForceFit, floatAvailableSpace);
     } else {
       // This quirk matches the one in nsBlockFrame::ReflowFloat
       // IE handles float tables in a very special way
 
       // see if the previous float is also a table and has "align"
       nsFloatCache* fc = mCurrentLineFloats.Head();
       nsIFrame* prevFrame = nsnull;
       while (fc) {
@@ -862,23 +873,25 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
               // since we cannot evangelize the world
               break;
             }
           }
         }
       }
 
       // the table does not fit anymore in this line so advance to next band 
-      mY += mAvailSpaceRect.height;
-      GetAvailableSpace(mY, aForceFit);
+      mY += floatAvailableSpace.height;
+      bandHasFloats =
+        GetFloatAvailableSpace(mY, aForceFit, floatAvailableSpace);
       // reflow the float again now since we have more space
       // XXXldb We really don't need to Reflow in a loop, we just need
       // to ComputeSize in a loop (once ComputeSize depends on
       // availableWidth, which should make this work again).
-      mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus);
+      mBlock->ReflowFloat(*this, floatAvailableSpace, placeholder,
+                          floatMargin, aReflowStatus);
       // Get the floats bounding box and margin information
       floatSize = floatFrame->GetSize() +
                      nsSize(floatMargin.LeftRight(), floatMargin.TopBottom());
     }
   }
   // If the float is continued, it will get the same absolute x value as its prev-in-flow
 
   // We don't worry about the geometry of the prev in flow, let the continuation
@@ -887,28 +900,28 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // Assign an x and y coordinate to the float. Note that the x,y
   // coordinates are computed <b>relative to the translation in the
   // spacemanager</b> which means that the impacted region will be
   // <b>inside</b> the border/padding area.
   PRBool isLeftFloat;
   nscoord floatX, floatY;
   if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) {
     isLeftFloat = PR_TRUE;
-    floatX = mAvailSpaceRect.x;
+    floatX = floatAvailableSpace.x;
   }
   else {
     isLeftFloat = PR_FALSE;
     if (!keepFloatOnSameLine) {
-      floatX = mAvailSpaceRect.XMost() - floatSize.width;
+      floatX = floatAvailableSpace.XMost() - floatSize.width;
     } 
     else {
       // this is the IE quirk (see few lines above)
       // the table is kept in the same line: don't let it overlap the
       // previous float 
-      floatX = mAvailSpaceRect.x;
+      floatX = floatAvailableSpace.x;
     }
   }
   *aIsLeftFloat = isLeftFloat;
   const nsMargin& borderPadding = BorderPadding();
   floatY = mY - borderPadding.top;
   if (floatY < 0) {
     // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
     // be higher than the top of its containing block."  (Since the
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -117,17 +117,19 @@ public:
                    nsPlaceholderFrame* aPlaceholderFrame,
                    nscoord             aAvailableWidth,
                    nsReflowStatus&     aReflowStatus);
   PRBool AddFloat(nsLineLayout&       aLineLayout,
                   nsPlaceholderFrame* aPlaceholderFrame,
                   PRBool              aInitialReflow,
                   nscoord             aAvailableWidth,
                   nsReflowStatus&     aReflowStatus);
-  PRBool CanPlaceFloat(const nsSize& aFloatSize, PRUint8 aFloats, PRBool aForceFit);
+  PRBool CanPlaceFloat(const nsSize& aFloatSize, PRUint8 aFloats,
+                       const nsRect& aFloatAvailableSpace,
+                       PRBool aBandHasFloats, PRBool aForceFit);
   PRBool FlowAndPlaceFloat(nsFloatCache*   aFloatCache,
                            PRBool*         aIsLeftFloat,
                            nsReflowStatus& aReflowStatus,
                            PRBool          aForceFit);
   PRBool PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats, PRBool aForceFit);
 
   // Returns the first coordinate >= aY that clears the
   // floats indicated by aBreakType and has enough width between floats