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 +