BlockReflowState.cpp (44032B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* state used in reflow of block frames */ 8 9 #include "BlockReflowState.h" 10 11 #include <algorithm> 12 13 #include "LayoutLogging.h" 14 #include "TextOverflow.h" 15 #include "fmt/format.h" 16 #include "mozilla/AutoRestore.h" 17 #include "mozilla/Preferences.h" 18 #include "mozilla/StaticPrefs_layout.h" 19 #include "nsBlockFrame.h" 20 #include "nsIFrameInlines.h" 21 #include "nsLineLayout.h" 22 #include "nsPresContext.h" 23 24 #ifdef DEBUG 25 # include "nsBlockDebugFlags.h" 26 #endif 27 28 using namespace mozilla; 29 using namespace mozilla::layout; 30 31 BlockReflowState::BlockReflowState( 32 const ReflowInput& aReflowInput, nsPresContext* aPresContext, 33 nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot, 34 bool aBlockNeedsFloatManager, const nscoord aConsumedBSize, 35 const nscoord aEffectiveContentBoxBSize, const nscoord aInset) 36 : mBlock(aFrame), 37 mPresContext(aPresContext), 38 mReflowInput(aReflowInput), 39 mContentArea(aReflowInput.GetWritingMode()), 40 mInsetForBalance(aInset), 41 mContainerSize(aReflowInput.ComputedSizeAsContainerIfConstrained()), 42 mOverflowTracker(nullptr), 43 mBorderPadding( 44 mReflowInput 45 .ComputedLogicalBorderPadding(mReflowInput.GetWritingMode()) 46 .ApplySkipSides(aFrame->PreReflowBlockLevelLogicalSkipSides())), 47 mMinLineHeight(aReflowInput.GetLineHeight()), 48 mLineNumber(0), 49 mTrailingClearFromPIF(UsedClear::None), 50 mConsumedBSize(aConsumedBSize), 51 mAlignContentShift(mBlock->GetAlignContentShift()) { 52 NS_ASSERTION(mConsumedBSize != NS_UNCONSTRAINEDSIZE, 53 "The consumed block-size should be constrained!"); 54 55 WritingMode wm = aReflowInput.GetWritingMode(); 56 57 if (aBStartMarginRoot || 0 != mBorderPadding.BStart(wm)) { 58 mFlags.mIsBStartMarginRoot = true; 59 mFlags.mShouldApplyBStartMargin = true; 60 } 61 if (aBEndMarginRoot || 0 != mBorderPadding.BEnd(wm)) { 62 mFlags.mIsBEndMarginRoot = true; 63 } 64 if (aBlockNeedsFloatManager) { 65 mFlags.mBlockNeedsFloatManager = true; 66 } 67 68 mFlags.mCanHaveOverflowMarkers = css::TextOverflow::CanHaveOverflowMarkers( 69 mBlock, css::TextOverflow::BeforeReflow::Yes); 70 71 MOZ_ASSERT(FloatManager(), 72 "Float manager should be valid when creating BlockReflowState!"); 73 74 // Save the coordinate system origin for later. 75 FloatManager()->GetTranslation(mFloatManagerI, mFloatManagerB); 76 FloatManager()->PushState(&mFloatManagerStateBefore); // never popped 77 78 mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); 79 80 LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedISize(), 81 "have unconstrained width; this should only result " 82 "from very large sizes, not attempts at intrinsic " 83 "width calculation"); 84 mContentArea.ISize(wm) = aReflowInput.ComputedISize(); 85 86 // Compute content area block-size. Unlike the inline-size, if we have a 87 // specified style block-size, we ignore it since extra content is managed by 88 // the "overflow" property. When we don't have a specified style block-size, 89 // then we may end up limiting our block-size if the available block-size is 90 // constrained (this situation occurs when we are paginated). 91 const nscoord availableBSize = aReflowInput.AvailableBSize(); 92 if (availableBSize != NS_UNCONSTRAINEDSIZE) { 93 // We are in a paginated situation. The block-end edge of the available 94 // space to reflow the children is within our block-end border and padding. 95 // If we're cloning our border and padding, and we're going to request 96 // additional continuations because of our excessive content-box block-size, 97 // then reserve some of our available space for our (cloned) block-end 98 // border and padding. 99 const bool reserveSpaceForBlockEndBP = 100 mReflowInput.mStyleBorder->mBoxDecorationBreak == 101 StyleBoxDecorationBreak::Clone && 102 (aEffectiveContentBoxBSize == NS_UNCONSTRAINEDSIZE || 103 aEffectiveContentBoxBSize + mBorderPadding.BStartEnd(wm) > 104 availableBSize); 105 const nscoord bp = reserveSpaceForBlockEndBP ? mBorderPadding.BStartEnd(wm) 106 : mBorderPadding.BStart(wm); 107 mContentArea.BSize(wm) = std::max(0, availableBSize - bp); 108 } else { 109 // When we are not in a paginated situation, then we always use a 110 // unconstrained block-size. 111 mContentArea.BSize(wm) = NS_UNCONSTRAINEDSIZE; 112 } 113 mContentArea.IStart(wm) = mBorderPadding.IStart(wm); 114 mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); 115 116 // Account for existing cached shift, we'll re-position in AlignContent() if 117 // needed. 118 if (mAlignContentShift) { 119 mBCoord += mAlignContentShift; 120 mContentArea.BStart(wm) += mAlignContentShift; 121 122 if (availableBSize != NS_UNCONSTRAINEDSIZE) { 123 mContentArea.BSize(wm) += mAlignContentShift; 124 } 125 } 126 127 mPrevChild = nullptr; 128 mCurrentLine = aFrame->LinesEnd(); 129 } 130 131 void BlockReflowState::UndoAlignContentShift() { 132 if (!mAlignContentShift) { 133 return; 134 } 135 136 mBCoord -= mAlignContentShift; 137 mContentArea.BStart(mReflowInput.GetWritingMode()) -= mAlignContentShift; 138 139 if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) { 140 mContentArea.BSize(mReflowInput.GetWritingMode()) -= mAlignContentShift; 141 } 142 } 143 144 void BlockReflowState::ComputeFloatAvoidingOffsets( 145 nsIFrame* aFloatAvoidingBlock, const LogicalRect& aFloatAvailableSpace, 146 nscoord& aIStartResult, nscoord& aIEndResult) const { 147 WritingMode wm = mReflowInput.GetWritingMode(); 148 // The frame is clueless about the float manager and therefore we 149 // only give it free space. An example is a table frame - the 150 // tables do not flow around floats. 151 // However, we can let its margins intersect floats. 152 NS_ASSERTION(aFloatAvailableSpace.IStart(wm) >= mContentArea.IStart(wm), 153 "bad avail space rect inline-coord"); 154 NS_ASSERTION(aFloatAvailableSpace.ISize(wm) == 0 || 155 aFloatAvailableSpace.IEnd(wm) <= mContentArea.IEnd(wm), 156 "bad avail space rect inline-size"); 157 158 nscoord iStartOffset, iEndOffset; 159 if (aFloatAvailableSpace.ISize(wm) == mContentArea.ISize(wm)) { 160 // We don't need to compute margins when there are no floats around. 161 iStartOffset = 0; 162 iEndOffset = 0; 163 } else { 164 const LogicalMargin frameMargin = 165 SizeComputationInput(aFloatAvoidingBlock, 166 mReflowInput.mRenderingContext, wm, 167 mContentArea.ISize(wm)) 168 .ComputedLogicalMargin(wm); 169 170 nscoord iStartFloatIOffset = 171 aFloatAvailableSpace.IStart(wm) - mContentArea.IStart(wm); 172 iStartOffset = std::max(iStartFloatIOffset, frameMargin.IStart(wm)) - 173 frameMargin.IStart(wm); 174 iStartOffset = std::max(iStartOffset, 0); // in case of negative margin 175 nscoord iEndFloatIOffset = 176 mContentArea.IEnd(wm) - aFloatAvailableSpace.IEnd(wm); 177 iEndOffset = 178 std::max(iEndFloatIOffset, frameMargin.IEnd(wm)) - frameMargin.IEnd(wm); 179 iEndOffset = std::max(iEndOffset, 0); // in case of negative margin 180 } 181 aIStartResult = iStartOffset; 182 aIEndResult = iEndOffset; 183 } 184 185 LogicalRect BlockReflowState::ComputeBlockAvailSpace( 186 nsIFrame* aFrame, const nsFlowAreaRect& aFloatAvailableSpace, 187 bool aBlockAvoidsFloats) { 188 #ifdef REALLY_NOISY_REFLOW 189 printf("CBAS frame=%p has floats %d\n", aFrame, 190 aFloatAvailableSpace.HasFloats()); 191 #endif 192 WritingMode wm = mReflowInput.GetWritingMode(); 193 LogicalRect result(wm); 194 result.BStart(wm) = mBCoord; 195 // Note: ContentBSize() and ContentBEnd() are not our content-box size and its 196 // block-end edge. They really mean "the available block-size for children", 197 // and "the block-end edge of the available space for children". 198 result.BSize(wm) = ContentBSize() == NS_UNCONSTRAINEDSIZE 199 ? NS_UNCONSTRAINEDSIZE 200 : ContentBEnd() - mBCoord; 201 // mBCoord might be greater than ContentBEnd() if the block's top margin 202 // pushes it off the page/column. Negative available block-size can confuse 203 // other code and is nonsense in principle. 204 205 // XXX Do we really want this condition to be this restrictive (i.e., 206 // more restrictive than it used to be)? The |else| here is allowed 207 // by the CSS spec, but only out of desperation given implementations, 208 // and the behavior it leads to is quite undesirable (it can cause 209 // things to become extremely narrow when they'd fit quite well a 210 // little bit lower). Should the else be a quirk or something that 211 // applies to a specific set of frame classes and no new ones? 212 // If we did that, then for those frames where the condition below is 213 // true but nsBlockFrame::BlockCanIntersectFloats is false, 214 // nsBlockFrame::ISizeToClearPastFloats would need to use the 215 // shrink-wrap formula, max(MinISize, min(avail width, PrefISize)) 216 // rather than just using MinISize. 217 NS_ASSERTION( 218 nsBlockFrame::BlockCanIntersectFloats(aFrame) == !aBlockAvoidsFloats, 219 "unexpected replaced width"); 220 if (!aBlockAvoidsFloats) { 221 if (aFloatAvailableSpace.HasFloats()) { 222 // Use the float-edge property to determine how the child block 223 // will interact with the float. 224 const nsStyleBorder* borderStyle = aFrame->StyleBorder(); 225 switch (borderStyle->mFloatEdge) { 226 default: 227 case StyleFloatEdge::ContentBox: // content and only content does 228 // runaround of floats 229 // The child block will flow around the float. Therefore 230 // give it all of the available space. 231 result.IStart(wm) = mContentArea.IStart(wm); 232 result.ISize(wm) = mContentArea.ISize(wm); 233 break; 234 case StyleFloatEdge::MarginBox: { 235 // The child block's margins should be placed adjacent to, 236 // but not overlap the float. 237 result.IStart(wm) = aFloatAvailableSpace.mRect.IStart(wm); 238 result.ISize(wm) = aFloatAvailableSpace.mRect.ISize(wm); 239 } break; 240 } 241 } else { 242 // Since there are no floats present the float-edge property 243 // doesn't matter therefore give the block element all of the 244 // available space since it will flow around the float itself. 245 result.IStart(wm) = mContentArea.IStart(wm); 246 result.ISize(wm) = mContentArea.ISize(wm); 247 } 248 } else { 249 nscoord iStartOffset, iEndOffset; 250 ComputeFloatAvoidingOffsets(aFrame, aFloatAvailableSpace.mRect, 251 iStartOffset, iEndOffset); 252 result.IStart(wm) = mContentArea.IStart(wm) + iStartOffset; 253 result.ISize(wm) = mContentArea.ISize(wm) - iStartOffset - iEndOffset; 254 } 255 256 #ifdef REALLY_NOISY_REFLOW 257 printf(" CBAS: result %d %d %d %d\n", result.IStart(wm), result.BStart(wm), 258 result.ISize(wm), result.BSize(wm)); 259 #endif 260 261 return result; 262 } 263 264 LogicalSize BlockReflowState::ComputeAvailableSizeForFloat() const { 265 const auto wm = mReflowInput.GetWritingMode(); 266 const nscoord availBSize = ContentBSize() == NS_UNCONSTRAINEDSIZE 267 ? NS_UNCONSTRAINEDSIZE 268 : std::max(0, ContentBEnd() - mBCoord); 269 return LogicalSize(wm, ContentISize(), availBSize); 270 } 271 272 bool BlockReflowState::FloatAvoidingBlockFitsInAvailSpace( 273 nsIFrame* aFloatAvoidingBlock, 274 const nsFlowAreaRect& aFloatAvailableSpace) const { 275 if (!aFloatAvailableSpace.HasFloats()) { 276 // If there aren't any floats here, then we always fit. 277 // We check this before calling ISizeToClearPastFloats, which is 278 // somewhat expensive. 279 return true; 280 } 281 282 // |aFloatAvailableSpace| was computed as having a negative size, which means 283 // there are floats on both sides pushing inwards past each other, and 284 // |aFloatAvoidingBlock| would necessarily intersect a float if we put it 285 // here. So, it doesn't fit. 286 if (aFloatAvailableSpace.ISizeIsActuallyNegative()) { 287 return false; 288 } 289 290 WritingMode wm = mReflowInput.GetWritingMode(); 291 nsBlockFrame::FloatAvoidingISizeToClear replacedISize = 292 nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect, 293 aFloatAvoidingBlock); 294 // The inline-start side of the replaced element should be offset by 295 // the larger of the float intrusion or the replaced element's own 296 // start margin. The inline-end side is similar, except for Web 297 // compatibility we ignore the margin. 298 return std::max( 299 aFloatAvailableSpace.mRect.IStart(wm) - mContentArea.IStart(wm), 300 replacedISize.marginIStart) + 301 replacedISize.borderBoxISize + 302 (mContentArea.IEnd(wm) - aFloatAvailableSpace.mRect.IEnd(wm)) <= 303 mContentArea.ISize(wm); 304 } 305 306 nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceWithState( 307 WritingMode aCBWM, nscoord aBCoord, ShapeType aShapeType, 308 nsFloatManager::SavedState* aState) const { 309 WritingMode wm = mReflowInput.GetWritingMode(); 310 #ifdef DEBUG 311 // Verify that the caller setup the coordinate system properly 312 nscoord wI, wB; 313 FloatManager()->GetTranslation(wI, wB); 314 315 NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB), 316 "bad coord system"); 317 #endif 318 319 nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX) 320 ? nscoord_MAX 321 : std::max(mContentArea.BEnd(wm) - aBCoord, 0); 322 nsFlowAreaRect result = FloatManager()->GetFlowArea( 323 aCBWM, wm, aBCoord, blockSize, BandInfoType::BandFromPoint, aShapeType, 324 mContentArea, aState, ContainerSize()); 325 // Keep the inline size >= 0 for compatibility with nsSpaceManager. 326 if (result.mRect.ISize(wm) < 0) { 327 result.mRect.ISize(wm) = 0; 328 } 329 330 #ifdef DEBUG 331 if (nsBlockFrame::gNoisyReflow) { 332 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 333 fmt::println(FMT_STRING("{} band={} hasFloats={}"), __func__, 334 ToString(result.mRect), YesOrNo(result.HasFloats())); 335 } 336 #endif 337 return result; 338 } 339 340 nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceForBSize( 341 WritingMode aCBWM, nscoord aBCoord, nscoord aBSize, 342 nsFloatManager::SavedState* aState) const { 343 WritingMode wm = mReflowInput.GetWritingMode(); 344 #ifdef DEBUG 345 // Verify that the caller setup the coordinate system properly 346 nscoord wI, wB; 347 FloatManager()->GetTranslation(wI, wB); 348 349 NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB), 350 "bad coord system"); 351 #endif 352 nsFlowAreaRect result = FloatManager()->GetFlowArea( 353 aCBWM, wm, aBCoord, aBSize, BandInfoType::WidthWithinHeight, 354 ShapeType::ShapeOutside, mContentArea, aState, ContainerSize()); 355 // Keep the width >= 0 for compatibility with nsSpaceManager. 356 if (result.mRect.ISize(wm) < 0) { 357 result.mRect.ISize(wm) = 0; 358 } 359 360 #ifdef DEBUG 361 if (nsBlockFrame::gNoisyReflow) { 362 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 363 fmt::println(FMT_STRING("{} band={} hasFloats={}"), __func__, 364 ToString(result.mRect), YesOrNo(result.HasFloats())); 365 } 366 #endif 367 return result; 368 } 369 370 /* 371 * Reconstruct the vertical margin before the line |aLine| in order to 372 * do an incremental reflow that begins with |aLine| without reflowing 373 * the line before it. |aLine| may point to the fencepost at the end of 374 * the line list, and it is used this way since we (for now, anyway) 375 * always need to recover margins at the end of a block. 376 * 377 * The reconstruction involves walking backward through the line list to 378 * find any collapsed margins preceding the line that would have been in 379 * the reflow input's |mPrevBEndMargin| when we reflowed that line in 380 * a full reflow (under the rule in CSS2 that all adjacent vertical 381 * margins of blocks collapse). 382 */ 383 void BlockReflowState::ReconstructMarginBefore(nsLineList::iterator aLine) { 384 mPrevBEndMargin.Zero(); 385 nsBlockFrame* block = mBlock; 386 387 nsLineList::iterator firstLine = block->LinesBegin(); 388 for (;;) { 389 --aLine; 390 if (aLine->IsBlock()) { 391 mPrevBEndMargin = aLine->GetCarriedOutBEndMargin(); 392 break; 393 } 394 if (!aLine->IsEmpty()) { 395 break; 396 } 397 if (aLine == firstLine) { 398 // If the top margin was carried out (and thus already applied), 399 // set it to zero. Either way, we're done. 400 if (!mFlags.mIsBStartMarginRoot) { 401 mPrevBEndMargin.Zero(); 402 } 403 break; 404 } 405 } 406 } 407 408 void BlockReflowState::AppendPushedFloatChain(nsIFrame* aFloatCont) { 409 nsFrameList* pushedFloats = mBlock->EnsurePushedFloats(); 410 while (true) { 411 aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW); 412 pushedFloats->AppendFrame(mBlock, aFloatCont); 413 aFloatCont = aFloatCont->GetNextInFlow(); 414 if (!aFloatCont || aFloatCont->GetParent() != mBlock) { 415 break; 416 } 417 mBlock->StealFrame(aFloatCont); 418 } 419 } 420 421 /** 422 * Restore information about floats into the float manager for an 423 * incremental reflow, and simultaneously push the floats by 424 * |aDeltaBCoord|, which is the amount |aLine| was pushed relative to its 425 * parent. The recovery of state is one of the things that makes 426 * incremental reflow O(N^2) and this state should really be kept 427 * around, attached to the frame tree. 428 */ 429 void BlockReflowState::RecoverFloats(nsLineList::iterator aLine, 430 nscoord aDeltaBCoord) { 431 WritingMode wm = mReflowInput.GetWritingMode(); 432 if (aLine->HasFloats()) { 433 // Place the floats into the float manager again. Also slide 434 // them, just like the regular frames on the line. 435 for (nsIFrame* floatFrame : aLine->Floats()) { 436 if (aDeltaBCoord != 0) { 437 floatFrame->MovePositionBy(nsPoint(0, aDeltaBCoord)); 438 } 439 #ifdef DEBUG 440 if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { 441 nscoord tI, tB; 442 FloatManager()->GetTranslation(tI, tB); 443 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 444 printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI, tB, mFloatManagerI, 445 mFloatManagerB); 446 floatFrame->ListTag(stdout); 447 LogicalRect region = 448 nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()); 449 printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n", aDeltaBCoord, 450 region.IStart(wm), region.BStart(wm), region.ISize(wm), 451 region.BSize(wm)); 452 } 453 #endif 454 FloatManager()->AddFloat( 455 floatFrame, 456 nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()), wm, 457 ContainerSize()); 458 } 459 } else if (aLine->IsBlock()) { 460 nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *FloatManager(), wm, 461 ContainerSize()); 462 } 463 } 464 465 /** 466 * Everything done in this function is done O(N) times for each pass of 467 * reflow so it is O(N*M) where M is the number of incremental reflow 468 * passes. That's bad. Don't do stuff here. 469 * 470 * When this function is called, |aLine| has just been slid by |aDeltaBCoord| 471 * and the purpose of RecoverStateFrom is to ensure that the 472 * BlockReflowState is in the same state that it would have been in 473 * had the line just been reflowed. 474 * 475 * Most of the state recovery that we have to do involves floats. 476 */ 477 void BlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, 478 nscoord aDeltaBCoord) { 479 // Make the line being recovered the current line 480 mCurrentLine = aLine; 481 482 // Place floats for this line into the float manager 483 if (aLine->HasFloats() || aLine->IsBlock()) { 484 RecoverFloats(aLine, aDeltaBCoord); 485 486 #ifdef DEBUG 487 if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { 488 FloatManager()->List(stdout); 489 } 490 #endif 491 } 492 } 493 494 // This is called by the line layout's AddFloat method when a 495 // place-holder frame is reflowed in a line. If the float is a 496 // left-most child (it's x coordinate is at the line's left margin) 497 // then the float is place immediately, otherwise the float 498 // placement is deferred until the line has been reflowed. 499 500 // XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- 501 // technically we're supposed let the current line flow around the 502 // float as well unless it won't fit next to what we already have. 503 // But nobody else implements it that way... 504 bool BlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, 505 nscoord aAvailableISize) { 506 MOZ_ASSERT(aLineLayout, "must have line layout"); 507 MOZ_ASSERT(mBlock->LinesEnd() != mCurrentLine, "null ptr"); 508 MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), 509 "aFloat must be an out-of-flow frame"); 510 511 MOZ_ASSERT(aFloat->GetParent(), "float must have parent"); 512 MOZ_ASSERT(aFloat->GetParent()->IsBlockFrameOrSubclass(), 513 "float's parent must be block"); 514 if (aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW) || 515 aFloat->GetParent() != mBlock) { 516 MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW | 517 NS_FRAME_FIRST_REFLOW), 518 "float should be in this block unless it was marked as " 519 "pushed out-of-flow, or just inserted"); 520 MOZ_ASSERT(aFloat->GetParent()->FirstContinuation() == 521 mBlock->FirstContinuation()); 522 // If, in a previous reflow, the float was pushed entirely to 523 // another column/page, we need to steal it back. (We might just 524 // push it again, though.) Likewise, if that previous reflow 525 // reflowed this block but not its next continuation, we might need 526 // to steal it from our own float-continuations list. 527 // 528 // For more about pushed floats, see the comment above 529 // nsBlockFrame::DrainPushedFloats. 530 auto* floatParent = static_cast<nsBlockFrame*>(aFloat->GetParent()); 531 floatParent->StealFrame(aFloat); 532 533 aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW); 534 535 // Appending is fine, since if a float was pushed to the next 536 // page/column, all later floats were also pushed. 537 mBlock->EnsureFloats()->AppendFrame(mBlock, aFloat); 538 } 539 540 // Because we are in the middle of reflowing a placeholder frame 541 // within a line (and possibly nested in an inline frame or two 542 // that's a child of our block) we need to restore the space 543 // manager's translation to the space that the block resides in 544 // before placing the float. 545 nscoord oI, oB; 546 FloatManager()->GetTranslation(oI, oB); 547 nscoord dI = oI - mFloatManagerI; 548 nscoord dB = oB - mFloatManagerB; 549 FloatManager()->Translate(-dI, -dB); 550 551 bool placed = false; 552 553 // Now place the float immediately if possible. Otherwise stash it 554 // away in mBelowCurrentLineFloats and place it later. 555 // If one or more floats has already been pushed to the next line, 556 // don't let this one go on the current line, since that would violate 557 // float ordering. 558 bool shouldPlaceFloatBelowCurrentLine = false; 559 if (mBelowCurrentLineFloats.IsEmpty()) { 560 // If the current line is empty, we don't impose any inline-size constraint 561 // from the line layout. 562 Maybe<nscoord> availableISizeInCurrentLine = 563 aLineLayout->LineIsEmpty() ? Nothing() : Some(aAvailableISize); 564 PlaceFloatResult result = 565 FlowAndPlaceFloat(aFloat, availableISizeInCurrentLine); 566 if (result == PlaceFloatResult::Placed) { 567 placed = true; 568 // Pass on updated available space to the current inline reflow engine 569 WritingMode wm = mReflowInput.GetWritingMode(); 570 // If we have mLineBSize, we are reflowing the line again due to 571 // LineReflowStatus::RedoMoreFloats. We should use mLineBSize to query the 572 // correct available space. 573 nsFlowAreaRect floatAvailSpace = 574 mLineBSize.isNothing() 575 ? GetFloatAvailableSpace(wm, mBCoord) 576 : GetFloatAvailableSpaceForBSize(wm, mBCoord, mLineBSize.value(), 577 nullptr); 578 LogicalRect availSpace(wm, floatAvailSpace.mRect.IStart(wm), mBCoord, 579 floatAvailSpace.mRect.ISize(wm), 580 floatAvailSpace.mRect.BSize(wm)); 581 aLineLayout->UpdateBand(wm, availSpace, aFloat); 582 // Record this float in the current-line list 583 mCurrentLineFloats.AppendElement(aFloat); 584 } else if (result == PlaceFloatResult::ShouldPlaceInNextContinuation) { 585 (*aLineLayout->GetLine())->SetHadFloatPushed(); 586 } else { 587 MOZ_ASSERT(result == PlaceFloatResult::ShouldPlaceBelowCurrentLine); 588 shouldPlaceFloatBelowCurrentLine = true; 589 } 590 } else { 591 shouldPlaceFloatBelowCurrentLine = true; 592 } 593 594 if (shouldPlaceFloatBelowCurrentLine) { 595 // Always claim to be placed; we don't know whether we fit yet, so we 596 // deal with this in PlaceBelowCurrentLineFloats 597 placed = true; 598 // This float will be placed after the line is done (it is a 599 // below-current-line float). 600 mBelowCurrentLineFloats.AppendElement(aFloat); 601 } 602 603 // Restore coordinate system 604 FloatManager()->Translate(dI, dB); 605 606 return placed; 607 } 608 609 bool BlockReflowState::CanPlaceFloat( 610 nscoord aFloatISize, const nsFlowAreaRect& aFloatAvailableSpace) { 611 // A float fits at a given block-dir position if there are no floats 612 // at its inline-dir position (no matter what its inline size) or if 613 // its inline size fits in the space remaining after prior floats have 614 // been placed. 615 // FIXME: We should allow overflow by up to half a pixel here (bug 21193). 616 return !aFloatAvailableSpace.HasFloats() || 617 aFloatAvailableSpace.mRect.ISize(mReflowInput.GetWritingMode()) >= 618 aFloatISize; 619 } 620 621 // Return the inline-size that the float (including margins) will take up 622 // in the writing mode of the containing block. If this returns 623 // NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that 624 // has block-size:auto, and we'll need to actually reflow it to find out 625 // how much inline-size it will occupy in the containing block's mode. 626 static nscoord FloatMarginISize(WritingMode aCBWM, 627 const ReflowInput& aFloatRI) { 628 if (aFloatRI.ComputedSize(aCBWM).ISize(aCBWM) == NS_UNCONSTRAINEDSIZE) { 629 return NS_UNCONSTRAINEDSIZE; // reflow is needed to get the true size 630 } 631 return aFloatRI.ComputedSizeWithMarginBorderPadding(aCBWM).ISize(aCBWM); 632 } 633 634 // A frame property that stores the last shape source / margin / etc. if there's 635 // any shape, in order to invalidate the float area properly when it changes. 636 // 637 // TODO(emilio): This could really belong to GetRegionFor / StoreRegionFor, but 638 // when I tried it was a bit awkward because of the logical -> physical 639 // conversion that happens there. 640 // 641 // Maybe all this code could be refactored to make this cleaner, but keeping the 642 // two properties separated was slightly nicer. 643 struct ShapeInvalidationData { 644 StyleShapeOutside mShapeOutside{StyleShapeOutside::None()}; 645 float mShapeImageThreshold = 0.0; 646 LengthPercentage mShapeMargin; 647 648 ShapeInvalidationData() = default; 649 650 explicit ShapeInvalidationData(const nsStyleDisplay& aDisplay) { 651 Update(aDisplay); 652 } 653 654 static bool IsNeeded(const nsStyleDisplay& aDisplay) { 655 return !aDisplay.mShapeOutside.IsNone(); 656 } 657 658 void Update(const nsStyleDisplay& aDisplay) { 659 MOZ_ASSERT(IsNeeded(aDisplay)); 660 mShapeOutside = aDisplay.mShapeOutside; 661 mShapeImageThreshold = aDisplay.mShapeImageThreshold; 662 mShapeMargin = aDisplay.mShapeMargin; 663 } 664 665 bool Matches(const nsStyleDisplay& aDisplay) const { 666 return mShapeOutside == aDisplay.mShapeOutside && 667 mShapeImageThreshold == aDisplay.mShapeImageThreshold && 668 mShapeMargin == aDisplay.mShapeMargin; 669 } 670 }; 671 672 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ShapeInvalidationDataProperty, 673 ShapeInvalidationData) 674 675 BlockReflowState::PlaceFloatResult BlockReflowState::FlowAndPlaceFloat( 676 nsIFrame* aFloat, Maybe<nscoord> aAvailableISizeInCurrentLine) { 677 MOZ_ASSERT(aFloat->GetParent() == mBlock, "Float frame has wrong parent"); 678 679 WritingMode wm = mReflowInput.GetWritingMode(); 680 // Save away the block-dir coordinate before placing the float. We will 681 // restore mBCoord at the end after placing the float. This is 682 // necessary because any adjustments to mBCoord during the float 683 // placement are for the float only, not for any non-floating 684 // content. 685 AutoRestore<nscoord> restoreBCoord(mBCoord); 686 687 // Whether the block-direction position available to place a float has been 688 // pushed down due to the presence of other floats. 689 auto HasFloatPushedDown = [this, &restoreBCoord]() { 690 return mBCoord != restoreBCoord.SavedValue(); 691 }; 692 693 // Grab the float's display information 694 const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay(); 695 696 // The float's old region, so we can propagate damage. 697 LogicalRect oldRegion = 698 nsFloatManager::GetRegionFor(wm, aFloat, ContainerSize()); 699 700 ShapeInvalidationData* invalidationData = 701 aFloat->GetProperty(ShapeInvalidationDataProperty()); 702 703 // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't 704 // ``above'' another float that preceded it in the flow. 705 mBCoord = std::max(FloatManager()->LowestFloatBStart(), mBCoord); 706 707 // See if the float should clear any preceding floats... 708 // XXX We need to mark this float somehow so that it gets reflowed 709 // when floats are inserted before it. 710 if (StyleClear::None != floatDisplay->mClear) { 711 // XXXldb Does this handle vertical margins correctly? 712 auto [bCoord, result] = ClearFloats(mBCoord, floatDisplay->UsedClear(wm)); 713 if (result == ClearFloatsResult::FloatsPushedOrSplit) { 714 PushFloatPastBreak(aFloat); 715 return PlaceFloatResult::ShouldPlaceInNextContinuation; 716 } 717 mBCoord = bCoord; 718 } 719 720 LogicalSize availSize = ComputeAvailableSizeForFloat(); 721 const WritingMode floatWM = aFloat->GetWritingMode(); 722 Maybe<ReflowInput> floatRI(std::in_place, mPresContext, mReflowInput, aFloat, 723 availSize.ConvertTo(floatWM, wm)); 724 725 nscoord floatMarginISize = FloatMarginISize(wm, *floatRI); 726 LogicalMargin floatMargin = floatRI->ComputedLogicalMargin(wm); 727 nsReflowStatus reflowStatus; 728 729 // If it's a floating first-letter, we need to reflow it before we 730 // know how wide it is (since we don't compute which letters are part 731 // of the first letter until reflow!). 732 // We also need to do this early reflow if FloatMarginISize returned 733 // an unconstrained inline-size, which can occur if the float had an 734 // orthogonal writing mode and 'auto' block-size (in its mode). 735 bool earlyFloatReflow = 736 aFloat->IsLetterFrame() || floatMarginISize == NS_UNCONSTRAINEDSIZE; 737 if (earlyFloatReflow) { 738 mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus); 739 floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm); 740 NS_ASSERTION(reflowStatus.IsComplete(), 741 "letter frames and orthogonal floats with auto block-size " 742 "shouldn't break, and if they do now, then they're breaking " 743 "at the wrong point"); 744 } 745 746 // Now we've computed the float's margin inline-size. 747 if (aAvailableISizeInCurrentLine && 748 floatMarginISize > *aAvailableISizeInCurrentLine) { 749 // The float cannot fit in the available inline-size of the current line. 750 // Let's notify our caller to place it later. 751 return PlaceFloatResult::ShouldPlaceBelowCurrentLine; 752 } 753 754 // Find a place to place the float. The CSS2 spec doesn't want 755 // floats overlapping each other or sticking out of the containing 756 // block if possible (CSS2 spec section 9.5.1, see the rule list). 757 UsedFloat floatStyle = floatDisplay->UsedFloat(wm); 758 MOZ_ASSERT(UsedFloat::Left == floatStyle || UsedFloat::Right == floatStyle, 759 "Invalid float type!"); 760 761 // Are we required to place at least part of the float because we're 762 // at the top of the page (to avoid an infinite loop of pushing and 763 // breaking). 764 bool mustPlaceFloat = 765 mReflowInput.mFlags.mIsTopOfPage && IsAdjacentWithBStart(); 766 767 // Get the band of available space with respect to margin box. 768 nsFlowAreaRect floatAvailableSpace = 769 GetFloatAvailableSpaceForPlacingFloat(wm, mBCoord); 770 771 for (;;) { 772 if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE && 773 floatAvailableSpace.mRect.BSize(wm) <= 0 && !mustPlaceFloat) { 774 // No space, nowhere to put anything. 775 PushFloatPastBreak(aFloat); 776 return PlaceFloatResult::ShouldPlaceInNextContinuation; 777 } 778 779 if (CanPlaceFloat(floatMarginISize, floatAvailableSpace)) { 780 // We found an appropriate place. 781 break; 782 } 783 784 // Nope. try to advance to the next band. 785 mBCoord += floatAvailableSpace.mRect.BSize(wm); 786 floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(wm, mBCoord); 787 mustPlaceFloat = false; 788 } 789 790 // If the float is continued, it will get the same absolute x value as its 791 // prev-in-flow 792 793 // We don't worry about the geometry of the prev in flow, let the continuation 794 // place and size itself as required. 795 796 // Assign inline and block dir coordinates to the float. We don't use 797 // LineLeft() and LineRight() here, because we would only have to 798 // convert the result back into this block's writing mode. 799 LogicalPoint floatPos(wm); 800 bool leftFloat = floatStyle == UsedFloat::Left; 801 802 if (leftFloat == wm.IsBidiLTR()) { 803 floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm); 804 } else { 805 floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize; 806 } 807 // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not 808 // be higher than the top of its containing block." (Since the 809 // containing block is the content edge of the block box, this 810 // means the margin edge of the float can't be higher than the 811 // content edge of the block that contains it.) 812 floatPos.B(wm) = std::max(mBCoord, ContentBStart()); 813 814 // Reflow the float after computing its vertical position so it knows 815 // where to break. 816 if (!earlyFloatReflow) { 817 const LogicalSize oldAvailSize = availSize; 818 availSize = ComputeAvailableSizeForFloat(); 819 if (oldAvailSize != availSize) { 820 floatRI.reset(); 821 floatRI.emplace(mPresContext, mReflowInput, aFloat, 822 availSize.ConvertTo(floatWM, wm)); 823 } 824 // Normally the mIsTopOfPage state is copied from the parent reflow input. 825 // However, when reflowing a float, if we've placed other floats that force 826 // this float being pushed down, we should unset the mIsTopOfPage bit. 827 if (floatRI->mFlags.mIsTopOfPage && HasFloatPushedDown()) { 828 // HasFloatPushedDown() implies that we increased mBCoord, and we 829 // should've turned off mustPlaceFloat when we did that. 830 NS_ASSERTION(!mustPlaceFloat, 831 "mustPlaceFloat shouldn't be set if we're not at the " 832 "top-of-page!"); 833 floatRI->mFlags.mIsTopOfPage = false; 834 } 835 mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus); 836 } 837 if (aFloat->GetPrevInFlow()) { 838 floatMargin.BStart(wm) = 0; 839 } 840 if (reflowStatus.IsIncomplete()) { 841 floatMargin.BEnd(wm) = 0; 842 } 843 844 // If the float cannot fit (e.g. via fragmenting itself if applicable), or if 845 // we're forced to break before it for CSS break-* reasons, then it needs to 846 // be pushed in its entirety to the next column/page. 847 // 848 // Note we use the available block-size in floatRI rather than use 849 // availSize.BSize() because nsBlockReflowContext::ReflowBlock() might adjust 850 // floatRI's available size. 851 const nscoord availBSize = floatRI->AvailableSize(floatWM).BSize(floatWM); 852 const bool isTruncated = 853 availBSize != NS_UNCONSTRAINEDSIZE && aFloat->BSize(floatWM) > availBSize; 854 if ((!floatRI->mFlags.mIsTopOfPage && isTruncated) || 855 reflowStatus.IsInlineBreakBefore()) { 856 PushFloatPastBreak(aFloat); 857 return PlaceFloatResult::ShouldPlaceInNextContinuation; 858 } 859 860 // We can't use aFloat->ShouldAvoidBreakInside(mReflowInput) here since 861 // its mIsTopOfPage may be true even though the float isn't at the 862 // top when floatPos.B(wm) > 0. 863 if (ContentBSize() != NS_UNCONSTRAINEDSIZE && !mustPlaceFloat && 864 (!mReflowInput.mFlags.mIsTopOfPage || floatPos.B(wm) > 0) && 865 StyleBreakWithin::Avoid == aFloat->StyleDisplay()->mBreakInside && 866 (!reflowStatus.IsFullyComplete() || 867 aFloat->BSize(wm) + floatMargin.BStartEnd(wm) > 868 ContentBEnd() - floatPos.B(wm)) && 869 !aFloat->GetPrevInFlow()) { 870 PushFloatPastBreak(aFloat); 871 return PlaceFloatResult::ShouldPlaceInNextContinuation; 872 } 873 874 // Calculate the actual origin of the float frame's border rect 875 // relative to the parent block; the margin must be added in 876 // to get the border rect 877 LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm), 878 floatMargin.BStart(wm) + floatPos.B(wm)); 879 880 // If float is relatively positioned, factor that in as well 881 const LogicalMargin floatOffsets = floatRI->ComputedLogicalOffsets(wm); 882 ReflowInput::ApplyRelativePositioning(aFloat, wm, floatOffsets, &origin, 883 ContainerSize()); 884 885 // Position the float. 886 bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin; 887 if (moved) { 888 aFloat->SetPosition(wm, origin, ContainerSize()); 889 } 890 891 // Update the float combined area state 892 // XXX Floats should really just get invalidated here if necessary 893 mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreasRelativeToParent()); 894 895 // Place the float in the float manager 896 // calculate region 897 LogicalRect region = nsFloatManager::CalculateRegionFor( 898 wm, aFloat, floatMargin, ContainerSize()); 899 // if the float split, then take up all of the vertical height 900 if (reflowStatus.IsIncomplete() && (NS_UNCONSTRAINEDSIZE != ContentBSize())) { 901 region.BSize(wm) = 902 std::max(region.BSize(wm), ContentBSize() - floatPos.B(wm)); 903 } 904 FloatManager()->AddFloat(aFloat, region, wm, ContainerSize()); 905 906 // store region 907 nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize()); 908 909 const bool invalidationDataNeeded = 910 ShapeInvalidationData::IsNeeded(*floatDisplay); 911 912 // If the float's dimensions or shape have changed, note the damage in the 913 // float manager. 914 if (!region.IsEqualEdges(oldRegion) || 915 !!invalidationData != invalidationDataNeeded || 916 (invalidationData && !invalidationData->Matches(*floatDisplay))) { 917 // XXXwaterson conservative: we could probably get away with noting 918 // less damage; e.g., if only height has changed, then only note the 919 // area into which the float has grown or from which the float has 920 // shrunk. 921 nscoord blockStart = std::min(region.BStart(wm), oldRegion.BStart(wm)); 922 nscoord blockEnd = std::max(region.BEnd(wm), oldRegion.BEnd(wm)); 923 FloatManager()->IncludeInDamage(blockStart, blockEnd); 924 } 925 926 if (invalidationDataNeeded) { 927 if (invalidationData) { 928 invalidationData->Update(*floatDisplay); 929 } else { 930 aFloat->SetProperty(ShapeInvalidationDataProperty(), 931 new ShapeInvalidationData(*floatDisplay)); 932 } 933 } else if (invalidationData) { 934 invalidationData = nullptr; 935 aFloat->RemoveProperty(ShapeInvalidationDataProperty()); 936 } 937 938 if (!reflowStatus.IsFullyComplete()) { 939 mBlock->SplitFloat(*this, aFloat, reflowStatus); 940 } else { 941 MOZ_ASSERT(!aFloat->GetNextInFlow()); 942 } 943 944 #ifdef DEBUG 945 if (nsBlockFrame::gNoisyFloatManager) { 946 nscoord tI, tB; 947 FloatManager()->GetTranslation(tI, tB); 948 mBlock->ListTag(stdout); 949 printf(": FlowAndPlaceFloat: AddFloat: tIB=%d,%d (%d,%d) {%d,%d,%d,%d}\n", 950 tI, tB, mFloatManagerI, mFloatManagerB, region.IStart(wm), 951 region.BStart(wm), region.ISize(wm), region.BSize(wm)); 952 } 953 954 if (nsBlockFrame::gNoisyReflow) { 955 nsRect r = aFloat->GetRect(); 956 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 957 printf("placed float: "); 958 aFloat->ListTag(stdout); 959 printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); 960 } 961 #endif 962 963 return PlaceFloatResult::Placed; 964 } 965 966 void BlockReflowState::PushFloatPastBreak(nsIFrame* aFloat) { 967 // This ensures that we: 968 // * don't try to place later but smaller floats (which CSS says 969 // must have their tops below the top of this float) 970 // * don't waste much time trying to reflow this float again until 971 // after the break 972 WritingMode wm = mReflowInput.GetWritingMode(); 973 UsedFloat floatStyle = aFloat->StyleDisplay()->UsedFloat(wm); 974 if (floatStyle == UsedFloat::Left) { 975 FloatManager()->SetPushedLeftFloatPastBreak(); 976 } else { 977 MOZ_ASSERT(floatStyle == UsedFloat::Right, "Unexpected float value!"); 978 FloatManager()->SetPushedRightFloatPastBreak(); 979 } 980 981 // Put the float on the pushed floats list, even though it 982 // isn't actually a continuation. 983 mBlock->StealFrame(aFloat); 984 AppendPushedFloatChain(aFloat); 985 mReflowStatus.SetOverflowIncomplete(); 986 } 987 988 /** 989 * Place below-current-line floats. 990 */ 991 void BlockReflowState::PlaceBelowCurrentLineFloats(nsLineBox* aLine) { 992 MOZ_ASSERT(!mBelowCurrentLineFloats.IsEmpty()); 993 nsTArray<nsIFrame*> floatsPlacedInLine; 994 for (nsIFrame* f : mBelowCurrentLineFloats) { 995 #ifdef DEBUG 996 if (nsBlockFrame::gNoisyReflow) { 997 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 998 printf("placing bcl float: "); 999 f->ListTag(stdout); 1000 printf("\n"); 1001 } 1002 #endif 1003 // Place the float 1004 PlaceFloatResult result = FlowAndPlaceFloat(f); 1005 MOZ_ASSERT(result != PlaceFloatResult::ShouldPlaceBelowCurrentLine, 1006 "We are already dealing with below current line floats!"); 1007 if (result == PlaceFloatResult::Placed) { 1008 floatsPlacedInLine.AppendElement(f); 1009 } 1010 } 1011 if (floatsPlacedInLine.Length() != mBelowCurrentLineFloats.Length()) { 1012 // We have some floats having ShouldPlaceInNextContinuation result. 1013 aLine->SetHadFloatPushed(); 1014 } 1015 aLine->AppendFloats(std::move(floatsPlacedInLine)); 1016 mBelowCurrentLineFloats.Clear(); 1017 } 1018 1019 std::tuple<nscoord, BlockReflowState::ClearFloatsResult> 1020 BlockReflowState::ClearFloats(nscoord aBCoord, UsedClear aClearType, 1021 nsIFrame* aFloatAvoidingBlock) { 1022 #ifdef DEBUG 1023 if (nsBlockFrame::gNoisyReflow) { 1024 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 1025 printf("clear floats: in: aBCoord=%d\n", aBCoord); 1026 } 1027 #endif 1028 1029 if (!FloatManager()->HasAnyFloats()) { 1030 return {aBCoord, ClearFloatsResult::BCoordNoChange}; 1031 } 1032 1033 nscoord newBCoord = aBCoord; 1034 1035 if (aClearType != UsedClear::None) { 1036 newBCoord = FloatManager()->ClearFloats(newBCoord, aClearType); 1037 1038 if (FloatManager()->ClearContinues(aClearType)) { 1039 return {newBCoord, ClearFloatsResult::FloatsPushedOrSplit}; 1040 } 1041 } 1042 1043 if (aFloatAvoidingBlock) { 1044 auto cbWM = aFloatAvoidingBlock->GetContainingBlock()->GetWritingMode(); 1045 for (;;) { 1046 nsFlowAreaRect floatAvailableSpace = 1047 GetFloatAvailableSpace(cbWM, newBCoord); 1048 if (FloatAvoidingBlockFitsInAvailSpace(aFloatAvoidingBlock, 1049 floatAvailableSpace)) { 1050 break; 1051 } 1052 // See the analogous code for inlines in 1053 // nsBlockFrame::DoReflowInlineFrames 1054 if (!AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) { 1055 // Stop trying to clear here; we'll just get pushed to the 1056 // next column or page and try again there. 1057 break; 1058 } 1059 } 1060 } 1061 1062 #ifdef DEBUG 1063 if (nsBlockFrame::gNoisyReflow) { 1064 nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); 1065 printf("clear floats: out: y=%d\n", newBCoord); 1066 } 1067 #endif 1068 1069 ClearFloatsResult result = newBCoord == aBCoord 1070 ? ClearFloatsResult::BCoordNoChange 1071 : ClearFloatsResult::BCoordAdvanced; 1072 return {newBCoord, result}; 1073 }