1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * rendering object for CSS display:block, inline-block, and list-item
9 * boxes, also used for various anonymous boxes
12 #include "nsBlockFrame.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/Util.h"
18 #include "nsAbsoluteContainingBlock.h"
19 #include "nsBlockReflowContext.h"
20 #include "nsBlockReflowState.h"
21 #include "nsBulletFrame.h"
22 #include "nsLineBox.h"
23 #include "nsLineLayout.h"
24 #include "nsPlaceholderFrame.h"
25 #include "nsStyleConsts.h"
26 #include "nsFrameManager.h"
27 #include "nsPresContext.h"
28 #include "nsIPresShell.h"
29 #include "nsStyleContext.h"
30 #include "nsHTMLParts.h"
31 #include "nsGkAtoms.h"
32 #include "nsGenericHTMLElement.h"
33 #include "nsAttrValueInlines.h"
35 #include "nsFloatManager.h"
39 #include "nsAutoPtr.h"
40 #include "nsIScrollableFrame.h"
43 #include "nsIDOMHTMLDocument.h"
45 #include "nsLayoutUtils.h"
46 #include "nsDisplayList.h"
47 #include "nsCSSAnonBoxes.h"
48 #include "nsCSSFrameConstructor.h"
49 #include "nsRenderingContext.h"
50 #include "TextOverflow.h"
51 #include "nsIFrameInlines.h"
54 #include "nsBidiPresUtils.h"
57 static const int MIN_LINES_NEEDING_CURSOR
= 20;
59 static const PRUnichar kDiscCharacter
= 0x2022;
60 static const PRUnichar kCircleCharacter
= 0x25e6;
61 static const PRUnichar kSquareCharacter
= 0x25aa;
63 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
65 using namespace mozilla
;
66 using namespace mozilla::css
;
67 using namespace mozilla::layout
;
70 #include "nsBlockDebugFlags.h"
72 bool nsBlockFrame::gLamePaintMetrics
;
73 bool nsBlockFrame::gLameReflowMetrics
;
74 bool nsBlockFrame::gNoisy
;
75 bool nsBlockFrame::gNoisyDamageRepair
;
76 bool nsBlockFrame::gNoisyIntrinsic
;
77 bool nsBlockFrame::gNoisyReflow
;
78 bool nsBlockFrame::gReallyNoisyReflow
;
79 bool nsBlockFrame::gNoisyFloatManager
;
80 bool nsBlockFrame::gVerifyLines
;
81 bool nsBlockFrame::gDisableResizeOpt
;
83 int32_t nsBlockFrame::gNoiseIndent
;
85 struct BlockDebugFlags
{
90 static const BlockDebugFlags gFlags
[] = {
91 { "reflow", &nsBlockFrame::gNoisyReflow
},
92 { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow
},
93 { "intrinsic", &nsBlockFrame::gNoisyIntrinsic
},
94 { "float-manager", &nsBlockFrame::gNoisyFloatManager
},
95 { "verify-lines", &nsBlockFrame::gVerifyLines
},
96 { "damage-repair", &nsBlockFrame::gNoisyDamageRepair
},
97 { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics
},
98 { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics
},
99 { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt
},
101 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
106 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
107 const BlockDebugFlags
* bdf
= gFlags
;
108 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
109 for (; bdf
< end
; bdf
++) {
110 printf(" %s\n", bdf
->name
);
112 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
113 printf("names (no whitespace)\n");
117 nsBlockFrame::InitDebugFlags()
119 static bool firstTime
= true;
122 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
126 char* cm
= PL_strchr(flags
, ',');
130 const BlockDebugFlags
* bdf
= gFlags
;
131 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
132 for (; bdf
< end
; bdf
++) {
133 if (PL_strcasecmp(bdf
->name
, flags
) == 0) {
135 printf("nsBlockFrame: setting %s debug flag on\n", bdf
->name
);
158 // add in a sanity check for absurdly deep frame trees. See bug 42138
159 // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
160 #define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
162 //----------------------------------------------------------------------
164 // Debugging support code
167 const char* nsBlockFrame::kReflowCommandType
[] = {
176 #ifdef REALLY_NOISY_FIRST_LINE
178 DumpStyleGeneaology(nsIFrame
* aFrame
, const char* gap
)
181 nsFrame::ListTag(stdout
, aFrame
);
183 nsStyleContext
* sc
= aFrame
->StyleContext();
184 while (nullptr != sc
) {
187 psc
= sc
->GetParent();
194 #ifdef REFLOW_STATUS_COVERAGE
196 RecordReflowStatus(bool aChildIsBlock
, nsReflowStatus aFrameReflowStatus
)
198 static uint32_t record
[2];
201 // 1: child-is-inline
203 if (!aChildIsBlock
) index
|= 1;
205 // Compute new status
206 uint32_t newS
= record
[index
];
207 if (NS_INLINE_IS_BREAK(aFrameReflowStatus
)) {
208 if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus
)) {
211 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus
)) {
218 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus
)) {
225 // Log updates to the status that yield different values
226 if (record
[index
] != newS
) {
227 record
[index
] = newS
;
228 printf("record(%d): %02x %02x\n", index
, record
[0], record
[1]);
233 // Destructor function for the overflowLines frame property
235 DestroyOverflowLines(void* aPropertyValue
)
237 NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
240 NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty
, DestroyOverflowLines
)
241 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty
)
242 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty
)
243 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty
)
244 NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty
, nullptr)
246 //----------------------------------------------------------------------
249 NS_NewBlockFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, uint32_t aFlags
)
251 nsBlockFrame
* it
= new (aPresShell
) nsBlockFrame(aContext
);
252 it
->SetFlags(aFlags
);
256 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame
)
258 nsBlockFrame::~nsBlockFrame()
263 nsBlockFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
266 DestroyAbsoluteFrames(aDestructRoot
);
267 mFloats
.DestroyFramesFrom(aDestructRoot
);
268 nsPresContext
* presContext
= PresContext();
269 nsIPresShell
* shell
= presContext
->PresShell();
270 nsLineBox::DeleteLineList(presContext
, mLines
, aDestructRoot
,
273 FramePropertyTable
* props
= presContext
->PropertyTable();
275 if (HasPushedFloats()) {
276 SafelyDestroyFrameListProp(aDestructRoot
, shell
, props
,
277 PushedFloatProperty());
278 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
281 // destroy overflow lines now
282 FrameLines
* overflowLines
= RemoveOverflowLines();
284 nsLineBox::DeleteLineList(presContext
, overflowLines
->mLines
,
285 aDestructRoot
, &overflowLines
->mFrames
);
286 delete overflowLines
;
289 if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) {
290 SafelyDestroyFrameListProp(aDestructRoot
, shell
, props
,
291 OverflowOutOfFlowsProperty());
292 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
295 if (HasOutsideBullet()) {
296 SafelyDestroyFrameListProp(aDestructRoot
, shell
, props
,
297 OutsideBulletProperty());
298 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET
);
301 nsBlockFrameSuper::DestroyFrom(aDestructRoot
);
304 /* virtual */ nsILineIterator
*
305 nsBlockFrame::GetLineIterator()
307 nsLineIterator
* it
= new nsLineIterator
;
311 const nsStyleVisibility
* visibility
= StyleVisibility();
312 nsresult rv
= it
->Init(mLines
, visibility
->mDirection
== NS_STYLE_DIRECTION_RTL
);
320 NS_QUERYFRAME_HEAD(nsBlockFrame
)
321 NS_QUERYFRAME_ENTRY(nsBlockFrame
)
322 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper
)
325 nsBlockFrame::GetSplittableType() const
327 return NS_FRAME_SPLITTABLE_NON_RECTANGULAR
;
332 nsBlockFrame::List(FILE* out
, int32_t aIndent
, uint32_t aFlags
) const
334 ListGeneric(out
, aIndent
, aFlags
);
341 if (!mLines
.empty()) {
342 const_line_iterator line
= begin_lines(), line_end
= end_lines();
343 for ( ; line
!= line_end
; ++line
) {
344 line
->List(out
, aIndent
, aFlags
);
348 // Output the overflow lines.
349 const FrameLines
* overflowLines
= GetOverflowLines();
350 if (overflowLines
&& !overflowLines
->mLines
.empty()) {
351 IndentBy(out
, aIndent
);
352 fprintf(out
, "Overflow-lines %p/%p <\n", overflowLines
, &overflowLines
->mFrames
);
353 const_line_iterator line
= overflowLines
->mLines
.begin(),
354 line_end
= overflowLines
->mLines
.end();
355 for ( ; line
!= line_end
; ++line
) {
356 line
->List(out
, aIndent
+ 1, aFlags
);
358 IndentBy(out
, aIndent
);
362 // skip the principal list - we printed the lines above
363 // skip the overflow list - we printed the overflow lines above
364 ChildListIterator
lists(this);
365 ChildListIDs
skip(kPrincipalList
| kOverflowList
);
366 for (; !lists
.IsDone(); lists
.Next()) {
367 if (skip
.Contains(lists
.CurrentID())) {
370 IndentBy(out
, aIndent
);
371 fprintf(out
, "%s %p <\n", mozilla::layout::ChildListName(lists
.CurrentID()),
372 &GetChildList(lists
.CurrentID()));
373 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
374 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
375 nsIFrame
* kid
= childFrames
.get();
376 kid
->List(out
, aIndent
+ 1, aFlags
);
378 IndentBy(out
, aIndent
);
383 IndentBy(out
, aIndent
);
387 NS_IMETHODIMP_(nsFrameState
)
388 nsBlockFrame::GetDebugStateBits() const
390 // We don't want to include our cursor flag in the bits the
391 // regression tester looks at
392 return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR
;
396 nsBlockFrame::GetFrameName(nsAString
& aResult
) const
398 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult
);
403 nsBlockFrame::GetType() const
405 return nsGkAtoms::blockFrame
;
409 nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey
)
412 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame2
,
413 "unexpected block frame in SVG text");
414 GetParent()->InvalidateFrame();
417 nsBlockFrameSuper::InvalidateFrame(aDisplayItemKey
);
421 nsBlockFrame::InvalidateFrameWithRect(const nsRect
& aRect
, uint32_t aDisplayItemKey
)
424 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame2
,
425 "unexpected block frame in SVG text");
426 GetParent()->InvalidateFrame();
429 nsBlockFrameSuper::InvalidateFrameWithRect(aRect
, aDisplayItemKey
);
433 nsBlockFrame::GetBaseline() const
436 if (nsLayoutUtils::GetLastLineBaseline(this, &result
))
438 return nsFrame::GetBaseline();
442 nsBlockFrame::GetCaretBaseline() const
444 nsRect contentRect
= GetContentRect();
445 nsMargin bp
= GetUsedBorderAndPadding();
447 if (!mLines
.empty()) {
448 const_line_iterator line
= begin_lines();
449 const nsLineBox
* firstLine
= line
;
450 if (firstLine
->GetChildCount()) {
451 return bp
.top
+ firstLine
->mFirstChild
->GetCaretBaseline();
454 nsRefPtr
<nsFontMetrics
> fm
;
455 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
456 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
), inflation
);
457 return nsLayoutUtils::GetCenteredFontBaseline(fm
, nsHTMLReflowState::
458 CalcLineHeight(StyleContext(), contentRect
.height
, inflation
)) +
462 /////////////////////////////////////////////////////////////////////////////
463 // Child frame enumeration
466 nsBlockFrame::GetChildList(ChildListID aListID
) const
471 case kOverflowList
: {
472 FrameLines
* overflowLines
= GetOverflowLines();
473 return overflowLines
? overflowLines
->mFrames
: nsFrameList::EmptyList();
477 case kOverflowOutOfFlowList
: {
478 const nsFrameList
* list
= GetOverflowOutOfFlows();
479 return list
? *list
: nsFrameList::EmptyList();
481 case kPushedFloatsList
: {
482 const nsFrameList
* list
= GetPushedFloats();
483 return list
? *list
: nsFrameList::EmptyList();
486 const nsFrameList
* list
= GetOutsideBulletList();
487 return list
? *list
: nsFrameList::EmptyList();
490 return nsContainerFrame::GetChildList(aListID
);
495 nsBlockFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const
497 nsContainerFrame::GetChildLists(aLists
);
498 FrameLines
* overflowLines
= GetOverflowLines();
500 overflowLines
->mFrames
.AppendIfNonempty(aLists
, kOverflowList
);
502 const nsFrameList
* list
= GetOverflowOutOfFlows();
504 list
->AppendIfNonempty(aLists
, kOverflowOutOfFlowList
);
506 mFloats
.AppendIfNonempty(aLists
, kFloatList
);
507 list
= GetOutsideBulletList();
509 list
->AppendIfNonempty(aLists
, kBulletList
);
511 list
= GetPushedFloats();
513 list
->AppendIfNonempty(aLists
, kPushedFloatsList
);
518 nsBlockFrame::IsFloatContainingBlock() const
524 ReparentFrame(nsIFrame
* aFrame
, nsIFrame
* aOldParent
, nsIFrame
* aNewParent
)
526 NS_ASSERTION(aOldParent
== aFrame
->GetParent(),
527 "Parent not consistent with expectations");
529 aFrame
->SetParent(aNewParent
);
531 // When pushing and pulling frames we need to check for whether any
532 // views need to be reparented
533 nsContainerFrame::ReparentFrameView(aFrame
->PresContext(), aFrame
,
534 aOldParent
, aNewParent
);
538 ReparentFrames(nsFrameList
& aFrameList
, nsIFrame
* aOldParent
,
539 nsIFrame
* aNewParent
)
541 for (nsFrameList::Enumerator
e(aFrameList
); !e
.AtEnd(); e
.Next()) {
542 ReparentFrame(e
.get(), aOldParent
, aNewParent
);
547 * Remove the first line from aFromLines and adjust the associated frame list
548 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
549 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
550 * that were extracted from the head of aFromFrames.
551 * aFromLines must contain at least one line, the line may be empty.
552 * @return true if aFromLines becomes empty
555 RemoveFirstLine(nsLineList
& aFromLines
, nsFrameList
& aFromFrames
,
556 nsLineBox
** aOutLine
, nsFrameList
* aOutFrames
)
558 nsLineList_iterator removedLine
= aFromLines
.begin();
559 *aOutLine
= removedLine
;
560 nsLineList_iterator next
= aFromLines
.erase(removedLine
);
561 bool isLastLine
= next
== aFromLines
.end();
562 nsIFrame
* lastFrame
= isLastLine
? aFromFrames
.LastChild()
563 : next
->mFirstChild
->GetPrevSibling();
564 nsFrameList::FrameLinkEnumerator
linkToBreak(aFromFrames
, lastFrame
);
565 *aOutFrames
= aFromFrames
.ExtractHead(linkToBreak
);
569 //////////////////////////////////////////////////////////////////////
573 nsBlockFrame::MarkIntrinsicWidthsDirty()
575 nsBlockFrame
* dirtyBlock
= static_cast<nsBlockFrame
*>(FirstContinuation());
576 dirtyBlock
->mMinWidth
= NS_INTRINSIC_WIDTH_UNKNOWN
;
577 dirtyBlock
->mPrefWidth
= NS_INTRINSIC_WIDTH_UNKNOWN
;
578 if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
579 for (nsIFrame
* frame
= dirtyBlock
; frame
;
580 frame
= frame
->GetNextContinuation()) {
581 frame
->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
585 nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
589 nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState()
591 nsPresContext
*presContext
= PresContext();
592 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext
)) {
595 bool inflationEnabled
=
596 !presContext
->mInflationDisabledForShrinkWrap
;
597 if (inflationEnabled
!=
598 !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED
)) {
599 mMinWidth
= NS_INTRINSIC_WIDTH_UNKNOWN
;
600 mPrefWidth
= NS_INTRINSIC_WIDTH_UNKNOWN
;
601 if (inflationEnabled
) {
602 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
604 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
609 /* virtual */ nscoord
610 nsBlockFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
612 nsIFrame
* firstInFlow
= FirstContinuation();
613 if (firstInFlow
!= this)
614 return firstInFlow
->GetMinWidth(aRenderingContext
);
616 DISPLAY_MIN_WIDTH(this, mMinWidth
);
618 CheckIntrinsicCacheAgainstShrinkWrapState();
620 if (mMinWidth
!= NS_INTRINSIC_WIDTH_UNKNOWN
)
624 if (gNoisyIntrinsic
) {
625 IndentBy(stdout
, gNoiseIndent
);
627 printf(": GetMinWidth\n");
629 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
632 for (nsBlockFrame
* curFrame
= this; curFrame
;
633 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
634 curFrame
->LazyMarkLinesDirty();
637 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)
639 InlineMinWidthData data
;
640 for (nsBlockFrame
* curFrame
= this; curFrame
;
641 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
642 for (line_iterator line
= curFrame
->begin_lines(), line_end
= curFrame
->end_lines();
643 line
!= line_end
; ++line
)
646 if (gNoisyIntrinsic
) {
647 IndentBy(stdout
, gNoiseIndent
);
648 printf("line (%s%s)\n",
649 line
->IsBlock() ? "block" : "inline",
650 line
->IsEmpty() ? ", empty" : "");
652 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
654 if (line
->IsBlock()) {
655 data
.ForceBreak(aRenderingContext
);
656 data
.currentLine
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
657 line
->mFirstChild
, nsLayoutUtils::MIN_WIDTH
);
658 data
.ForceBreak(aRenderingContext
);
660 if (!curFrame
->GetPrevContinuation() &&
661 line
== curFrame
->begin_lines()) {
662 // Only add text-indent if it has no percentages; using a
663 // percentage basis of 0 unconditionally would give strange
664 // behavior for calc(10%-3px).
665 const nsStyleCoord
&indent
= StyleText()->mTextIndent
;
666 if (indent
.ConvertsToLength())
667 data
.currentLine
+= nsRuleNode::ComputeCoordPercentCalc(indent
, 0);
669 // XXX Bug NNNNNN Should probably handle percentage text-indent.
672 data
.lineContainer
= curFrame
;
673 nsIFrame
*kid
= line
->mFirstChild
;
674 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
675 ++i
, kid
= kid
->GetNextSibling()) {
676 kid
->AddInlineMinWidth(aRenderingContext
, &data
);
680 if (gNoisyIntrinsic
) {
681 IndentBy(stdout
, gNoiseIndent
);
682 printf("min: [prevLines=%d currentLine=%d]\n",
683 data
.prevLines
, data
.currentLine
);
688 data
.ForceBreak(aRenderingContext
);
690 mMinWidth
= data
.prevLines
;
694 /* virtual */ nscoord
695 nsBlockFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
697 nsIFrame
* firstInFlow
= FirstContinuation();
698 if (firstInFlow
!= this)
699 return firstInFlow
->GetPrefWidth(aRenderingContext
);
701 DISPLAY_PREF_WIDTH(this, mPrefWidth
);
703 CheckIntrinsicCacheAgainstShrinkWrapState();
705 if (mPrefWidth
!= NS_INTRINSIC_WIDTH_UNKNOWN
)
709 if (gNoisyIntrinsic
) {
710 IndentBy(stdout
, gNoiseIndent
);
712 printf(": GetPrefWidth\n");
714 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
717 for (nsBlockFrame
* curFrame
= this; curFrame
;
718 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
719 curFrame
->LazyMarkLinesDirty();
722 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)
724 InlinePrefWidthData data
;
725 for (nsBlockFrame
* curFrame
= this; curFrame
;
726 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
727 for (line_iterator line
= curFrame
->begin_lines(), line_end
= curFrame
->end_lines();
728 line
!= line_end
; ++line
)
731 if (gNoisyIntrinsic
) {
732 IndentBy(stdout
, gNoiseIndent
);
733 printf("line (%s%s)\n",
734 line
->IsBlock() ? "block" : "inline",
735 line
->IsEmpty() ? ", empty" : "");
737 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
739 if (line
->IsBlock()) {
740 data
.ForceBreak(aRenderingContext
);
741 data
.currentLine
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
742 line
->mFirstChild
, nsLayoutUtils::PREF_WIDTH
);
743 data
.ForceBreak(aRenderingContext
);
745 if (!curFrame
->GetPrevContinuation() &&
746 line
== curFrame
->begin_lines()) {
747 // Only add text-indent if it has no percentages; using a
748 // percentage basis of 0 unconditionally would give strange
749 // behavior for calc(10%-3px).
750 const nsStyleCoord
&indent
= StyleText()->mTextIndent
;
751 if (indent
.ConvertsToLength())
752 data
.currentLine
+= nsRuleNode::ComputeCoordPercentCalc(indent
, 0);
754 // XXX Bug NNNNNN Should probably handle percentage text-indent.
757 data
.lineContainer
= curFrame
;
758 nsIFrame
*kid
= line
->mFirstChild
;
759 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
760 ++i
, kid
= kid
->GetNextSibling()) {
761 kid
->AddInlinePrefWidth(aRenderingContext
, &data
);
765 if (gNoisyIntrinsic
) {
766 IndentBy(stdout
, gNoiseIndent
);
767 printf("pref: [prevLines=%d currentLine=%d]\n",
768 data
.prevLines
, data
.currentLine
);
773 data
.ForceBreak(aRenderingContext
);
775 mPrefWidth
= data
.prevLines
;
780 nsBlockFrame::ComputeTightBounds(gfxContext
* aContext
) const
783 if (StyleContext()->HasTextDecorationLines()) {
784 return GetVisualOverflowRect();
786 return ComputeSimpleTightBounds(aContext
);
790 AvailableSpaceShrunk(const nsRect
& aOldAvailableSpace
,
791 const nsRect
& aNewAvailableSpace
)
793 if (aNewAvailableSpace
.width
== 0) {
794 // Positions are not significant if the width is zero.
795 return aOldAvailableSpace
.width
!= 0;
797 NS_ASSERTION(aOldAvailableSpace
.x
<= aNewAvailableSpace
.x
&&
798 aOldAvailableSpace
.XMost() >= aNewAvailableSpace
.XMost(),
799 "available space should never grow");
800 return aOldAvailableSpace
.width
!= aNewAvailableSpace
.width
;
804 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState
& aReflowState
,
807 // The issue here is that for a 'height' of 'auto' the reflow state
808 // code won't know how to calculate the containing block height
809 // because it's calculated bottom up. So we use our own computed
810 // size as the dimensions.
811 nsIFrame
* frame
= aReflowState
.frame
;
813 nsSize
cbSize(aFrameSize
);
814 // Containing block is relative to the padding edge
815 const nsMargin
& border
=
816 aReflowState
.mComputedBorderPadding
- aReflowState
.mComputedPadding
;
817 cbSize
.width
-= border
.LeftRight();
818 cbSize
.height
-= border
.TopBottom();
820 if (frame
->GetParent()->GetContent() == frame
->GetContent() &&
821 frame
->GetParent()->GetType() != nsGkAtoms::canvasFrame
) {
822 // We are a wrapped frame for the content (and the wrapper is not the
823 // canvas frame, whose size is not meaningful here).
824 // Use the container's dimensions, if they have been precomputed.
825 // XXX This is a hack! We really should be waiting until the outermost
826 // frame is fully reflowed and using the resulting dimensions, even
827 // if they're intrinsic.
828 // In fact we should be attaching absolute children to the outermost
829 // frame and not always sticking them in block frames.
831 // First, find the reflow state for the outermost frame for this
833 const nsHTMLReflowState
* aLastRS
= &aReflowState
;
834 const nsHTMLReflowState
* lastButOneRS
= &aReflowState
;
835 while (aLastRS
->parentReflowState
&&
836 aLastRS
->parentReflowState
->frame
->GetContent() == frame
->GetContent()) {
837 lastButOneRS
= aLastRS
;
838 aLastRS
= aLastRS
->parentReflowState
;
840 if (aLastRS
!= &aReflowState
) {
841 // Scrollbars need to be specifically excluded, if present, because they are outside the
842 // padding-edge. We need better APIs for getting the various boxes from a frame.
843 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(aLastRS
->frame
);
844 nsMargin
scrollbars(0,0,0,0);
847 scrollFrame
->GetDesiredScrollbarSizes(aLastRS
->frame
->PresContext(),
848 aLastRS
->rendContext
);
849 if (!lastButOneRS
->mFlags
.mAssumingHScrollbar
) {
850 scrollbars
.top
= scrollbars
.bottom
= 0;
852 if (!lastButOneRS
->mFlags
.mAssumingVScrollbar
) {
853 scrollbars
.left
= scrollbars
.right
= 0;
856 // We found a reflow state for the outermost wrapping frame, so use
857 // its computed metrics if available
858 if (aLastRS
->ComputedWidth() != NS_UNCONSTRAINEDSIZE
) {
859 cbSize
.width
= std::max(0,
860 aLastRS
->ComputedWidth() + aLastRS
->mComputedPadding
.LeftRight() - scrollbars
.LeftRight());
862 if (aLastRS
->ComputedHeight() != NS_UNCONSTRAINEDSIZE
) {
863 cbSize
.height
= std::max(0,
864 aLastRS
->ComputedHeight() + aLastRS
->mComputedPadding
.TopBottom() - scrollbars
.TopBottom());
873 nsBlockFrame::Reflow(nsPresContext
* aPresContext
,
874 nsHTMLReflowMetrics
& aMetrics
,
875 const nsHTMLReflowState
& aReflowState
,
876 nsReflowStatus
& aStatus
)
878 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
879 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
882 IndentBy(stdout
, gNoiseIndent
);
884 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
885 aReflowState
.availableWidth
, aReflowState
.availableHeight
,
886 aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight());
888 AutoNoisyIndenter
indent(gNoisy
);
889 PRTime start
= 0; // Initialize these variablies to silence the compiler.
890 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
891 if (gLameReflowMetrics
) {
893 ctc
= nsLineBox::GetCtorCount();
897 const nsHTMLReflowState
*reflowState
= &aReflowState
;
898 nscoord consumedHeight
= GetConsumedHeight();
899 nscoord effectiveComputedHeight
= GetEffectiveComputedHeight(aReflowState
,
901 Maybe
<nsHTMLReflowState
> mutableReflowState
;
902 // If we have non-auto height, we're clipping our kids and we fit,
903 // make sure our kids fit too.
904 if (aReflowState
.availableHeight
!= NS_UNCONSTRAINEDSIZE
&&
905 aReflowState
.ComputedHeight() != NS_AUTOHEIGHT
&&
906 ShouldApplyOverflowClipping(this, aReflowState
.mStyleDisplay
)) {
907 nsMargin heightExtras
= aReflowState
.mComputedBorderPadding
;
908 if (GetSkipSides() & NS_SIDE_TOP
) {
909 heightExtras
.top
= 0;
911 // Bottom margin never causes us to create continuations, so we
912 // don't need to worry about whether it fits in its entirety.
913 heightExtras
.top
+= aReflowState
.mComputedMargin
.top
;
916 if (effectiveComputedHeight
+ heightExtras
.TopBottom() <=
917 aReflowState
.availableHeight
) {
918 mutableReflowState
.construct(aReflowState
);
919 mutableReflowState
.ref().availableHeight
= NS_UNCONSTRAINEDSIZE
;
920 reflowState
= mutableReflowState
.addr();
924 // See comment below about oldSize. Use *only* for the
925 // abs-pos-containing-block-size-change optimization!
926 nsSize oldSize
= GetSize();
928 // Should we create a float manager?
929 nsAutoFloatManager
autoFloatManager(const_cast<nsHTMLReflowState
&>(*reflowState
));
931 // XXXldb If we start storing the float manager in the frame rather
932 // than keeping it around only during reflow then we should create it
933 // only when there are actually floats to manage. Otherwise things
934 // like tables will gain significant bloat.
935 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
936 if (needFloatManager
)
937 autoFloatManager
.CreateFloatManager(aPresContext
);
939 // OK, some lines may be reflowed. Blow away any saved line cursor
940 // because we may invalidate the nondecreasing
941 // overflowArea.VisualOverflow().y/yMost invariant, and we may even
942 // delete the line with the line cursor.
945 if (IsFrameTreeTooDeep(*reflowState
, aMetrics
, aStatus
)) {
949 bool topMarginRoot
, bottomMarginRoot
;
950 IsMarginRoot(&topMarginRoot
, &bottomMarginRoot
);
952 // Cache the consumed height in the block reflow state so that we don't have
953 // to continually recompute it.
954 nsBlockReflowState
state(*reflowState
, aPresContext
, this,
955 topMarginRoot
, bottomMarginRoot
, needFloatManager
,
959 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)
960 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
963 if (RenumberLists(aPresContext
)) {
964 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
969 // ALWAYS drain overflow. We never want to leave the previnflow's
970 // overflow lines hanging around; block reflow depends on the
971 // overflow line lists being cleared out between reflow passes.
972 DrainOverflowLines();
974 // Handle paginated overflow (see nsContainerFrame.h)
975 nsOverflowAreas ocBounds
;
976 nsReflowStatus ocStatus
= NS_FRAME_COMPLETE
;
977 if (GetPrevInFlow()) {
978 ReflowOverflowContainerChildren(aPresContext
, *reflowState
, ocBounds
, 0,
982 // Now that we're done cleaning up our overflow container lists, we can
983 // give |state| its nsOverflowContinuationTracker.
984 nsOverflowContinuationTracker
tracker(aPresContext
, this, false);
985 state
.mOverflowTracker
= &tracker
;
987 // Drain & handle pushed floats
988 DrainPushedFloats(state
);
989 nsOverflowAreas fcBounds
;
990 nsReflowStatus fcStatus
= NS_FRAME_COMPLETE
;
991 ReflowPushedFloats(state
, fcBounds
, fcStatus
);
993 // If we're not dirty (which means we'll mark everything dirty later)
994 // and our width has changed, mark the lines dirty that we need to
995 // mark dirty for a resize reflow.
996 if (!(GetStateBits() & NS_FRAME_IS_DIRTY
) && reflowState
->mFlags
.mHResize
) {
997 PrepareResizeReflow(state
);
1000 LazyMarkLinesDirty();
1002 mState
&= ~NS_FRAME_FIRST_REFLOW
;
1005 rv
= ReflowDirtyLines(state
);
1007 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1008 // this frame from a previous iteration of reflow, then we should not return
1009 // a status of NS_FRAME_COMPLETE, since we actually have overflow, it's just
1010 // already been handled.
1012 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1013 // and reflow them, but just in case it does, this is a safety precaution so
1014 // we don't end up with a placeholder pointing to frames that have already
1015 // been deleted as part of removing our next-in-flow.
1016 if (NS_FRAME_IS_COMPLETE(state
.mReflowStatus
)) {
1017 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1019 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1020 NS_MergeReflowStatusInto(&state
.mReflowStatus
, NS_FRAME_NOT_COMPLETE
);
1023 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1027 NS_ASSERTION(NS_SUCCEEDED(rv
), "reflow dirty lines failed");
1028 if (NS_FAILED(rv
)) return rv
;
1030 NS_MergeReflowStatusInto(&state
.mReflowStatus
, ocStatus
);
1031 NS_MergeReflowStatusInto(&state
.mReflowStatus
, fcStatus
);
1033 // If we end in a BR with clear and affected floats continue,
1034 // we need to continue, too.
1035 if (NS_UNCONSTRAINEDSIZE
!= reflowState
->availableHeight
&&
1036 NS_FRAME_IS_COMPLETE(state
.mReflowStatus
) &&
1037 state
.mFloatManager
->ClearContinues(FindTrailingClear())) {
1038 NS_FRAME_SET_INCOMPLETE(state
.mReflowStatus
);
1041 if (!NS_FRAME_IS_FULLY_COMPLETE(state
.mReflowStatus
)) {
1042 if (HasOverflowLines() || HasPushedFloats()) {
1043 state
.mReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
1047 ListTag(stdout
); printf(": block is not fully complete\n");
1051 // Place the "marker" (bullet) frame if it is placed next to a block
1054 // According to the CSS2 spec, section 12.6.1, the "marker" box
1055 // participates in the height calculation of the list-item box's
1058 // There are exactly two places a bullet can be placed: near the
1059 // first or second line. It's only placed on the second line in a
1060 // rare case: an empty first line followed by a second line that
1061 // contains a block (example: <LI>\n<P>... ). This is where
1062 // the second case can happen.
1063 if (HasOutsideBullet() && !mLines
.empty() &&
1064 (mLines
.front()->IsBlock() ||
1065 (0 == mLines
.front()->mBounds
.height
&&
1066 mLines
.front() != mLines
.back() &&
1067 mLines
.begin().next()->IsBlock()))) {
1068 // Reflow the bullet
1069 nsHTMLReflowMetrics metrics
;
1070 // XXX Use the entire line when we fix bug 25888.
1071 nsLayoutUtils::LinePosition position
;
1072 bool havePosition
= nsLayoutUtils::GetFirstLinePosition(this, &position
);
1073 nscoord lineTop
= havePosition
? position
.mTop
1074 : reflowState
->mComputedBorderPadding
.top
;
1075 nsIFrame
* bullet
= GetOutsideBullet();
1076 ReflowBullet(bullet
, state
, metrics
, lineTop
);
1077 NS_ASSERTION(!BulletIsEmpty() || metrics
.height
== 0,
1078 "empty bullet took up space");
1080 if (havePosition
&& !BulletIsEmpty()) {
1081 // We have some lines to align the bullet with.
1083 // Doing the alignment using the baseline will also cater for
1084 // bullets that are placed next to a child block (bug 92896)
1086 // Tall bullets won't look particularly nice here...
1087 nsRect bbox
= bullet
->GetRect();
1088 bbox
.y
= position
.mBaseline
- metrics
.ascent
;
1089 bullet
->SetRect(bbox
);
1091 // Otherwise just leave the bullet where it is, up against our top padding.
1096 // Compute our final size
1097 nscoord bottomEdgeOfChildren
;
1098 ComputeFinalSize(*reflowState
, state
, aMetrics
, &bottomEdgeOfChildren
);
1099 nsRect areaBounds
= nsRect(0, 0, aMetrics
.width
, aMetrics
.height
);
1100 ComputeOverflowAreas(areaBounds
, reflowState
->mStyleDisplay
,
1101 bottomEdgeOfChildren
, aMetrics
.mOverflowAreas
);
1102 // Factor overflow container child bounds into the overflow area
1103 aMetrics
.mOverflowAreas
.UnionWith(ocBounds
);
1104 // Factor pushed float child bounds into the overflow area
1105 aMetrics
.mOverflowAreas
.UnionWith(fcBounds
);
1107 // Let the absolutely positioned container reflow any absolutely positioned
1108 // child frames that need to be reflowed, e.g., elements with a percentage
1109 // based width/height
1110 // We want to do this under either of two conditions:
1111 // 1. If we didn't do the incremental reflow above.
1112 // 2. If our size changed.
1113 // Even though it's the padding edge that's the containing block, we
1114 // can use our rect (the border edge) since if the border style
1115 // changed, the reflow would have been targeted at us so we'd satisfy
1117 // XXX checking oldSize is bogus, there are various reasons we might have
1118 // reflowed but our size might not have been changed to what we
1119 // asked for (e.g., we ended up being pushed to a new page)
1120 // When WillReflowAgainForClearance is true, we will reflow again without
1121 // resetting the size. Because of this, we must not reflow our abs-pos children
1122 // in that situation --- what we think is our "new size"
1123 // will not be our real new size. This also happens to be more efficient.
1124 if (HasAbsolutelyPositionedChildren()) {
1125 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1126 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1127 if (reflowState
->WillReflowAgainForClearance() ||
1129 // Make sure that when we reflow again we'll actually reflow all the abs
1130 // pos frames that might conceivably depend on our size (or all of them,
1131 // if we're dirty right now and interrupted; in that case we also need
1132 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1133 // better than that, because we don't really know what our size will be,
1134 // and it might in fact not change on the followup reflow!
1135 if (haveInterrupt
&& (GetStateBits() & NS_FRAME_IS_DIRTY
)) {
1136 absoluteContainer
->MarkAllFramesDirty();
1138 absoluteContainer
->MarkSizeDependentFramesDirty();
1141 nsSize containingBlockSize
=
1142 CalculateContainingBlockSizeForAbsolutes(*reflowState
,
1143 nsSize(aMetrics
.width
,
1146 // Mark frames that depend on changes we just made to this frame as dirty:
1147 // Now we can assume that the padding edge hasn't moved.
1148 // We need to reflow the absolutes if one of them depends on
1149 // its placeholder position, or the containing block size in a
1150 // direction in which the containing block size might have
1152 bool cbWidthChanged
= aMetrics
.width
!= oldSize
.width
;
1153 bool isRoot
= !GetContent()->GetParent();
1154 // If isRoot and we have auto height, then we are the initial
1155 // containing block and the containing block height is the
1156 // viewport height, which can't change during incremental
1158 bool cbHeightChanged
=
1159 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== reflowState
->ComputedHeight()) &&
1160 aMetrics
.height
!= oldSize
.height
;
1162 nsRect
containingBlock(nsPoint(0, 0), containingBlockSize
);
1163 absoluteContainer
->Reflow(this, aPresContext
, *reflowState
,
1164 state
.mReflowStatus
,
1165 containingBlock
, true,
1166 cbWidthChanged
, cbHeightChanged
,
1167 &aMetrics
.mOverflowAreas
);
1169 //XXXfr Why isn't this rv (and others in this file) checked/returned?
1173 FinishAndStoreOverflow(&aMetrics
);
1175 // Clear the float manager pointer in the block reflow state so we
1176 // don't waste time translating the coordinate system back on a dead
1178 if (needFloatManager
)
1179 state
.mFloatManager
= nullptr;
1181 aStatus
= state
.mReflowStatus
;
1184 // Between when we drain pushed floats and when we complete reflow,
1185 // we're allowed to have multiple continuations of the same float on
1186 // our floats list, since a first-in-flow might get pushed to a later
1187 // continuation of its containing block. But it's not permitted
1188 // outside that time.
1189 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1192 IndentBy(stdout
, gNoiseIndent
);
1194 printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
1195 aStatus
, NS_FRAME_IS_COMPLETE(aStatus
) ? "" : "not ",
1196 aMetrics
.width
, aMetrics
.height
,
1197 aMetrics
.mCarriedOutBottomMargin
.get());
1198 if (HasOverflowAreas()) {
1199 printf(" overflow-vis={%d,%d,%d,%d}",
1200 aMetrics
.VisualOverflow().x
,
1201 aMetrics
.VisualOverflow().y
,
1202 aMetrics
.VisualOverflow().width
,
1203 aMetrics
.VisualOverflow().height
);
1204 printf(" overflow-scr={%d,%d,%d,%d}",
1205 aMetrics
.ScrollableOverflow().x
,
1206 aMetrics
.ScrollableOverflow().y
,
1207 aMetrics
.ScrollableOverflow().width
,
1208 aMetrics
.ScrollableOverflow().height
);
1213 if (gLameReflowMetrics
) {
1214 PRTime end
= PR_Now();
1216 int32_t ectc
= nsLineBox::GetCtorCount();
1217 int32_t numLines
= mLines
.size();
1218 if (!numLines
) numLines
= 1;
1219 PRTime delta
, perLineDelta
, lines
;
1220 lines
= int64_t(numLines
);
1221 delta
= end
- start
;
1222 perLineDelta
= delta
/ lines
;
1226 PR_snprintf(buf
, sizeof(buf
),
1227 ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
1228 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1229 printf("%s\n", buf
);
1233 NS_FRAME_SET_TRUNCATION(aStatus
, (*reflowState
), aMetrics
);
1238 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
1240 line_iterator begin
= begin_lines();
1241 line_iterator line
= end_lines();
1244 if (begin
== line
) {
1248 if (line
->mBounds
.height
!= 0 || !line
->CachedIsEmpty()) {
1251 if (line
->HasClearance()) {
1259 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState
& aReflowState
,
1260 nsBlockReflowState
& aState
,
1261 nsHTMLReflowMetrics
& aMetrics
,
1262 nscoord
* aBottomEdgeOfChildren
)
1264 const nsMargin
& borderPadding
= aState
.BorderPadding();
1265 #ifdef NOISY_FINAL_SIZE
1267 printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
1268 aState
.mY
, aState
.GetFlag(BRS_ISBOTTOMMARGINROOT
) ? "yes" : "no",
1269 aState
.mPrevBottomMargin
,
1270 borderPadding
.top
, borderPadding
.bottom
);
1273 // Compute final width
1275 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.left
,
1276 aReflowState
.ComputedWidth()),
1277 borderPadding
.right
);
1279 // Return bottom margin information
1280 // rbs says he hit this assertion occasionally (see bug 86947), so
1281 // just set the margin to zero and we'll figure out why later
1282 //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
1283 // "someone else set the margin");
1284 nscoord nonCarriedOutVerticalMargin
= 0;
1285 if (!aState
.GetFlag(BRS_ISBOTTOMMARGINROOT
)) {
1286 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1287 // line with clearance and a non-zero top margin and all
1288 // subsequent lines are empty, then we do not allow our children's
1289 // carried out bottom margin to be carried out of us and collapse
1290 // with our own bottom margin.
1291 if (CheckForCollapsedBottomMarginFromClearanceLine()) {
1292 // Convert the children's carried out margin to something that
1293 // we will include in our height
1294 nonCarriedOutVerticalMargin
= aState
.mPrevBottomMargin
.get();
1295 aState
.mPrevBottomMargin
.Zero();
1297 aMetrics
.mCarriedOutBottomMargin
= aState
.mPrevBottomMargin
;
1299 aMetrics
.mCarriedOutBottomMargin
.Zero();
1302 nscoord bottomEdgeOfChildren
= aState
.mY
+ nonCarriedOutVerticalMargin
;
1303 // Shrink wrap our height around our contents.
1304 if (aState
.GetFlag(BRS_ISBOTTOMMARGINROOT
) ||
1305 NS_UNCONSTRAINEDSIZE
!= aReflowState
.ComputedHeight()) {
1306 // When we are a bottom-margin root make sure that our last
1307 // childs bottom margin is fully applied. We also do this when
1308 // we have a computed height, since in that case the carried out
1309 // margin is not going to be applied anywhere, so we should note it
1310 // here to be included in the overflow area.
1311 // Apply the margin only if there's space for it.
1312 if (bottomEdgeOfChildren
< aState
.mReflowState
.availableHeight
)
1314 // Truncate bottom margin if it doesn't fit to our available height.
1315 bottomEdgeOfChildren
=
1316 std::min(bottomEdgeOfChildren
+ aState
.mPrevBottomMargin
.get(),
1317 aState
.mReflowState
.availableHeight
);
1320 if (aState
.GetFlag(BRS_FLOAT_MGR
)) {
1321 // Include the float manager's state to properly account for the
1322 // bottom margin of any floated elements; e.g., inside a table cell.
1323 nscoord floatHeight
=
1324 aState
.ClearFloats(bottomEdgeOfChildren
, NS_STYLE_CLEAR_LEFT_AND_RIGHT
,
1325 nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS
);
1326 bottomEdgeOfChildren
= std::max(bottomEdgeOfChildren
, floatHeight
);
1329 if (NS_UNCONSTRAINEDSIZE
!= aReflowState
.ComputedHeight()
1330 && (mParent
->GetType() != nsGkAtoms::columnSetFrame
||
1331 aReflowState
.parentReflowState
->availableHeight
== NS_UNCONSTRAINEDSIZE
)) {
1332 ComputeFinalHeight(aReflowState
, &aState
.mReflowStatus
,
1333 aState
.mY
+ nonCarriedOutVerticalMargin
,
1334 borderPadding
, aMetrics
, aState
.mConsumedHeight
);
1335 if (!NS_FRAME_IS_COMPLETE(aState
.mReflowStatus
)) {
1336 // Use the current height; continuations will take up the rest.
1337 // Do extend the height to at least consume the available
1338 // height, otherwise our left/right borders (for example) won't
1339 // extend all the way to the break.
1340 aMetrics
.height
= std::max(aReflowState
.availableHeight
,
1341 aState
.mY
+ nonCarriedOutVerticalMargin
);
1342 // ... but don't take up more height than is available
1343 nscoord effectiveComputedHeight
=
1344 GetEffectiveComputedHeight(aReflowState
, aState
.GetConsumedHeight());
1345 aMetrics
.height
= std::min(aMetrics
.height
,
1346 borderPadding
.top
+ effectiveComputedHeight
);
1347 // XXX It's pretty wrong that our bottom border still gets drawn on
1348 // on its own on the last-in-flow, even if we ran out of height
1349 // here. We need GetSkipSides to check whether we ran out of content
1350 // height in the current frame, not whether it's last-in-flow.
1353 // Don't carry out a bottom margin when our height is fixed.
1354 aMetrics
.mCarriedOutBottomMargin
.Zero();
1356 else if (NS_FRAME_IS_COMPLETE(aState
.mReflowStatus
)) {
1357 nscoord contentHeight
= bottomEdgeOfChildren
- borderPadding
.top
;
1358 nscoord autoHeight
= aReflowState
.ApplyMinMaxHeight(contentHeight
);
1359 if (autoHeight
!= contentHeight
) {
1360 // Our min-height or max-height made our height change. Don't carry out
1361 // our kids' bottom margins.
1362 aMetrics
.mCarriedOutBottomMargin
.Zero();
1364 autoHeight
+= borderPadding
.top
+ borderPadding
.bottom
;
1365 aMetrics
.height
= autoHeight
;
1368 NS_ASSERTION(aReflowState
.availableHeight
!= NS_UNCONSTRAINEDSIZE
,
1369 "Shouldn't be incomplete if availableHeight is UNCONSTRAINED.");
1370 aMetrics
.height
= std::max(aState
.mY
, aReflowState
.availableHeight
);
1371 if (aReflowState
.availableHeight
== NS_UNCONSTRAINEDSIZE
)
1372 // This should never happen, but it does. See bug 414255
1373 aMetrics
.height
= aState
.mY
;
1376 if (IS_TRUE_OVERFLOW_CONTAINER(this) &&
1377 NS_FRAME_IS_NOT_COMPLETE(aState
.mReflowStatus
)) {
1378 // Overflow containers can only be overflow complete.
1379 // Note that auto height overflow containers have no normal children
1380 NS_ASSERTION(aMetrics
.height
== 0, "overflow containers must be zero-height");
1381 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState
.mReflowStatus
);
1384 // Screen out negative heights --- can happen due to integer overflows :-(
1385 aMetrics
.height
= std::max(0, aMetrics
.height
);
1386 *aBottomEdgeOfChildren
= bottomEdgeOfChildren
;
1389 if (CRAZY_WIDTH(aMetrics
.width
) || CRAZY_HEIGHT(aMetrics
.height
)) {
1391 printf(": WARNING: desired:%d,%d\n", aMetrics
.width
, aMetrics
.height
);
1397 nsBlockFrame::ComputeOverflowAreas(const nsRect
& aBounds
,
1398 const nsStyleDisplay
* aDisplay
,
1399 nscoord aBottomEdgeOfChildren
,
1400 nsOverflowAreas
& aOverflowAreas
)
1402 // Compute the overflow areas of our children
1403 // XXX_perf: This can be done incrementally. It is currently one of
1404 // the things that makes incremental reflow O(N^2).
1405 nsOverflowAreas
areas(aBounds
, aBounds
);
1406 if (!ShouldApplyOverflowClipping(this, aDisplay
)) {
1407 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1410 areas
.UnionWith(line
->GetOverflowAreas());
1413 // Factor an outside bullet in; normally the bullet will be factored into
1414 // the line-box's overflow areas. However, if the line is a block
1415 // line then it won't; if there are no lines, it won't. So just
1416 // factor it in anyway (it can't hurt if it was already done).
1417 // XXXldb Can we just fix GetOverflowArea instead?
1418 nsIFrame
* outsideBullet
= GetOutsideBullet();
1419 if (outsideBullet
) {
1420 areas
.UnionAllWith(outsideBullet
->GetRect());
1423 // Factor in the bottom edge of the children. Child frames will be added
1424 // to the overflow area as we iterate through the lines, but their margins
1425 // won't, so we need to account for bottom margins here.
1426 // REVIEW: For now, we do this for both visual and scrollable area,
1427 // although when we make scrollable overflow area not be a subset of
1428 // visual, we can change this.
1429 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
1430 nsRect
& o
= areas
.Overflow(otype
);
1431 o
.height
= std::max(o
.YMost(), aBottomEdgeOfChildren
) - o
.y
;
1434 #ifdef NOISY_COMBINED_AREA
1436 printf(": ca=%d,%d,%d,%d\n", area
.x
, area
.y
, area
.width
, area
.height
);
1439 aOverflowAreas
= areas
;
1443 nsBlockFrame::UpdateOverflow()
1445 // We need to update the overflow areas of lines manually, as they
1446 // get cached and re-used otherwise. Lines aren't exposed as normal
1447 // frame children, so calling UnionChildOverflow alone will end up
1448 // using the old cached values.
1449 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1452 nsOverflowAreas lineAreas
;
1454 int32_t n
= line
->GetChildCount();
1455 for (nsIFrame
* lineFrame
= line
->mFirstChild
;
1456 n
> 0; lineFrame
= lineFrame
->GetNextSibling(), --n
) {
1457 ConsiderChildOverflow(lineAreas
, lineFrame
);
1460 // Consider the overflow areas of the floats attached to the line as well
1461 if (line
->HasFloats()) {
1462 for (nsFloatCache
* fc
= line
->GetFirstFloat(); fc
; fc
= fc
->Next()) {
1463 ConsiderChildOverflow(lineAreas
, fc
->mFloat
);
1467 line
->SetOverflowAreas(lineAreas
);
1470 // Line cursor invariants depend on the overflow areas of the lines, so
1471 // we must clear the line cursor since those areas may have changed.
1474 return nsBlockFrameSuper::UpdateOverflow();
1478 nsBlockFrame::LazyMarkLinesDirty()
1480 if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
) {
1481 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1482 line
!= line_end
; ++line
) {
1483 int32_t n
= line
->GetChildCount();
1484 for (nsIFrame
* lineFrame
= line
->mFirstChild
;
1485 n
> 0; lineFrame
= lineFrame
->GetNextSibling(), --n
) {
1486 if (NS_SUBTREE_DIRTY(lineFrame
)) {
1487 // NOTE: MarkLineDirty does more than just marking the line dirty.
1488 MarkLineDirty(line
, &mLines
);
1493 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
1498 nsBlockFrame::MarkLineDirty(line_iterator aLine
, const nsLineList
* aLineList
)
1502 aLine
->SetInvalidateTextRuns(true);
1505 IndentBy(stdout
, gNoiseIndent
);
1507 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
1511 // Mark previous line dirty if it's an inline line so that it can
1512 // maybe pullup something from the line just affected.
1513 // XXX We don't need to do this if aPrevLine ends in a break-after...
1514 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
1515 aLine
.prev()->IsInline()) {
1516 aLine
.prev()->MarkDirty();
1517 aLine
.prev()->SetInvalidateTextRuns(true);
1520 IndentBy(stdout
, gNoiseIndent
);
1522 printf(": mark prev-line %p dirty\n",
1523 static_cast<void*>(aLine
.prev().get()));
1530 * Test whether lines are certain to be aligned left so that we can make
1531 * resizing optimizations
1534 IsAlignedLeft(uint8_t aAlignment
,
1536 uint8_t aUnicodeBidi
,
1539 return aFrame
->IsSVGText() ||
1540 NS_STYLE_TEXT_ALIGN_LEFT
== aAlignment
||
1541 (((NS_STYLE_TEXT_ALIGN_DEFAULT
== aAlignment
&&
1542 NS_STYLE_DIRECTION_LTR
== aDirection
) ||
1543 (NS_STYLE_TEXT_ALIGN_END
== aAlignment
&&
1544 NS_STYLE_DIRECTION_RTL
== aDirection
)) &&
1545 !(NS_STYLE_UNICODE_BIDI_PLAINTEXT
& aUnicodeBidi
));
1549 nsBlockFrame::PrepareResizeReflow(nsBlockReflowState
& aState
)
1551 const nsStyleText
* styleText
= StyleText();
1552 const nsStyleTextReset
* styleTextReset
= StyleTextReset();
1553 // See if we can try and avoid marking all the lines as dirty
1554 bool tryAndSkipLines
=
1555 // The block must be LTR (bug 806284)
1556 StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_LTR
&&
1557 // The text must be left-aligned.
1558 IsAlignedLeft(styleText
->mTextAlign
,
1559 aState
.mReflowState
.mStyleVisibility
->mDirection
,
1560 styleTextReset
->mUnicodeBidi
,
1562 // The left content-edge must be a constant distance from the left
1564 !StylePadding()->mPadding
.GetLeft().HasPercent();
1567 if (gDisableResizeOpt
) {
1568 tryAndSkipLines
= false;
1571 if (!tryAndSkipLines
) {
1572 IndentBy(stdout
, gNoiseIndent
);
1574 printf(": marking all lines dirty: availWidth=%d textAlign=%d\n",
1575 aState
.mReflowState
.availableWidth
,
1576 styleText
->mTextAlign
);
1581 if (tryAndSkipLines
) {
1582 nscoord newAvailWidth
= aState
.mReflowState
.mComputedBorderPadding
.left
+
1583 aState
.mReflowState
.ComputedWidth();
1584 NS_ASSERTION(NS_UNCONSTRAINEDSIZE
!= aState
.mReflowState
.mComputedBorderPadding
.left
&&
1585 NS_UNCONSTRAINEDSIZE
!= aState
.mReflowState
.ComputedWidth(),
1586 "math on NS_UNCONSTRAINEDSIZE");
1590 IndentBy(stdout
, gNoiseIndent
);
1592 printf(": trying to avoid marking all lines dirty\n");
1596 // The last line might not be aligned left even if the rest of the block is
1597 bool skipLastLine
= NS_STYLE_TEXT_ALIGN_AUTO
== styleText
->mTextAlignLast
||
1598 IsAlignedLeft(styleText
->mTextAlignLast
,
1599 aState
.mReflowState
.mStyleVisibility
->mDirection
,
1600 styleTextReset
->mUnicodeBidi
,
1603 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1607 // We let child blocks make their own decisions the same
1609 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
1610 if (line
->IsBlock() ||
1611 line
->HasFloats() ||
1612 (!isLastLine
&& !line
->HasBreakAfter()) ||
1613 ((isLastLine
|| !line
->IsLineWrapped()) && !skipLastLine
) ||
1614 line
->ResizeReflowOptimizationDisabled() ||
1615 line
->IsImpactedByFloat() ||
1616 (line
->mBounds
.XMost() > newAvailWidth
)) {
1620 #ifdef REALLY_NOISY_REFLOW
1621 if (!line
->IsBlock()) {
1622 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
1623 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
1627 if (gNoisyReflow
&& !line
->IsDirty()) {
1628 IndentBy(stdout
, gNoiseIndent
+ 1);
1629 printf("skipped: line=%p next=%p %s %s%s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
1630 static_cast<void*>(line
.get()),
1631 static_cast<void*>((line
.next() != end_lines() ? line
.next().get() : nullptr)),
1632 line
->IsBlock() ? "block" : "inline",
1633 line
->HasBreakAfter() ? "has-break-after " : "",
1634 line
->HasFloats() ? "has-floats " : "",
1635 line
->IsImpactedByFloat() ? "impacted " : "",
1636 skipLastLine
? "last-line-left-aligned " : "",
1637 line
->GetBreakTypeBefore(), line
->GetBreakTypeAfter(),
1638 line
->mBounds
.XMost());
1644 // Mark everything dirty
1645 for (line_iterator line
= begin_lines(), line_end
= end_lines();
1654 //----------------------------------------
1657 * Propagate reflow "damage" from from earlier lines to the current
1658 * line. The reflow damage comes from the following sources:
1659 * 1. The regions of float damage remembered during reflow.
1660 * 2. The combination of nonzero |aDeltaY| and any impact by a float,
1661 * either the previous reflow or now.
1663 * When entering this function, |aLine| is still at its old position and
1664 * |aDeltaY| indicates how much it will later be slid (assuming it
1665 * doesn't get marked dirty and reflowed entirely).
1668 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState
& aState
,
1672 nsFloatManager
*floatManager
= aState
.mReflowState
.mFloatManager
;
1673 NS_ASSERTION((aState
.mReflowState
.parentReflowState
&&
1674 aState
.mReflowState
.parentReflowState
->mFloatManager
== floatManager
) ||
1675 aState
.mReflowState
.mBlockDelta
== 0, "Bad block delta passed in");
1677 // Check to see if there are any floats; if there aren't, there can't
1678 // be any float damage
1679 if (!floatManager
->HasAnyFloats())
1682 // Check the damage region recorded in the float damage.
1683 if (floatManager
->HasFloatDamage()) {
1684 // Need to check mBounds *and* mCombinedArea to find intersections
1685 // with aLine's floats
1686 nscoord lineYA
= aLine
->mBounds
.y
+ aDeltaY
;
1687 nscoord lineYB
= lineYA
+ aLine
->mBounds
.height
;
1688 // Scrollable overflow should be sufficient for things that affect
1690 nsRect overflow
= aLine
->GetOverflowArea(eScrollableOverflow
);
1691 nscoord lineYCombinedA
= overflow
.y
+ aDeltaY
;
1692 nscoord lineYCombinedB
= lineYCombinedA
+ overflow
.height
;
1693 if (floatManager
->IntersectsDamage(lineYA
, lineYB
) ||
1694 floatManager
->IntersectsDamage(lineYCombinedA
, lineYCombinedB
)) {
1700 // Check if the line is moving relative to the float manager
1701 if (aDeltaY
+ aState
.mReflowState
.mBlockDelta
!= 0) {
1702 if (aLine
->IsBlock()) {
1703 // Unconditionally reflow sliding blocks; we only really need to reflow
1704 // if there's a float impacting this block, but the current float manager
1705 // makes it difficult to check that. Therefore, we let the child block
1706 // decide what it needs to reflow.
1709 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
1710 nsFlowAreaRect floatAvailableSpace
=
1711 aState
.GetFloatAvailableSpaceForHeight(aLine
->mBounds
.y
+ aDeltaY
,
1712 aLine
->mBounds
.height
,
1715 #ifdef REALLY_NOISY_REFLOW
1716 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
1717 this, wasImpactedByFloat
, floatAvailableSpace
.mHasFloats
);
1720 // Mark the line dirty if it was or is affected by a float
1721 // We actually only really need to reflow if the amount of impact
1722 // changes, but that's not straightforward to check
1723 if (wasImpactedByFloat
|| floatAvailableSpace
.mHasFloats
) {
1730 static bool LineHasClear(nsLineBox
* aLine
) {
1731 return aLine
->IsBlock()
1732 ? (aLine
->GetBreakTypeBefore() ||
1733 (aLine
->mFirstChild
->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
1734 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
1735 : aLine
->HasFloatBreakAfter();
1740 * Reparent a whole list of floats from aOldParent to this block. The
1741 * floats might be taken from aOldParent's overflow list. They will be
1742 * removed from the list. They end up appended to our mFloats list.
1745 nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
, nsBlockFrame
* aOldParent
,
1746 bool aReparentSiblings
) {
1748 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
1749 if (list
.NotEmpty()) {
1750 for (nsIFrame
* f
= list
.FirstChild(); f
; f
= f
->GetNextSibling()) {
1751 ReparentFrame(f
, aOldParent
, this);
1753 mFloats
.AppendFrames(nullptr, list
);
1757 static void DumpLine(const nsBlockReflowState
& aState
, nsLineBox
* aLine
,
1758 nscoord aDeltaY
, int32_t aDeltaIndent
) {
1760 if (nsBlockFrame::gNoisyReflow
) {
1761 nsRect
ovis(aLine
->GetVisualOverflowArea());
1762 nsRect
oscr(aLine
->GetScrollableOverflowArea());
1763 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
1764 printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
1765 static_cast<void*>(aLine
), aState
.mY
,
1766 aLine
->IsDirty() ? "yes" : "no",
1767 aLine
->mBounds
.x
, aLine
->mBounds
.y
,
1768 aLine
->mBounds
.width
, aLine
->mBounds
.height
,
1769 ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
1770 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
,
1771 aDeltaY
, aState
.mPrevBottomMargin
.get(), aLine
->GetChildCount());
1777 * Reflow the dirty lines
1780 nsBlockFrame::ReflowDirtyLines(nsBlockReflowState
& aState
)
1782 nsresult rv
= NS_OK
;
1783 bool keepGoing
= true;
1784 bool repositionViews
= false; // should we really need this?
1785 bool foundAnyClears
= aState
.mFloatBreakType
!= NS_STYLE_CLEAR_NONE
;
1786 bool willReflowAgain
= false;
1790 IndentBy(stdout
, gNoiseIndent
);
1792 printf(": reflowing dirty lines");
1793 printf(" computedWidth=%d\n", aState
.mReflowState
.ComputedWidth());
1795 AutoNoisyIndenter
indent(gNoisyReflow
);
1798 bool selfDirty
= (GetStateBits() & NS_FRAME_IS_DIRTY
) ||
1799 (aState
.mReflowState
.mFlags
.mVResize
&&
1800 (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
));
1802 // Reflow our last line if our availableHeight has increased
1803 // so that we (and our last child) pull up content as necessary
1804 if (aState
.mReflowState
.availableHeight
!= NS_UNCONSTRAINEDSIZE
1805 && GetNextInFlow() && aState
.mReflowState
.availableHeight
> mRect
.height
) {
1806 line_iterator lastLine
= end_lines();
1807 if (lastLine
!= begin_lines()) {
1809 lastLine
->MarkDirty();
1812 // the amount by which we will slide the current line if it is not
1816 // whether we did NOT reflow the previous line and thus we need to
1817 // recompute the carried out margin before the line if we want to
1818 // reflow it or if its previous margin is dirty
1819 bool needToRecoverState
= false;
1820 // Float continuations were reflowed in ReflowPushedFloats
1821 bool reflowedFloat
= mFloats
.NotEmpty() &&
1822 (mFloats
.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
);
1823 bool lastLineMovedUp
= false;
1824 // We save up information about BR-clearance here
1825 uint8_t inlineFloatBreakType
= aState
.mFloatBreakType
;
1827 line_iterator line
= begin_lines(), line_end
= end_lines();
1829 // Reflow the lines that are already ours
1830 for ( ; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
1831 DumpLine(aState
, line
, deltaY
, 0);
1833 AutoNoisyIndenter
indent2(gNoisyReflow
);
1839 // This really sucks, but we have to look inside any blocks that have clear
1840 // elements inside them.
1841 // XXX what can we do smarter here?
1842 if (!line
->IsDirty() && line
->IsBlock() &&
1843 (line
->mFirstChild
->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
1847 nsIFrame
*replacedBlock
= nullptr;
1848 if (line
->IsBlock() &&
1849 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
1850 replacedBlock
= line
->mFirstChild
;
1853 // We have to reflow the line if it's a block whose clearance
1854 // might have changed, so detect that.
1855 if (!line
->IsDirty() &&
1856 (line
->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE
||
1858 nscoord curY
= aState
.mY
;
1859 // See where we would be after applying any clearance due to
1861 if (inlineFloatBreakType
!= NS_STYLE_CLEAR_NONE
) {
1862 curY
= aState
.ClearFloats(curY
, inlineFloatBreakType
);
1866 aState
.ClearFloats(curY
, line
->GetBreakTypeBefore(), replacedBlock
);
1868 if (line
->HasClearance()) {
1869 // Reflow the line if it might not have clearance anymore.
1871 // aState.mY is the clearance point which should be the
1872 // top border-edge of the block frame. If sliding the
1873 // block by deltaY isn't going to put it in the predicted
1874 // position, then we'd better reflow the line.
1875 || newY
!= line
->mBounds
.y
+ deltaY
) {
1879 // Reflow the line if the line might have clearance now.
1886 // We might have to reflow a line that is after a clearing BR.
1887 if (inlineFloatBreakType
!= NS_STYLE_CLEAR_NONE
) {
1888 aState
.mY
= aState
.ClearFloats(aState
.mY
, inlineFloatBreakType
);
1889 if (aState
.mY
!= line
->mBounds
.y
+ deltaY
) {
1890 // SlideLine is not going to put the line where the clearance
1891 // put it. Reflow the line to be sure.
1894 inlineFloatBreakType
= NS_STYLE_CLEAR_NONE
;
1897 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
1898 if (previousMarginWasDirty
) {
1899 // If the previous margin is dirty, reflow the current line
1901 line
->ClearPreviousMarginDirty();
1902 } else if (line
->mBounds
.YMost() + deltaY
> aState
.mBottomEdge
) {
1903 // Lines that aren't dirty but get slid past our height constraint must
1908 // If we have a constrained height (i.e., breaking columns/pages),
1909 // and the distance to the bottom might have changed, then we need
1910 // to reflow any line that might have floats in it, both because the
1911 // breakpoints within those floats may have changed and because we
1912 // might have to push/pull the floats in their entirety.
1913 // FIXME: What about a deltaY or height change that forces us to
1914 // push lines? Why does that work?
1915 if (!line
->IsDirty() &&
1916 aState
.mReflowState
.availableHeight
!= NS_UNCONSTRAINEDSIZE
&&
1917 (deltaY
!= 0 || aState
.mReflowState
.mFlags
.mVResize
||
1918 aState
.mReflowState
.mFlags
.mMustReflowPlaceholders
) &&
1919 (line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed())) {
1923 if (!line
->IsDirty()) {
1924 // See if there's any reflow damage that requires that we mark the
1926 PropagateFloatDamage(aState
, line
, deltaY
);
1929 if (needToRecoverState
&& line
->IsDirty()) {
1930 // We need to reconstruct the bottom margin only if we didn't
1931 // reflow the previous line and we do need to reflow (or repair
1932 // the top position of) the next line.
1933 aState
.ReconstructMarginAbove(line
);
1936 bool reflowedPrevLine
= !needToRecoverState
;
1937 if (needToRecoverState
) {
1938 needToRecoverState
= false;
1940 // Update aState.mPrevChild as if we had reflowed all of the frames in
1942 if (line
->IsDirty())
1943 NS_ASSERTION(line
->mFirstChild
->GetPrevSibling() ==
1944 line
.prev()->LastChild(), "unexpected line frames");
1945 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
1948 // Now repair the line and update |aState.mY| by calling
1949 // |ReflowLine| or |SlideLine|.
1950 // If we're going to reflow everything again, then no need to reflow
1951 // the dirty line ... unless the line has floats, in which case we'd
1952 // better reflow it now to refresh its float cache, which may contain
1953 // dangling frame pointers! Ugh! This reflow of the line may be
1954 // incorrect because we skipped reflowing previous lines (e.g., floats
1955 // may be placed incorrectly), but that's OK because we'll mark the
1956 // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..."
1957 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
1958 lastLineMovedUp
= true;
1960 bool maybeReflowingForFirstTime
=
1961 line
->mBounds
.x
== 0 && line
->mBounds
.y
== 0 &&
1962 line
->mBounds
.width
== 0 && line
->mBounds
.height
== 0;
1964 // Compute the dirty lines "before" YMost, after factoring in
1965 // the running deltaY value - the running value is implicit in
1967 nscoord oldY
= line
->mBounds
.y
;
1968 nscoord oldYMost
= line
->mBounds
.YMost();
1970 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
1971 "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
1973 // Reflow the dirty line. If it's an incremental reflow, then force
1974 // it to invalidate the dirty area if necessary
1975 rv
= ReflowLine(aState
, line
, &keepGoing
);
1976 NS_ENSURE_SUCCESS(rv
, rv
);
1978 if (aState
.mReflowState
.WillReflowAgainForClearance()) {
1980 willReflowAgain
= true;
1981 // Note that once we've entered this state, every line that gets here
1982 // (e.g. because it has floats) gets marked dirty and reflowed again.
1983 // in the next pass. This is important, see above.
1986 if (line
->HasFloats()) {
1987 reflowedFloat
= true;
1991 DumpLine(aState
, line
, deltaY
, -1);
1992 if (0 == line
->GetChildCount()) {
1993 DeleteLine(aState
, line
, line_end
);
1998 // Test to see whether the margin that should be carried out
1999 // to the next line (NL) might have changed. In ReflowBlockFrame
2000 // we call nextLine->MarkPreviousMarginDirty if the block's
2001 // actual carried-out bottom margin changed. So here we only
2002 // need to worry about the following effects:
2003 // 1) the line was just created, and it might now be blocking
2004 // a carried-out bottom margin from previous lines that
2005 // used to reach NL from reaching NL
2006 // 2) the line used to be empty, and is now not empty,
2007 // thus blocking a carried-out bottom margin from previous lines
2008 // that used to reach NL from reaching NL
2009 // 3) the line wasn't empty, but now is, so a carried-out
2010 // bottom margin from previous lines that didn't used to reach NL
2012 // 4) the line might have changed in a way that affects NL's
2013 // ShouldApplyTopMargin decision. The three things that matter
2014 // are the line's emptiness, its adjacency to the top of the block,
2015 // and whether it has clearance (the latter only matters if the block
2016 // was and is adjacent to the top and empty).
2018 // If the line is empty now, we can't reliably tell if the line was empty
2019 // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
2020 // This means the checks in 4) are redundant; if the line is empty now
2021 // we don't need to check 4), but if the line is not empty now and we're sure
2022 // it wasn't empty before, any adjacency and clearance changes are irrelevant
2023 // to the result of nextLine->ShouldApplyTopMargin.
2024 if (line
.next() != end_lines()) {
2025 bool maybeWasEmpty
= oldY
== line
.next()->mBounds
.y
;
2026 bool isEmpty
= line
->CachedIsEmpty();
2027 if (maybeReflowingForFirstTime
/*1*/ ||
2028 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
2029 line
.next()->MarkPreviousMarginDirty();
2030 // since it's marked dirty, nobody will care about |deltaY|
2034 // If the line was just reflowed for the first time, then its
2035 // old mBounds cannot be trusted so this deltaY computation is
2036 // bogus. But that's OK because we just did
2037 // MarkPreviousMarginDirty on the next line which will force it
2038 // to be reflowed, so this computation of deltaY will not be
2040 deltaY
= line
->mBounds
.YMost() - oldYMost
;
2042 // Now do an interrupt check. We want to do this only in the case when we
2043 // actually reflow the line, so that if we get back in here we'll get
2044 // further on the reflow before interrupting.
2045 aState
.mPresContext
->CheckForInterrupt(this);
2047 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
2048 // Nop except for blocks (we don't create overflow container
2049 // continuations for any inlines atm), so only checking mFirstChild
2052 lastLineMovedUp
= deltaY
< 0;
2055 SlideLine(aState
, line
, deltaY
);
2057 repositionViews
= true;
2059 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
2060 "Possibly stale float cache here!");
2061 if (willReflowAgain
&& line
->IsBlock()) {
2062 // If we're going to reflow everything again, and this line is a block,
2063 // then there is no need to recover float state. The line may contain
2064 // other lines with floats, but in that case RecoverStateFrom would only
2065 // add floats to the float manager. We don't need to do that because
2066 // everything's going to get reflowed again "for real". Calling
2067 // RecoverStateFrom in this situation could be lethal because the
2068 // block's descendant lines may have float caches containing dangling
2069 // frame pointers. Ugh!
2070 // If this line is inline, then we need to recover its state now
2071 // to make sure that we don't forget to move its floats by deltaY.
2073 // XXX EVIL O(N^2) EVIL
2074 aState
.RecoverStateFrom(line
, deltaY
);
2077 // Keep mY up to date in case we're propagating reflow damage
2078 // and also because our final height may depend on it. If the
2079 // line is inlines, then only update mY if the line is not
2080 // empty, because that's what PlaceLine does. (Empty blocks may
2081 // want to update mY, e.g. if they have clearance.)
2082 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
2083 aState
.mY
= line
->mBounds
.YMost();
2086 needToRecoverState
= true;
2088 if (reflowedPrevLine
&& !line
->IsBlock() &&
2089 aState
.mPresContext
->HasPendingInterrupt()) {
2090 // Need to make sure to pull overflows from any prev-in-flows
2091 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
2092 inlineKid
= inlineKid
->GetFirstPrincipalChild()) {
2093 inlineKid
->PullOverflowsFromPrevInFlow();
2098 // Record if we need to clear floats before reflowing the next
2099 // line. Note that inlineFloatBreakType will be handled and
2100 // cleared before the next line is processed, so there is no
2101 // need to combine break types here.
2102 if (line
->HasFloatBreakAfter()) {
2103 inlineFloatBreakType
= line
->GetBreakTypeAfter();
2106 if (LineHasClear(line
.get())) {
2107 foundAnyClears
= true;
2110 DumpLine(aState
, line
, deltaY
, -1);
2112 if (aState
.mPresContext
->HasPendingInterrupt()) {
2113 willReflowAgain
= true;
2114 // Another option here might be to leave |line| clean if
2115 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
2116 // that case the line really did reflow as it should have. Not sure
2117 // whether that would be safe, so doing this for now instead. Also not
2118 // sure whether we really want to mark all lines dirty after an
2119 // interrupt, but until we get better at propagating float damage we
2120 // really do need to do it this way; see comments inside MarkLineDirty.
2121 MarkLineDirtyForInterrupt(line
);
2125 // Handle BR-clearance from the last line of the block
2126 if (inlineFloatBreakType
!= NS_STYLE_CLEAR_NONE
) {
2127 aState
.mY
= aState
.ClearFloats(aState
.mY
, inlineFloatBreakType
);
2130 if (needToRecoverState
) {
2131 // Is this expensive?
2132 aState
.ReconstructMarginAbove(line
);
2134 // Update aState.mPrevChild as if we had reflowed all of the frames in
2136 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
2137 line
.prev()->LastChild(), "unexpected line frames");
2139 line
== line_end
? mFrames
.LastChild() : line
->mFirstChild
->GetPrevSibling();
2142 // Should we really have to do this?
2143 if (repositionViews
)
2144 nsContainerFrame::PlaceFrameView(this);
2146 // We can skip trying to pull up the next line if our height is constrained
2147 // (so we can report being incomplete) and there is no next in flow or we
2148 // were told not to or we know it will be futile, i.e.,
2149 // -- the next in flow is not changing
2150 // -- and we cannot have added more space for its first line to be
2152 // -- it's an incremental reflow of a descendant
2153 // -- and we didn't reflow any floats (so the available space
2155 // -- my chain of next-in-flows either has no first line, or its first
2156 // line isn't dirty.
2157 bool heightConstrained
=
2158 aState
.mReflowState
.availableHeight
!= NS_UNCONSTRAINEDSIZE
;
2159 bool skipPull
= willReflowAgain
&& heightConstrained
;
2160 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
2161 (aState
.mReflowState
.mFlags
.mNextInFlowUntouched
&&
2163 !(GetStateBits() & NS_FRAME_IS_DIRTY
) &&
2165 // We'll place lineIter at the last line of this block, so that
2166 // nsBlockInFlowLineIterator::Next() will take us to the first
2167 // line of my next-in-flow-chain. (But first, check that I
2168 // have any lines -- if I don't, just bail out of this
2170 line_iterator lineIter
= this->end_lines();
2171 if (lineIter
!= this->begin_lines()) {
2172 lineIter
--; // I have lines; step back from dummy iterator to last line.
2173 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
2175 // Check for next-in-flow-chain's first line.
2176 // (First, see if there is such a line, and second, see if it's clean)
2177 if (!bifLineIter
.Next() ||
2178 !bifLineIter
.GetLine()->IsDirty()) {
2184 if (skipPull
&& aState
.mNextInFlow
) {
2185 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
2186 if (IS_TRUE_OVERFLOW_CONTAINER(aState
.mNextInFlow
))
2187 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState
.mReflowStatus
);
2189 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
2192 if (!skipPull
&& aState
.mNextInFlow
) {
2193 // Pull data from a next-in-flow if there's still room for more
2195 while (keepGoing
&& aState
.mNextInFlow
) {
2196 // Grab first line from our next-in-flow
2197 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
2198 nsLineBox
* pulledLine
;
2199 nsFrameList pulledFrames
;
2200 if (!nextInFlow
->mLines
.empty()) {
2201 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
,
2202 &pulledLine
, &pulledFrames
);
2204 // Grab an overflow line if there are any
2205 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
2206 if (!overflowLines
) {
2207 aState
.mNextInFlow
=
2208 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
2212 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
2213 &pulledLine
, &pulledFrames
);
2215 nextInFlow
->DestroyOverflowLines();
2219 if (pulledFrames
.IsEmpty()) {
2220 // The line is empty. Try the next one.
2221 NS_ASSERTION(pulledLine
->GetChildCount() == 0 &&
2222 !pulledLine
->mFirstChild
, "bad empty line");
2223 nextInFlow
->FreeLineBox(pulledLine
);
2227 if (pulledLine
== nextInFlow
->GetLineCursor()) {
2228 nextInFlow
->ClearLineCursor();
2230 ReparentFrames(pulledFrames
, nextInFlow
, this);
2232 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
2233 "Unexpected last frame");
2234 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(), "should have a prevchild here");
2235 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
2236 "Incorrect aState.mPrevChild before inserting line at end");
2238 // Shift pulledLine's frames into our mFrames list.
2239 mFrames
.AppendFrames(nullptr, pulledFrames
);
2241 // Add line to our line list, and set its last child as our new prev-child
2242 line
= mLines
.before_insert(end_lines(), pulledLine
);
2243 aState
.mPrevChild
= mFrames
.LastChild();
2245 // Reparent floats whose placeholders are in the line.
2246 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
2248 DumpLine(aState
, pulledLine
, deltaY
, 0);
2250 AutoNoisyIndenter
indent2(gNoisyReflow
);
2253 if (aState
.mPresContext
->HasPendingInterrupt()) {
2254 MarkLineDirtyForInterrupt(line
);
2256 // Now reflow it and any lines that it makes during it's reflow
2257 // (we have to loop here because reflowing the line may cause a new
2258 // line to be created; see SplitLine's callers for examples of
2259 // when this happens).
2260 while (line
!= end_lines()) {
2261 rv
= ReflowLine(aState
, line
, &keepGoing
);
2262 NS_ENSURE_SUCCESS(rv
, rv
);
2264 if (aState
.mReflowState
.WillReflowAgainForClearance()) {
2267 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
2271 DumpLine(aState
, line
, deltaY
, -1);
2273 if (0 == line
->GetChildCount()) {
2274 DeleteLine(aState
, line
, line_end
);
2279 if (LineHasClear(line
.get())) {
2280 foundAnyClears
= true;
2283 if (aState
.mPresContext
->CheckForInterrupt(this)) {
2284 MarkLineDirtyForInterrupt(line
);
2288 // If this is an inline frame then its time to stop
2290 aState
.AdvanceToNextLine();
2295 if (NS_FRAME_IS_NOT_COMPLETE(aState
.mReflowStatus
)) {
2296 aState
.mReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
2297 } //XXXfr shouldn't set this flag when nextinflow has no lines
2300 // Handle an odd-ball case: a list-item with no lines
2301 if (HasOutsideBullet() && mLines
.empty()) {
2302 nsHTMLReflowMetrics metrics
;
2303 nsIFrame
* bullet
= GetOutsideBullet();
2304 ReflowBullet(bullet
, aState
, metrics
,
2305 aState
.mReflowState
.mComputedBorderPadding
.top
);
2306 NS_ASSERTION(!BulletIsEmpty() || metrics
.height
== 0,
2307 "empty bullet took up space");
2309 if (!BulletIsEmpty()) {
2310 // There are no lines so we have to fake up some y motion so that
2311 // we end up with *some* height.
2313 if (metrics
.ascent
== nsHTMLReflowMetrics::ASK_FOR_BASELINE
&&
2314 !nsLayoutUtils::GetFirstLineBaseline(bullet
, &metrics
.ascent
)) {
2315 metrics
.ascent
= metrics
.height
;
2318 nsRefPtr
<nsFontMetrics
> fm
;
2319 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
2320 nsLayoutUtils::FontSizeInflationFor(this));
2321 aState
.mReflowState
.rendContext
->SetFont(fm
); // FIXME: needed?
2324 nsLayoutUtils::GetCenteredFontBaseline(fm
, aState
.mMinLineHeight
);
2325 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
2327 aState
.mY
+= std::max(minAscent
, metrics
.ascent
) +
2328 std::max(minDescent
, metrics
.height
- metrics
.ascent
);
2330 nscoord offset
= minAscent
- metrics
.ascent
;
2332 bullet
->SetRect(bullet
->GetRect() + nsPoint(0, offset
));
2337 if (foundAnyClears
) {
2338 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
2340 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
2345 VerifyOverflowSituation();
2347 IndentBy(stdout
, gNoiseIndent
- 1);
2349 printf(": done reflowing dirty lines (status=%x)\n",
2350 aState
.mReflowStatus
);
2357 static void MarkAllDescendantLinesDirty(nsBlockFrame
* aBlock
)
2359 nsLineList::iterator line
= aBlock
->begin_lines();
2360 nsLineList::iterator endLine
= aBlock
->end_lines();
2361 while (line
!= endLine
) {
2362 if (line
->IsBlock()) {
2363 nsIFrame
* f
= line
->mFirstChild
;
2364 nsBlockFrame
* bf
= nsLayoutUtils::GetAsBlock(f
);
2366 MarkAllDescendantLinesDirty(bf
);
2375 nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
)
2379 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
2380 // marked the lines that need to be marked dirty based on our
2381 // vertical resize stuff. So we'll definitely reflow all those kids;
2382 // the only question is how they should behave.
2383 if (GetStateBits() & NS_FRAME_IS_DIRTY
) {
2384 // Mark all our child frames dirty so we make sure to reflow them
2386 int32_t n
= aLine
->GetChildCount();
2387 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
2388 f
= f
->GetNextSibling(), --n
) {
2389 f
->AddStateBits(NS_FRAME_IS_DIRTY
);
2391 // And mark all the floats whose reflows we might be skipping dirty too.
2392 if (aLine
->HasFloats()) {
2393 for (nsFloatCache
* fc
= aLine
->GetFirstFloat(); fc
; fc
= fc
->Next()) {
2394 fc
->mFloat
->AddStateBits(NS_FRAME_IS_DIRTY
);
2398 // Dirty all the descendant lines of block kids to handle float damage,
2399 // since our nsFloatManager will go away by the next time we're reflowing.
2400 // XXXbz Can we do something more like what PropagateFloatDamage does?
2401 // Would need to sort out the exact business with mBlockDelta for that....
2402 // This marks way too much dirty. If we ever make this better, revisit
2403 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
2404 nsBlockFrame
* bf
= nsLayoutUtils::GetAsBlock(aLine
->mFirstChild
);
2406 MarkAllDescendantLinesDirty(bf
);
2412 nsBlockFrame::DeleteLine(nsBlockReflowState
& aState
,
2413 nsLineList::iterator aLine
,
2414 nsLineList::iterator aLineEnd
)
2416 NS_PRECONDITION(0 == aLine
->GetChildCount(), "can't delete !empty line");
2417 if (0 == aLine
->GetChildCount()) {
2418 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
2419 "using function more generally than designed, "
2420 "but perhaps OK now");
2421 nsLineBox
* line
= aLine
;
2422 aLine
= mLines
.erase(aLine
);
2424 // Mark the previous margin of the next line dirty since we need to
2425 // recompute its top position.
2426 if (aLine
!= aLineEnd
)
2427 aLine
->MarkPreviousMarginDirty();
2432 * Reflow a line. The line will either contain a single block frame
2433 * or contain 1 or more inline frames. aKeepReflowGoing indicates
2434 * whether or not the caller should continue to reflow more lines.
2437 nsBlockFrame::ReflowLine(nsBlockReflowState
& aState
,
2438 line_iterator aLine
,
2439 bool* aKeepReflowGoing
)
2441 nsresult rv
= NS_OK
;
2443 NS_ABORT_IF_FALSE(aLine
->GetChildCount(), "reflowing empty line");
2445 // Setup the line-layout for the new line
2446 aState
.mCurrentLine
= aLine
;
2447 aLine
->ClearDirty();
2448 aLine
->InvalidateCachedIsEmpty();
2449 aLine
->ClearHadFloatPushed();
2451 // Now that we know what kind of line we have, reflow it
2452 if (aLine
->IsBlock()) {
2453 rv
= ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
2455 aLine
->SetLineWrapped(false);
2456 rv
= ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
2463 nsBlockFrame::PullFrame(nsBlockReflowState
& aState
,
2464 line_iterator aLine
)
2466 // First check our remaining lines.
2467 if (end_lines() != aLine
.next()) {
2468 return PullFrameFrom(aLine
, this, aLine
.next());
2471 NS_ASSERTION(!GetOverflowLines(),
2472 "Our overflow lines should have been removed at the start of reflow");
2474 // Try each next-in-flow.
2475 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
2476 while (nextInFlow
) {
2477 if (nextInFlow
->mLines
.empty()) {
2478 nextInFlow
->DrainSelfOverflowList();
2480 if (!nextInFlow
->mLines
.empty()) {
2481 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
2483 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
2484 aState
.mNextInFlow
= nextInFlow
;
2491 nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
2492 nsBlockFrame
* aFromContainer
,
2493 nsLineList::iterator aFromLine
)
2495 nsLineBox
* fromLine
= aFromLine
;
2496 NS_ABORT_IF_FALSE(fromLine
, "bad line to pull from");
2497 NS_ABORT_IF_FALSE(fromLine
->GetChildCount(), "empty line");
2498 NS_ABORT_IF_FALSE(aLine
->GetChildCount(), "empty line");
2500 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
2501 "Disagreement about whether it's a block or not");
2503 if (fromLine
->IsBlock()) {
2504 // If our line is not empty and the child in aFromLine is a block
2505 // then we cannot pull up the frame into this line. In this case
2509 // Take frame from fromLine
2510 nsIFrame
* frame
= fromLine
->mFirstChild
;
2511 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
2513 if (aFromContainer
!= this) {
2514 // The frame is being pulled from a next-in-flow; therefore we
2515 // need to add it to our sibling list.
2516 MOZ_ASSERT(aLine
== mLines
.back());
2517 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
2518 "should only pull from first line");
2519 aFromContainer
->mFrames
.RemoveFrame(frame
);
2521 // When pushing and pulling frames we need to check for whether any
2522 // views need to be reparented.
2523 ReparentFrame(frame
, aFromContainer
, this);
2524 mFrames
.AppendFrame(nullptr, frame
);
2526 // The frame might have (or contain) floats that need to be brought
2527 // over too. (pass 'false' since there are no siblings to check)
2528 ReparentFloats(frame
, aFromContainer
, false);
2530 MOZ_ASSERT(aLine
== aFromLine
.prev());
2533 aLine
->NoteFrameAdded(frame
);
2534 fromLine
->NoteFrameRemoved(frame
);
2536 if (fromLine
->GetChildCount() > 0) {
2537 // Mark line dirty now that we pulled a child
2538 fromLine
->MarkDirty();
2539 fromLine
->mFirstChild
= newFirstChild
;
2541 // Free up the fromLine now that it's empty.
2542 // Its bounds might need to be redrawn, though.
2543 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
2544 aFromLine
.next()->MarkPreviousMarginDirty();
2546 aFromContainer
->mLines
.erase(aFromLine
);
2547 // aFromLine is now invalid
2548 aFromContainer
->FreeLineBox(fromLine
);
2553 VerifyOverflowSituation();
2560 nsBlockFrame::SlideLine(nsBlockReflowState
& aState
,
2561 nsLineBox
* aLine
, nscoord aDY
)
2563 NS_PRECONDITION(aDY
!= 0, "why slide a line nowhere?");
2565 // Adjust line state
2566 aLine
->SlideBy(aDY
);
2568 // Adjust the frames in the line
2569 nsIFrame
* kid
= aLine
->mFirstChild
;
2574 if (aLine
->IsBlock()) {
2576 kid
->MovePositionBy(nsPoint(0, aDY
));
2579 // Make sure the frame's view and any child views are updated
2580 nsContainerFrame::PlaceFrameView(kid
);
2583 // Adjust the Y coordinate of the frames in the line.
2584 // Note: we need to re-position views even if aDY is 0, because
2585 // one of our parent frames may have moved and so the view's position
2586 // relative to its parent may have changed
2587 int32_t n
= aLine
->GetChildCount();
2590 kid
->MovePositionBy(nsPoint(0, aDY
));
2592 // Make sure the frame's view and any child views are updated
2593 nsContainerFrame::PlaceFrameView(kid
);
2594 kid
= kid
->GetNextSibling();
2600 nsBlockFrame::AttributeChanged(int32_t aNameSpaceID
,
2601 nsIAtom
* aAttribute
,
2604 nsresult rv
= nsBlockFrameSuper::AttributeChanged(aNameSpaceID
,
2605 aAttribute
, aModType
);
2607 if (NS_FAILED(rv
)) {
2610 if (nsGkAtoms::start
== aAttribute
||
2611 (nsGkAtoms::reversed
== aAttribute
&& mContent
->IsHTML(nsGkAtoms::ol
))) {
2612 nsPresContext
* presContext
= PresContext();
2614 // XXX Not sure if this is necessary anymore
2615 if (RenumberLists(presContext
)) {
2616 presContext
->PresShell()->
2617 FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
2618 NS_FRAME_HAS_DIRTY_CHILDREN
);
2621 else if (nsGkAtoms::value
== aAttribute
) {
2622 const nsStyleDisplay
* styleDisplay
= StyleDisplay();
2623 if (NS_STYLE_DISPLAY_LIST_ITEM
== styleDisplay
->mDisplay
) {
2624 // Search for the closest ancestor that's a block frame. We
2625 // make the assumption that all related list items share a
2626 // common block parent.
2627 // XXXldb I think that's a bad assumption.
2628 nsBlockFrame
* blockParent
= nsLayoutUtils::FindNearestBlockAncestor(this);
2630 // Tell the enclosing block frame to renumber list items within
2632 if (nullptr != blockParent
) {
2633 nsPresContext
* presContext
= PresContext();
2634 // XXX Not sure if this is necessary anymore
2635 if (blockParent
->RenumberLists(presContext
)) {
2636 presContext
->PresShell()->
2637 FrameNeedsReflow(blockParent
, nsIPresShell::eStyleChange
,
2638 NS_FRAME_HAS_DIRTY_CHILDREN
);
2648 IsNonAutoNonZeroHeight(const nsStyleCoord
& aCoord
)
2650 if (aCoord
.GetUnit() == eStyleUnit_Auto
)
2652 if (aCoord
.IsCoordPercentCalcUnit()) {
2653 // If we evaluate the length/percent/calc at a percentage basis of
2654 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
2655 // length, percent, or combination thereof. Test > 0 so we clamp
2656 // negative calc() results to 0.
2657 return nsRuleNode::ComputeCoordPercentCalc(aCoord
, nscoord_MAX
) > 0 ||
2658 nsRuleNode::ComputeCoordPercentCalc(aCoord
, 0) > 0;
2660 NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height");
2665 nsBlockFrame::IsSelfEmpty()
2667 // Blocks which are margin-roots (including inline-blocks) cannot be treated
2668 // as empty for margin-collapsing and other purposes. They're more like
2669 // replaced elements.
2670 if (GetStateBits() & NS_BLOCK_MARGIN_ROOT
)
2673 const nsStylePosition
* position
= StylePosition();
2675 if (IsNonAutoNonZeroHeight(position
->mMinHeight
) ||
2676 IsNonAutoNonZeroHeight(position
->mHeight
))
2679 const nsStyleBorder
* border
= StyleBorder();
2680 const nsStylePadding
* padding
= StylePadding();
2681 if (border
->GetComputedBorderWidth(NS_SIDE_TOP
) != 0 ||
2682 border
->GetComputedBorderWidth(NS_SIDE_BOTTOM
) != 0 ||
2683 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetTop()) ||
2684 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBottom())) {
2688 if (HasOutsideBullet() && !BulletIsEmpty()) {
2696 nsBlockFrame::CachedIsEmpty()
2698 if (!IsSelfEmpty()) {
2702 for (line_iterator line
= begin_lines(), line_end
= end_lines();
2706 if (!line
->CachedIsEmpty())
2714 nsBlockFrame::IsEmpty()
2716 if (!IsSelfEmpty()) {
2720 for (line_iterator line
= begin_lines(), line_end
= end_lines();
2724 if (!line
->IsEmpty())
2732 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState
& aState
,
2735 if (aState
.GetFlag(BRS_APPLYTOPMARGIN
)) {
2736 // Apply short-circuit check to avoid searching the line list
2740 if (!aState
.IsAdjacentWithTop()) {
2741 // If we aren't at the top Y coordinate then something of non-zero
2742 // height must have been placed. Therefore the childs top-margin
2744 aState
.SetFlag(BRS_APPLYTOPMARGIN
, true);
2748 // Determine if this line is "essentially" the first line
2749 line_iterator line
= begin_lines();
2750 if (aState
.GetFlag(BRS_HAVELINEADJACENTTOTOP
)) {
2751 line
= aState
.mLineAdjacentToTop
;
2753 while (line
!= aLine
) {
2754 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
2755 // A line which precedes aLine is non-empty, or has clearance,
2756 // so therefore the top margin applies.
2757 aState
.SetFlag(BRS_APPLYTOPMARGIN
, true);
2760 // No need to apply the top margin if the line has floats. We
2761 // should collapse anyway (bug 44419)
2763 aState
.SetFlag(BRS_HAVELINEADJACENTTOTOP
, true);
2764 aState
.mLineAdjacentToTop
= line
;
2767 // The line being reflowed is "essentially" the first line in the
2768 // block. Therefore its top-margin will be collapsed by the
2769 // generational collapsing logic with its parent (us).
2774 nsBlockFrame::ReflowBlockFrame(nsBlockReflowState
& aState
,
2775 line_iterator aLine
,
2776 bool* aKeepReflowGoing
)
2778 NS_PRECONDITION(*aKeepReflowGoing
, "bad caller");
2780 nsresult rv
= NS_OK
;
2782 nsIFrame
* frame
= aLine
->mFirstChild
;
2784 NS_ASSERTION(false, "program error - unexpected empty line");
2785 return NS_ERROR_NULL_POINTER
;
2788 // Prepare the block reflow engine
2789 const nsStyleDisplay
* display
= frame
->StyleDisplay();
2790 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowState
);
2792 uint8_t breakType
= display
->mBreakType
;
2793 if (NS_STYLE_CLEAR_NONE
!= aState
.mFloatBreakType
) {
2794 breakType
= nsLayoutUtils::CombineBreakType(breakType
,
2795 aState
.mFloatBreakType
);
2796 aState
.mFloatBreakType
= NS_STYLE_CLEAR_NONE
;
2799 // Clear past floats before the block if the clear style is not none
2800 aLine
->SetBreakTypeBefore(breakType
);
2802 // See if we should apply the top margin. If the block frame being
2803 // reflowed is a continuation (non-null prev-in-flow) then we don't
2804 // apply its top margin because it's not significant. Otherwise, dig
2806 bool applyTopMargin
=
2807 !frame
->GetPrevInFlow() && ShouldApplyTopMargin(aState
, aLine
);
2809 if (applyTopMargin
) {
2810 // The HasClearance setting is only valid if ShouldApplyTopMargin
2811 // returned false (in which case the top-margin-root set our
2812 // clearance flag). Otherwise clear it now. We'll set it later on
2813 // ourselves if necessary.
2814 aLine
->ClearHasClearance();
2816 bool treatWithClearance
= aLine
->HasClearance();
2818 bool mightClearFloats
= breakType
!= NS_STYLE_CLEAR_NONE
;
2819 nsIFrame
*replacedBlock
= nullptr;
2820 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
2821 mightClearFloats
= true;
2822 replacedBlock
= frame
;
2825 // If our top margin was counted as part of some parents top-margin
2826 // collapse and we are being speculatively reflowed assuming this
2827 // frame DID NOT need clearance, then we need to check that
2829 if (!treatWithClearance
&& !applyTopMargin
&& mightClearFloats
&&
2830 aState
.mReflowState
.mDiscoveredClearance
) {
2831 nscoord curY
= aState
.mY
+ aState
.mPrevBottomMargin
.get();
2832 nscoord clearY
= aState
.ClearFloats(curY
, breakType
, replacedBlock
);
2833 if (clearY
!= curY
) {
2834 // Looks like that assumption was invalid, we do need
2835 // clearance. Tell our ancestor so it can reflow again. It is
2836 // responsible for actually setting our clearance flag before
2838 treatWithClearance
= true;
2839 // Only record the first frame that requires clearance
2840 if (!*aState
.mReflowState
.mDiscoveredClearance
) {
2841 *aState
.mReflowState
.mDiscoveredClearance
= frame
;
2843 aState
.mPrevChild
= frame
;
2844 // Exactly what we do now is flexible since we'll definitely be
2849 if (treatWithClearance
) {
2850 applyTopMargin
= true;
2853 nsIFrame
* clearanceFrame
= nullptr;
2854 nscoord startingY
= aState
.mY
;
2855 nsCollapsingMargin incomingMargin
= aState
.mPrevBottomMargin
;
2857 // Save the original position of the frame so that we can reposition
2858 // its view as needed.
2859 nsPoint originalPosition
= frame
->GetPosition();
2862 nscoord topMargin
= 0;
2863 bool mayNeedRetry
= false;
2864 bool clearedFloats
= false;
2865 if (applyTopMargin
) {
2866 // Precompute the blocks top margin value so that we can get the
2867 // correct available space (there might be a float that's
2868 // already been placed below the aState.mPrevBottomMargin
2870 // Setup a reflowState to get the style computed margin-top
2871 // value. We'll use a reason of `resize' so that we don't fudge
2872 // any incremental reflow state.
2874 // The availSpace here is irrelevant to our needs - all we want
2875 // out if this setup is the margin-top value which doesn't depend
2876 // on the childs available space.
2877 // XXX building a complete nsHTMLReflowState just to get the margin-top
2878 // seems like a waste. And we do this for almost every block!
2879 nsSize
availSpace(aState
.mContentArea
.width
, NS_UNCONSTRAINEDSIZE
);
2880 nsHTMLReflowState
reflowState(aState
.mPresContext
, aState
.mReflowState
,
2883 if (treatWithClearance
) {
2884 aState
.mY
+= aState
.mPrevBottomMargin
.get();
2885 aState
.mPrevBottomMargin
.Zero();
2888 // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
2889 // that all child margins collapse down to clearanceFrame.
2890 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState
,
2891 &aState
.mPrevBottomMargin
, clearanceFrame
, &mayNeedRetry
);
2893 // XXX optimization; we could check the collapsing children to see if they are sure
2894 // to require clearance, and so avoid retrying them
2896 if (clearanceFrame
) {
2897 // Don't allow retries on the second pass. The clearance decisions for the
2898 // blocks whose top-margins collapse with ours are now fixed.
2899 mayNeedRetry
= false;
2902 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
2903 // We don't know if we need clearance and this is the first,
2904 // optimistic pass. So determine whether *this block* needs
2905 // clearance. Note that we do not allow the decision for whether
2906 // this block has clearance to change on the second pass; that
2907 // decision is only allowed to be made under the optimistic
2909 nscoord curY
= aState
.mY
+ aState
.mPrevBottomMargin
.get();
2910 nscoord clearY
= aState
.ClearFloats(curY
, breakType
, replacedBlock
);
2911 if (clearY
!= curY
) {
2912 // Looks like we need clearance and we didn't know about it already. So
2913 // recompute collapsed margin
2914 treatWithClearance
= true;
2915 // Remember this decision, needed for incremental reflow
2916 aLine
->SetHasClearance();
2918 // Apply incoming margins
2919 aState
.mY
+= aState
.mPrevBottomMargin
.get();
2920 aState
.mPrevBottomMargin
.Zero();
2922 // Compute the collapsed margin again, ignoring the incoming margin this time
2923 mayNeedRetry
= false;
2924 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState
,
2925 &aState
.mPrevBottomMargin
, clearanceFrame
, &mayNeedRetry
);
2929 // Temporarily advance the running Y value so that the
2930 // GetAvailableSpace method will return the right available
2931 // space. This undone as soon as the horizontal margins are
2933 topMargin
= aState
.mPrevBottomMargin
.get();
2935 if (treatWithClearance
) {
2936 nscoord currentY
= aState
.mY
;
2937 // advance mY to the clear position.
2938 aState
.mY
= aState
.ClearFloats(aState
.mY
, breakType
, replacedBlock
);
2940 clearedFloats
= aState
.mY
!= currentY
;
2942 // Compute clearance. It's the amount we need to add to the top
2943 // border-edge of the frame, after applying collapsed margins
2944 // from the frame and its children, to get it to line up with
2945 // the bottom of the floats. The former is currentY + topMargin,
2946 // the latter is the current aState.mY.
2947 // Note that negative clearance is possible
2948 clearance
= aState
.mY
- (currentY
+ topMargin
);
2950 // Add clearance to our top margin while we compute available
2951 // space for the frame
2952 topMargin
+= clearance
;
2954 // Note that aState.mY should stay where it is: at the top
2955 // border-edge of the frame
2957 // Advance aState.mY to the top border-edge of the frame.
2958 aState
.mY
+= topMargin
;
2962 // Here aState.mY is the top border-edge of the block.
2963 // Compute the available space for the block
2964 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
2965 #ifdef REALLY_NOISY_REFLOW
2966 printf("setting line %p isImpacted to %s\n",
2967 aLine
.get(), floatAvailableSpace
.mHasFloats
?"true":"false");
2969 aLine
->SetLineIsImpactedByFloat(floatAvailableSpace
.mHasFloats
);
2971 aState
.ComputeBlockAvailSpace(frame
, display
, floatAvailableSpace
,
2972 replacedBlock
!= nullptr, availSpace
);
2975 // (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats)
2976 // is to some degree out of paranoia: if we reliably eat up top
2977 // margins at the top of the page as we ought to, it wouldn't be
2979 if ((!aState
.mReflowState
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
2980 availSpace
.height
< 0) {
2981 // We know already that this child block won't fit on this
2982 // page/column due to the top margin or the clearance. So we need
2983 // to get out of here now. (If we don't, most blocks will handle
2984 // things fine, and report break-before, but zero-height blocks
2985 // won't, and will thus make their parent overly-large and force
2986 // *it* to be pushed in its entirety.)
2987 // Doing this means that we also don't need to worry about the
2988 // |availSpace.height += topMargin| below interacting with pushed
2989 // floats (which force nscoord_MAX clearance) to cause a
2990 // constrained height to turn into an unconstrained one.
2991 aState
.mY
= startingY
;
2992 aState
.mPrevBottomMargin
= incomingMargin
;
2993 *aKeepReflowGoing
= false;
2994 if (ShouldAvoidBreakInside(aState
.mReflowState
)) {
2995 aState
.mReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
2997 PushLines(aState
, aLine
.prev());
2998 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
3003 // Now put the Y coordinate back to the top of the top-margin +
3004 // clearance, and flow the block.
3005 aState
.mY
-= topMargin
;
3006 availSpace
.y
-= topMargin
;
3007 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.height
) {
3008 availSpace
.height
+= topMargin
;
3011 // Reflow the block into the available space
3012 // construct the html reflow state for the block. ReflowBlock
3013 // will initialize it
3014 nsHTMLReflowState
blockHtmlRS(aState
.mPresContext
, aState
.mReflowState
, frame
,
3016 blockHtmlRS
.mFlags
.mHasClearance
= aLine
->HasClearance();
3018 nsFloatManager::SavedState floatManagerState
;
3020 blockHtmlRS
.mDiscoveredClearance
= &clearanceFrame
;
3021 aState
.mFloatManager
->PushState(&floatManagerState
);
3022 } else if (!applyTopMargin
) {
3023 blockHtmlRS
.mDiscoveredClearance
= aState
.mReflowState
.mDiscoveredClearance
;
3026 nsReflowStatus frameReflowStatus
= NS_FRAME_COMPLETE
;
3027 rv
= brc
.ReflowBlock(availSpace
, applyTopMargin
, aState
.mPrevBottomMargin
,
3028 clearance
, aState
.IsAdjacentWithTop(),
3029 aLine
.get(), blockHtmlRS
, frameReflowStatus
, aState
);
3031 NS_ENSURE_SUCCESS(rv
, rv
);
3033 if (mayNeedRetry
&& clearanceFrame
) {
3034 aState
.mFloatManager
->PopState(&floatManagerState
);
3035 aState
.mY
= startingY
;
3036 aState
.mPrevBottomMargin
= incomingMargin
;
3040 aState
.mPrevChild
= frame
;
3042 if (blockHtmlRS
.WillReflowAgainForClearance()) {
3043 // If an ancestor of ours is going to reflow for clearance, we
3044 // need to avoid calling PlaceBlock, because it unsets dirty bits
3045 // on the child block (both itself, and through its call to
3046 // nsFrame::DidReflow), and those dirty bits imply dirtiness for
3047 // all of the child block, including the lines it didn't reflow.
3048 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
3049 "we need to call PositionChildViews");
3053 #if defined(REFLOW_STATUS_COVERAGE)
3054 RecordReflowStatus(true, frameReflowStatus
);
3057 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus
)) {
3058 // None of the child block fits.
3059 *aKeepReflowGoing
= false;
3060 if (ShouldAvoidBreakInside(aState
.mReflowState
)) {
3061 aState
.mReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
3063 PushLines(aState
, aLine
.prev());
3064 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
3068 // Note: line-break-after a block is a nop
3070 // Try to place the child block.
3071 // Don't force the block to fit if we have positive clearance, because
3072 // pushing it to the next page would give it more room.
3073 // Don't force the block to fit if it's impacted by a float. If it is,
3074 // then pushing it to the next page would give it more room. Note that
3075 // isImpacted doesn't include impact from the block's own floats.
3076 bool forceFit
= aState
.IsAdjacentWithTop() && clearance
<= 0 &&
3077 !floatAvailableSpace
.mHasFloats
;
3078 nsCollapsingMargin collapsedBottomMargin
;
3079 nsOverflowAreas overflowAreas
;
3080 *aKeepReflowGoing
= brc
.PlaceBlock(blockHtmlRS
, forceFit
, aLine
.get(),
3081 collapsedBottomMargin
,
3082 aLine
->mBounds
, overflowAreas
,
3084 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus
) &&
3085 ShouldAvoidBreakInside(aState
.mReflowState
)) {
3086 *aKeepReflowGoing
= false;
3089 if (aLine
->SetCarriedOutBottomMargin(collapsedBottomMargin
)) {
3090 line_iterator nextLine
= aLine
;
3092 if (nextLine
!= end_lines()) {
3093 nextLine
->MarkPreviousMarginDirty();
3097 aLine
->SetOverflowAreas(overflowAreas
);
3098 if (*aKeepReflowGoing
) {
3099 // Some of the child block fit
3101 // Advance to new Y position
3102 nscoord newY
= aLine
->mBounds
.YMost();
3105 // Continue the block frame now if it didn't completely fit in
3106 // the available space.
3107 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus
)) {
3108 bool madeContinuation
=
3109 CreateContinuationFor(aState
, nullptr, frame
);
3111 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
3112 NS_ASSERTION(nextFrame
, "We're supposed to have a next-in-flow by now");
3114 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus
)) {
3115 // If nextFrame used to be an overflow container, make it a normal block
3116 if (!madeContinuation
&&
3117 (NS_FRAME_IS_OVERFLOW_CONTAINER
& nextFrame
->GetStateBits())) {
3118 nsOverflowContinuationTracker::AutoFinish
fini(aState
.mOverflowTracker
, frame
);
3119 nsContainerFrame
* parent
=
3120 static_cast<nsContainerFrame
*>(nextFrame
->GetParent());
3121 rv
= parent
->StealFrame(aState
.mPresContext
, nextFrame
);
3122 NS_ENSURE_SUCCESS(rv
, rv
);
3124 ReparentFrame(nextFrame
, parent
, this);
3125 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
3126 madeContinuation
= true; // needs to be added to mLines
3127 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
3128 frameReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
3131 // Push continuation to a new line, but only if we actually made one.
3132 if (madeContinuation
) {
3133 nsLineBox
* line
= NewLineBox(nextFrame
, true);
3134 mLines
.after_insert(aLine
, line
);
3137 PushLines(aState
, aLine
);
3138 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
3140 // If we need to reflow the continuation of the block child,
3141 // then we'd better reflow our continuation
3142 if (frameReflowStatus
& NS_FRAME_REFLOW_NEXTINFLOW
) {
3143 aState
.mReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
3144 // We also need to make that continuation's line dirty so
3145 // it gets reflowed when we reflow our next in flow. The
3146 // nif's line must always be either a line of the nif's
3147 // parent block (only if we didn't make a continuation) or
3148 // else one of our own overflow lines. In the latter case
3149 // the line is already marked dirty, so just handle the
3151 if (!madeContinuation
) {
3152 nsBlockFrame
* nifBlock
=
3153 nsLayoutUtils::GetAsBlock(nextFrame
->GetParent());
3154 NS_ASSERTION(nifBlock
,
3155 "A block's child's next in flow's parent must be a block!");
3156 for (line_iterator line
= nifBlock
->begin_lines(),
3157 line_end
= nifBlock
->end_lines(); line
!= line_end
; ++line
) {
3158 if (line
->Contains(nextFrame
)) {
3165 *aKeepReflowGoing
= false;
3167 // The bottom margin for a block is only applied on the last
3168 // flow block. Since we just continued the child block frame,
3169 // we know that line->mFirstChild is not the last flow block
3170 // therefore zero out the running margin value.
3171 #ifdef NOISY_VERTICAL_MARGINS
3173 printf(": reflow incomplete, frame=");
3174 nsFrame::ListTag(stdout
, frame
);
3175 printf(" prevBottomMargin=%d, setting to zero\n",
3176 aState
.mPrevBottomMargin
);
3178 aState
.mPrevBottomMargin
.Zero();
3180 else { // frame is complete but its overflow is not complete
3181 // Disconnect the next-in-flow and put it in our overflow tracker
3182 if (!madeContinuation
&&
3183 !(NS_FRAME_IS_OVERFLOW_CONTAINER
& nextFrame
->GetStateBits())) {
3184 // It already exists, but as a normal next-in-flow, so we need
3185 // to dig it out of the child lists.
3186 nsContainerFrame
* parent
= static_cast<nsContainerFrame
*>
3187 (nextFrame
->GetParent());
3188 rv
= parent
->StealFrame(aState
.mPresContext
, nextFrame
);
3189 NS_ENSURE_SUCCESS(rv
, rv
);
3191 else if (madeContinuation
) {
3192 mFrames
.RemoveFrame(nextFrame
);
3195 // Put it in our overflow list
3196 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
3197 NS_MergeReflowStatusInto(&aState
.mReflowStatus
, frameReflowStatus
);
3199 #ifdef NOISY_VERTICAL_MARGINS
3201 printf(": reflow complete but overflow incomplete for ");
3202 nsFrame::ListTag(stdout
, frame
);
3203 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3204 aState
.mPrevBottomMargin
, collapsedBottomMargin
.get());
3206 aState
.mPrevBottomMargin
= collapsedBottomMargin
;
3209 else { // frame is fully complete
3210 #ifdef NOISY_VERTICAL_MARGINS
3212 printf(": reflow complete for ");
3213 nsFrame::ListTag(stdout
, frame
);
3214 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3215 aState
.mPrevBottomMargin
, collapsedBottomMargin
.get());
3217 aState
.mPrevBottomMargin
= collapsedBottomMargin
;
3219 #ifdef NOISY_VERTICAL_MARGINS
3222 nsFrame::ListTag(stdout
, frame
);
3223 printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3224 brc
.GetCarriedOutBottomMargin(), collapsedBottomMargin
.get(),
3225 aState
.mPrevBottomMargin
);
3228 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
3229 ShouldAvoidBreakInside(aState
.mReflowState
)) {
3230 // If it's our very first line *or* we're not at the top of the page
3231 // and we have page-break-inside:avoid, then we need to be pushed to
3232 // our parent's next-in-flow.
3233 aState
.mReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
3235 // Push the line that didn't fit and any lines that follow it
3236 // to our next-in-flow.
3237 PushLines(aState
, aLine
.prev());
3238 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
3242 break; // out of the reflow retry loop
3245 // Now that we've got its final position all figured out, position any child
3246 // views it may have. Note that the case when frame has a view got handled
3247 // by FinishReflowChild, but that function didn't have the coordinates needed
3248 // to correctly decide whether to reposition child views.
3249 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
3250 nsContainerFrame::PositionChildViews(frame
);
3260 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState
& aState
,
3261 line_iterator aLine
,
3262 bool* aKeepReflowGoing
)
3264 nsresult rv
= NS_OK
;
3265 *aKeepReflowGoing
= true;
3267 aLine
->SetLineIsImpactedByFloat(false);
3269 // Setup initial coordinate system for reflowing the inline frames
3270 // into. Apply a previous block frame's bottom margin first.
3271 if (ShouldApplyTopMargin(aState
, aLine
)) {
3272 aState
.mY
+= aState
.mPrevBottomMargin
.get();
3274 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
3276 LineReflowStatus lineReflowStatus
;
3278 nscoord availableSpaceHeight
= 0;
3280 bool allowPullUp
= true;
3281 nsIContent
* forceBreakInContent
= nullptr;
3282 int32_t forceBreakOffset
= -1;
3283 gfxBreakPriority forceBreakPriority
= eNoBreak
;
3285 nsFloatManager::SavedState floatManagerState
;
3286 aState
.mReflowState
.mFloatManager
->PushState(&floatManagerState
);
3288 // Once upon a time we allocated the first 30 nsLineLayout objects
3289 // on the stack, and then we switched to the heap. At that time
3290 // these objects were large (1100 bytes on a 32 bit system).
3291 // Then the nsLineLayout object was shrunk to 156 bytes by
3292 // removing some internal buffers. Given that it is so much
3293 // smaller, the complexity of 2 different ways of allocating
3294 // no longer makes sense. Now we always allocate on the stack.
3295 nsLineLayout
lineLayout(aState
.mPresContext
,
3296 aState
.mReflowState
.mFloatManager
,
3297 &aState
.mReflowState
, &aLine
);
3298 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
3299 if (forceBreakInContent
) {
3300 lineLayout
.ForceBreakAtPosition(forceBreakInContent
, forceBreakOffset
);
3302 rv
= DoReflowInlineFrames(aState
, lineLayout
, aLine
,
3303 floatAvailableSpace
, availableSpaceHeight
,
3304 &floatManagerState
, aKeepReflowGoing
,
3305 &lineReflowStatus
, allowPullUp
);
3306 lineLayout
.EndLineReflow();
3308 if (NS_FAILED(rv
)) {
3312 if (LINE_REFLOW_REDO_NO_PULL
== lineReflowStatus
||
3313 LINE_REFLOW_REDO_MORE_FLOATS
== lineReflowStatus
||
3314 LINE_REFLOW_REDO_NEXT_BAND
== lineReflowStatus
) {
3315 if (lineLayout
.NeedsBackup()) {
3316 NS_ASSERTION(!forceBreakInContent
, "Backing up twice; this should never be necessary");
3317 // If there is no saved break position, then this will set
3318 // set forceBreakInContent to null and we won't back up, which is
3320 forceBreakInContent
= lineLayout
.GetLastOptionalBreakPosition(&forceBreakOffset
, &forceBreakPriority
);
3322 forceBreakInContent
= nullptr;
3324 // restore the float manager state
3325 aState
.mReflowState
.mFloatManager
->PopState(&floatManagerState
);
3326 // Clear out float lists
3327 aState
.mCurrentLineFloats
.DeleteAll();
3328 aState
.mBelowCurrentLineFloats
.DeleteAll();
3331 // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
3332 allowPullUp
= false;
3333 } while (LINE_REFLOW_REDO_NO_PULL
== lineReflowStatus
);
3334 } while (LINE_REFLOW_REDO_MORE_FLOATS
== lineReflowStatus
);
3335 } while (LINE_REFLOW_REDO_NEXT_BAND
== lineReflowStatus
);
3341 nsBlockFrame::PushTruncatedLine(nsBlockReflowState
& aState
,
3342 line_iterator aLine
,
3343 bool* aKeepReflowGoing
)
3345 PushLines(aState
, aLine
.prev());
3346 *aKeepReflowGoing
= false;
3347 NS_FRAME_SET_INCOMPLETE(aState
.mReflowStatus
);
3351 static const char* LineReflowStatusNames
[] = {
3352 "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
3353 "LINE_REFLOW_REDO_MORE_FLOATS",
3354 "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
3359 nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState
& aState
,
3360 nsLineLayout
& aLineLayout
,
3361 line_iterator aLine
,
3362 nsFlowAreaRect
& aFloatAvailableSpace
,
3363 nscoord
& aAvailableSpaceHeight
,
3364 nsFloatManager::SavedState
*
3365 aFloatStateBeforeLine
,
3366 bool* aKeepReflowGoing
,
3367 LineReflowStatus
* aLineReflowStatus
,
3370 // Forget all of the floats on the line
3371 aLine
->FreeFloats(aState
.mFloatCacheFreeList
);
3372 aState
.mFloatOverflowAreas
.Clear();
3374 // We need to set this flag on the line if any of our reflow passes
3375 // are impacted by floats.
3376 if (aFloatAvailableSpace
.mHasFloats
)
3377 aLine
->SetLineIsImpactedByFloat(true);
3378 #ifdef REALLY_NOISY_REFLOW
3379 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
3380 this, aFloatAvailableSpace
.mHasFloats
);
3383 nscoord x
= aFloatAvailableSpace
.mRect
.x
;
3384 nscoord availWidth
= aFloatAvailableSpace
.mRect
.width
;
3385 nscoord availHeight
;
3386 if (aState
.GetFlag(BRS_UNCONSTRAINEDHEIGHT
)) {
3387 availHeight
= NS_UNCONSTRAINEDSIZE
;
3390 /* XXX get the height right! */
3391 availHeight
= aFloatAvailableSpace
.mRect
.height
;
3394 // Make sure to enable resize optimization before we call BeginLineReflow
3395 // because it might get disabled there
3396 aLine
->EnableResizeReflowOptimization();
3398 // For unicode-bidi: plaintext, we need to get the direction of the line from
3399 // the resolved paragraph level of the first frame on the line, not the block
3400 // frame, because the block frame could be split by hard line breaks into
3401 // multiple paragraphs with different base direction
3403 (StyleTextReset()->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_PLAINTEXT
) ?
3404 nsBidiPresUtils::GetFrameBaseLevel(aLine
->mFirstChild
) & 1 :
3405 StyleVisibility()->mDirection
;
3407 aLineLayout
.BeginLineReflow(x
, aState
.mY
,
3408 availWidth
, availHeight
,
3409 aFloatAvailableSpace
.mHasFloats
,
3410 false, /*XXX isTopOfPage*/
3413 aState
.SetFlag(BRS_LINE_LAYOUT_EMPTY
, false);
3415 // XXX Unfortunately we need to know this before reflowing the first
3416 // inline frame in the line. FIX ME.
3417 if ((0 == aLineLayout
.GetLineNumber()) &&
3418 (NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) &&
3419 (NS_BLOCK_HAS_FIRST_LETTER_STYLE
& mState
)) {
3420 aLineLayout
.SetFirstLetterStyleOK(true);
3422 NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) &&
3423 GetPrevContinuation()),
3424 "first letter child bit should only be on first continuation");
3426 // Reflow the frames that are already on the line first
3427 nsresult rv
= NS_OK
;
3428 LineReflowStatus lineReflowStatus
= LINE_REFLOW_OK
;
3430 nsIFrame
* frame
= aLine
->mFirstChild
;
3432 if (aFloatAvailableSpace
.mHasFloats
) {
3433 // There is a soft break opportunity at the start of the line, because
3434 // we can always move this line down below float(s).
3435 if (aLineLayout
.NotifyOptionalBreakPosition(frame
->GetContent(), 0, true, eNormalBreak
)) {
3436 lineReflowStatus
= LINE_REFLOW_REDO_NEXT_BAND
;
3440 // need to repeatedly call GetChildCount here, because the child
3441 // count can change during the loop!
3442 for (i
= 0; LINE_REFLOW_OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
3443 i
++, frame
= frame
->GetNextSibling()) {
3444 rv
= ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
,
3446 NS_ENSURE_SUCCESS(rv
, rv
);
3447 if (LINE_REFLOW_OK
!= lineReflowStatus
) {
3448 // It is possible that one or more of next lines are empty
3449 // (because of DeleteNextInFlowChild). If so, delete them now
3450 // in case we are finished.
3452 while ((aLine
!= end_lines()) && (0 == aLine
->GetChildCount())) {
3453 // XXX Is this still necessary now that DeleteNextInFlowChild
3454 // uses DoRemoveFrame?
3455 nsLineBox
*toremove
= aLine
;
3456 aLine
= mLines
.erase(aLine
);
3457 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
3458 FreeLineBox(toremove
);
3462 NS_ASSERTION(lineReflowStatus
!= LINE_REFLOW_TRUNCATED
,
3463 "ReflowInlineFrame should never determine that a line "
3464 "needs to go to the next page/column");
3468 // Don't pull up new frames into lines with continuation placeholders
3470 // Pull frames and reflow them until we can't
3471 while (LINE_REFLOW_OK
== lineReflowStatus
) {
3472 frame
= PullFrame(aState
, aLine
);
3477 while (LINE_REFLOW_OK
== lineReflowStatus
) {
3478 int32_t oldCount
= aLine
->GetChildCount();
3479 rv
= ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
,
3481 NS_ENSURE_SUCCESS(rv
, rv
);
3482 if (aLine
->GetChildCount() != oldCount
) {
3483 // We just created a continuation for aFrame AND its going
3484 // to end up on this line (e.g. :first-letter
3485 // situation). Therefore we have to loop here before trying
3486 // to pull another frame.
3487 frame
= frame
->GetNextSibling();
3496 aState
.SetFlag(BRS_LINE_LAYOUT_EMPTY
, aLineLayout
.LineIsEmpty());
3498 // We only need to backup if the line isn't going to be reflowed again anyway
3499 bool needsBackup
= aLineLayout
.NeedsBackup() &&
3500 (lineReflowStatus
== LINE_REFLOW_STOP
|| lineReflowStatus
== LINE_REFLOW_OK
);
3501 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
3502 NS_WARNING("We shouldn't be backing up more than once! "
3503 "Someone must have set a break opportunity beyond the available width, "
3504 "even though there were better break opportunities before it");
3505 needsBackup
= false;
3508 // We need to try backing up to before a text run
3510 gfxBreakPriority breakPriority
;
3511 nsIContent
* breakContent
= aLineLayout
.GetLastOptionalBreakPosition(&offset
, &breakPriority
);
3512 // XXX It's possible, in fact not unusual, for the break opportunity to already
3513 // be the end of the line. We should detect that and optimize to not
3517 lineReflowStatus
= LINE_REFLOW_REDO_NO_PULL
;
3520 // In case we reflow this line again, remember that we don't
3521 // need to force any breaking
3522 aLineLayout
.ClearOptionalBreakPosition();
3525 if (LINE_REFLOW_REDO_NEXT_BAND
== lineReflowStatus
) {
3526 // This happens only when we have a line that is impacted by
3527 // floats and the first element in the line doesn't fit with
3530 // What we do is to advance past the first float we find and
3531 // then reflow the line all over again.
3532 NS_ASSERTION(NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.height
,
3533 "unconstrained height on totally empty line");
3535 // See the analogous code for blocks in nsBlockReflowState::ClearFloats.
3536 if (aFloatAvailableSpace
.mRect
.height
> 0) {
3537 NS_ASSERTION(aFloatAvailableSpace
.mHasFloats
,
3538 "redo line on totally empty line with non-empty band...");
3539 // We should never hit this case if we've placed floats on the
3540 // line; if we have, then the GetFloatAvailableSpace call is wrong
3541 // and needs to happen after the caller pops the space manager
3543 aState
.mFloatManager
->AssertStateMatches(aFloatStateBeforeLine
);
3544 aState
.mY
+= aFloatAvailableSpace
.mRect
.height
;
3545 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
3547 NS_ASSERTION(NS_UNCONSTRAINEDSIZE
!= aState
.mReflowState
.availableHeight
,
3548 "We shouldn't be running out of height here");
3549 if (NS_UNCONSTRAINEDSIZE
== aState
.mReflowState
.availableHeight
) {
3550 // just move it down a bit to try to get out of this mess
3552 // We should never hit this case if we've placed floats on the
3553 // line; if we have, then the GetFloatAvailableSpace call is wrong
3554 // and needs to happen after the caller pops the space manager
3556 aState
.mFloatManager
->AssertStateMatches(aFloatStateBeforeLine
);
3557 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
3559 // There's nowhere to retry placing the line, so we want to push
3560 // it to the next page/column where its contents can fit not
3562 lineReflowStatus
= LINE_REFLOW_TRUNCATED
;
3563 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
3567 // XXX: a small optimization can be done here when paginating:
3568 // if the new Y coordinate is past the end of the block then
3569 // push the line and return now instead of later on after we are
3572 else if (LINE_REFLOW_TRUNCATED
!= lineReflowStatus
&&
3573 LINE_REFLOW_REDO_NO_PULL
!= lineReflowStatus
) {
3574 // If we are propagating out a break-before status then there is
3575 // no point in placing the line.
3576 if (!NS_INLINE_IS_BREAK_BEFORE(aState
.mReflowStatus
)) {
3577 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
3578 aFloatAvailableSpace
.mRect
, aAvailableSpaceHeight
,
3579 aKeepReflowGoing
)) {
3580 lineReflowStatus
= LINE_REFLOW_REDO_MORE_FLOATS
;
3581 // PlaceLine already called GetAvailableSpaceForHeight for us.
3587 printf("Line reflow status = %s\n", LineReflowStatusNames
[lineReflowStatus
]);
3591 if (aLineLayout
.GetDirtyNextLine()) {
3592 // aLine may have been pushed to the overflow lines.
3593 FrameLines
* overflowLines
= GetOverflowLines();
3594 // We can't just compare iterators front() to aLine here, since they may be in
3596 bool pushedToOverflowLines
= overflowLines
&&
3597 overflowLines
->mLines
.front() == aLine
.get();
3598 if (pushedToOverflowLines
) {
3599 // aLine is stale, it's associated with the main line list but it should
3600 // be associated with the overflow line list now
3601 aLine
= overflowLines
->mLines
.begin();
3603 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
3604 if (iter
.Next() && iter
.GetLine()->IsInline()) {
3605 iter
.GetLine()->MarkDirty();
3606 if (iter
.GetContainer() != this) {
3607 aState
.mReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
3612 *aLineReflowStatus
= lineReflowStatus
;
3618 * Reflow an inline frame. The reflow status is mapped from the frames
3619 * reflow status to the lines reflow status (not to our reflow status).
3620 * The line reflow status is simple: true means keep placing frames
3621 * on the line; false means don't (the line is done). If the line
3622 * has some sort of breaking affect then aLine's break-type will be set
3623 * to something other than NS_STYLE_CLEAR_NONE.
3626 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState
& aState
,
3627 nsLineLayout
& aLineLayout
,
3628 line_iterator aLine
,
3630 LineReflowStatus
* aLineReflowStatus
)
3632 NS_ENSURE_ARG_POINTER(aFrame
);
3634 *aLineReflowStatus
= LINE_REFLOW_OK
;
3636 #ifdef NOISY_FIRST_LETTER
3638 printf(": reflowing ");
3639 nsFrame::ListTag(stdout
, aFrame
);
3640 printf(" reflowingFirstLetter=%s\n",
3641 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
3644 // Reflow the inline frame
3645 nsReflowStatus frameReflowStatus
;
3647 nsresult rv
= aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
,
3648 nullptr, pushedFrame
);
3649 NS_ENSURE_SUCCESS(rv
, rv
);
3651 if (frameReflowStatus
& NS_FRAME_REFLOW_NEXTINFLOW
) {
3652 aLineLayout
.SetDirtyNextLine();
3655 NS_ENSURE_SUCCESS(rv
, rv
);
3656 #ifdef REALLY_NOISY_REFLOW_CHILD
3657 nsFrame::ListTag(stdout
, aFrame
);
3658 printf(": status=%x\n", frameReflowStatus
);
3661 #if defined(REFLOW_STATUS_COVERAGE)
3662 RecordReflowStatus(false, frameReflowStatus
);
3665 // Send post-reflow notification
3666 aState
.mPrevChild
= aFrame
;
3669 This is where we need to add logic to handle some odd behavior.
3670 For one thing, we should usually place at least one thing next
3671 to a left float, even when that float takes up all the width on a line.
3675 // Process the child frames reflow status. There are 5 cases:
3676 // complete, not-complete, break-before, break-after-complete,
3677 // break-after-not-complete. There are two situations: we are a
3678 // block or we are an inline. This makes a total of 10 cases
3679 // (fortunately, there is some overlap).
3680 aLine
->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE
);
3681 if (NS_INLINE_IS_BREAK(frameReflowStatus
) ||
3682 (NS_STYLE_CLEAR_NONE
!= aState
.mFloatBreakType
)) {
3683 // Always abort the line reflow (because a line break is the
3684 // minimal amount of break we do).
3685 *aLineReflowStatus
= LINE_REFLOW_STOP
;
3687 // XXX what should aLine's break-type be set to in all these cases?
3688 uint8_t breakType
= NS_INLINE_GET_BREAK_TYPE(frameReflowStatus
);
3689 NS_ASSERTION((NS_STYLE_CLEAR_NONE
!= breakType
) ||
3690 (NS_STYLE_CLEAR_NONE
!= aState
.mFloatBreakType
), "bad break type");
3691 NS_ASSERTION(NS_STYLE_CLEAR_LAST_VALUE
>= breakType
, "invalid break type");
3693 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus
)) {
3694 // Break-before cases.
3695 if (aFrame
== aLine
->mFirstChild
) {
3696 // If we break before the first frame on the line then we must
3697 // be trying to place content where there's no room (e.g. on a
3698 // line with wide floats). Inform the caller to reflow the
3699 // line after skipping past a float.
3700 *aLineReflowStatus
= LINE_REFLOW_REDO_NEXT_BAND
;
3703 // It's not the first child on this line so go ahead and split
3704 // the line. We will see the frame again on the next-line.
3705 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
3707 // If we're splitting the line because the frame didn't fit and it
3708 // was pushed, then mark the line as having word wrapped. We need to
3709 // know that if we're shrink wrapping our width
3711 aLine
->SetLineWrapped(true);
3716 // If a float split and its prev-in-flow was followed by a <BR>, then combine
3717 // the <BR>'s break type with the inline's break type (the inline will be the very
3718 // next frame after the split float).
3719 if (NS_STYLE_CLEAR_NONE
!= aState
.mFloatBreakType
) {
3720 breakType
= nsLayoutUtils::CombineBreakType(breakType
,
3721 aState
.mFloatBreakType
);
3722 aState
.mFloatBreakType
= NS_STYLE_CLEAR_NONE
;
3724 // Break-after cases
3725 if (breakType
== NS_STYLE_CLEAR_LINE
) {
3726 if (!aLineLayout
.GetLineEndsInBR()) {
3727 breakType
= NS_STYLE_CLEAR_NONE
;
3730 aLine
->SetBreakTypeAfter(breakType
);
3731 if (NS_FRAME_IS_COMPLETE(frameReflowStatus
)) {
3732 // Split line, but after the frame just reflowed
3733 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(), aLineReflowStatus
);
3735 if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus
) &&
3736 !aLineLayout
.GetLineEndsInBR()) {
3737 aLineLayout
.SetDirtyNextLine();
3743 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus
)) {
3744 // Create a continuation for the incomplete frame. Note that the
3745 // frame may already have a continuation.
3746 CreateContinuationFor(aState
, aLine
, aFrame
);
3748 // Remember that the line has wrapped
3749 if (!aLineLayout
.GetLineEndsInBR()) {
3750 aLine
->SetLineWrapped(true);
3753 // If we just ended a first-letter frame or reflowed a placeholder then
3754 // don't split the line and don't stop the line reflow...
3755 // But if we are going to stop anyways we'd better split the line.
3756 if ((!(frameReflowStatus
& NS_INLINE_BREAK_FIRST_LETTER_COMPLETE
) &&
3757 nsGkAtoms::placeholderFrame
!= aFrame
->GetType()) ||
3758 *aLineReflowStatus
== LINE_REFLOW_STOP
) {
3759 // Split line after the current frame
3760 *aLineReflowStatus
= LINE_REFLOW_STOP
;
3761 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(), aLineReflowStatus
);
3769 nsBlockFrame::CreateContinuationFor(nsBlockReflowState
& aState
,
3773 nsIFrame
* newFrame
= nullptr;
3775 if (!aFrame
->GetNextInFlow()) {
3776 newFrame
= aState
.mPresContext
->PresShell()->FrameConstructor()->
3777 CreateContinuingFrame(aState
.mPresContext
, aFrame
, this);
3779 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
3782 aLine
->NoteFrameAdded(newFrame
);
3792 nsBlockFrame::SplitFloat(nsBlockReflowState
& aState
,
3794 nsReflowStatus aFloatStatus
)
3796 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
3798 nsContainerFrame
*oldParent
=
3799 static_cast<nsContainerFrame
*>(nextInFlow
->GetParent());
3800 DebugOnly
<nsresult
> rv
= oldParent
->StealFrame(aState
.mPresContext
, nextInFlow
);
3801 NS_ASSERTION(NS_SUCCEEDED(rv
), "StealFrame failed");
3802 if (oldParent
!= this) {
3803 ReparentFrame(nextInFlow
, oldParent
, this);
3806 nextInFlow
= aState
.mPresContext
->PresShell()->FrameConstructor()->
3807 CreateContinuingFrame(aState
.mPresContext
, aFloat
, this);
3809 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus
))
3810 aFloat
->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
3812 // The containing block is now overflow-incomplete.
3813 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState
.mReflowStatus
);
3815 if (aFloat
->StyleDisplay()->mFloats
== NS_STYLE_FLOAT_LEFT
) {
3816 aState
.mFloatManager
->SetSplitLeftFloatAcrossBreak();
3818 NS_ABORT_IF_FALSE(aFloat
->StyleDisplay()->mFloats
==
3819 NS_STYLE_FLOAT_RIGHT
, "unexpected float side");
3820 aState
.mFloatManager
->SetSplitRightFloatAcrossBreak();
3823 aState
.AppendPushedFloat(nextInFlow
);
3827 static nsFloatCache
*
3828 GetLastFloat(nsLineBox
* aLine
)
3830 nsFloatCache
* fc
= aLine
->GetFirstFloat();
3831 while (fc
&& fc
->Next()) {
3838 CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
, nsFloatCache
* aFC
)
3842 NS_ASSERTION(!aFC
->mFloat
->GetPrevContinuation(),
3843 "float in a line should never be a continuation");
3844 NS_ASSERTION(!(aFC
->mFloat
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
),
3845 "float in a line should never be a pushed float");
3846 nsIFrame
* ph
= aBlock
->PresContext()->FrameManager()->
3847 GetPlaceholderFrameFor(aFC
->mFloat
->FirstInFlow());
3848 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
3849 if (f
->GetParent() == aBlock
)
3850 return aLine
->Contains(f
);
3852 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
3857 nsBlockFrame::SplitLine(nsBlockReflowState
& aState
,
3858 nsLineLayout
& aLineLayout
,
3859 line_iterator aLine
,
3861 LineReflowStatus
* aLineReflowStatus
)
3863 NS_ABORT_IF_FALSE(aLine
->IsInline(), "illegal SplitLine on block line");
3865 int32_t pushCount
= aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
3866 NS_ABORT_IF_FALSE(pushCount
>= 0, "bad push count");
3870 nsFrame::IndentBy(stdout
, gNoiseIndent
);
3871 printf("split line: from line=%p pushCount=%d aFrame=",
3872 static_cast<void*>(aLine
.get()), pushCount
);
3874 nsFrame::ListTag(stdout
, aFrame
);
3880 if (gReallyNoisyReflow
) {
3881 aLine
->List(stdout
, gNoiseIndent
+1);
3886 if (0 != pushCount
) {
3887 NS_ABORT_IF_FALSE(aLine
->GetChildCount() > pushCount
, "bad push");
3888 NS_ABORT_IF_FALSE(nullptr != aFrame
, "whoops");
3891 nsIFrame
*f
= aFrame
;
3892 int32_t count
= pushCount
;
3893 while (f
&& count
> 0) {
3894 f
= f
->GetNextSibling();
3897 NS_ASSERTION(count
== 0, "Not enough frames to push");
3901 // Put frames being split out into their own line
3902 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
3903 mLines
.after_insert(aLine
, newLine
);
3905 if (gReallyNoisyReflow
) {
3906 newLine
->List(stdout
, gNoiseIndent
+1);
3910 // Let line layout know that some frames are no longer part of its
3912 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
3914 // If floats have been placed whose placeholders have been pushed to the new
3915 // line, we need to reflow the old line again. We don't want to look at the
3916 // frames in the new line, because as a large paragraph is laid out the
3917 // we'd get O(N^2) performance. So instead we just check that the last
3918 // float and the last below-current-line float are still in aLine.
3919 if (!CheckPlaceholderInLine(this, aLine
, GetLastFloat(aLine
)) ||
3920 !CheckPlaceholderInLine(this, aLine
, aState
.mBelowCurrentLineFloats
.Tail())) {
3921 *aLineReflowStatus
= LINE_REFLOW_REDO_NO_PULL
;
3931 nsBlockFrame::IsLastLine(nsBlockReflowState
& aState
,
3932 line_iterator aLine
)
3934 while (++aLine
!= end_lines()) {
3935 // There is another line
3936 if (0 != aLine
->GetChildCount()) {
3937 // If the next line is a block line then this line is the last in a
3938 // group of inline lines.
3939 return aLine
->IsBlock();
3941 // The next line is empty, try the next one
3944 // XXX Not sure about this part
3945 // Try our next-in-flows lines to answer the question
3946 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*) GetNextInFlow();
3947 while (nullptr != nextInFlow
) {
3948 for (line_iterator line
= nextInFlow
->begin_lines(),
3949 line_end
= nextInFlow
->end_lines();
3953 if (0 != line
->GetChildCount())
3954 return line
->IsBlock();
3956 nextInFlow
= (nsBlockFrame
*) nextInFlow
->GetNextInFlow();
3959 // This is the last line - so don't allow justification
3964 nsBlockFrame::PlaceLine(nsBlockReflowState
& aState
,
3965 nsLineLayout
& aLineLayout
,
3966 line_iterator aLine
,
3967 nsFloatManager::SavedState
*aFloatStateBeforeLine
,
3968 nsRect
& aFloatAvailableSpace
,
3969 nscoord
& aAvailableSpaceHeight
,
3970 bool* aKeepReflowGoing
)
3972 // Trim extra white-space from the line before placing the frames
3973 aLineLayout
.TrimTrailingWhiteSpace();
3975 // Vertically align the frames on this line.
3977 // According to the CSS2 spec, section 12.6.1, the "marker" box
3978 // participates in the height calculation of the list-item box's
3981 // There are exactly two places a bullet can be placed: near the
3982 // first or second line. It's only placed on the second line in a
3983 // rare case: when the first line is empty.
3984 bool addedBullet
= false;
3985 if (HasOutsideBullet() &&
3986 ((aLine
== mLines
.front() &&
3987 (!aLineLayout
.IsZeroHeight() || (aLine
== mLines
.back()))) ||
3988 (mLines
.front() != mLines
.back() &&
3989 0 == mLines
.front()->mBounds
.height
&&
3990 aLine
== mLines
.begin().next()))) {
3991 nsHTMLReflowMetrics metrics
;
3992 nsIFrame
* bullet
= GetOutsideBullet();
3993 ReflowBullet(bullet
, aState
, metrics
, aState
.mY
);
3994 NS_ASSERTION(!BulletIsEmpty() || metrics
.height
== 0,
3995 "empty bullet took up space");
3996 aLineLayout
.AddBulletFrame(bullet
, metrics
);
3999 aLineLayout
.VerticalAlignLine();
4001 // We want to compare to the available space that we would have had in
4002 // the line's height *before* we placed any floats in the line itself.
4003 // Floats that are in the line are handled during line reflow (and may
4004 // result in floats being pushed to below the line or (I HOPE???) in a
4005 // reflow with a forced break position).
4006 nsRect
oldFloatAvailableSpace(aFloatAvailableSpace
);
4007 // As we redo for floats, we can't reduce the amount of height we're
4009 aAvailableSpaceHeight
= std::max(aAvailableSpaceHeight
, aLine
->mBounds
.height
);
4010 aFloatAvailableSpace
=
4011 aState
.GetFloatAvailableSpaceForHeight(aLine
->mBounds
.y
,
4012 aAvailableSpaceHeight
,
4013 aFloatStateBeforeLine
).mRect
;
4014 NS_ASSERTION(aFloatAvailableSpace
.y
== oldFloatAvailableSpace
.y
, "yikes");
4015 // Restore the height to the position of the next band.
4016 aFloatAvailableSpace
.height
= oldFloatAvailableSpace
.height
;
4017 // If the available space between the floats is smaller now that we
4018 // know the height, return false (and cause another pass with
4019 // LINE_REFLOW_REDO_MORE_FLOATS).
4020 if (AvailableSpaceShrunk(oldFloatAvailableSpace
, aFloatAvailableSpace
)) {
4026 static nscoord lastHeight
= 0;
4027 if (CRAZY_HEIGHT(aLine
->mBounds
.y
)) {
4028 lastHeight
= aLine
->mBounds
.y
;
4029 if (abs(aLine
->mBounds
.y
- lastHeight
) > CRAZY_H
/10) {
4030 nsFrame::ListTag(stdout
);
4031 printf(": line=%p y=%d line.bounds.height=%d\n",
4032 static_cast<void*>(aLine
.get()),
4033 aLine
->mBounds
.y
, aLine
->mBounds
.height
);
4042 // Only block frames horizontally align their children because
4043 // inline frames "shrink-wrap" around their children (therefore
4044 // there is no extra horizontal space).
4045 const nsStyleText
* styleText
= StyleText();
4048 * text-align-last defaults to the same value as text-align when
4049 * text-align-last is set to auto (except when text-align is set to justify),
4050 * so in that case we don't need to set isLastLine.
4052 * In other words, isLastLine really means isLastLineAndWeCare.
4056 ((NS_STYLE_TEXT_ALIGN_AUTO
!= styleText
->mTextAlignLast
||
4057 NS_STYLE_TEXT_ALIGN_JUSTIFY
== styleText
->mTextAlign
) &&
4058 (aLineLayout
.GetLineEndsInBR() ||
4059 IsLastLine(aState
, aLine
)));
4060 aLineLayout
.HorizontalAlignFrames(aLine
->mBounds
, isLastLine
);
4061 // XXX: not only bidi: right alignment can be broken after
4062 // RelativePositionFrames!!!
4063 // XXXldb Is something here considering relatively positioned frames at
4064 // other than their original positions?
4066 // XXXldb Why don't we do this earlier?
4067 if (aState
.mPresContext
->BidiEnabled()) {
4068 if (!aState
.mPresContext
->IsVisualMode() ||
4069 StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
4070 nsBidiPresUtils::ReorderFrames(aLine
->mFirstChild
, aLine
->GetChildCount());
4071 } // not visual mode
4075 // From here on, pfd->mBounds rectangles are incorrect because bidi
4076 // might have moved frames around!
4077 nsOverflowAreas overflowAreas
;
4078 aLineLayout
.RelativePositionFrames(overflowAreas
);
4079 aLine
->SetOverflowAreas(overflowAreas
);
4081 aLineLayout
.RemoveBulletFrame(GetOutsideBullet());
4084 // Inline lines do not have margins themselves; however they are
4085 // impacted by prior block margins. If this line ends up having some
4086 // height then we zero out the previous bottom margin value that was
4087 // already applied to the line's starting Y coordinate. Otherwise we
4088 // leave it be so that the previous blocks bottom margin can be
4089 // collapsed with a block that follows.
4092 if (!aLine
->CachedIsEmpty()) {
4093 // This line has some height. Therefore the application of the
4094 // previous-bottom-margin should stick.
4095 aState
.mPrevBottomMargin
.Zero();
4096 newY
= aLine
->mBounds
.YMost();
4099 // Don't let the previous-bottom-margin value affect the newY
4100 // coordinate (it was applied in ReflowInlineFrames speculatively)
4101 // since the line is empty.
4102 // We already called |ShouldApplyTopMargin|, and if we applied it
4103 // then BRS_APPLYTOPMARGIN is set.
4104 nscoord dy
= aState
.GetFlag(BRS_APPLYTOPMARGIN
)
4105 ? -aState
.mPrevBottomMargin
.get() : 0;
4106 newY
= aState
.mY
+ dy
;
4109 if (!NS_FRAME_IS_FULLY_COMPLETE(aState
.mReflowStatus
) &&
4110 ShouldAvoidBreakInside(aState
.mReflowState
)) {
4111 aLine
->AppendFloats(aState
.mCurrentLineFloats
);
4112 aState
.mReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
4116 // See if the line fit (our first line always does).
4117 if (mLines
.front() != aLine
&&
4118 newY
> aState
.mBottomEdge
&&
4119 aState
.mBottomEdge
!= NS_UNCONSTRAINEDSIZE
) {
4120 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
4121 if (ShouldAvoidBreakInside(aState
.mReflowState
)) {
4122 // All our content doesn't fit, start on the next page.
4123 aState
.mReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
4125 // Push aLine and all of its children and anything else that
4126 // follows to our next-in-flow.
4127 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4134 // Add the already placed current-line floats to the line
4135 aLine
->AppendFloats(aState
.mCurrentLineFloats
);
4137 // Any below current line floats to place?
4138 if (aState
.mBelowCurrentLineFloats
.NotEmpty()) {
4139 // Reflow the below-current-line floats, which places on the line's
4141 aState
.PlaceBelowCurrentLineFloats(aState
.mBelowCurrentLineFloats
, aLine
);
4142 aLine
->AppendFloats(aState
.mBelowCurrentLineFloats
);
4145 // When a line has floats, factor them into the combined-area
4147 if (aLine
->HasFloats()) {
4148 // Combine the float combined area (stored in aState) and the
4149 // value computed by the line layout code.
4150 nsOverflowAreas lineOverflowAreas
;
4151 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
4152 nsRect
&o
= lineOverflowAreas
.Overflow(otype
);
4153 o
= aLine
->GetOverflowArea(otype
);
4154 #ifdef NOISY_COMBINED_AREA
4156 printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
4158 o
.x
, o
.y
, o
.width
, o
.height
,
4159 aState
.mFloatOverflowAreas
.Overflow(otype
).x
,
4160 aState
.mFloatOverflowAreas
.Overflow(otype
).y
,
4161 aState
.mFloatOverflowAreas
.Overflow(otype
).width
,
4162 aState
.mFloatOverflowAreas
.Overflow(otype
).height
);
4164 o
.UnionRect(aState
.mFloatOverflowAreas
.Overflow(otype
), o
);
4166 #ifdef NOISY_COMBINED_AREA
4167 printf(" ==> final lineCA=%d,%d,%d,%d\n",
4168 o
.x
, o
.y
, o
.width
, o
.height
);
4171 aLine
->SetOverflowAreas(lineOverflowAreas
);
4174 // Apply break-after clearing if necessary
4175 // This must stay in sync with |ReflowDirtyLines|.
4176 if (aLine
->HasFloatBreakAfter()) {
4177 aState
.mY
= aState
.ClearFloats(aState
.mY
, aLine
->GetBreakTypeAfter());
4183 nsBlockFrame::PushLines(nsBlockReflowState
& aState
,
4184 nsLineList::iterator aLineBefore
)
4186 // NOTE: aLineBefore is always a normal line, not an overflow line.
4187 // The following expression will assert otherwise.
4188 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
4190 nsLineList::iterator
overBegin(aLineBefore
.next());
4192 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
4193 bool firstLine
= overBegin
== begin_lines();
4195 if (overBegin
!= end_lines()) {
4196 // Remove floats in the lines from mFloats
4198 CollectFloats(overBegin
->mFirstChild
, floats
, true);
4200 if (floats
.NotEmpty()) {
4201 // Push the floats onto the front of the overflow out-of-flows list
4202 nsAutoOOFFrameList
oofs(this);
4203 oofs
.mList
.InsertFrames(nullptr, nullptr, floats
);
4206 // overflow lines can already exist in some cases, in particular,
4207 // when shrinkwrapping and we discover that the shrinkwap causes
4208 // the height of some child block to grow which creates additional
4209 // overflowing content. In such cases we must prepend the new
4210 // overflow to the existing overflow.
4211 FrameLines
* overflowLines
= RemoveOverflowLines();
4212 if (!overflowLines
) {
4213 // XXXldb use presshell arena!
4214 overflowLines
= new FrameLines();
4216 if (overflowLines
) {
4217 nsIFrame
* lineBeforeLastFrame
;
4219 lineBeforeLastFrame
= nullptr; // removes all frames
4221 nsIFrame
* f
= overBegin
->mFirstChild
;
4222 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
4223 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
4224 "unexpected line frames");
4226 nsFrameList pushedFrames
= mFrames
.RemoveFramesAfter(lineBeforeLastFrame
);
4227 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr, pushedFrames
);
4229 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
4230 overBegin
, end_lines());
4231 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
4232 // this takes ownership but it won't delete it immediately so we
4233 // can keep using it.
4234 SetOverflowLines(overflowLines
);
4236 // Mark all the overflow lines dirty so that they get reflowed when
4237 // they are pulled up by our next-in-flow.
4239 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4240 for (line_iterator line
= overflowLines
->mLines
.begin(),
4241 line_end
= overflowLines
->mLines
.end();
4246 line
->MarkPreviousMarginDirty();
4247 line
->mBounds
.SetRect(0, 0, 0, 0);
4248 if (line
->HasFloats()) {
4249 line
->FreeFloats(aState
.mFloatCacheFreeList
);
4256 VerifyOverflowSituation();
4260 // The overflowLines property is stored as a pointer to a line list,
4261 // which must be deleted. However, the following functions all maintain
4262 // the invariant that the property is never set if the list is empty.
4265 nsBlockFrame::DrainOverflowLines()
4268 VerifyOverflowSituation();
4271 // Steal the prev-in-flow's overflow lines and prepend them.
4272 bool didFindOverflow
= false;
4273 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
4275 prevBlock
->ClearLineCursor();
4276 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
4277 if (overflowLines
) {
4278 // Make all the frames on the overflow line list mine.
4279 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
4281 // Make the overflow out-of-flow frames mine too.
4282 nsAutoOOFFrameList
oofs(prevBlock
);
4283 if (oofs
.mList
.NotEmpty()) {
4284 ReparentFrames(oofs
.mList
, prevBlock
, this);
4285 mFloats
.InsertFrames(nullptr, nullptr, oofs
.mList
);
4288 if (!mLines
.empty()) {
4289 // Remember to recompute the margins on the first line. This will
4290 // also recompute the correct deltaY if necessary.
4291 mLines
.front()->MarkPreviousMarginDirty();
4293 // The overflow lines have already been marked dirty and their previous
4294 // margins marked dirty also.
4296 // Prepend the overflow frames/lines to our principal list.
4297 mFrames
.InsertFrames(nullptr, nullptr, overflowLines
->mFrames
);
4298 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
4299 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
4300 delete overflowLines
;
4301 didFindOverflow
= true;
4305 // Now append our own overflow lines.
4306 return DrainSelfOverflowList() || didFindOverflow
;
4310 nsBlockFrame::DrainSelfOverflowList()
4312 nsAutoPtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
4313 if (!ourOverflowLines
) {
4317 // No need to reparent frames in our own overflow lines/oofs, because they're
4318 // already ours. But we should put overflow floats back in mFloats.
4319 nsAutoOOFFrameList
oofs(this);
4320 if (oofs
.mList
.NotEmpty()) {
4321 // The overflow floats go after our regular floats.
4322 mFloats
.AppendFrames(nullptr, oofs
.mList
);
4325 if (!ourOverflowLines
->mLines
.empty()) {
4326 mFrames
.AppendFrames(nullptr, ourOverflowLines
->mFrames
);
4327 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
4333 * Pushed floats are floats whose placeholders are in a previous
4334 * continuation. They might themselves be next-continuations of a float
4335 * that partially fit in an earlier continuation, or they might be the
4336 * first continuation of a float that couldn't be placed at all.
4338 * Pushed floats live permanently at the beginning of a block's float
4339 * list, where they must live *before* any floats whose placeholders are
4342 * Temporarily, during reflow, they also live on the pushed floats list,
4343 * which only holds them between (a) when one continuation pushes them to
4344 * its pushed floats list because they don't fit and (b) when the next
4345 * continuation pulls them onto the beginning of its float list.
4347 * DrainPushedFloats sets up pushed floats the way we need them at the
4348 * start of reflow; they are then reflowed by ReflowPushedFloats (which
4349 * might push some of them on). Floats with placeholders in this block
4350 * are reflowed by (nsBlockReflowState/nsLineLayout)::AddFloat, which
4351 * also maintains these invariants.
4354 nsBlockFrame::DrainPushedFloats(nsBlockReflowState
& aState
)
4357 // Between when we drain pushed floats and when we complete reflow,
4358 // we're allowed to have multiple continuations of the same float on
4359 // our floats list, since a first-in-flow might get pushed to a later
4360 // continuation of its containing block. But it's not permitted
4361 // outside that time.
4362 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
4365 // If we're getting reflowed multiple times without our
4366 // next-continuation being reflowed, we might need to pull back floats
4367 // that we just put in the list to be pushed to our next-in-flow.
4368 // We don't want to pull back any next-in-flows of floats on our own
4369 // float list, and we only need to pull back first-in-flows whose
4370 // placeholders were in earlier blocks (since first-in-flows whose
4371 // placeholders are in this block will get pulled appropriately by
4372 // AddFloat, and will then be more likely to be in the correct order).
4373 // FIXME: What if there's a continuation in our pushed floats list
4374 // whose prev-in-flow is in a previous continuation of this block
4375 // rather than this block? Might we need to pull it back so we don't
4376 // report ourselves complete?
4377 // FIXME: Maybe we should just pull all of them back?
4378 nsPresContext
* presContext
= PresContext();
4379 nsFrameList
* ourPushedFloats
= GetPushedFloats();
4380 if (ourPushedFloats
) {
4381 // When we pull back floats, we want to put them with the pushed
4382 // floats, which must live at the start of our float list, but we
4383 // want them at the end of those pushed floats.
4384 // FIXME: This isn't quite right! What if they're all pushed floats?
4385 nsIFrame
*insertionPrevSibling
= nullptr; /* beginning of list */
4386 for (nsIFrame
* f
= mFloats
.FirstChild();
4387 f
&& (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
);
4388 f
= f
->GetNextSibling()) {
4389 insertionPrevSibling
= f
;
4392 for (nsIFrame
*f
= ourPushedFloats
->LastChild(), *next
; f
; f
= next
) {
4393 next
= f
->GetPrevSibling();
4395 if (f
->GetPrevContinuation()) {
4398 nsPlaceholderFrame
*placeholder
=
4399 presContext
->FrameManager()->GetPlaceholderFrameFor(f
);
4400 nsIFrame
*floatOriginalParent
= presContext
->PresShell()->
4401 FrameConstructor()->GetFloatContainingBlock(placeholder
);
4402 if (floatOriginalParent
!= this) {
4403 // This is a first continuation that was pushed from one of our
4404 // previous continuations. Take it out of the pushed floats
4405 // list and put it in our floats list, before any of our
4406 // floats, but after other pushed floats.
4407 ourPushedFloats
->RemoveFrame(f
);
4408 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
4413 if (ourPushedFloats
->IsEmpty()) {
4414 RemovePushedFloats()->Delete(presContext
->PresShell());
4418 // After our prev-in-flow has completed reflow, it may have a pushed
4419 // floats list, containing floats that we need to own. Take these.
4420 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
4422 AutoFrameListPtr
list(presContext
, prevBlock
->RemovePushedFloats());
4423 if (list
&& list
->NotEmpty()) {
4424 mFloats
.InsertFrames(this, nullptr, *list
);
4429 nsBlockFrame::FrameLines
*
4430 nsBlockFrame::GetOverflowLines() const
4432 if (!HasOverflowLines()) {
4436 static_cast<FrameLines
*>(Properties().Get(OverflowLinesProperty()));
4437 NS_ASSERTION(prop
&& !prop
->mLines
.empty() &&
4438 prop
->mLines
.front()->GetChildCount() == 0 ? prop
->mFrames
.IsEmpty() :
4439 prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
4440 "value should always be stored and non-empty when state set");
4444 nsBlockFrame::FrameLines
*
4445 nsBlockFrame::RemoveOverflowLines()
4447 if (!HasOverflowLines()) {
4451 static_cast<FrameLines
*>(Properties().Remove(OverflowLinesProperty()));
4452 NS_ASSERTION(prop
&& !prop
->mLines
.empty() &&
4453 prop
->mLines
.front()->GetChildCount() == 0 ? prop
->mFrames
.IsEmpty() :
4454 prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
4455 "value should always be stored and non-empty when state set");
4456 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
4461 nsBlockFrame::DestroyOverflowLines()
4463 NS_ASSERTION(HasOverflowLines(), "huh?");
4465 static_cast<FrameLines
*>(Properties().Remove(OverflowLinesProperty()));
4466 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
4467 "value should always be stored but empty when destroying");
4468 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
4472 // This takes ownership of aOverflowLines.
4473 // XXX We should allocate overflowLines from presShell arena!
4475 nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
)
4477 NS_ASSERTION(aOverflowLines
, "null lines");
4478 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
4479 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
4480 aOverflowLines
->mFrames
.FirstChild(),
4481 "invalid overflow lines / frames");
4482 NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES
),
4483 "Overwriting existing overflow lines");
4485 FrameProperties props
= Properties();
4486 // Verify that we won't overwrite an existing overflow list
4487 NS_ASSERTION(!props
.Get(OverflowLinesProperty()), "existing overflow list");
4488 props
.Set(OverflowLinesProperty(), aOverflowLines
);
4489 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
4493 nsBlockFrame::GetOverflowOutOfFlows() const
4495 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
4498 nsFrameList
* result
=
4499 GetPropTableFrames(PresContext(), OverflowOutOfFlowsProperty());
4500 NS_ASSERTION(result
, "value should always be non-empty when state set");
4504 // This takes ownership of the frames
4506 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList
& aList
,
4507 nsFrameList
* aPropValue
)
4509 NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) ==
4510 !!aPropValue
, "state does not match value");
4512 if (aList
.IsEmpty()) {
4513 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
4516 nsPresContext
* pc
= PresContext();
4517 nsFrameList
* list
= RemovePropTableFrames(pc
, OverflowOutOfFlowsProperty());
4518 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
4520 list
->Delete(pc
->PresShell());
4521 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
4523 else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) {
4524 NS_ASSERTION(aPropValue
== GetPropTableFrames(PresContext(),
4525 OverflowOutOfFlowsProperty()),
4526 "prop value mismatch");
4527 *aPropValue
= aList
;
4530 nsPresContext
* pc
= PresContext();
4531 SetPropTableFrames(pc
, new (pc
->PresShell()) nsFrameList(aList
),
4532 OverflowOutOfFlowsProperty());
4533 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
4538 nsBlockFrame::GetInsideBullet() const
4540 if (!HasInsideBullet()) {
4543 NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
4544 nsBulletFrame
* frame
=
4545 static_cast<nsBulletFrame
*>(Properties().Get(InsideBulletProperty()));
4546 NS_ASSERTION(frame
&& frame
->GetType() == nsGkAtoms::bulletFrame
,
4547 "bogus inside bullet frame");
4552 nsBlockFrame::GetOutsideBullet() const
4554 nsFrameList
* list
= GetOutsideBulletList();
4555 return list
? static_cast<nsBulletFrame
*>(list
->FirstChild())
4560 nsBlockFrame::GetOutsideBulletList() const
4562 if (!HasOutsideBullet()) {
4565 NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
4567 static_cast<nsFrameList
*>(Properties().Get(OutsideBulletProperty()));
4568 NS_ASSERTION(list
&& list
->GetLength() == 1 &&
4569 list
->FirstChild()->GetType() == nsGkAtoms::bulletFrame
,
4570 "bogus outside bullet list");
4575 nsBlockFrame::GetPushedFloats() const
4577 if (!HasPushedFloats()) {
4580 nsFrameList
* result
=
4581 static_cast<nsFrameList
*>(Properties().Get(PushedFloatProperty()));
4582 NS_ASSERTION(result
, "value should always be non-empty when state set");
4587 nsBlockFrame::EnsurePushedFloats()
4589 nsFrameList
*result
= GetPushedFloats();
4593 result
= new (PresContext()->PresShell()) nsFrameList
;
4594 Properties().Set(PushedFloatProperty(), result
);
4595 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
4601 nsBlockFrame::RemovePushedFloats()
4603 if (!HasPushedFloats()) {
4606 nsFrameList
*result
=
4607 static_cast<nsFrameList
*>(Properties().Remove(PushedFloatProperty()));
4608 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
4609 NS_ASSERTION(result
, "value should always be non-empty when state set");
4613 //////////////////////////////////////////////////////////////////////
4614 // Frame list manipulation routines
4617 nsBlockFrame::AppendFrames(ChildListID aListID
,
4618 nsFrameList
& aFrameList
)
4620 if (aFrameList
.IsEmpty()) {
4623 if (aListID
!= kPrincipalList
) {
4624 if (kAbsoluteList
== aListID
) {
4625 return nsContainerFrame::AppendFrames(aListID
, aFrameList
);
4627 else if (kFloatList
== aListID
) {
4628 mFloats
.AppendFrames(nullptr, aFrameList
);
4632 NS_ERROR("unexpected child list");
4633 return NS_ERROR_INVALID_ARG
;
4637 // Find the proper last-child for where the append should go
4638 nsIFrame
* lastKid
= mFrames
.LastChild();
4639 NS_ASSERTION((mLines
.empty() ? nullptr : mLines
.back()->LastChild()) ==
4640 lastKid
, "out-of-sync mLines / mFrames");
4642 // Add frames after the last child
4643 #ifdef NOISY_REFLOW_REASON
4645 printf(": append ");
4646 nsFrame::ListTag(stdout
, aFrameList
);
4649 nsFrame::ListTag(stdout
, lastKid
);
4654 AddFrames(aFrameList
, lastKid
);
4655 PresContext()->PresShell()->
4656 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
4657 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
4662 nsBlockFrame::InsertFrames(ChildListID aListID
,
4663 nsIFrame
* aPrevFrame
,
4664 nsFrameList
& aFrameList
)
4666 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
4667 "inserting after sibling frame with different parent");
4669 if (aListID
!= kPrincipalList
) {
4670 if (kAbsoluteList
== aListID
) {
4671 return nsContainerFrame::InsertFrames(aListID
, aPrevFrame
, aFrameList
);
4673 else if (kFloatList
== aListID
) {
4674 mFloats
.InsertFrames(this, aPrevFrame
, aFrameList
);
4678 else if (kNoReflowPrincipalList
== aListID
) {}
4681 NS_ERROR("unexpected child list");
4682 return NS_ERROR_INVALID_ARG
;
4686 #ifdef NOISY_REFLOW_REASON
4688 printf(": insert ");
4689 nsFrame::ListTag(stdout
, aFrameList
);
4692 nsFrame::ListTag(stdout
, aPrevFrame
);
4697 AddFrames(aFrameList
, aPrevFrame
);
4700 if (aListID
!= kNoReflowPrincipalList
)
4702 PresContext()->PresShell()->
4703 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
4704 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
4709 ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
)
4711 nsIAtom
* type
= aLastFrame
->GetType();
4712 if (type
== nsGkAtoms::brFrame
) {
4715 // XXX the IS_DIRTY check is a wallpaper for bug 822910.
4716 if (type
== nsGkAtoms::textFrame
&&
4717 !(aLastFrame
->GetStateBits() & NS_FRAME_IS_DIRTY
)) {
4718 return aLastFrame
->HasTerminalNewline() &&
4719 aLastFrame
->StyleText()->NewlineIsSignificant();
4725 nsBlockFrame::AddFrames(nsFrameList
& aFrameList
, nsIFrame
* aPrevSibling
)
4727 // Clear our line cursor, since our lines may change.
4730 if (aFrameList
.IsEmpty()) {
4734 // If we're inserting at the beginning of our list and we have an
4735 // inside bullet, insert after that bullet.
4736 if (!aPrevSibling
&& HasInsideBullet()) {
4737 aPrevSibling
= GetInsideBullet();
4740 // Attempt to find the line that contains the previous sibling
4741 FrameLines
* overflowLines
;
4742 nsLineList
* lineList
= &mLines
;
4743 nsLineList::iterator prevSibLine
= lineList
->end();
4744 int32_t prevSiblingIndex
= -1;
4746 // XXX_perf This is technically O(N^2) in some cases, but by using
4747 // RFind instead of Find, we make it O(N) in the most common case,
4748 // which is appending content.
4750 // Find the line that contains the previous sibling
4751 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
4752 prevSibLine
, mFrames
.LastChild(),
4753 &prevSiblingIndex
)) {
4754 // Not in mLines - try overflow lines.
4755 overflowLines
= GetOverflowLines();
4756 lineList
= overflowLines
? &overflowLines
->mLines
: nullptr;
4757 if (overflowLines
) {
4758 prevSibLine
= overflowLines
->mLines
.end();
4759 prevSiblingIndex
= -1;
4760 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
4762 overflowLines
->mFrames
.LastChild(),
4763 &prevSiblingIndex
)) {
4768 // Note: defensive code! RFindLineContaining must not return
4769 // false in this case, so if it does...
4770 NS_NOTREACHED("prev sibling not in line list");
4772 aPrevSibling
= nullptr;
4773 prevSibLine
= lineList
->end();
4778 // Find the frame following aPrevSibling so that we can join up the
4779 // two lists of frames.
4781 // Split line containing aPrevSibling in two if the insertion
4782 // point is somewhere in the middle of the line.
4783 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
4785 // Split the line in two where the frame(s) are being inserted.
4786 nsLineBox
* line
= NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
4787 lineList
->after_insert(prevSibLine
, line
);
4788 // Mark prevSibLine dirty and as needing textrun invalidation, since
4789 // we may be breaking up text in the line. Its previous line may also
4790 // need to be invalidated because it may be able to pull some text up.
4791 MarkLineDirty(prevSibLine
, lineList
);
4792 // The new line will also need its textruns recomputed because of the
4795 line
->SetInvalidateTextRuns(true);
4798 else if (! lineList
->empty()) {
4799 lineList
->front()->MarkDirty();
4800 lineList
->front()->SetInvalidateTextRuns(true);
4802 nsFrameList
& frames
= lineList
== &mLines
? mFrames
: overflowLines
->mFrames
;
4803 const nsFrameList::Slice
& newFrames
=
4804 frames
.InsertFrames(nullptr, aPrevSibling
, aFrameList
);
4806 // Walk through the new frames being added and update the line data
4807 // structures to fit.
4808 for (nsFrameList::Enumerator
e(newFrames
); !e
.AtEnd(); e
.Next()) {
4809 nsIFrame
* newFrame
= e
.get();
4810 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
4811 "Unexpected aPrevSibling");
4812 NS_ASSERTION(newFrame
->GetType() != nsGkAtoms::placeholderFrame
||
4813 (!newFrame
->IsAbsolutelyPositioned() &&
4814 !newFrame
->IsFloating()),
4815 "Placeholders should not float or be positioned");
4817 bool isBlock
= newFrame
->IsBlockOutside();
4819 // If the frame is a block frame, or if there is no previous line or if the
4820 // previous line is a block line we need to make a new line. We also make
4821 // a new line, as an optimization, in the two cases we know we'll need it:
4822 // if the previous line ended with a <br>, or if it has significant whitespace
4823 // and ended in a newline.
4824 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
4825 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
4826 // Create a new line for the frame and add its line to the line
4828 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
4829 if (prevSibLine
!= lineList
->end()) {
4830 // Append new line after prevSibLine
4831 lineList
->after_insert(prevSibLine
, line
);
4835 // New line is going before the other lines
4836 lineList
->push_front(line
);
4837 prevSibLine
= lineList
->begin();
4841 prevSibLine
->NoteFrameAdded(newFrame
);
4842 // We're adding inline content to prevSibLine, so we need to mark it
4843 // dirty, ensure its textruns are recomputed, and possibly do the same
4844 // to its previous line since that line may be able to pull content up.
4845 MarkLineDirty(prevSibLine
, lineList
);
4848 aPrevSibling
= newFrame
;
4852 MOZ_ASSERT(aFrameList
.IsEmpty());
4858 nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
)
4860 // Find which line contains the float, so we can update
4862 line_iterator line
= begin_lines(), line_end
= end_lines();
4863 for ( ; line
!= line_end
; ++line
) {
4864 if (line
->IsInline() && line
->RemoveFloat(aFloat
)) {
4871 nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
)
4874 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
4875 // frame list properties.
4876 if (!mFloats
.ContainsFrame(aFloat
)) {
4877 MOZ_ASSERT((GetOverflowOutOfFlows() &&
4878 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
4879 (GetPushedFloats() &&
4880 GetPushedFloats()->ContainsFrame(aFloat
)),
4881 "aFloat is not our child or on an unexpected frame list");
4885 if (mFloats
.StartRemoveFrame(aFloat
)) {
4889 nsFrameList
* list
= GetPushedFloats();
4890 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
4892 // XXXmats not yet - need to investigate nsBlockReflowState::mPushedFloats
4893 // first so we don't leave it pointing to a deleted list.
4894 if (list
->IsEmpty()) {
4895 delete RemovePushedFloats();
4902 nsAutoOOFFrameList
oofs(this);
4903 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
4909 static void MarkSameFloatManagerLinesDirty(nsBlockFrame
* aBlock
)
4911 nsBlockFrame
* blockWithFloatMgr
= aBlock
;
4912 while (!(blockWithFloatMgr
->GetStateBits() & NS_BLOCK_FLOAT_MGR
)) {
4913 nsBlockFrame
* bf
= nsLayoutUtils::GetAsBlock(blockWithFloatMgr
->GetParent());
4917 blockWithFloatMgr
= bf
;
4920 // Mark every line at and below the line where the float was
4921 // dirty, and mark their lines dirty too. We could probably do
4922 // something more efficient --- e.g., just dirty the lines that intersect
4923 // the float vertically.
4924 MarkAllDescendantLinesDirty(blockWithFloatMgr
);
4928 * Returns true if aFrame is a block that has one or more float children.
4930 static bool BlockHasAnyFloats(nsIFrame
* aFrame
)
4932 nsBlockFrame
* block
= nsLayoutUtils::GetAsBlock(aFrame
);
4935 if (block
->GetFirstChild(nsIFrame::kFloatList
))
4938 nsLineList::iterator line
= block
->begin_lines();
4939 nsLineList::iterator endLine
= block
->end_lines();
4940 while (line
!= endLine
) {
4941 if (line
->IsBlock() && BlockHasAnyFloats(line
->mFirstChild
))
4949 nsBlockFrame::RemoveFrame(ChildListID aListID
,
4950 nsIFrame
* aOldFrame
)
4952 nsresult rv
= NS_OK
;
4954 #ifdef NOISY_REFLOW_REASON
4956 printf(": remove ");
4957 nsFrame::ListTag(stdout
, aOldFrame
);
4961 if (aListID
== kPrincipalList
) {
4962 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
4963 rv
= DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
4965 MarkSameFloatManagerLinesDirty(this);
4968 else if (kAbsoluteList
== aListID
) {
4969 nsContainerFrame::RemoveFrame(aListID
, aOldFrame
);
4972 else if (kFloatList
== aListID
) {
4973 // Make sure to mark affected lines dirty for the float frame
4974 // we are removing; this way is a bit messy, but so is the rest of the code.
4976 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
4977 "RemoveFrame should not be called on pushed floats.");
4978 for (nsIFrame
* f
= aOldFrame
;
4979 f
&& !(f
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
);
4980 f
= f
->GetNextContinuation()) {
4981 MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame
*>(f
->GetParent()));
4983 DoRemoveOutOfFlowFrame(aOldFrame
);
4986 else if (kNoReflowPrincipalList
== aListID
) {
4987 // Skip the call to |FrameNeedsReflow| below by returning now.
4988 return DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
4992 NS_ERROR("unexpected child list");
4993 rv
= NS_ERROR_INVALID_ARG
;
4996 if (NS_SUCCEEDED(rv
)) {
4997 PresContext()->PresShell()->
4998 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
4999 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5005 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame
* aFrame
)
5007 // The containing block is always the parent of aFrame.
5008 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
5010 // Remove aFrame from the appropriate list.
5011 if (aFrame
->IsAbsolutelyPositioned()) {
5012 // This also deletes the next-in-flows
5013 block
->GetAbsoluteContainingBlock()->RemoveFrame(block
,
5018 // First remove aFrame's next-in-flows.
5019 nsIFrame
* nif
= aFrame
->GetNextInFlow();
5021 static_cast<nsContainerFrame
*>(nif
->GetParent())
5022 ->DeleteNextInFlowChild(aFrame
->PresContext(), nif
, false);
5024 // Now remove aFrame from its child list and Destroy it.
5025 block
->RemoveFloatFromFloatCache(aFrame
);
5026 block
->RemoveFloat(aFrame
);
5032 * This helps us iterate over the list of all normal + overflow lines
5035 nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
5036 nsLineList::iterator
* aStartIterator
,
5037 nsLineList::iterator
* aEndIterator
,
5038 bool* aInOverflowLines
,
5039 FrameLines
** aOverflowLines
)
5041 if (*aIterator
== *aEndIterator
) {
5042 if (!*aInOverflowLines
) {
5043 // Try the overflow lines
5044 *aInOverflowLines
= true;
5045 FrameLines
* lines
= GetOverflowLines();
5047 *aStartIterator
= lines
->mLines
.begin();
5048 *aIterator
= *aStartIterator
;
5049 *aEndIterator
= lines
->mLines
.end();
5050 *aOverflowLines
= lines
;
5056 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5057 line_iterator aLine
)
5058 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
)
5060 // This will assert if aLine isn't in mLines of aFrame:
5061 DebugOnly
<bool> check
= aLine
== mFrame
->begin_lines();
5064 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5065 line_iterator aLine
, bool aInOverflow
)
5066 : mFrame(aFrame
), mLine(aLine
),
5067 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
5072 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5073 bool* aFoundValidLine
)
5074 : mFrame(aFrame
), mLineList(&aFrame
->mLines
)
5076 mLine
= aFrame
->begin_lines();
5077 *aFoundValidLine
= FindValidLine();
5081 FindChildContaining(nsBlockFrame
* aFrame
, nsIFrame
* aFindFrame
)
5083 NS_ASSERTION(aFrame
, "must have frame");
5086 nsIFrame
* block
= aFrame
;
5088 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
5091 block
= block
->GetNextContinuation();
5095 if (!(child
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
))
5097 aFindFrame
= aFrame
->PresContext()->FrameManager()->GetPlaceholderFrameFor(child
);
5103 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5104 nsIFrame
* aFindFrame
, bool* aFoundValidLine
)
5105 : mFrame(aFrame
), mLineList(&aFrame
->mLines
)
5107 *aFoundValidLine
= false;
5109 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
5113 // Try to use the cursor if it exists, otherwise fall back to the first line
5114 nsLineBox
* cursor
= aFrame
->GetLineCursor();
5116 line_iterator iter
= aFrame
->begin_lines();
5117 if (iter
!= aFrame
->end_lines()) {
5123 // Perform a simultaneous forward and reverse search starting from the
5125 nsBlockFrame::line_iterator line
= aFrame
->line(cursor
);
5126 nsBlockFrame::reverse_line_iterator rline
= aFrame
->rline(cursor
);
5127 nsBlockFrame::line_iterator line_end
= aFrame
->end_lines();
5128 nsBlockFrame::reverse_line_iterator rline_end
= aFrame
->rend_lines();
5129 // rline is positioned on the line containing 'cursor', so it's not
5130 // rline_end. So we can safely increment it (i.e. move it to one line
5131 // earlier) to start searching there.
5133 while (line
!= line_end
|| rline
!= rline_end
) {
5134 if (line
!= line_end
) {
5135 if (line
->Contains(child
)) {
5136 *aFoundValidLine
= true;
5142 if (rline
!= rline_end
) {
5143 if (rline
->Contains(child
)) {
5144 *aFoundValidLine
= true;
5151 // Didn't find the line
5154 // If we reach here, it means that we have not been able to find the
5155 // desired frame in our in-flow lines. So we should start looking at
5156 // our overflow lines. In order to do that, we set mLine to the end
5157 // iterator so that FindValidLine starts to look at overflow lines,
5160 mLine
= aFrame
->end_lines();
5162 if (!FindValidLine())
5166 if (mLine
->Contains(child
)) {
5167 *aFoundValidLine
= true;
5173 nsBlockFrame::line_iterator
5174 nsBlockInFlowLineIterator::End()
5176 return mLineList
->end();
5180 nsBlockInFlowLineIterator::IsLastLineInList()
5182 line_iterator end
= End();
5183 return mLine
!= end
&& mLine
.next() == end
;
5187 nsBlockInFlowLineIterator::Next()
5190 return FindValidLine();
5194 nsBlockInFlowLineIterator::Prev()
5196 line_iterator begin
= mLineList
->begin();
5197 if (mLine
!= begin
) {
5201 bool currentlyInOverflowLines
= GetInOverflow();
5203 if (currentlyInOverflowLines
) {
5204 mLineList
= &mFrame
->mLines
;
5205 mLine
= mLineList
->end();
5206 if (mLine
!= mLineList
->begin()) {
5211 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
5214 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
5215 if (overflowLines
) {
5216 mLineList
= &overflowLines
->mLines
;
5217 mLine
= mLineList
->end();
5218 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
5223 currentlyInOverflowLines
= !currentlyInOverflowLines
;
5228 nsBlockInFlowLineIterator::FindValidLine()
5230 line_iterator end
= mLineList
->end();
5233 bool currentlyInOverflowLines
= GetInOverflow();
5235 if (currentlyInOverflowLines
) {
5236 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
5239 mLineList
= &mFrame
->mLines
;
5240 mLine
= mLineList
->begin();
5241 if (mLine
!= mLineList
->end())
5244 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
5245 if (overflowLines
) {
5246 mLineList
= &overflowLines
->mLines
;
5247 mLine
= mLineList
->begin();
5248 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
5252 currentlyInOverflowLines
= !currentlyInOverflowLines
;
5256 static nsresult
RemoveBlockChild(nsIFrame
* aFrame
,
5257 bool aRemoveOnlyFluidContinuations
)
5262 nsBlockFrame
* nextBlock
= nsLayoutUtils::GetAsBlock(aFrame
->GetParent());
5263 NS_ASSERTION(nextBlock
,
5264 "Our child's continuation's parent is not a block?");
5265 return nextBlock
->DoRemoveFrame(aFrame
,
5266 (aRemoveOnlyFluidContinuations
? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS
));
5269 // This function removes aDeletedFrame and all its continuations. It
5270 // is optimized for deleting a whole series of frames. The easy
5271 // implementation would invoke itself recursively on
5272 // aDeletedFrame->GetNextContinuation, then locate the line containing
5273 // aDeletedFrame and remove aDeletedFrame from that line. But here we
5274 // start by locating aDeletedFrame and then scanning from that point
5275 // on looking for continuations.
5277 nsBlockFrame::DoRemoveFrame(nsIFrame
* aDeletedFrame
, uint32_t aFlags
)
5279 // Clear our line cursor, since our lines may change.
5282 nsPresContext
* presContext
= PresContext();
5283 if (aDeletedFrame
->GetStateBits() &
5284 (NS_FRAME_OUT_OF_FLOW
| NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
5285 if (!aDeletedFrame
->GetPrevInFlow()) {
5286 NS_ASSERTION(aDeletedFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
5287 "Expected out-of-flow frame");
5288 DoRemoveOutOfFlowFrame(aDeletedFrame
);
5291 nsContainerFrame::DeleteNextInFlowChild(presContext
, aDeletedFrame
,
5292 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
5297 // Find the line that contains deletedFrame
5298 nsLineList::iterator line_start
= mLines
.begin(),
5299 line_end
= mLines
.end();
5300 nsLineList::iterator line
= line_start
;
5301 FrameLines
* overflowLines
= nullptr;
5302 bool searchingOverflowList
= false;
5303 // Make sure we look in the overflow lines even if the normal line
5305 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
5307 while (line
!= line_end
) {
5308 if (line
->Contains(aDeletedFrame
)) {
5312 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
5316 if (line
== line_end
) {
5317 NS_ERROR("can't find deleted frame in lines");
5318 return NS_ERROR_FAILURE
;
5321 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
5322 if (line
!= line_start
) {
5323 line
.prev()->MarkDirty();
5324 line
.prev()->SetInvalidateTextRuns(true);
5326 else if (searchingOverflowList
&& !mLines
.empty()) {
5327 mLines
.back()->MarkDirty();
5328 mLines
.back()->SetInvalidateTextRuns(true);
5332 while (line
!= line_end
&& aDeletedFrame
) {
5333 NS_ASSERTION(this == aDeletedFrame
->GetParent(), "messed up delete code");
5334 NS_ASSERTION(line
->Contains(aDeletedFrame
), "frame not in line");
5336 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
5338 line
->SetInvalidateTextRuns(true);
5341 // If the frame being deleted is the last one on the line then
5342 // optimize away the line->Contains(next-in-flow) call below.
5343 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
5344 if (!isLastFrameOnLine
) {
5345 line_iterator next
= line
.next();
5346 nsIFrame
* lastFrame
= next
!= line_end
?
5347 next
->mFirstChild
->GetPrevSibling() :
5348 (searchingOverflowList
? overflowLines
->mFrames
.LastChild() :
5349 mFrames
.LastChild());
5350 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
5351 "unexpected line frames");
5352 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
5355 // Remove aDeletedFrame from the line
5356 if (line
->mFirstChild
== aDeletedFrame
) {
5357 // We should be setting this to null if aDeletedFrame
5358 // is the only frame on the line. HOWEVER in that case
5359 // we will be removing the line anyway, see below.
5360 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
5363 // Hmm, this won't do anything if we're removing a frame in the first
5364 // overflow line... Hopefully doesn't matter
5366 if (line
!= line_end
&& !line
->IsBlock()) {
5367 // Since we just removed a frame that follows some inline
5368 // frames, we need to reflow the previous line.
5373 // Take aDeletedFrame out of the sibling list. Note that
5374 // prevSibling will only be nullptr when we are deleting the very
5375 // first frame in the main or overflow list.
5376 if (searchingOverflowList
) {
5377 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
5379 mFrames
.RemoveFrame(aDeletedFrame
);
5382 // Update the child count of the line to be accurate
5383 line
->NoteFrameRemoved(aDeletedFrame
);
5385 // Destroy frame; capture its next continuation first in case we need
5386 // to destroy that too.
5387 nsIFrame
* deletedNextContinuation
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
) ?
5388 aDeletedFrame
->GetNextContinuation() : aDeletedFrame
->GetNextInFlow();
5389 #ifdef NOISY_REMOVE_FRAME
5390 printf("DoRemoveFrame: %s line=%p frame=",
5391 searchingOverflowList
?"overflow":"normal", line
.get());
5392 nsFrame::ListTag(stdout
, aDeletedFrame
);
5393 printf(" prevSibling=%p deletedNextContinuation=%p\n",
5394 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
5397 // If next-in-flow is an overflow container, must remove it first.
5398 if (deletedNextContinuation
&&
5399 deletedNextContinuation
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
) {
5400 static_cast<nsContainerFrame
*>(deletedNextContinuation
->GetParent())
5401 ->DeleteNextInFlowChild(presContext
, deletedNextContinuation
, false);
5402 deletedNextContinuation
= nullptr;
5405 aDeletedFrame
->Destroy();
5406 aDeletedFrame
= deletedNextContinuation
;
5408 bool haveAdvancedToNextLine
= false;
5409 // If line is empty, remove it now.
5410 if (0 == line
->GetChildCount()) {
5411 #ifdef NOISY_REMOVE_FRAME
5412 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
5413 searchingOverflowList
?"overflow":"normal", line
.get());
5415 nsLineBox
*cur
= line
;
5416 if (!searchingOverflowList
) {
5417 line
= mLines
.erase(line
);
5418 // Invalidate the space taken up by the line.
5419 // XXX We need to do this if we're removing a frame as a result of
5420 // a call to RemoveFrame(), but we may not need to do this in all
5422 #ifdef NOISY_BLOCK_INVALIDATE
5423 nsRect
visOverflow(cur
->GetVisualOverflowArea());
5424 printf("%p invalidate 10 (%d, %d, %d, %d)\n",
5425 this, visOverflow
.x
, visOverflow
.y
,
5426 visOverflow
.width
, visOverflow
.height
);
5429 line
= overflowLines
->mLines
.erase(line
);
5430 if (overflowLines
->mLines
.empty()) {
5431 DestroyOverflowLines();
5432 overflowLines
= nullptr;
5433 // We just invalidated our iterators. Since we were in
5434 // the overflow lines list, which is now empty, set them
5435 // so we're at the end of the regular line list.
5436 line_start
= mLines
.begin();
5437 line_end
= mLines
.end();
5443 // If we're removing a line, ReflowDirtyLines isn't going to
5444 // know that it needs to slide lines unless something is marked
5445 // dirty. So mark the previous margin of the next line dirty if
5447 if (line
!= line_end
) {
5448 line
->MarkPreviousMarginDirty();
5450 haveAdvancedToNextLine
= true;
5452 // Make the line that just lost a frame dirty, and advance to
5454 if (!deletedNextContinuation
|| isLastFrameOnLine
||
5455 !line
->Contains(deletedNextContinuation
)) {
5458 haveAdvancedToNextLine
= true;
5462 if (deletedNextContinuation
) {
5463 // See if we should keep looking in the current flow's line list.
5464 if (deletedNextContinuation
->GetParent() != this) {
5465 // The deceased frames continuation is not a child of the
5466 // current block. So break out of the loop so that we advance
5467 // to the next parent.
5469 // If we have a continuation in a different block then all bets are
5470 // off regarding whether we are deleting frames without actual content,
5471 // so don't propagate FRAMES_ARE_EMPTY any further.
5472 aFlags
&= ~FRAMES_ARE_EMPTY
;
5476 // If we advanced to the next line then check if we should switch to the
5477 // overflow line list.
5478 if (haveAdvancedToNextLine
) {
5479 if (line
!= line_end
&& !searchingOverflowList
&&
5480 !line
->Contains(deletedNextContinuation
)) {
5481 // We have advanced to the next *normal* line but the next-in-flow
5482 // is not there - force a switch to the overflow line list.
5486 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
5488 #ifdef NOISY_REMOVE_FRAME
5489 printf("DoRemoveFrame: now on %s line=%p\n",
5490 searchingOverflowList
?"overflow":"normal", line
.get());
5496 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
5497 line
.next()->MarkDirty();
5498 line
.next()->SetInvalidateTextRuns(true);
5503 VerifyOverflowSituation();
5506 // Advance to next flow block if the frame has more continuations
5507 return RemoveBlockChild(aDeletedFrame
, !(aFlags
& REMOVE_FIXED_CONTINUATIONS
));
5511 FindBlockLineFor(nsIFrame
* aChild
,
5512 nsLineList::iterator aBegin
,
5513 nsLineList::iterator aEnd
,
5514 nsLineList::iterator
* aResult
)
5516 MOZ_ASSERT(aChild
->IsBlockOutside());
5517 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
5518 MOZ_ASSERT(line
->GetChildCount() > 0);
5519 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
5520 MOZ_ASSERT(line
->GetChildCount() == 1);
5529 FindInlineLineFor(nsIFrame
* aChild
,
5530 const nsFrameList
& aFrameList
,
5531 nsLineList::iterator aBegin
,
5532 nsLineList::iterator aEnd
,
5533 nsLineList::iterator
* aResult
)
5535 MOZ_ASSERT(!aChild
->IsBlockOutside());
5536 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
5537 MOZ_ASSERT(line
->GetChildCount() > 0);
5538 if (!line
->IsBlock()) {
5539 // Optimize by comparing the line's last child first.
5540 nsLineList::iterator next
= line
.next();
5541 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
5542 : next
->mFirstChild
->GetPrevSibling()) ||
5543 line
->Contains(aChild
)) {
5553 FindLineFor(nsIFrame
* aChild
,
5554 const nsFrameList
& aFrameList
,
5555 nsLineList::iterator aBegin
,
5556 nsLineList::iterator aEnd
,
5557 nsLineList::iterator
* aResult
)
5559 return aChild
->IsBlockOutside() ?
5560 FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
) :
5561 FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
5565 nsBlockFrame::StealFrame(nsPresContext
* aPresContext
,
5569 MOZ_ASSERT(aChild
->GetParent() == this);
5571 if ((aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) &&
5572 aChild
->IsFloating()) {
5573 RemoveFloat(aChild
);
5577 if ((aChild
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
)
5579 return nsContainerFrame::StealFrame(aPresContext
, aChild
);
5582 MOZ_ASSERT(!(aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
));
5584 nsLineList::iterator line
;
5585 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
5586 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
5588 FrameLines
* overflowLines
= GetOverflowLines();
5589 DebugOnly
<bool> found
;
5590 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
5591 overflowLines
->mLines
.begin(),
5592 overflowLines
->mLines
.end(), &line
);
5594 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
5595 overflowLines
->mLines
);
5596 if (overflowLines
->mLines
.empty()) {
5597 DestroyOverflowLines();
5605 nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
, nsLineList::iterator aLine
,
5606 nsFrameList
& aFrameList
, nsLineList
& aLineList
)
5608 aFrameList
.RemoveFrame(aChild
);
5609 if (aChild
== aLine
->mFirstChild
) {
5610 aLine
->mFirstChild
= aChild
->GetNextSibling();
5612 aLine
->NoteFrameRemoved(aChild
);
5613 if (aLine
->GetChildCount() > 0) {
5616 // The line became empty - destroy it.
5617 nsLineBox
* lineBox
= aLine
;
5618 aLine
= aLineList
.erase(aLine
);
5619 if (aLine
!= aLineList
.end()) {
5620 aLine
->MarkPreviousMarginDirty();
5622 FreeLineBox(lineBox
);
5627 nsBlockFrame::DeleteNextInFlowChild(nsPresContext
* aPresContext
,
5628 nsIFrame
* aNextInFlow
,
5629 bool aDeletingEmptyFrames
)
5631 NS_PRECONDITION(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
5633 if (aNextInFlow
->GetStateBits() &
5634 (NS_FRAME_OUT_OF_FLOW
| NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
5635 nsContainerFrame::DeleteNextInFlowChild(aPresContext
,
5636 aNextInFlow
, aDeletingEmptyFrames
);
5640 if (aDeletingEmptyFrames
) {
5641 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
5644 DoRemoveFrame(aNextInFlow
,
5645 aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
5649 ////////////////////////////////////////////////////////////////////////
5653 nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState
& aState
,
5654 const nsRect
& aFloatAvailableSpace
,
5655 nsIFrame
* aFloatFrame
)
5657 // Compute the available width. By default, assume the width of the
5658 // containing block.
5660 const nsStyleDisplay
* floatDisplay
= aFloatFrame
->StyleDisplay();
5662 if (NS_STYLE_DISPLAY_TABLE
!= floatDisplay
->mDisplay
||
5663 eCompatibility_NavQuirks
!= aState
.mPresContext
->CompatibilityMode() ) {
5664 availWidth
= aState
.mContentArea
.width
;
5667 // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
5668 // give tables only the available space
5669 // if they can shrink we may not be constrained to place
5670 // them in the next line
5671 availWidth
= aFloatAvailableSpace
.width
;
5674 nscoord availHeight
= NS_UNCONSTRAINEDSIZE
== aState
.mContentArea
.height
5675 ? NS_UNCONSTRAINEDSIZE
5676 : std::max(0, aState
.mContentArea
.YMost() - aState
.mY
);
5678 #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS
5679 if (availHeight
!= NS_UNCONSTRAINEDSIZE
&&
5680 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame
)) {
5681 // Tell the float it has unrestricted height, so it won't break.
5682 // If the float doesn't actually fit in the column it will fail to be
5683 // placed, and either move to the top of the next column or just
5685 availHeight
= NS_UNCONSTRAINEDSIZE
;
5689 return nsRect(aState
.mContentArea
.x
,
5690 aState
.mContentArea
.y
,
5691 availWidth
, availHeight
);
5695 nsBlockFrame::ComputeFloatWidth(nsBlockReflowState
& aState
,
5696 const nsRect
& aFloatAvailableSpace
,
5699 NS_PRECONDITION(aFloat
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
5700 "aFloat must be an out-of-flow frame");
5701 // Reflow the float.
5702 nsRect availSpace
= AdjustFloatAvailableSpace(aState
, aFloatAvailableSpace
,
5705 nsHTMLReflowState
floatRS(aState
.mPresContext
, aState
.mReflowState
, aFloat
,
5707 return floatRS
.ComputedWidth() + floatRS
.mComputedBorderPadding
.LeftRight() +
5708 floatRS
.mComputedMargin
.LeftRight();
5712 nsBlockFrame::ReflowFloat(nsBlockReflowState
& aState
,
5713 const nsRect
& aAdjustedAvailableSpace
,
5715 nsMargin
& aFloatMargin
,
5716 nsMargin
& aFloatOffsets
,
5717 bool aFloatPushedDown
,
5718 nsReflowStatus
& aReflowStatus
)
5720 NS_PRECONDITION(aFloat
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
5721 "aFloat must be an out-of-flow frame");
5722 // Reflow the float.
5723 aReflowStatus
= NS_FRAME_COMPLETE
;
5726 printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
5728 aFloatAvailableSpace
.x
, aFloatAvailableSpace
.y
,
5729 aFloatAvailableSpace
.width
, aFloatAvailableSpace
.height
5733 nsHTMLReflowState
floatRS(aState
.mPresContext
, aState
.mReflowState
, aFloat
,
5734 nsSize(aAdjustedAvailableSpace
.width
,
5735 aAdjustedAvailableSpace
.height
));
5737 // Normally the mIsTopOfPage state is copied from the parent reflow
5738 // state. However, when reflowing a float, if we've placed other
5739 // floats that force this float *down* or *narrower*, we should unset
5740 // the mIsTopOfPage state.
5741 // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
5742 // variable below, which has the exact same effect. Perhaps it should
5743 // be merged into that, except that the test for narrowing here is not
5744 // about adjacency with the top, so it seems misleading.
5745 if (floatRS
.mFlags
.mIsTopOfPage
&&
5746 (aFloatPushedDown
||
5747 aAdjustedAvailableSpace
.width
!= aState
.mContentArea
.width
)) {
5748 floatRS
.mFlags
.mIsTopOfPage
= false;
5751 // Setup a block reflow context to reflow the float.
5752 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowState
);
5755 bool isAdjacentWithTop
= aState
.IsAdjacentWithTop();
5757 nsIFrame
* clearanceFrame
= nullptr;
5760 nsCollapsingMargin margin
;
5761 bool mayNeedRetry
= false;
5762 floatRS
.mDiscoveredClearance
= nullptr;
5763 // Only first in flow gets a top margin.
5764 if (!aFloat
->GetPrevInFlow()) {
5765 nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS
, &margin
,
5766 clearanceFrame
, &mayNeedRetry
);
5768 if (mayNeedRetry
&& !clearanceFrame
) {
5769 floatRS
.mDiscoveredClearance
= &clearanceFrame
;
5770 // We don't need to push the float manager state because the the block has its own
5771 // float manager that will be destroyed and recreated
5775 rv
= brc
.ReflowBlock(aAdjustedAvailableSpace
, true, margin
,
5776 0, isAdjacentWithTop
,
5778 aReflowStatus
, aState
);
5779 } while (NS_SUCCEEDED(rv
) && clearanceFrame
);
5781 if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus
) &&
5782 ShouldAvoidBreakInside(floatRS
)) {
5783 aReflowStatus
= NS_INLINE_LINE_BREAK_BEFORE();
5784 } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus
) &&
5785 (NS_UNCONSTRAINEDSIZE
== aAdjustedAvailableSpace
.height
)) {
5786 // An incomplete reflow status means we should split the float
5787 // if the height is constrained (bug 145305).
5788 aReflowStatus
= NS_FRAME_COMPLETE
;
5791 if (aReflowStatus
& NS_FRAME_REFLOW_NEXTINFLOW
) {
5792 aState
.mReflowStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
5795 if (aFloat
->GetType() == nsGkAtoms::letterFrame
) {
5796 // We never split floating first letters; an incomplete state for
5797 // such frames simply means that there is more content to be
5798 // reflowed on the line.
5799 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus
))
5800 aReflowStatus
= NS_FRAME_COMPLETE
;
5803 if (NS_FAILED(rv
)) {
5807 // Capture the margin and offsets information for the caller
5808 aFloatMargin
= floatRS
.mComputedMargin
; // float margins don't collapse
5809 aFloatOffsets
= floatRS
.mComputedOffsets
;
5811 const nsHTMLReflowMetrics
& metrics
= brc
.GetMetrics();
5813 // Set the rect, make sure the view is properly sized and positioned,
5814 // and tell the frame we're done reflowing it
5815 // XXXldb This seems like the wrong place to be doing this -- shouldn't
5816 // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
5817 // we've positioned the float, and shouldn't we be doing the equivalent
5818 // of |PlaceFrameView| here?
5819 aFloat
->SetSize(nsSize(metrics
.width
, metrics
.height
));
5820 if (aFloat
->HasView()) {
5821 nsContainerFrame::SyncFrameViewAfterReflow(aState
.mPresContext
, aFloat
,
5823 metrics
.VisualOverflow(),
5824 NS_FRAME_NO_MOVE_VIEW
);
5826 // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
5827 aFloat
->DidReflow(aState
.mPresContext
, &floatRS
,
5828 nsDidReflowStatus::FINISHED
);
5831 printf("end ReflowFloat %p, sized to %d,%d\n",
5832 aFloat
, metrics
.width
, metrics
.height
);
5839 nsBlockFrame::FindTrailingClear()
5841 // find the break type of the last line
5842 for (nsIFrame
* b
= this; b
; b
= b
->GetPrevInFlow()) {
5843 nsBlockFrame
* block
= static_cast<nsBlockFrame
*>(b
);
5844 line_iterator endLine
= block
->end_lines();
5845 if (endLine
!= block
->begin_lines()) {
5847 return endLine
->GetBreakTypeAfter();
5850 return NS_STYLE_CLEAR_NONE
;
5854 nsBlockFrame::ReflowPushedFloats(nsBlockReflowState
& aState
,
5855 nsOverflowAreas
& aOverflowAreas
,
5856 nsReflowStatus
& aStatus
)
5858 // Pushed floats live at the start of our float list; see comment
5859 // above nsBlockFrame::DrainPushedFloats.
5860 for (nsIFrame
* f
= mFloats
.FirstChild(), *next
;
5861 f
&& (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
);
5863 // save next sibling now, since reflowing could push the entire
5864 // float, changing its siblings
5865 next
= f
->GetNextSibling();
5867 // When we push a first-continuation float in a non-initial reflow,
5868 // it's possible that we end up with two continuations with the same
5869 // parent. This happens if, on the previous reflow of the block or
5870 // a previous reflow of the line containing the block, the float was
5871 // split between continuations A and B of the parent, but on the
5872 // current reflow, none of the float can fit in A.
5874 // When this happens, we might even have the two continuations
5875 // out-of-order due to the management of the pushed floats. In
5876 // particular, if the float's placeholder was in a pushed line that
5877 // we reflowed before it was pushed, and we split the float during
5878 // that reflow, we might have the continuation of the float before
5879 // the float itself. (In the general case, however, it's correct
5880 // for floats in the pushed floats list to come before floats
5881 // anchored in pushed lines; however, in this case it's wrong. We
5882 // should probably find a way to fix it somehow, since it leads to
5883 // incorrect layout in some cases.)
5885 // When we have these out-of-order continuations, we might hit the
5886 // next-continuation before the previous-continuation. When that
5887 // happens, just push it. When we reflow the next continuation,
5888 // we'll either pull all of its content back and destroy it (by
5889 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
5890 // pull it out of its current position and push it again (and
5891 // potentially repeat this cycle for the next continuation, although
5892 // hopefully then they'll be in the right order).
5894 // We should also need this code for the in-order case if the first
5895 // continuation of a float gets moved across more than one
5896 // continuation of the containing block. In this case we'd manage
5897 // to push the second continuation without this check, but not the
5899 nsIFrame
*prevContinuation
= f
->GetPrevContinuation();
5900 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
5901 mFloats
.RemoveFrame(f
);
5902 aState
.AppendPushedFloat(f
);
5906 // Always call FlowAndPlaceFloat; we might need to place this float
5907 // if didn't belong to this block the last time it was reflowed.
5908 aState
.FlowAndPlaceFloat(f
);
5910 ConsiderChildOverflow(aOverflowAreas
, f
);
5913 // If there are continued floats, then we may need to continue BR clearance
5914 if (0 != aState
.ClearFloats(0, NS_STYLE_CLEAR_LEFT_AND_RIGHT
)) {
5915 aState
.mFloatBreakType
= static_cast<nsBlockFrame
*>(GetPrevInFlow())
5916 ->FindTrailingClear();
5921 nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
)
5923 // Recover our own floats
5924 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
5925 // belong to our next-in-flow
5926 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
; f
= f
->GetNextSibling()) {
5927 nsRect region
= nsFloatManager::GetRegionFor(f
);
5928 aFloatManager
.AddFloat(f
, region
);
5929 if (!stop
&& f
->GetNextInFlow())
5930 stop
= f
->GetNextInFlow();
5933 // Recurse into our overflow container children
5934 for (nsIFrame
* oc
= GetFirstChild(kOverflowContainersList
);
5935 oc
; oc
= oc
->GetNextSibling()) {
5936 RecoverFloatsFor(oc
, aFloatManager
);
5939 // Recurse into our normal children
5940 for (nsBlockFrame::line_iterator line
= begin_lines(); line
!= end_lines(); ++line
) {
5941 if (line
->IsBlock()) {
5942 RecoverFloatsFor(line
->mFirstChild
, aFloatManager
);
5948 nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
5949 nsFloatManager
& aFloatManager
)
5951 NS_PRECONDITION(aFrame
, "null frame");
5952 // Only blocks have floats
5953 nsBlockFrame
* block
= nsLayoutUtils::GetAsBlock(aFrame
);
5954 // Don't recover any state inside a block that has its own space manager
5955 // (we don't currently have any blocks like this, though, thanks to our
5956 // use of extra frames for 'overflow')
5957 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
5958 // If the element is relatively positioned, then adjust x and y
5959 // accordingly so that we consider relatively positioned frames
5960 // at their original position.
5961 nsPoint pos
= block
->GetNormalPosition();
5962 aFloatManager
.Translate(pos
.x
, pos
.y
);
5963 block
->RecoverFloats(aFloatManager
);
5964 aFloatManager
.Translate(-pos
.x
, -pos
.y
);
5968 //////////////////////////////////////////////////////////////////////
5969 // Painting, event handling
5972 static void ComputeVisualOverflowArea(nsLineList
& aLines
,
5973 nscoord aWidth
, nscoord aHeight
,
5976 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
5977 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
5980 // Compute min and max x/y values for the reflowed frame's
5982 nsRect
visOverflow(line
->GetVisualOverflowArea());
5983 nscoord x
= visOverflow
.x
;
5984 nscoord y
= visOverflow
.y
;
5985 nscoord xmost
= x
+ visOverflow
.width
;
5986 nscoord ymost
= y
+ visOverflow
.height
;
6003 aResult
.width
= xb
- xa
;
6004 aResult
.height
= yb
- ya
;
6009 nsBlockFrame::IsVisibleInSelection(nsISelection
* aSelection
)
6011 if (mContent
->IsHTML() && (mContent
->Tag() == nsGkAtoms::html
||
6012 mContent
->Tag() == nsGkAtoms::body
))
6015 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(mContent
));
6017 nsresult rv
= aSelection
->ContainsNode(node
, true, &visible
);
6018 return NS_SUCCEEDED(rv
) && visible
;
6022 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
6023 if (nsBlockFrame::gNoisyDamageRepair
) {
6024 nsFrame::IndentBy(stdout
, aDepth
+1);
6025 nsRect lineArea
= aLine
->GetVisualOverflowArea();
6026 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6027 aDrawn
? "draw" : "skip",
6028 static_cast<void*>(aLine
),
6029 aLine
->mBounds
.x
, aLine
->mBounds
.y
,
6030 aLine
->mBounds
.width
, aLine
->mBounds
.height
,
6031 lineArea
.x
, lineArea
.y
,
6032 lineArea
.width
, lineArea
.height
);
6038 DisplayLine(nsDisplayListBuilder
* aBuilder
, const nsRect
& aLineArea
,
6039 const nsRect
& aDirtyRect
, nsBlockFrame::line_iterator
& aLine
,
6040 int32_t aDepth
, int32_t& aDrawnLines
, const nsDisplayListSet
& aLists
,
6041 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
) {
6042 // If the line's combined area (which includes child frames that
6043 // stick outside of the line's bounding box or our bounding box)
6044 // intersects the dirty rect then paint the line.
6045 bool intersect
= aLineArea
.Intersects(aDirtyRect
);
6047 if (nsBlockFrame::gLamePaintMetrics
) {
6050 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
6052 // The line might contain a placeholder for a visible out-of-flow, in which
6053 // case we need to descend into it. If there is such a placeholder, we will
6054 // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
6055 // In particular, we really want to check ShouldDescendIntoFrame()
6056 // on all the frames on the line, but that might be expensive. So
6057 // we approximate it by checking it on aFrame; if it's true for any
6058 // frame in the line, it's also true for aFrame.
6059 bool lineInline
= aLine
->IsInline();
6060 bool lineMayHaveTextOverflow
= aTextOverflow
&& lineInline
;
6061 if (!intersect
&& !aBuilder
->ShouldDescendIntoFrame(aFrame
) &&
6062 !lineMayHaveTextOverflow
)
6065 // Collect our line's display items in a temporary nsDisplayListCollection,
6066 // so that we can apply any "text-overflow" clipping to the entire collection
6067 // without affecting previous lines.
6068 nsDisplayListCollection collection
;
6070 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
6071 // Inline-level child backgrounds go on the regular child content list.
6072 nsDisplayListSet
childLists(collection
,
6073 lineInline
? collection
.Content() : collection
.BlockBorderBackgrounds());
6075 uint32_t flags
= lineInline
? nsIFrame::DISPLAY_CHILD_INLINE
: 0;
6077 nsIFrame
* kid
= aLine
->mFirstChild
;
6078 int32_t n
= aLine
->GetChildCount();
6080 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
,
6082 kid
= kid
->GetNextSibling();
6085 if (lineMayHaveTextOverflow
) {
6086 aTextOverflow
->ProcessLine(collection
, aLine
.get());
6089 collection
.MoveTo(aLists
);
6093 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
6094 const nsRect
& aDirtyRect
,
6095 const nsDisplayListSet
& aLists
)
6097 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
6100 if (gNoisyDamageRepair
) {
6103 ::ComputeVisualOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
6104 nsFrame::IndentBy(stdout
, depth
);
6106 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6107 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
,
6108 aDirtyRect
.x
, aDirtyRect
.y
, aDirtyRect
.width
, aDirtyRect
.height
,
6109 ca
.x
, ca
.y
, ca
.width
, ca
.height
);
6111 PRTime start
= 0; // Initialize these variables to silence the compiler.
6112 if (gLamePaintMetrics
) {
6118 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
6120 if (GetPrevInFlow()) {
6121 DisplayOverflowContainers(aBuilder
, aDirtyRect
, aLists
);
6122 for (nsIFrame
* f
= mFloats
.FirstChild(); f
; f
= f
->GetNextSibling()) {
6123 if (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
)
6124 BuildDisplayListForChild(aBuilder
, f
, aDirtyRect
, aLists
);
6128 aBuilder
->MarkFramesForDisplayList(this, mFloats
, aDirtyRect
);
6130 // Prepare for text-overflow processing.
6131 nsAutoPtr
<TextOverflow
> textOverflow(
6132 TextOverflow::WillProcessLines(aBuilder
, this));
6134 // We'll collect our lines' display items here, & then append this to aLists.
6135 nsDisplayListCollection linesDisplayListCollection
;
6137 // Don't use the line cursor if we might have a descendant placeholder ...
6138 // it might skip lines that contain placeholders but don't themselves
6139 // intersect with the dirty area.
6140 // In particular, we really want to check ShouldDescendIntoFrame()
6141 // on all our child frames, but that might be expensive. So we
6142 // approximate it by checking it on |this|; if it's true for any
6143 // frame in our child list, it's also true for |this|.
6144 nsLineBox
* cursor
= aBuilder
->ShouldDescendIntoFrame(this) ?
6145 nullptr : GetFirstLineContaining(aDirtyRect
.y
);
6146 line_iterator line_end
= end_lines();
6149 for (line_iterator line
= mLines
.begin(cursor
);
6152 nsRect lineArea
= line
->GetVisualOverflowArea();
6153 if (!lineArea
.IsEmpty()) {
6154 // Because we have a cursor, the combinedArea.ys are non-decreasing.
6155 // Once we've passed aDirtyRect.YMost(), we can never see it again.
6156 if (lineArea
.y
>= aDirtyRect
.YMost()) {
6159 DisplayLine(aBuilder
, lineArea
, aDirtyRect
, line
, depth
, drawnLines
,
6160 linesDisplayListCollection
, this, textOverflow
);
6164 bool nonDecreasingYs
= true;
6165 int32_t lineCount
= 0;
6166 nscoord lastY
= INT32_MIN
;
6167 nscoord lastYMost
= INT32_MIN
;
6168 for (line_iterator line
= begin_lines();
6171 nsRect lineArea
= line
->GetVisualOverflowArea();
6172 DisplayLine(aBuilder
, lineArea
, aDirtyRect
, line
, depth
, drawnLines
,
6173 linesDisplayListCollection
, this, textOverflow
);
6174 if (!lineArea
.IsEmpty()) {
6175 if (lineArea
.y
< lastY
6176 || lineArea
.YMost() < lastYMost
) {
6177 nonDecreasingYs
= false;
6180 lastYMost
= lineArea
.YMost();
6185 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
6190 // Pick up the resulting text-overflow markers. We append them to
6191 // PositionedDescendants just before we append the lines' display items,
6192 // so that our text-overflow markers will appear on top of this block's
6193 // normal content but below any of its its' positioned children.
6195 aLists
.PositionedDescendants()->AppendToTop(&textOverflow
->GetMarkers());
6197 linesDisplayListCollection
.MoveTo(aLists
);
6199 if (HasOutsideBullet()) {
6200 // Display outside bullets manually
6201 nsIFrame
* bullet
= GetOutsideBullet();
6202 BuildDisplayListForChild(aBuilder
, bullet
, aDirtyRect
, aLists
);
6206 if (gLamePaintMetrics
) {
6207 PRTime end
= PR_Now();
6209 int32_t numLines
= mLines
.size();
6210 if (!numLines
) numLines
= 1;
6211 PRTime lines
, deltaPerLine
, delta
;
6212 lines
= int64_t(numLines
);
6213 delta
= end
- start
;
6214 deltaPerLine
= delta
/ lines
;
6218 PR_snprintf(buf
, sizeof(buf
),
6219 ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
6220 delta
, deltaPerLine
,
6221 numLines
, drawnLines
, numLines
- drawnLines
);
6222 printf("%s\n", buf
);
6227 #ifdef ACCESSIBILITY
6229 nsBlockFrame::AccessibleType()
6231 // block frame may be for <hr>
6232 if (mContent
->Tag() == nsGkAtoms::hr
) {
6233 return a11y::eHTMLHRType
;
6236 if (!HasBullet() || !PresContext()) {
6237 if (!mContent
->GetParent()) {
6238 // Don't create accessible objects for the root content node, they are redundant with
6239 // the nsDocAccessible object created with the document node
6240 return a11y::eNoType
;
6243 nsCOMPtr
<nsIDOMHTMLDocument
> htmlDoc
=
6244 do_QueryInterface(mContent
->GetDocument());
6246 nsCOMPtr
<nsIDOMHTMLElement
> body
;
6247 htmlDoc
->GetBody(getter_AddRefs(body
));
6248 if (SameCOMIdentity(body
, mContent
)) {
6249 // Don't create accessible objects for the body, they are redundant with
6250 // the nsDocAccessible object created with the document node
6251 return a11y::eNoType
;
6255 // Not a bullet, treat as normal HTML container
6256 return a11y::eHyperTextType
;
6259 // Create special list bullet accessible
6260 return a11y::eHTMLLiType
;
6264 void nsBlockFrame::ClearLineCursor()
6266 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
)) {
6270 Properties().Delete(LineCursorProperty());
6271 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
6274 void nsBlockFrame::SetupLineCursor()
6276 if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
6277 || mLines
.empty()) {
6281 Properties().Set(LineCursorProperty(), mLines
.front());
6282 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
6285 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
)
6287 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
)) {
6291 FrameProperties props
= Properties();
6293 nsLineBox
* property
= static_cast<nsLineBox
*>
6294 (props
.Get(LineCursorProperty()));
6295 line_iterator cursor
= mLines
.begin(property
);
6296 nsRect cursorArea
= cursor
->GetVisualOverflowArea();
6298 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
)
6299 && cursor
!= mLines
.front()) {
6300 cursor
= cursor
.prev();
6301 cursorArea
= cursor
->GetVisualOverflowArea();
6303 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
)
6304 && cursor
!= mLines
.back()) {
6305 cursor
= cursor
.next();
6306 cursorArea
= cursor
->GetVisualOverflowArea();
6309 if (cursor
.get() != property
) {
6310 props
.Set(LineCursorProperty(), cursor
.get());
6313 return cursor
.get();
6317 nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
)
6319 // See if the child is absolutely positioned
6320 if (aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
&&
6321 aChild
->IsAbsolutelyPositioned()) {
6323 } else if (aChild
== GetOutsideBullet()) {
6324 // The bullet lives in the first line, unless the first line has
6325 // height 0 and there is a second line, in which case it lives
6326 // in the second line.
6327 line_iterator bulletLine
= begin_lines();
6328 if (bulletLine
!= end_lines() && bulletLine
->mBounds
.height
== 0 &&
6329 bulletLine
!= mLines
.back()) {
6330 bulletLine
= bulletLine
.next();
6333 if (bulletLine
!= end_lines()) {
6334 MarkLineDirty(bulletLine
, &mLines
);
6336 // otherwise we have an empty line list, and ReflowDirtyLines
6337 // will handle reflowing the bullet.
6339 // Note that we should go through our children to mark lines dirty
6340 // before the next reflow. Doing it now could make things O(N^2)
6341 // since finding the right line is O(N).
6342 // We don't need to worry about marking lines on the overflow list
6343 // as dirty; we're guaranteed to reflow them if we take them off the
6345 // However, we might have gotten a float, in which case we need to
6346 // reflow the line containing its placeholder. So find the
6347 // ancestor-or-self of the placeholder that's a child of the block,
6348 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
6349 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6350 // We need to take some care to handle the case where a float is in
6351 // a different continuation than its placeholder, including marking
6352 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6353 if (!(aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
6354 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
6356 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
6357 nsIFrame
*thisFC
= FirstContinuation();
6358 nsIFrame
*placeholderPath
=
6359 PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild
);
6360 // SVG code sometimes sends FrameNeedsReflow notifications during
6361 // frame destruction, leading to null placeholders, but we're safe
6363 if (placeholderPath
) {
6365 nsIFrame
*parent
= placeholderPath
->GetParent();
6366 if (parent
->GetContent() == mContent
&&
6367 parent
->FirstContinuation() == thisFC
) {
6368 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
6371 placeholderPath
= parent
;
6373 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
6378 nsBlockFrameSuper::ChildIsDirty(aChild
);
6382 nsBlockFrame::Init(nsIContent
* aContent
,
6384 nsIFrame
* aPrevInFlow
)
6387 // Copy over the inherited block frame bits from the prev-in-flow.
6388 SetFlags(aPrevInFlow
->GetStateBits() &
6389 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
6392 nsBlockFrameSuper::Init(aContent
, aParent
, aPrevInFlow
);
6395 aPrevInFlow
->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)
6396 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
6398 if ((GetStateBits() &
6399 (NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) ==
6400 (NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) {
6401 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
6406 nsBlockFrame::SetInitialChildList(ChildListID aListID
,
6407 nsFrameList
& aChildList
)
6409 NS_ASSERTION(aListID
!= kPrincipalList
||
6410 (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET
|
6411 NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET
)) == 0,
6412 "how can we have a bullet already?");
6414 if (kAbsoluteList
== aListID
) {
6415 nsContainerFrame::SetInitialChildList(aListID
, aChildList
);
6417 else if (kFloatList
== aListID
) {
6418 mFloats
.SetFrames(aChildList
);
6421 nsPresContext
* presContext
= PresContext();
6424 // The only times a block that is an anonymous box is allowed to have a
6425 // first-letter frame are when it's the block inside a non-anonymous cell,
6426 // the block inside a fieldset, a scrolled content block, or a column
6427 // content block. Note that this means that blocks which are the anonymous
6428 // block in {ib} splits do NOT get first-letter frames. Note that
6429 // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the
6431 nsIAtom
*pseudo
= StyleContext()->GetPseudo();
6432 bool haveFirstLetterStyle
=
6434 (pseudo
== nsCSSAnonBoxes::cellContent
&&
6435 mParent
->StyleContext()->GetPseudo() == nullptr) ||
6436 pseudo
== nsCSSAnonBoxes::fieldsetContent
||
6437 pseudo
== nsCSSAnonBoxes::scrolledContent
||
6438 pseudo
== nsCSSAnonBoxes::columnContent
||
6439 pseudo
== nsCSSAnonBoxes::mozSVGText
) &&
6440 !IsFrameOfType(eMathML
) &&
6441 nsRefPtr
<nsStyleContext
>(GetFirstLetterStyle(presContext
)) != nullptr;
6442 NS_ASSERTION(haveFirstLetterStyle
==
6443 ((mState
& NS_BLOCK_HAS_FIRST_LETTER_STYLE
) != 0),
6444 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
6447 AddFrames(aChildList
, nullptr);
6449 // Create a list bullet if this is a list-item. Note that this is
6450 // done here so that RenumberLists will work (it needs the bullets
6451 // to store the bullet numbers). Also note that due to various
6452 // wrapper frames (scrollframes, columns) we want to use the
6453 // outermost (primary, ideally, but it's not set yet when we get
6454 // here) frame of our content for the display check. On the other
6455 // hand, we look at ourselves for the GetPrevInFlow() check, since
6456 // for a columnset we don't want a bullet per column. Note that
6457 // the outermost frame for the content is the primary frame in
6458 // most cases; the ones when it's not (like tables) can't be
6459 // NS_STYLE_DISPLAY_LIST_ITEM).
6460 nsIFrame
* possibleListItem
= this;
6462 nsIFrame
* parent
= possibleListItem
->GetParent();
6463 if (parent
->GetContent() != GetContent()) {
6466 possibleListItem
= parent
;
6468 if (NS_STYLE_DISPLAY_LIST_ITEM
==
6469 possibleListItem
->StyleDisplay()->mDisplay
&&
6471 // Resolve style for the bullet frame
6472 const nsStyleList
* styleList
= StyleList();
6473 nsCSSPseudoElements::Type pseudoType
;
6474 switch (styleList
->mListStyleType
) {
6475 case NS_STYLE_LIST_STYLE_DISC
:
6476 case NS_STYLE_LIST_STYLE_CIRCLE
:
6477 case NS_STYLE_LIST_STYLE_SQUARE
:
6478 pseudoType
= nsCSSPseudoElements::ePseudo_mozListBullet
;
6481 pseudoType
= nsCSSPseudoElements::ePseudo_mozListNumber
;
6485 nsIPresShell
*shell
= presContext
->PresShell();
6487 nsStyleContext
* parentStyle
=
6488 CorrectStyleParentFrame(this,
6489 nsCSSPseudoElements::GetPseudoAtom(pseudoType
))->StyleContext();
6490 nsRefPtr
<nsStyleContext
> kidSC
= shell
->StyleSet()->
6491 ResolvePseudoElementStyle(mContent
->AsElement(), pseudoType
,
6494 // Create bullet frame
6495 nsBulletFrame
* bullet
= new (shell
) nsBulletFrame(kidSC
);
6496 bullet
->Init(mContent
, this, nullptr);
6498 // If the list bullet frame should be positioned inside then add
6499 // it to the flow now.
6500 if (NS_STYLE_LIST_STYLE_POSITION_INSIDE
==
6501 styleList
->mListStylePosition
) {
6502 nsFrameList
bulletList(bullet
, bullet
);
6503 AddFrames(bulletList
, nullptr);
6504 Properties().Set(InsideBulletProperty(), bullet
);
6505 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET
);
6507 nsFrameList
* bulletList
= new (shell
) nsFrameList(bullet
, bullet
);
6508 Properties().Set(OutsideBulletProperty(), bulletList
);
6509 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET
);
6518 nsBlockFrame::BulletIsEmpty() const
6520 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->mDisplay
==
6521 NS_STYLE_DISPLAY_LIST_ITEM
&& HasOutsideBullet(),
6522 "should only care when we have an outside bullet");
6523 const nsStyleList
* list
= StyleList();
6524 return list
->mListStyleType
== NS_STYLE_LIST_STYLE_NONE
&&
6525 !list
->GetListStyleImage();
6529 nsBlockFrame::GetBulletText(nsAString
& aText
) const
6533 const nsStyleList
* myList
= StyleList();
6534 if (myList
->GetListStyleImage() ||
6535 myList
->mListStyleType
== NS_STYLE_LIST_STYLE_DISC
) {
6536 aText
.Assign(kDiscCharacter
);
6538 else if (myList
->mListStyleType
== NS_STYLE_LIST_STYLE_CIRCLE
) {
6539 aText
.Assign(kCircleCharacter
);
6541 else if (myList
->mListStyleType
== NS_STYLE_LIST_STYLE_SQUARE
) {
6542 aText
.Assign(kSquareCharacter
);
6544 else if (myList
->mListStyleType
!= NS_STYLE_LIST_STYLE_NONE
) {
6545 nsBulletFrame
* bullet
= GetBullet();
6548 bullet
->GetListItemText(*myList
, text
);
6556 nsBlockFrame::FrameStartsCounterScope(nsIFrame
* aFrame
)
6558 nsIContent
* content
= aFrame
->GetContent();
6559 if (!content
|| !content
->IsHTML())
6562 nsIAtom
*localName
= content
->NodeInfo()->NameAtom();
6563 return localName
== nsGkAtoms::ol
||
6564 localName
== nsGkAtoms::ul
||
6565 localName
== nsGkAtoms::dir
||
6566 localName
== nsGkAtoms::menu
;
6570 nsBlockFrame::RenumberLists(nsPresContext
* aPresContext
)
6572 if (!FrameStartsCounterScope(this)) {
6573 // If this frame doesn't start a counter scope then we don't need
6574 // to renumber child list items.
6578 MOZ_ASSERT(mContent
->IsHTML(),
6579 "FrameStartsCounterScope should only return true for HTML elements");
6581 // Setup initial list ordinal value
6582 // XXX Map html's start property to counter-reset style
6583 int32_t ordinal
= 1;
6585 if (mContent
->Tag() == nsGkAtoms::ol
&&
6586 mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::reversed
)) {
6592 nsGenericHTMLElement
*hc
= nsGenericHTMLElement::FromContent(mContent
);
6593 // Must be non-null, since FrameStartsCounterScope only returns true
6594 // for HTML elements.
6595 MOZ_ASSERT(hc
, "How is mContent not HTML?");
6596 const nsAttrValue
* attr
= hc
->GetParsedAttr(nsGkAtoms::start
);
6597 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
6598 ordinal
= attr
->GetIntegerValue();
6599 } else if (increment
< 0) {
6600 // <ol reversed> case, or some other case with a negative increment: count
6601 // up the child list
6603 for (nsIContent
* kid
= mContent
->GetFirstChild(); kid
;
6604 kid
= kid
->GetNextSibling()) {
6605 if (kid
->IsHTML(nsGkAtoms::li
)) {
6606 // FIXME: This isn't right in terms of what CSS says to do for
6607 // overflow of counters (but it only matters when this node has
6608 // more than numeric_limits<int32_t>::max() children).
6609 ordinal
-= increment
;
6614 // Get to first-in-flow
6615 nsBlockFrame
* block
= static_cast<nsBlockFrame
*>(FirstInFlow());
6616 return RenumberListsInBlock(aPresContext
, block
, &ordinal
, 0, increment
);
6620 nsBlockFrame::RenumberListsInBlock(nsPresContext
* aPresContext
,
6621 nsBlockFrame
* aBlockFrame
,
6626 // Examine each line in the block
6627 bool foundValidLine
;
6628 nsBlockInFlowLineIterator
bifLineIter(aBlockFrame
, &foundValidLine
);
6630 if (!foundValidLine
)
6633 bool renumberedABullet
= false;
6636 nsLineList::iterator line
= bifLineIter
.GetLine();
6637 nsIFrame
* kid
= line
->mFirstChild
;
6638 int32_t n
= line
->GetChildCount();
6640 bool kidRenumberedABullet
= RenumberListsFor(aPresContext
, kid
, aOrdinal
,
6641 aDepth
, aIncrement
);
6642 if (kidRenumberedABullet
) {
6644 renumberedABullet
= true;
6646 kid
= kid
->GetNextSibling();
6648 } while (bifLineIter
.Next());
6650 // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
6651 // the bullet and the caller of RenumberLists. But the caller itself
6652 // has to be responsible for setting the bit itself, since that caller
6653 // might be making a FrameNeedsReflow call, which requires that the
6654 // bit not be set yet.
6655 if (renumberedABullet
&& aDepth
!= 0) {
6656 aBlockFrame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
6659 return renumberedABullet
;
6663 nsBlockFrame::RenumberListsFor(nsPresContext
* aPresContext
,
6669 NS_PRECONDITION(aPresContext
&& aKid
&& aOrdinal
, "null params are immoral!");
6671 // add in a sanity check for absurdly deep frame trees. See bug 42138
6672 if (MAX_DEPTH_FOR_LIST_RENUMBERING
< aDepth
)
6675 // if the frame is a placeholder, then get the out of flow frame
6676 nsIFrame
* kid
= nsPlaceholderFrame::GetRealFrameFor(aKid
);
6677 const nsStyleDisplay
* display
= kid
->StyleDisplay();
6679 // drill down through any wrappers to the real frame
6680 kid
= kid
->GetContentInsertionFrame();
6682 // possible there is no content insertion frame
6686 bool kidRenumberedABullet
= false;
6688 // If the frame is a list-item and the frame implements our
6689 // block frame API then get its bullet and set the list item
6691 if (NS_STYLE_DISPLAY_LIST_ITEM
== display
->mDisplay
) {
6692 // Make certain that the frame is a block frame in case
6693 // something foreign has crept in.
6694 nsBlockFrame
* listItem
= nsLayoutUtils::GetAsBlock(kid
);
6696 nsBulletFrame
* bullet
= listItem
->GetBullet();
6699 *aOrdinal
= bullet
->SetListItemOrdinal(*aOrdinal
, &changed
, aIncrement
);
6701 kidRenumberedABullet
= true;
6703 // The ordinal changed - mark the bullet frame, and any
6704 // intermediate frames between it and the block (are there
6705 // ever any?), dirty.
6706 // The calling code will make the necessary FrameNeedsReflow
6707 // call for the list ancestor.
6708 bullet
->AddStateBits(NS_FRAME_IS_DIRTY
);
6709 nsIFrame
*f
= bullet
;
6711 nsIFrame
*parent
= f
->GetParent();
6712 parent
->ChildIsDirty(f
);
6714 } while (f
!= listItem
);
6718 // XXX temporary? if the list-item has child list-items they
6719 // should be numbered too; especially since the list-item is
6720 // itself (ASSUMED!) not to be a counter-resetter.
6721 bool meToo
= RenumberListsInBlock(aPresContext
, listItem
, aOrdinal
,
6722 aDepth
+ 1, aIncrement
);
6724 kidRenumberedABullet
= true;
6728 else if (NS_STYLE_DISPLAY_BLOCK
== display
->mDisplay
) {
6729 if (FrameStartsCounterScope(kid
)) {
6730 // Don't bother recursing into a block frame that is a new
6731 // counter scope. Any list-items in there will be handled by
6735 // If the display=block element is a block frame then go ahead
6736 // and recurse into it, as it might have child list-items.
6737 nsBlockFrame
* kidBlock
= nsLayoutUtils::GetAsBlock(kid
);
6739 kidRenumberedABullet
= RenumberListsInBlock(aPresContext
, kidBlock
,
6740 aOrdinal
, aDepth
+ 1,
6745 return kidRenumberedABullet
;
6749 nsBlockFrame::ReflowBullet(nsIFrame
* aBulletFrame
,
6750 nsBlockReflowState
& aState
,
6751 nsHTMLReflowMetrics
& aMetrics
,
6754 const nsHTMLReflowState
&rs
= aState
.mReflowState
;
6756 // Reflow the bullet now
6758 // Make up a width since it doesn't really matter (XXX).
6759 availSize
.width
= aState
.mContentArea
.width
;
6760 availSize
.height
= NS_UNCONSTRAINEDSIZE
;
6762 // Get the reason right.
6763 // XXXwaterson Should this look just like the logic in
6764 // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
6765 nsHTMLReflowState
reflowState(aState
.mPresContext
, rs
,
6766 aBulletFrame
, availSize
);
6767 nsReflowStatus status
;
6768 aBulletFrame
->WillReflow(aState
.mPresContext
);
6769 aBulletFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowState
, status
);
6771 // Get the float available space using our saved state from before we
6772 // started reflowing the block, so that we ignore any floats inside
6774 // FIXME: aLineTop isn't actually set correctly by some callers, since
6775 // they reposition the line.
6776 nsRect floatAvailSpace
=
6777 aState
.GetFloatAvailableSpaceWithState(aLineTop
,
6778 &aState
.mFloatManagerStateBefore
)
6780 // FIXME (bug 25888): need to check the entire region that the first
6781 // line overlaps, not just the top pixel.
6783 // Place the bullet now. We want to place the bullet relative to the
6784 // border-box of the associated block (using the right/left margin of
6785 // the bullet frame as separation). However, if a line box would be
6786 // displaced by floats that are *outside* the associated block, we
6787 // want to displace it by the same amount. That is, we act as though
6788 // the edge of the floats is the content-edge of the block, and place
6789 // the bullet at a position offset from there by the block's padding,
6790 // the block's border, and the bullet frame's margin.
6792 if (rs
.mStyleVisibility
->mDirection
== NS_STYLE_DIRECTION_LTR
) {
6793 // The floatAvailSpace.x gives us the content/float edge. Then we
6794 // subtract out the left border/padding and the bullet's width and
6795 // margin to offset the position.
6796 x
= floatAvailSpace
.x
- rs
.mComputedBorderPadding
.left
6797 - reflowState
.mComputedMargin
.right
- aMetrics
.width
;
6799 // The XMost() of the available space give us offsets from the left
6800 // border edge. Then we add the right border/padding and the
6801 // bullet's margin to offset the position.
6802 x
= floatAvailSpace
.XMost() + rs
.mComputedBorderPadding
.right
6803 + reflowState
.mComputedMargin
.left
;
6806 // Approximate the bullets position; vertical alignment will provide
6807 // the final vertical location.
6808 nscoord y
= aState
.mContentArea
.y
;
6809 aBulletFrame
->SetRect(nsRect(x
, y
, aMetrics
.width
, aMetrics
.height
));
6810 aBulletFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowState
,
6811 nsDidReflowStatus::FINISHED
);
6814 // This is used to scan frames for any float placeholders, add their
6815 // floats to the list represented by aList, and remove the
6816 // floats from whatever list they might be in. We don't search descendants
6817 // that are float containing blocks. Floats that or not children of 'this'
6818 // are ignored (they are not added to aList).
6820 nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
6821 bool aCollectSiblings
)
6824 // Don't descend into float containing blocks.
6825 if (!aFrame
->IsFloatContainingBlock()) {
6826 nsIFrame
*outOfFlowFrame
=
6827 aFrame
->GetType() == nsGkAtoms::placeholderFrame
?
6828 nsLayoutUtils::GetFloatFromPlaceholder(aFrame
) : nullptr;
6829 if (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
6830 RemoveFloat(outOfFlowFrame
);
6831 aList
.AppendFrame(nullptr, outOfFlowFrame
);
6832 // FIXME: By not pulling floats whose parent is one of our
6833 // later siblings, are we risking the pushed floats getting
6835 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
6838 DoCollectFloats(aFrame
->GetFirstPrincipalChild(), aList
, true);
6839 DoCollectFloats(aFrame
->GetFirstChild(kOverflowList
), aList
, true);
6841 if (!aCollectSiblings
)
6843 aFrame
= aFrame
->GetNextSibling();
6848 nsBlockFrame::CheckFloats(nsBlockReflowState
& aState
)
6851 // If any line is still dirty, that must mean we're going to reflow this
6852 // block again soon (e.g. because we bailed out after noticing that
6853 // clearance was imposed), so don't worry if the floats are out of sync.
6854 bool anyLineDirty
= false;
6856 // Check that the float list is what we would have built
6857 nsAutoTArray
<nsIFrame
*, 8> lineFloats
;
6858 for (line_iterator line
= begin_lines(), line_end
= end_lines();
6859 line
!= line_end
; ++line
) {
6860 if (line
->HasFloats()) {
6861 nsFloatCache
* fc
= line
->GetFirstFloat();
6863 lineFloats
.AppendElement(fc
->mFloat
);
6867 if (line
->IsDirty()) {
6868 anyLineDirty
= true;
6872 nsAutoTArray
<nsIFrame
*, 8> storedFloats
;
6875 for (nsIFrame
* f
= mFloats
.FirstChild(); f
; f
= f
->GetNextSibling()) {
6876 if (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
)
6878 storedFloats
.AppendElement(f
);
6879 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
6885 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) && !anyLineDirty
) {
6886 NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
6887 #if defined(DEBUG_roc)
6888 nsFrame::RootFrameList(PresContext(), stdout
, 0);
6889 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
6890 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
6892 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
6893 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
6899 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
6900 if (oofs
&& oofs
->NotEmpty()) {
6901 // Floats that were pushed should be removed from our float
6902 // manager. Otherwise the float manager's YMost or XMost might
6903 // be larger than necessary, causing this block to get an
6904 // incorrect desired height (or width). Some of these floats
6905 // may not actually have been added to the float manager because
6906 // they weren't reflowed before being pushed; that's OK,
6907 // RemoveRegions will ignore them. It is safe to do this here
6908 // because we know from here on the float manager will only be
6909 // used for its XMost and YMost, not to place new floats and
6911 aState
.mFloatManager
->RemoveTrailingRegions(oofs
->FirstChild());
6916 nsBlockFrame::IsMarginRoot(bool* aTopMarginRoot
, bool* aBottomMarginRoot
)
6918 if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT
)) {
6919 nsIFrame
* parent
= GetParent();
6920 if (!parent
|| parent
->IsFloatContainingBlock()) {
6921 *aTopMarginRoot
= false;
6922 *aBottomMarginRoot
= false;
6925 if (parent
->GetType() == nsGkAtoms::columnSetFrame
) {
6926 *aTopMarginRoot
= GetPrevInFlow() == nullptr;
6927 *aBottomMarginRoot
= GetNextInFlow() == nullptr;
6932 *aTopMarginRoot
= true;
6933 *aBottomMarginRoot
= true;
6938 nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
)
6940 NS_PRECONDITION(aBlock
, "Must have a frame");
6941 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock
), "aBlock must be a block");
6943 nsIFrame
* parent
= aBlock
->GetParent();
6944 return (aBlock
->GetStateBits() & NS_BLOCK_FLOAT_MGR
) ||
6945 (parent
&& !parent
->IsFloatContainingBlock());
6950 nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
)
6952 return aFrame
->IsFrameOfType(nsIFrame::eBlockFrame
) &&
6953 !aFrame
->IsFrameOfType(nsIFrame::eReplaced
) &&
6954 !(aFrame
->GetStateBits() & NS_BLOCK_FLOAT_MGR
);
6957 // Note that this width can vary based on the vertical position.
6958 // However, the cases where it varies are the cases where the width fits
6959 // in the available space given, which means that variation shouldn't
6962 nsBlockFrame::ReplacedElementWidthToClear
6963 nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState
& aState
,
6964 const nsRect
& aFloatAvailableSpace
,
6967 nscoord leftOffset
, rightOffset
;
6968 nsCSSOffsetState
offsetState(aFrame
, aState
.mReflowState
.rendContext
,
6969 aState
.mContentArea
.width
);
6971 ReplacedElementWidthToClear result
;
6972 aState
.ComputeReplacedBlockOffsetsForFloats(aFrame
, aFloatAvailableSpace
,
6973 leftOffset
, rightOffset
);
6974 nscoord availWidth
= aState
.mContentArea
.width
- leftOffset
- rightOffset
;
6976 // We actually don't want the min width here; see bug 427782; we only
6977 // want to displace if the width won't compute to a value small enough
6979 // All we really need here is the result of ComputeSize, and we
6980 // could *almost* get that from an nsCSSOffsetState, except for the
6982 nsSize
availSpace(availWidth
, NS_UNCONSTRAINEDSIZE
);
6983 nsHTMLReflowState
reflowState(aState
.mPresContext
, aState
.mReflowState
,
6984 aFrame
, availSpace
);
6985 result
.borderBoxWidth
= reflowState
.ComputedWidth() +
6986 reflowState
.mComputedBorderPadding
.LeftRight();
6987 // Use the margins from offsetState rather than reflowState so that
6988 // they aren't reduced by ignoring margins in overconstrained cases.
6989 result
.marginLeft
= offsetState
.mComputedMargin
.left
;
6990 result
.marginRight
= offsetState
.mComputedMargin
.right
;
6996 nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
)
6998 nsBlockFrame
* block
= nullptr;
7000 block
= nsLayoutUtils::GetAsBlock(aCandidate
);
7002 // yay, candidate is a block!
7005 // Not a block. Check its parent next.
7006 aCandidate
= aCandidate
->GetParent();
7008 NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
7013 nsBlockFrame::ComputeFinalHeight(const nsHTMLReflowState
& aReflowState
,
7014 nsReflowStatus
* aStatus
,
7015 nscoord aContentHeight
,
7016 const nsMargin
& aBorderPadding
,
7017 nsHTMLReflowMetrics
& aMetrics
,
7021 // Figure out how much of the computed height should be
7022 // applied to this frame.
7023 nscoord computedHeightLeftOver
= GetEffectiveComputedHeight(aReflowState
,
7025 NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
7026 && computedHeightLeftOver
),
7027 "overflow container must not have computedHeightLeftOver");
7030 NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding
.top
,
7031 computedHeightLeftOver
),
7032 aBorderPadding
.bottom
);
7034 if (NS_FRAME_IS_NOT_COMPLETE(*aStatus
)
7035 && aMetrics
.height
< aReflowState
.availableHeight
) {
7036 // We ran out of height on this page but we're incomplete
7037 // Set status to complete except for overflow
7038 NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus
);
7041 if (NS_FRAME_IS_COMPLETE(*aStatus
)) {
7042 if (computedHeightLeftOver
> 0 &&
7043 NS_UNCONSTRAINEDSIZE
!= aReflowState
.availableHeight
&&
7044 aMetrics
.height
> aReflowState
.availableHeight
) {
7045 if (ShouldAvoidBreakInside(aReflowState
)) {
7046 *aStatus
= NS_INLINE_LINE_BREAK_BEFORE();
7049 // We don't fit and we consumed some of the computed height,
7050 // so we should consume all the available height and then
7051 // break. If our bottom border/padding straddles the break
7052 // point, then this will increase our height and push the
7053 // border/padding to the next page/column.
7054 aMetrics
.height
= std::max(aReflowState
.availableHeight
,
7056 NS_FRAME_SET_INCOMPLETE(*aStatus
);
7057 if (!GetNextInFlow())
7058 *aStatus
|= NS_FRAME_REFLOW_NEXTINFLOW
;
7065 nsBlockFrame::ResolveBidi()
7067 NS_ASSERTION(!GetPrevInFlow(),
7068 "ResolveBidi called on non-first continuation");
7070 nsPresContext
* presContext
= PresContext();
7071 if (!presContext
->BidiEnabled()) {
7075 return nsBidiPresUtils::Resolve(this);
7081 nsBlockFrame::VerifyLines(bool aFinalCheckOK
)
7083 if (!gVerifyLines
) {
7086 if (mLines
.empty()) {
7090 nsLineBox
* cursor
= GetLineCursor();
7092 // Add up the counts on each line. Also validate that IsFirstLine is
7095 line_iterator line
, line_end
;
7096 for (line
= begin_lines(), line_end
= end_lines();
7099 if (line
== cursor
) {
7102 if (aFinalCheckOK
) {
7103 NS_ABORT_IF_FALSE(line
->GetChildCount(), "empty line");
7104 if (line
->IsBlock()) {
7105 NS_ASSERTION(1 == line
->GetChildCount(), "bad first line");
7108 count
+= line
->GetChildCount();
7111 // Then count the frames
7112 int32_t frameCount
= 0;
7113 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
7116 frame
= frame
->GetNextSibling();
7118 NS_ASSERTION(count
== frameCount
, "bad line list");
7120 // Next: test that each line has right number of frames on it
7121 for (line
= begin_lines(), line_end
= end_lines();
7124 count
= line
->GetChildCount();
7125 frame
= line
->mFirstChild
;
7126 while (--count
>= 0) {
7127 frame
= frame
->GetNextSibling();
7130 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
7131 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
7136 FrameLines
* overflowLines
= GetOverflowLines();
7137 if (overflowLines
) {
7138 line_iterator line
= overflowLines
->mLines
.begin();
7139 line_iterator line_end
= overflowLines
->mLines
.end();
7140 for (; line
!= line_end
; ++line
) {
7141 if (line
== cursor
) {
7148 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
7152 nsBlockFrame::VerifyOverflowSituation()
7154 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
7156 FrameLines
* overflowLines
= flow
->GetOverflowLines();
7157 if (overflowLines
) {
7158 NS_ASSERTION(!overflowLines
->mLines
.empty(),
7159 "should not be empty if present");
7160 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
7161 "bad overflow lines");
7162 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
7163 overflowLines
->mFrames
.FirstChild(),
7164 "bad overflow frames / lines");
7166 nsLineBox
* cursor
= flow
->GetLineCursor();
7168 line_iterator line
= flow
->begin_lines();
7169 line_iterator line_end
= flow
->end_lines();
7170 for (; line
!= line_end
&& line
!= cursor
; ++line
)
7172 if (line
== line_end
&& overflowLines
) {
7173 line
= overflowLines
->mLines
.begin();
7174 line_end
= overflowLines
->mLines
.end();
7175 for (; line
!= line_end
&& line
!= cursor
; ++line
)
7178 MOZ_ASSERT(line
!= line_end
, "stale LineCursorProperty");
7180 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
7185 nsBlockFrame::GetDepth() const
7188 nsIFrame
* parent
= mParent
;
7190 parent
= parent
->GetParent();