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 * David Baron <dbaron@dbaron.org>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* class that a parent frame uses to reflow a block frame */
42 #include "nsBlockReflowContext.h"
43 #include "nsLineLayout.h"
44 #include "nsSpaceManager.h"
45 #include "nsIFontMetrics.h"
46 #include "nsPresContext.h"
47 #include "nsFrameManager.h"
48 #include "nsIContent.h"
49 #include "nsStyleContext.h"
50 #include "nsHTMLContainerFrame.h"
51 #include "nsBlockFrame.h"
52 #include "nsLineBox.h"
53 #include "nsIDOMHTMLTableCellElement.h"
54 #include "nsIDOMHTMLBodyElement.h"
55 #include "nsGkAtoms.h"
57 #include "nsLayoutUtils.h"
60 #undef NOISY_MAX_ELEMENT_SIZE
61 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
62 #undef NOISY_VERTICAL_MARGINS
64 #undef NOISY_MAX_ELEMENT_SIZE
65 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
66 #undef NOISY_VERTICAL_MARGINS
69 nsBlockReflowContext::nsBlockReflowContext(nsPresContext
* aPresContext
,
70 const nsHTMLReflowState
& aParentRS
)
71 : mPresContext(aPresContext
),
72 mOuterReflowState(aParentRS
),
77 static nsIFrame
* DescendIntoBlockLevelFrame(nsIFrame
* aFrame
)
79 nsIAtom
* type
= aFrame
->GetType();
80 if (type
== nsGkAtoms::columnSetFrame
)
81 return DescendIntoBlockLevelFrame(aFrame
->GetFirstChild(nsnull
));
86 nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState
& aRS
,
87 nsCollapsingMargin
* aMargin
, nsIFrame
* aClearanceFrame
,
88 PRBool
* aMayNeedRetry
, PRBool
* aBlockIsEmpty
)
90 // Include frame's top margin
91 aMargin
->Include(aRS
.mComputedMargin
.top
);
93 // The inclusion of the bottom margin when empty is done by the caller
94 // since it doesn't need to be done by the top-level (non-recursive)
97 #ifdef NOISY_VERTICAL_MARGINS
98 nsFrame::ListTag(stdout
, aRS
.frame
);
99 printf(": %d => %d\n", aRS
.mComputedMargin
.top
, aMargin
->get());
102 PRBool dirtiedLine
= PR_FALSE
;
103 PRBool setBlockIsEmpty
= PR_FALSE
;
105 // Calculate the frame's generational top-margin from its child
106 // blocks. Note that if the frame has a non-zero top-border or
107 // top-padding then this step is skipped because it will be a margin
108 // root. It is also skipped if the frame is a margin root for other
110 nsIFrame
* frame
= DescendIntoBlockLevelFrame(aRS
.frame
);
111 nsPresContext
* prescontext
= frame
->PresContext();
112 if (0 == aRS
.mComputedBorderPadding
.top
&&
113 nsLayoutUtils::GetAsBlock(frame
) &&
114 !nsBlockFrame::BlockIsMarginRoot(frame
)) {
115 // iterate not just through the lines of 'block' but also its
116 // overflow lines and the normal and overflow lines of its next in
117 // flows. Note that this will traverse some frames more than once:
118 // for example, if A contains B and A->nextinflow contains
119 // B->nextinflow, we'll traverse B->nextinflow twice. But this is
120 // OK because our traversal is idempotent.
121 for (nsBlockFrame
* block
= static_cast<nsBlockFrame
*>(frame
);
122 block
; block
= static_cast<nsBlockFrame
*>(block
->GetNextInFlow())) {
123 for (PRBool overflowLines
= PR_FALSE
; overflowLines
<= PR_TRUE
; ++overflowLines
) {
124 nsBlockFrame::line_iterator line
;
125 nsBlockFrame::line_iterator line_end
;
126 PRBool anyLines
= PR_TRUE
;
128 nsLineList
* lines
= block
->GetOverflowLines();
132 line
= lines
->begin();
133 line_end
= lines
->end();
136 line
= block
->begin_lines();
137 line_end
= block
->end_lines();
139 for (; anyLines
&& line
!= line_end
; ++line
) {
140 if (!aClearanceFrame
&& line
->HasClearance()) {
141 // If we don't have a clearance frame, then we're computing
142 // the collapsed margin in the first pass, assuming that all
143 // lines have no clearance. So clear their clearance flags.
144 line
->ClearHasClearance();
146 dirtiedLine
= PR_TRUE
;
150 if (line
->IsInline()) {
151 isEmpty
= line
->IsEmpty();
153 nsIFrame
* kid
= line
->mFirstChild
;
154 if (kid
== aClearanceFrame
) {
155 line
->SetHasClearance();
157 dirtiedLine
= PR_TRUE
;
160 // Here is where we recur. Now that we have determined that a
161 // generational collapse is required we need to compute the
162 // child blocks margin and so in so that we can look into
163 // it. For its margins to be computed we need to have a reflow
166 // We may have to construct an extra reflow state here if
167 // we drilled down through a block wrapper. At the moment
168 // we can only drill down one level so we only have to support
169 // one extra reflow state.
170 const nsHTMLReflowState
* outerReflowState
= &aRS
;
171 if (frame
!= aRS
.frame
) {
172 NS_ASSERTION(frame
->GetParent() == aRS
.frame
,
173 "Can only drill through one level of block wrapper");
174 nsSize
availSpace(aRS
.ComputedWidth(), aRS
.ComputedHeight());
175 outerReflowState
= new nsHTMLReflowState(prescontext
,
176 aRS
, frame
, availSpace
);
177 if (!outerReflowState
)
181 nsSize
availSpace(outerReflowState
->ComputedWidth(),
182 outerReflowState
->ComputedHeight());
183 nsHTMLReflowState
innerReflowState(prescontext
,
184 *outerReflowState
, kid
,
186 // Record that we're being optimistic by assuming the kid
188 if (kid
->GetStyleDisplay()->mBreakType
!= NS_STYLE_CLEAR_NONE
) {
189 *aMayNeedRetry
= PR_TRUE
;
191 if (ComputeCollapsedTopMargin(innerReflowState
, aMargin
, aClearanceFrame
, aMayNeedRetry
, &isEmpty
)) {
193 dirtiedLine
= PR_TRUE
;
196 aMargin
->Include(innerReflowState
.mComputedMargin
.bottom
);
198 if (outerReflowState
!= &aRS
) {
199 delete const_cast<nsHTMLReflowState
*>(outerReflowState
);
203 if (!setBlockIsEmpty
&& aBlockIsEmpty
) {
204 setBlockIsEmpty
= PR_TRUE
;
205 *aBlockIsEmpty
= PR_FALSE
;
210 if (!setBlockIsEmpty
&& aBlockIsEmpty
) {
211 // The first time we reach here is when this is the first block
212 // and we have processed all its normal lines.
213 setBlockIsEmpty
= PR_TRUE
;
214 // All lines are empty, or we wouldn't be here!
215 *aBlockIsEmpty
= aRS
.frame
->IsSelfEmpty();
223 if (!setBlockIsEmpty
&& aBlockIsEmpty
) {
224 *aBlockIsEmpty
= aRS
.frame
->IsEmpty();
227 #ifdef NOISY_VERTICAL_MARGINS
228 nsFrame::ListTag(stdout
, aRS
.frame
);
229 printf(": => %d\n", aMargin
->get());
236 nsBlockReflowContext::ReflowBlock(const nsRect
& aSpace
,
237 PRBool aApplyTopMargin
,
238 nsCollapsingMargin
& aPrevMargin
,
240 PRBool aIsAdjacentWithTop
,
242 nsHTMLReflowState
& aFrameRS
,
243 nsReflowStatus
& aFrameReflowStatus
,
244 nsBlockReflowState
& aState
)
247 mFrame
= aFrameRS
.frame
;
250 if (!aIsAdjacentWithTop
) {
251 aFrameRS
.mFlags
.mIsTopOfPage
= PR_FALSE
; // make sure this is cleared
254 if (aApplyTopMargin
) {
255 mTopMargin
= aPrevMargin
;
257 #ifdef NOISY_VERTICAL_MARGINS
258 nsFrame::ListTag(stdout
, mOuterReflowState
.frame
);
259 printf(": reflowing ");
260 nsFrame::ListTag(stdout
, mFrame
);
261 printf(" margin => %d, clearance => %d\n", mTopMargin
.get(), aClearance
);
264 // Adjust the available height if its constrained so that the
265 // child frame doesn't think it can reflow into its margin area.
266 if (NS_UNCONSTRAINEDSIZE
!= aFrameRS
.availableHeight
) {
267 aFrameRS
.availableHeight
-= mTopMargin
.get() + aClearance
;
271 nscoord tx
= 0, ty
= 0;
272 // The values of x and y do not matter for floats, so don't bother calculating
273 // them. Floats are guaranteed to have their own space manager, so tx and ty
274 // don't matter. mX and mY don't matter becacuse they are only used in
275 // PlaceBlock, which is not used for floats.
277 // Compute x/y coordinate where reflow will begin. Use the rules
278 // from 10.3.3 to determine what to apply. At this point in the
279 // reflow auto left/right margins will have a zero value.
281 nscoord x
= mSpace
.x
+ aFrameRS
.mComputedMargin
.left
;
282 nscoord y
= mSpace
.y
+ mTopMargin
.get() + aClearance
;
284 if ((mFrame
->GetStateBits() & NS_BLOCK_SPACE_MGR
) == 0)
285 aFrameRS
.mBlockDelta
= mOuterReflowState
.mBlockDelta
+ y
- aLine
->mBounds
.y
;
290 // Compute the translation to be used for adjusting the spacemanagager
291 // coordinate system for the frame. The spacemanager coordinates are
292 // <b>inside</b> the callers border+padding, but the x/y coordinates
293 // are not (recall that frame coordinates are relative to the parents
294 // origin and that the parents border/padding is <b>inside</b> the
295 // parent frame. Therefore we have to subtract out the parents
296 // border+padding before translating.
297 tx
= x
- mOuterReflowState
.mComputedBorderPadding
.left
;
298 ty
= y
- mOuterReflowState
.mComputedBorderPadding
.top
;
301 // Let frame know that we are reflowing it
302 mFrame
->WillReflow(mPresContext
);
305 mMetrics
.width
= nscoord(0xdeadbeef);
306 mMetrics
.height
= nscoord(0xdeadbeef);
309 mOuterReflowState
.mSpaceManager
->Translate(tx
, ty
);
310 rv
= mFrame
->Reflow(mPresContext
, mMetrics
, aFrameRS
, aFrameReflowStatus
);
311 mOuterReflowState
.mSpaceManager
->Translate(-tx
, -ty
);
314 if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus
)) {
315 if (CRAZY_WIDTH(mMetrics
.width
) || CRAZY_HEIGHT(mMetrics
.height
)) {
316 printf("nsBlockReflowContext: ");
317 nsFrame::ListTag(stdout
, mFrame
);
318 printf(" metrics=%d,%d!\n", mMetrics
.width
, mMetrics
.height
);
320 if ((mMetrics
.width
== nscoord(0xdeadbeef)) ||
321 (mMetrics
.height
== nscoord(0xdeadbeef))) {
322 printf("nsBlockReflowContext: ");
323 nsFrame::ListTag(stdout
, mFrame
);
324 printf(" didn't set w/h %d,%d!\n", mMetrics
.width
, mMetrics
.height
);
329 if (!(NS_FRAME_OUTSIDE_CHILDREN
& mFrame
->GetStateBits())) {
330 // Provide overflow area for child that doesn't have any
331 mMetrics
.mOverflowArea
.x
= 0;
332 mMetrics
.mOverflowArea
.y
= 0;
333 mMetrics
.mOverflowArea
.width
= mMetrics
.width
;
334 mMetrics
.mOverflowArea
.height
= mMetrics
.height
;
337 if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus
) ||
338 (mFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
339 // If frame is complete and has a next-in-flow, we need to delete
340 // them now. Do not do this when a break-before is signaled because
341 // the frame is going to get reflowed again (and may end up wanting
342 // a next-in-flow where it ends up), unless it is an out of flow frame.
343 if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus
)) {
344 nsIFrame
* kidNextInFlow
= mFrame
->GetNextInFlow();
345 if (nsnull
!= kidNextInFlow
) {
346 // Remove all of the childs next-in-flows. Make sure that we ask
347 // the right parent to do the removal (it's possible that the
348 // parent is not this because we are executing pullup code).
349 // Floats will eventually be removed via nsBlockFrame::RemoveFloat
350 // which detaches the placeholder from the float.
351 /* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */
352 aState
.mOverflowTracker
.Finish(mFrame
);
353 static_cast<nsHTMLContainerFrame
*>(kidNextInFlow
->GetParent())
354 ->DeleteNextInFlowChild(mPresContext
, kidNextInFlow
);
363 * Attempt to place the block frame within the available space. If
364 * it fits, apply horizontal positioning (CSS 10.3.3), collapse
365 * margins (CSS2 8.3.1). Also apply relative positioning.
368 nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState
& aReflowState
,
371 nsCollapsingMargin
& aBottomMarginResult
,
372 nsRect
& aInFlowBounds
,
373 nsRect
& aCombinedRect
,
374 nsReflowStatus aReflowStatus
)
376 // Compute collapsed bottom margin value.
377 if (NS_FRAME_IS_COMPLETE(aReflowStatus
)) {
378 aBottomMarginResult
= mMetrics
.mCarriedOutBottomMargin
;
379 aBottomMarginResult
.Include(aReflowState
.mComputedMargin
.bottom
);
381 // The used bottom-margin is set to zero above a break.
382 aBottomMarginResult
.Zero();
387 nscoord backupContainingBlockAdvance
= 0;
389 // Check whether the block's bottom margin collapses with its top
390 // margin. See CSS 2.1 section 8.3.1; those rules seem to match
391 // nsBlockFrame::IsEmpty(). Any such block must have zero height so
392 // check that first. Note that a block can have clearance and still
393 // have adjoining top/bottom margins, because the clearance goes
394 // above the top margin.
395 // Mark the frame as non-dirty; it has been reflowed (or we wouldn't
396 // be here), and we don't want to assert in CachedIsEmpty()
397 mFrame
->RemoveStateBits(NS_FRAME_IS_DIRTY
);
398 PRBool empty
= 0 == mMetrics
.height
&& aLine
->CachedIsEmpty();
400 // Collapse the bottom margin with the top margin that was already
402 aBottomMarginResult
.Include(mTopMargin
);
404 #ifdef NOISY_VERTICAL_MARGINS
406 nsFrame::ListTag(stdout
, mOuterReflowState
.frame
);
408 nsFrame::ListTag(stdout
, mFrame
);
409 printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n",
412 // Section 8.3.1 of CSS 2.1 says that blocks with adjoining
413 // top/bottom margins whose top margin collapses with their
414 // parent's top margin should have their top border-edge at the
415 // top border-edge of their parent. We actually don't have to do
416 // anything special to make this happen. In that situation,
417 // nsBlockFrame::ShouldApplyTopMargin will have returned PR_FALSE,
418 // and mTopMargin and aClearance will have been zero in
421 // If we did apply our top margin, but now we're collapsing it
422 // into the bottom margin, we need to back up the containing
423 // block's y-advance by our top margin so that it doesn't get
424 // counted twice. Note that here we're allowing the line's bounds
425 // to become different from the block's position; we do this
426 // because the containing block will place the next line at the
427 // line's YMost, and it must place the next line at a different
428 // point from where this empty block will be.
429 backupContainingBlockAdvance
= mTopMargin
.get();
432 // See if the frame fit. If it's the first frame or empty then it
433 // always fits. If the height is unconstrained then it always fits,
434 // even if there's some sort of integer overflow that makes y +
435 // mMetrics.height appear to go beyond the available height.
436 if (!empty
&& !aForceFit
&& mSpace
.height
!= NS_UNCONSTRAINEDSIZE
) {
437 nscoord yMost
= y
- backupContainingBlockAdvance
+ mMetrics
.height
;
438 if (yMost
> mSpace
.YMost()) {
439 // didn't fit, we must acquit.
440 mFrame
->DidReflow(mPresContext
, &aReflowState
, NS_FRAME_REFLOW_FINISHED
);
445 aInFlowBounds
= nsRect(x
, y
- backupContainingBlockAdvance
,
446 mMetrics
.width
, mMetrics
.height
);
448 // Apply CSS relative positioning
449 const nsStyleDisplay
* styleDisp
= mFrame
->GetStyleDisplay();
450 if (NS_STYLE_POSITION_RELATIVE
== styleDisp
->mPosition
) {
451 x
+= aReflowState
.mComputedOffsets
.left
;
452 y
+= aReflowState
.mComputedOffsets
.top
;
455 // Now place the frame and complete the reflow process
456 nsContainerFrame::FinishReflowChild(mFrame
, mPresContext
, &aReflowState
, mMetrics
, x
, y
, 0);
458 aCombinedRect
= mMetrics
.mOverflowArea
+ nsPoint(x
, y
);