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

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

mercurial