Bug 1463745 Part 2: Change nsFlowAreaRect to also track whether it may widen in the block direction. draft
authorBrad Werth <bwerth@mozilla.com>
Wed, 30 May 2018 11:07:18 -0700
changeset 806318 9a0311b08b2a57c8c5255aaa4e57e69ec8920351
parent 806317 ece8a37a04528866a87d07ab6876e9f00ae87bb0
child 806319 bb26a70416fc1e63857d18e873ebb99634c23974
push id112855
push userbwerth@mozilla.com
push dateFri, 08 Jun 2018 23:06:18 +0000
bugs1463745
milestone62.0a1
Bug 1463745 Part 2: Change nsFlowAreaRect to also track whether it may widen in the block direction. MozReview-Commit-ID: FWKQEFDBFgr
layout/generic/BlockReflowInput.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsFloatManager.cpp
layout/generic/nsFloatManager.h
--- a/layout/generic/BlockReflowInput.cpp
+++ b/layout/generic/BlockReflowInput.cpp
@@ -220,17 +220,17 @@ GetBEndMarginClone(nsIFrame* aFrame,
 void
 BlockReflowInput::ComputeBlockAvailSpace(nsIFrame* aFrame,
                                          const nsFlowAreaRect& aFloatAvailableSpace,
                                          bool aBlockAvoidsFloats,
                                          LogicalRect& aResult)
 {
 #ifdef REALLY_NOISY_REFLOW
   printf("CBAS frame=%p has floats %d\n",
-         aFrame, aFloatAvailableSpace.mHasFloats);
+         aFrame, aFloatAvailableSpace.HasFloats());
 #endif
   WritingMode wm = mReflowInput.GetWritingMode();
   aResult.BStart(wm) = mBCoord;
   aResult.BSize(wm) = mFlags.mHasUnconstrainedBSize
     ? NS_UNCONSTRAINEDSIZE
     : mReflowInput.AvailableBSize() - mBCoord
       - GetBEndMarginClone(aFrame, mReflowInput.mRenderingContext, mContentArea, wm);
   // mBCoord might be greater than mBEndEdge if the block's top margin pushes
@@ -248,17 +248,17 @@ BlockReflowInput::ComputeBlockAvailSpace
   // true but nsBlockFrame::BlockCanIntersectFloats is false,
   // nsBlockFrame::ISizeToClearPastFloats would need to use the
   // shrink-wrap formula, max(MIN_ISIZE, min(avail width, PREF_ISIZE))
   // rather than just using MIN_ISIZE.
   NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) ==
                  !aBlockAvoidsFloats,
                "unexpected replaced width");
   if (!aBlockAvoidsFloats) {
-    if (aFloatAvailableSpace.mHasFloats) {
+    if (aFloatAvailableSpace.HasFloats()) {
       // Use the float-edge property to determine how the child block
       // will interact with the float.
       const nsStyleBorder* borderStyle = aFrame->StyleBorder();
       switch (borderStyle->mFloatEdge) {
         default:
         case StyleFloatEdge::ContentBox:  // content and only content does runaround of floats
           // The child block will flow around the float. Therefore
           // give it all of the available space.
@@ -296,17 +296,17 @@ BlockReflowInput::ComputeBlockAvailSpace
          aResult.ISize(wm), aResult.BSize(wm));
 #endif
 }
 
 bool
 BlockReflowInput::ReplacedBlockFitsInAvailSpace(nsIFrame* aReplacedBlock,
                             const nsFlowAreaRect& aFloatAvailableSpace) const
 {
-  if (!aFloatAvailableSpace.mHasFloats) {
+  if (!aFloatAvailableSpace.HasFloats()) {
     // If there aren't any floats here, then we always fit.
     // We check this before calling ISizeToClearPastFloats, which is
     // somewhat expensive.
     return true;
   }
   WritingMode wm = mReflowInput.GetWritingMode();
   nsBlockFrame::ReplacedElementISizeToClear replacedISize =
     nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect,
@@ -350,17 +350,17 @@ BlockReflowInput::GetFloatAvailableSpace
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
     printf("%s: band=%d,%d,%d,%d hasfloats=%d\n", __func__,
            result.mRect.IStart(wm), result.mRect.BStart(wm),
-           result.mRect.ISize(wm), result.mRect.BSize(wm), result.mHasFloats);
+           result.mRect.ISize(wm), result.mRect.BSize(wm), result.HasFloats());
   }
 #endif
   return result;
 }
 
 nsFlowAreaRect
 BlockReflowInput::GetFloatAvailableSpaceForBSize(
                       nscoord aBCoord, nscoord aBSize,
@@ -385,17 +385,17 @@ BlockReflowInput::GetFloatAvailableSpace
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
     printf("%s: space=%d,%d,%d,%d hasfloats=%d\n", __func__,
            result.mRect.IStart(wm), result.mRect.BStart(wm),
-           result.mRect.ISize(wm), result.mRect.BSize(wm), result.mHasFloats);
+           result.mRect.ISize(wm), result.mRect.BSize(wm), result.HasFloats());
   }
 #endif
   return result;
 }
 
 /*
  * Reconstruct the vertical margin before the line |aLine| in order to
  * do an incremental reflow that begins with |aLine| without reflowing
@@ -664,17 +664,17 @@ bool
 BlockReflowInput::CanPlaceFloat(nscoord aFloatISize,
                                   const nsFlowAreaRect& aFloatAvailableSpace)
 {
   // A float fits at a given block-dir position if there are no floats
   // at its inline-dir position (no matter what its inline size) or if
   // its inline size fits in the space remaining after prior floats have
   // been placed.
   // FIXME: We should allow overflow by up to half a pixel here (bug 21193).
-  return !aFloatAvailableSpace.mHasFloats ||
+  return !aFloatAvailableSpace.HasFloats() ||
     aFloatAvailableSpace.mRect.ISize(mReflowInput.GetWritingMode()) >=
       aFloatISize;
 }
 
 // Return the inline-size that the float (including margins) will take up
 // in the writing mode of the containing block. If this returns
 // NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that
 // has block-size:auto, and we'll need to actually reflow it to find out
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -2065,23 +2065,23 @@ nsBlockFrame::PropagateFloatDamage(Block
       bool wasImpactedByFloat = aLine->IsImpactedByFloat();
       nsFlowAreaRect floatAvailableSpace =
         aState.GetFloatAvailableSpaceForBSize(aLine->BStart() + aDeltaBCoord,
                                               aLine->BSize(),
                                               nullptr);
 
 #ifdef REALLY_NOISY_REFLOW
     printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
-           this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
+           this, wasImpactedByFloat, floatAvailableSpace.HasFloats());
 #endif
 
       // Mark the line dirty if it was or is affected by a float
       // We actually only really need to reflow if the amount of impact
       // changes, but that's not straightforward to check
-      if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
+      if (wasImpactedByFloat || floatAvailableSpace.HasFloats()) {
         aLine->MarkDirty();
       }
     }
   }
 }
 
 static bool LineHasClear(nsLineBox* aLine) {
   return aLine->IsBlock()
@@ -2817,17 +2817,17 @@ nsBlockFrame::ReflowLine(BlockReflowInpu
 
     // Store the line's float edges for text-overflow analysis if needed.
     aLine->ClearFloatEdges();
     if (aState.mFlags.mCanHaveTextOverflow) {
       WritingMode wm = aLine->mWritingMode;
       nsFlowAreaRect r = aState.GetFloatAvailableSpaceForBSize(aLine->BStart(),
                                                                aLine->BSize(),
                                                                nullptr);
-      if (r.mHasFloats) {
+      if (r.HasFloats()) {
         LogicalRect so =
           aLine->GetOverflowArea(eScrollableOverflow, wm, aLine->mContainerSize);
         nscoord s = r.mRect.IStart(wm);
         nscoord e = r.mRect.IEnd(wm);
         if (so.IEnd(wm) > e || so.IStart(wm) < s) {
           // This line is overlapping a float - store the edges marking the area
           // between the floats for text-overflow analysis.
           aLine->SetFloatEdges(s, e);
@@ -3428,18 +3428,18 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
     Maybe<ReflowInput> blockHtmlRI;
     blockHtmlRI.emplace(
       aState.mPresContext, aState.mReflowInput, frame,
       availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
 
     nsFloatManager::SavedState floatManagerState;
     nsReflowStatus frameReflowStatus;
     do {
-      if (floatAvailableSpace.mHasFloats) {
-        // Set if floatAvailableSpace.mHasFloats is true for any
+      if (floatAvailableSpace.HasFloats()) {
+        // Set if floatAvailableSpace.HasFloats() is true for any
         // iteration of the loop.
         aLine->SetLineIsImpactedByFloat(true);
       }
 
       // We might need to store into mDiscoveredClearance later if it's
       // currently null; we want to overwrite any writes that
       // brc.ReflowBlock() below does, so we need to remember now
       // whether it's empty.
@@ -3595,17 +3595,17 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
 
       // Try to place the child block.
       // Don't force the block to fit if we have positive clearance, because
       // pushing it to the next page would give it more room.
       // Don't force the block to fit if it's impacted by a float. If it is,
       // then pushing it to the next page would give it more room. Note that
       // isImpacted doesn't include impact from the block's own floats.
       bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
-        !floatAvailableSpace.mHasFloats;
+        !floatAvailableSpace.HasFloats();
       nsCollapsingMargin collapsedBEndMargin;
       nsOverflowAreas overflowAreas;
       *aKeepReflowGoing = brc.PlaceBlock(*blockHtmlRI, forceFit, aLine.get(),
                                          collapsedBEndMargin,
                                          overflowAreas,
                                          frameReflowStatus);
       if (!frameReflowStatus.IsFullyComplete() &&
           ShouldAvoidBreakInside(aState.mReflowInput)) {
@@ -3885,21 +3885,21 @@ nsBlockFrame::DoReflowInlineFrames(Block
                                    bool aAllowPullUp)
 {
   // Forget all of the floats on the line
   aLine->FreeFloats(aState.mFloatCacheFreeList);
   aState.mFloatOverflowAreas.Clear();
 
   // We need to set this flag on the line if any of our reflow passes
   // are impacted by floats.
-  if (aFloatAvailableSpace.mHasFloats)
+  if (aFloatAvailableSpace.HasFloats())
     aLine->SetLineIsImpactedByFloat(true);
 #ifdef REALLY_NOISY_REFLOW
   printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
-         this, aFloatAvailableSpace.mHasFloats);
+         this, aFloatAvailableSpace.HasFloats());
 #endif
 
   WritingMode outerWM = aState.mReflowInput.GetWritingMode();
   WritingMode lineWM = WritingModeForLine(outerWM, aLine->mFirstChild);
   LogicalRect lineRect =
     aFloatAvailableSpace.mRect.ConvertTo(lineWM, outerWM,
                                          aState.ContainerSize());
 
@@ -3915,17 +3915,17 @@ nsBlockFrame::DoReflowInlineFrames(Block
   }
 
   // Make sure to enable resize optimization before we call BeginLineReflow
   // because it might get disabled there
   aLine->EnableResizeReflowOptimization();
 
   aLineLayout.BeginLineReflow(iStart, aState.mBCoord,
                               availISize, availBSize,
-                              aFloatAvailableSpace.mHasFloats,
+                              aFloatAvailableSpace.HasFloats(),
                               false, /*XXX isTopOfPage*/
                               lineWM, aState.mContainerSize);
 
   aState.mFlags.mIsLineLayoutEmpty = false;
 
   // XXX Unfortunately we need to know this before reflowing the first
   // inline frame in the line. FIX ME.
   if ((0 == aLineLayout.GetLineNumber()) &&
@@ -3937,17 +3937,17 @@ nsBlockFrame::DoReflowInlineFrames(Block
                  GetPrevContinuation()),
                "first letter child bit should only be on first continuation");
 
   // Reflow the frames that are already on the line first
   LineReflowStatus lineReflowStatus = LineReflowStatus::OK;
   int32_t i;
   nsIFrame* frame = aLine->mFirstChild;
 
