1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla Communicator client code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Steve Clark <buster@netscape.com>
25 * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 * L. David Baron <dbaron@dbaron.org>
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
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"
65 #include "nsIFontMetrics.h"
66 #include "nsHTMLParts.h"
67 #include "nsGkAtoms.h"
68 #include "nsIDOMEvent.h"
69 #include "nsGenericHTMLElement.h"
71 #include "nsStyleChangeList.h"
72 #include "nsFrameSelection.h"
73 #include "nsSpaceManager.h"
74 #include "nsIntervalSet.h"
77 #include "nsGUIEvent.h"
78 #include "nsLayoutErrors.h"
79 #include "nsAutoPtr.h"
80 #include "nsIServiceManager.h"
81 #include "nsIScrollableFrame.h"
83 #include "nsIDOMHTMLDocument.h"
84 #include "nsIAccessibilityService.h"
86 #include "nsLayoutUtils.h"
87 #include "nsBoxLayoutState.h"
88 #include "nsDisplayList.h"
89 #include "nsContentErrors.h"
90 #include "nsCSSAnonBoxes.h"
93 #include "nsBidiPresUtils.h"
96 #include "nsIDOMHTMLBodyElement.h"
97 #include "nsIDOMHTMLHtmlElement.h"
99 static const int MIN_LINES_NEEDING_CURSOR
= 20;
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
{
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]))
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");
150 nsBlockFrame::InitDebugFlags()
152 static PRBool firstTime
= PR_TRUE
;
154 firstTime
= PR_FALSE
;
155 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
157 PRBool error
= PR_FALSE
;
159 char* cm
= PL_strchr(flags
, ',');
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
);
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
200 const char* nsBlockFrame::kReflowCommandType
[] = {
209 #ifdef REALLY_NOISY_FIRST_LINE
211 DumpStyleGeneaology(nsIFrame
* aFrame
, const char* gap
)
214 nsFrame::ListTag(stdout
, aFrame
);
216 nsStyleContext
* sc
= aFrame
->GetStyleContext();
217 while (nsnull
!= sc
) {
220 psc
= sc
->GetParent();
227 #ifdef REFLOW_STATUS_COVERAGE
229 RecordReflowStatus(PRBool aChildIsBlock
, nsReflowStatus aFrameReflowStatus
)
231 static PRUint32 record
[2];
234 // 1: child-is-inline
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
)) {
244 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus
)) {
251 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus
)) {
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]);
266 //----------------------------------------------------------------------
268 const nsIID kBlockFrameCID
= NS_BLOCK_FRAME_CID
;
271 NS_NewBlockFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRUint32 aFlags
)
273 nsBlockFrame
* it
= new (aPresShell
) nsBlockFrame(aContext
);
275 it
->SetFlags(aFlags
);
280 nsBlockFrame::~nsBlockFrame()
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()) {
309 mFloats
.DestroyFrames();
311 nsPresContext
* presContext
= PresContext();
313 nsLineBox::DeleteLineList(presContext
, mLines
);
315 // destroy overflow lines now
316 nsLineList
* overflowLines
= RemoveOverflowLines();
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();
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));
338 if (aIID
.Equals(NS_GET_IID(nsILineIterator
)) ||
339 aIID
.Equals(NS_GET_IID(nsILineIteratorNavigator
)))
341 nsLineIterator
* it
= new nsLineIterator
;
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
);
354 *aInstancePtr
= NS_STATIC_CAST(void*,
355 NS_STATIC_CAST(nsILineIteratorNavigator
*, it
));
358 return nsBlockFrameSuper::QueryInterface(aIID
, aInstancePtr
);
362 nsBlockFrame::GetSplittableType() const
364 return NS_FRAME_SPLITTABLE_NON_RECTANGULAR
;
369 nsBlockFrame::List(FILE* out
, PRInt32 aIndent
) const
371 IndentBy(out
, aIndent
);
373 #ifdef DEBUG_waterson
374 fprintf(out
, " [parent=%p]", mParent
);
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
);
394 fprintf(out
, " [state=%08x]", mState
);
396 nsBlockFrame
* f
= NS_CONST_CAST(nsBlockFrame
*, this);
397 nsRect
* overflowArea
= f
->GetOverflowAreaProperty(PR_FALSE
);
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();
415 fprintf(out
, " sc=%p(i=%d,b=%d)",
416 NS_STATIC_CAST(void*, mStyleContext
), numInlineLines
, numBlockLines
);
417 nsIAtom
* pseudoTag
= mStyleContext
->GetPseudoType();
419 nsAutoString atomString
;
420 pseudoTag
->ToString(atomString
);
421 fprintf(out
, " pst=%s",
422 NS_LossyConvertUTF16toASCII(atomString
).get());
429 if (! mLines
.empty()) {
430 for (const_line_iterator line
= begin_lines(), line_end
= end_lines();
434 line
->List(out
, aIndent
);
438 nsIAtom
* listName
= nsnull
;
439 PRInt32 listIndex
= 0;
441 listName
= GetAdditionalChildListName(listIndex
++);
442 if (nsnull
== listName
) {
445 nsIFrame
* kid
= GetFirstChild(listName
);
447 IndentBy(out
, aIndent
);
449 if (nsnull
!= listName
) {
450 listName
->ToString(tmp
);
451 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
455 nsIFrameDebug
* frameDebug
;
457 if (NS_SUCCEEDED(CallQueryInterface(kid
, &frameDebug
))) {
458 frameDebug
->List(out
, aIndent
+ 1);
460 kid
= kid
->GetNextSibling();
462 IndentBy(out
, aIndent
);
468 IndentBy(out
, aIndent
);
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
;
483 nsBlockFrame::GetFrameName(nsAString
& aResult
) const
485 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult
);
490 nsBlockFrame::GetType() const
492 return nsGkAtoms::blockFrame
;
496 nsBlockFrame::InvalidateInternal(const nsRect
& aDamageRect
,
497 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
500 // Optimize by suppressing invalidation of areas that are clipped out
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
508 if (r
.IntersectRect(aDamageRect
, absPosClipRect
- nsPoint(aX
, aY
))) {
509 nsBlockFrameSuper::InvalidateInternal(r
, aX
, aY
, this, aImmediate
);
514 nsBlockFrameSuper::InvalidateInternal(aDamageRect
, aX
, aY
, this, aImmediate
);
518 nsBlockFrame::GetBaseline() const
520 NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty");
522 if (nsLayoutUtils::GetLastLineBaseline(this, &result
))
524 return nsFrame::GetBaseline();
527 /////////////////////////////////////////////////////////////////////////////
528 // Child frame enumeration
531 nsBlockFrame::GetFirstChild(nsIAtom
* aListName
) const
533 if (mAbsoluteContainer
.GetChildListName() == aListName
) {
534 nsIFrame
* result
= nsnull
;
535 mAbsoluteContainer
.FirstChild(this, aListName
, &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()) {
560 nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex
) const
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();
579 nsBlockFrame::IsContainingBlock() const
585 nsBlockFrame::IsFloatContainingBlock() const
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 //////////////////////////////////////////////////////////////////////
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
)
632 if (gNoisyIntrinsic
) {
633 IndentBy(stdout
, gNoiseIndent
);
635 printf(": GetMinWidth\n");
637 AutoNoisyIndenter
indent(gNoisyIntrinsic
);
644 InlineMinWidthData data
;
645 for (line_iterator line
= begin_lines(), line_end
= end_lines();
646 line
!= line_end
; ++line
)
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
);
657 if (line
->IsBlock()) {
658 data
.ForceBreak(aRenderingContext
);
659 data
.currentLine
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
660 line
->mFirstChild
, nsLayoutUtils::MIN_WIDTH
);
661 data
.ForceBreak(aRenderingContext
);
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
);
677 if (gNoisyIntrinsic
) {
678 IndentBy(stdout
, gNoiseIndent
);
679 printf("min: [prevLines=%d currentLine=%d]\n",
680 data
.prevLines
, data
.currentLine
);
684 data
.ForceBreak(aRenderingContext
);
686 mMinWidth
= data
.prevLines
;
690 /* virtual */ nscoord
691 nsBlockFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
693 DISPLAY_PREF_WIDTH(this, mPrefWidth
);
694 if (mPrefWidth
!= NS_INTRINSIC_WIDTH_UNKNOWN
)
698 if (gNoisyIntrinsic
) {
699 IndentBy(stdout
, gNoiseIndent
);
701 printf(": GetPrefWidth\n");
703 AutoNoisyIndenter
indent(gNoisyIntrinsic
);
710 InlinePrefWidthData data
;
711 for (line_iterator line
= begin_lines(), line_end
= end_lines();
712 line
!= line_end
; ++line
)
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
);
723 if (line
->IsBlock()) {
724 data
.ForceBreak(aRenderingContext
);
725 data
.currentLine
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
726 line
->mFirstChild
, nsLayoutUtils::PREF_WIDTH
);
727 data
.ForceBreak(aRenderingContext
);
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
);
743 if (gNoisyIntrinsic
) {
744 IndentBy(stdout
, gNoiseIndent
);
745 printf("pref: [prevLines=%d currentLine=%d]\n",
746 data
.prevLines
, data
.currentLine
);
750 data
.ForceBreak(aRenderingContext
);
752 mPrefWidth
= data
.prevLines
;
757 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState
& aReflowState
,
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
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);
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
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());
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
);
845 IndentBy(stdout
, gNoiseIndent
);
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
) {
856 ctc
= nsLineBox::GetCtorCount();
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.
878 if (IsFrameTreeTooDeep(aReflowState
, aMetrics
)) {
881 extern char* nsPresShell_ReflowStackPointerTop
;
883 char* newsp
= (char*) &marker
;
884 printf("XXX: frame tree is too deep; approx stack size = %d\n",
885 nsPresShell_ReflowStackPointerTop
- newsp
);
888 aStatus
= NS_FRAME_COMPLETE
;
892 PRBool marginRoot
= BlockIsMarginRoot(this);
893 nsBlockReflowState
state(aReflowState
, aPresContext
, this, aMetrics
,
894 marginRoot
, marginRoot
, needSpaceManager
);
900 if (RenumberLists(aPresContext
)) {
901 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
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
;
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
;
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.
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
);
969 if (!state
.mOverflowPlaceholders
.IsEmpty()) {
970 state
.mOverflowPlaceholders
.SortByContentOrder();
971 PRInt32 numOverflowPlace
= state
.mOverflowPlaceholders
.GetLength();
973 state
.NewLineBox(state
.mOverflowPlaceholders
.FirstChild(),
974 numOverflowPlace
, PR_FALSE
);
976 nsLineList
* overflowLines
= GetOverflowLines();
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.
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
);
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());
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
;
1026 ListTag(stdout
); printf(": block is not complete\n");
1033 // Place the "marker" (bullet) frame if it is placed next to a block
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
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() &&
1047 mLines
.front()->IsBlock() ||
1048 0 == mLines
.front()->mBounds
.height
)) {
1049 // Reflow the bullet
1050 nsHTMLReflowMetrics metrics
;
1051 ReflowBullet(state
, metrics
);
1054 if (!nsLayoutUtils::GetFirstLineBaseline(this, &baseline
)) {
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
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();
1080 nsHTMLReflowState
& reflowState
= (nsHTMLReflowState
&)aReflowState
;
1081 rv
= SetProperty(nsGkAtoms::spaceManagerProperty
,
1082 reflowState
.mSpaceManager
,
1083 nsnull
/* should be nsSpaceManagerDestroyer*/);
1085 autoSpaceManager
.DebugOrphanSpaceManager();
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
1100 if (mAbsoluteContainer
.HasAbsoluteFrames()) {
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
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
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
,
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
1140 if (needSpaceManager
)
1141 state
.mSpaceManager
= nsnull
;
1143 aStatus
= state
.mReflowStatus
;
1147 IndentBy(stdout
, gNoiseIndent
);
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
);
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
);
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
);
1183 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
1188 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
1190 line_iterator begin
= begin_lines();
1191 line_iterator line
= end_lines();
1194 if (begin
== line
) {
1198 if (line
->mBounds
.height
!= 0 || !line
->CachedIsEmpty()) {
1201 if (line
->HasClearance()) {
1209 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState
& aReflowState
,
1210 nsBlockReflowState
& aState
,
1211 nsHTMLReflowMetrics
& aMetrics
)
1213 const nsMargin
& borderPadding
= aState
.BorderPadding();
1214 #ifdef NOISY_FINAL_SIZE
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
);
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
;
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
;
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();
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.
1317 if (aReflowState
.mSpaceManager
->YMost(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
;
1336 if (CRAZY_WIDTH(aMetrics
.width
) || CRAZY_HEIGHT(aMetrics
.height
)) {
1338 printf(": WARNING: desired:%d,%d\n", aMetrics
.width
, aMetrics
.height
);
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();
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?
1364 area
.UnionRect(area
, mBullet
->GetRect());
1367 #ifdef NOISY_COMBINED_AREA
1369 printf(": ca=%d,%d,%d,%d\n", area
.x
, area
.y
, area
.width
, area
.height
);
1372 aMetrics
.mOverflowArea
= area
;
1376 nsBlockFrame::MarkLineDirty(line_iterator aLine
)
1382 IndentBy(stdout
, gNoiseIndent
);
1384 printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine
.get()));
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();
1397 IndentBy(stdout
, gNoiseIndent
);
1399 printf(": mark prev-line %p dirty\n",
1400 NS_STATIC_CAST(void*, aLine
.prev().get()));
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
1427 GetStylePadding()->mPadding
.GetLeftUnit() != eStyleUnit_Percent
;
1430 if (gDisableResizeOpt
) {
1431 tryAndSkipLines
= PR_FALSE
;
1434 if (!tryAndSkipLines
) {
1435 IndentBy(stdout
, gNoiseIndent
);
1437 printf(": marking all lines dirty: availWidth=%d textAlign=%d\n",
1438 aState
.mReflowState
.availableWidth
,
1439 styleText
->mTextAlign
);
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");
1453 IndentBy(stdout
, gNoiseIndent
);
1455 printf(": trying to avoid marking all lines dirty\n");
1459 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1463 // We let child blocks make their own decisions the same
1465 if (line
->IsBlock() ||
1466 line
->HasFloats() ||
1467 (line
!= mLines
.back() && !line
->HasBreakAfter()) ||
1468 line
->ResizeReflowOptimizationDisabled() ||
1469 line
->IsImpactedByFloat() ||
1470 (line
->mBounds
.XMost() > newAvailWidth
)) {
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 ");
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());
1497 // Mark everything dirty
1498 for (line_iterator line
= begin_lines(), line_end
= end_lines();
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
))
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();
1528 if (aFrame
== fc
->mPlaceholder
->GetOutOfFlowFrame())
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).
1549 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState
& aState
,
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
)) {
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
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
);
1589 // Mark the line dirty if:
1590 // 1. It used to be impacted by a float and now isn't, or vice
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())) {
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.
1617 nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
1618 nsBlockFrame
* aOldParent
, PRBool aFromOverflow
,
1619 PRBool aReparentSiblings
) {
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
) {
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());
1649 * Reflow the dirty lines
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
;
1661 IndentBy(stdout
, gNoiseIndent
);
1663 printf(": reflowing dirty lines");
1664 printf(" computedWidth=%d\n", aState
.mReflowState
.ComputedWidth());
1666 AutoNoisyIndenter
indent(gNoisyReflow
);
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
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);
1692 AutoNoisyIndenter
indent2(gNoisyReflow
);
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
)) {
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
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.
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
) {
1729 // Reflow the line if the line might have clearance now.
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.
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
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
1758 if (!line
->IsDirty()) {
1759 // See if there's any reflow damage that requires that we mark the
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
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
;
1806 DumpLine(aState
, line
, deltaY
, -1);
1807 if (0 == line
->GetChildCount()) {
1808 DeleteLine(aState
, line
, line_end
);
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
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
1855 deltaY
= line
->mBounds
.YMost() - oldYMost
;
1857 lastLineMovedUp
= deltaY
< 0;
1860 SlideLine(aState
, line
, deltaY
);
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
1918 // -- it's an incremental reflow of a descendant
1919 // -- and we didn't reflow any floats (so the available space
1921 // XXXldb We should also check that the first line of the next-in-flow
1923 if (!aState
.mNextInFlow
||
1924 (aState
.mReflowState
.mFlags
.mNextInFlowUntouched
&&
1926 !(GetStateBits() & NS_FRAME_IS_DIRTY
) &&
1928 if (aState
.mNextInFlow
) {
1929 aState
.mReflowStatus
|= NS_FRAME_NOT_COMPLETE
;
1932 // Pull data from a next-in-flow if there's still room for more
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();
1939 PRBool collectOverflowFloats
;
1940 if (nifLine
!= nextInFlow
->end_lines()) {
1941 if (HandleOverflowPlaceholdersOnPulledLine(aState
, nifLine
)) {
1942 // go around again in case the line was deleted
1946 nextInFlow
->mLines
.erase(nifLine
);
1947 collectOverflowFloats
= PR_FALSE
;
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
1956 if (!overflowLines
) {
1957 aState
.mNextInFlow
=
1958 NS_STATIC_CAST(nsBlockFrame
*, nextInFlow
->GetNextInFlow());
1961 nifLine
= overflowLines
->begin();
1962 NS_ASSERTION(nifLine
!= overflowLines
->end(),
1963 "Stored overflow line list should not be empty");
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
);
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();
1990 ReparentFrame(frame
, nextInFlow
, this);
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);
2008 AutoNoisyIndenter
indent2(gNoisyReflow
);
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);
2020 if (0 == line
->GetChildCount()) {
2021 DeleteLine(aState
, line
, line_end
);
2026 if (LineHasClear(line
.get())) {
2027 foundAnyClears
= PR_TRUE
;
2030 // If this is an inline frame then its time to stop
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
);
2054 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
2059 IndentBy(stdout
, gNoiseIndent
- 1);
2061 printf(": done reflowing dirty lines (status=%x)\n",
2062 aState
.mReflowStatus
);
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.
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...
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
);
2156 Invalidate(dirtyRect
);
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
);
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
);
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
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());
2199 Invalidate(dirtyRect
);
2206 * Pull frame from the next available location (one of our lines or
2207 * one of our next-in-flows lines).
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()) {
2221 PullFrameFrom(aState
, aLine
, this, PR_FALSE
, aLine
.next(), aFrameResult
);
2222 NS_ASSERTION(!retry
, "Shouldn't have to retry in the current block");
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
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
2252 nextInFlow
= (nsBlockFrame
*) nextInFlow
->GetNextInFlow();
2253 aState
.mNextInFlow
= nextInFlow
;
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.
2274 nsBlockFrame::PullFrameFrom(nsBlockReflowState
& aState
,
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
2293 aFrameResult
= nsnull
;
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
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();
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
;
2364 VerifyLines(PR_TRUE
);
2371 PlaceFrameView(nsIFrame
* aFrame
)
2373 if (aFrame
->HasView())
2374 nsContainerFrame::PositionFrameView(aFrame
);
2376 nsContainerFrame::PositionChildViews(aFrame
);
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
;
2396 if (aLine
->IsBlock()) {
2398 nsPoint p
= kid
->GetPosition();
2400 kid
->SetPosition(p
);
2403 // Make sure the frame's view and any child views are updated
2404 ::PlaceFrameView(kid
);
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();
2414 nsPoint p
= kid
->GetPosition();
2416 kid
->SetPosition(p
);
2418 // Make sure the frame's view and any child views are updated
2419 ::PlaceFrameView(kid
);
2420 kid
= kid
->GetNextSibling();
2426 nsBlockFrame::AttributeChanged(PRInt32 aNameSpaceID
,
2427 nsIAtom
* aAttribute
,
2430 nsresult rv
= nsBlockFrameSuper::AttributeChanged(aNameSpaceID
,
2431 aAttribute
, aModType
);
2433 if (NS_FAILED(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
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
);
2473 IsPaddingZero(nsStyleUnit aUnit
, nsStyleCoord
&aCoord
)
2475 return ((aUnit
== eStyleUnit_Coord
&& aCoord
.GetCoordValue() == 0) ||
2476 (aUnit
== eStyleUnit_Percent
&& aCoord
.GetPercentValue() == 0.0));
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)
2497 case eStyleUnit_Percent
:
2498 if (position
->mMinHeight
.GetPercentValue() != 0.0f
)
2505 switch (position
->mHeight
.GetUnit()) {
2506 case eStyleUnit_Auto
:
2508 case eStyleUnit_Coord
:
2509 if (position
->mHeight
.GetCoordValue() != 0)
2512 case eStyleUnit_Percent
:
2513 if (position
->mHeight
.GetPercentValue() != 0.0f
)
2520 const nsStyleBorder
* border
= GetStyleBorder();
2521 const nsStylePadding
* padding
= GetStylePadding();
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
))) {
2536 nsBlockFrame::CachedIsEmpty()
2538 if (!IsSelfEmpty()) {
2542 for (line_iterator line
= begin_lines(), line_end
= end_lines();
2546 if (!line
->CachedIsEmpty())
2554 nsBlockFrame::IsEmpty()
2556 if (!IsSelfEmpty()) {
2560 for (line_iterator line
= begin_lines(), line_end
= end_lines();
2564 if (!line
->IsEmpty())
2572 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState
& aState
,
2575 if (aState
.GetFlag(BRS_APPLYTOPMARGIN
)) {
2576 // Apply short-circuit check to avoid searching the line list
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
2584 aState
.SetFlag(BRS_APPLYTOPMARGIN
, 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
);
2600 // No need to apply the top margin if the line has floats. We
2601 // should collapse anyway (bug 44419)
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).
2614 nsBlockFrame::GetTopBlockChild(nsPresContext
* aPresContext
)
2619 nsLineBox
*firstLine
= mLines
.front();
2620 if (firstLine
->IsBlock())
2621 return firstLine
->mFirstChild
;
2623 if (!firstLine
->CachedIsEmpty())
2626 line_iterator secondLine
= begin_lines();
2628 if (secondLine
== end_lines() || !secondLine
->IsBlock())
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.
2638 nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState
& aState
,
2639 nsIFrame
* aLastPlaceholder
)
2641 nsIFrame
* undoPlaceholder
;
2642 if (aLastPlaceholder
) {
2643 undoPlaceholder
= aLastPlaceholder
->GetNextSibling();
2644 aLastPlaceholder
->SetNextSibling(nsnull
);
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
);
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();
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
;
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
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
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
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
2737 if (treatWithClearance
) {
2738 applyTopMargin
= PR_TRUE
;
2741 nsIFrame
* clearanceFrame
= nsnull
;
2742 nscoord startingY
= aState
.mY
;
2743 nsCollapsingMargin incomingMargin
= aState
.mPrevBottomMargin
;
2745 // Save the original position of the frame so that we can reposition
2746 // its view as needed.
2747 nsPoint originalPosition
= frame
->GetPosition();
2749 // Save the frame's current position. We might need it later.
2750 nscoord passOriginalY
= frame
->GetRect().y
;
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
,
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
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
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
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");
2856 PRBool isImpacted
= aState
.IsImpactedByFloat() ? PR_TRUE
: PR_FALSE
;
2857 aLine
->SetLineIsImpactedByFloat(isImpacted
);
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
;
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
;
2911 aState
.mPrevChild
= frame
;
2913 #if defined(REFLOW_STATUS_COVERAGE)
2914 RecordReflowStatus(PR_TRUE
, frameReflowStatus
);
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
;
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 &&
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
;
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();
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
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
)) {
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
3009 printf(": reflow incomplete, frame=");
3010 nsFrame::ListTag(stdout
, frame
);
3011 printf(" prevBottomMargin=%d, setting to zero\n",
3012 aState
.mPrevBottomMargin
);
3014 aState
.mPrevBottomMargin
.Zero();
3017 #ifdef NOISY_VERTICAL_MARGINS
3019 printf(": reflow complete for ");
3020 nsFrame::ListTag(stdout
, frame
);
3021 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3022 aState
.mPrevBottomMargin
, collapsedBottomMargin
.get());
3024 aState
.mPrevBottomMargin
= collapsedBottomMargin
;
3026 #ifdef NOISY_VERTICAL_MARGINS
3029 nsFrame::ListTag(stdout
, frame
);
3030 printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3031 brc
.GetCarriedOutBottomMargin(), collapsedBottomMargin
.get(),
3032 aState
.mPrevBottomMargin
);
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();
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
);
3064 VerifyLines(PR_TRUE
);
3070 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState
& aState
,
3071 line_iterator aLine
,
3072 PRBool
* aKeepReflowGoing
)
3074 nsresult rv
= NS_OK
;
3075 *aKeepReflowGoing
= PR_TRUE
;
3080 LineReflowStatus lineReflowStatus
= LINE_REFLOW_REDO_NEXT_BAND
;
3081 PRBool movedPastFloat
= PR_FALSE
;
3083 PRBool allowPullUp
= PR_TRUE
;
3084 nsIContent
* forceBreakInContent
= nsnull
;
3085 PRInt32 forceBreakOffset
= -1;
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
,
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
3116 forceBreakInContent
= lineLayout
.GetLastOptionalBreakPosition(&forceBreakOffset
);
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();
3129 if (1000 == spins
) {
3131 printf(": yikes! spinning on a line over 1000 times!\n");
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
);
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.
3159 nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState
& aState
,
3160 line_iterator aLine
,
3161 nsIFrame
* aLastPlaceholder
,
3162 PRBool
& aKeepReflowGoing
)
3164 UndoSplitPlaceholders(aState
, aLastPlaceholder
);
3166 line_iterator prevLine
= aLine
;
3168 PushLines(aState
, prevLine
);
3169 aKeepReflowGoing
= PR_FALSE
;
3170 aState
.mReflowStatus
= NS_FRAME_NOT_COMPLETE
;
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"
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
);
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
;
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
,
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
;
3240 nsIFrame
* frame
= aLine
->mFirstChild
;
3242 // Determine whether this is a line of placeholders for out-of-flow
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
,
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.
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
);
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
) {
3296 while (LINE_REFLOW_OK
== lineReflowStatus
) {
3297 PRInt32 oldCount
= aLine
->GetChildCount();
3298 rv
= ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
,
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();
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
;
3325 // We need to try backing up to before a text run
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
3333 lineReflowStatus
= LINE_REFLOW_REDO_NO_PULL
;
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
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
;
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
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
,
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
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
3401 printf("Line reflow status = %s\n", LineReflowStatusNames
[lineReflowStatus
]);
3404 *aLineReflowStatus
= lineReflowStatus
;
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.
3418 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState
& aState
,
3419 nsLineLayout
& aLineLayout
,
3420 line_iterator aLine
,
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
3433 printf(": reflowing ");
3434 nsFrame::ListTag(stdout
, aFrame
);
3435 printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter
? "on" : "off");
3438 // Reflow the inline frame
3439 nsReflowStatus frameReflowStatus
;
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()) {
3457 NS_ENSURE_SUCCESS(rv
, rv
);
3458 #ifdef REALLY_NOISY_REFLOW_CHILD
3459 nsFrame::ListTag(stdout
, aFrame
);
3460 printf(": status=%x\n", frameReflowStatus
);
3463 #if defined(REFLOW_STATUS_COVERAGE)
3464 RecordReflowStatus(PR_FALSE
, frameReflowStatus
);
3467 // Send post-reflow notification
3468 aState
.mPrevChild
= aFrame
;
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.
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
;
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
3514 aLine
->SetLineWrapped(PR_TRUE
);
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()) {
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
;
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()) {
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
;
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.
3621 nsBlockFrame::CreateContinuationFor(nsBlockReflowState
& aState
,
3624 PRBool
& aMadeNewFrame
)
3626 aMadeNewFrame
= PR_FALSE
;
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
;
3634 aLine
->SetChildCount(aLine
->GetChildCount() + 1);
3638 VerifyLines(PR_FALSE
);
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
);
3652 // Next in flow was not created because it already exists.
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
);
3671 static nsFloatCache
*
3672 GetLastFloat(nsLineBox
* aLine
)
3674 nsFloatCache
* fc
= aLine
->GetFirstFloat();
3675 while (fc
&& fc
->Next()) {
3682 CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
, nsFloatCache
* aFC
)
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!");
3695 nsBlockFrame::SplitLine(nsBlockReflowState
& aState
,
3696 nsLineLayout
& aLineLayout
,
3697 line_iterator aLine
,
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");
3708 nsFrame::IndentBy(stdout
, gNoiseIndent
);
3709 printf("split line: from line=%p pushCount=%d aFrame=",
3710 NS_STATIC_CAST(void*, aLine
.get()), pushCount
);
3712 nsFrame::ListTag(stdout
, aFrame
);
3718 if (gReallyNoisyReflow
) {
3719 aLine
->List(stdout
, gNoiseIndent
+1);
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
);
3733 return NS_ERROR_OUT_OF_MEMORY
;
3735 mLines
.after_insert(aLine
, newLine
);
3736 aLine
->SetChildCount(aLine
->GetChildCount() - pushCount
);
3738 if (gReallyNoisyReflow
) {
3739 newLine
->List(stdout
, gNoiseIndent
+1);
3743 // Let line layout know that some frames are no longer part of its
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
;
3758 VerifyLines(PR_TRUE
);
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();
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
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
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();
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
);
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?
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());
3873 } // not visual mode
3877 nsRect combinedArea
;
3878 aLineLayout
.RelativePositionFrames(combinedArea
); // XXXldb This returned width as -15, 2001-06-12, Bugzilla
3879 aLine
->SetCombinedArea(combinedArea
);
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.
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();
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
;
3929 // May be needed below
3930 PRBool wasAdjacentWIthTop
= aState
.IsAdjacentWithTop();
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
);
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
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
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
);
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
);
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());
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
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
,
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();
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
4055 aLineBefore
->LastChild()->SetNextSibling(nsnull
);
4058 VerifyOverflowSituation();
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.
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()) {
4090 HandleOverflowPlaceholdersForPulledFrame(aState
, f
);
4091 NS_ASSERTION(!changed
, "Shouldn't find any continuation placeholders inside inlines");
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.
4103 frame
= frame
->GetNextInFlow();
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.
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();
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
);
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
4143 lastOverflowPlace
= frame
;
4145 frame
= frame
->GetNextInFlow();
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
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
)) {
4165 HandleOverflowPlaceholdersForPulledFrame(aState
, aLine
->mFirstChild
);
4166 NS_ASSERTION(taken
, "We must have removed that frame");
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
) {
4177 HandleOverflowPlaceholdersForPulledFrame(aState
, f
);
4178 NS_ASSERTION(!taken
, "Shouldn't be any continuation placeholders on this line");
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.
4189 nsBlockFrame::DrainOverflowLines(nsBlockReflowState
& aState
)
4192 VerifyOverflowSituation();
4194 nsLineList
* overflowLines
= nsnull
;
4195 nsLineList
* ourOverflowLines
= nsnull
;
4197 // First grab the prev-in-flows overflow lines
4198 nsBlockFrame
* prevBlock
= (nsBlockFrame
*) GetPrevInFlow();
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
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
)) {
4288 lastFrame
->SetNextSibling(iter
->mFirstChild
);
4290 if (searchFirstLinesOnly
[i
]) {
4293 lastFrame
= iter
->LastChild();
4296 nsLineBox
* line
= iter
;
4297 iter
= ll
->erase(iter
);
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
4314 mFloats
.RemoveFrame(oof
);
4315 NS_ASSERTION(found
, "Float should have been put in our mFloats list");
4317 PRBool isAncestor
= nsLayoutUtils::IsProperAncestorFrame(this, fpif
);
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
4327 if (fpif
->GetParent() == prevBlock
) {
4328 keepPlaceholders
.InsertFrame(nsnull
, lastKP
, f
);
4329 keepOutOfFlows
.InsertFrame(nsnull
, lastKOOF
, oof
);
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
);
4355 // Just put it at the front of
4356 // fpAncestor->GetNextInFlow()'s lines.
4357 nsLineBox
* newLine
= aState
.NewLineBox(f
, 1, PR_FALSE
);
4359 nsBlockFrame
* target
=
4360 NS_STATIC_CAST(nsBlockFrame
*, fpAncestor
->GetNextInFlow());
4361 if (!target
->mLines
.empty()) {
4362 f
->SetNextSibling(target
->mLines
.front()->mFirstChild
);
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
);
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
);
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());
4437 nsBlockFrame::GetOverflowLines() const
4439 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES
)) {
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");
4450 nsBlockFrame::RemoveOverflowLines()
4452 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES
)) {
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
);
4463 // Destructor function for the overflowLines frame property
4465 DestroyOverflowLines(void* aFrame
,
4466 nsIAtom
* aPropertyName
,
4467 void* aPropertyValue
,
4470 if (aPropertyValue
) {
4471 nsLineList
* lines
= NS_STATIC_CAST(nsLineList
*, aPropertyValue
);
4472 nsPresContext
*context
= NS_STATIC_CAST(nsPresContext
*, aDtorData
);
4473 nsLineBox::DeleteLineList(context
, *lines
);
4478 // This takes ownership of aOverflowLines.
4479 // XXX We should allocate overflowLines from presShell arena!
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
);
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
4512 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList
& aList
)
4514 if (aList
.IsEmpty()) {
4515 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
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
);
4523 SetProperty(nsGkAtoms::overflowOutOfFlowsProperty
,
4524 aList
.FirstChild(), nsnull
);
4525 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
4530 nsBlockFrame::GetOverflowPlaceholders() const
4532 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS
)) {
4535 nsFrameList
* result
= NS_STATIC_CAST(nsFrameList
*,
4536 GetProperty(nsGkAtoms::overflowPlaceholdersProperty
));
4537 NS_ASSERTION(result
, "value should always be non-empty when state set");
4541 //////////////////////////////////////////////////////////////////////
4542 // Frame list manipulation routines
4545 nsBlockFrame::LastChild()
4547 if (! mLines
.empty()) {
4548 return mLines
.back()->LastChild();
4554 nsBlockFrame::AppendFrames(nsIAtom
* aListName
,
4555 nsIFrame
* aFrameList
)
4557 if (nsnull
== aFrameList
) {
4561 if (mAbsoluteContainer
.GetChildListName() == aListName
) {
4562 return mAbsoluteContainer
.AppendFrames(this, aListName
, aFrameList
);
4564 else if (nsGkAtoms::floatList
== aListName
) {
4565 mFloats
.AppendFrames(nsnull
, aFrameList
);
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();
4578 lastKid
= lastLine
->LastChild();
4581 // Add frames after the last child
4582 #ifdef NOISY_REFLOW_REASON
4584 printf(": append ");
4585 nsFrame::ListTag(stdout
, aFrameList
);
4588 nsFrame::ListTag(stdout
, lastKid
);
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?
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");
4610 if (mAbsoluteContainer
.GetChildListName() == aListName
) {
4611 return mAbsoluteContainer
.InsertFrames(this, aListName
, aPrevFrame
,
4614 else if (nsGkAtoms::floatList
== aListName
) {
4615 mFloats
.InsertFrames(this, aPrevFrame
, aFrameList
);
4619 else if (nsGkAtoms::nextBidi
== aListName
) {}
4622 NS_ERROR("unexpected child list");
4623 return NS_ERROR_INVALID_ARG
;
4627 #ifdef NOISY_REFLOW_REASON
4629 printf(": insert ");
4630 nsFrame::ListTag(stdout
, aFrameList
);
4633 nsFrame::ListTag(stdout
, aPrevFrame
);
4637 nsresult rv
= AddFrames(aFrameList
, aPrevFrame
);
4639 if (aListName
!= nsGkAtoms::nextBidi
)
4641 if (NS_SUCCEEDED(rv
)) {
4642 PresContext()->PresShell()->
4643 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
4644 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
4650 ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
)
4652 nsIAtom
* type
= aLastFrame
->GetType();
4653 if (type
== nsGkAtoms::brFrame
)
4655 if (type
== nsGkAtoms::textFrame
)
4656 return aLastFrame
->HasTerminalNewline() &&
4657 aLastFrame
->GetStyleText()->WhiteSpaceIsSignificant();
4658 if (type
== nsGkAtoms::placeholderFrame
)
4659 return IsContinuationPlaceholder(aLastFrame
);
4664 nsBlockFrame::AddFrames(nsIFrame
* aFrameList
,
4665 nsIFrame
* aPrevSibling
)
4667 // Clear our line cursor, since our lines may change.
4670 if (nsnull
== aFrameList
) {
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;
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
;
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;
4714 // Split the line in two where the frame(s) are being inserted.
4715 nsLineBox
* line
= NS_NewLineBox(presShell
, prevSiblingNextFrame
, rem
, PR_FALSE
);
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
;
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
4746 nsLineBox
* line
= NS_NewLineBox(presShell
, newFrame
, 1, isBlock
);
4748 return NS_ERROR_OUT_OF_MEMORY
;
4750 if (prevSibLine
!= end_lines()) {
4751 // Append new line after prevSibLine
4752 mLines
.after_insert(prevSibLine
, line
);
4756 // New line is going before the other lines
4757 mLines
.push_front(line
);
4758 prevSibLine
= begin_lines();
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
);
4775 VerifyLines(PR_TRUE
);
4780 nsBlockFrame::line_iterator
4781 nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
4782 // Find which line contains the float, so we can update
4784 line_iterator line
= begin_lines(), line_end
= end_lines();
4785 for ( ; line
!= line_end
; ++line
) {
4786 if (line
->IsInline() && line
->RemoveFloat(aFloat
)) {
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
);
4796 fm
->UnregisterPlaceholderFrame(placeholder
);
4797 placeholder
->SetOutOfFlowFrame(nsnull
);
4800 // Try to destroy if it's in mFloats.
4801 if (mFloats
.DestroyFrame(aFloat
)) {
4805 // Try our overflow list
4807 nsAutoOOFFrameList
oofs(this);
4808 if (oofs
.mList
.DestroyFrame(aFloat
)) {
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.
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
;
4828 if (NS_SUCCEEDED(f
->QueryInterface(kBlockFrameCID
, &bf
))) {
4829 MarkAllDescendantLinesDirty(NS_STATIC_CAST(nsBlockFrame
*, f
));
4837 static void MarkSameSpaceManagerLinesDirty(nsBlockFrame
* aBlock
)
4839 nsBlockFrame
* blockWithSpaceMgr
= aBlock
;
4840 while (!(blockWithSpaceMgr
->GetStateBits() & NS_BLOCK_SPACE_MGR
)) {
4842 if (NS_FAILED(blockWithSpaceMgr
->GetParent()->
4843 QueryInterface(kBlockFrameCID
, &bf
))) {
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
)
4862 if (NS_FAILED(aFrame
->QueryInterface(kBlockFrameCID
, &bf
)))
4864 nsBlockFrame
* block
= NS_STATIC_CAST(nsBlockFrame
*, aFrame
);
4865 if (block
->GetFirstChild(nsGkAtoms::floatList
))
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
))
4879 nsBlockFrame::RemoveFrame(nsIAtom
* aListName
,
4880 nsIFrame
* aOldFrame
)
4882 nsresult rv
= NS_OK
;
4884 #ifdef NOISY_REFLOW_REASON
4886 printf(": remove ");
4887 nsFrame::ListTag(stdout
, aOldFrame
);
4891 if (nsnull
== aListName
) {
4892 PRBool hasFloats
= BlockHasAnyFloats(aOldFrame
);
4893 rv
= DoRemoveFrame(aOldFrame
, PR_TRUE
, PR_FALSE
);
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);
4906 else if (nsGkAtoms::nextBidi
== aListName
) {
4907 // Skip the call to |FrameNeedsReflow| below by returning now.
4908 return DoRemoveFrame(aOldFrame
, PR_TRUE
, PR_FALSE
);
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?
4925 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame
* aFrame
)
4927 // First remove aFrame's next in flow
4928 nsIFrame
* nextInFlow
= aFrame
->GetNextInFlow();
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(),
4945 // This also destroys the frame.
4946 block
->RemoveFloat(aFrame
);
4951 * This helps us iterate over the list of all normal + overflow lines
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
)
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.
4990 nsBlockFrame::DoRemoveFrame(nsIFrame
* aDeletedFrame
, PRBool aDestroyFrames
,
4991 PRBool aRemoveOnlyFluidContinuations
)
4993 // Clear our line cursor, since our lines may change.
4996 if (aDeletedFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) {
4997 NS_ASSERTION(aDestroyFrames
, "We can't not destroy out of flows");
4998 DoRemoveOutOfFlowFrame(aDeletedFrame
);
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();
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
5027 TryAllLines(&line
, &line_end
, &searchingOverflowList
);
5028 while (line
!= line_end
) {
5029 nsIFrame
* frame
= line
->mFirstChild
;
5030 PRInt32 n
= line
->GetChildCount();
5032 if (frame
== aDeletedFrame
) {
5035 prevSibling
= frame
;
5036 frame
= frame
->GetNextSibling();
5039 TryAllLines(&line
, &line_end
, &searchingOverflowList
);
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
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.
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.
5086 prevSibling
->SetNextSibling(nextFrame
);
5089 // Update the child count of the line to be accurate
5090 PRInt32 lineChildCount
= line
->GetChildCount();
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
);
5105 if (aDestroyFrames
) {
5106 aDeletedFrame
->Destroy();
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());
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
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
);
5132 Invalidate(lineCombinedArea
);
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
5146 if (line
!= line_end
) {
5147 line
->MarkPreviousMarginDirty();
5149 haveAdvancedToNextLine
= PR_TRUE
;
5151 // Make the line that just lost a frame dirty, and advance to
5153 if (!deletedNextContinuation
|| isLastFrameOnLine
||
5154 !line
->Contains(deletedNextContinuation
)) {
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.
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.
5186 PRBool wasSearchingOverflowList
= searchingOverflowList
;
5187 TryAllLines(&line
, &line_end
, &searchingOverflowList
);
5188 if (NS_UNLIKELY(searchingOverflowList
&& !wasSearchingOverflowList
&&
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(),
5206 VerifyLines(PR_TRUE
);
5209 // Advance to next flow block if the frame has more continuations
5210 return RemoveBlockChild(aDeletedFrame
, aDestroyFrames
);
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 ////////////////////////////////////////////////////////////////////////
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
;
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
5245 // Compute the available width. By default, assume the width of the
5246 // containing block.
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
;
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
5280 nsHTMLReflowState
floatRS(aState
.mPresContext
, aState
.mReflowState
,
5282 nsSize(availSpace
.width
, availSpace
.height
));
5284 // Setup a block reflow state to reflow the float.
5285 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowState
);
5288 PRBool isAdjacentWithTop
= aState
.IsAdjacentWithTop();
5290 nsIFrame
* clearanceFrame
= nsnull
;
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
,
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();
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
)) {
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
);
5380 printf("end ReflowFloat %p, sized to %d,%d\n",
5381 floatFrame
, metrics
.width
, metrics
.height
);
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();
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");
5412 //////////////////////////////////////////////////////////////////////
5413 // Painting, event handling
5416 nsBlockFrame::GetSkipSides() const
5419 if (nsnull
!= GetPrevInFlow()) {
5420 skip
|= 1 << NS_SIDE_TOP
;
5422 if (nsnull
!= GetNextInFlow()) {
5423 skip
|= 1 << NS_SIDE_BOTTOM
;
5429 static void ComputeCombinedArea(nsLineList
& aLines
,
5430 nscoord aWidth
, nscoord aHeight
,
5433 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
5434 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
5437 // Compute min and max x/y values for the reflowed frame's
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
;
5460 aResult
.width
= xb
- xa
;
5461 aResult
.height
= yb
- ya
;
5466 nsBlockFrame::IsVisibleInSelection(nsISelection
* aSelection
)
5468 nsCOMPtr
<nsIDOMHTMLHtmlElement
> html(do_QueryInterface(mContent
));
5469 nsCOMPtr
<nsIDOMHTMLBodyElement
> body(do_QueryInterface(mContent
));
5473 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(mContent
));
5475 nsresult rv
= aSelection
->ContainsNode(node
, PR_TRUE
, &visible
);
5476 return NS_SUCCEEDED(rv
) && visible
;
5480 nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext
& aRenderingContext
,
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.
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
5519 // Only paint if we have a positive width
5521 aRenderingContext
.FillRect(start
+ aPt
.x
,
5522 aLine
->mBounds
.y
+ aLine
->GetAscent() - aOffset
+ aPt
.y
,
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
);
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
);
5553 if (nsBlockFrame::gLamePaintMetrics
) {
5556 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
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.
5562 !(aFrame
->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
))
5566 nsDisplayList aboveTextDecorations
;
5567 PRBool lineInline
= aLine
->IsInline();
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();
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
);
5594 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
5595 const nsRect
& aDirtyRect
,
5596 const nsDisplayListSet
& aLists
)
5598 PRInt32 drawnLines
; // Will only be used if set (gLamePaintMetrics).
5601 if (gNoisyDamageRepair
) {
5604 ::ComputeCombinedArea(mLines
, mRect
.width
, mRect
.height
, ca
);
5605 nsFrame::IndentBy(stdout
, depth
);
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
) {
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
;
5633 for (line_iterator line
= mLines
.begin(cursor
);
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()) {
5643 rv
= DisplayLine(aBuilder
, lineArea
, aDirtyRect
, line
, depth
, drawnLines
,
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();
5657 nsRect lineArea
= line
->GetCombinedArea();
5658 rv
= DisplayLine(aBuilder
, lineArea
, aDirtyRect
, line
, depth
, drawnLines
,
5662 if (!lineArea
.IsEmpty()) {
5663 if (lineArea
.y
< lastY
5664 || lineArea
.YMost() < lastYMost
) {
5665 nonDecreasingYs
= PR_FALSE
;
5668 lastYMost
= lineArea
.YMost();
5673 if (NS_SUCCEEDED(rv
) && nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
5678 if (NS_SUCCEEDED(rv
) && (nsnull
!= mBullet
) && HaveOutsideBullet()) {
5679 // Display outside bullets manually
5680 rv
= BuildDisplayListForChild(aBuilder
, mBullet
, aDirtyRect
, aLists
);
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
);
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
);
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());
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
),
5763 void nsBlockFrame::ClearLineCursor() {
5764 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
)) {
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()) {
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
)) {
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();
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()) {
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.
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 //////////////////////////////////////////////////////////////////////
5849 InLineList(nsLineList
& aLines
, nsIFrame
* aFrame
)
5851 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
5854 nsIFrame
* frame
= line
->mFirstChild
;
5855 PRInt32 n
= line
->GetChildCount();
5857 if (frame
== aFrame
) {
5860 frame
= frame
->GetNextSibling();
5867 InSiblingList(nsLineList
& aLines
, nsIFrame
* aFrame
)
5869 if (! aLines
.empty()) {
5870 nsIFrame
* frame
= aLines
.front()->mFirstChild
;
5872 if (frame
== aFrame
) {
5875 frame
= frame
->GetNextSibling();
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();
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
)) {
5903 if (aFrame
->GetParent() != (nsIFrame
*)this) {
5906 if ((skipLineList
|| InLineList(mLines
, aFrame
)) &&
5907 (skipSiblingList
|| InSiblingList(mLines
, aFrame
))) {
5910 nsLineList
* overflowLines
= GetOverflowLines();
5911 if (overflowLines
&& (skipLineList
|| InLineList(*overflowLines
, aFrame
)) &&
5912 (skipSiblingList
|| InSiblingList(*overflowLines
, aFrame
))) {
5919 nsBlockFrame::VerifyTree() const
5927 //////////////////////////////////////////////////////////////////////
5930 nsBlockFrame::Init(nsIContent
* aContent
,
5932 nsIFrame
* 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
);
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
);
5960 nsPresContext
* presContext
= PresContext();
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() &&
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");
5984 rv
= AddFrames(aChildList
, nsnull
);
5985 if (NS_FAILED(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
;
6006 pseudoElement
= nsCSSPseudoElements::mozListNumber
;
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
;
6030 mState
|= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET
;
6042 nsBlockFrame::FrameStartsCounterScope(nsIFrame
* aFrame
)
6044 nsIContent
* content
= aFrame
->GetContent();
6045 if (!content
|| !content
->IsNodeOfType(nsINode::eHTML
))
6048 nsIAtom
*localName
= content
->NodeInfo()->NameAtom();
6049 return localName
== nsGkAtoms::ol
||
6050 localName
== nsGkAtoms::ul
||
6051 localName
== nsGkAtoms::dir
||
6052 localName
== nsGkAtoms::menu
;
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.
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
);
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);
6083 nsBlockFrame::RenumberListsInBlock(nsPresContext
* aPresContext
,
6084 nsBlockFrame
* aBlockFrame
,
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();
6096 nsIFrame
* kid
= line
->mFirstChild
;
6097 PRInt32 n
= line
->GetChildCount();
6099 PRBool kidRenumberedABullet
= RenumberListsFor(aPresContext
, kid
, aOrdinal
, aDepth
);
6100 if (kidRenumberedABullet
) {
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
;
6116 nsBlockFrame::RenumberListsFor(nsPresContext
* aPresContext
,
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
)
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
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
) {
6147 *aOrdinal
= listItem
->mBullet
->SetListItemOrdinal(*aOrdinal
,
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);
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
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
;
6187 nsBlockFrame::ReflowBullet(nsBlockReflowState
& aState
,
6188 nsHTMLReflowMetrics
& aMetrics
)
6190 const nsHTMLReflowState
&rs
= aState
.mReflowState
;
6192 // Reflow the bullet now
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
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
:
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();
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
) {
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
);
6250 mFloats
.RemoveFrame(outOfFlowFrame
);
6252 aList
.InsertFrame(nsnull
, *aTail
, outOfFlowFrame
);
6253 *aTail
= outOfFlowFrame
;
6256 CollectFloats(aFrame
->GetFirstChild(nsnull
), aList
, aTail
, aFromOverflow
,
6259 if (!aCollectSiblings
)
6261 aFrame
= aFrame
->GetNextSibling();
6266 nsBlockFrame::CheckFloats(nsBlockReflowState
& aState
)
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();
6276 nsIFrame
* floatFrame
= fc
->mPlaceholder
->GetOutOfFlowFrame();
6277 lineFloats
.AppendElement(floatFrame
);
6283 nsAutoVoidArray storedFloats
;
6284 PRBool equal
= PR_TRUE
;
6286 for (nsIFrame
* f
= mFloats
.FirstChild(); f
; f
= f
->GetNextSibling()) {
6287 storedFloats
.AppendElement(f
);
6288 if (i
< lineFloats
.Count() && lineFloats
.ElementAt(i
) != f
) {
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
));
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
6320 aState
.mSpaceManager
->RemoveTrailingRegions(oofs
.FirstChild());
6326 nsBlockFrame::BlockIsMarginRoot(nsIFrame
* aBlock
)
6328 NS_PRECONDITION(aBlock
, "Must have a frame");
6330 nsBlockFrame
* blockFrame
;
6331 aBlock
->QueryInterface(kBlockFrameCID
, (void**)&blockFrame
);
6332 NS_ASSERTION(blockFrame
, "aBlock must be a block");
6335 nsIFrame
* parent
= aBlock
->GetParent();
6336 return (aBlock
->GetStateBits() & NS_BLOCK_MARGIN_ROOT
) ||
6337 (parent
&& !parent
->IsFloatContainingBlock() &&
6338 parent
->GetType() != nsGkAtoms::columnSetFrame
);
6343 nsBlockFrame::BlockNeedsSpaceManager(nsIFrame
* aBlock
)
6345 NS_PRECONDITION(aBlock
, "Must have a frame");
6347 nsBlockFrame
* blockFrame
;
6348 aBlock
->QueryInterface(kBlockFrameCID
, (void**)&blockFrame
);
6349 NS_ASSERTION(blockFrame
, "aBlock must be a block");
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
6361 nsBlockFrame::ResolveBidi()
6363 nsPresContext
* presContext
= PresContext();
6364 if (!presContext
->BidiEnabled()) {
6368 if (mLines
.empty()) {
6372 nsBidiPresUtils
* bidiUtils
= presContext
->GetBidiUtils();
6376 return bidiUtils
->Resolve(presContext
, this,
6377 mLines
.front()->mFirstChild
,
6378 IsVisualFormControl(presContext
));
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()) {
6393 PRUint32 options
= aPresContext
->GetBidi();
6394 if (IBMBIDI_CONTROLSTEXTMODE_LOGICAL
!= GET_BIDI_OPTION_CONTROLSTEXTMODE(options
)) {
6398 nsIContent
* content
= GetContent();
6399 for ( ; content
; content
= content
->GetParent()) {
6400 if (content
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
)) {
6411 nsBlockFrame::VerifyLines(PRBool aFinalCheckOK
)
6413 if (!gVerifyLines
) {
6416 if (mLines
.empty()) {
6420 // Add up the counts on each line. Also validate that IsFirstLine is
6423 PRBool seenBlock
= PR_FALSE
;
6424 line_iterator line
, line_end
;
6425 for (line
= begin_lines(), line_end
= end_lines();
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
;
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();
6453 count
= line
->GetChildCount();
6454 frame
= line
->mFirstChild
;
6455 while (--count
>= 0) {
6456 frame
= frame
->GetNextSibling();
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.
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();
6483 nsBlockFrame::GetDepth() const
6486 nsIFrame
* parent
= mParent
;
6488 parent
= parent
->GetParent();