layout/generic/nsBlockReflowState.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/generic/nsBlockReflowState.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1012 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +// vim:cindent:ts=2:et:sw=2:
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/* state used in reflow of block frames */
    1.11 +
    1.12 +#include "nsBlockReflowState.h"
    1.13 +
    1.14 +#include "mozilla/DebugOnly.h"
    1.15 +
    1.16 +#include "nsBlockFrame.h"
    1.17 +#include "nsLineLayout.h"
    1.18 +#include "nsPresContext.h"
    1.19 +#include "nsIFrameInlines.h"
    1.20 +#include "mozilla/AutoRestore.h"
    1.21 +#include <algorithm>
    1.22 +
    1.23 +#ifdef DEBUG
    1.24 +#include "nsBlockDebugFlags.h"
    1.25 +#endif
    1.26 +
    1.27 +using namespace mozilla;
    1.28 +using namespace mozilla::layout;
    1.29 +
    1.30 +nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
    1.31 +                                       nsPresContext* aPresContext,
    1.32 +                                       nsBlockFrame* aFrame,
    1.33 +                                       bool aTopMarginRoot,
    1.34 +                                       bool aBottomMarginRoot,
    1.35 +                                       bool aBlockNeedsFloatManager,
    1.36 +                                       nscoord aConsumedHeight)
    1.37 +  : mBlock(aFrame),
    1.38 +    mPresContext(aPresContext),
    1.39 +    mReflowState(aReflowState),
    1.40 +    mPushedFloats(nullptr),
    1.41 +    mOverflowTracker(nullptr),
    1.42 +    mPrevBottomMargin(),
    1.43 +    mLineNumber(0),
    1.44 +    mFlags(0),
    1.45 +    mFloatBreakType(NS_STYLE_CLEAR_NONE),
    1.46 +    mConsumedHeight(aConsumedHeight)
    1.47 +{
    1.48 +  SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr);
    1.49 +  SetFlag(BRS_ISOVERFLOWCONTAINER,
    1.50 +          IS_TRUE_OVERFLOW_CONTAINER(aFrame));
    1.51 +
    1.52 +  const nsMargin& borderPadding = BorderPadding();
    1.53 +  mContainerWidth = aReflowState.ComputedWidth() +
    1.54 +                    aReflowState.ComputedPhysicalBorderPadding().LeftRight();
    1.55 +
    1.56 +  if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) {
    1.57 +    SetFlag(BRS_ISTOPMARGINROOT, true);
    1.58 +  }
    1.59 +  if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) {
    1.60 +    SetFlag(BRS_ISBOTTOMMARGINROOT, true);
    1.61 +  }
    1.62 +  if (GetFlag(BRS_ISTOPMARGINROOT)) {
    1.63 +    SetFlag(BRS_APPLYTOPMARGIN, true);
    1.64 +  }
    1.65 +  if (aBlockNeedsFloatManager) {
    1.66 +    SetFlag(BRS_FLOAT_MGR, true);
    1.67 +  }
    1.68 +  
    1.69 +  mFloatManager = aReflowState.mFloatManager;
    1.70 +
    1.71 +  NS_ASSERTION(mFloatManager,
    1.72 +               "FloatManager should be set in nsBlockReflowState" );
    1.73 +  if (mFloatManager) {
    1.74 +    // Save the coordinate system origin for later.
    1.75 +    mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY);
    1.76 +    mFloatManager->PushState(&mFloatManagerStateBefore); // never popped
    1.77 +  }
    1.78 +
    1.79 +  mReflowStatus = NS_FRAME_COMPLETE;
    1.80 +
    1.81 +  mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow());
    1.82 +
    1.83 +  NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedWidth(),
    1.84 +                   "have unconstrained width; this should only result from "
    1.85 +                   "very large sizes, not attempts at intrinsic width "
    1.86 +                   "calculation");
    1.87 +  mContentArea.width = aReflowState.ComputedWidth();
    1.88 +
    1.89 +  // Compute content area height. Unlike the width, if we have a
    1.90 +  // specified style height we ignore it since extra content is
    1.91 +  // managed by the "overflow" property. When we don't have a
    1.92 +  // specified style height then we may end up limiting our height if
    1.93 +  // the availableHeight is constrained (this situation occurs when we
    1.94 +  // are paginated).
    1.95 +  if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight()) {
    1.96 +    // We are in a paginated situation. The bottom edge is just inside
    1.97 +    // the bottom border and padding. The content area height doesn't
    1.98 +    // include either border or padding edge.
    1.99 +    mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom;
   1.100 +    mContentArea.height = std::max(0, mBottomEdge - borderPadding.top);
   1.101 +  }
   1.102 +  else {
   1.103 +    // When we are not in a paginated situation then we always use
   1.104 +    // an constrained height.
   1.105 +    SetFlag(BRS_UNCONSTRAINEDHEIGHT, true);
   1.106 +    mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE;
   1.107 +  }
   1.108 +  mContentArea.x = borderPadding.left;
   1.109 +  mY = mContentArea.y = borderPadding.top;
   1.110 +
   1.111 +  mPrevChild = nullptr;
   1.112 +  mCurrentLine = aFrame->end_lines();
   1.113 +
   1.114 +  mMinLineHeight = aReflowState.CalcLineHeight();
   1.115 +}
   1.116 +
   1.117 +nscoord
   1.118 +nsBlockReflowState::GetConsumedHeight()
   1.119 +{
   1.120 +  if (mConsumedHeight == NS_INTRINSICSIZE) {
   1.121 +    mConsumedHeight = mBlock->GetConsumedHeight();
   1.122 +  }
   1.123 +
   1.124 +  return mConsumedHeight;
   1.125 +}
   1.126 +
   1.127 +void
   1.128 +nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame,
   1.129 +                                                         const nsRect& aFloatAvailableSpace,
   1.130 +                                                         nscoord& aLeftResult,
   1.131 +                                                         nscoord& aRightResult)
   1.132 +{
   1.133 +  // The frame is clueless about the float manager and therefore we
   1.134 +  // only give it free space. An example is a table frame - the
   1.135 +  // tables do not flow around floats.
   1.136 +  // However, we can let its margins intersect floats.
   1.137 +  NS_ASSERTION(aFloatAvailableSpace.x >= mContentArea.x, "bad avail space rect x");
   1.138 +  NS_ASSERTION(aFloatAvailableSpace.width == 0 ||
   1.139 +               aFloatAvailableSpace.XMost() <= mContentArea.XMost(),
   1.140 +               "bad avail space rect width");
   1.141 +
   1.142 +  nscoord leftOffset, rightOffset;
   1.143 +  if (aFloatAvailableSpace.width == mContentArea.width) {
   1.144 +    // We don't need to compute margins when there are no floats around.
   1.145 +    leftOffset = 0;
   1.146 +    rightOffset = 0;
   1.147 +  } else {
   1.148 +    nsMargin frameMargin;
   1.149 +    nsCSSOffsetState os(aFrame, mReflowState.rendContext, mContentArea.width);
   1.150 +    frameMargin = os.ComputedPhysicalMargin();
   1.151 +
   1.152 +    nscoord leftFloatXOffset = aFloatAvailableSpace.x - mContentArea.x;
   1.153 +    leftOffset = std::max(leftFloatXOffset, frameMargin.left) -
   1.154 +                 frameMargin.left;
   1.155 +    leftOffset = std::max(leftOffset, 0); // in case of negative margin
   1.156 +    nscoord rightFloatXOffset =
   1.157 +      mContentArea.XMost() - aFloatAvailableSpace.XMost();
   1.158 +    rightOffset = std::max(rightFloatXOffset, frameMargin.right) -
   1.159 +                  frameMargin.right;
   1.160 +    rightOffset = std::max(rightOffset, 0); // in case of negative margin
   1.161 +  }
   1.162 +  aLeftResult = leftOffset;
   1.163 +  aRightResult = rightOffset;
   1.164 +}
   1.165 +
   1.166 +// Compute the amount of available space for reflowing a block frame
   1.167 +// at the current Y coordinate. This method assumes that
   1.168 +// GetAvailableSpace has already been called.
   1.169 +void
   1.170 +nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
   1.171 +                                           const nsStyleDisplay* aDisplay,
   1.172 +                                           const nsFlowAreaRect& aFloatAvailableSpace,
   1.173 +                                           bool aBlockAvoidsFloats,
   1.174 +                                           nsRect& aResult)
   1.175 +{
   1.176 +#ifdef REALLY_NOISY_REFLOW
   1.177 +  printf("CBAS frame=%p has floats %d\n",
   1.178 +         aFrame, aFloatAvailableSpace.mHasFloats);
   1.179 +#endif
   1.180 +  aResult.y = mY;
   1.181 +  aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT)
   1.182 +    ? NS_UNCONSTRAINEDSIZE
   1.183 +    : mReflowState.AvailableHeight() - mY;
   1.184 +  // mY might be greater than mBottomEdge if the block's top margin pushes
   1.185 +  // it off the page/column. Negative available height can confuse other code
   1.186 +  // and is nonsense in principle.
   1.187 +
   1.188 +  // XXX Do we really want this condition to be this restrictive (i.e.,
   1.189 +  // more restrictive than it used to be)?  The |else| here is allowed
   1.190 +  // by the CSS spec, but only out of desperation given implementations,
   1.191 +  // and the behavior it leads to is quite undesirable (it can cause
   1.192 +  // things to become extremely narrow when they'd fit quite well a
   1.193 +  // little bit lower).  Should the else be a quirk or something that
   1.194 +  // applies to a specific set of frame classes and no new ones?
   1.195 +  // If we did that, then for those frames where the condition below is
   1.196 +  // true but nsBlockFrame::BlockCanIntersectFloats is false,
   1.197 +  // nsBlockFrame::WidthToClearPastFloats would need to use the
   1.198 +  // shrink-wrap formula, max(MIN_WIDTH, min(avail width, PREF_WIDTH))
   1.199 +  // rather than just using MIN_WIDTH.
   1.200 +  NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) == 
   1.201 +                 !aBlockAvoidsFloats,
   1.202 +               "unexpected replaced width");
   1.203 +  if (!aBlockAvoidsFloats) {
   1.204 +    if (aFloatAvailableSpace.mHasFloats) {
   1.205 +      // Use the float-edge property to determine how the child block
   1.206 +      // will interact with the float.
   1.207 +      const nsStyleBorder* borderStyle = aFrame->StyleBorder();
   1.208 +      switch (borderStyle->mFloatEdge) {
   1.209 +        default:
   1.210 +        case NS_STYLE_FLOAT_EDGE_CONTENT:  // content and only content does runaround of floats
   1.211 +          // The child block will flow around the float. Therefore
   1.212 +          // give it all of the available space.
   1.213 +          aResult.x = mContentArea.x;
   1.214 +          aResult.width = mContentArea.width;
   1.215 +          break;
   1.216 +        case NS_STYLE_FLOAT_EDGE_MARGIN:
   1.217 +          {
   1.218 +            // The child block's margins should be placed adjacent to,
   1.219 +            // but not overlap the float.
   1.220 +            aResult.x = aFloatAvailableSpace.mRect.x;
   1.221 +            aResult.width = aFloatAvailableSpace.mRect.width;
   1.222 +          }
   1.223 +          break;
   1.224 +      }
   1.225 +    }
   1.226 +    else {
   1.227 +      // Since there are no floats present the float-edge property
   1.228 +      // doesn't matter therefore give the block element all of the
   1.229 +      // available space since it will flow around the float itself.
   1.230 +      aResult.x = mContentArea.x;
   1.231 +      aResult.width = mContentArea.width;
   1.232 +    }
   1.233 +  }
   1.234 +  else {
   1.235 +    nscoord leftOffset, rightOffset;
   1.236 +    ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect,
   1.237 +                                         leftOffset, rightOffset);
   1.238 +    aResult.x = mContentArea.x + leftOffset;
   1.239 +    aResult.width = mContentArea.width - leftOffset - rightOffset;
   1.240 +  }
   1.241 +
   1.242 +#ifdef REALLY_NOISY_REFLOW
   1.243 +  printf("  CBAS: result %d %d %d %d\n", aResult.x, aResult.y, aResult.width, aResult.height);
   1.244 +#endif
   1.245 +}
   1.246 +
   1.247 +nsFlowAreaRect
   1.248 +nsBlockReflowState::GetFloatAvailableSpaceWithState(
   1.249 +                      nscoord aY,
   1.250 +                      nsFloatManager::SavedState *aState) const
   1.251 +{
   1.252 +#ifdef DEBUG
   1.253 +  // Verify that the caller setup the coordinate system properly
   1.254 +  nscoord wx, wy;
   1.255 +  mFloatManager->GetTranslation(wx, wy);
   1.256 +  NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY),
   1.257 +               "bad coord system");
   1.258 +#endif
   1.259 +
   1.260 +  nscoord height = (mContentArea.height == nscoord_MAX)
   1.261 +                     ? nscoord_MAX : std::max(mContentArea.YMost() - aY, 0);
   1.262 +  nsFlowAreaRect result =
   1.263 +    mFloatManager->GetFlowArea(aY, nsFloatManager::BAND_FROM_POINT,
   1.264 +                               height, mContentArea, aState);
   1.265 +  // Keep the width >= 0 for compatibility with nsSpaceManager.
   1.266 +  if (result.mRect.width < 0)
   1.267 +    result.mRect.width = 0;
   1.268 +
   1.269 +#ifdef DEBUG
   1.270 +  if (nsBlockFrame::gNoisyReflow) {
   1.271 +    nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.272 +    printf("GetAvailableSpace: band=%d,%d,%d,%d hasfloats=%d\n",
   1.273 +           result.mRect.x, result.mRect.y, result.mRect.width,
   1.274 +           result.mRect.height, result.mHasFloats);
   1.275 +  }
   1.276 +#endif
   1.277 +  return result;
   1.278 +}
   1.279 +
   1.280 +nsFlowAreaRect
   1.281 +nsBlockReflowState::GetFloatAvailableSpaceForHeight(
   1.282 +                      nscoord aY, nscoord aHeight,
   1.283 +                      nsFloatManager::SavedState *aState) const
   1.284 +{
   1.285 +#ifdef DEBUG
   1.286 +  // Verify that the caller setup the coordinate system properly
   1.287 +  nscoord wx, wy;
   1.288 +  mFloatManager->GetTranslation(wx, wy);
   1.289 +  NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY),
   1.290 +               "bad coord system");
   1.291 +#endif
   1.292 +
   1.293 +  nsFlowAreaRect result =
   1.294 +    mFloatManager->GetFlowArea(aY, nsFloatManager::WIDTH_WITHIN_HEIGHT,
   1.295 +                               aHeight, mContentArea, aState);
   1.296 +  // Keep the width >= 0 for compatibility with nsSpaceManager.
   1.297 +  if (result.mRect.width < 0)
   1.298 +    result.mRect.width = 0;
   1.299 +
   1.300 +#ifdef DEBUG
   1.301 +  if (nsBlockFrame::gNoisyReflow) {
   1.302 +    nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.303 +    printf("GetAvailableSpaceForHeight: space=%d,%d,%d,%d hasfloats=%d\n",
   1.304 +           result.mRect.x, result.mRect.y, result.mRect.width,
   1.305 +           result.mRect.height, result.mHasFloats);
   1.306 +  }
   1.307 +#endif
   1.308 +  return result;
   1.309 +}
   1.310 +
   1.311 +/*
   1.312 + * Reconstruct the vertical margin before the line |aLine| in order to
   1.313 + * do an incremental reflow that begins with |aLine| without reflowing
   1.314 + * the line before it.  |aLine| may point to the fencepost at the end of
   1.315 + * the line list, and it is used this way since we (for now, anyway)
   1.316 + * always need to recover margins at the end of a block.
   1.317 + *
   1.318 + * The reconstruction involves walking backward through the line list to
   1.319 + * find any collapsed margins preceding the line that would have been in
   1.320 + * the reflow state's |mPrevBottomMargin| when we reflowed that line in
   1.321 + * a full reflow (under the rule in CSS2 that all adjacent vertical
   1.322 + * margins of blocks collapse).
   1.323 + */
   1.324 +void
   1.325 +nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine)
   1.326 +{
   1.327 +  mPrevBottomMargin.Zero();
   1.328 +  nsBlockFrame *block = mBlock;
   1.329 +
   1.330 +  nsLineList::iterator firstLine = block->begin_lines();
   1.331 +  for (;;) {
   1.332 +    --aLine;
   1.333 +    if (aLine->IsBlock()) {
   1.334 +      mPrevBottomMargin = aLine->GetCarriedOutBottomMargin();
   1.335 +      break;
   1.336 +    }
   1.337 +    if (!aLine->IsEmpty()) {
   1.338 +      break;
   1.339 +    }
   1.340 +    if (aLine == firstLine) {
   1.341 +      // If the top margin was carried out (and thus already applied),
   1.342 +      // set it to zero.  Either way, we're done.
   1.343 +      if (!GetFlag(BRS_ISTOPMARGINROOT)) {
   1.344 +        mPrevBottomMargin.Zero();
   1.345 +      }
   1.346 +      break;
   1.347 +    }
   1.348 +  }
   1.349 +}
   1.350 +
   1.351 +void
   1.352 +nsBlockReflowState::SetupPushedFloatList()
   1.353 +{
   1.354 +  NS_ABORT_IF_FALSE(!GetFlag(BRS_PROPTABLE_FLOATCLIST) == !mPushedFloats,
   1.355 +                    "flag mismatch");
   1.356 +  if (!GetFlag(BRS_PROPTABLE_FLOATCLIST)) {
   1.357 +    // If we're being re-Reflow'd without our next-in-flow having been
   1.358 +    // reflowed, some pushed floats from our previous reflow might
   1.359 +    // still be on our pushed floats list.  However, that's
   1.360 +    // actually fine, since they'll all end up being stolen and
   1.361 +    // reordered into the correct order again.
   1.362 +    // (nsBlockFrame::ReflowDirtyLines ensures that any lines with
   1.363 +    // pushed floats are reflowed.)
   1.364 +    mPushedFloats = mBlock->EnsurePushedFloats();
   1.365 +    SetFlag(BRS_PROPTABLE_FLOATCLIST, true);
   1.366 +  }
   1.367 +}
   1.368 +
   1.369 +void
   1.370 +nsBlockReflowState::AppendPushedFloat(nsIFrame* aFloatCont)
   1.371 +{
   1.372 +  SetupPushedFloatList();
   1.373 +  aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT);
   1.374 +  mPushedFloats->AppendFrame(mBlock, aFloatCont);
   1.375 +}
   1.376 +
   1.377 +/**
   1.378 + * Restore information about floats into the float manager for an
   1.379 + * incremental reflow, and simultaneously push the floats by
   1.380 + * |aDeltaY|, which is the amount |aLine| was pushed relative to its
   1.381 + * parent.  The recovery of state is one of the things that makes
   1.382 + * incremental reflow O(N^2) and this state should really be kept
   1.383 + * around, attached to the frame tree.
   1.384 + */
   1.385 +void
   1.386 +nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine,
   1.387 +                                  nscoord aDeltaY)
   1.388 +{
   1.389 +  if (aLine->HasFloats()) {
   1.390 +    // Place the floats into the space-manager again. Also slide
   1.391 +    // them, just like the regular frames on the line.
   1.392 +    nsFloatCache* fc = aLine->GetFirstFloat();
   1.393 +    while (fc) {
   1.394 +      nsIFrame* floatFrame = fc->mFloat;
   1.395 +      if (aDeltaY != 0) {
   1.396 +        floatFrame->MovePositionBy(nsPoint(0, aDeltaY));
   1.397 +        nsContainerFrame::PositionFrameView(floatFrame);
   1.398 +        nsContainerFrame::PositionChildViews(floatFrame);
   1.399 +      }
   1.400 +#ifdef DEBUG
   1.401 +      if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
   1.402 +        nscoord tx, ty;
   1.403 +        mFloatManager->GetTranslation(tx, ty);
   1.404 +        nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.405 +        printf("RecoverFloats: txy=%d,%d (%d,%d) ",
   1.406 +               tx, ty, mFloatManagerX, mFloatManagerY);
   1.407 +        nsFrame::ListTag(stdout, floatFrame);
   1.408 +        nsRect region = nsFloatManager::GetRegionFor(floatFrame);
   1.409 +        printf(" aDeltaY=%d region={%d,%d,%d,%d}\n",
   1.410 +               aDeltaY, region.x, region.y, region.width, region.height);
   1.411 +      }
   1.412 +#endif
   1.413 +      mFloatManager->AddFloat(floatFrame,
   1.414 +                              nsFloatManager::GetRegionFor(floatFrame));
   1.415 +      fc = fc->Next();
   1.416 +    }
   1.417 +  } else if (aLine->IsBlock()) {
   1.418 +    nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager);
   1.419 +  }
   1.420 +}
   1.421 +
   1.422 +/**
   1.423 + * Everything done in this function is done O(N) times for each pass of
   1.424 + * reflow so it is O(N*M) where M is the number of incremental reflow
   1.425 + * passes.  That's bad.  Don't do stuff here.
   1.426 + *
   1.427 + * When this function is called, |aLine| has just been slid by |aDeltaY|
   1.428 + * and the purpose of RecoverStateFrom is to ensure that the
   1.429 + * nsBlockReflowState is in the same state that it would have been in
   1.430 + * had the line just been reflowed.
   1.431 + *
   1.432 + * Most of the state recovery that we have to do involves floats.
   1.433 + */
   1.434 +void
   1.435 +nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine,
   1.436 +                                     nscoord aDeltaY)
   1.437 +{
   1.438 +  // Make the line being recovered the current line
   1.439 +  mCurrentLine = aLine;
   1.440 +
   1.441 +  // Place floats for this line into the float manager
   1.442 +  if (aLine->HasFloats() || aLine->IsBlock()) {
   1.443 +    RecoverFloats(aLine, aDeltaY);
   1.444 +
   1.445 +#ifdef DEBUG
   1.446 +    if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
   1.447 +      mFloatManager->List(stdout);
   1.448 +    }
   1.449 +#endif
   1.450 +  }
   1.451 +}
   1.452 +
   1.453 +// This is called by the line layout's AddFloat method when a
   1.454 +// place-holder frame is reflowed in a line. If the float is a
   1.455 +// left-most child (it's x coordinate is at the line's left margin)
   1.456 +// then the float is place immediately, otherwise the float
   1.457 +// placement is deferred until the line has been reflowed.
   1.458 +
   1.459 +// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 --
   1.460 +// technically we're supposed let the current line flow around the
   1.461 +// float as well unless it won't fit next to what we already have.
   1.462 +// But nobody else implements it that way...
   1.463 +bool
   1.464 +nsBlockReflowState::AddFloat(nsLineLayout*       aLineLayout,
   1.465 +                             nsIFrame*           aFloat,
   1.466 +                             nscoord             aAvailableWidth)
   1.467 +{
   1.468 +  NS_PRECONDITION(aLineLayout, "must have line layout");
   1.469 +  NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr");
   1.470 +  NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
   1.471 +                  "aFloat must be an out-of-flow frame");
   1.472 +
   1.473 +  NS_ABORT_IF_FALSE(aFloat->GetParent(), "float must have parent");
   1.474 +  NS_ABORT_IF_FALSE(aFloat->GetParent()->IsFrameOfType(nsIFrame::eBlockFrame),
   1.475 +                    "float's parent must be block");
   1.476 +  NS_ABORT_IF_FALSE(aFloat->GetParent() == mBlock ||
   1.477 +                    (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
   1.478 +                    "float should be in this block unless it was marked as "
   1.479 +                    "pushed float");
   1.480 +  if (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
   1.481 +    // If, in a previous reflow, the float was pushed entirely to
   1.482 +    // another column/page, we need to steal it back.  (We might just
   1.483 +    // push it again, though.)  Likewise, if that previous reflow
   1.484 +    // reflowed this block but not its next continuation, we might need
   1.485 +    // to steal it from our own float-continuations list.
   1.486 +    //
   1.487 +    // For more about pushed floats, see the comment above
   1.488 +    // nsBlockFrame::DrainPushedFloats.
   1.489 +    nsBlockFrame *floatParent =
   1.490 +      static_cast<nsBlockFrame*>(aFloat->GetParent());
   1.491 +    floatParent->StealFrame(aFloat);
   1.492 +
   1.493 +    aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
   1.494 +
   1.495 +    // Appending is fine, since if a float was pushed to the next
   1.496 +    // page/column, all later floats were also pushed.
   1.497 +    mBlock->mFloats.AppendFrame(mBlock, aFloat);
   1.498 +  }
   1.499 +
   1.500 +  // Because we are in the middle of reflowing a placeholder frame
   1.501 +  // within a line (and possibly nested in an inline frame or two
   1.502 +  // that's a child of our block) we need to restore the space
   1.503 +  // manager's translation to the space that the block resides in
   1.504 +  // before placing the float.
   1.505 +  nscoord ox, oy;
   1.506 +  mFloatManager->GetTranslation(ox, oy);
   1.507 +  nscoord dx = ox - mFloatManagerX;
   1.508 +  nscoord dy = oy - mFloatManagerY;
   1.509 +  mFloatManager->Translate(-dx, -dy);
   1.510 +
   1.511 +  bool placed;
   1.512 +
   1.513 +  // Now place the float immediately if possible. Otherwise stash it
   1.514 +  // away in mPendingFloats and place it later.
   1.515 +  // If one or more floats has already been pushed to the next line,
   1.516 +  // don't let this one go on the current line, since that would violate
   1.517 +  // float ordering.
   1.518 +  nsRect floatAvailableSpace = GetFloatAvailableSpace().mRect;
   1.519 +  if (mBelowCurrentLineFloats.IsEmpty() &&
   1.520 +      (aLineLayout->LineIsEmpty() ||
   1.521 +       mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat)
   1.522 +       <= aAvailableWidth)) {
   1.523 +    // And then place it
   1.524 +    placed = FlowAndPlaceFloat(aFloat);
   1.525 +    if (placed) {
   1.526 +      // Pass on updated available space to the current inline reflow engine
   1.527 +      nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY);
   1.528 +      nsRect availSpace(nsPoint(floatAvailSpace.mRect.x, mY),
   1.529 +                        floatAvailSpace.mRect.Size());
   1.530 +      aLineLayout->UpdateBand(availSpace, aFloat);
   1.531 +      // Record this float in the current-line list
   1.532 +      mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
   1.533 +    } else {
   1.534 +      (*aLineLayout->GetLine())->SetHadFloatPushed();
   1.535 +    }
   1.536 +  }
   1.537 +  else {
   1.538 +    // Always claim to be placed; we don't know whether we fit yet, so we
   1.539 +    // deal with this in PlaceBelowCurrentLineFloats
   1.540 +    placed = true;
   1.541 +    // This float will be placed after the line is done (it is a
   1.542 +    // below-current-line float).
   1.543 +    mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
   1.544 +  }
   1.545 +
   1.546 +  // Restore coordinate system
   1.547 +  mFloatManager->Translate(dx, dy);
   1.548 +
   1.549 +  return placed;
   1.550 +}
   1.551 +
   1.552 +bool
   1.553 +nsBlockReflowState::CanPlaceFloat(nscoord aFloatWidth,
   1.554 +                                  const nsFlowAreaRect& aFloatAvailableSpace)
   1.555 +{
   1.556 +  // A float fits at a given vertical position if there are no floats at
   1.557 +  // its horizontal position (no matter what its width) or if its width
   1.558 +  // fits in the space remaining after prior floats have been placed.
   1.559 +  // FIXME: We should allow overflow by up to half a pixel here (bug 21193).
   1.560 +  return !aFloatAvailableSpace.mHasFloats ||
   1.561 +         aFloatAvailableSpace.mRect.width >= aFloatWidth;
   1.562 +}
   1.563 +
   1.564 +static nscoord
   1.565 +FloatMarginWidth(const nsHTMLReflowState& aCBReflowState,
   1.566 +                 nscoord aFloatAvailableWidth,
   1.567 +                 nsIFrame *aFloat,
   1.568 +                 const nsCSSOffsetState& aFloatOffsetState)
   1.569 +{
   1.570 +  AutoMaybeDisableFontInflation an(aFloat);
   1.571 +  return aFloat->ComputeSize(
   1.572 +    aCBReflowState.rendContext,
   1.573 +    nsSize(aCBReflowState.ComputedWidth(),
   1.574 +           aCBReflowState.ComputedHeight()),
   1.575 +    aFloatAvailableWidth,
   1.576 +    nsSize(aFloatOffsetState.ComputedPhysicalMargin().LeftRight(),
   1.577 +           aFloatOffsetState.ComputedPhysicalMargin().TopBottom()),
   1.578 +    nsSize(aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight() -
   1.579 +             aFloatOffsetState.ComputedPhysicalPadding().LeftRight(),
   1.580 +           aFloatOffsetState.ComputedPhysicalBorderPadding().TopBottom() -
   1.581 +             aFloatOffsetState.ComputedPhysicalPadding().TopBottom()),
   1.582 +    nsSize(aFloatOffsetState.ComputedPhysicalPadding().LeftRight(),
   1.583 +           aFloatOffsetState.ComputedPhysicalPadding().TopBottom()),
   1.584 +    true).width +
   1.585 +  aFloatOffsetState.ComputedPhysicalMargin().LeftRight() +
   1.586 +  aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight();
   1.587 +}
   1.588 +
   1.589 +bool
   1.590 +nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat)
   1.591 +{
   1.592 +  // Save away the Y coordinate before placing the float. We will
   1.593 +  // restore mY at the end after placing the float. This is
   1.594 +  // necessary because any adjustments to mY during the float
   1.595 +  // placement are for the float only, not for any non-floating
   1.596 +  // content.
   1.597 +  AutoRestore<nscoord> restoreY(mY);
   1.598 +  // FIXME: Should give AutoRestore a getter for the value to avoid this.
   1.599 +  const nscoord saveY = mY;
   1.600 +
   1.601 +  // Grab the float's display information
   1.602 +  const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay();
   1.603 +
   1.604 +  // The float's old region, so we can propagate damage.
   1.605 +  nsRect oldRegion = nsFloatManager::GetRegionFor(aFloat);
   1.606 +
   1.607 +  // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
   1.608 +  // ``above'' another float that preceded it in the flow.
   1.609 +  mY = std::max(mFloatManager->GetLowestFloatTop(), mY);
   1.610 +
   1.611 +  // See if the float should clear any preceding floats...
   1.612 +  // XXX We need to mark this float somehow so that it gets reflowed
   1.613 +  // when floats are inserted before it.
   1.614 +  if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
   1.615 +    // XXXldb Does this handle vertical margins correctly?
   1.616 +    mY = ClearFloats(mY, floatDisplay->mBreakType);
   1.617 +  }
   1.618 +    // Get the band of available space
   1.619 +  nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mY);
   1.620 +  nsRect adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
   1.621 +                                    floatAvailableSpace.mRect, aFloat);
   1.622 +
   1.623 +  NS_ASSERTION(aFloat->GetParent() == mBlock,
   1.624 +               "Float frame has wrong parent");
   1.625 +
   1.626 +  nsCSSOffsetState offsets(aFloat, mReflowState.rendContext,
   1.627 +                           mReflowState.ComputedWidth());
   1.628 +
   1.629 +  nscoord floatMarginWidth = FloatMarginWidth(mReflowState,
   1.630 +                                              adjustedAvailableSpace.width,
   1.631 +                                              aFloat, offsets);
   1.632 +
   1.633 +  nsMargin floatMargin; // computed margin
   1.634 +  nsMargin floatOffsets;
   1.635 +  nsReflowStatus reflowStatus;
   1.636 +
   1.637 +  // If it's a floating first-letter, we need to reflow it before we
   1.638 +  // know how wide it is (since we don't compute which letters are part
   1.639 +  // of the first letter until reflow!).
   1.640 +  bool isLetter = aFloat->GetType() == nsGkAtoms::letterFrame;
   1.641 +  if (isLetter) {
   1.642 +    mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
   1.643 +                        floatOffsets, false, reflowStatus);
   1.644 +    floatMarginWidth = aFloat->GetSize().width + floatMargin.LeftRight();
   1.645 +    NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus),
   1.646 +                 "letter frames shouldn't break, and if they do now, "
   1.647 +                 "then they're breaking at the wrong point");
   1.648 +  }
   1.649 +
   1.650 +  // Find a place to place the float. The CSS2 spec doesn't want
   1.651 +  // floats overlapping each other or sticking out of the containing
   1.652 +  // block if possible (CSS2 spec section 9.5.1, see the rule list).
   1.653 +  NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) ||
   1.654 +	       (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats),
   1.655 +	       "invalid float type");
   1.656 +
   1.657 +  // Can the float fit here?
   1.658 +  bool keepFloatOnSameLine = false;
   1.659 +
   1.660 +  // Are we required to place at least part of the float because we're
   1.661 +  // at the top of the page (to avoid an infinite loop of pushing and
   1.662 +  // breaking).
   1.663 +  bool mustPlaceFloat =
   1.664 +    mReflowState.mFlags.mIsTopOfPage && IsAdjacentWithTop();
   1.665 +
   1.666 +  for (;;) {
   1.667 +    if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
   1.668 +        floatAvailableSpace.mRect.height <= 0 &&
   1.669 +        !mustPlaceFloat) {
   1.670 +      // No space, nowhere to put anything.
   1.671 +      PushFloatPastBreak(aFloat);
   1.672 +      return false;
   1.673 +    }
   1.674 +
   1.675 +    if (CanPlaceFloat(floatMarginWidth, floatAvailableSpace)) {
   1.676 +      // We found an appropriate place.
   1.677 +      break;
   1.678 +    }
   1.679 +
   1.680 +    // Nope. try to advance to the next band.
   1.681 +    if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
   1.682 +          eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
   1.683 +
   1.684 +      mY += floatAvailableSpace.mRect.height;
   1.685 +      if (adjustedAvailableSpace.height != NS_UNCONSTRAINEDSIZE) {
   1.686 +        adjustedAvailableSpace.height -= floatAvailableSpace.mRect.height;
   1.687 +      }
   1.688 +      floatAvailableSpace = GetFloatAvailableSpace(mY);
   1.689 +    } else {
   1.690 +      // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace
   1.691 +      // IE handles float tables in a very special way
   1.692 +
   1.693 +      // see if the previous float is also a table and has "align"
   1.694 +      nsFloatCache* fc = mCurrentLineFloats.Head();
   1.695 +      nsIFrame* prevFrame = nullptr;
   1.696 +      while (fc) {
   1.697 +        if (fc->mFloat == aFloat) {
   1.698 +          break;
   1.699 +        }
   1.700 +        prevFrame = fc->mFloat;
   1.701 +        fc = fc->Next();
   1.702 +      }
   1.703 +      
   1.704 +      if(prevFrame) {
   1.705 +        //get the frame type
   1.706 +        if (nsGkAtoms::tableOuterFrame == prevFrame->GetType()) {
   1.707 +          //see if it has "align="
   1.708 +          // IE makes a difference between align and he float property
   1.709 +          nsIContent* content = prevFrame->GetContent();
   1.710 +          if (content) {
   1.711 +            // we're interested only if previous frame is align=left
   1.712 +            // IE messes things up when "right" (overlapping frames) 
   1.713 +            if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align,
   1.714 +                                     NS_LITERAL_STRING("left"), eIgnoreCase)) {
   1.715 +              keepFloatOnSameLine = true;
   1.716 +              // don't advance to next line (IE quirkie behaviour)
   1.717 +              // it breaks rule CSS2/9.5.1/1, but what the hell
   1.718 +              // since we cannot evangelize the world
   1.719 +              break;
   1.720 +            }
   1.721 +          }
   1.722 +        }
   1.723 +      }
   1.724 +
   1.725 +      // the table does not fit anymore in this line so advance to next band 
   1.726 +      mY += floatAvailableSpace.mRect.height;
   1.727 +      // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to
   1.728 +      // get a new width for the new band.
   1.729 +      floatAvailableSpace = GetFloatAvailableSpace(mY);
   1.730 +      adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
   1.731 +                                 floatAvailableSpace.mRect, aFloat);
   1.732 +      floatMarginWidth = FloatMarginWidth(mReflowState,
   1.733 +                                          adjustedAvailableSpace.width,
   1.734 +                                          aFloat, offsets);
   1.735 +    }
   1.736 +
   1.737 +    mustPlaceFloat = false;
   1.738 +  }
   1.739 +
   1.740 +  // If the float is continued, it will get the same absolute x value as its prev-in-flow
   1.741 +
   1.742 +  // We don't worry about the geometry of the prev in flow, let the continuation
   1.743 +  // place and size itself as required.
   1.744 +
   1.745 +  // Assign an x and y coordinate to the float.
   1.746 +  nscoord floatX, floatY;
   1.747 +  if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) {
   1.748 +    floatX = floatAvailableSpace.mRect.x;
   1.749 +  }
   1.750 +  else {
   1.751 +    if (!keepFloatOnSameLine) {
   1.752 +      floatX = floatAvailableSpace.mRect.XMost() - floatMarginWidth;
   1.753 +    } 
   1.754 +    else {
   1.755 +      // this is the IE quirk (see few lines above)
   1.756 +      // the table is kept in the same line: don't let it overlap the
   1.757 +      // previous float 
   1.758 +      floatX = floatAvailableSpace.mRect.x;
   1.759 +    }
   1.760 +  }
   1.761 +  // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
   1.762 +  // be higher than the top of its containing block."  (Since the
   1.763 +  // containing block is the content edge of the block box, this
   1.764 +  // means the margin edge of the float can't be higher than the
   1.765 +  // content edge of the block that contains it.)
   1.766 +  floatY = std::max(mY, mContentArea.y);
   1.767 +
   1.768 +  // Reflow the float after computing its vertical position so it knows
   1.769 +  // where to break.
   1.770 +  if (!isLetter) {
   1.771 +    bool pushedDown = mY != saveY;
   1.772 +    mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin,
   1.773 +                        floatOffsets, pushedDown, reflowStatus);
   1.774 +  }
   1.775 +  if (aFloat->GetPrevInFlow())
   1.776 +    floatMargin.top = 0;
   1.777 +  if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
   1.778 +    floatMargin.bottom = 0;
   1.779 +
   1.780 +  // In the case that we're in columns and not splitting floats, we need
   1.781 +  // to check here that the float's height fit, and if it didn't, bail.
   1.782 +  // (This code is only for DISABLE_FLOAT_BREAKING_IN_COLUMNS .)
   1.783 +  //
   1.784 +  // Likewise, if none of the float fit, and it needs to be pushed in
   1.785 +  // its entirety to the next page (NS_FRAME_IS_TRUNCATED or
   1.786 +  // NS_INLINE_IS_BREAK_BEFORE), we need to do the same.
   1.787 +  if ((mContentArea.height != NS_UNCONSTRAINEDSIZE &&
   1.788 +       adjustedAvailableSpace.height == NS_UNCONSTRAINEDSIZE &&
   1.789 +       !mustPlaceFloat &&
   1.790 +       aFloat->GetSize().height + floatMargin.TopBottom() >
   1.791 +         mContentArea.YMost() - floatY) ||
   1.792 +      NS_FRAME_IS_TRUNCATED(reflowStatus) ||
   1.793 +      NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) {
   1.794 +    PushFloatPastBreak(aFloat);
   1.795 +    return false;
   1.796 +  }
   1.797 +
   1.798 +  // We can't use aFloat->ShouldAvoidBreakInside(mReflowState) here since
   1.799 +  // its mIsTopOfPage may be true even though the float isn't at the
   1.800 +  // top when floatY > 0.
   1.801 +  if (mContentArea.height != NS_UNCONSTRAINEDSIZE &&
   1.802 +      !mustPlaceFloat && (!mReflowState.mFlags.mIsTopOfPage || floatY > 0) &&
   1.803 +      NS_STYLE_PAGE_BREAK_AVOID == aFloat->StyleDisplay()->mBreakInside &&
   1.804 +      (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus) ||
   1.805 +       aFloat->GetSize().height + floatMargin.TopBottom() >
   1.806 +         mContentArea.YMost() - floatY) &&
   1.807 +      !aFloat->GetPrevInFlow()) {
   1.808 +    PushFloatPastBreak(aFloat);
   1.809 +    return false;
   1.810 +  }
   1.811 +
   1.812 +  // Calculate the actual origin of the float frame's border rect
   1.813 +  // relative to the parent block; the margin must be added in
   1.814 +  // to get the border rect
   1.815 +  nsPoint origin(floatMargin.left + floatX,
   1.816 +                 floatMargin.top + floatY);
   1.817 +
   1.818 +  // If float is relatively positioned, factor that in as well
   1.819 +  nsHTMLReflowState::ApplyRelativePositioning(aFloat, floatOffsets, &origin);
   1.820 +
   1.821 +  // Position the float and make sure and views are properly
   1.822 +  // positioned. We need to explicitly position its child views as
   1.823 +  // well, since we're moving the float after flowing it.
   1.824 +  bool moved = aFloat->GetPosition() != origin;
   1.825 +  if (moved) {
   1.826 +    aFloat->SetPosition(origin);
   1.827 +    nsContainerFrame::PositionFrameView(aFloat);
   1.828 +    nsContainerFrame::PositionChildViews(aFloat);
   1.829 +  }
   1.830 +
   1.831 +  // Update the float combined area state
   1.832 +  // XXX Floats should really just get invalidated here if necessary
   1.833 +  mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() + origin);
   1.834 +
   1.835 +  // Place the float in the float manager
   1.836 +  // calculate region
   1.837 +  nsRect region = nsFloatManager::CalculateRegionFor(aFloat, floatMargin);
   1.838 +  // if the float split, then take up all of the vertical height
   1.839 +  if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) &&
   1.840 +      (NS_UNCONSTRAINEDSIZE != mContentArea.height)) {
   1.841 +    region.height = std::max(region.height, mContentArea.height - floatY);
   1.842 +  }
   1.843 +  DebugOnly<nsresult> rv =
   1.844 +    mFloatManager->AddFloat(aFloat, region);
   1.845 +  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement");
   1.846 +  // store region
   1.847 +  nsFloatManager::StoreRegionFor(aFloat, region);
   1.848 +
   1.849 +  // If the float's dimensions have changed, note the damage in the
   1.850 +  // float manager.
   1.851 +  if (!region.IsEqualEdges(oldRegion)) {
   1.852 +    // XXXwaterson conservative: we could probably get away with noting
   1.853 +    // less damage; e.g., if only height has changed, then only note the
   1.854 +    // area into which the float has grown or from which the float has
   1.855 +    // shrunk.
   1.856 +    nscoord top = std::min(region.y, oldRegion.y);
   1.857 +    nscoord bottom = std::max(region.YMost(), oldRegion.YMost());
   1.858 +    mFloatManager->IncludeInDamage(top, bottom);
   1.859 +  }
   1.860 +
   1.861 +  if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) {
   1.862 +    mBlock->SplitFloat(*this, aFloat, reflowStatus);
   1.863 +  }
   1.864 +
   1.865 +#ifdef NOISY_FLOATMANAGER
   1.866 +  nscoord tx, ty;
   1.867 +  mFloatManager->GetTranslation(tx, ty);
   1.868 +  nsFrame::ListTag(stdout, mBlock);
   1.869 +  printf(": FlowAndPlaceFloat: AddFloat: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
   1.870 +         tx, ty, mFloatManagerX, mFloatManagerY,
   1.871 +         region.x, region.y, region.width, region.height);
   1.872 +#endif
   1.873 +
   1.874 +#ifdef DEBUG
   1.875 +  if (nsBlockFrame::gNoisyReflow) {
   1.876 +    nsRect r = aFloat->GetRect();
   1.877 +    nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.878 +    printf("placed float: ");
   1.879 +    nsFrame::ListTag(stdout, aFloat);
   1.880 +    printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
   1.881 +  }
   1.882 +#endif
   1.883 +
   1.884 +  return true;
   1.885 +}
   1.886 +
   1.887 +void
   1.888 +nsBlockReflowState::PushFloatPastBreak(nsIFrame *aFloat)
   1.889 +{
   1.890 +  // This ensures that we:
   1.891 +  //  * don't try to place later but smaller floats (which CSS says
   1.892 +  //    must have their tops below the top of this float)
   1.893 +  //  * don't waste much time trying to reflow this float again until
   1.894 +  //    after the break
   1.895 +  if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
   1.896 +    mFloatManager->SetPushedLeftFloatPastBreak();
   1.897 +  } else {
   1.898 +    NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats ==
   1.899 +                        NS_STYLE_FLOAT_RIGHT,
   1.900 +                      "unexpected float value");
   1.901 +    mFloatManager->SetPushedRightFloatPastBreak();
   1.902 +  }
   1.903 +
   1.904 +  // Put the float on the pushed floats list, even though it
   1.905 +  // isn't actually a continuation.
   1.906 +  DebugOnly<nsresult> rv = mBlock->StealFrame(aFloat);
   1.907 +  NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed");
   1.908 +  AppendPushedFloat(aFloat);
   1.909 +
   1.910 +  NS_FRAME_SET_OVERFLOW_INCOMPLETE(mReflowStatus);
   1.911 +}
   1.912 +
   1.913 +/**
   1.914 + * Place below-current-line floats.
   1.915 + */
   1.916 +void
   1.917 +nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList,
   1.918 +                                                nsLineBox* aLine)
   1.919 +{
   1.920 +  nsFloatCache* fc = aList.Head();
   1.921 +  while (fc) {
   1.922 +#ifdef DEBUG
   1.923 +    if (nsBlockFrame::gNoisyReflow) {
   1.924 +      nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.925 +      printf("placing bcl float: ");
   1.926 +      nsFrame::ListTag(stdout, fc->mFloat);
   1.927 +      printf("\n");
   1.928 +    }
   1.929 +#endif
   1.930 +    // Place the float
   1.931 +    bool placed = FlowAndPlaceFloat(fc->mFloat);
   1.932 +    nsFloatCache *next = fc->Next();
   1.933 +    if (!placed) {
   1.934 +      aList.Remove(fc);
   1.935 +      delete fc;
   1.936 +      aLine->SetHadFloatPushed();
   1.937 +    }
   1.938 +    fc = next;
   1.939 +  }
   1.940 +}
   1.941 +
   1.942 +nscoord
   1.943 +nsBlockReflowState::ClearFloats(nscoord aY, uint8_t aBreakType,
   1.944 +                                nsIFrame *aReplacedBlock,
   1.945 +                                uint32_t aFlags)
   1.946 +{
   1.947 +#ifdef DEBUG
   1.948 +  if (nsBlockFrame::gNoisyReflow) {
   1.949 +    nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1.950 +    printf("clear floats: in: aY=%d\n", aY);
   1.951 +  }
   1.952 +#endif
   1.953 +
   1.954 +#ifdef NOISY_FLOAT_CLEARING
   1.955 +  printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
   1.956 +         aY, aBreakType);
   1.957 +  mFloatManager->List(stdout);
   1.958 +#endif
   1.959 +
   1.960 +  if (!mFloatManager->HasAnyFloats()) {
   1.961 +    return aY;
   1.962 +  }
   1.963 +
   1.964 +  nscoord newY = aY;
   1.965 +
   1.966 +  if (aBreakType != NS_STYLE_CLEAR_NONE) {
   1.967 +    newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags);
   1.968 +  }
   1.969 +
   1.970 +  if (aReplacedBlock) {
   1.971 +    for (;;) {
   1.972 +      nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newY);
   1.973 +      if (!floatAvailableSpace.mHasFloats) {
   1.974 +        // If there aren't any floats here, then we always fit.
   1.975 +        // We check this before calling WidthToClearPastFloats, which is
   1.976 +        // somewhat expensive.
   1.977 +        break;
   1.978 +      }
   1.979 +      nsBlockFrame::ReplacedElementWidthToClear replacedWidth =
   1.980 +        nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect,
   1.981 +                                             aReplacedBlock);
   1.982 +      if (std::max(floatAvailableSpace.mRect.x - mContentArea.x,
   1.983 +                 replacedWidth.marginLeft) +
   1.984 +            replacedWidth.borderBoxWidth +
   1.985 +            std::max(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(),
   1.986 +                   replacedWidth.marginRight) <=
   1.987 +          mContentArea.width) {
   1.988 +        break;
   1.989 +      }
   1.990 +      // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
   1.991 +      if (floatAvailableSpace.mRect.height > 0) {
   1.992 +        // See if there's room in the next band.
   1.993 +        newY += floatAvailableSpace.mRect.height;
   1.994 +      } else {
   1.995 +        if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
   1.996 +          // Stop trying to clear here; we'll just get pushed to the
   1.997 +          // next column or page and try again there.
   1.998 +          break;
   1.999 +        }
  1.1000 +        NS_NOTREACHED("avail space rect with zero height!");
  1.1001 +        newY += 1;
  1.1002 +      }
  1.1003 +    }
  1.1004 +  }
  1.1005 +
  1.1006 +#ifdef DEBUG
  1.1007 +  if (nsBlockFrame::gNoisyReflow) {
  1.1008 +    nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
  1.1009 +    printf("clear floats: out: y=%d\n", newY);
  1.1010 +  }
  1.1011 +#endif
  1.1012 +
  1.1013 +  return newY;
  1.1014 +}
  1.1015 +

mercurial