-  if (aFloatAvailableSpace.mHasFloats) {
+  if (aFloatAvailableSpace.HasFloats()) {
     // There is a soft break opportunity at the start of the line, because
     // we can always move this line down below float(s).
     if (aLineLayout.NotifyOptionalBreakPosition(
             frame, 0, true, gfxBreakPriority::eNormalBreak)) {
       lineReflowStatus = LineReflowStatus::RedoNextBand;
     }
   }
 
@@ -4038,17 +4038,17 @@ nsBlockFrame::DoReflowInlineFrames(Block
     // What we do is to advance past the first float we find and
     // then reflow the line all over again.
     NS_ASSERTION(NS_UNCONSTRAINEDSIZE !=
                  aFloatAvailableSpace.mRect.BSize(outerWM),
                  "unconstrained block size on totally empty line");
 
     // See the analogous code for blocks in BlockReflowInput::ClearFloats.
     if (aFloatAvailableSpace.mRect.BSize(outerWM) > 0) {
-      NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
+      NS_ASSERTION(aFloatAvailableSpace.HasFloats(),
                    "redo line on totally empty line with non-empty band...");
       // We should never hit this case if we've placed floats on the
       // line; if we have, then the GetFloatAvailableSpace call is wrong
       // and needs to happen after the caller pops the space manager
       // state.
       aState.FloatManager()->AssertStateMatches(aFloatStateBeforeLine);
       aState.mBCoord += aFloatAvailableSpace.mRect.BSize(outerWM);
       aFloatAvailableSpace = aState.GetFloatAvailableSpace();
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -139,17 +139,18 @@ nsFloatManager::GetFlowArea(WritingMode 
   }
 
   // If there are no floats at all, or we're below the last one, return
   // quickly.
   if (floatCount == 0 ||
       (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
        mFloats[floatCount-1].mRightBEnd <= blockStart)) {
     return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
-                          aContentArea.ISize(aWM), aBSize, false);
+                          aContentArea.ISize(aWM), aBSize,
+                          nsFlowAreaRectFlags::NO_FLAGS);
   }
 
   nscoord blockEnd;
   if (aBSize == nscoord_MAX) {
     // This warning (and the two below) are possible to hit on pages
     // with really large objects.
     NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height");
     blockEnd = nscoord_MAX;
@@ -165,16 +166,17 @@ nsFloatManager::GetFlowArea(WritingMode 
   if (lineRight < lineLeft) {
     NS_WARNING("bad value");
     lineRight = lineLeft;
   }
 
   // Walk backwards through the floats until we either hit the front of
   // the list or we're above |blockStart|.
   bool haveFloats = false;
+  bool mayWiden = false;
   for (uint32_t i = floatCount; i > 0; --i) {
     const FloatInfo &fi = mFloats[i-1];
     if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
       // There aren't any more floats that could intersect this band.
       break;
     }
     if (fi.IsEmpty(aShapeType)) {
       // Ignore empty float areas.
@@ -213,25 +215,30 @@ nsFloatManager::GetFlowArea(WritingMode 
           fi.LineRight(aShapeType, blockStart, bandBlockEnd);
         if (lineRightEdge > lineLeft) {
           lineLeft = lineRightEdge;
           // Only set haveFloats to true if the float is inside our
           // containing block.  This matches the spec for what some
           // callers want and disagrees for other callers, so we should
           // probably provide better information at some point.
           haveFloats = true;
+
+          // Our area may widen in the block direction if this float may
+          // narrow in the block direction.
+          mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
         }
       } else {
         // A right float
         nscoord lineLeftEdge =
           fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
         if (lineLeftEdge < lineRight) {
           lineRight = lineLeftEdge;
           // See above.
           haveFloats = true;
+          mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
         }
       }
 
       // Shrink our band's height if needed.
       if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) {
         blockEnd = floatBEnd;
       }
     }
@@ -240,18 +247,22 @@ nsFloatManager::GetFlowArea(WritingMode 
   nscoord blockSize = (blockEnd == nscoord_MAX) ?
                        nscoord_MAX : (blockEnd - blockStart);
   // convert back from LineLeft/Right to IStart
   nscoord inlineStart = aWM.IsBidiLTR()
                         ? lineLeft - mLineLeft
                         : mLineLeft - lineRight +
                           LogicalSize(aWM, aContainerSize).ISize(aWM);
 
+  nsFlowAreaRectFlags flags =
+    (haveFloats ? nsFlowAreaRectFlags::HAS_FLOATS : nsFlowAreaRectFlags::NO_FLAGS) |
+    (mayWiden ? nsFlowAreaRectFlags::MAY_WIDEN : nsFlowAreaRectFlags::NO_FLAGS);
+
   return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
-                        lineRight - lineLeft, blockSize, haveFloats);
+                        lineRight - lineLeft, blockSize, flags);
 }
 
 void
 nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
                          WritingMode aWM, const nsSize& aContainerSize)
 {
   CHECK_BLOCK_AND_LINE_DIR(aWM);
   NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -5,49 +5,67 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* class that manages rules for positioning floats */
 
 #ifndef nsFloatManager_h_
 #define nsFloatManager_h_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/TypedEnumBits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WritingModes.h"
 #include "nsCoord.h"
 #include "nsFrameList.h" // for DEBUG_FRAME_DUMP
 #include "nsIntervalSet.h"
 #include "nsPoint.h"
 #include "nsTArray.h"
 
 class nsIPresShell;
 class nsIFrame;
 class nsPresContext;
 namespace mozilla {
 struct ReflowInput;
 class StyleBasicShape;
 } // namespace mozilla
 
+enum class nsFlowAreaRectFlags : uint32_t {
+  NO_FLAGS   = 0,
+  HAS_FLOATS = 1 << 0,
+  MAY_WIDEN  = 1 << 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsFlowAreaRectFlags)
+
 /**
  * The available space for content not occupied by floats is divided
  * into a sequence of rectangles in the block direction.  However, we
  * need to know not only the rectangle, but also whether it was reduced
  * (from the content rectangle) by floats that actually intruded into
- * the content rectangle.
+ * the content rectangle. If it has been reduced by floats, then we also
+ * track whether the flow area might widen as the floats narrow in the
+ * block direction.
  */
 struct nsFlowAreaRect {
   mozilla::LogicalRect mRect;
-  bool mHasFloats;
+
+  nsFlowAreaRectFlags mAreaFlags;
 
   nsFlowAreaRect(mozilla::WritingMode aWritingMode,
                  nscoord aICoord, nscoord aBCoord,
                  nscoord aISize, nscoord aBSize,
-                 bool aHasFloats)
+                 nsFlowAreaRectFlags aAreaFlags)
     : mRect(aWritingMode, aICoord, aBCoord, aISize, aBSize)
-    , mHasFloats(aHasFloats) {}
+    , mAreaFlags(aAreaFlags) {}
+
+  bool HasFloats() const {
+    return (bool)(mAreaFlags & nsFlowAreaRectFlags::HAS_FLOATS);
+  }
+  bool MayWiden() const {
+    return (bool)(mAreaFlags & nsFlowAreaRectFlags::MAY_WIDEN);
+  }
 };
 
 #define NS_FLOAT_MANAGER_CACHE_SIZE 64
 
 /**
  * nsFloatManager is responsible for implementing CSS's rules for
  * positioning floats. An nsFloatManager object is created during reflow for
  * any block with NS_BLOCK_FLOAT_MGR. During reflow, the float manager for