Bug 449371 Firefox/Thunderbird crashes at exit [@ gdk_display_x11_finalize], p=Brian...
[wine-gecko.git] / layout / generic / nsBlockReflowState.cpp
blob13bd3202226e734bef64d3d480f15edbb170ca9a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Mozilla Communicator client code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Steve Clark <buster@netscape.com>
25 * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 * L. David Baron <dbaron@dbaron.org>
27 * Mats Palmgren <mats.palmgren@bredband.net>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 /* state used in reflow of block frames */
45 #include "nsBlockReflowContext.h"
46 #include "nsBlockReflowState.h"
47 #include "nsBlockFrame.h"
48 #include "nsLineLayout.h"
49 #include "nsPresContext.h"
50 #include "nsGkAtoms.h"
51 #include "nsIFrame.h"
52 #include "nsFrameManager.h"
54 #include "nsINameSpaceManager.h"
57 #ifdef DEBUG
58 #include "nsBlockDebugFlags.h"
59 #endif
61 nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
62 nsPresContext* aPresContext,
63 nsBlockFrame* aFrame,
64 const nsHTMLReflowMetrics& aMetrics,
65 PRBool aTopMarginRoot,
66 PRBool aBottomMarginRoot,
67 PRBool aBlockNeedsSpaceManager)
68 : mBlock(aFrame),
69 mPresContext(aPresContext),
70 mReflowState(aReflowState),
71 mOverflowTracker(aPresContext, aFrame, PR_FALSE),
72 mPrevBottomMargin(),
73 mLineNumber(0),
74 mFlags(0),
75 mFloatBreakType(NS_STYLE_CLEAR_NONE)
77 SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nsnull);
78 SetFlag(BRS_ISOVERFLOWCONTAINER,
79 IS_TRUE_OVERFLOW_CONTAINER(aFrame));
81 const nsMargin& borderPadding = BorderPadding();
83 if (aTopMarginRoot || 0 != aReflowState.mComputedBorderPadding.top) {
84 SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE);
86 if (aBottomMarginRoot || 0 != aReflowState.mComputedBorderPadding.bottom) {
87 SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE);
89 if (GetFlag(BRS_ISTOPMARGINROOT)) {
90 SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
92 if (aBlockNeedsSpaceManager) {
93 SetFlag(BRS_SPACE_MGR, PR_TRUE);
96 mSpaceManager = aReflowState.mSpaceManager;
98 NS_ASSERTION(mSpaceManager,
99 "SpaceManager should be set in nsBlockReflowState" );
100 if (mSpaceManager) {
101 // Translate into our content area and then save the
102 // coordinate system origin for later.
103 mSpaceManager->Translate(borderPadding.left, borderPadding.top);
104 mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
107 mReflowStatus = NS_FRAME_COMPLETE;
109 mPresContext = aPresContext;
110 mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow());
112 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedWidth(),
113 "no unconstrained widths should be present anymore");
114 mContentArea.width = aReflowState.ComputedWidth();
116 // Compute content area height. Unlike the width, if we have a
117 // specified style height we ignore it since extra content is
118 // managed by the "overflow" property. When we don't have a
119 // specified style height then we may end up limiting our height if
120 // the availableHeight is constrained (this situation occurs when we
121 // are paginated).
122 if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) {
123 // We are in a paginated situation. The bottom edge is just inside
124 // the bottom border and padding. The content area height doesn't
125 // include either border or padding edge.
126 mBottomEdge = aReflowState.availableHeight - borderPadding.bottom;
127 mContentArea.height = PR_MAX(0, mBottomEdge - borderPadding.top);
129 else {
130 // When we are not in a paginated situation then we always use
131 // an constrained height.
132 SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE);
133 mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE;
136 mY = borderPadding.top;
137 mBand.Init(mSpaceManager, mContentArea);
139 mPrevChild = nsnull;
140 mCurrentLine = aFrame->end_lines();
142 mMinLineHeight = nsHTMLReflowState::CalcLineHeight(aReflowState.frame);
144 // Calculate mOutsideBulletX
145 GetAvailableSpace();
146 // FIXME (bug 25888): need to check the entire region that the first
147 // line overlaps, not just the top pixel.
148 mOutsideBulletX =
149 mReflowState.mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR ?
150 mAvailSpaceRect.x :
151 PR_MIN(mReflowState.ComputedWidth(), mAvailSpaceRect.XMost()) +
152 mReflowState.mComputedBorderPadding.LeftRight();
155 void
156 nsBlockReflowState::SetupOverflowPlaceholdersProperty()
158 if (mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE ||
159 !mOverflowPlaceholders.IsEmpty()) {
160 mBlock->SetProperty(nsGkAtoms::overflowPlaceholdersProperty,
161 &mOverflowPlaceholders, nsnull);
162 mBlock->AddStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS);
166 nsBlockReflowState::~nsBlockReflowState()
168 NS_ASSERTION(mOverflowPlaceholders.IsEmpty(),
169 "Leaking overflow placeholder frames");
171 // Restore the coordinate system, unless the space manager is null,
172 // which means it was just destroyed.
173 if (mSpaceManager) {
174 const nsMargin& borderPadding = BorderPadding();
175 mSpaceManager->Translate(-borderPadding.left, -borderPadding.top);
178 if (mBlock->GetStateBits() & NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS) {
179 mBlock->UnsetProperty(nsGkAtoms::overflowPlaceholdersProperty);
180 mBlock->RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS);
184 nsLineBox*
185 nsBlockReflowState::NewLineBox(nsIFrame* aFrame,
186 PRInt32 aCount,
187 PRBool aIsBlock)
189 return NS_NewLineBox(mPresContext->PresShell(), aFrame, aCount, aIsBlock);
192 void
193 nsBlockReflowState::FreeLineBox(nsLineBox* aLine)
195 if (aLine) {
196 aLine->Destroy(mPresContext->PresShell());
200 void
201 nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame,
202 nscoord& aLeftResult,
203 nscoord& aRightResult,
204 nsBlockFrame::
205 ReplacedElementWidthToClear
206 *aReplacedWidth)
208 // The frame is clueless about the space manager and therefore we
209 // only give it free space. An example is a table frame - the
210 // tables do not flow around floats.
211 // However, we can let its margins intersect floats.
212 NS_ASSERTION(mAvailSpaceRect.x >= 0, "bad avail space rect x");
213 NS_ASSERTION(mAvailSpaceRect.width == 0 ||
214 mAvailSpaceRect.XMost() <= mContentArea.width,
215 "bad avail space rect width");
217 nscoord leftOffset, rightOffset;
218 if (mAvailSpaceRect.width == mContentArea.width) {
219 // We don't need to compute margins when there are no floats around.
220 leftOffset = 0;
221 rightOffset = 0;
222 } else {
223 // We pass in aReplacedWidth to make handling outer table frames
224 // work correctly. For outer table frames, we need to subtract off
225 // the margin that's going to be at the edge of them, since we're
226 // dealing with margin that it's really the child's responsibility
227 // to place.
228 nsCSSOffsetState os(aFrame, mReflowState.rendContext, mContentArea.width);
229 NS_ASSERTION(!aReplacedWidth ||
230 aFrame->GetType() == nsGkAtoms::tableOuterFrame ||
231 (aReplacedWidth->marginLeft == os.mComputedMargin.left &&
232 aReplacedWidth->marginRight == os.mComputedMargin.right),
233 "unexpected aReplacedWidth");
235 nscoord leftFloatXOffset = mAvailSpaceRect.x;
236 leftOffset = PR_MAX(leftFloatXOffset, os.mComputedMargin.left) -
237 (aReplacedWidth ? aReplacedWidth->marginLeft
238 : os.mComputedMargin.left);
239 leftOffset = PR_MAX(leftOffset, 0); // in case of negative margin
240 nscoord rightFloatXOffset = mContentArea.width - mAvailSpaceRect.XMost();
241 rightOffset = PR_MAX(rightFloatXOffset, os.mComputedMargin.right) -
242 (aReplacedWidth ? aReplacedWidth->marginRight
243 : os.mComputedMargin.right);
244 rightOffset = PR_MAX(rightOffset, 0); // in case of negative margin
246 aLeftResult = leftOffset;
247 aRightResult = rightOffset;
250 // Compute the amount of available space for reflowing a block frame
251 // at the current Y coordinate. This method assumes that
252 // GetAvailableSpace has already been called.
253 void
254 nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
255 const nsStyleDisplay* aDisplay,
256 PRBool aBlockAvoidsFloats,
257 nsRect& aResult)
259 #ifdef REALLY_NOISY_REFLOW
260 printf("CBAS frame=%p has float count %d\n", aFrame, mBand.GetFloatCount());
261 mBand.List();
262 #endif
263 aResult.y = mY;
264 aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT)
265 ? NS_UNCONSTRAINEDSIZE
266 : PR_MAX(0, mReflowState.availableHeight - mY);
267 // mY might be greater than mBottomEdge if the block's top margin pushes
268 // it off the page/column. Negative available height can confuse other code
269 // and is nonsense in principle.
271 const nsMargin& borderPadding = BorderPadding();
273 // XXX Do we really want this condition to be this restrictive (i.e.,
274 // more restrictive than it used to be)? The |else| here is allowed
275 // by the CSS spec, but only out of desperation given implementations,
276 // and the behavior it leads to is quite undesirable (it can cause
277 // things to become extremely narrow when they'd fit quite well a
278 // little bit lower). Should the else be a quirk or something that
279 // applies to a specific set of frame classes and no new ones?
280 // If we did that, then for those frames where the condition below is
281 // true but nsBlockFrame::BlockCanIntersectFloats is false,
282 // nsBlockFrame::WidthToClearPastFloats would need to use the
283 // shrink-wrap formula, max(MIN_WIDTH, min(avail width, PREF_WIDTH))
284 // rather than just using MIN_WIDTH.
285 NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) ==
286 !aBlockAvoidsFloats,
287 "unexpected replaced width");
288 if (!aBlockAvoidsFloats) {
289 if (mBand.GetFloatCount()) {
290 // Use the float-edge property to determine how the child block
291 // will interact with the float.
292 const nsStyleBorder* borderStyle = aFrame->GetStyleBorder();
293 switch (borderStyle->mFloatEdge) {
294 default:
295 case NS_STYLE_FLOAT_EDGE_CONTENT: // content and only content does runaround of floats
296 // The child block will flow around the float. Therefore
297 // give it all of the available space.
298 aResult.x = borderPadding.left;
299 aResult.width = mContentArea.width;
300 break;
301 case NS_STYLE_FLOAT_EDGE_MARGIN:
303 // The child block's margins should be placed adjacent to,
304 // but not overlap the float.
305 aResult.x = mAvailSpaceRect.x + borderPadding.left;
306 aResult.width = mAvailSpaceRect.width;
308 break;
311 else {
312 // Since there are no floats present the float-edge property
313 // doesn't matter therefore give the block element all of the
314 // available space since it will flow around the float itself.
315 aResult.x = borderPadding.left;
316 aResult.width = mContentArea.width;
319 else {
320 nsBlockFrame::ReplacedElementWidthToClear replacedWidthStruct;
321 nsBlockFrame::ReplacedElementWidthToClear *replacedWidth = nsnull;
322 if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) {
323 replacedWidth = &replacedWidthStruct;
324 replacedWidthStruct = nsBlockFrame::WidthToClearPastFloats(*this, aFrame);
327 nscoord leftOffset, rightOffset;
328 ComputeReplacedBlockOffsetsForFloats(aFrame, leftOffset, rightOffset,
329 replacedWidth);
330 aResult.x = borderPadding.left + leftOffset;
331 aResult.width = mContentArea.width - leftOffset - rightOffset;
334 #ifdef REALLY_NOISY_REFLOW
335 printf(" CBAS: result %d %d %d %d\n", aResult.x, aResult.y, aResult.width, aResult.height);
336 #endif
339 void
340 nsBlockReflowState::GetAvailableSpace(nscoord aY, PRBool aRelaxHeightConstraint)
342 #ifdef DEBUG
343 // Verify that the caller setup the coordinate system properly
344 nscoord wx, wy;
345 mSpaceManager->GetTranslation(wx, wy);
346 NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
347 "bad coord system");
348 #endif
350 mBand.GetAvailableSpace(aY - BorderPadding().top, aRelaxHeightConstraint,
351 mAvailSpaceRect);
353 #ifdef DEBUG
354 if (nsBlockFrame::gNoisyReflow) {
355 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
356 printf("GetAvailableSpace: band=%d,%d,%d,%d count=%d\n",
357 mAvailSpaceRect.x, mAvailSpaceRect.y,
358 mAvailSpaceRect.width, mAvailSpaceRect.height,
359 mBand.GetTrapezoidCount());
361 #endif
365 * Reconstruct the vertical margin before the line |aLine| in order to
366 * do an incremental reflow that begins with |aLine| without reflowing
367 * the line before it. |aLine| may point to the fencepost at the end of
368 * the line list, and it is used this way since we (for now, anyway)
369 * always need to recover margins at the end of a block.
371 * The reconstruction involves walking backward through the line list to
372 * find any collapsed margins preceding the line that would have been in
373 * the reflow state's |mPrevBottomMargin| when we reflowed that line in
374 * a full reflow (under the rule in CSS2 that all adjacent vertical
375 * margins of blocks collapse).
377 void
378 nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine)
380 mPrevBottomMargin.Zero();
381 nsBlockFrame *block = mBlock;
383 nsLineList::iterator firstLine = block->begin_lines();
384 for (;;) {
385 --aLine;
386 if (aLine->IsBlock()) {
387 mPrevBottomMargin = aLine->GetCarriedOutBottomMargin();
388 break;
390 if (!aLine->IsEmpty()) {
391 break;
393 if (aLine == firstLine) {
394 // If the top margin was carried out (and thus already applied),
395 // set it to zero. Either way, we're done.
396 if (!GetFlag(BRS_ISTOPMARGINROOT)) {
397 mPrevBottomMargin.Zero();
399 break;
405 * Restore information about floats into the space manager for an
406 * incremental reflow, and simultaneously push the floats by
407 * |aDeltaY|, which is the amount |aLine| was pushed relative to its
408 * parent. The recovery of state is one of the things that makes
409 * incremental reflow O(N^2) and this state should really be kept
410 * around, attached to the frame tree.
412 void
413 nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine,
414 nscoord aDeltaY)
416 if (aLine->HasFloats()) {
417 // Place the floats into the space-manager again. Also slide
418 // them, just like the regular frames on the line.
419 nsFloatCache* fc = aLine->GetFirstFloat();
420 while (fc) {
421 nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
422 if (aDeltaY != 0) {
423 fc->mRegion.y += aDeltaY;
424 nsPoint p = floatFrame->GetPosition();
425 floatFrame->SetPosition(nsPoint(p.x, p.y + aDeltaY));
426 nsContainerFrame::PositionFrameView(floatFrame);
427 nsContainerFrame::PositionChildViews(floatFrame);
429 #ifdef DEBUG
430 if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisySpaceManager) {
431 nscoord tx, ty;
432 mSpaceManager->GetTranslation(tx, ty);
433 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
434 printf("RecoverFloats: txy=%d,%d (%d,%d) ",
435 tx, ty, mSpaceManagerX, mSpaceManagerY);
436 nsFrame::ListTag(stdout, floatFrame);
437 printf(" aDeltaY=%d region={%d,%d,%d,%d}\n",
438 aDeltaY, fc->mRegion.x, fc->mRegion.y,
439 fc->mRegion.width, fc->mRegion.height);
441 #endif
442 mSpaceManager->AddRectRegion(floatFrame, fc->mRegion);
443 fc = fc->Next();
445 } else if (aLine->IsBlock()) {
446 nsBlockFrame *kid = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
447 // don't recover any state inside a block that has its own space
448 // manager (we don't currently have any blocks like this, though,
449 // thanks to our use of extra frames for 'overflow')
450 if (kid && !nsBlockFrame::BlockNeedsSpaceManager(kid)) {
451 nscoord tx = kid->mRect.x, ty = kid->mRect.y;
453 // If the element is relatively positioned, then adjust x and y
454 // accordingly so that we consider relatively positioned frames
455 // at their original position.
456 if (NS_STYLE_POSITION_RELATIVE == kid->GetStyleDisplay()->mPosition) {
457 nsPoint *offsets = static_cast<nsPoint*>
458 (mPresContext->PropertyTable()->GetProperty(kid,
459 nsGkAtoms::computedOffsetProperty));
461 if (offsets) {
462 tx -= offsets->x;
463 ty -= offsets->y;
467 mSpaceManager->Translate(tx, ty);
468 for (nsBlockFrame::line_iterator line = kid->begin_lines(),
469 line_end = kid->end_lines();
470 line != line_end;
471 ++line)
472 // Pass 0, not the real DeltaY, since these floats aren't
473 // moving relative to their parent block, only relative to
474 // the space manager.
475 RecoverFloats(line, 0);
476 mSpaceManager->Translate(-tx, -ty);
482 * Everything done in this function is done O(N) times for each pass of
483 * reflow so it is O(N*M) where M is the number of incremental reflow
484 * passes. That's bad. Don't do stuff here.
486 * When this function is called, |aLine| has just been slid by |aDeltaY|
487 * and the purpose of RecoverStateFrom is to ensure that the
488 * nsBlockReflowState is in the same state that it would have been in
489 * had the line just been reflowed.
491 * Most of the state recovery that we have to do involves floats.
493 void
494 nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine,
495 nscoord aDeltaY)
497 // Make the line being recovered the current line
498 mCurrentLine = aLine;
500 // Place floats for this line into the space manager
501 if (aLine->HasFloats() || aLine->IsBlock()) {
502 // Undo border/padding translation since the nsFloatCache's
503 // coordinates are relative to the frame not relative to the
504 // border/padding.
505 const nsMargin& bp = BorderPadding();
506 mSpaceManager->Translate(-bp.left, -bp.top);
508 RecoverFloats(aLine, aDeltaY);
510 #ifdef DEBUG
511 if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisySpaceManager) {
512 mSpaceManager->List(stdout);
514 #endif
515 // And then put the translation back again
516 mSpaceManager->Translate(bp.left, bp.top);
520 PRBool
521 nsBlockReflowState::IsImpactedByFloat() const
523 #ifdef REALLY_NOISY_REFLOW
524 printf("nsBlockReflowState::IsImpactedByFloat %p returned %d\n",
525 this, mBand.GetFloatCount());
526 #endif
527 return mBand.GetFloatCount() > 0;
531 PRBool
532 nsBlockReflowState::InitFloat(nsLineLayout& aLineLayout,
533 nsPlaceholderFrame* aPlaceholder,
534 nscoord aAvailableWidth,
535 nsReflowStatus& aReflowStatus)
537 // Set the geometric parent of the float
538 nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
539 floatFrame->SetParent(mBlock);
541 // Then add the float to the current line and place it when
542 // appropriate
543 return AddFloat(aLineLayout, aPlaceholder, PR_TRUE,
544 aAvailableWidth, aReflowStatus);
547 // This is called by the line layout's AddFloat method when a
548 // place-holder frame is reflowed in a line. If the float is a
549 // left-most child (it's x coordinate is at the line's left margin)
550 // then the float is place immediately, otherwise the float
551 // placement is deferred until the line has been reflowed.
553 // XXXldb This behavior doesn't quite fit with CSS1 and CSS2 --
554 // technically we're supposed let the current line flow around the
555 // float as well unless it won't fit next to what we already have.
556 // But nobody else implements it that way...
557 PRBool
558 nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout,
559 nsPlaceholderFrame* aPlaceholder,
560 PRBool aInitialReflow,
561 nscoord aAvailableWidth,
562 nsReflowStatus& aReflowStatus)
564 NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr");
566 aReflowStatus = NS_FRAME_COMPLETE;
567 // Allocate a nsFloatCache for the float
568 nsFloatCache* fc = mFloatCacheFreeList.Alloc();
569 fc->mPlaceholder = aPlaceholder;
571 PRBool placed;
573 // Now place the float immediately if possible. Otherwise stash it
574 // away in mPendingFloats and place it later.
575 // If one or more floats has already been pushed to the next line,
576 // don't let this one go on the current line, since that would violate
577 // float ordering.
578 if (mBelowCurrentLineFloats.IsEmpty() &&
579 (aLineLayout.LineIsEmpty() ||
580 mBlock->ComputeFloatWidth(*this, aPlaceholder) <= aAvailableWidth)) {
581 // Because we are in the middle of reflowing a placeholder frame
582 // within a line (and possibly nested in an inline frame or two
583 // that's a child of our block) we need to restore the space
584 // manager's translation to the space that the block resides in
585 // before placing the float.
586 nscoord ox, oy;
587 mSpaceManager->GetTranslation(ox, oy);
588 nscoord dx = ox - mSpaceManagerX;
589 nscoord dy = oy - mSpaceManagerY;
590 mSpaceManager->Translate(-dx, -dy);
592 // And then place it
593 PRBool isLeftFloat;
594 // force it to fit if we're at the top of the block and we can't
595 // break before this
596 PRBool forceFit = IsAdjacentWithTop() && !aLineLayout.LineIsBreakable();
597 placed = FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus, forceFit);
598 NS_ASSERTION(placed || !forceFit,
599 "If we asked for force-fit, it should have been placed");
600 if (forceFit || (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus))) {
601 // Pass on updated available space to the current inline reflow engine
602 GetAvailableSpace(mY, forceFit);
603 nsRect availSpace(nsPoint(mAvailSpaceRect.x + BorderPadding().left, mY),
604 mAvailSpaceRect.Size());
605 aLineLayout.UpdateBand(availSpace, isLeftFloat,
606 aPlaceholder->GetOutOfFlowFrame());
608 // Record this float in the current-line list
609 mCurrentLineFloats.Append(fc);
610 // If we can't break here, hide the fact that it's truncated
611 // XXX We can probably do this more cleanly
612 aReflowStatus &= ~NS_FRAME_TRUNCATED;
614 else {
615 if (IsAdjacentWithTop()) {
616 // Pushing the line to the next page won't give us any more space;
617 // therefore, we break.
618 NS_ASSERTION(aLineLayout.LineIsBreakable(),
619 "We can't get here unless forceFit is false");
620 aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
621 } else {
622 // Make sure we propagate the truncated status; this signals the
623 // block to push the line to the next page.
624 aReflowStatus |= NS_FRAME_TRUNCATED;
626 delete fc;
629 // Restore coordinate system
630 mSpaceManager->Translate(dx, dy);
632 else {
633 // Always claim to be placed; we don't know whether we fit yet, so we
634 // deal with this in PlaceBelowCurrentLineFloats
635 placed = PR_TRUE;
636 // This float will be placed after the line is done (it is a
637 // below-current-line float).
638 mBelowCurrentLineFloats.Append(fc);
639 if (aPlaceholder->GetNextInFlow()) {
640 // If the float might not be complete, mark it incomplete now to
641 // prevent its next-in-flow placeholders being torn down. We will destroy any
642 // placeholders later if PlaceBelowCurrentLineFloats finds the
643 // float is complete.
644 // Note that we could have unconstrained height and yet have
645 // a next-in-flow placeholder --- for example columns can switch
646 // from constrained height to unconstrained height.
647 if (aPlaceholder->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE) {
648 aReflowStatus = NS_FRAME_NOT_COMPLETE;
652 return placed;
655 PRBool
656 nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize,
657 PRUint8 aFloats, PRBool aForceFit)
659 // If the current Y coordinate is not impacted by any floats
660 // then by definition the float fits.
661 PRBool result = PR_TRUE;
662 if (0 != mBand.GetFloatCount()) {
663 // XXX We should allow overflow by up to half a pixel here (bug 21193).
664 if (mAvailSpaceRect.width < aFloatSize.width) {
665 // The available width is too narrow (and its been impacted by a
666 // prior float)
667 result = PR_FALSE;
671 if (!result)
672 return result;
674 // At this point we know that there is enough horizontal space for
675 // the float (somewhere). Lets see if there is enough vertical
676 // space.
677 if (NSCoordGreaterThan(aFloatSize.height, mAvailSpaceRect.height)) {
678 // The available height is too short. However, its possible that
679 // there is enough open space below which is not impacted by a
680 // float.
682 // Compute the X coordinate for the float based on its float
683 // type, assuming its placed on the current line. This is
684 // where the float will be placed horizontally if it can go
685 // here.
686 nscoord xa;
687 if (NS_STYLE_FLOAT_LEFT == aFloats) {
688 xa = mAvailSpaceRect.x;
690 else {
691 xa = mAvailSpaceRect.XMost() - aFloatSize.width;
693 // In case the float is too big, don't go past the left edge
694 // XXXldb This seems wrong, but we might want to fix bug 6976
695 // first.
696 if (xa < mAvailSpaceRect.x) {
697 xa = mAvailSpaceRect.x;
700 nscoord xb = xa + aFloatSize.width;
702 // Calculate the top and bottom y coordinates, again assuming
703 // that the float is placed on the current line.
704 const nsMargin& borderPadding = BorderPadding();
705 nscoord ya = mY - borderPadding.top;
706 if (ya < 0) {
707 // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
708 // be higher than the top of its containing block." (Since the
709 // containing block is the content edge of the block box, this
710 // means the margin edge of the float can't be higher than the
711 // content edge of the block that contains it.)
712 ya = 0;
714 nscoord yb = ya + aFloatSize.height;
716 nscoord saveY = mY;
717 for (;;) {
718 // Get the available space at the new Y coordinate
719 if (mAvailSpaceRect.height <= 0) {
720 // there is no more available space. We lose.
721 result = PR_FALSE;
722 break;
725 mY += mAvailSpaceRect.height;
726 GetAvailableSpace(mY, aForceFit);
728 if (0 != mBand.GetFloatCount()) {
729 if ((xa < mAvailSpaceRect.x) || (xb > mAvailSpaceRect.XMost())) {
730 // The float can't go here.
731 result = PR_FALSE;
732 break;
736 // See if there is now enough height for the float.
737 if (yb <= mY + mAvailSpaceRect.height) {
738 // Winner. The bottom Y coordinate of the float is in
739 // this band.
740 break;
744 // Restore Y coordinate and available space information
745 // regardless of the outcome.
746 mY = saveY;
747 GetAvailableSpace(mY, aForceFit);
750 return result;
753 PRBool
754 nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache,
755 PRBool* aIsLeftFloat,
756 nsReflowStatus& aReflowStatus,
757 PRBool aForceFit)
759 aReflowStatus = NS_FRAME_COMPLETE;
760 // Save away the Y coordinate before placing the float. We will
761 // restore mY at the end after placing the float. This is
762 // necessary because any adjustments to mY during the float
763 // placement are for the float only, not for any non-floating
764 // content.
765 nscoord saveY = mY;
767 nsPlaceholderFrame* placeholder = aFloatCache->mPlaceholder;
768 nsIFrame* floatFrame = placeholder->GetOutOfFlowFrame();
770 // Grab the float's display information
771 const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay();
773 // The float's old region, so we can propagate damage.
774 nsRect oldRegion = aFloatCache->mRegion;
776 // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
777 // ``above'' another float that preceded it in the flow.
778 mY = NS_MAX(mSpaceManager->GetLowestRegionTop() + BorderPadding().top, mY);
780 // See if the float should clear any preceding floats...
781 // XXX We need to mark this float somehow so that it gets reflowed
782 // when floats are inserted before it.
783 if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
784 // XXXldb Does this handle vertical margins correctly?
785 mY = ClearFloats(mY, floatDisplay->mBreakType);
787 // Get the band of available space
788 GetAvailableSpace(mY, aForceFit);
790 NS_ASSERTION(floatFrame->GetParent() == mBlock,
791 "Float frame has wrong parent");
793 // Reflow the float
794 nsMargin floatMargin;
795 mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus);
797 #ifdef DEBUG
798 if (nsBlockFrame::gNoisyReflow) {
799 nsRect region = floatFrame->GetRect();
800 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
801 printf("flowed float: ");
802 nsFrame::ListTag(stdout, floatFrame);
803 printf(" (%d,%d,%d,%d)\n",
804 region.x, region.y, region.width, region.height);
806 #endif
808 nsSize floatSize = floatFrame->GetSize() +
809 nsSize(floatMargin.LeftRight(), floatMargin.TopBottom());
811 // Find a place to place the float. The CSS2 spec doesn't want
812 // floats overlapping each other or sticking out of the containing
813 // block if possible (CSS2 spec section 9.5.1, see the rule list).
814 NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) ||
815 (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats),
816 "invalid float type");
818 // Can the float fit here?
819 PRBool keepFloatOnSameLine = PR_FALSE;
821 while (!CanPlaceFloat(floatSize, floatDisplay->mFloats, aForceFit)) {
822 if (mAvailSpaceRect.height <= 0) {
823 // No space, nowhere to put anything.
824 mY = saveY;
825 return PR_FALSE;
828 // Nope. try to advance to the next band.
829 if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
830 eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
832 mY += mAvailSpaceRect.height;
833 GetAvailableSpace(mY, aForceFit);
834 } else {
835 // This quirk matches the one in nsBlockFrame::ReflowFloat
836 // IE handles float tables in a very special way
838 // see if the previous float is also a table and has "align"
839 nsFloatCache* fc = mCurrentLineFloats.Head();
840 nsIFrame* prevFrame = nsnull;
841 while (fc) {
842 if (fc->mPlaceholder->GetOutOfFlowFrame() == floatFrame) {
843 break;
845 prevFrame = fc->mPlaceholder->GetOutOfFlowFrame();
846 fc = fc->Next();
849 if(prevFrame) {
850 //get the frame type
851 if (nsGkAtoms::tableOuterFrame == prevFrame->GetType()) {
852 //see if it has "align="
853 // IE makes a difference between align and he float property
854 nsIContent* content = prevFrame->GetContent();
855 if (content) {
856 // we're interested only if previous frame is align=left
857 // IE messes things up when "right" (overlapping frames)
858 if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align,
859 NS_LITERAL_STRING("left"), eIgnoreCase)) {
860 keepFloatOnSameLine = PR_TRUE;
861 // don't advance to next line (IE quirkie behaviour)
862 // it breaks rule CSS2/9.5.1/1, but what the hell
863 // since we cannot evangelize the world
864 break;
870 // the table does not fit anymore in this line so advance to next band
871 mY += mAvailSpaceRect.height;
872 GetAvailableSpace(mY, aForceFit);
873 // reflow the float again now since we have more space
874 // XXXldb We really don't need to Reflow in a loop, we just need
875 // to ComputeSize in a loop (once ComputeSize depends on
876 // availableWidth, which should make this work again).
877 mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus);
878 // Get the floats bounding box and margin information
879 floatSize = floatFrame->GetSize() +
880 nsSize(floatMargin.LeftRight(), floatMargin.TopBottom());
883 // If the float is continued, it will get the same absolute x value as its prev-in-flow
885 // We don't worry about the geometry of the prev in flow, let the continuation
886 // place and size itself as required.
888 // Assign an x and y coordinate to the float. Note that the x,y
889 // coordinates are computed <b>relative to the translation in the
890 // spacemanager</b> which means that the impacted region will be
891 // <b>inside</b> the border/padding area.
892 PRBool isLeftFloat;
893 nscoord floatX, floatY;
894 if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) {
895 isLeftFloat = PR_TRUE;
896 floatX = mAvailSpaceRect.x;
898 else {
899 isLeftFloat = PR_FALSE;
900 if (!keepFloatOnSameLine) {
901 floatX = mAvailSpaceRect.XMost() - floatSize.width;
903 else {
904 // this is the IE quirk (see few lines above)
905 // the table is kept in the same line: don't let it overlap the
906 // previous float
907 floatX = mAvailSpaceRect.x;
910 *aIsLeftFloat = isLeftFloat;
911 const nsMargin& borderPadding = BorderPadding();
912 floatY = mY - borderPadding.top;
913 if (floatY < 0) {
914 // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
915 // be higher than the top of its containing block." (Since the
916 // containing block is the content edge of the block box, this
917 // means the margin edge of the float can't be higher than the
918 // content edge of the block that contains it.)
919 floatY = 0;
922 // Place the float in the space manager
923 // if the float split, then take up all of the vertical height
924 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
925 (NS_UNCONSTRAINEDSIZE != mContentArea.height)) {
926 floatSize.height = PR_MAX(floatSize.height, mContentArea.height - floatY);
929 nsRect region(floatX, floatY, floatSize.width, floatSize.height);
931 // Don't send rectangles with negative margin-box width or height to
932 // the space manager; it can't deal with them.
933 if (region.width < 0) {
934 // Preserve the right margin-edge for left floats and the left
935 // margin-edge for right floats
936 if (isLeftFloat) {
937 region.x = region.XMost();
939 region.width = 0;
941 if (region.height < 0) {
942 region.height = 0;
944 #ifdef DEBUG
945 nsresult rv =
946 #endif
947 mSpaceManager->AddRectRegion(floatFrame, region);
948 NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement");
950 // Save away the floats region in the spacemanager, after making
951 // it relative to the containing block's frame instead of relative
952 // to the spacemanager translation (which is inset by the
953 // border+padding).
954 // XXX Maybe RecoverFloats should calc/add in the borderPadding itself?
955 // It's kind of confusing to have the spacemanager translation be different
956 // depending on what stage of reflow we're in.
957 aFloatCache->mRegion = region +
958 nsPoint(borderPadding.left, borderPadding.top);
960 // If the float's dimensions have changed, note the damage in the
961 // space manager.
962 if (aFloatCache->mRegion != oldRegion) {
963 // XXXwaterson conservative: we could probably get away with noting
964 // less damage; e.g., if only height has changed, then only note the
965 // area into which the float has grown or from which the float has
966 // shrunk.
967 nscoord top = NS_MIN(region.y, oldRegion.y);
968 nscoord bottom = NS_MAX(region.YMost(), oldRegion.YMost());
969 mSpaceManager->IncludeInDamage(top, bottom);
972 #ifdef NOISY_SPACEMANAGER
973 nscoord tx, ty;
974 mSpaceManager->GetTranslation(tx, ty);
975 nsFrame::ListTag(stdout, mBlock);
976 printf(": FlowAndPlaceFloat: AddRectRegion: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
977 tx, ty, mSpaceManagerX, mSpaceManagerY,
978 aFloatCache->mRegion.x, aFloatCache->mRegion.y,
979 aFloatCache->mRegion.width, aFloatCache->mRegion.height);
980 #endif
982 // Calculate the actual origin of the float frame's border rect
983 // relative to the parent block; floatX/Y must be converted from space-manager
984 // coordinates to parent coordinates, and the margin must be added in
985 // to get the border rect
986 nsPoint origin(borderPadding.left + floatMargin.left + floatX,
987 borderPadding.top + floatMargin.top + floatY);
989 // If float is relatively positioned, factor that in as well
990 origin += floatFrame->GetRelativeOffset(floatDisplay);
992 // Position the float and make sure and views are properly
993 // positioned. We need to explicitly position its child views as
994 // well, since we're moving the float after flowing it.
995 floatFrame->SetPosition(origin);
996 nsContainerFrame::PositionFrameView(floatFrame);
997 nsContainerFrame::PositionChildViews(floatFrame);
999 // Update the float combined area state
1000 nsRect combinedArea = floatFrame->GetOverflowRect() + origin;
1002 // XXX Floats should really just get invalidated here if necessary
1003 mFloatCombinedArea.UnionRect(combinedArea, mFloatCombinedArea);
1005 // Now restore mY
1006 mY = saveY;
1008 #ifdef DEBUG
1009 if (nsBlockFrame::gNoisyReflow) {
1010 nsRect r = floatFrame->GetRect();
1011 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
1012 printf("placed float: ");
1013 nsFrame::ListTag(stdout, floatFrame);
1014 printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
1016 #endif
1018 return PR_TRUE;
1022 * Place below-current-line floats.
1024 PRBool
1025 nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRBool aForceFit)
1027 nsFloatCache* fc = aList.Head();
1028 while (fc) {
1030 #ifdef DEBUG
1031 if (nsBlockFrame::gNoisyReflow) {
1032 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
1033 printf("placing bcl float: ");
1034 nsFrame::ListTag(stdout, fc->mPlaceholder->GetOutOfFlowFrame());
1035 printf("\n");
1037 #endif
1038 // Place the float
1039 PRBool isLeftFloat;
1040 nsReflowStatus reflowStatus;
1041 PRBool placed = FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus, aForceFit);
1042 NS_ASSERTION(placed || !aForceFit,
1043 "If we're in force-fit mode, we should have placed the float");
1045 if (!placed || (NS_FRAME_IS_TRUNCATED(reflowStatus) && !aForceFit)) {
1046 // return before processing all of the floats, since the line will be pushed.
1047 return PR_FALSE;
1049 else if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) {
1050 // Create a continuation for the incomplete float and its placeholder.
1051 nsresult rv = mBlock->SplitPlaceholder(*this, fc->mPlaceholder);
1052 if (NS_FAILED(rv))
1053 return PR_FALSE;
1054 } else {
1055 // XXX We could deal with truncated frames better by breaking before
1056 // the associated placeholder
1057 NS_WARN_IF_FALSE(!NS_FRAME_IS_TRUNCATED(reflowStatus),
1058 "This situation currently leads to data not printing");
1060 // Float is complete. We need to delete any leftover placeholders now.
1061 nsIFrame* nextPlaceholder = fc->mPlaceholder->GetNextInFlow();
1062 if (nextPlaceholder) {
1063 nsHTMLContainerFrame* parent =
1064 static_cast<nsHTMLContainerFrame*>(nextPlaceholder->GetParent());
1065 parent->DeleteNextInFlowChild(mPresContext, nextPlaceholder);
1069 fc = fc->Next();
1071 return PR_TRUE;
1074 nscoord
1075 nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType,
1076 nsIFrame *aReplacedBlock)
1078 #ifdef DEBUG
1079 if (nsBlockFrame::gNoisyReflow) {
1080 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
1081 printf("clear floats: in: aY=%d(%d)\n",
1082 aY, aY - BorderPadding().top);
1084 #endif
1086 #ifdef NOISY_FLOAT_CLEARING
1087 printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
1088 aY, aBreakType);
1089 mSpaceManager->List(stdout);
1090 #endif
1092 const nsMargin& bp = BorderPadding();
1093 nscoord newY = aY;
1095 if (aBreakType != NS_STYLE_CLEAR_NONE) {
1096 newY = bp.top + mSpaceManager->ClearFloats(newY - bp.top, aBreakType);
1099 if (aReplacedBlock) {
1100 for (;;) {
1101 GetAvailableSpace(newY, PR_FALSE);
1102 nsBlockFrame::ReplacedElementWidthToClear replacedWidth =
1103 nsBlockFrame::WidthToClearPastFloats(*this, aReplacedBlock);
1104 if (mBand.GetFloatCount() == 0 ||
1105 PR_MAX(mAvailSpaceRect.x, replacedWidth.marginLeft) +
1106 replacedWidth.borderBoxWidth +
1107 PR_MAX(mContentArea.width -
1108 PR_MIN(mContentArea.width, mAvailSpaceRect.XMost()),
1109 replacedWidth.marginRight) <=
1110 mContentArea.width) {
1111 break;
1113 // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
1114 if (mAvailSpaceRect.height > 0) {
1115 // See if there's room in the next band.
1116 newY += mAvailSpaceRect.height;
1117 } else {
1118 if (mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
1119 // Stop trying to clear here; we'll just get pushed to the
1120 // next column or page and try again there.
1121 break;
1123 NS_NOTREACHED("avail space rect with zero height!");
1124 newY += 1;
1127 // Restore mBand and mAvailSpaceRect to the way they were. This may
1128 // well not be needed, and we should probably come up with
1129 // well-defined rules about when these members are valid so that
1130 // it's clearly not needed.
1131 GetAvailableSpace();
1134 #ifdef DEBUG
1135 if (nsBlockFrame::gNoisyReflow) {
1136 nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
1137 printf("clear floats: out: y=%d(%d)\n", newY, newY - bp.top);
1139 #endif
1141 return newY;