backing out bug 347743 due to major crasher in 386332
[mozilla-central.git] / layout / generic / nsBlockFrame.cpp
blobf25638c54b6a3c03a79f7018fd7ed292edb24cd6
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 * IBM Corporation
28 * Mats Palmgren <mats.palmgren@bredband.net>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
45 * rendering object for CSS display:block and display:list-item objects,
46 * also used inside table cells
49 #include "nsCOMPtr.h"
50 #include "nsBlockFrame.h"
51 #include "nsBlockReflowContext.h"
52 #include "nsBlockReflowState.h"
53 #include "nsBlockBandData.h"
54 #include "nsBulletFrame.h"
55 #include "nsLineBox.h"
56 #include "nsInlineFrame.h"
57 #include "nsLineLayout.h"
58 #include "nsPlaceholderFrame.h"
59 #include "nsStyleConsts.h"
60 #include "nsFrameManager.h"
61 #include "nsPresContext.h"
62 #include "nsIPresShell.h"
63 #include "nsStyleContext.h"
64 #include "nsIView.h"
65 #include "nsIFontMetrics.h"
66 #include "nsHTMLParts.h"
67 #include "nsGkAtoms.h"
68 #include "nsIDOMEvent.h"
69 #include "nsGenericHTMLElement.h"
70 #include "prprf.h"
71 #include "nsStyleChangeList.h"
72 #include "nsFrameSelection.h"
73 #include "nsSpaceManager.h"
74 #include "nsIntervalSet.h"
75 #include "prenv.h"
76 #include "plstr.h"
77 #include "nsGUIEvent.h"
78 #include "nsLayoutErrors.h"
79 #include "nsAutoPtr.h"
80 #include "nsIServiceManager.h"
81 #include "nsIScrollableFrame.h"
82 #ifdef ACCESSIBILITY
83 #include "nsIDOMHTMLDocument.h"
84 #include "nsIAccessibilityService.h"
85 #endif
86 #include "nsLayoutUtils.h"
87 #include "nsBoxLayoutState.h"
88 #include "nsDisplayList.h"
89 #include "nsContentErrors.h"
90 #include "nsCSSAnonBoxes.h"
92 #ifdef IBMBIDI
93 #include "nsBidiPresUtils.h"
94 #endif // IBMBIDI
96 #include "nsIDOMHTMLBodyElement.h"
97 #include "nsIDOMHTMLHtmlElement.h"
99 static const int MIN_LINES_NEEDING_CURSOR = 20;
101 #ifdef DEBUG
102 #include "nsPrintfCString.h"
103 #include "nsBlockDebugFlags.h"
105 PRBool nsBlockFrame::gLamePaintMetrics;
106 PRBool nsBlockFrame::gLameReflowMetrics;
107 PRBool nsBlockFrame::gNoisy;
108 PRBool nsBlockFrame::gNoisyDamageRepair;
109 PRBool nsBlockFrame::gNoisyIntrinsic;
110 PRBool nsBlockFrame::gNoisyReflow;
111 PRBool nsBlockFrame::gReallyNoisyReflow;
112 PRBool nsBlockFrame::gNoisySpaceManager;
113 PRBool nsBlockFrame::gVerifyLines;
114 PRBool nsBlockFrame::gDisableResizeOpt;
116 PRInt32 nsBlockFrame::gNoiseIndent;
118 struct BlockDebugFlags {
119 const char* name;
120 PRBool* on;
123 static const BlockDebugFlags gFlags[] = {
124 { "reflow", &nsBlockFrame::gNoisyReflow },
125 { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
126 { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
127 { "space-manager", &nsBlockFrame::gNoisySpaceManager },
128 { "verify-lines", &nsBlockFrame::gVerifyLines },
129 { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
130 { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
131 { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
132 { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
134 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
136 static void
137 ShowDebugFlags()
139 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
140 const BlockDebugFlags* bdf = gFlags;
141 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
142 for (; bdf < end; bdf++) {
143 printf(" %s\n", bdf->name);
145 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
146 printf("names (no whitespace)\n");
149 void
150 nsBlockFrame::InitDebugFlags()
152 static PRBool firstTime = PR_TRUE;
153 if (firstTime) {
154 firstTime = PR_FALSE;
155 char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
156 if (flags) {
157 PRBool error = PR_FALSE;
158 for (;;) {
159 char* cm = PL_strchr(flags, ',');
160 if (cm) *cm = '\0';
162 PRBool found = PR_FALSE;
163 const BlockDebugFlags* bdf = gFlags;
164 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
165 for (; bdf < end; bdf++) {
166 if (PL_strcasecmp(bdf->name, flags) == 0) {
167 *(bdf->on) = PR_TRUE;
168 printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
169 gNoisy = PR_TRUE;
170 found = PR_TRUE;
171 break;
174 if (!found) {
175 error = PR_TRUE;
178 if (!cm) break;
179 *cm = ',';
180 flags = cm + 1;
182 if (error) {
183 ShowDebugFlags();
189 #endif
191 // add in a sanity check for absurdly deep frame trees. See bug 42138
192 // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
193 #define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
195 //----------------------------------------------------------------------
197 // Debugging support code
199 #ifdef DEBUG
200 const char* nsBlockFrame::kReflowCommandType[] = {
201 "ContentChanged",
202 "StyleChanged",
203 "ReflowDirty",
204 "Timeout",
205 "UserDefined",
207 #endif
209 #ifdef REALLY_NOISY_FIRST_LINE
210 static void
211 DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
213 fputs(gap, stdout);
214 nsFrame::ListTag(stdout, aFrame);
215 printf(": ");
216 nsStyleContext* sc = aFrame->GetStyleContext();
217 while (nsnull != sc) {
218 nsStyleContext* psc;
219 printf("%p ", sc);
220 psc = sc->GetParent();
221 sc = psc;
223 printf("\n");
225 #endif
227 #ifdef REFLOW_STATUS_COVERAGE
228 static void
229 RecordReflowStatus(PRBool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
231 static PRUint32 record[2];
233 // 0: child-is-block
234 // 1: child-is-inline
235 PRIntn index = 0;
236 if (!aChildIsBlock) index |= 1;
238 // Compute new status
239 PRUint32 newS = record[index];
240 if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
241 if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
242 newS |= 1;
244 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
245 newS |= 2;
247 else {
248 newS |= 4;
251 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
252 newS |= 8;
254 else {
255 newS |= 16;
258 // Log updates to the status that yield different values
259 if (record[index] != newS) {
260 record[index] = newS;
261 printf("record(%d): %02x %02x\n", index, record[0], record[1]);
264 #endif
266 //----------------------------------------------------------------------
268 const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
270 nsIFrame*
271 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags)
273 nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
274 if (it) {
275 it->SetFlags(aFlags);
277 return it;
280 nsBlockFrame::~nsBlockFrame()
284 void
285 nsBlockFrame::Destroy()
287 if (mState & NS_FRAME_GENERATED_CONTENT) {
288 // Make sure all the content nodes for the generated content inside
289 // this frame know it's going away.
290 // This is duplicated in nsInlineFrame::Destroy
291 // See also nsCSSFrameConstructor::CreateGeneratedContentFrame which
292 // created this frame.
294 // XXXbz would this be better done via a global structure in
295 // nsCSSFrameConstructor that could key off of
296 // GeneratedContentFrameRemoved or something? The problem is that
297 // our kids are gone by the time that's called.
298 nsContainerFrame::CleanupGeneratedContentIn(mContent, this);
301 mAbsoluteContainer.DestroyFrames(this);
302 // Outside bullets are not in our child-list so check for them here
303 // and delete them when present.
304 if (mBullet && HaveOutsideBullet()) {
305 mBullet->Destroy();
306 mBullet = nsnull;
309 mFloats.DestroyFrames();
311 nsPresContext* presContext = PresContext();
313 nsLineBox::DeleteLineList(presContext, mLines);
315 // destroy overflow lines now
316 nsLineList* overflowLines = RemoveOverflowLines();
317 if (overflowLines) {
318 nsLineBox::DeleteLineList(presContext, *overflowLines);
322 nsAutoOOFFrameList oofs(this);
323 oofs.mList.DestroyFrames();
324 // oofs is now empty and will remove the frame list property
327 nsBlockFrameSuper::Destroy();
330 NS_IMETHODIMP
331 nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
333 NS_PRECONDITION(aInstancePtr, "null out param");
334 if (aIID.Equals(kBlockFrameCID)) {
335 *aInstancePtr = NS_STATIC_CAST(void*, NS_STATIC_CAST(nsBlockFrame*, this));
336 return NS_OK;
338 if (aIID.Equals(NS_GET_IID(nsILineIterator)) ||
339 aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)))
341 nsLineIterator* it = new nsLineIterator;
342 if (!it) {
343 *aInstancePtr = nsnull;
344 return NS_ERROR_OUT_OF_MEMORY;
346 NS_ADDREF(it); // reference passed to caller
347 const nsStyleVisibility* visibility = GetStyleVisibility();
348 nsresult rv = it->Init(mLines,
349 visibility->mDirection == NS_STYLE_DIRECTION_RTL);
350 if (NS_FAILED(rv)) {
351 NS_RELEASE(it);
352 return rv;
354 *aInstancePtr = NS_STATIC_CAST(void*,
355 NS_STATIC_CAST(nsILineIteratorNavigator*, it));
356 return NS_OK;
358 return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
361 nsSplittableType
362 nsBlockFrame::GetSplittableType() const
364 return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
367 #ifdef DEBUG
368 NS_METHOD
369 nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
371 IndentBy(out, aIndent);
372 ListTag(out);
373 #ifdef DEBUG_waterson
374 fprintf(out, " [parent=%p]", mParent);
375 #endif
376 if (HasView()) {
377 fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
379 if (nsnull != mNextSibling) {
380 fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
383 // Output the flow linkage
384 if (nsnull != GetPrevInFlow()) {
385 fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, GetPrevInFlow()));
387 if (nsnull != GetNextInFlow()) {
388 fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, GetNextInFlow()));
391 // Output the rect and state
392 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
393 if (0 != mState) {
394 fprintf(out, " [state=%08x]", mState);
396 nsBlockFrame* f = NS_CONST_CAST(nsBlockFrame*, this);
397 nsRect* overflowArea = f->GetOverflowAreaProperty(PR_FALSE);
398 if (overflowArea) {
399 fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea->x, overflowArea->y,
400 overflowArea->width, overflowArea->height);
402 PRInt32 numInlineLines = 0;
403 PRInt32 numBlockLines = 0;
404 if (! mLines.empty()) {
405 for (const_line_iterator line = begin_lines(), line_end = end_lines();
406 line != line_end;
407 ++line)
409 if (line->IsBlock())
410 numBlockLines++;
411 else
412 numInlineLines++;
415 fprintf(out, " sc=%p(i=%d,b=%d)",
416 NS_STATIC_CAST(void*, mStyleContext), numInlineLines, numBlockLines);
417 nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
418 if (pseudoTag) {
419 nsAutoString atomString;
420 pseudoTag->ToString(atomString);
421 fprintf(out, " pst=%s",
422 NS_LossyConvertUTF16toASCII(atomString).get());
424 fputs("<\n", out);
426 aIndent++;
428 // Output the lines
429 if (! mLines.empty()) {
430 for (const_line_iterator line = begin_lines(), line_end = end_lines();
431 line != line_end;
432 ++line)
434 line->List(out, aIndent);
438 nsIAtom* listName = nsnull;
439 PRInt32 listIndex = 0;
440 for (;;) {
441 listName = GetAdditionalChildListName(listIndex++);
442 if (nsnull == listName) {
443 break;
445 nsIFrame* kid = GetFirstChild(listName);
446 if (kid) {
447 IndentBy(out, aIndent);
448 nsAutoString tmp;
449 if (nsnull != listName) {
450 listName->ToString(tmp);
451 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
453 fputs("<\n", out);
454 while (kid) {
455 nsIFrameDebug* frameDebug;
457 if (NS_SUCCEEDED(CallQueryInterface(kid, &frameDebug))) {
458 frameDebug->List(out, aIndent + 1);
460 kid = kid->GetNextSibling();
462 IndentBy(out, aIndent);
463 fputs(">\n", out);
467 aIndent--;
468 IndentBy(out, aIndent);
469 fputs(">\n", out);
471 return NS_OK;
474 NS_IMETHODIMP_(nsFrameState)
475 nsBlockFrame::GetDebugStateBits() const
477 // We don't want to include our cursor flag in the bits the
478 // regression tester looks at
479 return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
482 NS_IMETHODIMP
483 nsBlockFrame::GetFrameName(nsAString& aResult) const
485 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
487 #endif
489 nsIAtom*
490 nsBlockFrame::GetType() const
492 return nsGkAtoms::blockFrame;
495 void
496 nsBlockFrame::InvalidateInternal(const nsRect& aDamageRect,
497 nscoord aX, nscoord aY, nsIFrame* aForChild,
498 PRBool aImmediate)
500 // Optimize by suppressing invalidation of areas that are clipped out
501 // with CSS 'clip'.
502 const nsStyleDisplay* disp = GetStyleDisplay();
503 nsRect absPosClipRect;
504 if (GetAbsPosClipRect(disp, &absPosClipRect, GetSize())) {
505 // Restrict the invalidated area to abs-pos clip rect
506 // abs-pos clipping clips everything in the frame
507 nsRect r;
508 if (r.IntersectRect(aDamageRect, absPosClipRect - nsPoint(aX, aY))) {
509 nsBlockFrameSuper::InvalidateInternal(r, aX, aY, this, aImmediate);
511 return;
514 nsBlockFrameSuper::InvalidateInternal(aDamageRect, aX, aY, this, aImmediate);
517 nscoord
518 nsBlockFrame::GetBaseline() const
520 NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty");
521 nscoord result;
522 if (nsLayoutUtils::GetLastLineBaseline(this, &result))
523 return result;
524 return nsFrame::GetBaseline();
527 /////////////////////////////////////////////////////////////////////////////
528 // Child frame enumeration
530 nsIFrame*
531 nsBlockFrame::GetFirstChild(nsIAtom* aListName) const
533 if (mAbsoluteContainer.GetChildListName() == aListName) {
534 nsIFrame* result = nsnull;
535 mAbsoluteContainer.FirstChild(this, aListName, &result);
536 return result;
538 else if (nsnull == aListName) {
539 return (mLines.empty()) ? nsnull : mLines.front()->mFirstChild;
541 else if (aListName == nsGkAtoms::overflowList) {
542 nsLineList* overflowLines = GetOverflowLines();
543 return overflowLines ? overflowLines->front()->mFirstChild : nsnull;
545 else if (aListName == nsGkAtoms::overflowOutOfFlowList) {
546 return GetOverflowOutOfFlows().FirstChild();
548 else if (aListName == nsGkAtoms::floatList) {
549 return mFloats.FirstChild();
551 else if (aListName == nsGkAtoms::bulletList) {
552 if (HaveOutsideBullet()) {
553 return mBullet;
556 return nsnull;
559 nsIAtom*
560 nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex) const
562 switch (aIndex) {
563 case NS_BLOCK_FRAME_FLOAT_LIST_INDEX:
564 return nsGkAtoms::floatList;
565 case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
566 return nsGkAtoms::bulletList;
567 case NS_BLOCK_FRAME_OVERFLOW_LIST_INDEX:
568 return nsGkAtoms::overflowList;
569 case NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX:
570 return nsGkAtoms::overflowOutOfFlowList;
571 case NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX:
572 return mAbsoluteContainer.GetChildListName();
573 default:
574 return nsnull;
578 /* virtual */ PRBool
579 nsBlockFrame::IsContainingBlock() const
581 return PR_TRUE;
584 /* virtual */ PRBool
585 nsBlockFrame::IsFloatContainingBlock() const
587 return PR_TRUE;
590 static PRBool IsContinuationPlaceholder(nsIFrame* aFrame)
592 return aFrame->GetPrevInFlow() &&
593 nsGkAtoms::placeholderFrame == aFrame->GetType();
596 static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent,
597 nsIFrame* aNewParent) {
598 NS_ASSERTION(aOldParent == aFrame->GetParent(),
599 "Parent not consistent with exepectations");
601 aFrame->SetParent(aNewParent);
603 // When pushing and pulling frames we need to check for whether any
604 // views need to be reparented
605 nsHTMLContainerFrame::ReparentFrameView(aFrame->PresContext(), aFrame,
606 aOldParent, aNewParent);
609 //////////////////////////////////////////////////////////////////////
610 // Frame structure methods
612 //////////////////////////////////////////////////////////////////////
613 // Reflow methods
615 /* virtual */ void
616 nsBlockFrame::MarkIntrinsicWidthsDirty()
618 mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
619 mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
621 nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
624 /* virtual */ nscoord
625 nsBlockFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
627 DISPLAY_MIN_WIDTH(this, mMinWidth);
628 if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
629 return mMinWidth;
631 #ifdef DEBUG
632 if (gNoisyIntrinsic) {
633 IndentBy(stdout, gNoiseIndent);
634 ListTag(stdout);
635 printf(": GetMinWidth\n");
637 AutoNoisyIndenter indent(gNoisyIntrinsic);
638 #endif
640 #ifdef IBMBIDI
641 ResolveBidi();
642 #endif // IBMBIDI
644 InlineMinWidthData data;
645 for (line_iterator line = begin_lines(), line_end = end_lines();
646 line != line_end; ++line)
648 #ifdef DEBUG
649 if (gNoisyIntrinsic) {
650 IndentBy(stdout, gNoiseIndent);
651 printf("line (%s%s)\n",
652 line->IsBlock() ? "block" : "inline",
653 line->IsEmpty() ? ", empty" : "");
655 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
656 #endif
657 if (line->IsBlock()) {
658 data.ForceBreak(aRenderingContext);
659 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
660 line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
661 data.ForceBreak(aRenderingContext);
662 } else {
663 if (line == begin_lines() && !GetPrevContinuation()) {
664 const nsStyleCoord &indent = GetStyleText()->mTextIndent;
665 if (indent.GetUnit() == eStyleUnit_Coord)
666 data.currentLine += indent.GetCoordValue();
668 // XXX Bug NNNNNN Should probably handle percentage text-indent.
670 nsIFrame *kid = line->mFirstChild;
671 for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
672 ++i, kid = kid->GetNextSibling()) {
673 kid->AddInlineMinWidth(aRenderingContext, &data);
676 #ifdef DEBUG
677 if (gNoisyIntrinsic) {
678 IndentBy(stdout, gNoiseIndent);
679 printf("min: [prevLines=%d currentLine=%d]\n",
680 data.prevLines, data.currentLine);
682 #endif
684 data.ForceBreak(aRenderingContext);
686 mMinWidth = data.prevLines;
687 return mMinWidth;
690 /* virtual */ nscoord
691 nsBlockFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
693 DISPLAY_PREF_WIDTH(this, mPrefWidth);
694 if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
695 return mPrefWidth;
697 #ifdef DEBUG
698 if (gNoisyIntrinsic) {
699 IndentBy(stdout, gNoiseIndent);
700 ListTag(stdout);
701 printf(": GetPrefWidth\n");
703 AutoNoisyIndenter indent(gNoisyIntrinsic);
704 #endif
706 #ifdef IBMBIDI
707 ResolveBidi();
708 #endif // IBMBIDI
710 InlinePrefWidthData data;
711 for (line_iterator line = begin_lines(), line_end = end_lines();
712 line != line_end; ++line)
714 #ifdef DEBUG
715 if (gNoisyIntrinsic) {
716 IndentBy(stdout, gNoiseIndent);
717 printf("line (%s%s)\n",
718 line->IsBlock() ? "block" : "inline",
719 line->IsEmpty() ? ", empty" : "");
721 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
722 #endif
723 if (line->IsBlock()) {
724 data.ForceBreak(aRenderingContext);
725 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
726 line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
727 data.ForceBreak(aRenderingContext);
728 } else {
729 if (line == begin_lines() && !GetPrevContinuation()) {
730 const nsStyleCoord &indent = GetStyleText()->mTextIndent;
731 if (indent.GetUnit() == eStyleUnit_Coord)
732 data.currentLine += indent.GetCoordValue();
734 // XXX Bug NNNNNN Should probably handle percentage text-indent.
736 nsIFrame *kid = line->mFirstChild;
737 for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
738 ++i, kid = kid->GetNextSibling()) {
739 kid->AddInlinePrefWidth(aRenderingContext, &data);
742 #ifdef DEBUG
743 if (gNoisyIntrinsic) {
744 IndentBy(stdout, gNoiseIndent);
745 printf("pref: [prevLines=%d currentLine=%d]\n",
746 data.prevLines, data.currentLine);
748 #endif
750 data.ForceBreak(aRenderingContext);
752 mPrefWidth = data.prevLines;
753 return mPrefWidth;
756 static nsSize
757 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
758 nsSize aFrameSize)
760 // The issue here is that for a 'height' of 'auto' the reflow state
761 // code won't know how to calculate the containing block height
762 // because it's calculated bottom up. So we use our own computed
763 // size as the dimensions. We don't really want to do this for the
764 // initial containing block
765 nsIFrame* frame = aReflowState.frame;
766 if (nsLayoutUtils::IsInitialContainingBlock(frame)) {
767 return nsSize(-1, -1);
770 nsSize cbSize(aFrameSize);
771 // Containing block is relative to the padding edge
772 const nsMargin& border = aReflowState.mStyleBorder->GetBorder();
773 cbSize.width -= border.left + border.right;
774 cbSize.height -= border.top + border.bottom;
776 if (frame->GetParent()->GetContent() == frame->GetContent()) {
777 // We are a wrapped frame for the content. Use the container's
778 // dimensions, if they have been precomputed.
779 // XXX This is a hack! We really should be waiting until the outermost
780 // frame is fully reflowed and using the resulting dimensions, even
781 // if they're intrinsic.
782 // In fact we should be attaching absolute children to the outermost
783 // frame and not always sticking them in block frames.
785 // First, find the reflow state for the outermost frame for this
786 // content.
787 const nsHTMLReflowState* aLastRS = &aReflowState;
788 const nsHTMLReflowState* lastButOneRS = &aReflowState;
789 while (aLastRS->parentReflowState &&
790 aLastRS->parentReflowState->frame->GetContent() == frame->GetContent()) {
791 lastButOneRS = aLastRS;
792 aLastRS = aLastRS->parentReflowState;
794 if (aLastRS != &aReflowState) {
795 // The wrapper frame should be block-level. If it isn't, how the
796 // heck did it end up wrapping this block frame?
797 NS_ASSERTION(aLastRS->frame->GetStyleDisplay()->IsBlockOutside(),
798 "Wrapping frame should be block-level");
799 // Scrollbars need to be specifically excluded, if present, because they are outside the
800 // padding-edge. We need better APIs for getting the various boxes from a frame.
801 nsIScrollableFrame* scrollFrame;
802 CallQueryInterface(aLastRS->frame, &scrollFrame);
803 nsMargin scrollbars(0,0,0,0);
804 if (scrollFrame) {
805 nsBoxLayoutState dummyState(aLastRS->frame->PresContext(),
806 aLastRS->rendContext);
807 scrollbars = scrollFrame->GetDesiredScrollbarSizes(&dummyState);
808 // XXX We should account for the horizontal scrollbar too --- but currently
809 // nsGfxScrollFrame assumes nothing depends on the presence (or absence) of
810 // a horizontal scrollbar, so accounting for it would create incremental
811 // reflow bugs.
812 //if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
813 scrollbars.top = scrollbars.bottom = 0;
815 if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
816 scrollbars.left = scrollbars.right = 0;
819 // We found a reflow state for the outermost wrapping frame, so use
820 // its computed metrics if available
821 if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
822 cbSize.width = PR_MAX(0,
823 aLastRS->ComputedWidth() + aLastRS->mComputedPadding.LeftRight() - scrollbars.LeftRight());
825 if (aLastRS->mComputedHeight != NS_UNCONSTRAINEDSIZE) {
826 cbSize.height = PR_MAX(0,
827 aLastRS->mComputedHeight + aLastRS->mComputedPadding.TopBottom() - scrollbars.TopBottom());
832 return cbSize;
835 NS_IMETHODIMP
836 nsBlockFrame::Reflow(nsPresContext* aPresContext,
837 nsHTMLReflowMetrics& aMetrics,
838 const nsHTMLReflowState& aReflowState,
839 nsReflowStatus& aStatus)
841 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
842 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
843 #ifdef DEBUG
844 if (gNoisyReflow) {
845 IndentBy(stdout, gNoiseIndent);
846 ListTag(stdout);
847 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
848 aReflowState.availableWidth, aReflowState.availableHeight,
849 aReflowState.ComputedWidth(), aReflowState.mComputedHeight);
851 AutoNoisyIndenter indent(gNoisy);
852 PRTime start = LL_ZERO; // Initialize these variablies to silence the compiler.
853 PRInt32 ctc = 0; // We only use these if they are set (gLameReflowMetrics).
854 if (gLameReflowMetrics) {
855 start = PR_Now();
856 ctc = nsLineBox::GetCtorCount();
858 #endif
860 nsSize oldSize = GetSize();
862 // Should we create a space manager?
863 nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState));
865 // XXXldb If we start storing the space manager in the frame rather
866 // than keeping it around only during reflow then we should create it
867 // only when there are actually floats to manage. Otherwise things
868 // like tables will gain significant bloat.
869 PRBool needSpaceManager = nsBlockFrame::BlockNeedsSpaceManager(this);
870 if (needSpaceManager)
871 autoSpaceManager.CreateSpaceManagerFor(aPresContext, this);
873 // OK, some lines may be reflowed. Blow away any saved line cursor because
874 // we may invalidate the nondecreasing combinedArea.y/yMost invariant,
875 // and we may even delete the line with the line cursor.
876 ClearLineCursor();
878 if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
879 #ifdef DEBUG_kipp
881 extern char* nsPresShell_ReflowStackPointerTop;
882 char marker;
883 char* newsp = (char*) &marker;
884 printf("XXX: frame tree is too deep; approx stack size = %d\n",
885 nsPresShell_ReflowStackPointerTop - newsp);
887 #endif
888 aStatus = NS_FRAME_COMPLETE;
889 return NS_OK;
892 PRBool marginRoot = BlockIsMarginRoot(this);
893 nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
894 marginRoot, marginRoot, needSpaceManager);
896 #ifdef IBMBIDI
897 ResolveBidi();
898 #endif // IBMBIDI
900 if (RenumberLists(aPresContext)) {
901 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
904 nsresult rv = NS_OK;
906 // ALWAYS drain overflow. We never want to leave the previnflow's
907 // overflow lines hanging around; block reflow depends on the
908 // overflow line lists being cleared out between reflow passes.
909 DrainOverflowLines(state);
910 state.SetupOverflowPlaceholdersProperty();
912 // If we're not dirty (which means we'll mark everything dirty later)
913 // and our width has changed, mark the lines dirty that we need to
914 // mark dirty for a resize reflow.
915 if (aReflowState.mFlags.mHResize)
916 PrepareResizeReflow(state);
918 mState &= ~NS_FRAME_FIRST_REFLOW;
920 // Now reflow...
921 rv = ReflowDirtyLines(state);
922 NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
923 if (NS_FAILED(rv)) return rv;
925 // If the block is complete, put continuted floats in the closest ancestor
926 // block that uses the same space manager and leave the block complete; this
927 // allows subsequent lines on the page to be impacted by floats. If the
928 // block is incomplete or there is no ancestor using the same space manager,
929 // put continued floats at the beginning of the first overflow line.
930 if (state.mOverflowPlaceholders.NotEmpty()) {
931 NS_ASSERTION(aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE,
932 "Somehow we failed to fit all content, even though we have unlimited space!");
933 if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
934 // find the nearest block ancestor that uses the same space manager
935 for (const nsHTMLReflowState* ancestorRS = aReflowState.parentReflowState;
936 ancestorRS;
937 ancestorRS = ancestorRS->parentReflowState) {
938 nsIFrame* ancestor = ancestorRS->frame;
939 nsIAtom* fType = ancestor->GetType();
940 if ((nsGkAtoms::blockFrame == fType || nsGkAtoms::areaFrame == fType) &&
941 aReflowState.mSpaceManager == ancestorRS->mSpaceManager) {
942 // Put the continued floats in ancestor since it uses the same space manager
943 nsFrameList* ancestorPlace =
944 ((nsBlockFrame*)ancestor)->GetOverflowPlaceholders();
945 // The ancestor should have this list, since it's being reflowed. But maybe
946 // it isn't because of reflow roots or something.
947 if (ancestorPlace) {
948 for (nsIFrame* f = state.mOverflowPlaceholders.FirstChild();
949 f; f = f->GetNextSibling()) {
950 NS_ASSERTION(IsContinuationPlaceholder(f),
951 "Overflow placeholders must be continuation placeholders");
952 ReparentFrame(f, this, ancestorRS->frame);
953 nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
954 mFloats.RemoveFrame(oof);
955 ReparentFrame(oof, this, ancestorRS->frame);
956 // Clear the next-sibling in case the frame wasn't in mFloats
957 oof->SetNextSibling(nsnull);
958 // Do not put the float into any child frame list, because
959 // placeholders in the overflow-placeholder block-state list
960 // don't keep their out of flows in a child frame list.
962 ancestorPlace->AppendFrames(nsnull, state.mOverflowPlaceholders.FirstChild());
963 state.mOverflowPlaceholders.SetFrames(nsnull);
964 break;
969 if (!state.mOverflowPlaceholders.IsEmpty()) {
970 state.mOverflowPlaceholders.SortByContentOrder();
971 PRInt32 numOverflowPlace = state.mOverflowPlaceholders.GetLength();
972 nsLineBox* newLine =
973 state.NewLineBox(state.mOverflowPlaceholders.FirstChild(),
974 numOverflowPlace, PR_FALSE);
975 if (newLine) {
976 nsLineList* overflowLines = GetOverflowLines();
977 if (overflowLines) {
978 // Need to put the overflow placeholders' floats into our
979 // overflow-out-of-flows list, since the overflow placeholders are
980 // going onto our overflow line list. Put them last, because that's
981 // where the placeholders are going.
982 nsFrameList floats;
983 nsIFrame* lastFloat = nsnull;
984 for (nsIFrame* f = state.mOverflowPlaceholders.FirstChild();
985 f; f = f->GetNextSibling()) {
986 NS_ASSERTION(IsContinuationPlaceholder(f),
987 "Overflow placeholders must be continuation placeholders");
988 nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
989 // oof is not currently in any child list
990 floats.InsertFrames(nsnull, lastFloat, oof);
991 lastFloat = oof;
994 // Put the new placeholders *last* in the overflow lines
995 // because they might have previnflows in the overflow lines.
996 nsIFrame* lastChild = overflowLines->back()->LastChild();
997 lastChild->SetNextSibling(state.mOverflowPlaceholders.FirstChild());
998 // Create a new line as the last line and put the
999 // placeholders there
1000 overflowLines->push_back(newLine);
1002 nsAutoOOFFrameList oofs(this);
1003 oofs.mList.AppendFrames(nsnull, floats.FirstChild());
1005 else {
1006 mLines.push_back(newLine);
1007 nsLineList::iterator nextToLastLine = ----end_lines();
1008 PushLines(state, nextToLastLine);
1010 state.mOverflowPlaceholders.SetFrames(nsnull);
1012 state.mReflowStatus |= NS_FRAME_NOT_COMPLETE | NS_FRAME_REFLOW_NEXTINFLOW;
1016 if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
1017 if (GetOverflowLines()) {
1018 state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1021 if (NS_STYLE_OVERFLOW_CLIP == aReflowState.mStyleDisplay->mOverflowX) {
1022 state.mReflowStatus = NS_FRAME_COMPLETE;
1024 else {
1025 #ifdef DEBUG_kipp
1026 ListTag(stdout); printf(": block is not complete\n");
1027 #endif
1031 CheckFloats(state);
1033 // Place the "marker" (bullet) frame if it is placed next to a block
1034 // child.
1036 // According to the CSS2 spec, section 12.6.1, the "marker" box
1037 // participates in the height calculation of the list-item box's
1038 // first line box.
1040 // There are exactly two places a bullet can be placed: near the
1041 // first or second line. It's only placed on the second line in a
1042 // rare case: an empty first line followed by a second line that
1043 // contains a block (example: <LI>\n<P>... ). This is where
1044 // the second case can happen.
1045 if (mBullet && HaveOutsideBullet() &&
1046 (mLines.empty() ||
1047 mLines.front()->IsBlock() ||
1048 0 == mLines.front()->mBounds.height)) {
1049 // Reflow the bullet
1050 nsHTMLReflowMetrics metrics;
1051 ReflowBullet(state, metrics);
1053 nscoord baseline;
1054 if (!nsLayoutUtils::GetFirstLineBaseline(this, &baseline)) {
1055 baseline = 0;
1058 // Doing the alignment using the baseline will also cater for
1059 // bullets that are placed next to a child block (bug 92896)
1061 // Tall bullets won't look particularly nice here...
1062 nsRect bbox = mBullet->GetRect();
1063 bbox.y = baseline - metrics.ascent;
1064 mBullet->SetRect(bbox);
1067 // Compute our final size
1068 ComputeFinalSize(aReflowState, state, aMetrics);
1070 ComputeCombinedArea(aReflowState, aMetrics);
1072 // see if verifyReflow is enabled, and if so store off the space manager pointer
1073 #ifdef DEBUG
1074 PRInt32 verifyReflowFlags = nsIPresShell::GetVerifyReflowFlags();
1075 if (VERIFY_REFLOW_INCLUDE_SPACE_MANAGER & verifyReflowFlags)
1077 // this is a leak of the space manager, but it's only in debug if verify reflow is enabled, so not a big deal
1078 nsIPresShell *shell = aPresContext->GetPresShell();
1079 if (shell) {
1080 nsHTMLReflowState& reflowState = (nsHTMLReflowState&)aReflowState;
1081 rv = SetProperty(nsGkAtoms::spaceManagerProperty,
1082 reflowState.mSpaceManager,
1083 nsnull /* should be nsSpaceManagerDestroyer*/);
1085 autoSpaceManager.DebugOrphanSpaceManager();
1088 #endif
1090 // Let the absolutely positioned container reflow any absolutely positioned
1091 // child frames that need to be reflowed, e.g., elements with a percentage
1092 // based width/height
1093 // We want to do this under either of two conditions:
1094 // 1. If we didn't do the incremental reflow above.
1095 // 2. If our size changed.
1096 // Even though it's the padding edge that's the containing block, we
1097 // can use our rect (the border edge) since if the border style
1098 // changed, the reflow would have been targeted at us so we'd satisfy
1099 // condition 1.
1100 if (mAbsoluteContainer.HasAbsoluteFrames()) {
1101 nsRect childBounds;
1102 nsSize containingBlockSize
1103 = CalculateContainingBlockSizeForAbsolutes(aReflowState,
1104 nsSize(aMetrics.width, aMetrics.height));
1106 // Mark frames that depend on changes we just made to this frame as dirty:
1107 // Now we can assume that the padding edge hasn't moved.
1108 // We need to reflow the absolutes if one of them depends on
1109 // its placeholder position, or the containing block size in a
1110 // direction in which the containing block size might have
1111 // changed.
1112 PRBool cbWidthChanged = aMetrics.width != oldSize.width;
1113 PRBool isRoot = !GetContent()->GetParent();
1114 // If isRoot and we have auto height, then we are the initial
1115 // containing block and the containing block height is the
1116 // viewport height, which can't change during incremental
1117 // reflow.
1118 PRBool cbHeightChanged =
1119 !(isRoot && NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) &&
1120 aMetrics.height != oldSize.height;
1122 rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState,
1123 containingBlockSize.width,
1124 containingBlockSize.height,
1125 cbWidthChanged, cbHeightChanged,
1126 &childBounds);
1128 // Factor the absolutely positioned child bounds into the overflow area
1129 aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds);
1132 // Determine if we need to repaint our border, background or outline
1133 CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
1135 FinishAndStoreOverflow(&aMetrics);
1137 // Clear the space manager pointer in the block reflow state so we
1138 // don't waste time translating the coordinate system back on a dead
1139 // space manager.
1140 if (needSpaceManager)
1141 state.mSpaceManager = nsnull;
1143 aStatus = state.mReflowStatus;
1145 #ifdef DEBUG
1146 if (gNoisyReflow) {
1147 IndentBy(stdout, gNoiseIndent);
1148 ListTag(stdout);
1149 printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
1150 aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
1151 aMetrics.width, aMetrics.height,
1152 aMetrics.mCarriedOutBottomMargin.get());
1153 if (mState & NS_FRAME_OUTSIDE_CHILDREN) {
1154 printf(" combinedArea={%d,%d,%d,%d}",
1155 aMetrics.mOverflowArea.x,
1156 aMetrics.mOverflowArea.y,
1157 aMetrics.mOverflowArea.width,
1158 aMetrics.mOverflowArea.height);
1160 printf("\n");
1163 if (gLameReflowMetrics) {
1164 PRTime end = PR_Now();
1166 PRInt32 ectc = nsLineBox::GetCtorCount();
1167 PRInt32 numLines = mLines.size();
1168 if (!numLines) numLines = 1;
1169 PRTime delta, perLineDelta, lines;
1170 LL_I2L(lines, numLines);
1171 LL_SUB(delta, end, start);
1172 LL_DIV(perLineDelta, delta, lines);
1174 ListTag(stdout);
1175 char buf[400];
1176 PR_snprintf(buf, sizeof(buf),
1177 ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
1178 delta, perLineDelta, numLines, ectc - ctc);
1179 printf("%s\n", buf);
1181 #endif
1183 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
1184 return rv;
1187 PRBool
1188 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
1190 line_iterator begin = begin_lines();
1191 line_iterator line = end_lines();
1193 while (PR_TRUE) {
1194 if (begin == line) {
1195 return PR_FALSE;
1197 --line;
1198 if (line->mBounds.height != 0 || !line->CachedIsEmpty()) {
1199 return PR_FALSE;
1201 if (line->HasClearance()) {
1202 return PR_TRUE;
1205 // not reached
1208 void
1209 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
1210 nsBlockReflowState& aState,
1211 nsHTMLReflowMetrics& aMetrics)
1213 const nsMargin& borderPadding = aState.BorderPadding();
1214 #ifdef NOISY_FINAL_SIZE
1215 ListTag(stdout);
1216 printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
1217 aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
1218 aState.mPrevBottomMargin,
1219 borderPadding.top, borderPadding.bottom);
1220 #endif
1222 // Compute final width
1223 aMetrics.width = borderPadding.left + aReflowState.ComputedWidth() +
1224 borderPadding.right;
1226 // Return bottom margin information
1227 // rbs says he hit this assertion occasionally (see bug 86947), so
1228 // just set the margin to zero and we'll figure out why later
1229 //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
1230 // "someone else set the margin");
1231 nscoord nonCarriedOutVerticalMargin = 0;
1232 if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
1233 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1234 // line with clearance and a non-zero top margin and all
1235 // subsequent lines are empty, then we do not allow our children's
1236 // carried out bottom margin to be carried out of us and collapse
1237 // with our own bottom margin.
1238 if (CheckForCollapsedBottomMarginFromClearanceLine()) {
1239 // Convert the children's carried out margin to something that
1240 // we will include in our height
1241 nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
1242 aState.mPrevBottomMargin.Zero();
1244 aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
1245 } else {
1246 aMetrics.mCarriedOutBottomMargin.Zero();
1249 // Compute final height
1250 if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) {
1251 // Figure out how much of the computed height should be
1252 // applied to this frame.
1253 nscoord computedHeightLeftOver = aReflowState.mComputedHeight;
1254 if (GetPrevInFlow()) {
1255 // Reduce the height by the computed height of prev-in-flows.
1256 for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
1257 computedHeightLeftOver -= prev->GetRect().height;
1259 // We just subtracted our top-border padding, since it was included in the
1260 // first frame's height. Add it back to get the content height.
1261 computedHeightLeftOver += aReflowState.mComputedBorderPadding.top;
1262 // We may have stretched the frame beyond its computed height. Oh well.
1263 computedHeightLeftOver = PR_MAX(0, computedHeightLeftOver);
1266 if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1267 aMetrics.height = borderPadding.top + computedHeightLeftOver + borderPadding.bottom;
1268 if (computedHeightLeftOver > 0 &&
1269 aMetrics.height > aReflowState.availableHeight) {
1270 // We don't fit and we consumed some of the computed height,
1271 // so we should consume all the available height and then
1272 // break. If our bottom border/padding straddles the break
1273 // point, then this will increase our height and push the
1274 // border/padding to the next page/column.
1275 aMetrics.height = aReflowState.availableHeight;
1276 aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
1279 else {
1280 // Use the current height; continuations will take up the rest.
1281 // Do extend the height to at least consume the available
1282 // height, otherwise our left/right borders (for example) won't
1283 // extend all the way to the break.
1284 aMetrics.height = PR_MAX(aReflowState.availableHeight,
1285 aState.mY + nonCarriedOutVerticalMargin);
1286 // ... but don't take up more height than is available
1287 aMetrics.height = PR_MIN(aMetrics.height,
1288 borderPadding.top + computedHeightLeftOver);
1289 // XXX It's pretty wrong that our bottom border still gets drawn on
1290 // on its own on the last-in-flow, even if we ran out of height
1291 // here. We need GetSkipSides to check whether we ran out of content
1292 // height in the current frame, not whether it's last-in-flow.
1295 // Don't carry out a bottom margin when our height is fixed.
1296 aMetrics.mCarriedOutBottomMargin.Zero();
1298 else {
1299 nscoord autoHeight = aState.mY + nonCarriedOutVerticalMargin;
1301 // Shrink wrap our height around our contents.
1302 if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
1303 // When we are a bottom-margin root make sure that our last
1304 // childs bottom margin is fully applied.
1305 // Apply the margin only if there's space for it.
1306 if (autoHeight < aState.mReflowState.availableHeight)
1308 // Truncate bottom margin if it doesn't fit to our available height.
1309 autoHeight = PR_MIN(autoHeight + aState.mPrevBottomMargin.get(), aState.mReflowState.availableHeight);
1313 if (aState.GetFlag(BRS_SPACE_MGR)) {
1314 // Include the space manager's state to properly account for the
1315 // bottom margin of any floated elements; e.g., inside a table cell.
1316 nscoord ymost;
1317 if (aReflowState.mSpaceManager->YMost(ymost) &&
1318 autoHeight < ymost)
1319 autoHeight = ymost;
1322 // Apply min/max values
1323 autoHeight -= borderPadding.top;
1324 nscoord oldAutoHeight = autoHeight;
1325 aReflowState.ApplyMinMaxConstraints(nsnull, &autoHeight);
1326 if (autoHeight != oldAutoHeight) {
1327 // Our min-height or max-height made our height change. Don't carry out
1328 // our kids' bottom margins.
1329 aMetrics.mCarriedOutBottomMargin.Zero();
1331 autoHeight += borderPadding.top + borderPadding.bottom;
1332 aMetrics.height = autoHeight;
1335 #ifdef DEBUG_blocks
1336 if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
1337 ListTag(stdout);
1338 printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
1340 #endif
1343 void
1344 nsBlockFrame::ComputeCombinedArea(const nsHTMLReflowState& aReflowState,
1345 nsHTMLReflowMetrics& aMetrics)
1347 // Compute the combined area of our children
1348 // XXX_perf: This can be done incrementally. It is currently one of
1349 // the things that makes incremental reflow O(N^2).
1350 nsRect area(0, 0, aMetrics.width, aMetrics.height);
1351 if (NS_STYLE_OVERFLOW_CLIP != aReflowState.mStyleDisplay->mOverflowX) {
1352 for (line_iterator line = begin_lines(), line_end = end_lines();
1353 line != line_end;
1354 ++line) {
1355 area.UnionRect(area, line->GetCombinedArea());
1358 // Factor the bullet in; normally the bullet will be factored into
1359 // the line-box's combined area. However, if the line is a block
1360 // line then it won't; if there are no lines, it won't. So just
1361 // factor it in anyway (it can't hurt if it was already done).
1362 // XXXldb Can we just fix GetCombinedArea instead?
1363 if (mBullet) {
1364 area.UnionRect(area, mBullet->GetRect());
1367 #ifdef NOISY_COMBINED_AREA
1368 ListTag(stdout);
1369 printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height);
1370 #endif
1372 aMetrics.mOverflowArea = area;
1375 nsresult
1376 nsBlockFrame::MarkLineDirty(line_iterator aLine)
1378 // Mark aLine dirty
1379 aLine->MarkDirty();
1380 #ifdef DEBUG
1381 if (gNoisyReflow) {
1382 IndentBy(stdout, gNoiseIndent);
1383 ListTag(stdout);
1384 printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine.get()));
1386 #endif
1388 // Mark previous line dirty if it's an inline line so that it can
1389 // maybe pullup something from the line just affected.
1390 // XXX We don't need to do this if aPrevLine ends in a break-after...
1391 if (aLine != mLines.front() &&
1392 aLine->IsInline() &&
1393 aLine.prev()->IsInline()) {
1394 aLine.prev()->MarkDirty();
1395 #ifdef DEBUG
1396 if (gNoisyReflow) {
1397 IndentBy(stdout, gNoiseIndent);
1398 ListTag(stdout);
1399 printf(": mark prev-line %p dirty\n",
1400 NS_STATIC_CAST(void*, aLine.prev().get()));
1402 #endif
1405 return NS_OK;
1408 nsresult
1409 nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
1411 // we need to calculate if any part of then block itself
1412 // is impacted by a float (bug 19579)
1413 aState.GetAvailableSpace();
1415 const nsStyleText* styleText = GetStyleText();
1416 // See if we can try and avoid marking all the lines as dirty
1417 PRBool tryAndSkipLines =
1418 // There must be no floats.
1419 !aState.IsImpactedByFloat() &&
1420 // The text must be left-aligned.
1421 (NS_STYLE_TEXT_ALIGN_LEFT == styleText->mTextAlign ||
1422 (NS_STYLE_TEXT_ALIGN_DEFAULT == styleText->mTextAlign &&
1423 NS_STYLE_DIRECTION_LTR ==
1424 aState.mReflowState.mStyleVisibility->mDirection)) &&
1425 // The left content-edge must be a constant distance from the left
1426 // border-edge.
1427 GetStylePadding()->mPadding.GetLeftUnit() != eStyleUnit_Percent;
1429 #ifdef DEBUG
1430 if (gDisableResizeOpt) {
1431 tryAndSkipLines = PR_FALSE;
1433 if (gNoisyReflow) {
1434 if (!tryAndSkipLines) {
1435 IndentBy(stdout, gNoiseIndent);
1436 ListTag(stdout);
1437 printf(": marking all lines dirty: availWidth=%d textAlign=%d\n",
1438 aState.mReflowState.availableWidth,
1439 styleText->mTextAlign);
1442 #endif
1444 if (tryAndSkipLines) {
1445 nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left +
1446 aState.mReflowState.ComputedWidth();
1447 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedBorderPadding.left &&
1448 NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(),
1449 "math on NS_UNCONSTRAINEDSIZE");
1451 #ifdef DEBUG
1452 if (gNoisyReflow) {
1453 IndentBy(stdout, gNoiseIndent);
1454 ListTag(stdout);
1455 printf(": trying to avoid marking all lines dirty\n");
1457 #endif
1459 for (line_iterator line = begin_lines(), line_end = end_lines();
1460 line != line_end;
1461 ++line)
1463 // We let child blocks make their own decisions the same
1464 // way we are here.
1465 if (line->IsBlock() ||
1466 line->HasFloats() ||
1467 (line != mLines.back() && !line->HasBreakAfter()) ||
1468 line->ResizeReflowOptimizationDisabled() ||
1469 line->IsImpactedByFloat() ||
1470 (line->mBounds.XMost() > newAvailWidth)) {
1471 line->MarkDirty();
1474 #ifdef REALLY_NOISY_REFLOW
1475 if (!line->IsBlock()) {
1476 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
1477 line.get(), line->IsImpactedByFloat() ? "" : "not ");
1479 #endif
1480 #ifdef DEBUG
1481 if (gNoisyReflow && !line->IsDirty()) {
1482 IndentBy(stdout, gNoiseIndent + 1);
1483 printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
1484 NS_STATIC_CAST(void*, line.get()),
1485 NS_STATIC_CAST(void*, (line.next() != end_lines() ? line.next().get() : nsnull)),
1486 line->IsBlock() ? "block" : "inline",
1487 line->HasBreakAfter() ? "has-break-after " : "",
1488 line->HasFloats() ? "has-floats " : "",
1489 line->IsImpactedByFloat() ? "impacted " : "",
1490 line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
1491 line->mBounds.XMost());
1493 #endif
1496 else {
1497 // Mark everything dirty
1498 for (line_iterator line = begin_lines(), line_end = end_lines();
1499 line != line_end;
1500 ++line)
1502 line->MarkDirty();
1505 return NS_OK;
1508 //----------------------------------------
1510 nsBlockFrame::line_iterator
1511 nsBlockFrame::FindLineFor(nsIFrame* aFrame)
1513 NS_PRECONDITION(aFrame, "why pass a null frame?");
1514 line_iterator line = begin_lines(),
1515 line_end = end_lines();
1516 for ( ; line != line_end; ++line) {
1517 // If the target frame is in-flow, and this line contains the it,
1518 // then we've found our line.
1519 if (line->Contains(aFrame))
1520 return line;
1522 // If the target frame is floated, and this line contains the
1523 // float's placeholder, then we've found our line.
1524 if (line->HasFloats()) {
1525 for (nsFloatCache *fc = line->GetFirstFloat();
1526 fc != nsnull;
1527 fc = fc->Next()) {
1528 if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame())
1529 return line;
1534 return line_end;
1538 * Propagate reflow "damage" from from earlier lines to the current
1539 * line. The reflow damage comes from the following sources:
1540 * 1. The regions of float damage remembered during reflow.
1541 * 2. The combination of nonzero |aDeltaY| and any impact by a float,
1542 * either the previous reflow or now.
1544 * When entering this function, |aLine| is still at its old position and
1545 * |aDeltaY| indicates how much it will later be slid (assuming it
1546 * doesn't get marked dirty and reflowed entirely).
1548 void
1549 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
1550 nsLineBox* aLine,
1551 nscoord aDeltaY)
1553 NS_PRECONDITION(!aLine->IsDirty(), "should never be called on dirty lines");
1555 // Check the damage region recorded in the float damage.
1556 nsSpaceManager *spaceManager = aState.mReflowState.mSpaceManager;
1557 if (spaceManager->HasFloatDamage()) {
1558 nscoord lineYA = aLine->mBounds.y + aDeltaY;
1559 nscoord lineYB = lineYA + aLine->mBounds.height;
1560 if (spaceManager->IntersectsDamage(lineYA, lineYB)) {
1561 aLine->MarkDirty();
1562 return;
1566 if (aDeltaY) {
1567 // Cases we need to find:
1569 // 1. the line was impacted by a float and now isn't
1570 // 2. the line wasn't impacted by a float and now is
1571 // 3. the line is impacted by a float both before and after and
1572 // the float has changed position relative to the line (or it's
1573 // a different float). (XXXPerf we don't currently
1574 // check whether the float changed size. We currently just
1575 // mark blocks dirty and ignore any possibility of damage to
1576 // inlines by it being a different float with a different
1577 // size.)
1579 // XXXPerf: An optimization: if the line was and is completely
1580 // impacted by a float and the float hasn't changed size,
1581 // then we don't need to mark the line dirty.
1582 aState.GetAvailableSpace(aLine->mBounds.y + aDeltaY, PR_FALSE);
1583 PRBool wasImpactedByFloat = aLine->IsImpactedByFloat();
1584 PRBool isImpactedByFloat = aState.IsImpactedByFloat();
1585 #ifdef REALLY_NOISY_REFLOW
1586 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
1587 this, wasImpactedByFloat, isImpactedByFloat);
1588 #endif
1589 // Mark the line dirty if:
1590 // 1. It used to be impacted by a float and now isn't, or vice
1591 // versa.
1592 // 2. It is impacted by a float and it is a block, which means
1593 // that more or less of the line could be impacted than was in
1594 // the past. (XXXPerf This could be optimized further, since
1595 // we're marking the whole line dirty.)
1596 if ((wasImpactedByFloat != isImpactedByFloat) ||
1597 (isImpactedByFloat && aLine->IsBlock())) {
1598 aLine->MarkDirty();
1603 static void PlaceFrameView(nsIFrame* aFrame);
1605 static PRBool LineHasClear(nsLineBox* aLine) {
1606 return aLine->GetBreakTypeBefore() || aLine->HasFloatBreakAfter()
1607 || (aLine->IsBlock() && (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN));
1612 * Reparent a whole list of floats from aOldParent to this block. The
1613 * floats might be taken from aOldParent's overflow list. They will be
1614 * removed from the list. They end up appended to our mFloats list.
1616 void
1617 nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
1618 nsBlockFrame* aOldParent, PRBool aFromOverflow,
1619 PRBool aReparentSiblings) {
1620 nsFrameList list;
1621 nsIFrame* tail = nsnull;
1622 aOldParent->CollectFloats(aFirstFrame, list, &tail, aFromOverflow, aReparentSiblings);
1623 if (list.NotEmpty()) {
1624 for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) {
1625 ReparentFrame(f, aOldParent, this);
1627 mFloats.AppendFrames(nsnull, list.FirstChild());
1631 static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine,
1632 nscoord aDeltaY, PRInt32 aDeltaIndent) {
1633 #ifdef DEBUG
1634 if (nsBlockFrame::gNoisyReflow) {
1635 nsRect lca(aLine->GetCombinedArea());
1636 nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
1637 printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
1638 NS_STATIC_CAST(void*, aLine), aState.mY,
1639 aLine->IsDirty() ? "yes" : "no",
1640 aLine->mBounds.x, aLine->mBounds.y,
1641 aLine->mBounds.width, aLine->mBounds.height,
1642 lca.x, lca.y, lca.width, lca.height,
1643 aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount());
1645 #endif
1649 * Reflow the dirty lines
1651 nsresult
1652 nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
1654 nsresult rv = NS_OK;
1655 PRBool keepGoing = PR_TRUE;
1656 PRBool repositionViews = PR_FALSE; // should we really need this?
1657 PRBool foundAnyClears = PR_FALSE;
1659 #ifdef DEBUG
1660 if (gNoisyReflow) {
1661 IndentBy(stdout, gNoiseIndent);
1662 ListTag(stdout);
1663 printf(": reflowing dirty lines");
1664 printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth());
1666 AutoNoisyIndenter indent(gNoisyReflow);
1667 #endif
1669 PRBool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
1670 (aState.mReflowState.mFlags.mVResize &&
1671 (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT));
1673 // the amount by which we will slide the current line if it is not
1674 // dirty
1675 nscoord deltaY = 0;
1677 // whether we did NOT reflow the previous line and thus we need to
1678 // recompute the carried out margin before the line if we want to
1679 // reflow it or if its previous margin is dirty
1680 PRBool needToRecoverState = PR_FALSE;
1681 PRBool reflowedFloat = PR_FALSE;
1682 PRBool lastLineMovedUp = PR_FALSE;
1683 // We save up information about BR-clearance here
1684 PRUint8 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
1686 line_iterator line = begin_lines(), line_end = end_lines();
1688 // Reflow the lines that are already ours
1689 for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
1690 DumpLine(aState, line, deltaY, 0);
1691 #ifdef DEBUG
1692 AutoNoisyIndenter indent2(gNoisyReflow);
1693 #endif
1695 if (selfDirty)
1696 line->MarkDirty();
1698 // This really sucks, but we have to look inside any blocks that have clear
1699 // elements inside them.
1700 // XXX what can we do smarter here?
1701 if (!line->IsDirty() && line->IsBlock() &&
1702 (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
1703 line->MarkDirty();
1706 // We have to reflow the line if it's a block whose clearance
1707 // might have changed, so detect that.
1708 if (!line->IsDirty() && line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE) {
1709 nscoord curY = aState.mY;
1710 // See where we would be after applying any clearance due to
1711 // BRs.
1712 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1713 curY = aState.ClearFloats(curY, inlineFloatBreakType);
1716 nscoord newY = aState.ClearFloats(curY, line->GetBreakTypeBefore());
1718 if (line->HasClearance()) {
1719 // Reflow the line if it might not have clearance anymore.
1720 if (newY == curY
1721 // aState.mY is the clearance point which should be the
1722 // top border-edge of the block frame. If sliding the
1723 // block by deltaY isn't going to put it in the predicted
1724 // position, then we'd better reflow the line.
1725 || newY != line->mBounds.y + deltaY) {
1726 line->MarkDirty();
1728 } else {
1729 // Reflow the line if the line might have clearance now.
1730 if (curY != newY) {
1731 line->MarkDirty();
1736 // We might have to reflow a line that is after a clearing BR.
1737 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1738 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
1739 if (aState.mY != line->mBounds.y + deltaY) {
1740 // SlideLine is not going to put the line where the clearance
1741 // put it. Reflow the line to be sure.
1742 line->MarkDirty();
1744 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
1747 PRBool previousMarginWasDirty = line->IsPreviousMarginDirty();
1748 if (previousMarginWasDirty) {
1749 // If the previous margin is dirty, reflow the current line
1750 line->MarkDirty();
1751 line->ClearPreviousMarginDirty();
1752 } else if (line->mBounds.YMost() + deltaY > aState.mBottomEdge) {
1753 // Lines that aren't dirty but get slid past our height constraint must
1754 // be reflowed.
1755 line->MarkDirty();
1758 if (!line->IsDirty()) {
1759 // See if there's any reflow damage that requires that we mark the
1760 // line dirty.
1761 PropagateFloatDamage(aState, line, deltaY);
1764 if (needToRecoverState && line->IsDirty()) {
1765 // We need to reconstruct the bottom margin only if we didn't
1766 // reflow the previous line and we do need to reflow (or repair
1767 // the top position of) the next line.
1768 aState.ReconstructMarginAbove(line);
1771 if (needToRecoverState) {
1772 needToRecoverState = PR_FALSE;
1774 // Update aState.mPrevChild as if we had reflowed all of the frames in
1775 // this line. This is expensive in some cases, since it requires
1776 // walking |GetNextSibling|.
1777 if (line->IsDirty())
1778 aState.mPrevChild = line.prev()->LastChild();
1781 // Now repair the line and update |aState.mY| by calling
1782 // |ReflowLine| or |SlideLine|.
1783 if (line->IsDirty()) {
1784 lastLineMovedUp = PR_TRUE;
1786 PRBool maybeReflowingForFirstTime =
1787 line->mBounds.x == 0 && line->mBounds.y == 0 &&
1788 line->mBounds.width == 0 && line->mBounds.height == 0;
1790 // Compute the dirty lines "before" YMost, after factoring in
1791 // the running deltaY value - the running value is implicit in
1792 // aState.mY.
1793 nscoord oldY = line->mBounds.y;
1794 nscoord oldYMost = line->mBounds.YMost();
1796 // Reflow the dirty line. If it's an incremental reflow, then force
1797 // it to invalidate the dirty area if necessary
1798 rv = ReflowLine(aState, line, &keepGoing);
1799 NS_ENSURE_SUCCESS(rv, rv);
1801 if (line->HasFloats()) {
1802 reflowedFloat = PR_TRUE;
1805 if (!keepGoing) {
1806 DumpLine(aState, line, deltaY, -1);
1807 if (0 == line->GetChildCount()) {
1808 DeleteLine(aState, line, line_end);
1810 break;
1813 // Test to see whether the margin that should be carried out
1814 // to the next line (NL) might have changed. In ReflowBlockFrame
1815 // we call nextLine->MarkPreviousMarginDirty if the block's
1816 // actual carried-out bottom margin changed. So here we only
1817 // need to worry about the following effects:
1818 // 1) the line was just created, and it might now be blocking
1819 // a carried-out bottom margin from previous lines that
1820 // used to reach NL from reaching NL
1821 // 2) the line used to be empty, and is now not empty,
1822 // thus blocking a carried-out bottom margin from previous lines
1823 // that used to reach NL from reaching NL
1824 // 3) the line wasn't empty, but now is, so a carried-out
1825 // bottom margin from previous lines that didn't used to reach NL
1826 // now does
1827 // 4) the line might have changed in a way that affects NL's
1828 // ShouldApplyTopMargin decision. The three things that matter
1829 // are the line's emptiness, its adjacency to the top of the block,
1830 // and whether it has clearance (the latter only matters if the block
1831 // was and is adjacent to the top and empty).
1833 // If the line is empty now, we can't reliably tell if the line was empty
1834 // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
1835 // This means the checks in 4) are redundant; if the line is empty now
1836 // we don't need to check 4), but if the line is not empty now and we're sure
1837 // it wasn't empty before, any adjacency and clearance changes are irrelevant
1838 // to the result of nextLine->ShouldApplyTopMargin.
1839 if (line.next() != end_lines()) {
1840 PRBool maybeWasEmpty = oldY == oldYMost;
1841 PRBool isEmpty = line->mBounds.height == 0 && line->CachedIsEmpty();
1842 if (maybeReflowingForFirstTime /*1*/ ||
1843 (isEmpty || maybeWasEmpty) /*2/3/4*/) {
1844 line.next()->MarkPreviousMarginDirty();
1845 // since it's marked dirty, nobody will care about |deltaY|
1849 // If the line was just reflowed for the first time, then its
1850 // old mBounds cannot be trusted so this deltaY computation is
1851 // bogus. But that's OK because we just did
1852 // MarkPreviousMarginDirty on the next line which will force it
1853 // to be reflowed, so this computation of deltaY will not be
1854 // used.
1855 deltaY = line->mBounds.YMost() - oldYMost;
1856 } else {
1857 lastLineMovedUp = deltaY < 0;
1859 if (deltaY != 0)
1860 SlideLine(aState, line, deltaY);
1861 else
1862 repositionViews = PR_TRUE;
1864 // XXX EVIL O(N^2) EVIL
1865 aState.RecoverStateFrom(line, deltaY);
1867 // Keep mY up to date in case we're propagating reflow damage
1868 // and also because our final height may depend on it. If the
1869 // line is inlines, then only update mY if the line is not
1870 // empty, because that's what PlaceLine does. (Empty blocks may
1871 // want to update mY, e.g. if they have clearance.)
1872 if (line->IsBlock() || !line->CachedIsEmpty()) {
1873 aState.mY = line->mBounds.YMost();
1876 needToRecoverState = PR_TRUE;
1879 // Record if we need to clear floats before reflowing the next
1880 // line. Note that inlineFloatBreakType will be handled and
1881 // cleared before the next line is processed, so there is no
1882 // need to combine break types here.
1883 if (line->HasFloatBreakAfter()) {
1884 inlineFloatBreakType = line->GetBreakTypeAfter();
1887 if (LineHasClear(line.get())) {
1888 foundAnyClears = PR_TRUE;
1891 DumpLine(aState, line, deltaY, -1);
1894 // Handle BR-clearance from the last line of the block
1895 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1896 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
1899 if (needToRecoverState) {
1900 // Is this expensive?
1901 aState.ReconstructMarginAbove(line);
1903 // Update aState.mPrevChild as if we had reflowed all of the frames in
1904 // the last line. This is expensive in some cases, since it requires
1905 // walking |GetNextSibling|.
1906 aState.mPrevChild = line.prev()->LastChild();
1909 // Should we really have to do this?
1910 if (repositionViews)
1911 ::PlaceFrameView(this);
1913 // We can skip trying to pull up the next line if there is no next
1914 // in flow or we were told not to or we know it will be futile, i.e.,
1915 // -- the next in flow is not changing
1916 // -- and we cannot have added more space for its first line to be
1917 // pulled up into,
1918 // -- it's an incremental reflow of a descendant
1919 // -- and we didn't reflow any floats (so the available space
1920 // didn't change)
1921 // XXXldb We should also check that the first line of the next-in-flow
1922 // isn't dirty.
1923 if (!aState.mNextInFlow ||
1924 (aState.mReflowState.mFlags.mNextInFlowUntouched &&
1925 !lastLineMovedUp &&
1926 !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
1927 !reflowedFloat)) {
1928 if (aState.mNextInFlow) {
1929 aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
1931 } else {
1932 // Pull data from a next-in-flow if there's still room for more
1933 // content here.
1934 while (keepGoing && (nsnull != aState.mNextInFlow)) {
1935 // Grab first line from our next-in-flow
1936 nsBlockFrame* nextInFlow = aState.mNextInFlow;
1937 line_iterator nifLine = nextInFlow->begin_lines();
1938 nsLineBox *toMove;
1939 PRBool collectOverflowFloats;
1940 if (nifLine != nextInFlow->end_lines()) {
1941 if (HandleOverflowPlaceholdersOnPulledLine(aState, nifLine)) {
1942 // go around again in case the line was deleted
1943 continue;
1945 toMove = nifLine;
1946 nextInFlow->mLines.erase(nifLine);
1947 collectOverflowFloats = PR_FALSE;
1948 } else {
1949 // Grab an overflow line if there are any
1950 nsLineList* overflowLines = nextInFlow->GetOverflowLines();
1951 if (overflowLines &&
1952 HandleOverflowPlaceholdersOnPulledLine(aState, overflowLines->front())) {
1953 // go around again in case the line was deleted
1954 continue;
1956 if (!overflowLines) {
1957 aState.mNextInFlow =
1958 NS_STATIC_CAST(nsBlockFrame*, nextInFlow->GetNextInFlow());
1959 continue;
1961 nifLine = overflowLines->begin();
1962 NS_ASSERTION(nifLine != overflowLines->end(),
1963 "Stored overflow line list should not be empty");
1964 toMove = nifLine;
1965 nextInFlow->RemoveOverflowLines();
1966 nifLine = overflowLines->erase(nifLine);
1967 if (nifLine != overflowLines->end()) {
1968 // We need to this remove-and-put-back dance because we want
1969 // to avoid making the overflow line list empty while it's
1970 // stored in the property (because the property has the
1971 // invariant that the list is never empty).
1972 nextInFlow->SetOverflowLines(overflowLines);
1974 collectOverflowFloats = PR_TRUE;
1977 if (0 == toMove->GetChildCount()) {
1978 // The line is empty. Try the next one.
1979 NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
1980 aState.FreeLineBox(toMove);
1981 continue;
1984 // XXX move to a subroutine: run-in, overflow, pullframe and this do this
1985 // Make the children in the line ours.
1986 nsIFrame* frame = toMove->mFirstChild;
1987 nsIFrame* lastFrame = nsnull;
1988 PRInt32 n = toMove->GetChildCount();
1989 while (--n >= 0) {
1990 ReparentFrame(frame, nextInFlow, this);
1991 lastFrame = frame;
1992 frame = frame->GetNextSibling();
1994 lastFrame->SetNextSibling(nsnull);
1996 // Reparent floats whose placeholders are in the line.
1997 ReparentFloats(toMove->mFirstChild, nextInFlow, collectOverflowFloats, PR_TRUE);
1999 // Add line to our line list
2000 if (aState.mPrevChild) {
2001 aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
2004 line = mLines.before_insert(end_lines(), toMove);
2006 DumpLine(aState, toMove, deltaY, 0);
2007 #ifdef DEBUG
2008 AutoNoisyIndenter indent2(gNoisyReflow);
2009 #endif
2011 // Now reflow it and any lines that it makes during it's reflow
2012 // (we have to loop here because reflowing the line may case a new
2013 // line to be created; see SplitLine's callers for examples of
2014 // when this happens).
2015 while (line != end_lines()) {
2016 rv = ReflowLine(aState, line, &keepGoing);
2017 NS_ENSURE_SUCCESS(rv, rv);
2018 DumpLine(aState, line, deltaY, -1);
2019 if (!keepGoing) {
2020 if (0 == line->GetChildCount()) {
2021 DeleteLine(aState, line, line_end);
2023 break;
2026 if (LineHasClear(line.get())) {
2027 foundAnyClears = PR_TRUE;
2030 // If this is an inline frame then its time to stop
2031 ++line;
2032 aState.AdvanceToNextLine();
2036 if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
2037 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
2041 // Handle an odd-ball case: a list-item with no lines
2042 if (mBullet && HaveOutsideBullet() && mLines.empty()) {
2043 nsHTMLReflowMetrics metrics;
2044 ReflowBullet(aState, metrics);
2046 // There are no lines so we have to fake up some y motion so that
2047 // we end up with *some* height.
2048 aState.mY += metrics.height;
2051 if (foundAnyClears) {
2052 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2053 } else {
2054 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2057 #ifdef DEBUG
2058 if (gNoisyReflow) {
2059 IndentBy(stdout, gNoiseIndent - 1);
2060 ListTag(stdout);
2061 printf(": done reflowing dirty lines (status=%x)\n",
2062 aState.mReflowStatus);
2064 #endif
2066 return rv;
2069 void
2070 nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
2071 nsLineList::iterator aLine,
2072 nsLineList::iterator aLineEnd)
2074 NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
2075 if (0 == aLine->GetChildCount()) {
2076 NS_ASSERTION(aState.mCurrentLine == aLine,
2077 "using function more generally than designed, "
2078 "but perhaps OK now");
2079 nsLineBox *line = aLine;
2080 aLine = mLines.erase(aLine);
2081 aState.FreeLineBox(line);
2082 // Mark the previous margin of the next line dirty since we need to
2083 // recompute its top position.
2084 if (aLine != aLineEnd)
2085 aLine->MarkPreviousMarginDirty();
2090 * Takes two rectangles whose origins must be the same, and computes
2091 * the difference between their union and their intersection as two
2092 * rectangles. (This difference is a superset of the difference
2093 * between the two rectangles.)
2095 static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
2096 nsRect* aHStrip, nsRect* aVStrip) {
2097 NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
2098 "expected rects at the same position");
2099 nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width),
2100 PR_MAX(aR1.height, aR2.height));
2101 nscoord VStripStart = PR_MIN(aR1.width, aR2.width);
2102 nscoord HStripStart = PR_MIN(aR1.height, aR2.height);
2103 *aVStrip = unionRect;
2104 aVStrip->x += VStripStart;
2105 aVStrip->width -= VStripStart;
2106 *aHStrip = unionRect;
2107 aHStrip->y += HStripStart;
2108 aHStrip->height -= HStripStart;
2112 * Reflow a line. The line will either contain a single block frame
2113 * or contain 1 or more inline frames. aKeepReflowGoing indicates
2114 * whether or not the caller should continue to reflow more lines.
2116 nsresult
2117 nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
2118 line_iterator aLine,
2119 PRBool* aKeepReflowGoing)
2121 nsresult rv = NS_OK;
2123 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
2125 // Setup the line-layout for the new line
2126 aState.mCurrentLine = aLine;
2127 aLine->ClearDirty();
2128 aLine->InvalidateCachedIsEmpty();
2130 // Now that we know what kind of line we have, reflow it
2131 if (aLine->IsBlock()) {
2132 nsRect oldBounds = aLine->mFirstChild->GetRect();
2133 nsRect oldCombinedArea(aLine->GetCombinedArea());
2134 rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
2135 nsRect newBounds = aLine->mFirstChild->GetRect();
2137 // We expect blocks to damage any area inside their bounds that is
2138 // dirty; however, if the frame changes size or position then we
2139 // need to do some repainting.
2140 // XXX roc --- the above statement is ambiguous about whether 'bounds'
2141 // means the frame's bounds or overflowArea, and in fact this is a source
2142 // of much confusion and bugs. Thus the following hack considers *both*
2143 // overflowArea and bounds. This should be considered a temporary hack
2144 // until we decide how it's really supposed to work.
2145 nsRect lineCombinedArea(aLine->GetCombinedArea());
2146 if (oldCombinedArea.TopLeft() != lineCombinedArea.TopLeft() ||
2147 oldBounds.TopLeft() != newBounds.TopLeft()) {
2148 // The block has moved, and so to be safe we need to repaint
2149 // XXX We need to improve on this...
2150 nsRect dirtyRect;
2151 dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea);
2152 #ifdef NOISY_BLOCK_INVALIDATE
2153 printf("%p invalidate 6 (%d, %d, %d, %d)\n",
2154 this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2155 #endif
2156 Invalidate(dirtyRect);
2157 } else {
2158 nsRect combinedAreaHStrip, combinedAreaVStrip;
2159 nsRect boundsHStrip, boundsVStrip;
2160 GetRectDifferenceStrips(oldBounds, newBounds,
2161 &boundsHStrip, &boundsVStrip);
2162 GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea,
2163 &combinedAreaHStrip, &combinedAreaVStrip);
2165 #ifdef NOISY_BLOCK_INVALIDATE
2166 printf("%p invalidate boundsVStrip (%d, %d, %d, %d)\n",
2167 this, boundsVStrip.x, boundsVStrip.y, boundsVStrip.width, boundsVStrip.height);
2168 printf("%p invalidate boundsHStrip (%d, %d, %d, %d)\n",
2169 this, boundsHStrip.x, boundsHStrip.y, boundsHStrip.width, boundsHStrip.height);
2170 printf("%p invalidate combinedAreaVStrip (%d, %d, %d, %d)\n",
2171 this, combinedAreaVStrip.x, combinedAreaVStrip.y, combinedAreaVStrip.width, combinedAreaVStrip.height);
2172 printf("%p invalidate combinedAreaHStrip (%d, %d, %d, %d)\n",
2173 this, combinedAreaHStrip.x, combinedAreaHStrip.y, combinedAreaHStrip.width, combinedAreaHStrip.height);
2174 #endif
2175 // The first thing Invalidate does is check if the rect is empty, so
2176 // don't bother doing that here.
2177 Invalidate(boundsVStrip);
2178 Invalidate(boundsHStrip);
2179 Invalidate(combinedAreaVStrip);
2180 Invalidate(combinedAreaHStrip);
2183 else {
2184 nsRect oldCombinedArea(aLine->GetCombinedArea());
2185 aLine->SetLineWrapped(PR_FALSE);
2187 rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
2189 // We don't really know what changed in the line, so use the union
2190 // of the old and new combined areas
2191 nsRect dirtyRect;
2192 dirtyRect.UnionRect(oldCombinedArea, aLine->GetCombinedArea());
2193 #ifdef NOISY_BLOCK_INVALIDATE
2194 printf("%p invalidate (%d, %d, %d, %d)\n",
2195 this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2196 if (aLine->IsForceInvalidate())
2197 printf(" dirty line is %p\n", NS_STATIC_CAST(void*, aLine.get());
2198 #endif
2199 Invalidate(dirtyRect);
2202 return rv;
2206 * Pull frame from the next available location (one of our lines or
2207 * one of our next-in-flows lines).
2209 nsresult
2210 nsBlockFrame::PullFrame(nsBlockReflowState& aState,
2211 line_iterator aLine,
2212 nsIFrame*& aFrameResult)
2214 aFrameResult = nsnull;
2216 // First check our remaining lines
2217 if (end_lines() != aLine.next()) {
2218 #ifdef DEBUG
2219 PRBool retry =
2220 #endif
2221 PullFrameFrom(aState, aLine, this, PR_FALSE, aLine.next(), aFrameResult);
2222 NS_ASSERTION(!retry, "Shouldn't have to retry in the current block");
2223 return NS_OK;
2226 NS_ASSERTION(!GetOverflowLines(),
2227 "Our overflow lines should have been removed at the start of reflow");
2229 // Try each next in flows
2230 nsBlockFrame* nextInFlow = aState.mNextInFlow;
2231 while (nextInFlow) {
2232 // first normal lines, then overflow lines
2233 if (!nextInFlow->mLines.empty()) {
2234 if (PullFrameFrom(aState, aLine, nextInFlow, PR_FALSE,
2235 nextInFlow->mLines.begin(), aFrameResult)) {
2236 // try again with the same value of nextInFlow
2237 continue;
2239 break;
2242 nsLineList* overflowLines = nextInFlow->GetOverflowLines();
2243 if (overflowLines) {
2244 if (PullFrameFrom(aState, aLine, nextInFlow, PR_TRUE,
2245 overflowLines->begin(), aFrameResult)) {
2246 // try again with the same value of nextInFlow
2247 continue;
2249 break;
2252 nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
2253 aState.mNextInFlow = nextInFlow;
2256 return NS_OK;
2260 * Try to pull a frame out of a line pointed at by aFromLine. If
2261 * aUpdateGeometricParent is set then the pulled frames geometric parent
2262 * will be updated (e.g. when pulling from a next-in-flows line list).
2264 * Note: pulling a frame from a line that is a place-holder frame
2265 * doesn't automatically remove the corresponding float from the
2266 * line's float array. This happens indirectly: either the line gets
2267 * emptied (and destroyed) or the line gets reflowed (because we mark
2268 * it dirty) and the code at the top of ReflowLine empties the
2269 * array. So eventually, it will be removed, just not right away.
2271 * @return PR_TRUE to force retrying of the pull.
2273 PRBool
2274 nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
2275 nsLineBox* aLine,
2276 nsBlockFrame* aFromContainer,
2277 PRBool aFromOverflowLine,
2278 nsLineList::iterator aFromLine,
2279 nsIFrame*& aFrameResult)
2281 nsLineBox* fromLine = aFromLine;
2282 NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
2283 NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
2284 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
2286 NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->GetStyleDisplay()->IsBlockOutside(),
2287 "Disagreement about whether it's a block or not");
2289 if (fromLine->IsBlock()) {
2290 // If our line is not empty and the child in aFromLine is a block
2291 // then we cannot pull up the frame into this line. In this case
2292 // we stop pulling.
2293 aFrameResult = nsnull;
2295 else {
2296 // Take frame from fromLine
2297 nsIFrame* frame = fromLine->mFirstChild;
2299 if (aFromContainer != this) {
2300 if (HandleOverflowPlaceholdersForPulledFrame(aState, frame)) {
2301 // we lost this one, retry
2302 return PR_TRUE;
2305 aLine->LastChild()->SetNextSibling(frame);
2307 // when aFromContainer is 'this', then aLine->LastChild()'s next sibling
2308 // is already set correctly.
2309 aLine->SetChildCount(aLine->GetChildCount() + 1);
2311 PRInt32 fromLineChildCount = fromLine->GetChildCount();
2312 if (0 != --fromLineChildCount) {
2313 // Mark line dirty now that we pulled a child
2314 fromLine->SetChildCount(fromLineChildCount);
2315 fromLine->MarkDirty();
2316 fromLine->mFirstChild = frame->GetNextSibling();
2318 else {
2319 // Free up the fromLine now that it's empty
2320 // Its bounds might need to be redrawn, though.
2321 // XXX WHY do we invalidate the bounds AND the combined area? doesn't
2322 // the combined area always enclose the bounds?
2323 Invalidate(fromLine->mBounds);
2324 nsLineList* fromLineList = aFromOverflowLine
2325 ? aFromContainer->RemoveOverflowLines()
2326 : &aFromContainer->mLines;
2327 if (aFromLine.next() != fromLineList->end())
2328 aFromLine.next()->MarkPreviousMarginDirty();
2330 Invalidate(fromLine->GetCombinedArea());
2331 fromLineList->erase(aFromLine);
2332 // Note that aFromLine just got incremented, so don't use it again here!
2333 aState.FreeLineBox(fromLine);
2335 // Put any remaining overflow lines back.
2336 if (aFromOverflowLine && !fromLineList->empty()) {
2337 aFromContainer->SetOverflowLines(fromLineList);
2341 // Change geometric parents
2342 if (aFromContainer != this) {
2343 // When pushing and pulling frames we need to check for whether any
2344 // views need to be reparented
2345 NS_ASSERTION(frame->GetParent() == aFromContainer, "unexpected parent frame");
2347 ReparentFrame(frame, aFromContainer, this);
2349 // The frame is being pulled from a next-in-flow; therefore we
2350 // need to add it to our sibling list.
2351 frame->SetNextSibling(nsnull);
2352 if (nsnull != aState.mPrevChild) {
2353 aState.mPrevChild->SetNextSibling(frame);
2356 // The frame might have (or contain) floats that need to be
2357 // brought over too.
2358 ReparentFloats(frame, aFromContainer, aFromOverflowLine, PR_TRUE);
2361 // Stop pulling because we found a frame to pull
2362 aFrameResult = frame;
2363 #ifdef DEBUG
2364 VerifyLines(PR_TRUE);
2365 #endif
2367 return PR_FALSE;
2370 static void
2371 PlaceFrameView(nsIFrame* aFrame)
2373 if (aFrame->HasView())
2374 nsContainerFrame::PositionFrameView(aFrame);
2375 else
2376 nsContainerFrame::PositionChildViews(aFrame);
2379 void
2380 nsBlockFrame::SlideLine(nsBlockReflowState& aState,
2381 nsLineBox* aLine, nscoord aDY)
2383 NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
2385 Invalidate(aLine->GetCombinedArea());
2386 // Adjust line state
2387 aLine->SlideBy(aDY);
2388 Invalidate(aLine->GetCombinedArea());
2390 // Adjust the frames in the line
2391 nsIFrame* kid = aLine->mFirstChild;
2392 if (!kid) {
2393 return;
2396 if (aLine->IsBlock()) {
2397 if (aDY) {
2398 nsPoint p = kid->GetPosition();
2399 p.y += aDY;
2400 kid->SetPosition(p);
2403 // Make sure the frame's view and any child views are updated
2404 ::PlaceFrameView(kid);
2406 else {
2407 // Adjust the Y coordinate of the frames in the line.
2408 // Note: we need to re-position views even if aDY is 0, because
2409 // one of our parent frames may have moved and so the view's position
2410 // relative to its parent may have changed
2411 PRInt32 n = aLine->GetChildCount();
2412 while (--n >= 0) {
2413 if (aDY) {
2414 nsPoint p = kid->GetPosition();
2415 p.y += aDY;
2416 kid->SetPosition(p);
2418 // Make sure the frame's view and any child views are updated
2419 ::PlaceFrameView(kid);
2420 kid = kid->GetNextSibling();
2425 NS_IMETHODIMP
2426 nsBlockFrame::AttributeChanged(PRInt32 aNameSpaceID,
2427 nsIAtom* aAttribute,
2428 PRInt32 aModType)
2430 nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID,
2431 aAttribute, aModType);
2433 if (NS_FAILED(rv)) {
2434 return rv;
2436 if (nsGkAtoms::start == aAttribute) {
2437 nsPresContext* presContext = PresContext();
2439 // XXX Not sure if this is necessary anymore
2440 if (RenumberLists(presContext)) {
2441 presContext->PresShell()->
2442 FrameNeedsReflow(this, nsIPresShell::eStyleChange,
2443 NS_FRAME_HAS_DIRTY_CHILDREN);
2446 else if (nsGkAtoms::value == aAttribute) {
2447 const nsStyleDisplay* styleDisplay = GetStyleDisplay();
2448 if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
2449 // Search for the closest ancestor that's a block frame. We
2450 // make the assumption that all related list items share a
2451 // common block parent.
2452 // XXXldb I think that's a bad assumption.
2453 nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
2455 // Tell the enclosing block frame to renumber list items within
2456 // itself
2457 if (nsnull != blockParent) {
2458 nsPresContext* presContext = PresContext();
2459 // XXX Not sure if this is necessary anymore
2460 if (blockParent->RenumberLists(presContext)) {
2461 presContext->PresShell()->
2462 FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange,
2463 NS_FRAME_HAS_DIRTY_CHILDREN);
2469 return rv;
2472 inline PRBool
2473 IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
2475 return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
2476 (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
2479 inline PRBool
2480 IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
2482 return (aUnit == eStyleUnit_Auto ||
2483 (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
2484 (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
2487 /* virtual */ PRBool
2488 nsBlockFrame::IsSelfEmpty()
2490 const nsStylePosition* position = GetStylePosition();
2492 switch (position->mMinHeight.GetUnit()) {
2493 case eStyleUnit_Coord:
2494 if (position->mMinHeight.GetCoordValue() != 0)
2495 return PR_FALSE;
2496 break;
2497 case eStyleUnit_Percent:
2498 if (position->mMinHeight.GetPercentValue() != 0.0f)
2499 return PR_FALSE;
2500 break;
2501 default:
2502 return PR_FALSE;
2505 switch (position->mHeight.GetUnit()) {
2506 case eStyleUnit_Auto:
2507 break;
2508 case eStyleUnit_Coord:
2509 if (position->mHeight.GetCoordValue() != 0)
2510 return PR_FALSE;
2511 break;
2512 case eStyleUnit_Percent:
2513 if (position->mHeight.GetPercentValue() != 0.0f)
2514 return PR_FALSE;
2515 break;
2516 default:
2517 return PR_FALSE;
2520 const nsStyleBorder* border = GetStyleBorder();
2521 const nsStylePadding* padding = GetStylePadding();
2522 nsStyleCoord coord;
2523 if (border->GetBorderWidth(NS_SIDE_TOP) != 0 ||
2524 border->GetBorderWidth(NS_SIDE_BOTTOM) != 0 ||
2525 !IsPaddingZero(padding->mPadding.GetTopUnit(),
2526 padding->mPadding.GetTop(coord)) ||
2527 !IsPaddingZero(padding->mPadding.GetBottomUnit(),
2528 padding->mPadding.GetBottom(coord))) {
2529 return PR_FALSE;
2532 return PR_TRUE;
2535 PRBool
2536 nsBlockFrame::CachedIsEmpty()
2538 if (!IsSelfEmpty()) {
2539 return PR_FALSE;
2542 for (line_iterator line = begin_lines(), line_end = end_lines();
2543 line != line_end;
2544 ++line)
2546 if (!line->CachedIsEmpty())
2547 return PR_FALSE;
2550 return PR_TRUE;
2553 PRBool
2554 nsBlockFrame::IsEmpty()
2556 if (!IsSelfEmpty()) {
2557 return PR_FALSE;
2560 for (line_iterator line = begin_lines(), line_end = end_lines();
2561 line != line_end;
2562 ++line)
2564 if (!line->IsEmpty())
2565 return PR_FALSE;
2568 return PR_TRUE;
2571 PRBool
2572 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
2573 nsLineBox* aLine)
2575 if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
2576 // Apply short-circuit check to avoid searching the line list
2577 return PR_TRUE;
2580 if (!aState.IsAdjacentWithTop()) {
2581 // If we aren't at the top Y coordinate then something of non-zero
2582 // height must have been placed. Therefore the childs top-margin
2583 // applies.
2584 aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
2585 return PR_TRUE;
2588 // Determine if this line is "essentially" the first line
2589 line_iterator line = begin_lines();
2590 if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
2591 line = aState.mLineAdjacentToTop;
2593 while (line != aLine) {
2594 if (!line->CachedIsEmpty() || line->HasClearance()) {
2595 // A line which precedes aLine is non-empty, or has clearance,
2596 // so therefore the top margin applies.
2597 aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
2598 return PR_TRUE;
2600 // No need to apply the top margin if the line has floats. We
2601 // should collapse anyway (bug 44419)
2602 ++line;
2603 aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, PR_TRUE);
2604 aState.mLineAdjacentToTop = line;
2607 // The line being reflowed is "essentially" the first line in the
2608 // block. Therefore its top-margin will be collapsed by the
2609 // generational collapsing logic with its parent (us).
2610 return PR_FALSE;
2613 nsIFrame*
2614 nsBlockFrame::GetTopBlockChild(nsPresContext* aPresContext)
2616 if (mLines.empty())
2617 return nsnull;
2619 nsLineBox *firstLine = mLines.front();
2620 if (firstLine->IsBlock())
2621 return firstLine->mFirstChild;
2623 if (!firstLine->CachedIsEmpty())
2624 return nsnull;
2626 line_iterator secondLine = begin_lines();
2627 ++secondLine;
2628 if (secondLine == end_lines() || !secondLine->IsBlock())
2629 return nsnull;
2631 return secondLine->mFirstChild;
2634 // If placeholders/floats split during reflowing a line, but that line will
2635 // be put on the next page, then put the placeholders/floats back the way
2636 // they were before the line was reflowed.
2637 void
2638 nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState,
2639 nsIFrame* aLastPlaceholder)
2641 nsIFrame* undoPlaceholder;
2642 if (aLastPlaceholder) {
2643 undoPlaceholder = aLastPlaceholder->GetNextSibling();
2644 aLastPlaceholder->SetNextSibling(nsnull);
2646 else {
2647 undoPlaceholder = aState.mOverflowPlaceholders.FirstChild();
2648 aState.mOverflowPlaceholders.SetFrames(nsnull);
2650 // remove the next in flows of the placeholders that need to be removed
2651 for (nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, undoPlaceholder);
2652 placeholder; ) {
2653 NS_ASSERTION(!placeholder->GetNextInFlow(), "Must be the last placeholder");
2655 nsFrameManager* fm = aState.mPresContext->GetPresShell()->FrameManager();
2656 fm->UnregisterPlaceholderFrame(placeholder);
2657 placeholder->SetOutOfFlowFrame(nsnull);
2659 // XXX we probably should be doing something with oof here. But what?
2661 nsSplittableFrame::RemoveFromFlow(placeholder);
2662 nsIFrame* savePlaceholder = placeholder;
2663 placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, placeholder->GetNextSibling());
2664 savePlaceholder->Destroy();
2668 nsresult
2669 nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
2670 line_iterator aLine,
2671 PRBool* aKeepReflowGoing)
2673 NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
2675 nsresult rv = NS_OK;
2677 nsIFrame* frame = aLine->mFirstChild;
2678 if (!frame) {
2679 NS_ASSERTION(PR_FALSE, "program error - unexpected empty line");
2680 return NS_ERROR_NULL_POINTER;
2683 // Prepare the block reflow engine
2684 const nsStyleDisplay* display = frame->GetStyleDisplay();
2685 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
2687 PRUint8 breakType = display->mBreakType;
2688 // If a float split and its prev-in-flow was followed by a <BR>, then combine
2689 // the <BR>'s break type with the block's break type (the block will be the very
2690 // next frame after the split float).
2691 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
2692 breakType = nsLayoutUtils::CombineBreakType(breakType,
2693 aState.mFloatBreakType);
2694 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
2697 // Clear past floats before the block if the clear style is not none
2698 aLine->SetBreakTypeBefore(breakType);
2700 // See if we should apply the top margin. If the block frame being
2701 // reflowed is a continuation (non-null prev-in-flow) then we don't
2702 // apply its top margin because it's not significant. Otherwise, dig
2703 // deeper.
2704 PRBool applyTopMargin =
2705 !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
2707 if (applyTopMargin) {
2708 // The HasClearance setting is only valid if ShouldApplyTopMargin
2709 // returned PR_FALSE (in which case the top-margin-root set our
2710 // clearance flag). Otherwise clear it now. We'll set it later on
2711 // ourselves if necessary.
2712 aLine->ClearHasClearance();
2714 PRBool treatWithClearance = aLine->HasClearance();
2715 // If our top margin was counted as part of some parents top-margin
2716 // collapse and we are being speculatively reflowed assuming this
2717 // frame DID NOT need clearance, then we need to check that
2718 // assumption.
2719 if (!treatWithClearance && !applyTopMargin && breakType != NS_STYLE_CLEAR_NONE &&
2720 aState.mReflowState.mDiscoveredClearance) {
2721 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
2722 nscoord clearY = aState.ClearFloats(curY, breakType);
2723 if (clearY != curY) {
2724 // Looks like that assumption was invalid, we do need
2725 // clearance. Tell our ancestor so it can reflow again. It is
2726 // responsible for actually setting our clearance flag before
2727 // the next reflow.
2728 treatWithClearance = PR_TRUE;
2729 // Only record the first frame that requires clearance
2730 if (!*aState.mReflowState.mDiscoveredClearance) {
2731 *aState.mReflowState.mDiscoveredClearance = frame;
2733 // Exactly what we do now is flexible since we'll definitely be
2734 // reflowed.
2737 if (treatWithClearance) {
2738 applyTopMargin = PR_TRUE;
2741 nsIFrame* clearanceFrame = nsnull;
2742 nscoord startingY = aState.mY;
2743 nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
2744 nscoord clearance;
2745 // Save the original position of the frame so that we can reposition
2746 // its view as needed.
2747 nsPoint originalPosition = frame->GetPosition();
2748 while (PR_TRUE) {
2749 // Save the frame's current position. We might need it later.
2750 nscoord passOriginalY = frame->GetRect().y;
2752 clearance = 0;
2753 nscoord topMargin = 0;
2754 PRBool mayNeedRetry = PR_FALSE;
2755 if (applyTopMargin) {
2756 // Precompute the blocks top margin value so that we can get the
2757 // correct available space (there might be a float that's
2758 // already been placed below the aState.mPrevBottomMargin
2760 // Setup a reflowState to get the style computed margin-top
2761 // value. We'll use a reason of `resize' so that we don't fudge
2762 // any incremental reflow state.
2764 // The availSpace here is irrelevant to our needs - all we want
2765 // out if this setup is the margin-top value which doesn't depend
2766 // on the childs available space.
2767 // XXX building a complete nsHTMLReflowState just to get the margin-top
2768 // seems like a waste. And we do this for almost every block!
2769 nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
2770 nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
2771 frame, availSpace);
2773 if (treatWithClearance) {
2774 aState.mY += aState.mPrevBottomMargin.get();
2775 aState.mPrevBottomMargin.Zero();
2778 // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
2779 // that all child margins collapse down to clearanceFrame.
2780 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
2781 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
2783 // XXX optimization; we could check the collapsing children to see if they are sure
2784 // to require clearance, and so avoid retrying them
2786 if (clearanceFrame) {
2787 // Don't allow retries on the second pass. The clearance decisions for the
2788 // blocks whose top-margins collapse with ours are now fixed.
2789 mayNeedRetry = PR_FALSE;
2792 if (!treatWithClearance && !clearanceFrame && breakType != NS_STYLE_CLEAR_NONE) {
2793 // We don't know if we need clearance and this is the first,
2794 // optimistic pass. So determine whether *this block* needs
2795 // clearance. Note that we do not allow the decision for whether
2796 // this block has clearance to change on the second pass; that
2797 // decision is only allowed to be made under the optimistic
2798 // first pass.
2799 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
2800 nscoord clearY = aState.ClearFloats(curY, breakType);
2801 if (clearY != curY) {
2802 // Looks like we need clearance and we didn't know about it already. So
2803 // recompute collapsed margin
2804 treatWithClearance = PR_TRUE;
2805 // Remember this decision, needed for incremental reflow
2806 aLine->SetHasClearance();
2808 // Apply incoming margins
2809 aState.mY += aState.mPrevBottomMargin.get();
2810 aState.mPrevBottomMargin.Zero();
2812 // Compute the collapsed margin again, ignoring the incoming margin this time
2813 mayNeedRetry = PR_FALSE;
2814 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
2815 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
2819 // Temporarily advance the running Y value so that the
2820 // GetAvailableSpace method will return the right available
2821 // space. This undone as soon as the horizontal margins are
2822 // computed.
2823 topMargin = aState.mPrevBottomMargin.get();
2825 if (treatWithClearance) {
2826 nscoord currentY = aState.mY;
2827 // advance mY to the clear position.
2828 aState.mY = aState.ClearFloats(aState.mY, breakType);
2830 // Compute clearance. It's the amount we need to add to the top
2831 // border-edge of the frame, after applying collapsed margins
2832 // from the frame and its children, to get it to line up with
2833 // the bottom of the floats. The former is currentY + topMargin,
2834 // the latter is the current aState.mY.
2835 // Note that negative clearance is possible
2836 clearance = aState.mY - (currentY + topMargin);
2838 // Add clearance to our top margin while we compute available
2839 // space for the frame
2840 topMargin += clearance;
2842 // Note that aState.mY should stay where it is: at the top
2843 // border-edge of the frame
2844 } else {
2845 // Advance aState.mY to the top border-edge of the frame.
2846 aState.mY += topMargin;
2850 // Here aState.mY is the top border-edge of the block.
2851 // Compute the available space for the block
2852 aState.GetAvailableSpace();
2853 #ifdef REALLY_NOISY_REFLOW
2854 printf("setting line %p isImpacted to %s\n", aLine.get(), aState.IsImpactedByFloat()?"true":"false");
2855 #endif
2856 PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
2857 aLine->SetLineIsImpactedByFloat(isImpacted);
2858 nsRect availSpace;
2859 aState.ComputeBlockAvailSpace(frame, display, availSpace);
2861 // Now put the Y coordinate back to the top of the top-margin +
2862 // clearance, and flow the block.
2863 aState.mY -= topMargin;
2864 availSpace.y -= topMargin;
2865 if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
2866 availSpace.height += topMargin;
2869 // keep track of the last overflow float in case we need to undo any new additions
2870 nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild();
2872 // Reflow the block into the available space
2873 nsMargin computedOffsets;
2874 // construct the html reflow state for the block. ReflowBlock
2875 // will initialize it
2876 nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
2877 nsSize(availSpace.width, availSpace.height));
2878 blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
2880 nsSpaceManager::SavedState spaceManagerState;
2881 if (mayNeedRetry) {
2882 blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
2883 aState.mSpaceManager->PushState(&spaceManagerState);
2884 } else if (!applyTopMargin) {
2885 blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
2888 nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
2889 rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
2890 clearance, aState.IsAdjacentWithTop(), computedOffsets,
2891 blockHtmlRS, frameReflowStatus);
2893 // If this was a second-pass reflow and the block's vertical position
2894 // changed, invalidates from the first pass might have happened in the
2895 // wrong places. Invalidate the entire overflow rect at the new position.
2896 if (!mayNeedRetry && clearanceFrame &&
2897 frame->GetRect().y != passOriginalY) {
2898 Invalidate(frame->GetOverflowRect() + frame->GetPosition());
2901 NS_ENSURE_SUCCESS(rv, rv);
2903 if (mayNeedRetry && clearanceFrame) {
2904 UndoSplitPlaceholders(aState, lastPlaceholder);
2905 aState.mSpaceManager->PopState(&spaceManagerState);
2906 aState.mY = startingY;
2907 aState.mPrevBottomMargin = incomingMargin;
2908 continue;
2911 aState.mPrevChild = frame;
2913 #if defined(REFLOW_STATUS_COVERAGE)
2914 RecordReflowStatus(PR_TRUE, frameReflowStatus);
2915 #endif
2917 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
2918 // None of the child block fits.
2919 UndoSplitPlaceholders(aState, lastPlaceholder);
2920 PushLines(aState, aLine.prev());
2921 *aKeepReflowGoing = PR_FALSE;
2922 aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
2924 else {
2925 // Note: line-break-after a block is a nop
2927 // Try to place the child block.
2928 // Don't force the block to fit if we have positive clearance, because
2929 // pushing it to the next page would give it more room.
2930 // Don't force the block to fit if it's impacted by a float. If it is,
2931 // then pushing it to the next page would give it more room. Note that
2932 // isImpacted doesn't include impact from the block's own floats.
2933 PRBool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
2934 !isImpacted;
2935 nsCollapsingMargin collapsedBottomMargin;
2936 nsRect combinedArea(0,0,0,0);
2937 *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
2938 computedOffsets, collapsedBottomMargin,
2939 aLine->mBounds, combinedArea, frameReflowStatus);
2940 if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
2941 line_iterator nextLine = aLine;
2942 ++nextLine;
2943 if (nextLine != end_lines()) {
2944 nextLine->MarkPreviousMarginDirty();
2948 aLine->SetCombinedArea(combinedArea);
2949 if (*aKeepReflowGoing) {
2950 // Some of the child block fit
2952 // Advance to new Y position
2953 nscoord newY = aLine->mBounds.YMost();
2954 aState.mY = newY;
2956 // Continue the block frame now if it didn't completely fit in
2957 // the available space.
2958 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
2959 PRBool madeContinuation;
2960 rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
2961 NS_ENSURE_SUCCESS(rv, rv);
2963 nsIFrame* nextFrame = frame->GetNextInFlow();
2965 // Push continuation to a new line, but only if we actually made one.
2966 if (madeContinuation) {
2967 nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
2968 NS_ENSURE_TRUE(line, NS_ERROR_OUT_OF_MEMORY);
2969 mLines.after_insert(aLine, line);
2972 // Advance to next line since some of the block fit. That way
2973 // only the following lines will be pushed.
2974 PushLines(aState, aLine);
2975 aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
2976 // If we need to reflow the continuation of the block child,
2977 // then we'd better reflow our continuation
2978 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
2979 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
2980 // We also need to make that continuation's line dirty so
2981 // it gets reflowed when we reflow our next in flow. The
2982 // nif's line must always be either a line of the nif's
2983 // parent block (only if we didn't make a continuation) or
2984 // else one of our own overflow lines. In the latter case
2985 // the line is already marked dirty, so just handle the
2986 // first case.
2987 if (!madeContinuation) {
2988 nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent());
2989 NS_ASSERTION(nifBlock->GetType() == nsGkAtoms::blockFrame
2990 || nifBlock->GetType() == nsGkAtoms::areaFrame,
2991 "A block's child's next in flow's parent must be a block!");
2992 for (line_iterator line = nifBlock->begin_lines(),
2993 line_end = nifBlock->end_lines(); line != line_end; ++line) {
2994 if (line->Contains(nextFrame)) {
2995 line->MarkDirty();
2996 break;
3001 *aKeepReflowGoing = PR_FALSE;
3003 // The bottom margin for a block is only applied on the last
3004 // flow block. Since we just continued the child block frame,
3005 // we know that line->mFirstChild is not the last flow block
3006 // therefore zero out the running margin value.
3007 #ifdef NOISY_VERTICAL_MARGINS
3008 ListTag(stdout);
3009 printf(": reflow incomplete, frame=");
3010 nsFrame::ListTag(stdout, frame);
3011 printf(" prevBottomMargin=%d, setting to zero\n",
3012 aState.mPrevBottomMargin);
3013 #endif
3014 aState.mPrevBottomMargin.Zero();
3016 else {
3017 #ifdef NOISY_VERTICAL_MARGINS
3018 ListTag(stdout);
3019 printf(": reflow complete for ");
3020 nsFrame::ListTag(stdout, frame);
3021 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3022 aState.mPrevBottomMargin, collapsedBottomMargin.get());
3023 #endif
3024 aState.mPrevBottomMargin = collapsedBottomMargin;
3026 #ifdef NOISY_VERTICAL_MARGINS
3027 ListTag(stdout);
3028 printf(": frame=");
3029 nsFrame::ListTag(stdout, frame);
3030 printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3031 brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
3032 aState.mPrevBottomMargin);
3033 #endif
3035 else {
3036 // None of the block fits. Determine the correct reflow status.
3037 if (aLine == mLines.front()) {
3038 // If it's our very first line then we need to be pushed to
3039 // our parents next-in-flow. Therefore, return break-before
3040 // status for our reflow status.
3041 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
3043 else {
3044 // Push the line that didn't fit and any lines that follow it
3045 // to our next-in-flow.
3046 UndoSplitPlaceholders(aState, lastPlaceholder);
3047 PushLines(aState, aLine.prev());
3048 aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3052 break; // out of the reflow retry loop
3055 // Now that we've got its final position all figured out, position any child
3056 // views it may have. Note that the case when frame has a view got handled
3057 // by FinishReflowChild, but that function didn't have the coordinates needed
3058 // to correctly decide whether to reposition child views.
3059 if (originalPosition != frame->GetPosition() && !frame->HasView()) {
3060 nsContainerFrame::PositionChildViews(frame);
3063 #ifdef DEBUG
3064 VerifyLines(PR_TRUE);
3065 #endif
3066 return rv;
3069 nsresult
3070 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
3071 line_iterator aLine,
3072 PRBool* aKeepReflowGoing)
3074 nsresult rv = NS_OK;
3075 *aKeepReflowGoing = PR_TRUE;
3077 #ifdef DEBUG
3078 PRInt32 spins = 0;
3079 #endif
3080 LineReflowStatus lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3081 PRBool movedPastFloat = PR_FALSE;
3082 do {
3083 PRBool allowPullUp = PR_TRUE;
3084 nsIContent* forceBreakInContent = nsnull;
3085 PRInt32 forceBreakOffset = -1;
3086 do {
3087 nsSpaceManager::SavedState spaceManagerState;
3088 aState.mReflowState.mSpaceManager->PushState(&spaceManagerState);
3090 // Once upon a time we allocated the first 30 nsLineLayout objects
3091 // on the stack, and then we switched to the heap. At that time
3092 // these objects were large (1100 bytes on a 32 bit system).
3093 // Then the nsLineLayout object was shrunk to 156 bytes by
3094 // removing some internal buffers. Given that it is so much
3095 // smaller, the complexity of 2 different ways of allocating
3096 // no longer makes sense. Now we always allocate on the stack
3097 nsLineLayout lineLayout(aState.mPresContext,
3098 aState.mReflowState.mSpaceManager,
3099 &aState.mReflowState, &aLine);
3100 lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
3101 if (forceBreakInContent) {
3102 lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
3104 rv = DoReflowInlineFrames(aState, lineLayout, aLine,
3105 aKeepReflowGoing, &lineReflowStatus,
3106 allowPullUp);
3107 lineLayout.EndLineReflow();
3109 if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
3110 LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3111 if (lineLayout.NeedsBackup()) {
3112 NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary");
3113 // If there is no saved break position, then this will set
3114 // set forceBreakInContent to null and we won't back up, which is
3115 // correct.
3116 forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset);
3117 } else {
3118 forceBreakInContent = nsnull;
3120 // restore the space manager state
3121 aState.mReflowState.mSpaceManager->PopState(&spaceManagerState);
3122 // Clear out float lists
3123 aState.mCurrentLineFloats.DeleteAll();
3124 aState.mBelowCurrentLineFloats.DeleteAll();
3127 #ifdef DEBUG
3128 spins++;
3129 if (1000 == spins) {
3130 ListTag(stdout);
3131 printf(": yikes! spinning on a line over 1000 times!\n");
3132 NS_ABORT();
3134 #endif
3136 // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
3137 allowPullUp = PR_FALSE;
3138 } while (NS_SUCCEEDED(rv) && LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
3140 if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3141 movedPastFloat = PR_TRUE;
3143 } while (NS_SUCCEEDED(rv) && LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
3145 // If we did at least one REDO_FOR_FLOAT, then the line did not fit next to some float.
3146 // Mark it as impacted by a float, even if it no longer is next to a float.
3147 if (movedPastFloat) {
3148 aLine->SetLineIsImpactedByFloat(PR_TRUE);
3151 return rv;
3154 // If at least one float on the line was complete, not at the top of
3155 // page, but was truncated, then restore the overflow floats to what
3156 // they were before and push the line. The floats that will be removed
3157 // from the list aren't yet known by the block's next in flow.
3158 void
3159 nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState,
3160 line_iterator aLine,
3161 nsIFrame* aLastPlaceholder,
3162 PRBool& aKeepReflowGoing)
3164 UndoSplitPlaceholders(aState, aLastPlaceholder);
3166 line_iterator prevLine = aLine;
3167 --prevLine;
3168 PushLines(aState, prevLine);
3169 aKeepReflowGoing = PR_FALSE;
3170 aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3173 #ifdef DEBUG
3174 static const char* LineReflowStatusNames[] = {
3175 "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
3176 "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
3178 #endif
3180 nsresult
3181 nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
3182 nsLineLayout& aLineLayout,
3183 line_iterator aLine,
3184 PRBool* aKeepReflowGoing,
3185 LineReflowStatus* aLineReflowStatus,
3186 PRBool aAllowPullUp)
3188 // Forget all of the floats on the line
3189 aLine->FreeFloats(aState.mFloatCacheFreeList);
3190 aState.mFloatCombinedArea.SetRect(0, 0, 0, 0);
3192 // Setup initial coordinate system for reflowing the inline frames
3193 // into. Apply a previous block frame's bottom margin first.
3194 if (ShouldApplyTopMargin(aState, aLine)) {
3195 aState.mY += aState.mPrevBottomMargin.get();
3197 aState.GetAvailableSpace();
3198 PRBool impactedByFloats = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
3199 aLine->SetLineIsImpactedByFloat(impactedByFloats);
3200 #ifdef REALLY_NOISY_REFLOW
3201 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
3202 this, impactedByFloats);
3203 #endif
3205 const nsMargin& borderPadding = aState.BorderPadding();
3206 nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
3207 nscoord availWidth = aState.mAvailSpaceRect.width;
3208 nscoord availHeight;
3209 if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
3210 availHeight = NS_UNCONSTRAINEDSIZE;
3212 else {
3213 /* XXX get the height right! */
3214 availHeight = aState.mAvailSpaceRect.height;
3217 // Make sure to enable resize optimization before we call BeginLineReflow
3218 // because it might get disabled there
3219 aLine->EnableResizeReflowOptimization();
3221 aLineLayout.BeginLineReflow(x, aState.mY,
3222 availWidth, availHeight,
3223 impactedByFloats,
3224 PR_FALSE /*XXX isTopOfPage*/);
3226 // XXX Unfortunately we need to know this before reflowing the first
3227 // inline frame in the line. FIX ME.
3228 if ((0 == aLineLayout.GetLineNumber()) &&
3229 (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
3230 aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
3233 // keep track of the last overflow float in case we need to undo any new additions
3234 nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild();
3236 // Reflow the frames that are already on the line first
3237 nsresult rv = NS_OK;
3238 LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
3239 PRInt32 i;
3240 nsIFrame* frame = aLine->mFirstChild;
3242 // Determine whether this is a line of placeholders for out-of-flow
3243 // continuations
3244 PRBool isContinuingPlaceholders = PR_FALSE;
3246 if (impactedByFloats) {
3247 // There is a soft break opportunity at the start of the line, because
3248 // we can always move this line down below float(s).
3249 if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, PR_TRUE)) {
3250 lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3254 // need to repeatedly call GetChildCount here, because the child
3255 // count can change during the loop!
3256 for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
3257 i++, frame = frame->GetNextSibling()) {
3258 if (IsContinuationPlaceholder(frame)) {
3259 isContinuingPlaceholders = PR_TRUE;
3261 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3262 &lineReflowStatus);
3263 NS_ENSURE_SUCCESS(rv, rv);
3264 if (LINE_REFLOW_OK != lineReflowStatus) {
3265 // It is possible that one or more of next lines are empty
3266 // (because of DeleteNextInFlowChild). If so, delete them now
3267 // in case we are finished.
3268 ++aLine;
3269 while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
3270 // XXX Is this still necessary now that DeleteNextInFlowChild
3271 // uses DoRemoveFrame?
3272 nsLineBox *toremove = aLine;
3273 aLine = mLines.erase(aLine);
3274 NS_ASSERTION(nsnull == toremove->mFirstChild, "bad empty line");
3275 aState.FreeLineBox(toremove);
3277 --aLine;
3279 if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
3280 // Push the line with the truncated float
3281 PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
3286 // Don't pull up new frames into lines with continuation placeholders
3287 if (!isContinuingPlaceholders && aAllowPullUp) {
3288 // Pull frames and reflow them until we can't
3289 while (LINE_REFLOW_OK == lineReflowStatus) {
3290 rv = PullFrame(aState, aLine, frame);
3291 NS_ENSURE_SUCCESS(rv, rv);
3292 if (nsnull == frame) {
3293 break;
3296 while (LINE_REFLOW_OK == lineReflowStatus) {
3297 PRInt32 oldCount = aLine->GetChildCount();
3298 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3299 &lineReflowStatus);
3300 NS_ENSURE_SUCCESS(rv, rv);
3301 if (aLine->GetChildCount() != oldCount) {
3302 // We just created a continuation for aFrame AND its going
3303 // to end up on this line (e.g. :first-letter
3304 // situation). Therefore we have to loop here before trying
3305 // to pull another frame.
3306 frame = frame->GetNextSibling();
3308 else {
3309 break;
3315 // We only need to backup if the line isn't going to be reflowed again anyway
3316 PRBool needsBackup = aLineLayout.NeedsBackup() &&
3317 (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK);
3318 if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
3319 NS_WARNING("We shouldn't be backing up more than once! "
3320 "Someone must have set a break opportunity beyond the available width, "
3321 "even though there were better break opportunities before it");
3322 needsBackup = PR_FALSE;
3324 if (needsBackup) {
3325 // We need to try backing up to before a text run
3326 PRInt32 offset;
3327 nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset);
3328 // XXX It's possible, in fact not unusual, for the break opportunity to already
3329 // be the end of the line. We should detect that and optimize to not
3330 // re-do the line.
3331 if (breakContent) {
3332 // We can back up!
3333 lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
3335 } else {
3336 // In case we reflow this line again, remember that we don't
3337 // need to force any breaking
3338 aLineLayout.ClearOptionalBreakPosition();
3341 if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3342 // This happens only when we have a line that is impacted by
3343 // floats and the first element in the line doesn't fit with
3344 // the floats.
3346 // What we do is to advance past the first float we find and
3347 // then reflow the line all over again.
3348 NS_ASSERTION(aState.IsImpactedByFloat(),
3349 "redo line on totally empty line");
3350 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
3351 "unconstrained height on totally empty line");
3353 if (aState.mAvailSpaceRect.height > 0) {
3354 aState.mY += aState.mAvailSpaceRect.height;
3355 } else {
3356 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight,
3357 "We shouldn't be running out of height here");
3358 if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableHeight) {
3359 // just move it down a bit to try to get out of this mess
3360 aState.mY += 1;
3361 } else {
3362 // There's nowhere to retry placing the line. Just treat it as if
3363 // we placed the float but it was truncated so we need this line
3364 // to go to the next page/column.
3365 lineReflowStatus = LINE_REFLOW_TRUNCATED;
3366 // Push the line that didn't fit
3367 PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder,
3368 *aKeepReflowGoing);
3372 // We don't want to advance by the bottom margin anymore (we did it
3373 // once at the beginning of this function, which will just be called
3374 // again), and we certainly don't want to go back if it's negative
3375 // (infinite loop, bug 153429).
3376 aState.mPrevBottomMargin.Zero();
3378 // XXX: a small optimization can be done here when paginating:
3379 // if the new Y coordinate is past the end of the block then
3380 // push the line and return now instead of later on after we are
3381 // past the float.
3383 else if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus) {
3384 // We don't want to advance by the bottom margin anymore (we did it
3385 // once at the beginning of this function, which will just be called
3386 // again), and we certainly don't want to go back if it's negative
3387 // (infinite loop, bug 153429).
3388 aState.mPrevBottomMargin.Zero();
3390 else if (LINE_REFLOW_TRUNCATED != lineReflowStatus) {
3391 // If we are propagating out a break-before status then there is
3392 // no point in placing the line.
3393 if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
3394 if (PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing)) {
3395 UndoSplitPlaceholders(aState, lastPlaceholder); // undo since we pushed the current line
3399 #ifdef DEBUG
3400 if (gNoisyReflow) {
3401 printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
3403 #endif
3404 *aLineReflowStatus = lineReflowStatus;
3406 return rv;
3410 * Reflow an inline frame. The reflow status is mapped from the frames
3411 * reflow status to the lines reflow status (not to our reflow status).
3412 * The line reflow status is simple: PR_TRUE means keep placing frames
3413 * on the line; PR_FALSE means don't (the line is done). If the line
3414 * has some sort of breaking affect then aLine's break-type will be set
3415 * to something other than NS_STYLE_CLEAR_NONE.
3417 nsresult
3418 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
3419 nsLineLayout& aLineLayout,
3420 line_iterator aLine,
3421 nsIFrame* aFrame,
3422 LineReflowStatus* aLineReflowStatus)
3424 NS_ENSURE_ARG_POINTER(aFrame);
3426 *aLineReflowStatus = LINE_REFLOW_OK;
3428 // If it's currently ok to be reflowing in first-letter style then
3429 // we must be about to reflow a frame that has first-letter style.
3430 PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
3431 #ifdef NOISY_FIRST_LETTER
3432 ListTag(stdout);
3433 printf(": reflowing ");
3434 nsFrame::ListTag(stdout, aFrame);
3435 printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter ? "on" : "off");
3436 #endif
3438 // Reflow the inline frame
3439 nsReflowStatus frameReflowStatus;
3440 PRBool pushedFrame;
3441 nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
3442 nsnull, pushedFrame);
3443 NS_ENSURE_SUCCESS(rv, rv);
3445 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
3446 // we need to ensure that the frame's nextinflow gets reflowed.
3447 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3448 nsBlockFrame* ourNext = NS_STATIC_CAST(nsBlockFrame*, GetNextInFlow());
3449 if (ourNext && aFrame->GetNextInFlow()) {
3450 line_iterator f = ourNext->FindLineFor(aFrame->GetNextInFlow());
3451 if (f != ourNext->end_lines()) {
3452 f->MarkDirty();
3457 NS_ENSURE_SUCCESS(rv, rv);
3458 #ifdef REALLY_NOISY_REFLOW_CHILD
3459 nsFrame::ListTag(stdout, aFrame);
3460 printf(": status=%x\n", frameReflowStatus);
3461 #endif
3463 #if defined(REFLOW_STATUS_COVERAGE)
3464 RecordReflowStatus(PR_FALSE, frameReflowStatus);
3465 #endif
3467 // Send post-reflow notification
3468 aState.mPrevChild = aFrame;
3470 /* XXX
3471 This is where we need to add logic to handle some odd behavior.
3472 For one thing, we should usually place at least one thing next
3473 to a left float, even when that float takes up all the width on a line.
3474 see bug 22496
3477 // Process the child frames reflow status. There are 5 cases:
3478 // complete, not-complete, break-before, break-after-complete,
3479 // break-after-not-complete. There are two situations: we are a
3480 // block or we are an inline. This makes a total of 10 cases
3481 // (fortunately, there is some overlap).
3482 aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
3483 if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
3484 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
3485 // Always abort the line reflow (because a line break is the
3486 // minimal amount of break we do).
3487 *aLineReflowStatus = LINE_REFLOW_STOP;
3489 // XXX what should aLine's break-type be set to in all these cases?
3490 PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
3491 NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) ||
3492 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
3493 NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet");
3495 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3496 // Break-before cases.
3497 if (aFrame == aLine->mFirstChild) {
3498 // If we break before the first frame on the line then we must
3499 // be trying to place content where there's no room (e.g. on a
3500 // line with wide floats). Inform the caller to reflow the
3501 // line after skipping past a float.
3502 *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3504 else {
3505 // It's not the first child on this line so go ahead and split
3506 // the line. We will see the frame again on the next-line.
3507 rv = SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
3508 NS_ENSURE_SUCCESS(rv, rv);
3510 // If we're splitting the line because the frame didn't fit and it
3511 // was pushed, then mark the line as having word wrapped. We need to
3512 // know that if we're shrink wrapping our width
3513 if (pushedFrame) {
3514 aLine->SetLineWrapped(PR_TRUE);
3518 else {
3519 // If a float split and its prev-in-flow was followed by a <BR>, then combine
3520 // the <BR>'s break type with the inline's break type (the inline will be the very
3521 // next frame after the split float).
3522 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
3523 breakType = nsLayoutUtils::CombineBreakType(breakType,
3524 aState.mFloatBreakType);
3525 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
3527 // Break-after cases
3528 if (breakType == NS_STYLE_CLEAR_LINE) {
3529 if (!aLineLayout.GetLineEndsInBR()) {
3530 breakType = NS_STYLE_CLEAR_NONE;
3533 aLine->SetBreakTypeAfter(breakType);
3534 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3535 // Create a continuation for the incomplete frame. Note that the
3536 // frame may already have a continuation.
3537 PRBool madeContinuation;
3538 rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
3539 NS_ENSURE_SUCCESS(rv, rv);
3540 if (!aLineLayout.GetLineEndsInBR()) {
3541 // Remember that the line has wrapped
3542 aLine->SetLineWrapped(PR_TRUE);
3546 // Split line, but after the frame just reflowed
3547 rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3548 NS_ENSURE_SUCCESS(rv, rv);
3550 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus) ||
3551 (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
3552 !aLineLayout.GetLineEndsInBR())) {
3553 // Mark next line dirty in case SplitLine didn't end up
3554 // pushing any frames.
3555 nsLineList_iterator next = aLine.next();
3556 if (next != end_lines() && !next->IsBlock()) {
3557 next->MarkDirty();
3562 else if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3563 // Frame is not-complete, no special breaking status
3565 nsIAtom* frameType = aFrame->GetType();
3567 // Create a continuation for the incomplete frame. Note that the
3568 // frame may already have a continuation.
3569 PRBool madeContinuation;
3570 rv = (nsGkAtoms::placeholderFrame == frameType)
3571 ? SplitPlaceholder(aState, aFrame)
3572 : CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
3573 NS_ENSURE_SUCCESS(rv, rv);
3575 // Remember that the line has wrapped
3576 if (!aLineLayout.GetLineEndsInBR()) {
3577 aLine->SetLineWrapped(PR_TRUE);
3580 // If we are reflowing the first letter frame or a placeholder then
3581 // don't split the line and don't stop the line reflow...
3582 PRBool splitLine = !reflowingFirstLetter &&
3583 nsGkAtoms::placeholderFrame != frameType;
3584 if (reflowingFirstLetter) {
3585 if ((nsGkAtoms::inlineFrame == frameType) ||
3586 (nsGkAtoms::lineFrame == frameType)) {
3587 splitLine = PR_TRUE;
3591 if (splitLine) {
3592 // Split line after the current frame
3593 *aLineReflowStatus = LINE_REFLOW_STOP;
3594 rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3595 NS_ENSURE_SUCCESS(rv, rv);
3597 // Mark next line dirty in case SplitLine didn't end up
3598 // pushing any frames.
3599 nsLineList_iterator next = aLine.next();
3600 if (next != end_lines() && !next->IsBlock()) {
3601 next->MarkDirty();
3605 else if (NS_FRAME_IS_TRUNCATED(frameReflowStatus)) {
3606 // if the frame is a placeholder and was complete but truncated (and not at the top
3607 // of page), the entire line will be pushed to give it another chance to not truncate.
3608 if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
3609 *aLineReflowStatus = LINE_REFLOW_TRUNCATED;
3613 return NS_OK;
3617 * Create a continuation, if necessary, for aFrame. Place it in aLine
3618 * if aLine is not null. Set aMadeNewFrame to PR_TRUE if a new frame is created.
3620 nsresult
3621 nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
3622 nsLineBox* aLine,
3623 nsIFrame* aFrame,
3624 PRBool& aMadeNewFrame)
3626 aMadeNewFrame = PR_FALSE;
3627 nsresult rv;
3628 nsIFrame* nextInFlow;
3629 rv = CreateNextInFlow(aState.mPresContext, this, aFrame, nextInFlow);
3630 NS_ENSURE_SUCCESS(rv, rv);
3631 if (nsnull != nextInFlow) {
3632 aMadeNewFrame = PR_TRUE;
3633 if (aLine) {
3634 aLine->SetChildCount(aLine->GetChildCount() + 1);
3637 #ifdef DEBUG
3638 VerifyLines(PR_FALSE);
3639 #endif
3640 return rv;
3643 nsresult
3644 nsBlockFrame::SplitPlaceholder(nsBlockReflowState& aState,
3645 nsIFrame* aPlaceholder)
3647 nsIFrame* nextInFlow;
3648 nsresult rv = CreateNextInFlow(aState.mPresContext, this, aPlaceholder, nextInFlow);
3649 NS_ENSURE_SUCCESS(rv, rv);
3651 if (!nextInFlow) {
3652 // Next in flow was not created because it already exists.
3653 return NS_OK;
3656 // put the sibling list back to what it was before the continuation was created
3657 nsIFrame *contFrame = aPlaceholder->GetNextSibling();
3658 nsIFrame *next = contFrame->GetNextSibling();
3659 aPlaceholder->SetNextSibling(next);
3660 contFrame->SetNextSibling(nsnull);
3662 NS_ASSERTION(IsContinuationPlaceholder(contFrame),
3663 "Didn't create the right kind of frame");
3665 // The new out of flow frame does not get put anywhere; the out-of-flows
3666 // for placeholders in mOverflowPlaceholders are not kept in any child list
3667 aState.mOverflowPlaceholders.AppendFrame(this, contFrame);
3668 return NS_OK;
3671 static nsFloatCache*
3672 GetLastFloat(nsLineBox* aLine)
3674 nsFloatCache* fc = aLine->GetFirstFloat();
3675 while (fc && fc->Next()) {
3676 fc = fc->Next();
3678 return fc;
3681 static PRBool
3682 CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
3684 if (!aFC)
3685 return PR_TRUE;
3686 for (nsIFrame* f = aFC->mPlaceholder; f; f = f->GetParent()) {
3687 if (f->GetParent() == aBlock)
3688 return aLine->Contains(f);
3690 NS_ASSERTION(PR_FALSE, "aBlock is not an ancestor of aFrame!");
3691 return PR_TRUE;
3694 nsresult
3695 nsBlockFrame::SplitLine(nsBlockReflowState& aState,
3696 nsLineLayout& aLineLayout,
3697 line_iterator aLine,
3698 nsIFrame* aFrame,
3699 LineReflowStatus* aLineReflowStatus)
3701 NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
3703 PRInt32 pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
3704 NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count");
3706 #ifdef DEBUG
3707 if (gNoisyReflow) {
3708 nsFrame::IndentBy(stdout, gNoiseIndent);
3709 printf("split line: from line=%p pushCount=%d aFrame=",
3710 NS_STATIC_CAST(void*, aLine.get()), pushCount);
3711 if (aFrame) {
3712 nsFrame::ListTag(stdout, aFrame);
3714 else {
3715 printf("(null)");
3717 printf("\n");
3718 if (gReallyNoisyReflow) {
3719 aLine->List(stdout, gNoiseIndent+1);
3722 #endif
3724 if (0 != pushCount) {
3725 NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
3726 NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops");
3727 NS_ASSERTION(nsFrameList(aFrame).GetLength() >= pushCount,
3728 "Not enough frames to push");
3730 // Put frames being split out into their own line
3731 nsLineBox* newLine = aState.NewLineBox(aFrame, pushCount, PR_FALSE);
3732 if (!newLine) {
3733 return NS_ERROR_OUT_OF_MEMORY;
3735 mLines.after_insert(aLine, newLine);
3736 aLine->SetChildCount(aLine->GetChildCount() - pushCount);
3737 #ifdef DEBUG
3738 if (gReallyNoisyReflow) {
3739 newLine->List(stdout, gNoiseIndent+1);
3741 #endif
3743 // Let line layout know that some frames are no longer part of its
3744 // state.
3745 aLineLayout.SplitLineTo(aLine->GetChildCount());
3747 // If floats have been placed whose placeholders have been pushed to the new
3748 // line, we need to reflow the old line again. We don't want to look at the
3749 // frames in the new line, because as a large paragraph is laid out the
3750 // we'd get O(N^2) performance. So instead we just check that the last
3751 // float and the last below-current-line float are still in aLine.
3752 if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
3753 !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
3754 *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
3757 #ifdef DEBUG
3758 VerifyLines(PR_TRUE);
3759 #endif
3761 return NS_OK;
3764 PRBool
3765 nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState,
3766 line_iterator aLine)
3768 while (++aLine != end_lines()) {
3769 // There is another line
3770 if (0 != aLine->GetChildCount()) {
3771 // If the next line is a block line then we must not justify
3772 // this line because it means that this line is the last in a
3773 // group of inline lines.
3774 return !aLine->IsBlock();
3776 // The next line is empty, try the next one
3779 // XXX Not sure about this part
3780 // Try our next-in-flows lines to answer the question
3781 nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
3782 while (nsnull != nextInFlow) {
3783 for (line_iterator line = nextInFlow->begin_lines(),
3784 line_end = nextInFlow->end_lines();
3785 line != line_end;
3786 ++line)
3788 if (0 != line->GetChildCount())
3789 return !line->IsBlock();
3791 nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
3794 // This is the last line - so don't allow justification
3795 return PR_FALSE;
3798 PRBool
3799 nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
3800 nsLineLayout& aLineLayout,
3801 line_iterator aLine,
3802 PRBool* aKeepReflowGoing)
3804 // Trim extra white-space from the line before placing the frames
3805 aLineLayout.TrimTrailingWhiteSpace();
3807 // Vertically align the frames on this line.
3809 // According to the CSS2 spec, section 12.6.1, the "marker" box
3810 // participates in the height calculation of the list-item box's
3811 // first line box.
3813 // There are exactly two places a bullet can be placed: near the
3814 // first or second line. It's only placed on the second line in a
3815 // rare case: an empty first line followed by a second line that
3816 // contains a block (example: <LI>\n<P>... ).
3818 // For this code, only the first case is possible because this
3819 // method is used for placing a line of inline frames. If the rare
3820 // case is happening then the worst that will happen is that the
3821 // bullet frame will be reflowed twice.
3822 PRBool addedBullet = PR_FALSE;
3823 if (mBullet && HaveOutsideBullet() && (aLine == mLines.front()) &&
3824 (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) {
3825 nsHTMLReflowMetrics metrics;
3826 ReflowBullet(aState, metrics);
3827 aLineLayout.AddBulletFrame(mBullet, metrics);
3828 addedBullet = PR_TRUE;
3830 aLineLayout.VerticalAlignLine();
3832 #ifdef DEBUG
3834 static nscoord lastHeight = 0;
3835 if (CRAZY_HEIGHT(aLine->mBounds.y)) {
3836 lastHeight = aLine->mBounds.y;
3837 if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
3838 nsFrame::ListTag(stdout);
3839 printf(": line=%p y=%d line.bounds.height=%d\n",
3840 NS_STATIC_CAST(void*, aLine.get()),
3841 aLine->mBounds.y, aLine->mBounds.height);
3844 else {
3845 lastHeight = 0;
3848 #endif
3850 // Only block frames horizontally align their children because
3851 // inline frames "shrink-wrap" around their children (therefore
3852 // there is no extra horizontal space).
3853 const nsStyleText* styleText = GetStyleText();
3854 PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign &&
3855 !aLineLayout.GetLineEndsInBR() &&
3856 ShouldJustifyLine(aState, aLine);
3857 aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
3858 // XXX: not only bidi: right alignment can be broken after
3859 // RelativePositionFrames!!!
3860 // XXXldb Is something here considering relatively positioned frames at
3861 // other than their original positions?
3862 #ifdef IBMBIDI
3863 // XXXldb Why don't we do this earlier?
3864 if (aState.mPresContext->BidiEnabled()) {
3865 if (!aState.mPresContext->IsVisualMode()) {
3866 nsBidiPresUtils* bidiUtils = aState.mPresContext->GetBidiUtils();
3868 if (bidiUtils && bidiUtils->IsSuccessful() ) {
3869 bidiUtils->ReorderFrames(aState.mPresContext,
3870 aState.mReflowState.rendContext,
3871 aLine->mFirstChild, aLine->GetChildCount());
3872 } // bidiUtils
3873 } // not visual mode
3874 } // bidi enabled
3875 #endif // IBMBIDI
3877 nsRect combinedArea;
3878 aLineLayout.RelativePositionFrames(combinedArea); // XXXldb This returned width as -15, 2001-06-12, Bugzilla
3879 aLine->SetCombinedArea(combinedArea);
3880 if (addedBullet) {
3881 aLineLayout.RemoveBulletFrame(mBullet);
3884 // Inline lines do not have margins themselves; however they are
3885 // impacted by prior block margins. If this line ends up having some
3886 // height then we zero out the previous bottom margin value that was
3887 // already applied to the line's starting Y coordinate. Otherwise we
3888 // leave it be so that the previous blocks bottom margin can be
3889 // collapsed with a block that follows.
3890 nscoord newY;
3892 if (!aLine->CachedIsEmpty()) {
3893 // This line has some height. Therefore the application of the
3894 // previous-bottom-margin should stick.
3895 aState.mPrevBottomMargin.Zero();
3896 newY = aLine->mBounds.YMost();
3898 else {
3899 // Don't let the previous-bottom-margin value affect the newY
3900 // coordinate (it was applied in ReflowInlineFrames speculatively)
3901 // since the line is empty.
3902 // We already called |ShouldApplyTopMargin|, and if we applied it
3903 // then BRS_APPLYTOPMARGIN is set.
3904 nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
3905 ? -aState.mPrevBottomMargin.get() : 0;
3906 newY = aState.mY + dy;
3907 aLine->SlideBy(dy); // XXXldb Do we really want to do this?
3910 // See if the line fit. If it doesn't we need to push it. Our first
3911 // line will always fit.
3912 if (mLines.front() != aLine &&
3913 newY > aState.mBottomEdge &&
3914 aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
3915 // Push this line and all of its children and anything else that
3916 // follows to our next-in-flow
3917 NS_ASSERTION((aState.mCurrentLine == aLine), "oops");
3918 PushLines(aState, aLine.prev());
3920 // Stop reflow and whack the reflow status if reflow hasn't
3921 // already been stopped.
3922 if (*aKeepReflowGoing) {
3923 aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
3924 *aKeepReflowGoing = PR_FALSE;
3926 return PR_TRUE;
3929 // May be needed below
3930 PRBool wasAdjacentWIthTop = aState.IsAdjacentWithTop();
3932 aState.mY = newY;
3934 // Add the already placed current-line floats to the line
3935 aLine->AppendFloats(aState.mCurrentLineFloats);
3937 // Any below current line floats to place?
3938 if (aState.mBelowCurrentLineFloats.NotEmpty()) {
3939 // Reflow the below-current-line floats, then add them to the
3940 // lines float list if there aren't any truncated floats.
3941 if (aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats,
3942 wasAdjacentWIthTop)) {
3943 aLine->AppendFloats(aState.mBelowCurrentLineFloats);
3945 else {
3946 // At least one float is truncated, so fix up any placeholders that got split and
3947 // push the line. XXX It may be better to put the float on the next line, but this
3948 // is not common enough to justify the complexity. Or maybe it is now...
3950 nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild();
3951 PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
3955 // When a line has floats, factor them into the combined-area
3956 // computations.
3957 if (aLine->HasFloats()) {
3958 // Combine the float combined area (stored in aState) and the
3959 // value computed by the line layout code.
3960 nsRect lineCombinedArea(aLine->GetCombinedArea());
3961 #ifdef NOISY_COMBINED_AREA
3962 ListTag(stdout);
3963 printf(": lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
3964 lineCombinedArea.x, lineCombinedArea.y,
3965 lineCombinedArea.width, lineCombinedArea.height,
3966 aState.mFloatCombinedArea.x, aState.mFloatCombinedArea.y,
3967 aState.mFloatCombinedArea.width,
3968 aState.mFloatCombinedArea.height);
3969 #endif
3970 lineCombinedArea.UnionRect(aState.mFloatCombinedArea, lineCombinedArea);
3972 aLine->SetCombinedArea(lineCombinedArea);
3973 #ifdef NOISY_COMBINED_AREA
3974 printf(" ==> final lineCA=%d,%d,%d,%d\n",
3975 lineCombinedArea.x, lineCombinedArea.y,
3976 lineCombinedArea.width, lineCombinedArea.height);
3977 #endif
3980 // Apply break-after clearing if necessary
3981 // This must stay in sync with |ReflowDirtyLines|.
3982 if (aLine->HasFloatBreakAfter()) {
3983 aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
3986 return PR_FALSE;
3989 void
3990 nsBlockFrame::PushLines(nsBlockReflowState& aState,
3991 nsLineList::iterator aLineBefore)
3993 nsLineList::iterator overBegin(aLineBefore.next());
3995 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
3996 PRBool firstLine = overBegin == begin_lines();
3998 if (overBegin != end_lines()) {
3999 // Remove floats in the lines from mFloats
4000 nsFrameList floats;
4001 nsIFrame* tail = nsnull;
4002 CollectFloats(overBegin->mFirstChild, floats, &tail, PR_FALSE, PR_TRUE);
4004 if (floats.NotEmpty()) {
4005 // Push the floats onto the front of the overflow out-of-flows list
4006 nsFrameList oofs = GetOverflowOutOfFlows();
4007 if (oofs.NotEmpty()) {
4008 floats.InsertFrames(nsnull, tail, oofs.FirstChild());
4010 SetOverflowOutOfFlows(floats);
4013 // overflow lines can already exist in some cases, in particular,
4014 // when shrinkwrapping and we discover that the shrinkwap causes
4015 // the height of some child block to grow which creates additional
4016 // overflowing content. In such cases we must prepend the new
4017 // overflow to the existing overflow.
4018 nsLineList* overflowLines = RemoveOverflowLines();
4019 if (!overflowLines) {
4020 // XXXldb use presshell arena!
4021 overflowLines = new nsLineList();
4023 if (overflowLines) {
4024 if (!overflowLines->empty()) {
4025 mLines.back()->LastChild()->SetNextSibling(overflowLines->front()->mFirstChild);
4027 overflowLines->splice(overflowLines->begin(), mLines, overBegin,
4028 end_lines());
4029 NS_ASSERTION(!overflowLines->empty(), "should not be empty");
4030 // this takes ownership but it won't delete it immediately so we
4031 // can keep using it.
4032 SetOverflowLines(overflowLines);
4034 // Mark all the overflow lines dirty so that they get reflowed when
4035 // they are pulled up by our next-in-flow.
4037 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4038 for (line_iterator line = overflowLines->begin(),
4039 line_end = overflowLines->end();
4040 line != line_end;
4041 ++line)
4043 line->MarkDirty();
4044 line->MarkPreviousMarginDirty();
4045 line->mBounds.SetRect(0, 0, 0, 0);
4046 if (line->HasFloats()) {
4047 line->FreeFloats(aState.mFloatCacheFreeList);
4053 // Break frame sibling list
4054 if (!firstLine)
4055 aLineBefore->LastChild()->SetNextSibling(nsnull);
4057 #ifdef DEBUG
4058 VerifyOverflowSituation();
4059 #endif
4063 * Call this when a frame will be pulled from the block's
4064 * next-in-flow into this frame. If it's a continuation placeholder,
4065 * it should not be here so we push it into our overflow placeholders
4066 * list. To avoid creating holes (e.g., the following block doesn't
4067 * have a placeholder but the block after it does) we also need to
4068 * pull all the following placeholders and put them in our overflow
4069 * placeholders list too.
4071 * If it's a first-in-flow placeholder, or it contains one, then we
4072 * need to do this to the continuation placeholders.
4074 * We return PR_TRUE if we removed the frame and it cannot be used.
4075 * If we return PR_FALSE then the frame *must* be pulled immediately.
4077 PRBool
4078 nsBlockFrame::HandleOverflowPlaceholdersForPulledFrame(
4079 nsBlockReflowState& aState, nsIFrame* aFrame)
4081 if (nsGkAtoms::placeholderFrame != aFrame->GetType()) {
4082 // Descend into children that are not float containing blocks.
4083 // We should encounter only first-in-flow placeholders, so the
4084 // frame subtree rooted at aFrame should not change.
4085 if (!aFrame->IsFloatContainingBlock()) {
4086 for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
4087 #ifdef DEBUG
4088 PRBool changed =
4089 #endif
4090 HandleOverflowPlaceholdersForPulledFrame(aState, f);
4091 NS_ASSERTION(!changed, "Shouldn't find any continuation placeholders inside inlines");
4094 return PR_FALSE;
4097 PRBool taken = PR_TRUE;
4098 nsIFrame* frame = aFrame;
4099 if (!aFrame->GetPrevInFlow()) {
4100 // First in flow frame. We only want to deal with its
4101 // next in flows, if there are any.
4102 taken = PR_FALSE;
4103 frame = frame->GetNextInFlow();
4104 if (!frame)
4105 return PR_FALSE;
4108 nsBlockFrame* parent = NS_STATIC_CAST(nsBlockFrame*, frame->GetParent());
4109 // Remove aFrame and all its next in flows from their parents, but
4110 // don't destroy the frames.
4111 #ifdef DEBUG
4112 nsresult rv =
4113 #endif
4114 parent->DoRemoveFrame(frame, PR_FALSE);
4115 NS_ASSERTION(NS_SUCCEEDED(rv), "frame should be in parent's lists");
4117 nsIFrame* lastOverflowPlace = aState.mOverflowPlaceholders.LastChild();
4118 while (frame) {
4119 NS_ASSERTION(IsContinuationPlaceholder(frame),
4120 "Should only be dealing with continuation placeholders here");
4122 parent = NS_STATIC_CAST(nsBlockFrame*, frame->GetParent());
4123 ReparentFrame(frame, parent, this);
4125 // continuation placeholders are always direct children of a block
4126 nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(frame);
4128 if (!parent->mFloats.RemoveFrame(outOfFlow)) {
4129 nsAutoOOFFrameList oofs(parent);
4130 #ifdef DEBUG
4131 PRBool found =
4132 #endif
4133 oofs.mList.RemoveFrame(outOfFlow);
4134 NS_ASSERTION(found, "Must have the out of flow in some child list");
4136 ReparentFrame(outOfFlow, parent, this);
4138 aState.mOverflowPlaceholders.InsertFrames(nsnull, lastOverflowPlace, frame);
4139 // outOfFlow isn't inserted anywhere yet. Eventually the overflow
4140 // placeholders get put into the overflow lines, and at the same time we
4141 // insert the placeholders' out of flows into the overflow out-of-flows
4142 // list.
4143 lastOverflowPlace = frame;
4145 frame = frame->GetNextInFlow();
4148 return taken;
4152 * Call this when a line will be pulled from the block's
4153 * next-in-flow's line.
4155 * @return PR_TRUE we consumed the entire line, delete it and try again
4157 PRBool
4158 nsBlockFrame::HandleOverflowPlaceholdersOnPulledLine(
4159 nsBlockReflowState& aState, nsLineBox* aLine)
4161 // First, see if it's a line of continuation placeholders. If it
4162 // is, remove one and retry.
4163 if (aLine->mFirstChild && IsContinuationPlaceholder(aLine->mFirstChild)) {
4164 PRBool taken =
4165 HandleOverflowPlaceholdersForPulledFrame(aState, aLine->mFirstChild);
4166 NS_ASSERTION(taken, "We must have removed that frame");
4167 return PR_TRUE;
4170 // OK, it's a normal line. Scan it for floats with continuations that
4171 // need to be taken care of. We won't need to change the line.
4172 PRInt32 n = aLine->GetChildCount();
4173 for (nsIFrame* f = aLine->mFirstChild; n > 0; f = f->GetNextSibling(), --n) {
4174 #ifdef DEBUG
4175 PRBool taken =
4176 #endif
4177 HandleOverflowPlaceholdersForPulledFrame(aState, f);
4178 NS_ASSERTION(!taken, "Shouldn't be any continuation placeholders on this line");
4181 return PR_FALSE;
4184 // The overflowLines property is stored as a pointer to a line list,
4185 // which must be deleted. However, the following functions all maintain
4186 // the invariant that the property is never set if the list is empty.
4188 PRBool
4189 nsBlockFrame::DrainOverflowLines(nsBlockReflowState& aState)
4191 #ifdef DEBUG
4192 VerifyOverflowSituation();
4193 #endif
4194 nsLineList* overflowLines = nsnull;
4195 nsLineList* ourOverflowLines = nsnull;
4197 // First grab the prev-in-flows overflow lines
4198 nsBlockFrame* prevBlock = (nsBlockFrame*) GetPrevInFlow();
4199 if (prevBlock) {
4200 overflowLines = prevBlock->RemoveOverflowLines();
4201 if (overflowLines) {
4202 NS_ASSERTION(! overflowLines->empty(),
4203 "overflow lines should never be set and empty");
4204 // Make all the frames on the overflow line list mine
4205 nsIFrame* frame = overflowLines->front()->mFirstChild;
4206 while (nsnull != frame) {
4207 ReparentFrame(frame, prevBlock, this);
4209 // Get the next frame
4210 frame = frame->GetNextSibling();
4213 // make the overflow out-of-flow frames mine too
4214 nsAutoOOFFrameList oofs(prevBlock);
4215 if (oofs.mList.NotEmpty()) {
4216 for (nsIFrame* f = oofs.mList.FirstChild(); f; f = f->GetNextSibling()) {
4217 ReparentFrame(f, prevBlock, this);
4219 mFloats.InsertFrames(nsnull, nsnull, oofs.mList.FirstChild());
4220 oofs.mList.SetFrames(nsnull);
4224 // The lines on the overflow list have already been marked dirty and their
4225 // previous margins marked dirty also.
4228 // Don't need to reparent frames in our own overflow lines/oofs, because they're
4229 // already ours. But we should put overflow floats back in mFloats.
4230 ourOverflowLines = RemoveOverflowLines();
4231 if (ourOverflowLines) {
4232 nsAutoOOFFrameList oofs(this);
4233 if (oofs.mList.NotEmpty()) {
4234 // The overflow floats go after our regular floats
4235 mFloats.AppendFrames(nsnull, oofs.mList.FirstChild());
4236 oofs.mList.SetFrames(nsnull);
4240 if (!overflowLines && !ourOverflowLines) {
4241 // nothing to do; always the case for non-constrained-height reflows
4242 return PR_FALSE;
4245 NS_ASSERTION(aState.mOverflowPlaceholders.IsEmpty(),
4246 "Should have no overflow placeholders yet");
4248 // HANDLING CONTINUATION PLACEHOLDERS (floats only at the moment, because
4249 // abs-pos frames don't have continuations)
4251 // All continuation placeholders need to be moved to the front of
4252 // our line list. We also need to maintain the invariant that at
4253 // most one frame for a given piece of content is in our normal
4254 // child list, by pushing all but the first placeholder to our
4255 // overflow placeholders list.
4257 // One problem we have to deal with is that some of these
4258 // continuation placeholders may have been donated to us by a
4259 // descendant block that was complete. We need to push them down to
4260 // a lower block if possible.
4262 // We keep the lists ordered so that prev in flows come before their
4263 // next in flows. We do not worry about properly ordering the
4264 // placeholders for different content relative to each other until
4265 // the end. Then we sort them.
4267 // When we're shuffling placeholders we also need to shuffle their out of
4268 // flows to match. As we put placeholders into keepPlaceholders, we unhook
4269 // their floats from mFloats. Later we put the floats back based on the
4270 // order of the placeholders.
4271 nsIFrame* lastOP = nsnull;
4272 nsFrameList keepPlaceholders;
4273 nsFrameList keepOutOfFlows;
4274 nsIFrame* lastKP = nsnull;
4275 nsIFrame* lastKOOF = nsnull;
4276 nsLineList* lineLists[3] = { overflowLines, &mLines, ourOverflowLines };
4277 static const PRPackedBool searchFirstLinesOnly[3] = { PR_FALSE, PR_TRUE, PR_FALSE };
4278 for (PRInt32 i = 0; i < 3; ++i) {
4279 nsLineList* ll = lineLists[i];
4280 if (ll && !ll->empty()) {
4281 line_iterator iter = ll->begin();
4282 line_iterator iter_end = ll->end();
4283 nsIFrame* lastFrame = nsnull;
4284 while (iter != iter_end) {
4285 PRUint32 n = iter->GetChildCount();
4286 if (n == 0 || !IsContinuationPlaceholder(iter->mFirstChild)) {
4287 if (lastFrame) {
4288 lastFrame->SetNextSibling(iter->mFirstChild);
4290 if (searchFirstLinesOnly[i]) {
4291 break;
4293 lastFrame = iter->LastChild();
4294 ++iter;
4295 } else {
4296 nsLineBox* line = iter;
4297 iter = ll->erase(iter);
4298 nsIFrame* next;
4299 for (nsPlaceholderFrame* f = NS_STATIC_CAST(nsPlaceholderFrame*, line->mFirstChild);
4300 n > 0; --n, f = NS_STATIC_CAST(nsPlaceholderFrame*, next)) {
4301 if (!IsContinuationPlaceholder(f)) {
4302 NS_ASSERTION(IsContinuationPlaceholder(f),
4303 "Line frames should all be continuation placeholders");
4305 next = f->GetNextSibling();
4306 nsIFrame* fpif = f->GetPrevInFlow();
4307 nsIFrame* oof = f->GetOutOfFlowFrame();
4309 // Take this out of mFloats for now. We may put it back later in
4310 // this function
4311 #ifdef DEBUG
4312 PRBool found =
4313 #endif
4314 mFloats.RemoveFrame(oof);
4315 NS_ASSERTION(found, "Float should have been put in our mFloats list");
4317 PRBool isAncestor = nsLayoutUtils::IsProperAncestorFrame(this, fpif);
4318 if (isAncestor) {
4319 // oops. we already have a prev-in-flow for this
4320 // placeholder. We have to move this frame out of here. We
4321 // can put it in our overflow placeholders.
4322 aState.mOverflowPlaceholders.InsertFrame(nsnull, lastOP, f);
4323 // Let oof dangle for now, because placeholders in
4324 // mOverflowPlaceholders do not keep their floats in any child list
4325 lastOP = f;
4326 } else {
4327 if (fpif->GetParent() == prevBlock) {
4328 keepPlaceholders.InsertFrame(nsnull, lastKP, f);
4329 keepOutOfFlows.InsertFrame(nsnull, lastKOOF, oof);
4330 lastKP = f;
4331 lastKOOF = oof;
4332 } else {
4333 // Ok, now we're in the tough situation where some child
4334 // of prevBlock was complete and pushed its overflow
4335 // placeholders up to prevBlock's overflow. We might be
4336 // able to find a more appropriate parent for f somewhere
4337 // down in our descendants.
4338 NS_ASSERTION(nsLayoutUtils::IsProperAncestorFrame(prevBlock, fpif),
4339 "bad prev-in-flow ancestor chain");
4340 // Find the first ancestor of f's prev-in-flow that has a next in flow
4341 // that can contain the float.
4342 // That next in flow should become f's parent.
4343 nsIFrame* fpAncestor;
4344 for (fpAncestor = fpif->GetParent();
4345 !fpAncestor->GetNextInFlow() || !fpAncestor->IsFloatContainingBlock();
4346 fpAncestor = fpAncestor->GetParent())
4348 if (fpAncestor == prevBlock) {
4349 // We're still the best parent.
4350 keepPlaceholders.InsertFrame(nsnull, lastKP, f);
4351 keepOutOfFlows.InsertFrame(nsnull, lastKOOF, oof);
4352 lastKP = f;
4353 lastKOOF = oof;
4354 } else {
4355 // Just put it at the front of
4356 // fpAncestor->GetNextInFlow()'s lines.
4357 nsLineBox* newLine = aState.NewLineBox(f, 1, PR_FALSE);
4358 if (newLine) {
4359 nsBlockFrame* target =
4360 NS_STATIC_CAST(nsBlockFrame*, fpAncestor->GetNextInFlow());
4361 if (!target->mLines.empty()) {
4362 f->SetNextSibling(target->mLines.front()->mFirstChild);
4363 } else {
4364 f->SetNextSibling(nsnull);
4366 target->mLines.push_front(newLine);
4367 ReparentFrame(f, this, target);
4369 target->mFloats.InsertFrame(nsnull, nsnull, oof);
4370 ReparentFrame(oof, this, target);
4376 aState.FreeLineBox(line);
4379 if (lastFrame) {
4380 lastFrame->SetNextSibling(nsnull);
4385 // Now join the line lists into mLines
4386 if (overflowLines) {
4387 if (!overflowLines->empty()) {
4388 // Join the line lists
4389 if (! mLines.empty())
4391 // Remember to recompute the margins on the first line. This will
4392 // also recompute the correct deltaY if necessary.
4393 mLines.front()->MarkPreviousMarginDirty();
4394 // Join the sibling lists together
4395 nsIFrame* lastFrame = overflowLines->back()->LastChild();
4396 lastFrame->SetNextSibling(mLines.front()->mFirstChild);
4398 // Place overflow lines at the front of our line list
4399 mLines.splice(mLines.begin(), *overflowLines);
4400 NS_ASSERTION(overflowLines->empty(), "splice should empty list");
4402 delete overflowLines;
4404 if (ourOverflowLines) {
4405 if (!ourOverflowLines->empty()) {
4406 if (!mLines.empty()) {
4407 mLines.back()->LastChild()->
4408 SetNextSibling(ourOverflowLines->front()->mFirstChild);
4410 // append the overflow to mLines
4411 mLines.splice(mLines.end(), *ourOverflowLines);
4413 delete ourOverflowLines;
4416 // store the placeholders that we're keeping in our frame list
4417 if (keepPlaceholders.NotEmpty()) {
4418 keepPlaceholders.SortByContentOrder();
4419 nsLineBox* newLine = aState.NewLineBox(keepPlaceholders.FirstChild(),
4420 keepPlaceholders.GetLength(), PR_FALSE);
4421 if (newLine) {
4422 if (!mLines.empty()) {
4423 keepPlaceholders.LastChild()->SetNextSibling(mLines.front()->mFirstChild);
4425 mLines.push_front(newLine);
4428 // Put the placeholders' out of flows into the float list
4429 keepOutOfFlows.SortByContentOrder();
4430 mFloats.InsertFrames(nsnull, nsnull, keepOutOfFlows.FirstChild());
4433 return PR_TRUE;
4436 nsLineList*
4437 nsBlockFrame::GetOverflowLines() const
4439 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES)) {
4440 return nsnull;
4442 nsLineList* lines = NS_STATIC_CAST(nsLineList*,
4443 GetProperty(nsGkAtoms::overflowLinesProperty));
4444 NS_ASSERTION(lines && !lines->empty(),
4445 "value should always be stored and non-empty when state set");
4446 return lines;
4449 nsLineList*
4450 nsBlockFrame::RemoveOverflowLines()
4452 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES)) {
4453 return nsnull;
4455 nsLineList* lines = NS_STATIC_CAST(nsLineList*,
4456 UnsetProperty(nsGkAtoms::overflowLinesProperty));
4457 NS_ASSERTION(lines && !lines->empty(),
4458 "value should always be stored and non-empty when state set");
4459 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4460 return lines;
4463 // Destructor function for the overflowLines frame property
4464 static void
4465 DestroyOverflowLines(void* aFrame,
4466 nsIAtom* aPropertyName,
4467 void* aPropertyValue,
4468 void* aDtorData)
4470 if (aPropertyValue) {
4471 nsLineList* lines = NS_STATIC_CAST(nsLineList*, aPropertyValue);
4472 nsPresContext *context = NS_STATIC_CAST(nsPresContext*, aDtorData);
4473 nsLineBox::DeleteLineList(context, *lines);
4474 delete lines;
4478 // This takes ownership of aOverflowLines.
4479 // XXX We should allocate overflowLines from presShell arena!
4480 nsresult
4481 nsBlockFrame::SetOverflowLines(nsLineList* aOverflowLines)
4483 NS_ASSERTION(aOverflowLines, "null lines");
4484 NS_ASSERTION(!aOverflowLines->empty(), "empty lines");
4485 NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
4486 "Overwriting existing overflow lines");
4488 nsPresContext *presContext = PresContext();
4489 nsresult rv = presContext->PropertyTable()->
4490 SetProperty(this, nsGkAtoms::overflowLinesProperty, aOverflowLines,
4491 DestroyOverflowLines, presContext);
4492 // Verify that we didn't overwrite an existing overflow list
4493 NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow list");
4494 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4495 return rv;
4498 nsFrameList
4499 nsBlockFrame::GetOverflowOutOfFlows() const
4501 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4502 return nsFrameList();
4504 nsIFrame* result = NS_STATIC_CAST(nsIFrame*,
4505 GetProperty(nsGkAtoms::overflowOutOfFlowsProperty));
4506 NS_ASSERTION(result, "value should always be non-empty when state set");
4507 return nsFrameList(result);
4510 // This takes ownership of the frames
4511 void
4512 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList)
4514 if (aList.IsEmpty()) {
4515 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4516 return;
4518 nsIFrame* result = NS_STATIC_CAST(nsIFrame*,
4519 UnsetProperty(nsGkAtoms::overflowOutOfFlowsProperty));
4520 NS_ASSERTION(result, "value should always be non-empty when state set");
4521 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4522 } else {
4523 SetProperty(nsGkAtoms::overflowOutOfFlowsProperty,
4524 aList.FirstChild(), nsnull);
4525 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4529 nsFrameList*
4530 nsBlockFrame::GetOverflowPlaceholders() const
4532 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS)) {
4533 return nsnull;
4535 nsFrameList* result = NS_STATIC_CAST(nsFrameList*,
4536 GetProperty(nsGkAtoms::overflowPlaceholdersProperty));
4537 NS_ASSERTION(result, "value should always be non-empty when state set");
4538 return result;
4541 //////////////////////////////////////////////////////////////////////
4542 // Frame list manipulation routines
4544 nsIFrame*
4545 nsBlockFrame::LastChild()
4547 if (! mLines.empty()) {
4548 return mLines.back()->LastChild();
4550 return nsnull;
4553 NS_IMETHODIMP
4554 nsBlockFrame::AppendFrames(nsIAtom* aListName,
4555 nsIFrame* aFrameList)
4557 if (nsnull == aFrameList) {
4558 return NS_OK;
4560 if (aListName) {
4561 if (mAbsoluteContainer.GetChildListName() == aListName) {
4562 return mAbsoluteContainer.AppendFrames(this, aListName, aFrameList);
4564 else if (nsGkAtoms::floatList == aListName) {
4565 mFloats.AppendFrames(nsnull, aFrameList);
4566 return NS_OK;
4568 else {
4569 NS_ERROR("unexpected child list");
4570 return NS_ERROR_INVALID_ARG;
4574 // Find the proper last-child for where the append should go
4575 nsIFrame* lastKid = nsnull;
4576 nsLineBox* lastLine = mLines.empty() ? nsnull : mLines.back();
4577 if (lastLine) {
4578 lastKid = lastLine->LastChild();
4581 // Add frames after the last child
4582 #ifdef NOISY_REFLOW_REASON
4583 ListTag(stdout);
4584 printf(": append ");
4585 nsFrame::ListTag(stdout, aFrameList);
4586 if (lastKid) {
4587 printf(" after ");
4588 nsFrame::ListTag(stdout, lastKid);
4590 printf("\n");
4591 #endif
4592 nsresult rv = AddFrames(aFrameList, lastKid);
4593 if (NS_SUCCEEDED(rv)) {
4594 PresContext()->PresShell()->
4595 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4596 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4598 return rv;
4601 NS_IMETHODIMP
4602 nsBlockFrame::InsertFrames(nsIAtom* aListName,
4603 nsIFrame* aPrevFrame,
4604 nsIFrame* aFrameList)
4606 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
4607 "inserting after sibling frame with different parent");
4609 if (aListName) {
4610 if (mAbsoluteContainer.GetChildListName() == aListName) {
4611 return mAbsoluteContainer.InsertFrames(this, aListName, aPrevFrame,
4612 aFrameList);
4614 else if (nsGkAtoms::floatList == aListName) {
4615 mFloats.InsertFrames(this, aPrevFrame, aFrameList);
4616 return NS_OK;
4618 #ifdef IBMBIDI
4619 else if (nsGkAtoms::nextBidi == aListName) {}
4620 #endif // IBMBIDI
4621 else {
4622 NS_ERROR("unexpected child list");
4623 return NS_ERROR_INVALID_ARG;
4627 #ifdef NOISY_REFLOW_REASON
4628 ListTag(stdout);
4629 printf(": insert ");
4630 nsFrame::ListTag(stdout, aFrameList);
4631 if (aPrevFrame) {
4632 printf(" after ");
4633 nsFrame::ListTag(stdout, aPrevFrame);
4635 printf("\n");
4636 #endif
4637 nsresult rv = AddFrames(aFrameList, aPrevFrame);
4638 #ifdef IBMBIDI
4639 if (aListName != nsGkAtoms::nextBidi)
4640 #endif // IBMBIDI
4641 if (NS_SUCCEEDED(rv)) {
4642 PresContext()->PresShell()->
4643 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4644 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4646 return rv;
4649 static PRBool
4650 ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
4652 nsIAtom* type = aLastFrame->GetType();
4653 if (type == nsGkAtoms::brFrame)
4654 return PR_TRUE;
4655 if (type == nsGkAtoms::textFrame)
4656 return aLastFrame->HasTerminalNewline() &&
4657 aLastFrame->GetStyleText()->WhiteSpaceIsSignificant();
4658 if (type == nsGkAtoms::placeholderFrame)
4659 return IsContinuationPlaceholder(aLastFrame);
4660 return PR_FALSE;
4663 nsresult
4664 nsBlockFrame::AddFrames(nsIFrame* aFrameList,
4665 nsIFrame* aPrevSibling)
4667 // Clear our line cursor, since our lines may change.
4668 ClearLineCursor();
4670 if (nsnull == aFrameList) {
4671 return NS_OK;
4674 // If we're inserting at the beginning of our list and we have an
4675 // inside bullet, insert after that bullet.
4676 if (!aPrevSibling && mBullet && !HaveOutsideBullet()) {
4677 NS_ASSERTION(!nsFrameList(aFrameList).ContainsFrame(mBullet),
4678 "Trying to make mBullet prev sibling to itself");
4679 aPrevSibling = mBullet;
4682 nsIPresShell *presShell = PresContext()->PresShell();
4684 // Attempt to find the line that contains the previous sibling
4685 nsLineList::iterator prevSibLine = end_lines();
4686 PRInt32 prevSiblingIndex = -1;
4687 if (aPrevSibling) {
4688 // XXX_perf This is technically O(N^2) in some cases, but by using
4689 // RFind instead of Find, we make it O(N) in the most common case,
4690 // which is appending content.
4692 // Find the line that contains the previous sibling
4693 if (! nsLineBox::RFindLineContaining(aPrevSibling,
4694 begin_lines(), prevSibLine,
4695 &prevSiblingIndex)) {
4696 // Note: defensive code! RFindLineContaining must not return
4697 // false in this case, so if it does...
4698 NS_NOTREACHED("prev sibling not in line list");
4699 aPrevSibling = nsnull;
4700 prevSibLine = end_lines();
4704 // Find the frame following aPrevSibling so that we can join up the
4705 // two lists of frames.
4706 nsIFrame* prevSiblingNextFrame = nsnull;
4707 if (aPrevSibling) {
4708 prevSiblingNextFrame = aPrevSibling->GetNextSibling();
4710 // Split line containing aPrevSibling in two if the insertion
4711 // point is somewhere in the middle of the line.
4712 PRInt32 rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
4713 if (rem) {
4714 // Split the line in two where the frame(s) are being inserted.
4715 nsLineBox* line = NS_NewLineBox(presShell, prevSiblingNextFrame, rem, PR_FALSE);
4716 if (!line) {
4717 return NS_ERROR_OUT_OF_MEMORY;
4719 mLines.after_insert(prevSibLine, line);
4720 prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem);
4721 prevSibLine->MarkDirty();
4724 // Now (partially) join the sibling lists together
4725 aPrevSibling->SetNextSibling(aFrameList);
4727 else if (! mLines.empty()) {
4728 prevSiblingNextFrame = mLines.front()->mFirstChild;
4731 // Walk through the new frames being added and update the line data
4732 // structures to fit.
4733 nsIFrame* newFrame = aFrameList;
4734 while (newFrame) {
4735 PRBool isBlock = newFrame->GetStyleDisplay()->IsBlockOutside();
4737 // If the frame is a block frame, or if there is no previous line or if the
4738 // previous line is a block line we need to make a new line. We also make
4739 // a new line, as an optimization, in the three cases we know we'll need it:
4740 // if the previous line ended with a <br>, if it has significant whitespace and
4741 // ended in a newline, or if it contains continuation placeholders.
4742 if (isBlock || prevSibLine == end_lines() || prevSibLine->IsBlock() ||
4743 (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
4744 // Create a new line for the frame and add its line to the line
4745 // list.
4746 nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock);
4747 if (!line) {
4748 return NS_ERROR_OUT_OF_MEMORY;
4750 if (prevSibLine != end_lines()) {
4751 // Append new line after prevSibLine
4752 mLines.after_insert(prevSibLine, line);
4753 ++prevSibLine;
4755 else {
4756 // New line is going before the other lines
4757 mLines.push_front(line);
4758 prevSibLine = begin_lines();
4761 else {
4762 prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1);
4763 prevSibLine->MarkDirty();
4766 aPrevSibling = newFrame;
4767 newFrame = newFrame->GetNextSibling();
4769 if (prevSiblingNextFrame) {
4770 // Connect the last new frame to the remainder of the sibling list
4771 aPrevSibling->SetNextSibling(prevSiblingNextFrame);
4774 #ifdef DEBUG
4775 VerifyLines(PR_TRUE);
4776 #endif
4777 return NS_OK;
4780 nsBlockFrame::line_iterator
4781 nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
4782 // Find which line contains the float, so we can update
4783 // the float cache.
4784 line_iterator line = begin_lines(), line_end = end_lines();
4785 for ( ; line != line_end; ++line) {
4786 if (line->IsInline() && line->RemoveFloat(aFloat)) {
4787 break;
4791 // Unlink the placeholder *after* we searched the lines, because
4792 // the line search uses the placeholder relationship.
4793 nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager();
4794 nsPlaceholderFrame* placeholder = fm->GetPlaceholderFrameFor(aFloat);
4795 if (placeholder) {
4796 fm->UnregisterPlaceholderFrame(placeholder);
4797 placeholder->SetOutOfFlowFrame(nsnull);
4800 // Try to destroy if it's in mFloats.
4801 if (mFloats.DestroyFrame(aFloat)) {
4802 return line;
4805 // Try our overflow list
4807 nsAutoOOFFrameList oofs(this);
4808 if (oofs.mList.DestroyFrame(aFloat)) {
4809 return line_end;
4813 // If this is during reflow, it could be the out-of-flow frame for a
4814 // placeholder in our block reflow state's mOverflowPlaceholders. But that's
4815 // OK; it's not part of any child list, so we can just go ahead and delete it.
4816 aFloat->Destroy();
4817 return line_end;
4820 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
4822 nsLineList::iterator line = aBlock->begin_lines();
4823 nsLineList::iterator endLine = aBlock->end_lines();
4824 while (line != endLine) {
4825 if (line->IsBlock()) {
4826 nsIFrame* f = line->mFirstChild;
4827 void* bf;
4828 if (NS_SUCCEEDED(f->QueryInterface(kBlockFrameCID, &bf))) {
4829 MarkAllDescendantLinesDirty(NS_STATIC_CAST(nsBlockFrame*, f));
4832 line->MarkDirty();
4833 ++line;
4837 static void MarkSameSpaceManagerLinesDirty(nsBlockFrame* aBlock)
4839 nsBlockFrame* blockWithSpaceMgr = aBlock;
4840 while (!(blockWithSpaceMgr->GetStateBits() & NS_BLOCK_SPACE_MGR)) {
4841 void* bf;
4842 if (NS_FAILED(blockWithSpaceMgr->GetParent()->
4843 QueryInterface(kBlockFrameCID, &bf))) {
4844 break;
4846 blockWithSpaceMgr = NS_STATIC_CAST(nsBlockFrame*, blockWithSpaceMgr->GetParent());
4849 // Mark every line at and below the line where the float was
4850 // dirty, and mark their lines dirty too. We could probably do
4851 // something more efficient --- e.g., just dirty the lines that intersect
4852 // the float vertically.
4853 MarkAllDescendantLinesDirty(blockWithSpaceMgr);
4857 * Returns PR_TRUE if aFrame is a block that has one or more float children.
4859 static PRBool BlockHasAnyFloats(nsIFrame* aFrame)
4861 void* bf;
4862 if (NS_FAILED(aFrame->QueryInterface(kBlockFrameCID, &bf)))
4863 return PR_FALSE;
4864 nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, aFrame);
4865 if (block->GetFirstChild(nsGkAtoms::floatList))
4866 return PR_TRUE;
4868 nsLineList::iterator line = block->begin_lines();
4869 nsLineList::iterator endLine = block->end_lines();
4870 while (line != endLine) {
4871 if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
4872 return PR_TRUE;
4873 ++line;
4875 return PR_FALSE;
4878 NS_IMETHODIMP
4879 nsBlockFrame::RemoveFrame(nsIAtom* aListName,
4880 nsIFrame* aOldFrame)
4882 nsresult rv = NS_OK;
4884 #ifdef NOISY_REFLOW_REASON
4885 ListTag(stdout);
4886 printf(": remove ");
4887 nsFrame::ListTag(stdout, aOldFrame);
4888 printf("\n");
4889 #endif
4891 if (nsnull == aListName) {
4892 PRBool hasFloats = BlockHasAnyFloats(aOldFrame);
4893 rv = DoRemoveFrame(aOldFrame, PR_TRUE, PR_FALSE);
4894 if (hasFloats) {
4895 MarkSameSpaceManagerLinesDirty(this);
4898 else if (mAbsoluteContainer.GetChildListName() == aListName) {
4899 return mAbsoluteContainer.RemoveFrame(this, aListName, aOldFrame);
4901 else if (nsGkAtoms::floatList == aListName) {
4902 RemoveFloat(aOldFrame);
4903 MarkSameSpaceManagerLinesDirty(this);
4905 #ifdef IBMBIDI
4906 else if (nsGkAtoms::nextBidi == aListName) {
4907 // Skip the call to |FrameNeedsReflow| below by returning now.
4908 return DoRemoveFrame(aOldFrame, PR_TRUE, PR_FALSE);
4910 #endif // IBMBIDI
4911 else {
4912 NS_ERROR("unexpected child list");
4913 rv = NS_ERROR_INVALID_ARG;
4916 if (NS_SUCCEEDED(rv)) {
4917 PresContext()->PresShell()->
4918 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4919 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4921 return rv;
4924 void
4925 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
4927 // First remove aFrame's next in flow
4928 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
4929 if (nextInFlow) {
4930 nsBlockFrame::DoRemoveOutOfFlowFrame(nextInFlow);
4932 // Now remove aFrame
4933 const nsStyleDisplay* display = aFrame->GetStyleDisplay();
4935 // The containing block is always the parent of aFrame.
4936 nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
4937 // Remove aFrame from the appropriate list.
4938 if (display->IsAbsolutelyPositioned()) {
4939 block->mAbsoluteContainer.RemoveFrame(block,
4940 block->mAbsoluteContainer.GetChildListName(),
4941 aFrame);
4942 aFrame->Destroy();
4944 else {
4945 // This also destroys the frame.
4946 block->RemoveFloat(aFrame);
4951 * This helps us iterate over the list of all normal + overflow lines
4953 void
4954 nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
4955 nsLineList::iterator* aEndIterator,
4956 PRBool* aInOverflowLines) {
4957 if (*aIterator == *aEndIterator) {
4958 if (!*aInOverflowLines) {
4959 *aInOverflowLines = PR_TRUE;
4960 // Try the overflow lines
4961 nsLineList* overflowLines = GetOverflowLines();
4962 if (overflowLines) {
4963 *aIterator = overflowLines->begin();
4964 *aEndIterator = overflowLines->end();
4970 static nsresult RemoveBlockChild(nsIFrame* aFrame, PRBool aDestroyFrames)
4972 if (!aFrame)
4973 return NS_OK;
4975 nsBlockFrame* nextBlock = NS_STATIC_CAST(nsBlockFrame*, aFrame->GetParent());
4976 NS_ASSERTION(nextBlock->GetType() == nsGkAtoms::blockFrame ||
4977 nextBlock->GetType() == nsGkAtoms::areaFrame,
4978 "Our child's continuation's parent is not a block?");
4979 return nextBlock->DoRemoveFrame(aFrame, aDestroyFrames);
4982 // This function removes aDeletedFrame and all its continuations. It
4983 // is optimized for deleting a whole series of frames. The easy
4984 // implementation would invoke itself recursively on
4985 // aDeletedFrame->GetNextContinuation, then locate the line containing
4986 // aDeletedFrame and remove aDeletedFrame from that line. But here we
4987 // start by locating aDeletedFrame and then scanning from that point
4988 // on looking for continuations.
4989 nsresult
4990 nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRBool aDestroyFrames,
4991 PRBool aRemoveOnlyFluidContinuations)
4993 // Clear our line cursor, since our lines may change.
4994 ClearLineCursor();
4996 if (aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
4997 NS_ASSERTION(aDestroyFrames, "We can't not destroy out of flows");
4998 DoRemoveOutOfFlowFrame(aDeletedFrame);
4999 return NS_OK;
5002 nsPresContext* presContext = PresContext();
5003 nsIPresShell* presShell = presContext->PresShell();
5005 PRBool isPlaceholder = nsGkAtoms::placeholderFrame == aDeletedFrame->GetType();
5006 if (isPlaceholder) {
5007 nsFrameList* overflowPlaceholders = GetOverflowPlaceholders();
5008 if (overflowPlaceholders && overflowPlaceholders->RemoveFrame(aDeletedFrame)) {
5009 nsIFrame* nif = aDeletedFrame->GetNextInFlow();
5010 if (aDestroyFrames) {
5011 aDeletedFrame->Destroy();
5012 } else {
5013 aDeletedFrame->SetNextSibling(nsnull);
5015 return RemoveBlockChild(nif, aDestroyFrames);
5019 // Find the line and the previous sibling that contains
5020 // deletedFrame; we also find the pointer to the line.
5021 nsLineList::iterator line = mLines.begin(),
5022 line_end = mLines.end();
5023 PRBool searchingOverflowList = PR_FALSE;
5024 nsIFrame* prevSibling = nsnull;
5025 // Make sure we look in the overflow lines even if the normal line
5026 // list is empty
5027 TryAllLines(&line, &line_end, &searchingOverflowList);
5028 while (line != line_end) {
5029 nsIFrame* frame = line->mFirstChild;
5030 PRInt32 n = line->GetChildCount();
5031 while (--n >= 0) {
5032 if (frame == aDeletedFrame) {
5033 goto found_frame;
5035 prevSibling = frame;
5036 frame = frame->GetNextSibling();
5038 ++line;
5039 TryAllLines(&line, &line_end, &searchingOverflowList);
5041 found_frame:;
5042 if (line == line_end) {
5043 NS_ERROR("can't find deleted frame in lines");
5044 return NS_ERROR_FAILURE;
5047 if (prevSibling && !prevSibling->GetNextSibling()) {
5048 // We must have found the first frame in the overflow line list. So
5049 // there is no prevSibling
5050 prevSibling = nsnull;
5052 NS_ASSERTION(!prevSibling || prevSibling->GetNextSibling() == aDeletedFrame, "bad prevSibling");
5054 while ((line != line_end) && (nsnull != aDeletedFrame)) {
5055 NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
5056 NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
5058 // If the frame being deleted is the last one on the line then
5059 // optimize away the line->Contains(next-in-flow) call below.
5060 PRBool isLastFrameOnLine = (1 == line->GetChildCount() ||
5061 line->LastChild() == aDeletedFrame);
5063 // Remove aDeletedFrame from the line
5064 nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
5065 if (line->mFirstChild == aDeletedFrame) {
5066 // We should be setting this to null if aDeletedFrame
5067 // is the only frame on the line. HOWEVER in that case
5068 // we will be removing the line anyway, see below.
5069 line->mFirstChild = nextFrame;
5072 // Hmm, this won't do anything if we're removing a frame in the first
5073 // overflow line... Hopefully doesn't matter
5074 --line;
5075 if (line != line_end && !line->IsBlock()) {
5076 // Since we just removed a frame that follows some inline
5077 // frames, we need to reflow the previous line.
5078 line->MarkDirty();
5080 ++line;
5082 // Take aDeletedFrame out of the sibling list. Note that
5083 // prevSibling will only be nsnull when we are deleting the very
5084 // first frame in the main or overflow list.
5085 if (prevSibling) {
5086 prevSibling->SetNextSibling(nextFrame);
5089 // Update the child count of the line to be accurate
5090 PRInt32 lineChildCount = line->GetChildCount();
5091 lineChildCount--;
5092 line->SetChildCount(lineChildCount);
5094 // Destroy frame; capture its next continuation first in case we need
5095 // to destroy that too.
5096 nsIFrame* deletedNextContinuation = aRemoveOnlyFluidContinuations ?
5097 aDeletedFrame->GetNextInFlow() : aDeletedFrame->GetNextContinuation();
5098 #ifdef NOISY_REMOVE_FRAME
5099 printf("DoRemoveFrame: %s line=%p frame=",
5100 searchingOverflowList?"overflow":"normal", line.get());
5101 nsFrame::ListTag(stdout, aDeletedFrame);
5102 printf(" prevSibling=%p deletedNextContinuation=%p\n", prevSibling, deletedNextContinuation);
5103 #endif
5105 if (aDestroyFrames) {
5106 aDeletedFrame->Destroy();
5107 } else {
5108 aDeletedFrame->SetNextSibling(nsnull);
5110 aDeletedFrame = deletedNextContinuation;
5112 PRBool haveAdvancedToNextLine = PR_FALSE;
5113 // If line is empty, remove it now.
5114 if (0 == lineChildCount) {
5115 #ifdef NOISY_REMOVE_FRAME
5116 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
5117 searchingOverflowList?"overflow":"normal", line.get());
5118 #endif
5119 nsLineBox *cur = line;
5120 if (!searchingOverflowList) {
5121 line = mLines.erase(line);
5122 // Invalidate the space taken up by the line.
5123 // XXX We need to do this if we're removing a frame as a result of
5124 // a call to RemoveFrame(), but we may not need to do this in all
5125 // cases...
5126 nsRect lineCombinedArea(cur->GetCombinedArea());
5127 #ifdef NOISY_BLOCK_INVALIDATE
5128 printf("%p invalidate 10 (%d, %d, %d, %d)\n",
5129 this, lineCombinedArea.x, lineCombinedArea.y,
5130 lineCombinedArea.width, lineCombinedArea.height);
5131 #endif
5132 Invalidate(lineCombinedArea);
5133 } else {
5134 nsLineList* lineList = RemoveOverflowLines();
5135 line = lineList->erase(line);
5136 if (!lineList->empty()) {
5137 SetOverflowLines(lineList);
5140 cur->Destroy(presShell);
5142 // If we're removing a line, ReflowDirtyLines isn't going to
5143 // know that it needs to slide lines unless something is marked
5144 // dirty. So mark the previous margin of the next line dirty if
5145 // there is one.
5146 if (line != line_end) {
5147 line->MarkPreviousMarginDirty();
5149 haveAdvancedToNextLine = PR_TRUE;
5150 } else {
5151 // Make the line that just lost a frame dirty, and advance to
5152 // the next line.
5153 if (!deletedNextContinuation || isLastFrameOnLine ||
5154 !line->Contains(deletedNextContinuation)) {
5155 line->MarkDirty();
5156 ++line;
5157 haveAdvancedToNextLine = PR_TRUE;
5161 if (deletedNextContinuation) {
5162 // Continuations for placeholder frames don't always appear in
5163 // consecutive lines. So for placeholders, just continue the slow easy way.
5164 if (isPlaceholder) {
5165 return RemoveBlockChild(deletedNextContinuation, aDestroyFrames);
5168 // See if we should keep looking in the current flow's line list.
5169 if (deletedNextContinuation->GetParent() != this) {
5170 // The deceased frames continuation is not a child of the
5171 // current block. So break out of the loop so that we advance
5172 // to the next parent.
5173 break;
5176 // If we advanced to the next line then check if we should switch to the
5177 // overflow line list.
5178 if (haveAdvancedToNextLine) {
5179 if (line != line_end && !searchingOverflowList &&
5180 !line->Contains(deletedNextContinuation)) {
5181 // We have advanced to the next *normal* line but the next-in-flow
5182 // is not there - force a switch to the overflow line list.
5183 line = line_end;
5186 PRBool wasSearchingOverflowList = searchingOverflowList;
5187 TryAllLines(&line, &line_end, &searchingOverflowList);
5188 if (NS_UNLIKELY(searchingOverflowList && !wasSearchingOverflowList &&
5189 prevSibling)) {
5190 // We switched to the overflow line list and we have a prev sibling
5191 // (in the main list), in this case we don't want to pick up any
5192 // sibling list from the deceased frames (bug 344557).
5193 prevSibling->SetNextSibling(nsnull);
5194 prevSibling = nsnull;
5196 #ifdef NOISY_REMOVE_FRAME
5197 printf("DoRemoveFrame: now on %s line=%p prevSibling=%p\n",
5198 searchingOverflowList?"overflow":"normal", line.get(),
5199 prevSibling);
5200 #endif
5205 #ifdef DEBUG
5206 VerifyLines(PR_TRUE);
5207 #endif
5209 // Advance to next flow block if the frame has more continuations
5210 return RemoveBlockChild(aDeletedFrame, aDestroyFrames);
5213 void
5214 nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
5215 nsIFrame* aNextInFlow)
5217 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
5218 NS_PRECONDITION(prevInFlow, "bad next-in-flow");
5219 NS_PRECONDITION(IsChild(aNextInFlow), "bad geometric parent");
5221 DoRemoveFrame(aNextInFlow);
5224 ////////////////////////////////////////////////////////////////////////
5225 // Float support
5227 nsresult
5228 nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
5229 nsPlaceholderFrame* aPlaceholder,
5230 nsMargin& aFloatMargin,
5231 nsReflowStatus& aReflowStatus)
5233 // Reflow the float.
5234 nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
5235 aReflowStatus = NS_FRAME_COMPLETE;
5237 #ifdef NOISY_FLOAT
5238 printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
5239 aPlaceholder->GetOutOfFlowFrame(), this,
5240 aState.mAvailSpaceRect.x, aState.mAvailSpaceRect.y,
5241 aState.mAvailSpaceRect.width, aState.mAvailSpaceRect.height
5243 #endif
5245 // Compute the available width. By default, assume the width of the
5246 // containing block.
5247 nscoord availWidth;
5248 const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay();
5250 if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
5251 eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
5252 availWidth = aState.mContentArea.width;
5254 else {
5255 // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
5256 // give tables only the available space
5257 // if they can shrink we may not be constrained to place
5258 // them in the next line
5259 availWidth = aState.mAvailSpaceRect.width;
5260 // round down to twips per pixel so that we fit
5261 // needed when prev. float has procentage width
5262 // (maybe is a table flaw that makes table chose to round up
5263 // but I don't want to change that, too risky)
5264 nscoord twp = nsPresContext::CSSPixelsToAppUnits(1);
5265 availWidth -= availWidth % twp;
5268 // aState.mY is relative to the border-top, make it relative to the content-top
5269 nscoord contentYOffset = aState.mY - aState.BorderPadding().top;
5270 nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height
5271 ? NS_UNCONSTRAINEDSIZE
5272 : PR_MAX(0, aState.mContentArea.height - contentYOffset);
5274 nsRect availSpace(aState.BorderPadding().left,
5275 aState.BorderPadding().top,
5276 availWidth, availHeight);
5278 // construct the html reflow state for the float. ReflowBlock will
5279 // initialize it.
5280 nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
5281 floatFrame,
5282 nsSize(availSpace.width, availSpace.height));
5284 // Setup a block reflow state to reflow the float.
5285 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
5287 // Reflow the float
5288 PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
5290 nsIFrame* clearanceFrame = nsnull;
5291 nsresult rv;
5292 do {
5293 nsCollapsingMargin margin;
5294 PRBool mayNeedRetry = PR_FALSE;
5295 floatRS.mDiscoveredClearance = nsnull;
5296 // Only first in flow gets a top margin.
5297 if (!floatFrame->GetPrevInFlow()) {
5298 nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
5299 clearanceFrame, &mayNeedRetry);
5301 if (mayNeedRetry && !clearanceFrame) {
5302 floatRS.mDiscoveredClearance = &clearanceFrame;
5303 // We don't need to push the space manager state because the the block has its own
5304 // space manager that will be destroyed and recreated
5308 nsMargin offsets; // Don't bother returning this to the caller; it's stored
5309 // on a frame property anyawy
5310 rv = brc.ReflowBlock(availSpace, PR_TRUE, margin,
5311 0, isAdjacentWithTop,
5312 offsets, floatRS,
5313 aReflowStatus);
5314 } while (NS_SUCCEEDED(rv) && clearanceFrame);
5316 // An incomplete reflow status means we should split the float
5317 // if the height is constrained (bug 145305).
5318 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight))
5319 aReflowStatus = NS_FRAME_COMPLETE;
5321 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
5322 // Float is now complete, so delete the placeholder's next in
5323 // flows, if any; their floats (which are this float's continuations)
5324 // have already been deleted.
5325 // XXX won't this be done later in nsLineLayout::ReflowFrame anyway??
5326 nsIFrame* nextInFlow = aPlaceholder->GetNextInFlow();
5327 if (nextInFlow) {
5328 NS_STATIC_CAST(nsHTMLContainerFrame*, nextInFlow->GetParent())
5329 ->DeleteNextInFlowChild(aState.mPresContext, nextInFlow);
5330 // that takes care of all subsequent nextinflows too
5333 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
5334 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
5337 if (floatFrame->GetType() == nsGkAtoms::letterFrame) {
5338 // We never split floating first letters; an incomplete state for
5339 // such frames simply means that there is more content to be
5340 // reflowed on the line.
5341 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
5342 aReflowStatus = NS_FRAME_COMPLETE;
5345 if (NS_FAILED(rv)) {
5346 return rv;
5349 // Capture the margin information for the caller
5350 const nsMargin& m = brc.GetMargin();
5351 aFloatMargin.top = brc.GetTopMargin();
5352 aFloatMargin.right = m.right;
5353 // Only last in flows get a bottom margin
5354 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
5355 brc.GetCarriedOutBottomMargin().Include(m.bottom);
5357 aFloatMargin.bottom = brc.GetCarriedOutBottomMargin().get();
5358 aFloatMargin.left = m.left;
5360 const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
5362 // Set the rect, make sure the view is properly sized and positioned,
5363 // and tell the frame we're done reflowing it
5364 // XXXldb This seems like the wrong place to be doing this -- shouldn't
5365 // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
5366 // we've positioned the float, and shouldn't we be doing the equivalent
5367 // of |::PlaceFrameView| here?
5368 floatFrame->SetSize(nsSize(metrics.width, metrics.height));
5369 if (floatFrame->HasView()) {
5370 nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, floatFrame,
5371 floatFrame->GetView(),
5372 &metrics.mOverflowArea,
5373 NS_FRAME_NO_MOVE_VIEW);
5375 // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
5376 floatFrame->DidReflow(aState.mPresContext, &floatRS,
5377 NS_FRAME_REFLOW_FINISHED);
5379 #ifdef NOISY_FLOAT
5380 printf("end ReflowFloat %p, sized to %d,%d\n",
5381 floatFrame, metrics.width, metrics.height);
5382 #endif
5384 // If the placeholder was continued and its first-in-flow was followed by a
5385 // <BR>, then cache the <BR>'s break type in aState.mFloatBreakType so that
5386 // the next frame after the placeholder can combine that break type with its own
5387 nsIFrame* prevPlaceholder = aPlaceholder->GetPrevInFlow();
5388 if (prevPlaceholder) {
5389 // the break occurs only after the last continued placeholder
5390 PRBool lastPlaceholder = PR_TRUE;
5391 nsIFrame* next = aPlaceholder->GetNextSibling();
5392 if (next) {
5393 if (nsGkAtoms::placeholderFrame == next->GetType()) {
5394 lastPlaceholder = PR_FALSE;
5397 if (lastPlaceholder) {
5398 // get the containing block of prevPlaceholder which is our prev-in-flow
5399 if (GetPrevInFlow()) {
5400 // get the break type of the last line in mPrevInFlow
5401 line_iterator endLine = --((nsBlockFrame*)GetPrevInFlow())->end_lines();
5402 if (endLine->HasFloatBreakAfter()) {
5403 aState.mFloatBreakType = endLine->GetBreakTypeAfter();
5406 else NS_ASSERTION(PR_FALSE, "no prev in flow");
5409 return NS_OK;
5412 //////////////////////////////////////////////////////////////////////
5413 // Painting, event handling
5415 PRIntn
5416 nsBlockFrame::GetSkipSides() const
5418 PRIntn skip = 0;
5419 if (nsnull != GetPrevInFlow()) {
5420 skip |= 1 << NS_SIDE_TOP;
5422 if (nsnull != GetNextInFlow()) {
5423 skip |= 1 << NS_SIDE_BOTTOM;
5425 return skip;
5428 #ifdef DEBUG
5429 static void ComputeCombinedArea(nsLineList& aLines,
5430 nscoord aWidth, nscoord aHeight,
5431 nsRect& aResult)
5433 nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
5434 for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
5435 line != line_end;
5436 ++line) {
5437 // Compute min and max x/y values for the reflowed frame's
5438 // combined areas
5439 nsRect lineCombinedArea(line->GetCombinedArea());
5440 nscoord x = lineCombinedArea.x;
5441 nscoord y = lineCombinedArea.y;
5442 nscoord xmost = x + lineCombinedArea.width;
5443 nscoord ymost = y + lineCombinedArea.height;
5444 if (x < xa) {
5445 xa = x;
5447 if (xmost > xb) {
5448 xb = xmost;
5450 if (y < ya) {
5451 ya = y;
5453 if (ymost > yb) {
5454 yb = ymost;
5458 aResult.x = xa;
5459 aResult.y = ya;
5460 aResult.width = xb - xa;
5461 aResult.height = yb - ya;
5463 #endif
5465 PRBool
5466 nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
5468 nsCOMPtr<nsIDOMHTMLHtmlElement> html(do_QueryInterface(mContent));
5469 nsCOMPtr<nsIDOMHTMLBodyElement> body(do_QueryInterface(mContent));
5470 if (html || body)
5471 return PR_TRUE;
5473 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
5474 PRBool visible;
5475 nsresult rv = aSelection->ContainsNode(node, PR_TRUE, &visible);
5476 return NS_SUCCEEDED(rv) && visible;
5479 /* virtual */ void
5480 nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
5481 nsPoint aPt,
5482 nsLineBox* aLine,
5483 nscolor aColor,
5484 nscoord aOffset,
5485 nscoord aAscent,
5486 nscoord aSize)
5488 aRenderingContext.SetColor(aColor);
5489 NS_ASSERTION(!aLine->IsBlock(), "Why did we ask for decorations on a block?");
5491 nscoord start = aLine->mBounds.x;
5492 nscoord width = aLine->mBounds.width;
5494 if (aLine == begin_lines().get()) {
5495 // Adjust for the text-indent. See similar code in
5496 // nsLineLayout::BeginLineReflow.
5497 nscoord indent = 0;
5498 const nsStyleText* styleText = GetStyleText();
5499 nsStyleUnit unit = styleText->mTextIndent.GetUnit();
5500 if (eStyleUnit_Coord == unit) {
5501 indent = styleText->mTextIndent.GetCoordValue();
5502 } else if (eStyleUnit_Percent == unit) {
5503 // It's a percentage of the containing block width.
5504 nsIFrame* containingBlock =
5505 nsHTMLReflowState::GetContainingBlockFor(this);
5506 NS_ASSERTION(containingBlock, "Must have containing block!");
5507 indent = nscoord(styleText->mTextIndent.GetPercentValue() *
5508 containingBlock->GetContentRect().width);
5511 // Adjust the start position and the width of the decoration by the
5512 // value of the indent. Note that indent can be negative; that's OK.
5513 // It'll just increase the width (which can also happen to be
5514 // negative!).
5515 start += indent;
5516 width -= indent;
5519 // Only paint if we have a positive width
5520 if (width > 0) {
5521 aRenderingContext.FillRect(start + aPt.x,
5522 aLine->mBounds.y + aLine->GetAscent() - aOffset + aPt.y,
5523 width, aSize);
5527 #ifdef DEBUG
5528 static void DebugOutputDrawLine(PRInt32 aDepth, nsLineBox* aLine, PRBool aDrawn) {
5529 if (nsBlockFrame::gNoisyDamageRepair) {
5530 nsFrame::IndentBy(stdout, aDepth+1);
5531 nsRect lineArea = aLine->GetCombinedArea();
5532 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
5533 aDrawn ? "draw" : "skip",
5534 NS_STATIC_CAST(void*, aLine),
5535 aLine->mBounds.x, aLine->mBounds.y,
5536 aLine->mBounds.width, aLine->mBounds.height,
5537 lineArea.x, lineArea.y,
5538 lineArea.width, lineArea.height);
5541 #endif
5543 static nsresult
5544 DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
5545 const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
5546 PRInt32 aDepth, PRInt32& aDrawnLines, const nsDisplayListSet& aLists,
5547 nsBlockFrame* aFrame) {
5548 // If the line's combined area (which includes child frames that
5549 // stick outside of the line's bounding box or our bounding box)
5550 // intersects the dirty rect then paint the line.
5551 PRBool intersect = aLineArea.Intersects(aDirtyRect);
5552 #ifdef DEBUG
5553 if (nsBlockFrame::gLamePaintMetrics) {
5554 aDrawnLines++;
5556 DebugOutputDrawLine(aDepth, aLine.get(), intersect);
5557 #endif
5558 // The line might contain a placeholder for a visible out-of-flow, in which
5559 // case we need to descend into it. If there is such a placeholder, we will
5560 // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
5561 if (!intersect &&
5562 !(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
5563 return NS_OK;
5565 nsresult rv;
5566 nsDisplayList aboveTextDecorations;
5567 PRBool lineInline = aLine->IsInline();
5568 if (lineInline) {
5569 // Display the text-decoration for the hypothetical anonymous inline box
5570 // that wraps these inlines
5571 rv = aFrame->DisplayTextDecorations(aBuilder, aLists.Content(),
5572 &aboveTextDecorations, aLine);
5573 NS_ENSURE_SUCCESS(rv, rv);
5576 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
5577 // Inline-level child backgrounds go on the regular child content list.
5578 nsDisplayListSet childLists(aLists,
5579 lineInline ? aLists.Content() : aLists.BlockBorderBackgrounds());
5580 nsIFrame* kid = aLine->mFirstChild;
5581 PRInt32 n = aLine->GetChildCount();
5582 while (--n >= 0) {
5583 rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, childLists,
5584 lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0);
5585 NS_ENSURE_SUCCESS(rv, rv);
5586 kid = kid->GetNextSibling();
5589 aLists.Content()->AppendToTop(&aboveTextDecorations);
5590 return NS_OK;
5593 NS_IMETHODIMP
5594 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
5595 const nsRect& aDirtyRect,
5596 const nsDisplayListSet& aLists)
5598 PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
5599 PRInt32 depth = 0;
5600 #ifdef DEBUG
5601 if (gNoisyDamageRepair) {
5602 depth = GetDepth();
5603 nsRect ca;
5604 ::ComputeCombinedArea(mLines, mRect.width, mRect.height, ca);
5605 nsFrame::IndentBy(stdout, depth);
5606 ListTag(stdout);
5607 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
5608 mRect.x, mRect.y, mRect.width, mRect.height,
5609 aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
5610 ca.x, ca.y, ca.width, ca.height);
5612 PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
5613 if (gLamePaintMetrics) {
5614 start = PR_Now();
5615 drawnLines = 0;
5617 #endif
5619 DisplayBorderBackgroundOutline(aBuilder, aLists);
5621 aBuilder->MarkFramesForDisplayList(this, mFloats.FirstChild(), aDirtyRect);
5622 aBuilder->MarkFramesForDisplayList(this, mAbsoluteContainer.GetFirstChild(), aDirtyRect);
5624 // Don't use the line cursor if we might have a descendant placeholder ...
5625 // it might skip lines that contain placeholders but don't themselves
5626 // intersect with the dirty area.
5627 nsLineBox* cursor = GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
5628 ? nsnull : GetFirstLineContaining(aDirtyRect.y);
5629 line_iterator line_end = end_lines();
5630 nsresult rv = NS_OK;
5632 if (cursor) {
5633 for (line_iterator line = mLines.begin(cursor);
5634 line != line_end;
5635 ++line) {
5636 nsRect lineArea = line->GetCombinedArea();
5637 if (!lineArea.IsEmpty()) {
5638 // Because we have a cursor, the combinedArea.ys are non-decreasing.
5639 // Once we've passed aDirtyRect.YMost(), we can never see it again.
5640 if (lineArea.y >= aDirtyRect.YMost()) {
5641 break;
5643 rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
5644 aLists, this);
5645 if (NS_FAILED(rv))
5646 break;
5649 } else {
5650 PRBool nonDecreasingYs = PR_TRUE;
5651 PRInt32 lineCount = 0;
5652 nscoord lastY = PR_INT32_MIN;
5653 nscoord lastYMost = PR_INT32_MIN;
5654 for (line_iterator line = begin_lines();
5655 line != line_end;
5656 ++line) {
5657 nsRect lineArea = line->GetCombinedArea();
5658 rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
5659 aLists, this);
5660 if (NS_FAILED(rv))
5661 break;
5662 if (!lineArea.IsEmpty()) {
5663 if (lineArea.y < lastY
5664 || lineArea.YMost() < lastYMost) {
5665 nonDecreasingYs = PR_FALSE;
5667 lastY = lineArea.y;
5668 lastYMost = lineArea.YMost();
5670 lineCount++;
5673 if (NS_SUCCEEDED(rv) && nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
5674 SetupLineCursor();
5678 if (NS_SUCCEEDED(rv) && (nsnull != mBullet) && HaveOutsideBullet()) {
5679 // Display outside bullets manually
5680 rv = BuildDisplayListForChild(aBuilder, mBullet, aDirtyRect, aLists);
5683 #ifdef DEBUG
5684 if (gLamePaintMetrics) {
5685 PRTime end = PR_Now();
5687 PRInt32 numLines = mLines.size();
5688 if (!numLines) numLines = 1;
5689 PRTime lines, deltaPerLine, delta;
5690 LL_I2L(lines, numLines);
5691 LL_SUB(delta, end, start);
5692 LL_DIV(deltaPerLine, delta, lines);
5694 ListTag(stdout);
5695 char buf[400];
5696 PR_snprintf(buf, sizeof(buf),
5697 ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
5698 delta, deltaPerLine,
5699 numLines, drawnLines, numLines - drawnLines);
5700 printf("%s\n", buf);
5702 #endif
5704 return rv;
5707 #ifdef ACCESSIBILITY
5708 NS_IMETHODIMP nsBlockFrame::GetAccessible(nsIAccessible** aAccessible)
5710 *aAccessible = nsnull;
5711 nsCOMPtr<nsIAccessibilityService> accService =
5712 do_GetService("@mozilla.org/accessibilityService;1");
5713 NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
5715 // block frame may be for <hr>
5716 if (mContent->Tag() == nsGkAtoms::hr) {
5717 return accService->CreateHTMLHRAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
5720 nsPresContext *aPresContext = PresContext();
5721 if (!mBullet || !aPresContext) {
5722 if (!mContent || !mContent->GetParent()) {
5723 // Don't create accessible objects for the root content node, they are redundant with
5724 // the nsDocAccessible object created with the document node
5725 return NS_ERROR_FAILURE;
5728 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
5729 do_QueryInterface(mContent->GetDocument());
5730 if (htmlDoc) {
5731 nsCOMPtr<nsIDOMHTMLElement> body;
5732 htmlDoc->GetBody(getter_AddRefs(body));
5733 if (SameCOMIdentity(body, mContent)) {
5734 // Don't create accessible objects for the body, they are redundant with
5735 // the nsDocAccessible object created with the document node
5736 return NS_ERROR_FAILURE;
5740 // Not a bullet, treat as normal HTML container
5741 return accService->CreateHyperTextAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
5744 // Create special list bullet accessible
5745 const nsStyleList* myList = GetStyleList();
5746 nsAutoString bulletText;
5747 if (myList->mListStyleImage || myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC ||
5748 myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE ||
5749 myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
5750 bulletText.Assign(PRUnichar(0x2022));; // Unicode bullet character
5752 else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
5753 mBullet->GetListItemText(*myList, bulletText);
5756 return accService->CreateHTMLLIAccessible(NS_STATIC_CAST(nsIFrame*, this),
5757 NS_STATIC_CAST(nsIFrame*, mBullet),
5758 bulletText,
5759 aAccessible);
5761 #endif
5763 void nsBlockFrame::ClearLineCursor() {
5764 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
5765 return;
5768 UnsetProperty(nsGkAtoms::lineCursorProperty);
5769 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
5772 void nsBlockFrame::SetupLineCursor() {
5773 if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
5774 || mLines.empty()) {
5775 return;
5778 SetProperty(nsGkAtoms::lineCursorProperty,
5779 mLines.front(), nsnull);
5780 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
5783 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
5784 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
5785 return nsnull;
5788 nsLineBox* property = NS_STATIC_CAST(nsLineBox*,
5789 GetProperty(nsGkAtoms::lineCursorProperty));
5790 line_iterator cursor = mLines.begin(property);
5791 nsRect cursorArea = cursor->GetCombinedArea();
5793 while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
5794 && cursor != mLines.front()) {
5795 cursor = cursor.prev();
5796 cursorArea = cursor->GetCombinedArea();
5798 while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
5799 && cursor != mLines.back()) {
5800 cursor = cursor.next();
5801 cursorArea = cursor->GetCombinedArea();
5804 if (cursor.get() != property) {
5805 SetProperty(nsGkAtoms::lineCursorProperty,
5806 cursor.get(), nsnull);
5809 return cursor.get();
5812 /* virtual */ void
5813 nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
5815 // See if the child is absolutely positioned
5816 if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
5817 aChild->GetStyleDisplay()->IsAbsolutelyPositioned()) {
5818 // do nothing
5819 } else if (aChild == mBullet && HaveOutsideBullet()) {
5820 // The bullet lives in the first line, unless the first line has
5821 // height 0 and there is a second line, in which case it lives
5822 // in the second line.
5823 line_iterator bulletLine = begin_lines();
5824 if (bulletLine != end_lines() && bulletLine->mBounds.height == 0 &&
5825 bulletLine != mLines.back()) {
5826 bulletLine = bulletLine.next();
5829 if (bulletLine != end_lines()) {
5830 MarkLineDirty(bulletLine);
5832 // otherwise we have an empty line list, and ReflowDirtyLines
5833 // will handle reflowing the bullet.
5834 } else {
5835 // Mark the line containing the child frame dirty.
5836 line_iterator fline = FindLineFor(aChild);
5837 if (fline != end_lines())
5838 MarkLineDirty(fline);
5841 nsBlockFrameSuper::ChildIsDirty(aChild);
5844 //////////////////////////////////////////////////////////////////////
5845 // Start Debugging
5847 #ifdef NS_DEBUG
5848 static PRBool
5849 InLineList(nsLineList& aLines, nsIFrame* aFrame)
5851 for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
5852 line != line_end;
5853 ++line) {
5854 nsIFrame* frame = line->mFirstChild;
5855 PRInt32 n = line->GetChildCount();
5856 while (--n >= 0) {
5857 if (frame == aFrame) {
5858 return PR_TRUE;
5860 frame = frame->GetNextSibling();
5863 return PR_FALSE;
5866 static PRBool
5867 InSiblingList(nsLineList& aLines, nsIFrame* aFrame)
5869 if (! aLines.empty()) {
5870 nsIFrame* frame = aLines.front()->mFirstChild;
5871 while (frame) {
5872 if (frame == aFrame) {
5873 return PR_TRUE;
5875 frame = frame->GetNextSibling();
5878 return PR_FALSE;
5881 PRBool
5882 nsBlockFrame::IsChild(nsIFrame* aFrame)
5884 // Continued out-of-flows don't satisfy InLineList(), continued out-of-flows
5885 // and placeholders don't satisfy InSiblingList().
5886 PRBool skipLineList = PR_FALSE;
5887 PRBool skipSiblingList = PR_FALSE;
5888 nsIFrame* prevInFlow = aFrame->GetPrevInFlow();
5889 PRBool isPlaceholder = nsGkAtoms::placeholderFrame == aFrame->GetType();
5890 if (prevInFlow) {
5891 nsFrameState state = aFrame->GetStateBits();
5892 skipLineList = (state & NS_FRAME_OUT_OF_FLOW);
5893 skipSiblingList = isPlaceholder || (state & NS_FRAME_OUT_OF_FLOW);
5896 if (isPlaceholder) {
5897 nsFrameList* overflowPlaceholders = GetOverflowPlaceholders();
5898 if (overflowPlaceholders && overflowPlaceholders->ContainsFrame(aFrame)) {
5899 return PR_TRUE;
5903 if (aFrame->GetParent() != (nsIFrame*)this) {
5904 return PR_FALSE;
5906 if ((skipLineList || InLineList(mLines, aFrame)) &&
5907 (skipSiblingList || InSiblingList(mLines, aFrame))) {
5908 return PR_TRUE;
5910 nsLineList* overflowLines = GetOverflowLines();
5911 if (overflowLines && (skipLineList || InLineList(*overflowLines, aFrame)) &&
5912 (skipSiblingList || InSiblingList(*overflowLines, aFrame))) {
5913 return PR_TRUE;
5915 return PR_FALSE;
5918 NS_IMETHODIMP
5919 nsBlockFrame::VerifyTree() const
5921 // XXX rewrite this
5922 return NS_OK;
5924 #endif
5926 // End Debugging
5927 //////////////////////////////////////////////////////////////////////
5929 NS_IMETHODIMP
5930 nsBlockFrame::Init(nsIContent* aContent,
5931 nsIFrame* aParent,
5932 nsIFrame* aPrevInFlow)
5934 if (aPrevInFlow) {
5935 // Copy over the block/area frame type flags
5936 nsBlockFrame* blockFrame = (nsBlockFrame*)aPrevInFlow;
5938 SetFlags(blockFrame->mState &
5939 (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET));
5942 nsresult rv = nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow);
5944 return rv;
5947 NS_IMETHODIMP
5948 nsBlockFrame::SetInitialChildList(nsIAtom* aListName,
5949 nsIFrame* aChildList)
5951 nsresult rv = NS_OK;
5953 if (mAbsoluteContainer.GetChildListName() == aListName) {
5954 mAbsoluteContainer.SetInitialChildList(this, aListName, aChildList);
5956 else if (nsGkAtoms::floatList == aListName) {
5957 mFloats.SetFrames(aChildList);
5959 else {
5960 nsPresContext* presContext = PresContext();
5962 #ifdef DEBUG
5963 // The only times a block that is an anonymous box is allowed to have a
5964 // first-letter frame are when it's the block inside a non-anonymous cell,
5965 // the block inside a fieldset, a scrolled content block, or a column
5966 // content block. Note that this means that blocks which are the anonymous
5967 // block in {ib} splits do NOT get first-letter frames. Also, a block that
5968 // has a previous continuation can't have a first letter frame.
5969 nsIAtom *pseudo = GetStyleContext()->GetPseudoType();
5970 PRBool haveFirstLetterStyle =
5971 !GetPrevContinuation() &&
5972 (!pseudo ||
5973 (pseudo == nsCSSAnonBoxes::cellContent &&
5974 mParent->GetStyleContext()->GetPseudoType() == nsnull) ||
5975 pseudo == nsCSSAnonBoxes::fieldsetContent ||
5976 pseudo == nsCSSAnonBoxes::scrolledContent ||
5977 pseudo == nsCSSAnonBoxes::columnContent) &&
5978 nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nsnull;
5979 NS_ASSERTION(haveFirstLetterStyle ==
5980 ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
5981 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
5982 #endif
5984 rv = AddFrames(aChildList, nsnull);
5985 if (NS_FAILED(rv)) {
5986 return rv;
5989 // Create list bullet if this is a list-item. Note that this is done
5990 // here so that RenumberLists will work (it needs the bullets to
5991 // store the bullet numbers).
5992 const nsStyleDisplay* styleDisplay = GetStyleDisplay();
5993 if ((nsnull == GetPrevInFlow()) &&
5994 (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) &&
5995 (nsnull == mBullet)) {
5996 // Resolve style for the bullet frame
5997 const nsStyleList* styleList = GetStyleList();
5998 nsIAtom *pseudoElement;
5999 switch (styleList->mListStyleType) {
6000 case NS_STYLE_LIST_STYLE_DISC:
6001 case NS_STYLE_LIST_STYLE_CIRCLE:
6002 case NS_STYLE_LIST_STYLE_SQUARE:
6003 pseudoElement = nsCSSPseudoElements::mozListBullet;
6004 break;
6005 default:
6006 pseudoElement = nsCSSPseudoElements::mozListNumber;
6007 break;
6010 nsIPresShell *shell = presContext->PresShell();
6012 nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
6013 ResolvePseudoStyleFor(mContent, pseudoElement, mStyleContext);
6015 // Create bullet frame
6016 nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
6017 if (nsnull == bullet) {
6018 return NS_ERROR_OUT_OF_MEMORY;
6020 bullet->Init(mContent, this, nsnull);
6022 // If the list bullet frame should be positioned inside then add
6023 // it to the flow now.
6024 if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
6025 styleList->mListStylePosition) {
6026 AddFrames(bullet, nsnull);
6027 mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
6029 else {
6030 mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
6033 mBullet = bullet;
6037 return NS_OK;
6040 // static
6041 PRBool
6042 nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
6044 nsIContent* content = aFrame->GetContent();
6045 if (!content || !content->IsNodeOfType(nsINode::eHTML))
6046 return PR_FALSE;
6048 nsIAtom *localName = content->NodeInfo()->NameAtom();
6049 return localName == nsGkAtoms::ol ||
6050 localName == nsGkAtoms::ul ||
6051 localName == nsGkAtoms::dir ||
6052 localName == nsGkAtoms::menu;
6055 PRBool
6056 nsBlockFrame::RenumberLists(nsPresContext* aPresContext)
6058 if (!FrameStartsCounterScope(this)) {
6059 // If this frame doesn't start a counter scope then we don't need
6060 // to renumber child list items.
6061 return PR_FALSE;
6064 // Setup initial list ordinal value
6065 // XXX Map html's start property to counter-reset style
6066 PRInt32 ordinal = 1;
6068 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
6070 if (hc) {
6071 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
6072 if (attr && attr->Type() == nsAttrValue::eInteger) {
6073 ordinal = attr->GetIntegerValue();
6077 // Get to first-in-flow
6078 nsBlockFrame* block = (nsBlockFrame*) GetFirstInFlow();
6079 return RenumberListsInBlock(aPresContext, block, &ordinal, 0);
6082 PRBool
6083 nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext,
6084 nsBlockFrame* aBlockFrame,
6085 PRInt32* aOrdinal,
6086 PRInt32 aDepth)
6088 PRBool renumberedABullet = PR_FALSE;
6090 while (nsnull != aBlockFrame) {
6091 // Examine each line in the block
6092 for (line_iterator line = aBlockFrame->begin_lines(),
6093 line_end = aBlockFrame->end_lines();
6094 line != line_end;
6095 ++line) {
6096 nsIFrame* kid = line->mFirstChild;
6097 PRInt32 n = line->GetChildCount();
6098 while (--n >= 0) {
6099 PRBool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal, aDepth);
6100 if (kidRenumberedABullet) {
6101 line->MarkDirty();
6102 renumberedABullet = PR_TRUE;
6104 kid = kid->GetNextSibling();
6108 // Advance to the next continuation
6109 aBlockFrame = NS_STATIC_CAST(nsBlockFrame*, aBlockFrame->GetNextInFlow());
6112 return renumberedABullet;
6115 PRBool
6116 nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
6117 nsIFrame* aKid,
6118 PRInt32* aOrdinal,
6119 PRInt32 aDepth)
6121 NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
6123 // add in a sanity check for absurdly deep frame trees. See bug 42138
6124 if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
6125 return PR_FALSE;
6127 PRBool kidRenumberedABullet = PR_FALSE;
6129 // if the frame is a placeholder, then get the out of flow frame
6130 nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid);
6132 // drill down through any wrappers to the real frame
6133 kid = kid->GetContentInsertionFrame();
6135 // If the frame is a list-item and the frame implements our
6136 // block frame API then get its bullet and set the list item
6137 // ordinal.
6138 const nsStyleDisplay* display = kid->GetStyleDisplay();
6139 if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
6140 // Make certain that the frame is a block frame in case
6141 // something foreign has crept in.
6142 nsBlockFrame* listItem;
6143 nsresult rv = kid->QueryInterface(kBlockFrameCID, (void**)&listItem);
6144 if (NS_SUCCEEDED(rv)) {
6145 if (nsnull != listItem->mBullet) {
6146 PRBool changed;
6147 *aOrdinal = listItem->mBullet->SetListItemOrdinal(*aOrdinal,
6148 &changed);
6149 if (changed) {
6150 kidRenumberedABullet = PR_TRUE;
6152 // Invalidate the bullet content area since it may look different now
6153 nsRect damageRect(nsPoint(0, 0), listItem->mBullet->GetSize());
6154 listItem->mBullet->Invalidate(damageRect);
6158 // XXX temporary? if the list-item has child list-items they
6159 // should be numbered too; especially since the list-item is
6160 // itself (ASSUMED!) not to be a counter-resetter.
6161 PRBool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal, aDepth + 1);
6162 if (meToo) {
6163 kidRenumberedABullet = PR_TRUE;
6167 else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
6168 if (FrameStartsCounterScope(kid)) {
6169 // Don't bother recursing into a block frame that is a new
6170 // counter scope. Any list-items in there will be handled by
6171 // it.
6173 else {
6174 // If the display=block element is a block frame then go ahead
6175 // and recurse into it, as it might have child list-items.
6176 nsBlockFrame* kidBlock;
6177 nsresult rv = kid->QueryInterface(kBlockFrameCID, (void**) &kidBlock);
6178 if (NS_SUCCEEDED(rv)) {
6179 kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock, aOrdinal, aDepth + 1);
6183 return kidRenumberedABullet;
6186 void
6187 nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
6188 nsHTMLReflowMetrics& aMetrics)
6190 const nsHTMLReflowState &rs = aState.mReflowState;
6192 // Reflow the bullet now
6193 nsSize availSize;
6194 // Make up a width since it doesn't really matter (XXX).
6195 availSize.width = rs.ComputedWidth();
6196 availSize.height = NS_UNCONSTRAINEDSIZE;
6198 // Get the reason right.
6199 // XXXwaterson Should this look just like the logic in
6200 // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
6201 nsHTMLReflowState reflowState(aState.mPresContext, rs,
6202 mBullet, availSize);
6203 nsReflowStatus status;
6204 mBullet->WillReflow(aState.mPresContext);
6205 mBullet->Reflow(aState.mPresContext, aMetrics, reflowState, status);
6207 // Place the bullet now; use its right margin to distance it
6208 // from the rest of the frames in the line
6209 nscoord x =
6210 #ifdef IBMBIDI
6211 (NS_STYLE_DIRECTION_RTL == GetStyleVisibility()->mDirection)
6212 // According to the CSS2 spec, section 12.6.1, outside marker box
6213 // is distanced from the associated principal box's border edge.
6214 // |rs.availableWidth| reflects exactly a border edge: it includes
6215 // border, padding, and content area, without margins.
6216 ? rs.ComputedWidth() + rs.mComputedBorderPadding.LeftRight() +
6217 reflowState.mComputedMargin.left :
6218 #endif
6219 - reflowState.mComputedMargin.right - aMetrics.width;
6221 // Approximate the bullets position; vertical alignment will provide
6222 // the final vertical location.
6223 const nsMargin& bp = aState.BorderPadding();
6224 nscoord y = bp.top;
6225 mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
6226 mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED);
6229 // This is used to scan frames for any float placeholders, add their
6230 // floats to the list represented by aHead and aTail, and remove the
6231 // floats from whatever list they might be in. We don't search descendants
6232 // that are float containing blocks. The floats must be children of 'this'.
6233 void nsBlockFrame::CollectFloats(nsIFrame* aFrame, nsFrameList& aList, nsIFrame** aTail,
6234 PRBool aFromOverflow, PRBool aCollectSiblings) {
6235 while (aFrame) {
6236 // Don't descend into float containing blocks.
6237 if (!aFrame->IsFloatContainingBlock()) {
6238 nsIFrame *outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
6239 if (outOfFlowFrame) {
6240 // Make sure that its parent is us. Otherwise we don't want
6241 // to mess around with it because it belongs to someone
6242 // else. I think this could happen if the overflow lines
6243 // contain a block descendant which owns its own floats.
6244 NS_ASSERTION(outOfFlowFrame->GetParent() == this,
6245 "Out of flow frame doesn't have the expected parent");
6246 if (aFromOverflow) {
6247 nsAutoOOFFrameList oofs(this);
6248 oofs.mList.RemoveFrame(outOfFlowFrame);
6249 } else {
6250 mFloats.RemoveFrame(outOfFlowFrame);
6252 aList.InsertFrame(nsnull, *aTail, outOfFlowFrame);
6253 *aTail = outOfFlowFrame;
6256 CollectFloats(aFrame->GetFirstChild(nsnull), aList, aTail, aFromOverflow,
6257 PR_TRUE);
6259 if (!aCollectSiblings)
6260 break;
6261 aFrame = aFrame->GetNextSibling();
6265 void
6266 nsBlockFrame::CheckFloats(nsBlockReflowState& aState)
6268 #ifdef DEBUG
6269 // Check that the float list is what we would have built
6270 nsAutoVoidArray lineFloats;
6271 for (line_iterator line = begin_lines(), line_end = end_lines();
6272 line != line_end; ++line) {
6273 if (line->HasFloats()) {
6274 nsFloatCache* fc = line->GetFirstFloat();
6275 while (fc) {
6276 nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
6277 lineFloats.AppendElement(floatFrame);
6278 fc = fc->Next();
6283 nsAutoVoidArray storedFloats;
6284 PRBool equal = PR_TRUE;
6285 PRInt32 i = 0;
6286 for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
6287 storedFloats.AppendElement(f);
6288 if (i < lineFloats.Count() && lineFloats.ElementAt(i) != f) {
6289 equal = PR_FALSE;
6291 ++i;
6294 if (!equal || lineFloats.Count() != storedFloats.Count()) {
6295 NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
6296 #if defined(DEBUG_roc)
6297 nsIFrameDebug::RootFrameList(PresContext(), stdout, 0);
6298 for (i = 0; i < lineFloats.Count(); ++i) {
6299 printf("Line float: %p\n", lineFloats.ElementAt(i));
6301 for (i = 0; i < storedFloats.Count(); ++i) {
6302 printf("Stored float: %p\n", storedFloats.ElementAt(i));
6304 #endif
6306 #endif
6308 nsFrameList oofs = GetOverflowOutOfFlows();
6309 if (oofs.NotEmpty()) {
6310 // Floats that were pushed should be removed from our space
6311 // manager. Otherwise the space manager's YMost or XMost might
6312 // be larger than necessary, causing this block to get an
6313 // incorrect desired height (or width). Some of these floats
6314 // may not actually have been added to the space manager because
6315 // they weren't reflowed before being pushed; that's OK,
6316 // RemoveRegions will ignore them. It is safe to do this here
6317 // because we know from here on the space manager will only be
6318 // used for its XMost and YMost, not to place new floats and
6319 // lines.
6320 aState.mSpaceManager->RemoveTrailingRegions(oofs.FirstChild());
6324 /* static */
6325 PRBool
6326 nsBlockFrame::BlockIsMarginRoot(nsIFrame* aBlock)
6328 NS_PRECONDITION(aBlock, "Must have a frame");
6329 #ifdef DEBUG
6330 nsBlockFrame* blockFrame;
6331 aBlock->QueryInterface(kBlockFrameCID, (void**)&blockFrame);
6332 NS_ASSERTION(blockFrame, "aBlock must be a block");
6333 #endif
6335 nsIFrame* parent = aBlock->GetParent();
6336 return (aBlock->GetStateBits() & NS_BLOCK_MARGIN_ROOT) ||
6337 (parent && !parent->IsFloatContainingBlock() &&
6338 parent->GetType() != nsGkAtoms::columnSetFrame);
6341 /* static */
6342 PRBool
6343 nsBlockFrame::BlockNeedsSpaceManager(nsIFrame* aBlock)
6345 NS_PRECONDITION(aBlock, "Must have a frame");
6346 #ifdef DEBUG
6347 nsBlockFrame* blockFrame;
6348 aBlock->QueryInterface(kBlockFrameCID, (void**)&blockFrame);
6349 NS_ASSERTION(blockFrame, "aBlock must be a block");
6350 #endif
6352 nsIFrame* parent = aBlock->GetParent();
6353 return (aBlock->GetStateBits() & NS_BLOCK_SPACE_MGR) ||
6354 (parent && !parent->IsFloatContainingBlock());
6357 // XXX keep the text-run data in the first-in-flow of the block
6359 #ifdef IBMBIDI
6360 nsresult
6361 nsBlockFrame::ResolveBidi()
6363 nsPresContext* presContext = PresContext();
6364 if (!presContext->BidiEnabled()) {
6365 return NS_OK;
6368 if (mLines.empty()) {
6369 return NS_OK;
6372 nsBidiPresUtils* bidiUtils = presContext->GetBidiUtils();
6373 if (!bidiUtils)
6374 return NS_OK;
6376 return bidiUtils->Resolve(presContext, this,
6377 mLines.front()->mFirstChild,
6378 IsVisualFormControl(presContext));
6381 PRBool
6382 nsBlockFrame::IsVisualFormControl(nsPresContext* aPresContext)
6384 // This check is only necessary on visual bidi pages, because most
6385 // visual pages use logical order for form controls so that they will
6386 // display correctly on native widgets in OSs with Bidi support.
6387 // So bail out if the page is not visual, or if the pref is
6388 // set to use visual order on forms in visual pages
6389 if (!aPresContext->IsVisualMode()) {
6390 return PR_FALSE;
6393 PRUint32 options = aPresContext->GetBidi();
6394 if (IBMBIDI_CONTROLSTEXTMODE_LOGICAL != GET_BIDI_OPTION_CONTROLSTEXTMODE(options)) {
6395 return PR_FALSE;
6398 nsIContent* content = GetContent();
6399 for ( ; content; content = content->GetParent()) {
6400 if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
6401 return PR_TRUE;
6405 return PR_FALSE;
6407 #endif
6409 #ifdef DEBUG
6410 void
6411 nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
6413 if (!gVerifyLines) {
6414 return;
6416 if (mLines.empty()) {
6417 return;
6420 // Add up the counts on each line. Also validate that IsFirstLine is
6421 // set properly.
6422 PRInt32 count = 0;
6423 PRBool seenBlock = PR_FALSE;
6424 line_iterator line, line_end;
6425 for (line = begin_lines(), line_end = end_lines();
6426 line != line_end;
6427 ++line) {
6428 if (aFinalCheckOK) {
6429 NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
6430 if (line->IsBlock()) {
6431 seenBlock = PR_TRUE;
6433 if (line->IsBlock()) {
6434 NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
6437 count += line->GetChildCount();
6440 // Then count the frames
6441 PRInt32 frameCount = 0;
6442 nsIFrame* frame = mLines.front()->mFirstChild;
6443 while (frame) {
6444 frameCount++;
6445 frame = frame->GetNextSibling();
6447 NS_ASSERTION(count == frameCount, "bad line list");
6449 // Next: test that each line has right number of frames on it
6450 for (line = begin_lines(), line_end = end_lines();
6451 line != line_end;
6453 count = line->GetChildCount();
6454 frame = line->mFirstChild;
6455 while (--count >= 0) {
6456 frame = frame->GetNextSibling();
6458 ++line;
6459 if ((line != line_end) && (0 != line->GetChildCount())) {
6460 NS_ASSERTION(frame == line->mFirstChild, "bad line list");
6465 // Its possible that a frame can have some frames on an overflow
6466 // list. But its never possible for multiple frames to have overflow
6467 // lists. Check that this fact is actually true.
6468 void
6469 nsBlockFrame::VerifyOverflowSituation()
6471 nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow();
6472 while (nsnull != flow) {
6473 nsLineList* overflowLines = GetOverflowLines();
6474 if (nsnull != overflowLines) {
6475 NS_ASSERTION(! overflowLines->empty(), "should not be empty if present");
6476 NS_ASSERTION(overflowLines->front()->mFirstChild, "bad overflow list");
6478 flow = (nsBlockFrame*) flow->GetNextInFlow();
6482 PRInt32
6483 nsBlockFrame::GetDepth() const
6485 PRInt32 depth = 0;
6486 nsIFrame* parent = mParent;
6487 while (parent) {
6488 parent = parent->GetParent();
6489 depth++;
6491 return depth;
6493 #endif