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
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.
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"
52 #include "nsFrameManager.h"
54 #include "nsINameSpaceManager.h"
58 #include "nsBlockDebugFlags.h"
61 nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState
& aReflowState
,
62 nsPresContext
* aPresContext
,
64 const nsHTMLReflowMetrics
& aMetrics
,
65 PRBool aTopMarginRoot
,
66 PRBool aBottomMarginRoot
,
67 PRBool aBlockNeedsSpaceManager
)
69 mPresContext(aPresContext
),
70 mReflowState(aReflowState
),
71 mOverflowTracker(aPresContext
, aFrame
, PR_FALSE
),
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" );
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
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
);
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
);
140 mCurrentLine
= aFrame
->end_lines();
142 mMinLineHeight
= nsHTMLReflowState::CalcLineHeight(aReflowState
.frame
);
144 // Calculate mOutsideBulletX
146 // FIXME (bug 25888): need to check the entire region that the first
147 // line overlaps, not just the top pixel.
149 mReflowState
.mStyleVisibility
->mDirection
== NS_STYLE_DIRECTION_LTR
?
151 PR_MIN(mReflowState
.ComputedWidth(), mAvailSpaceRect
.XMost()) +
152 mReflowState
.mComputedBorderPadding
.LeftRight();
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.
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
);
185 nsBlockReflowState::NewLineBox(nsIFrame
* aFrame
,
189 return NS_NewLineBox(mPresContext
->PresShell(), aFrame
, aCount
, aIsBlock
);
193 nsBlockReflowState::FreeLineBox(nsLineBox
* aLine
)
196 aLine
->Destroy(mPresContext
->PresShell());
201 nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame
* aFrame
,
202 nscoord
& aLeftResult
,
203 nscoord
& aRightResult
,
205 ReplacedElementWidthToClear
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.
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
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.
254 nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame
* aFrame
,
255 const nsStyleDisplay
* aDisplay
,
256 PRBool aBlockAvoidsFloats
,
259 #ifdef REALLY_NOISY_REFLOW
260 printf("CBAS frame=%p has float count %d\n", aFrame
, mBand
.GetFloatCount());
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
) ==
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
) {
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
;
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
;
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
;
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
,
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
);
340 nsBlockReflowState::GetAvailableSpace(nscoord aY
, PRBool aRelaxHeightConstraint
)
343 // Verify that the caller setup the coordinate system properly
345 mSpaceManager
->GetTranslation(wx
, wy
);
346 NS_ASSERTION((wx
== mSpaceManagerX
) && (wy
== mSpaceManagerY
),
350 mBand
.GetAvailableSpace(aY
- BorderPadding().top
, aRelaxHeightConstraint
,
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());
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).
378 nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine
)
380 mPrevBottomMargin
.Zero();
381 nsBlockFrame
*block
= mBlock
;
383 nsLineList::iterator firstLine
= block
->begin_lines();
386 if (aLine
->IsBlock()) {
387 mPrevBottomMargin
= aLine
->GetCarriedOutBottomMargin();
390 if (!aLine
->IsEmpty()) {
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();
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.
413 nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine
,
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();
421 nsIFrame
* floatFrame
= fc
->mPlaceholder
->GetOutOfFlowFrame();
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
);
430 if (nsBlockFrame::gNoisyReflow
|| nsBlockFrame::gNoisySpaceManager
) {
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
);
442 mSpaceManager
->AddRectRegion(floatFrame
, fc
->mRegion
);
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
));
467 mSpaceManager
->Translate(tx
, ty
);
468 for (nsBlockFrame::line_iterator line
= kid
->begin_lines(),
469 line_end
= kid
->end_lines();
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.
494 nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine
,
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
505 const nsMargin
& bp
= BorderPadding();
506 mSpaceManager
->Translate(-bp
.left
, -bp
.top
);
508 RecoverFloats(aLine
, aDeltaY
);
511 if (nsBlockFrame::gNoisyReflow
|| nsBlockFrame::gNoisySpaceManager
) {
512 mSpaceManager
->List(stdout
);
515 // And then put the translation back again
516 mSpaceManager
->Translate(bp
.left
, bp
.top
);
521 nsBlockReflowState::IsImpactedByFloat() const
523 #ifdef REALLY_NOISY_REFLOW
524 printf("nsBlockReflowState::IsImpactedByFloat %p returned %d\n",
525 this, mBand
.GetFloatCount());
527 return mBand
.GetFloatCount() > 0;
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
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...
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
;
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
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.
587 mSpaceManager
->GetTranslation(ox
, oy
);
588 nscoord dx
= ox
- mSpaceManagerX
;
589 nscoord dy
= oy
- mSpaceManagerY
;
590 mSpaceManager
->Translate(-dx
, -dy
);
594 // force it to fit if we're at the top of the block and we can't
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
;
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();
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
;
629 // Restore coordinate system
630 mSpaceManager
->Translate(dx
, dy
);
633 // Always claim to be placed; we don't know whether we fit yet, so we
634 // deal with this in PlaceBelowCurrentLineFloats
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
;
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
674 // At this point we know that there is enough horizontal space for
675 // the float (somewhere). Lets see if there is enough vertical
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
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
687 if (NS_STYLE_FLOAT_LEFT
== aFloats
) {
688 xa
= mAvailSpaceRect
.x
;
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
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
;
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.)
714 nscoord yb
= ya
+ aFloatSize
.height
;
718 // Get the available space at the new Y coordinate
719 if (mAvailSpaceRect
.height
<= 0) {
720 // there is no more available space. We lose.
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.
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
744 // Restore Y coordinate and available space information
745 // regardless of the outcome.
747 GetAvailableSpace(mY
, aForceFit
);
754 nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache
* aFloatCache
,
755 PRBool
* aIsLeftFloat
,
756 nsReflowStatus
& aReflowStatus
,
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
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");
794 nsMargin floatMargin
;
795 mBlock
->ReflowFloat(*this, placeholder
, floatMargin
, aReflowStatus
);
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
);
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.
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
);
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
;
842 if (fc
->mPlaceholder
->GetOutOfFlowFrame() == floatFrame
) {
845 prevFrame
= fc
->mPlaceholder
->GetOutOfFlowFrame();
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();
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
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.
893 nscoord floatX
, floatY
;
894 if (NS_STYLE_FLOAT_LEFT
== floatDisplay
->mFloats
) {
895 isLeftFloat
= PR_TRUE
;
896 floatX
= mAvailSpaceRect
.x
;
899 isLeftFloat
= PR_FALSE
;
900 if (!keepFloatOnSameLine
) {
901 floatX
= mAvailSpaceRect
.XMost() - floatSize
.width
;
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
907 floatX
= mAvailSpaceRect
.x
;
910 *aIsLeftFloat
= isLeftFloat
;
911 const nsMargin
& borderPadding
= BorderPadding();
912 floatY
= mY
- borderPadding
.top
;
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.)
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
937 region
.x
= region
.XMost();
941 if (region
.height
< 0) {
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
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
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
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
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
);
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
);
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
);
1022 * Place below-current-line floats.
1025 nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList
& aList
, PRBool aForceFit
)
1027 nsFloatCache
* fc
= aList
.Head();
1031 if (nsBlockFrame::gNoisyReflow
) {
1032 nsFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1033 printf("placing bcl float: ");
1034 nsFrame::ListTag(stdout
, fc
->mPlaceholder
->GetOutOfFlowFrame());
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.
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
);
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
);
1075 nsBlockReflowState::ClearFloats(nscoord aY
, PRUint8 aBreakType
,
1076 nsIFrame
*aReplacedBlock
)
1079 if (nsBlockFrame::gNoisyReflow
) {
1080 nsFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1081 printf("clear floats: in: aY=%d(%d)\n",
1082 aY
, aY
- BorderPadding().top
);
1086 #ifdef NOISY_FLOAT_CLEARING
1087 printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
1089 mSpaceManager
->List(stdout
);
1092 const nsMargin
& bp
= BorderPadding();
1095 if (aBreakType
!= NS_STYLE_CLEAR_NONE
) {
1096 newY
= bp
.top
+ mSpaceManager
->ClearFloats(newY
- bp
.top
, aBreakType
);
1099 if (aReplacedBlock
) {
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
) {
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
;
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.
1123 NS_NOTREACHED("avail space rect with zero height!");
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();
1135 if (nsBlockFrame::gNoisyReflow
) {
1136 nsFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1137 printf("clear floats: out: y=%d(%d)\n", newY
, newY
- bp
.top
);