layout/generic/nsBlockReflowState.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 // vim:cindent:ts=2:et:sw=2:
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /* state used in reflow of block frames */
     9 #include "nsBlockReflowState.h"
    11 #include "mozilla/DebugOnly.h"
    13 #include "nsBlockFrame.h"
    14 #include "nsLineLayout.h"
    15 #include "nsPresContext.h"
    16 #include "nsIFrameInlines.h"
    17 #include "mozilla/AutoRestore.h"
    18 #include <algorithm>
    20 #ifdef DEBUG
    21 #include "nsBlockDebugFlags.h"
    22 #endif
    24 using namespace mozilla;
    25 using namespace mozilla::layout;
    27 nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
    28                                        nsPresContext* aPresContext,
    29                                        nsBlockFrame* aFrame,
    30                                        bool aTopMarginRoot,
    31                                        bool aBottomMarginRoot,
    32                                        bool aBlockNeedsFloatManager,
    33                                        nscoord aConsumedHeight)
    34   : mBlock(aFrame),
    35     mPresContext(aPresContext),
    36     mReflowState(aReflowState),
    37     mPushedFloats(nullptr),
    38     mOverflowTracker(nullptr),
    39     mPrevBottomMargin(),
    40     mLineNumber(0),
    41     mFlags(0),
    42     mFloatBreakType(NS_STYLE_CLEAR_NONE),
    43     mConsumedHeight(aConsumedHeight)
    44 {
    45   SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr);
    46   SetFlag(BRS_ISOVERFLOWCONTAINER,
    47           IS_TRUE_OVERFLOW_CONTAINER(aFrame));
    49   const nsMargin& borderPadding = BorderPadding();
    50   mContainerWidth = aReflowState.ComputedWidth() +
    51                     aReflowState.ComputedPhysicalBorderPadding().LeftRight();
    53   if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) {
    54     SetFlag(BRS_ISTOPMARGINROOT, true);
    55   }
    56   if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) {
    57     SetFlag(BRS_ISBOTTOMMARGINROOT, true);
    58   }
    59   if (GetFlag(BRS_ISTOPMARGINROOT)) {
    60     SetFlag(BRS_APPLYTOPMARGIN, true);
    61   }
    62   if (aBlockNeedsFloatManager) {
    63     SetFlag(BRS_FLOAT_MGR, true);
    64   }
    66   mFloatManager = aReflowState.mFloatManager;
    68   NS_ASSERTION(mFloatManager,
    69                "FloatManager should be set in nsBlockReflowState" );
    70   if (mFloatManager) {
    71     // Save the coordinate system origin for later.
    72     mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY);
    73     mFloatManager->PushState(&mFloatManagerStateBefore); // never popped
    74   }
    76   mReflowStatus = NS_FRAME_COMPLETE;
    78   mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow());
    80   NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedWidth(),
    81                    "have unconstrained width; this should only result from "
    82                    "very large sizes, not attempts at intrinsic width "
    83                    "calculation");
    84   mContentArea.width = aReflowState.ComputedWidth();
    86   // Compute content area height. Unlike the width, if we have a
    87   // specified style height we ignore it since extra content is
    88   // managed by the "overflow" property. When we don't have a
    89   // specified style height then we may end up limiting our height if
    90   // the availableHeight is constrained (this situation occurs when we
    91   // are paginated).
    92   if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight()) {
    93     // We are in a paginated situation. The bottom edge is just inside
    94     // the bottom border and padding. The content area height doesn't
    95     // include either border or padding edge.
    96     mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom;
    97     mContentArea.height = std::max(0, mBottomEdge - borderPadding.top);
    98   }
    99   else {
   100     // When we are not in a paginated situation then we always use
   101     // an constrained height.
   102     SetFlag(BRS_UNCONSTRAINEDHEIGHT, true);
   103     mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE;
   104   }
   105   mContentArea.x = borderPadding.left;
   106   mY = mContentArea.y = borderPadding.top;
   108   mPrevChild = nullptr;
   109   mCurrentLine = aFrame->end_lines();
   111   mMinLineHeight = aReflowState.CalcLineHeight();
   112 }
   114 nscoord
   115 nsBlockReflowState::GetConsumedHeight()
   116 {
   117   if (mConsumedHeight == NS_INTRINSICSIZE) {
   118     mConsumedHeight = mBlock->GetConsumedHeight();
   119   }
   121   return mConsumedHeight;
   122 }
   124 void
   125 nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame,
   126                                                          const nsRect& aFloatAvailableSpace,
   127                                                          nscoord& aLeftResult,
   128                                                          nscoord& aRightResult)
   129 {
   130   // The frame is clueless about the float manager and therefore we
   131   // only give it free space. An example is a table frame - the
   132   // tables do not flow around floats.
   133   // However, we can let its margins intersect floats.
   134   NS_ASSERTION(aFloatAvailableSpace.x >= mContentArea.x, "bad avail space rect x");
   135   NS_ASSERTION(aFloatAvailableSpace.width == 0 ||
   136                aFloatAvailableSpace.XMost() <= mContentArea.XMost(),
   137                "bad avail space rect width");
   139   nscoord leftOffset, rightOffset;
   140   if (aFloatAvailableSpace.width == mContentArea.width) {
   141     // We don't need to compute margins when there are no floats around.
   142     leftOffset = 0;
   143     rightOffset = 0;
   144   } else {
   145     nsMargin frameMargin;
   146     nsCSSOffsetState os(aFrame, mReflowState.rendContext, mContentArea.width);
   147     frameMargin = os.ComputedPhysicalMargin();
   149     nscoord leftFloatXOffset = aFloatAvailableSpace.x - mContentArea.x;
   150     leftOffset = std::max(leftFloatXOffset, frameMargin.left) -
   151                  frameMargin.left;
   152     leftOffset = std::max(leftOffset, 0); // in case of negative margin
   153     nscoord rightFloatXOffset =
   154       mContentArea.XMost() - aFloatAvailableSpace.XMost();
   155     rightOffset = std::max(rightFloatXOffset, frameMargin.right) -
   156                   frameMargin.right;
   157     rightOffset = std::max(rightOffset, 0); // in case of negative margin
   158   }
   159   aLeftResult = leftOffset;
   160   aRightResult = rightOffset;
   161 }
   163 // Compute the amount of available space for reflowing a block frame
   164 // at the current Y coordinate. This method assumes that
   165 // GetAvailableSpace has already been called.
   166 void
   167 nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
   168                                            const nsStyleDisplay* aDisplay,
   169                                            const nsFlowAreaRect& aFloatAvailableSpace,
   170                                            bool aBlockAvoidsFloats,
   171                                            nsRect& aResult)
   172 {
   173 #ifdef REALLY_NOISY_REFLOW
   174   printf("CBAS frame=%p has floats %d\n",
   175          aFrame, aFloatAvailableSpace.mHasFloats);
   176 #endif
   177   aResult.y = mY;
   178   aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT)
   179     ? NS_UNCONSTRAINEDSIZE
   180     : mReflowState.AvailableHeight() - mY;
   181   // mY might be greater than mBottomEdge if the block's top margin pushes
   182   // it off the page/column. Negative available height can confuse other code
   183   // and is nonsense in principle.
   185   // XXX Do we really want this condition to be this restrictive (i.e.,
   186   // more restrictive than it used to be)?  The |else| here is allowed
   187   // by the CSS spec, but only out of desperation given implementations,
   188   // and the behavior it leads to is quite undesirable (it can cause
   189   // things to become extremely narrow when they'd fit quite well a
   190   // little bit lower).  Should the else be a quirk or something that
   191   // applies to a specific set of frame classes and no new ones?
   192   // If we did that, then for those frames where the condition below is
   193   // true but nsBlockFrame::BlockCanIntersectFloats is false,
   194   // nsBlockFrame::WidthToClearPastFloats would need to use the
   195   // shrink-wrap formula, max(MIN_WIDTH, min(avail width, PREF_WIDTH))
   196   // rather than just using MIN_WIDTH.
   197   NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) == 
   198                  !aBlockAvoidsFloats,
   199                "unexpected replaced width");
   200   if (!aBlockAvoidsFloats) {
   201     if (aFloatAvailableSpace.mHasFloats) {
   202       // Use the float-edge property to determine how the child block
   203       // will interact with the float.
   204       const nsStyleBorder* borderStyle = aFrame->StyleBorder();
   205       switch (borderStyle->mFloatEdge) {
   206         default:
   207         case NS_STYLE_FLOAT_EDGE_CONTENT:  // content and only content does runaround of floats
   208           // The child block will flow around the float. Therefore
   209           // give it all of the available space.
   210           aResult.x = mContentArea.x;
   211           aResult.width = mContentArea.width;
   212           break;
   213         case NS_STYLE_FLOAT_EDGE_MARGIN:
   214           {
   215             // The child block's margins should be placed adjacent to,
   216             // but not overlap the float.
   217             aResult.x = aFloatAvailableSpace.mRect.x;
   218             aResult.width = aFloatAvailableSpace.mRect.width;
   219           }
   220           break;
   221       }
   222     }
   223     else {
   224       // Since there are no floats present the float-edge property
   225       // doesn't matter therefore give the block element all of the
   226       // available space since it will flow around the float itself.
   227       aResult.x = mContentArea.x;
   228       aResult.width = mContentArea.width;
   229     }
   230   }
   231   else {
   232     nscoord leftOffset, rightOffset;
   233     ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect,
   234                                          leftOffset, rightOffset);
   235     aResult.x = mContentArea.x + leftOffset;
   236     aResult.width = mContentArea.width - leftOffset - rightOffset;
   237   }
   239 #ifdef REALLY_NOISY_REFLOW
   240   printf("  CBAS: result %d %d %d %d\n", aResult.x, aResult.y, aResult.width, aResult.height);
   241 #endif
   242 }
   244 nsFlowAreaRect
   245 nsBlockReflowState::GetFloatAvailableSpaceWithState(
   246                       nscoord aY,
   247                       nsFloatManager::SavedState *aState) const
   248 {
   249 #ifdef DEBUG
   250   // Verify that the caller setup the coordinate system properly
   251   nscoord wx, wy;
   252   mFloatManager->GetTranslation(wx, wy);
   253   NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY),
   254                "bad coord system");
   255 #endif
   257   nscoord height = (mContentArea.height == nscoord_MAX)
   258                      ? nscoord_MAX : std::max(mContentArea.YMost() - aY, 0);
   259   nsFlowAreaRect result =
   260     mFloatManager->GetFlowArea(aY, nsFloatManager::BAND_FROM_POINT,
   261                                height, mContentArea, aState);
   262   // Keep the width >= 0 for compatibility with nsSpaceManager.
   263   if (result.mRect.width < 0)
   264     result.mRect.width = 0;
   266 #ifdef DEBUG
   267   if (nsBlockFrame::gNoisyReflow) {
   268     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   269     printf("GetAvailableSpace: band=%d,%d,%d,%d hasfloats=%d\n",
   270            result.mRect.x, result.mRect.y, result.mRect.width,
   271            result.mRect.height, result.mHasFloats);
   272   }
   273 #endif
   274   return result;
   275 }
   277 nsFlowAreaRect
   278 nsBlockReflowState::GetFloatAvailableSpaceForHeight(
   279                       nscoord aY, nscoord aHeight,
   280                       nsFloatManager::SavedState *aState) const
   281 {
   282 #ifdef DEBUG
   283   // Verify that the caller setup the coordinate system properly
   284   nscoord wx, wy;
   285   mFloatManager->GetTranslation(wx, wy);
   286   NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY),
   287                "bad coord system");
   288 #endif
   290   nsFlowAreaRect result =
   291     mFloatManager->GetFlowArea(aY, nsFloatManager::WIDTH_WITHIN_HEIGHT,
   292                                aHeight, mContentArea, aState);
   293   // Keep the width >= 0 for compatibility with nsSpaceManager.
   294   if (result.mRect.width < 0)
   295     result.mRect.width = 0;
   297 #ifdef DEBUG
   298   if (nsBlockFrame::gNoisyReflow) {
   299     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   300     printf("GetAvailableSpaceForHeight: space=%d,%d,%d,%d hasfloats=%d\n",
   301            result.mRect.x, result.mRect.y, result.mRect.width,
   302            result.mRect.height, result.mHasFloats);
   303   }
   304 #endif
   305   return result;
   306 }
   308 /*
   309  * Reconstruct the vertical margin before the line |aLine| in order to
   310  * do an incremental reflow that begins with |aLine| without reflowing
   311  * the line before it.  |aLine| may point to the fencepost at the end of
   312  * the line list, and it is used this way since we (for now, anyway)
   313  * always need to recover margins at the end of a block.
   314  *
   315  * The reconstruction involves walking backward through the line list to
   316  * find any collapsed margins preceding the line that would have been in
   317  * the reflow state's |mPrevBottomMargin| when we reflowed that line in
   318  * a full reflow (under the rule in CSS2 that all adjacent vertical
   319  * margins of blocks collapse).
   320  */
   321 void
   322 nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine)
   323 {
   324   mPrevBottomMargin.Zero();
   325   nsBlockFrame *block = mBlock;
   327   nsLineList::iterator firstLine = block->begin_lines();
   328   for (;;) {
   329     --aLine;
   330     if (aLine->IsBlock()) {
   331       mPrevBottomMargin = aLine->GetCarriedOutBottomMargin();
   332       break;
   333     }
   334     if (!aLine->IsEmpty()) {
   335       break;
   336     }
   337     if (aLine == firstLine) {
   338       // If the top margin was carried out (and thus already applied),
   339       // set it to zero.  Either way, we're done.
   340       if (!GetFlag(BRS_ISTOPMARGINROOT)) {
   341         mPrevBottomMargin.Zero();
   342       }
   343       break;
   344     }
   345   }
   346 }
   348 void
   349 nsBlockReflowState::SetupPushedFloatList()
   350 {
   351   NS_ABORT_IF_FALSE(!GetFlag(BRS_PROPTABLE_FLOATCLIST) == !mPushedFloats,
   352                     "flag mismatch");
   353   if (!GetFlag(BRS_PROPTABLE_FLOATCLIST)) {
   354     // If we're being re-Reflow'd without our next-in-flow having been
   355     // reflowed, some pushed floats from our previous reflow might
   356     // still be on our pushed floats list.  However, that's
   357     // actually fine, since they'll all end up being stolen and
   358     // reordered into the correct order again.
   359     // (nsBlockFrame::ReflowDirtyLines ensures that any lines with
   360     // pushed floats are reflowed.)
   361     mPushedFloats = mBlock->EnsurePushedFloats();
   362     SetFlag(BRS_PROPTABLE_FLOATCLIST, true);
   363   }
   364 }
   366 void
   367 nsBlockReflowState::AppendPushedFloat(nsIFrame* aFloatCont)
   368 {
   369   SetupPushedFloatList();
   370   aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT);
   371   mPushedFloats->AppendFrame(mBlock, aFloatCont);
   372 }
   374 /**
   375  * Restore information about floats into the float manager for an
   376  * incremental reflow, and simultaneously push the floats by
   377  * |aDeltaY|, which is the amount |aLine| was pushed relative to its
   378  * parent.  The recovery of state is one of the things that makes
   379  * incremental reflow O(N^2) and this state should really be kept
   380  * around, attached to the frame tree.
   381  */
   382 void
   383 nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine,
   384                                   nscoord aDeltaY)
   385 {
   386   if (aLine->HasFloats()) {
   387     // Place the floats into the space-manager again. Also slide
   388     // them, just like the regular frames on the line.
   389     nsFloatCache* fc = aLine->GetFirstFloat();
   390     while (fc) {
   391       nsIFrame* floatFrame = fc->mFloat;
   392       if (aDeltaY != 0) {
   393         floatFrame->MovePositionBy(nsPoint(0, aDeltaY));
   394         nsContainerFrame::PositionFrameView(floatFrame);
   395         nsContainerFrame::PositionChildViews(floatFrame);
   396       }
   397 #ifdef DEBUG
   398       if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
   399         nscoord tx, ty;
   400         mFloatManager->GetTranslation(tx, ty);
   401         nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   402         printf("RecoverFloats: txy=%d,%d (%d,%d) ",
   403                tx, ty, mFloatManagerX, mFloatManagerY);
   404         nsFrame::ListTag(stdout, floatFrame);
   405         nsRect region = nsFloatManager::GetRegionFor(floatFrame);
   406         printf(" aDeltaY=%d region={%d,%d,%d,%d}\n",
   407                aDeltaY, region.x, region.y, region.width, region.height);
   408       }
   409 #endif
   410       mFloatManager->AddFloat(floatFrame,
   411                               nsFloatManager::GetRegionFor(floatFrame));
   412       fc = fc->Next();
   413     }
   414   } else if (aLine->IsBlock()) {
   415     nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager);
   416   }
   417 }
   419 /**
   420  * Everything done in this function is done O(N) times for each pass of
   421  * reflow so it is O(N*M) where M is the number of incremental reflow
   422  * passes.  That's bad.  Don't do stuff here.
   423  *
   424  * When this function is called, |aLine| has just been slid by |aDeltaY|
   425  * and the purpose of RecoverStateFrom is to ensure that the
   426  * nsBlockReflowState is in the same state that it would have been in
   427  * had the line just been reflowed.
   428  *
   429  * Most of the state recovery that we have to do involves floats.
   430  */
   431 void
   432 nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine,
   433                                      nscoord aDeltaY)
   434 {
   435   // Make the line being recovered the current line
   436   mCurrentLine = aLine;
   438   // Place floats for this line into the float manager
   439   if (aLine->HasFloats() || aLine->IsBlock()) {
   440     RecoverFloats(aLine, aDeltaY);
   442 #ifdef DEBUG
   443     if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
   444       mFloatManager->List(stdout);
   445     }
   446 #endif
   447   }
   448 }
   450 // This is called by the line layout's AddFloat method when a
   451 // place-holder frame is reflowed in a line. If the float is a
   452 // left-most child (it's x coordinate is at the line's left margin)
   453 // then the float is place immediately, otherwise the float
   454 // placement is deferred until the line has been reflowed.
   456 // XXXldb This behavior doesn't quite fit with CSS1 and CSS2 --
   457 // technically we're supposed let the current line flow around the
   458 // float as well unless it won't fit next to what we already have.
   459 // But nobody else implements it that way...
   460 bool
   461 nsBlockReflowState::AddFloat(nsLineLayout*       aLineLayout,
   462                              nsIFrame*           aFloat,
   463                              nscoord             aAvailableWidth)
   464 {
   465   NS_PRECONDITION(aLineLayout, "must have line layout");
   466   NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr");
   467   NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
   468                   "aFloat must be an out-of-flow frame");
   470   NS_ABORT_IF_FALSE(aFloat->GetParent(), "float must have parent");
   471   NS_ABORT_IF_FALSE(aFloat->GetParent()->IsFrameOfType(nsIFrame::eBlockFrame),
   472                     "float's parent must be block");
   473   NS_ABORT_IF_FALSE(aFloat->GetParent() == mBlock ||
   474                     (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
   475                     "float should be in this block unless it was marked as "
   476                     "pushed float");
   477   if (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
   478     // If, in a previous reflow, the float was pushed entirely to
   479     // another column/page, we need to steal it back.  (We might just
   480     // push it again, though.)  Likewise, if that previous reflow
   481     // reflowed this block but not its next continuation, we might need
   482     // to steal it from our own float-continuations list.
   483     //
   484     // For more about pushed floats, see the comment above
   485     // nsBlockFrame::DrainPushedFloats.
   486     nsBlockFrame *floatParent =
   487       static_cast<nsBlockFrame*>(aFloat->GetParent());
   488     floatParent->StealFrame(aFloat);
   490     aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
   492     // Appending is fine, since if a float was pushed to the next
   493     // page/column, all later floats were also pushed.
   494     mBlock->mFloats.AppendFrame(mBlock, aFloat);
   495   }
   497   // Because we are in the middle of reflowing a placeholder frame
   498   // within a line (and possibly nested in an inline frame or two
   499   // that's a child of our block) we need to restore the space
   500   // manager's translation to the space that the block resides in
   501   // before placing the float.
   502   nscoord ox, oy;
   503   mFloatManager->GetTranslation(ox, oy);
   504   nscoord dx = ox - mFloatManagerX;
   505   nscoord dy = oy - mFloatManagerY;
   506   mFloatManager->Translate(-dx, -dy);
   508   bool placed;
   510   // Now place the float immediately if possible. Otherwise stash it
   511   // away in mPendingFloats and place it later.
   512   // If one or more floats has already been pushed to the next line,
   513   // don't let this one go on the current line, since that would violate
   514   // float ordering.
   515   nsRect floatAvailableSpace = GetFloatAvailableSpace().mRect;
   516   if (mBelowCurrentLineFloats.IsEmpty() &&
   517       (aLineLayout->LineIsEmpty() ||
   518        mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat)
   519        <= aAvailableWidth)) {
   520     // And then place it
   521     placed = FlowAndPlaceFloat(aFloat);
   522     if (placed) {
   523       // Pass on updated available space to the current inline reflow engine
   524       nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY);
   525       nsRect availSpace(nsPoint(floatAvailSpace.mRect.x, mY),
   526                         floatAvailSpace.mRect.Size());
   527       aLineLayout->UpdateBand(availSpace, aFloat);
   528       // Record this float in the current-line list
   529       mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
   530     } else {
   531       (*aLineLayout->GetLine())->SetHadFloatPushed();
   532     }
   533   }
   534   else {
   535     // Always claim to be placed; we don't know whether we fit yet, so we
   536     // deal with this in PlaceBelowCurrentLineFloats
   537     placed = true;
   538     // This float will be placed after the line is done (it is a
   539     // below-current-line float).
   540     mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
   541   }
   543   // Restore coordinate system
   544   mFloatManager->Translate(dx, dy);
   546   return placed;
   547 }
   549 bool
   550 nsBlockReflowState::CanPlaceFloat(nscoord aFloatWidth,
   551                                   const nsFlowAreaRect& aFloatAvailableSpace)
   552 {
   553   // A float fits at a given vertical position if there are no floats at
   554   // its horizontal position (no matter what its width) or if its width
   555   // fits in the space remaining after prior floats have been placed.
   556   // FIXME: We should allow overflow by up to half a pixel here (bug 21193).
   557   return !aFloatAvailableSpace.mHasFloats ||
   558          aFloatAvailableSpace.mRect.width >= aFloatWidth;
   559 }
   561 static nscoord
   562 FloatMarginWidth(const nsHTMLReflowState& aCBReflowState,
   563                  nscoord aFloatAvailableWidth,
   564                  nsIFrame *aFloat,
   565                  const nsCSSOffsetState& aFloatOffsetState)
   566 {
   567   AutoMaybeDisableFontInflation an(aFloat);
   568   return aFloat->ComputeSize(
   569     aCBReflowState.rendContext,
   570     nsSize(aCBReflowState.ComputedWidth(),
   571            aCBReflowState.ComputedHeight()),
   572     aFloatAvailableWidth,
   573     nsSize(aFloatOffsetState.ComputedPhysicalMargin().LeftRight(),
   574            aFloatOffsetState.ComputedPhysicalMargin().TopBottom()),
   575     nsSize(aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight() -
   576              aFloatOffsetState.ComputedPhysicalPadding().LeftRight(),
   577            aFloatOffsetState.ComputedPhysicalBorderPadding().TopBottom() -
   578              aFloatOffsetState.ComputedPhysicalPadding().TopBottom()),
   579     nsSize(aFloatOffsetState.ComputedPhysicalPadding().LeftRight(),
   580            aFloatOffsetState.ComputedPhysicalPadding().TopBottom()),
   581     true).width +
   582   aFloatOffsetState.ComputedPhysicalMargin().LeftRight() +
   583   aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight();
   584 }
   586 bool
   587 nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat)
   588 {
   589   // Save away the Y coordinate before placing the float. We will
   590   // restore mY at the end after placing the float. This is
   591   // necessary because any adjustments to mY during the float
   592   // placement are for the float only, not for any non-floating
   593   // content.
   594   AutoRestore<nscoord> restoreY(mY);
   595   // FIXME: Should give AutoRestore a getter for the value to avoid this.
   596   const nscoord saveY = mY;
   598   // Grab the float's display information
   599   const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay();
   601   // The float's old region, so we can propagate damage.
   602   nsRect oldRegion = nsFloatManager::GetRegionFor(aFloat);
   604   // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
   605   // ``above'' another float that preceded it in the flow.
   606   mY = std::max(mFloatManager->GetLowestFloatTop(), mY);
   608   // See if the float should clear any preceding floats...
   609   // XXX We need to mark this float somehow so that it gets reflowed
   610   // when floats are inserted before it.
   611   if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
   612     // XXXldb Does this handle vertical margins correctly?
   613     mY = ClearFloats(mY, floatDisplay->mBreakType);
   614   }
   615     // Get the band of available space
   616   nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mY);
   617   nsRect adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
   618                                     floatAvailableSpace.mRect, aFloat);
   620   NS_ASSERTION(aFloat->GetParent() == mBlock,
   621                "Float frame has wrong parent");
   623   nsCSSOffsetState offsets(aFloat, mReflowState.rendContext,
   624                            mReflowState.ComputedWidth());
   626   nscoord floatMarginWidth = FloatMarginWidth(mReflowState,
   627                                               adjustedAvailableSpace.width,
   628                                               aFloat, offsets);
   630   nsMargin floatMargin; // computed margin
   631   nsMargin floatOffsets;
   632   nsReflowStatus reflowStatus;
   634   // If it's a floating first-letter, we need to reflow it before we
   635   // know how wide it is (since we don't compute which letters are part
   636   // of the first letter until reflow!).
   637   bool isLetter = aFloat->GetType() == nsGkAtoms::letterFrame;
   638   if (isLetter) {
   639     mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
   640                         floatOffsets, false, reflowStatus);
   641     floatMarginWidth = aFloat->GetSize().width + floatMargin.LeftRight();
   642     NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus),
   643                  "letter frames shouldn't break, and if they do now, "
   644                  "then they're breaking at the wrong point");
   645   }
   647   // Find a place to place the float. The CSS2 spec doesn't want
   648   // floats overlapping each other or sticking out of the containing
   649   // block if possible (CSS2 spec section 9.5.1, see the rule list).
   650   NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) ||
   651 	       (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats),
   652 	       "invalid float type");
   654   // Can the float fit here?
   655   bool keepFloatOnSameLine = false;
   657   // Are we required to place at least part of the float because we're
   658   // at the top of the page (to avoid an infinite loop of pushing and
   659   // breaking).
   660   bool mustPlaceFloat =
   661     mReflowState.mFlags.mIsTopOfPage && IsAdjacentWithTop();
   663   for (;;) {
   664     if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
   665         floatAvailableSpace.mRect.height <= 0 &&
   666         !mustPlaceFloat) {
   667       // No space, nowhere to put anything.
   668       PushFloatPastBreak(aFloat);
   669       return false;
   670     }
   672     if (CanPlaceFloat(floatMarginWidth, floatAvailableSpace)) {
   673       // We found an appropriate place.
   674       break;
   675     }
   677     // Nope. try to advance to the next band.
   678     if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
   679           eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
   681       mY += floatAvailableSpace.mRect.height;
   682       if (adjustedAvailableSpace.height != NS_UNCONSTRAINEDSIZE) {
   683         adjustedAvailableSpace.height -= floatAvailableSpace.mRect.height;
   684       }
   685       floatAvailableSpace = GetFloatAvailableSpace(mY);
   686     } else {
   687       // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace
   688       // IE handles float tables in a very special way
   690       // see if the previous float is also a table and has "align"
   691       nsFloatCache* fc = mCurrentLineFloats.Head();
   692       nsIFrame* prevFrame = nullptr;
   693       while (fc) {
   694         if (fc->mFloat == aFloat) {
   695           break;
   696         }
   697         prevFrame = fc->mFloat;
   698         fc = fc->Next();
   699       }
   701       if(prevFrame) {
   702         //get the frame type
   703         if (nsGkAtoms::tableOuterFrame == prevFrame->GetType()) {
   704           //see if it has "align="
   705           // IE makes a difference between align and he float property
   706           nsIContent* content = prevFrame->GetContent();
   707           if (content) {
   708             // we're interested only if previous frame is align=left
   709             // IE messes things up when "right" (overlapping frames) 
   710             if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align,
   711                                      NS_LITERAL_STRING("left"), eIgnoreCase)) {
   712               keepFloatOnSameLine = true;
   713               // don't advance to next line (IE quirkie behaviour)
   714               // it breaks rule CSS2/9.5.1/1, but what the hell
   715               // since we cannot evangelize the world
   716               break;
   717             }
   718           }
   719         }
   720       }
   722       // the table does not fit anymore in this line so advance to next band 
   723       mY += floatAvailableSpace.mRect.height;
   724       // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to
   725       // get a new width for the new band.
   726       floatAvailableSpace = GetFloatAvailableSpace(mY);
   727       adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
   728                                  floatAvailableSpace.mRect, aFloat);
   729       floatMarginWidth = FloatMarginWidth(mReflowState,
   730                                           adjustedAvailableSpace.width,
   731                                           aFloat, offsets);
   732     }
   734     mustPlaceFloat = false;
   735   }
   737   // If the float is continued, it will get the same absolute x value as its prev-in-flow
   739   // We don't worry about the geometry of the prev in flow, let the continuation
   740   // place and size itself as required.
   742   // Assign an x and y coordinate to the float.
   743   nscoord floatX, floatY;
   744   if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) {
   745     floatX = floatAvailableSpace.mRect.x;
   746   }
   747   else {
   748     if (!keepFloatOnSameLine) {
   749       floatX = floatAvailableSpace.mRect.XMost() - floatMarginWidth;
   750     } 
   751     else {
   752       // this is the IE quirk (see few lines above)
   753       // the table is kept in the same line: don't let it overlap the
   754       // previous float 
   755       floatX = floatAvailableSpace.mRect.x;
   756     }
   757   }
   758   // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
   759   // be higher than the top of its containing block."  (Since the
   760   // containing block is the content edge of the block box, this
   761   // means the margin edge of the float can't be higher than the
   762   // content edge of the block that contains it.)
   763   floatY = std::max(mY, mContentArea.y);
   765   // Reflow the float after computing its vertical position so it knows
   766   // where to break.
   767   if (!isLetter) {
   768     bool pushedDown = mY != saveY;
   769     mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
   770                         floatOffsets, pushedDown, reflowStatus);
   771   }
   772   if (aFloat->GetPrevInFlow())
   773     floatMargin.top = 0;
   774   if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
   775     floatMargin.bottom = 0;
   777   // In the case that we're in columns and not splitting floats, we need
   778   // to check here that the float's height fit, and if it didn't, bail.
   779   // (This code is only for DISABLE_FLOAT_BREAKING_IN_COLUMNS .)
   780   //
   781   // Likewise, if none of the float fit, and it needs to be pushed in
   782   // its entirety to the next page (NS_FRAME_IS_TRUNCATED or
   783   // NS_INLINE_IS_BREAK_BEFORE), we need to do the same.
   784   if ((mContentArea.height != NS_UNCONSTRAINEDSIZE &&
   785        adjustedAvailableSpace.height == NS_UNCONSTRAINEDSIZE &&
   786        !mustPlaceFloat &&
   787        aFloat->GetSize().height + floatMargin.TopBottom() >
   788          mContentArea.YMost() - floatY) ||
   789       NS_FRAME_IS_TRUNCATED(reflowStatus) ||
   790       NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
   791     PushFloatPastBreak(aFloat);
   792     return false;
   793   }
   795   // We can't use aFloat->ShouldAvoidBreakInside(mReflowState) here since
   796   // its mIsTopOfPage may be true even though the float isn't at the
   797   // top when floatY > 0.
   798   if (mContentArea.height != NS_UNCONSTRAINEDSIZE &&
   799       !mustPlaceFloat && (!mReflowState.mFlags.mIsTopOfPage || floatY > 0) &&
   800       NS_STYLE_PAGE_BREAK_AVOID == aFloat->StyleDisplay()->mBreakInside &&
   801       (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus) ||
   802        aFloat->GetSize().height + floatMargin.TopBottom() >
   803          mContentArea.YMost() - floatY) &&
   804       !aFloat->GetPrevInFlow()) {
   805     PushFloatPastBreak(aFloat);
   806     return false;
   807   }
   809   // Calculate the actual origin of the float frame's border rect
   810   // relative to the parent block; the margin must be added in
   811   // to get the border rect
   812   nsPoint origin(floatMargin.left + floatX,
   813                  floatMargin.top + floatY);
   815   // If float is relatively positioned, factor that in as well
   816   nsHTMLReflowState::ApplyRelativePositioning(aFloat, floatOffsets, &origin);
   818   // Position the float and make sure and views are properly
   819   // positioned. We need to explicitly position its child views as
   820   // well, since we're moving the float after flowing it.
   821   bool moved = aFloat->GetPosition() != origin;
   822   if (moved) {
   823     aFloat->SetPosition(origin);
   824     nsContainerFrame::PositionFrameView(aFloat);
   825     nsContainerFrame::PositionChildViews(aFloat);
   826   }
   828   // Update the float combined area state
   829   // XXX Floats should really just get invalidated here if necessary
   830   mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() + origin);
   832   // Place the float in the float manager
   833   // calculate region
   834   nsRect region = nsFloatManager::CalculateRegionFor(aFloat, floatMargin);
   835   // if the float split, then take up all of the vertical height
   836   if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) &&
   837       (NS_UNCONSTRAINEDSIZE != mContentArea.height)) {
   838     region.height = std::max(region.height, mContentArea.height - floatY);
   839   }
   840   DebugOnly<nsresult> rv =
   841     mFloatManager->AddFloat(aFloat, region);
   842   NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement");
   843   // store region
   844   nsFloatManager::StoreRegionFor(aFloat, region);
   846   // If the float's dimensions have changed, note the damage in the
   847   // float manager.
   848   if (!region.IsEqualEdges(oldRegion)) {
   849     // XXXwaterson conservative: we could probably get away with noting
   850     // less damage; e.g., if only height has changed, then only note the
   851     // area into which the float has grown or from which the float has
   852     // shrunk.
   853     nscoord top = std::min(region.y, oldRegion.y);
   854     nscoord bottom = std::max(region.YMost(), oldRegion.YMost());
   855     mFloatManager->IncludeInDamage(top, bottom);
   856   }
   858   if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) {
   859     mBlock->SplitFloat(*this, aFloat, reflowStatus);
   860   }
   862 #ifdef NOISY_FLOATMANAGER
   863   nscoord tx, ty;
   864   mFloatManager->GetTranslation(tx, ty);
   865   nsFrame::ListTag(stdout, mBlock);
   866   printf(": FlowAndPlaceFloat: AddFloat: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
   867          tx, ty, mFloatManagerX, mFloatManagerY,
   868          region.x, region.y, region.width, region.height);
   869 #endif
   871 #ifdef DEBUG
   872   if (nsBlockFrame::gNoisyReflow) {
   873     nsRect r = aFloat->GetRect();
   874     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   875     printf("placed float: ");
   876     nsFrame::ListTag(stdout, aFloat);
   877     printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
   878   }
   879 #endif
   881   return true;
   882 }
   884 void
   885 nsBlockReflowState::PushFloatPastBreak(nsIFrame *aFloat)
   886 {
   887   // This ensures that we:
   888   //  * don't try to place later but smaller floats (which CSS says
   889   //    must have their tops below the top of this float)
   890   //  * don't waste much time trying to reflow this float again until
   891   //    after the break
   892   if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
   893     mFloatManager->SetPushedLeftFloatPastBreak();
   894   } else {
   895     NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats ==
   896                         NS_STYLE_FLOAT_RIGHT,
   897                       "unexpected float value");
   898     mFloatManager->SetPushedRightFloatPastBreak();
   899   }
   901   // Put the float on the pushed floats list, even though it
   902   // isn't actually a continuation.
   903   DebugOnly<nsresult> rv = mBlock->StealFrame(aFloat);
   904   NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed");
   905   AppendPushedFloat(aFloat);
   907   NS_FRAME_SET_OVERFLOW_INCOMPLETE(mReflowStatus);
   908 }
   910 /**
   911  * Place below-current-line floats.
   912  */
   913 void
   914 nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList,
   915                                                 nsLineBox* aLine)
   916 {
   917   nsFloatCache* fc = aList.Head();
   918   while (fc) {
   919 #ifdef DEBUG
   920     if (nsBlockFrame::gNoisyReflow) {
   921       nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   922       printf("placing bcl float: ");
   923       nsFrame::ListTag(stdout, fc->mFloat);
   924       printf("\n");
   925     }
   926 #endif
   927     // Place the float
   928     bool placed = FlowAndPlaceFloat(fc->mFloat);
   929     nsFloatCache *next = fc->Next();
   930     if (!placed) {
   931       aList.Remove(fc);
   932       delete fc;
   933       aLine->SetHadFloatPushed();
   934     }
   935     fc = next;
   936   }
   937 }
   939 nscoord
   940 nsBlockReflowState::ClearFloats(nscoord aY, uint8_t aBreakType,
   941                                 nsIFrame *aReplacedBlock,
   942                                 uint32_t aFlags)
   943 {
   944 #ifdef DEBUG
   945   if (nsBlockFrame::gNoisyReflow) {
   946     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   947     printf("clear floats: in: aY=%d\n", aY);
   948   }
   949 #endif
   951 #ifdef NOISY_FLOAT_CLEARING
   952   printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
   953          aY, aBreakType);
   954   mFloatManager->List(stdout);
   955 #endif
   957   if (!mFloatManager->HasAnyFloats()) {
   958     return aY;
   959   }
   961   nscoord newY = aY;
   963   if (aBreakType != NS_STYLE_CLEAR_NONE) {
   964     newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags);
   965   }
   967   if (aReplacedBlock) {
   968     for (;;) {
   969       nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newY);
   970       if (!floatAvailableSpace.mHasFloats) {
   971         // If there aren't any floats here, then we always fit.
   972         // We check this before calling WidthToClearPastFloats, which is
   973         // somewhat expensive.
   974         break;
   975       }
   976       nsBlockFrame::ReplacedElementWidthToClear replacedWidth =
   977         nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect,
   978                                              aReplacedBlock);
   979       if (std::max(floatAvailableSpace.mRect.x - mContentArea.x,
   980                  replacedWidth.marginLeft) +
   981             replacedWidth.borderBoxWidth +
   982             std::max(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(),
   983                    replacedWidth.marginRight) <=
   984           mContentArea.width) {
   985         break;
   986       }
   987       // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
   988       if (floatAvailableSpace.mRect.height > 0) {
   989         // See if there's room in the next band.
   990         newY += floatAvailableSpace.mRect.height;
   991       } else {
   992         if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
   993           // Stop trying to clear here; we'll just get pushed to the
   994           // next column or page and try again there.
   995           break;
   996         }
   997         NS_NOTREACHED("avail space rect with zero height!");
   998         newY += 1;
   999       }
  1003 #ifdef DEBUG
  1004   if (nsBlockFrame::gNoisyReflow) {
  1005     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
  1006     printf("clear floats: out: y=%d\n", newY);
  1008 #endif
  1010   return newY;

mercurial