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