1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "gfxContext.h"
16 #include "mozilla/ComputedStyle.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/ToString.h"
21 #include "mozilla/UniquePtr.h"
24 #include "nsAbsoluteContainingBlock.h"
25 #include "nsBlockReflowContext.h"
26 #include "BlockReflowInput.h"
27 #include "nsBulletFrame.h"
28 #include "nsFontMetrics.h"
29 #include "nsGenericHTMLElement.h"
30 #include "nsLineBox.h"
31 #include "nsLineLayout.h"
32 #include "nsPlaceholderFrame.h"
33 #include "nsStyleConsts.h"
34 #include "nsFrameManager.h"
35 #include "nsPresContext.h"
36 #include "nsPresContextInlines.h"
37 #include "nsHTMLParts.h"
38 #include "nsGkAtoms.h"
39 #include "nsAttrValueInlines.h"
40 #include "mozilla/Sprintf.h"
41 #include "nsFloatManager.h"
45 #include "nsIScrollableFrame.h"
47 #include "nsLayoutUtils.h"
48 #include "nsDisplayList.h"
49 #include "nsCSSAnonBoxes.h"
50 #include "nsCSSFrameConstructor.h"
51 #include "TextOverflow.h"
52 #include "nsIFrameInlines.h"
53 #include "CounterStyleManager.h"
54 #include "mozilla/dom/HTMLDetailsElement.h"
55 #include "mozilla/dom/HTMLSummaryElement.h"
56 #include "mozilla/dom/Selection.h"
57 #include "mozilla/PresShell.h"
58 #include "mozilla/RestyleManager.h"
59 #include "mozilla/ServoStyleSet.h"
60 #include "mozilla/Telemetry.h"
61 #include "nsFlexContainerFrame.h"
63 #include "nsBidiPresUtils.h"
67 static const int MIN_LINES_NEEDING_CURSOR
= 20;
69 static const char16_t kDiscCharacter
= 0x2022;
71 using namespace mozilla
;
72 using namespace mozilla::css
;
73 using namespace mozilla::dom
;
74 using namespace mozilla::layout
;
75 using ShapeType
= nsFloatManager::ShapeType
;
76 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags
;
78 static void MarkAllDescendantLinesDirty(nsBlockFrame
* aBlock
) {
79 nsLineList::iterator line
= aBlock
->LinesBegin();
80 nsLineList::iterator endLine
= aBlock
->LinesEnd();
81 while (line
!= endLine
) {
82 if (line
->IsBlock()) {
83 nsIFrame
* f
= line
->mFirstChild
;
84 nsBlockFrame
* bf
= do_QueryFrame(f
);
86 MarkAllDescendantLinesDirty(bf
);
94 static void MarkSameFloatManagerLinesDirty(nsBlockFrame
* aBlock
) {
95 nsBlockFrame
* blockWithFloatMgr
= aBlock
;
96 while (!(blockWithFloatMgr
->GetStateBits() & NS_BLOCK_FLOAT_MGR
)) {
97 nsBlockFrame
* bf
= do_QueryFrame(blockWithFloatMgr
->GetParent());
101 blockWithFloatMgr
= bf
;
104 // Mark every line at and below the line where the float was
105 // dirty, and mark their lines dirty too. We could probably do
106 // something more efficient --- e.g., just dirty the lines that intersect
107 // the float vertically.
108 MarkAllDescendantLinesDirty(blockWithFloatMgr
);
112 * Returns true if aFrame is a block that has one or more float children.
114 static bool BlockHasAnyFloats(nsIFrame
* aFrame
) {
115 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
116 if (!block
) return false;
117 if (block
->GetChildList(nsIFrame::kFloatList
).FirstChild()) return true;
119 nsLineList::iterator line
= block
->LinesBegin();
120 nsLineList::iterator endLine
= block
->LinesEnd();
121 while (line
!= endLine
) {
122 if (line
->IsBlock() && BlockHasAnyFloats(line
->mFirstChild
)) return true;
129 # include "nsBlockDebugFlags.h"
131 bool nsBlockFrame::gLamePaintMetrics
;
132 bool nsBlockFrame::gLameReflowMetrics
;
133 bool nsBlockFrame::gNoisy
;
134 bool nsBlockFrame::gNoisyDamageRepair
;
135 bool nsBlockFrame::gNoisyIntrinsic
;
136 bool nsBlockFrame::gNoisyReflow
;
137 bool nsBlockFrame::gReallyNoisyReflow
;
138 bool nsBlockFrame::gNoisyFloatManager
;
139 bool nsBlockFrame::gVerifyLines
;
140 bool nsBlockFrame::gDisableResizeOpt
;
142 int32_t nsBlockFrame::gNoiseIndent
;
144 struct BlockDebugFlags
{
149 static const BlockDebugFlags gFlags
[] = {
150 {"reflow", &nsBlockFrame::gNoisyReflow
},
151 {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow
},
152 {"intrinsic", &nsBlockFrame::gNoisyIntrinsic
},
153 {"float-manager", &nsBlockFrame::gNoisyFloatManager
},
154 {"verify-lines", &nsBlockFrame::gVerifyLines
},
155 {"damage-repair", &nsBlockFrame::gNoisyDamageRepair
},
156 {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics
},
157 {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics
},
158 {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt
},
160 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
162 static void ShowDebugFlags() {
163 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
164 const BlockDebugFlags
* bdf
= gFlags
;
165 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
166 for (; bdf
< end
; bdf
++) {
167 printf(" %s\n", bdf
->name
);
169 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
170 printf("names (no whitespace)\n");
173 void nsBlockFrame::InitDebugFlags() {
174 static bool firstTime
= true;
177 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
181 char* cm
= PL_strchr(flags
, ',');
185 const BlockDebugFlags
* bdf
= gFlags
;
186 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
187 for (; bdf
< end
; bdf
++) {
188 if (PL_strcasecmp(bdf
->name
, flags
) == 0) {
190 printf("nsBlockFrame: setting %s debug flag on\n", bdf
->name
);
213 //----------------------------------------------------------------------
215 // Debugging support code
218 const char* nsBlockFrame::kReflowCommandType
[] = {
219 "ContentChanged", "StyleChanged", "ReflowDirty", "Timeout", "UserDefined",
222 const char* nsBlockFrame::LineReflowStatusToString(
223 LineReflowStatus aLineReflowStatus
) const {
224 switch (aLineReflowStatus
) {
225 case LineReflowStatus::OK
:
226 return "LINE_REFLOW_OK";
227 case LineReflowStatus::Stop
:
228 return "LINE_REFLOW_STOP";
229 case LineReflowStatus::RedoNoPull
:
230 return "LINE_REFLOW_REDO_NO_PULL";
231 case LineReflowStatus::RedoMoreFloats
:
232 return "LINE_REFLOW_REDO_MORE_FLOATS";
233 case LineReflowStatus::RedoNextBand
:
234 return "LINE_REFLOW_REDO_NEXT_BAND";
235 case LineReflowStatus::Truncated
:
236 return "LINE_REFLOW_TRUNCATED";
243 #ifdef REFLOW_STATUS_COVERAGE
244 static void RecordReflowStatus(bool aChildIsBlock
,
245 const nsReflowStatus
& aFrameReflowStatus
) {
246 static uint32_t record
[2];
249 // 1: child-is-inline
251 if (!aChildIsBlock
) index
|= 1;
253 // Compute new status
254 uint32_t newS
= record
[index
];
255 if (aFrameReflowStatus
.IsInlineBreak()) {
256 if (aFrameReflowStatus
.IsInlineBreakBefore()) {
258 } else if (aFrameReflowStatus
.IsIncomplete()) {
263 } else if (aFrameReflowStatus
.IsIncomplete()) {
269 // Log updates to the status that yield different values
270 if (record
[index
] != newS
) {
271 record
[index
] = newS
;
272 printf("record(%d): %02x %02x\n", index
, record
[0], record
[1]);
277 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty
,
278 nsBlockFrame::FrameLines
)
279 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty
)
280 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty
)
281 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty
)
282 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty
, nsIFrame
)
283 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty
, nscoord
)
285 //----------------------------------------------------------------------
287 nsBlockFrame
* NS_NewBlockFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
288 return new (aPresShell
) nsBlockFrame(aStyle
, aPresShell
->GetPresContext());
291 nsBlockFrame
* NS_NewBlockFormattingContext(PresShell
* aPresShell
,
292 ComputedStyle
* aComputedStyle
) {
293 nsBlockFrame
* blockFrame
= NS_NewBlockFrame(aPresShell
, aComputedStyle
);
294 blockFrame
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
298 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame
)
300 nsBlockFrame::~nsBlockFrame() {}
302 void nsBlockFrame::AddSizeOfExcludingThisForTree(
303 nsWindowSizes
& aWindowSizes
) const {
304 nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes
);
306 // Add the size of any nsLineBox::mFrames hashtables we might have:
307 for (ConstLineIterator line
= LinesBegin(), line_end
= LinesEnd();
308 line
!= line_end
; ++line
) {
309 line
->AddSizeOfExcludingThis(aWindowSizes
);
311 const FrameLines
* overflowLines
= GetOverflowLines();
313 ConstLineIterator line
= overflowLines
->mLines
.begin(),
314 line_end
= overflowLines
->mLines
.end();
315 for (; line
!= line_end
; ++line
) {
316 line
->AddSizeOfExcludingThis(aWindowSizes
);
321 void nsBlockFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
322 PostDestroyData
& aPostDestroyData
) {
324 DestroyAbsoluteFrames(aDestructRoot
, aPostDestroyData
);
325 mFloats
.DestroyFramesFrom(aDestructRoot
, aPostDestroyData
);
326 nsPresContext
* presContext
= PresContext();
327 mozilla::PresShell
* presShell
= presContext
->PresShell();
328 nsLineBox::DeleteLineList(presContext
, mLines
, aDestructRoot
, &mFrames
,
331 if (HasPushedFloats()) {
332 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
333 PushedFloatProperty());
334 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
337 // destroy overflow lines now
338 FrameLines
* overflowLines
= RemoveOverflowLines();
340 nsLineBox::DeleteLineList(presContext
, overflowLines
->mLines
, aDestructRoot
,
341 &overflowLines
->mFrames
, aPostDestroyData
);
342 delete overflowLines
;
345 if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) {
346 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
347 OverflowOutOfFlowsProperty());
348 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
351 if (HasOutsideMarker()) {
352 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
353 OutsideMarkerProperty());
354 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
357 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
361 nsILineIterator
* nsBlockFrame::GetLineIterator() {
362 nsLineIterator
* it
= new nsLineIterator
;
363 if (!it
) return nullptr;
365 const nsStyleVisibility
* visibility
= StyleVisibility();
367 it
->Init(mLines
, visibility
->mDirection
== NS_STYLE_DIRECTION_RTL
);
375 NS_QUERYFRAME_HEAD(nsBlockFrame
)
376 NS_QUERYFRAME_ENTRY(nsBlockFrame
)
377 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
379 #ifdef DEBUG_FRAME_DUMP
380 void nsBlockFrame::List(FILE* out
, const char* aPrefix
, uint32_t aFlags
) const {
382 ListGeneric(str
, aPrefix
, aFlags
);
384 fprintf_stderr(out
, "%s<\n", str
.get());
386 nsCString
pfx(aPrefix
);
390 if (!mLines
.empty()) {
391 ConstLineIterator line
= LinesBegin(), line_end
= LinesEnd();
392 for (; line
!= line_end
; ++line
) {
393 line
->List(out
, pfx
.get(), aFlags
);
397 // Output the overflow lines.
398 const FrameLines
* overflowLines
= GetOverflowLines();
399 if (overflowLines
&& !overflowLines
->mLines
.empty()) {
400 fprintf_stderr(out
, "%sOverflow-lines %p/%p <\n", pfx
.get(), overflowLines
,
401 &overflowLines
->mFrames
);
402 nsCString
nestedPfx(pfx
);
404 ConstLineIterator line
= overflowLines
->mLines
.begin(),
405 line_end
= overflowLines
->mLines
.end();
406 for (; line
!= line_end
; ++line
) {
407 line
->List(out
, nestedPfx
.get(), aFlags
);
409 fprintf_stderr(out
, "%s>\n", pfx
.get());
412 // skip the principal list - we printed the lines above
413 // skip the overflow list - we printed the overflow lines above
414 ChildListIterator
lists(this);
415 ChildListIDs skip
= {kPrincipalList
, kOverflowList
};
416 for (; !lists
.IsDone(); lists
.Next()) {
417 if (skip
.contains(lists
.CurrentID())) {
420 fprintf_stderr(out
, "%s%s %p <\n", pfx
.get(),
421 mozilla::layout::ChildListName(lists
.CurrentID()),
422 &GetChildList(lists
.CurrentID()));
423 nsCString
nestedPfx(pfx
);
425 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
426 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
427 nsIFrame
* kid
= childFrames
.get();
428 kid
->List(out
, nestedPfx
.get(), aFlags
);
430 fprintf_stderr(out
, "%s>\n", pfx
.get());
433 fprintf_stderr(out
, "%s>\n", aPrefix
);
436 nsresult
nsBlockFrame::GetFrameName(nsAString
& aResult
) const {
437 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult
);
441 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
442 bool aRebuildDisplayItems
) {
443 if (nsSVGUtils::IsInSVGTextSubtree(this)) {
444 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
445 "unexpected block frame in SVG text");
446 GetParent()->InvalidateFrame();
449 nsContainerFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
452 void nsBlockFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
453 uint32_t aDisplayItemKey
,
454 bool aRebuildDisplayItems
) {
455 if (nsSVGUtils::IsInSVGTextSubtree(this)) {
456 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
457 "unexpected block frame in SVG text");
458 GetParent()->InvalidateFrame();
461 nsContainerFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
462 aRebuildDisplayItems
);
465 nscoord
nsBlockFrame::GetLogicalBaseline(WritingMode aWM
) const {
466 auto lastBaseline
= BaselineBOffset(aWM
, BaselineSharingGroup::Last
,
467 AlignmentContext::Inline
);
468 return BSize(aWM
) - lastBaseline
;
471 bool nsBlockFrame::GetNaturalBaselineBOffset(
472 mozilla::WritingMode aWM
, BaselineSharingGroup aBaselineGroup
,
473 nscoord
* aBaseline
) const {
474 if (aBaselineGroup
== BaselineSharingGroup::First
) {
475 return nsLayoutUtils::GetFirstLineBaseline(aWM
, this, aBaseline
);
478 if (StyleDisplay()->IsContainLayout()) {
482 for (ConstReverseLineIterator line
= LinesRBegin(), line_end
= LinesREnd();
483 line
!= line_end
; ++line
) {
484 if (line
->IsBlock()) {
486 nsIFrame
* kid
= line
->mFirstChild
;
487 if (!aWM
.IsOrthogonalTo(kid
->GetWritingMode()) &&
488 kid
->GetVerticalAlignBaseline(aWM
, &offset
)) {
489 // Ignore relative positioning for baseline calculations.
490 const nsSize
& sz
= line
->mContainerSize
;
491 offset
+= kid
->GetLogicalNormalPosition(aWM
, sz
).B(aWM
);
492 *aBaseline
= BSize(aWM
) - offset
;
496 // XXX Is this the right test? We have some bogus empty lines
497 // floating around, but IsEmpty is perhaps too weak.
498 if (line
->BSize() != 0 || !line
->IsEmpty()) {
499 *aBaseline
= BSize(aWM
) - (line
->BStart() + line
->GetLogicalAscent());
507 nscoord
nsBlockFrame::GetCaretBaseline() const {
508 nsRect contentRect
= GetContentRect();
509 nsMargin bp
= GetUsedBorderAndPadding();
511 if (!mLines
.empty()) {
512 ConstLineIterator line
= LinesBegin();
513 const nsLineBox
* firstLine
= line
;
514 if (firstLine
->GetChildCount()) {
515 return bp
.top
+ firstLine
->mFirstChild
->GetCaretBaseline();
518 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
519 RefPtr
<nsFontMetrics
> fm
=
520 nsLayoutUtils::GetFontMetricsForFrame(this, inflation
);
521 nscoord lineHeight
= ReflowInput::CalcLineHeight(
522 GetContent(), Style(), PresContext(), contentRect
.height
, inflation
);
523 const WritingMode wm
= GetWritingMode();
524 return nsLayoutUtils::GetCenteredFontBaseline(fm
, lineHeight
,
525 wm
.IsLineInverted()) +
529 /////////////////////////////////////////////////////////////////////////////
530 // Child frame enumeration
532 const nsFrameList
& nsBlockFrame::GetChildList(ChildListID aListID
) const {
536 case kOverflowList
: {
537 FrameLines
* overflowLines
= GetOverflowLines();
538 return overflowLines
? overflowLines
->mFrames
: nsFrameList::EmptyList();
542 case kOverflowOutOfFlowList
: {
543 const nsFrameList
* list
= GetOverflowOutOfFlows();
544 return list
? *list
: nsFrameList::EmptyList();
546 case kPushedFloatsList
: {
547 const nsFrameList
* list
= GetPushedFloats();
548 return list
? *list
: nsFrameList::EmptyList();
551 const nsFrameList
* list
= GetOutsideMarkerList();
552 return list
? *list
: nsFrameList::EmptyList();
555 return nsContainerFrame::GetChildList(aListID
);
559 void nsBlockFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const {
560 nsContainerFrame::GetChildLists(aLists
);
561 FrameLines
* overflowLines
= GetOverflowLines();
563 overflowLines
->mFrames
.AppendIfNonempty(aLists
, kOverflowList
);
565 const nsFrameList
* list
= GetOverflowOutOfFlows();
567 list
->AppendIfNonempty(aLists
, kOverflowOutOfFlowList
);
569 mFloats
.AppendIfNonempty(aLists
, kFloatList
);
570 list
= GetOutsideMarkerList();
572 list
->AppendIfNonempty(aLists
, kBulletList
);
574 list
= GetPushedFloats();
576 list
->AppendIfNonempty(aLists
, kPushedFloatsList
);
581 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
583 static void ReparentFrame(nsIFrame
* aFrame
, nsContainerFrame
* aOldParent
,
584 nsContainerFrame
* aNewParent
) {
585 NS_ASSERTION(aOldParent
== aFrame
->GetParent(),
586 "Parent not consistent with expectations");
588 aFrame
->SetParent(aNewParent
);
590 // When pushing and pulling frames we need to check for whether any
591 // views need to be reparented
592 nsContainerFrame::ReparentFrameView(aFrame
, aOldParent
, aNewParent
);
595 static void ReparentFrames(nsFrameList
& aFrameList
,
596 nsContainerFrame
* aOldParent
,
597 nsContainerFrame
* aNewParent
) {
598 for (nsFrameList::Enumerator
e(aFrameList
); !e
.AtEnd(); e
.Next()) {
599 ReparentFrame(e
.get(), aOldParent
, aNewParent
);
604 * Remove the first line from aFromLines and adjust the associated frame list
605 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
606 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
607 * that were extracted from the head of aFromFrames.
608 * aFromLines must contain at least one line, the line may be empty.
609 * @return true if aFromLines becomes empty
611 static bool RemoveFirstLine(nsLineList
& aFromLines
, nsFrameList
& aFromFrames
,
612 nsLineBox
** aOutLine
, nsFrameList
* aOutFrames
) {
613 nsLineList_iterator removedLine
= aFromLines
.begin();
614 *aOutLine
= removedLine
;
615 nsLineList_iterator next
= aFromLines
.erase(removedLine
);
616 bool isLastLine
= next
== aFromLines
.end();
617 nsIFrame
* lastFrame
= isLastLine
? aFromFrames
.LastChild()
618 : next
->mFirstChild
->GetPrevSibling();
619 nsFrameList::FrameLinkEnumerator
linkToBreak(aFromFrames
, lastFrame
);
620 *aOutFrames
= aFromFrames
.ExtractHead(linkToBreak
);
624 //////////////////////////////////////////////////////////////////////
628 void nsBlockFrame::MarkIntrinsicISizesDirty() {
629 nsBlockFrame
* dirtyBlock
= static_cast<nsBlockFrame
*>(FirstContinuation());
630 dirtyBlock
->mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
631 dirtyBlock
->mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
632 if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
633 for (nsIFrame
* frame
= dirtyBlock
; frame
;
634 frame
= frame
->GetNextContinuation()) {
635 frame
->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
639 nsContainerFrame::MarkIntrinsicISizesDirty();
642 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
643 nsPresContext
* presContext
= PresContext();
644 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext
)) {
647 bool inflationEnabled
= !presContext
->mInflationDisabledForShrinkWrap
;
648 if (inflationEnabled
!=
649 !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED
)) {
650 mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
651 mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
652 if (inflationEnabled
) {
653 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
655 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
661 nscoord
nsBlockFrame::GetMinISize(gfxContext
* aRenderingContext
) {
662 nsIFrame
* firstInFlow
= FirstContinuation();
663 if (firstInFlow
!= this) return firstInFlow
->GetMinISize(aRenderingContext
);
665 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
667 CheckIntrinsicCacheAgainstShrinkWrapState();
669 if (mCachedMinISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
670 return mCachedMinISize
;
673 if (StyleDisplay()->IsContainSize()) {
675 return mCachedMinISize
;
679 if (gNoisyIntrinsic
) {
680 IndentBy(stdout
, gNoiseIndent
);
682 printf(": GetMinISize\n");
684 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
687 for (nsBlockFrame
* curFrame
= this; curFrame
;
688 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
689 curFrame
->LazyMarkLinesDirty();
692 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
) {
696 const bool whiteSpaceCanWrap
= StyleText()->WhiteSpaceCanWrapStyle();
697 InlineMinISizeData data
;
698 for (nsBlockFrame
* curFrame
= this; curFrame
;
699 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
700 for (LineIterator line
= curFrame
->LinesBegin(),
701 line_end
= curFrame
->LinesEnd();
702 line
!= line_end
; ++line
) {
704 if (gNoisyIntrinsic
) {
705 IndentBy(stdout
, gNoiseIndent
);
706 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
707 line
->IsEmpty() ? ", empty" : "");
709 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
711 if (line
->IsBlock()) {
713 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
714 aRenderingContext
, line
->mFirstChild
, nsLayoutUtils::MIN_ISIZE
);
717 if (!curFrame
->GetPrevContinuation() &&
718 line
== curFrame
->LinesBegin()) {
719 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
722 data
.SetLineContainer(curFrame
);
723 nsIFrame
* kid
= line
->mFirstChild
;
724 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
725 ++i
, kid
= kid
->GetNextSibling()) {
726 kid
->AddInlineMinISize(aRenderingContext
, &data
);
727 if (whiteSpaceCanWrap
&& data
.mTrailingWhitespace
) {
728 data
.OptionallyBreak();
733 if (gNoisyIntrinsic
) {
734 IndentBy(stdout
, gNoiseIndent
);
735 printf("min: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
743 mCachedMinISize
= data
.mPrevLines
;
744 return mCachedMinISize
;
748 nscoord
nsBlockFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
749 nsIFrame
* firstInFlow
= FirstContinuation();
750 if (firstInFlow
!= this) return firstInFlow
->GetPrefISize(aRenderingContext
);
752 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
754 CheckIntrinsicCacheAgainstShrinkWrapState();
756 if (mCachedPrefISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
757 return mCachedPrefISize
;
760 if (StyleDisplay()->IsContainSize()) {
761 mCachedPrefISize
= 0;
762 return mCachedPrefISize
;
766 if (gNoisyIntrinsic
) {
767 IndentBy(stdout
, gNoiseIndent
);
769 printf(": GetPrefISize\n");
771 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
774 for (nsBlockFrame
* curFrame
= this; curFrame
;
775 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
776 curFrame
->LazyMarkLinesDirty();
779 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
) ResolveBidi();
780 InlinePrefISizeData data
;
781 for (nsBlockFrame
* curFrame
= this; curFrame
;
782 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
783 for (LineIterator line
= curFrame
->LinesBegin(),
784 line_end
= curFrame
->LinesEnd();
785 line
!= line_end
; ++line
) {
787 if (gNoisyIntrinsic
) {
788 IndentBy(stdout
, gNoiseIndent
);
789 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
790 line
->IsEmpty() ? ", empty" : "");
792 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
794 if (line
->IsBlock()) {
795 StyleClear breakType
;
796 if (!data
.mLineIsEmpty
|| BlockCanIntersectFloats(line
->mFirstChild
)) {
797 breakType
= StyleClear::Both
;
799 breakType
= line
->mFirstChild
->StyleDisplay()->mBreakType
;
801 data
.ForceBreak(breakType
);
802 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
803 aRenderingContext
, line
->mFirstChild
, nsLayoutUtils::PREF_ISIZE
);
806 if (!curFrame
->GetPrevContinuation() &&
807 line
== curFrame
->LinesBegin()) {
808 nscoord indent
= StyleText()->mTextIndent
.Resolve(0);
809 data
.mCurrentLine
+= indent
;
810 // XXXmats should the test below be indent > 0?
811 if (indent
!= nscoord(0)) {
812 data
.mLineIsEmpty
= false;
816 data
.SetLineContainer(curFrame
);
817 nsIFrame
* kid
= line
->mFirstChild
;
818 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
819 ++i
, kid
= kid
->GetNextSibling()) {
820 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
824 if (gNoisyIntrinsic
) {
825 IndentBy(stdout
, gNoiseIndent
);
826 printf("pref: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
834 mCachedPrefISize
= data
.mPrevLines
;
835 return mCachedPrefISize
;
838 nsRect
nsBlockFrame::ComputeTightBounds(DrawTarget
* aDrawTarget
) const {
840 if (Style()->HasTextDecorationLines()) {
841 return GetVisualOverflowRect();
843 return ComputeSimpleTightBounds(aDrawTarget
);
847 nsresult
nsBlockFrame::GetPrefWidthTightBounds(gfxContext
* aRenderingContext
,
848 nscoord
* aX
, nscoord
* aXMost
) {
849 nsIFrame
* firstInFlow
= FirstContinuation();
850 if (firstInFlow
!= this) {
851 return firstInFlow
->GetPrefWidthTightBounds(aRenderingContext
, aX
, aXMost
);
858 InlinePrefISizeData data
;
859 for (nsBlockFrame
* curFrame
= this; curFrame
;
860 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
861 for (LineIterator line
= curFrame
->LinesBegin(),
862 line_end
= curFrame
->LinesEnd();
863 line
!= line_end
; ++line
) {
864 nscoord childX
, childXMost
;
865 if (line
->IsBlock()) {
867 rv
= line
->mFirstChild
->GetPrefWidthTightBounds(aRenderingContext
,
868 &childX
, &childXMost
);
869 NS_ENSURE_SUCCESS(rv
, rv
);
870 *aX
= std::min(*aX
, childX
);
871 *aXMost
= std::max(*aXMost
, childXMost
);
873 if (!curFrame
->GetPrevContinuation() &&
874 line
== curFrame
->LinesBegin()) {
875 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
878 data
.SetLineContainer(curFrame
);
879 nsIFrame
* kid
= line
->mFirstChild
;
880 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
881 ++i
, kid
= kid
->GetNextSibling()) {
882 rv
= kid
->GetPrefWidthTightBounds(aRenderingContext
, &childX
,
884 NS_ENSURE_SUCCESS(rv
, rv
);
885 *aX
= std::min(*aX
, data
.mCurrentLine
+ childX
);
886 *aXMost
= std::max(*aXMost
, data
.mCurrentLine
+ childXMost
);
887 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
898 * Return whether aNewAvailableSpace is smaller *on either side*
899 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
900 * if we need to redo layout on an line, replaced block, or block
901 * formatting context, because its height (which we used to compute
902 * aNewAvailableSpace) caused it to intersect additional floats.
904 static bool AvailableSpaceShrunk(WritingMode aWM
,
905 const LogicalRect
& aOldAvailableSpace
,
906 const LogicalRect
& aNewAvailableSpace
,
907 bool aCanGrow
/* debug-only */) {
908 if (aNewAvailableSpace
.ISize(aWM
) == 0) {
909 // Positions are not significant if the inline size is zero.
910 return aOldAvailableSpace
.ISize(aWM
) != 0;
914 aNewAvailableSpace
.IStart(aWM
) <= aOldAvailableSpace
.IStart(aWM
) ||
915 aNewAvailableSpace
.IEnd(aWM
) <= aOldAvailableSpace
.IEnd(aWM
),
916 "available space should not shrink on the start side and "
917 "grow on the end side");
919 aNewAvailableSpace
.IStart(aWM
) >= aOldAvailableSpace
.IStart(aWM
) ||
920 aNewAvailableSpace
.IEnd(aWM
) >= aOldAvailableSpace
.IEnd(aWM
),
921 "available space should not grow on the start side and "
922 "shrink on the end side");
925 aOldAvailableSpace
.IStart(aWM
) <= aNewAvailableSpace
.IStart(aWM
) &&
926 aOldAvailableSpace
.IEnd(aWM
) >= aNewAvailableSpace
.IEnd(aWM
),
927 "available space should never grow");
929 // Have we shrunk on either side?
930 return aNewAvailableSpace
.IStart(aWM
) > aOldAvailableSpace
.IStart(aWM
) ||
931 aNewAvailableSpace
.IEnd(aWM
) < aOldAvailableSpace
.IEnd(aWM
);
934 static LogicalSize
CalculateContainingBlockSizeForAbsolutes(
935 WritingMode aWM
, const ReflowInput
& aReflowInput
, LogicalSize aFrameSize
) {
936 // The issue here is that for a 'height' of 'auto' the reflow input
937 // code won't know how to calculate the containing block height
938 // because it's calculated bottom up. So we use our own computed
939 // size as the dimensions.
940 nsIFrame
* frame
= aReflowInput
.mFrame
;
942 LogicalSize
cbSize(aFrameSize
);
943 // Containing block is relative to the padding edge
944 const LogicalMargin
& border
=
945 LogicalMargin(aWM
, aReflowInput
.ComputedPhysicalBorderPadding() -
946 aReflowInput
.ComputedPhysicalPadding());
947 cbSize
.ISize(aWM
) -= border
.IStartEnd(aWM
);
948 cbSize
.BSize(aWM
) -= border
.BStartEnd(aWM
);
950 if (frame
->GetParent()->GetContent() == frame
->GetContent() &&
951 !frame
->GetParent()->IsCanvasFrame()) {
952 // We are a wrapped frame for the content (and the wrapper is not the
953 // canvas frame, whose size is not meaningful here).
954 // Use the container's dimensions, if they have been precomputed.
955 // XXX This is a hack! We really should be waiting until the outermost
956 // frame is fully reflowed and using the resulting dimensions, even
957 // if they're intrinsic.
958 // In fact we should be attaching absolute children to the outermost
959 // frame and not always sticking them in block frames.
961 // First, find the reflow input for the outermost frame for this
962 // content, except for fieldsets where the inner anonymous frame has
963 // the correct padding area with the legend taken into account.
964 const ReflowInput
* aLastRI
= &aReflowInput
;
965 const ReflowInput
* lastButOneRI
= &aReflowInput
;
966 while (aLastRI
->mParentReflowInput
&&
967 aLastRI
->mParentReflowInput
->mFrame
->GetContent() ==
968 frame
->GetContent() &&
969 !aLastRI
->mParentReflowInput
->mFrame
->IsFieldSetFrame()) {
970 lastButOneRI
= aLastRI
;
971 aLastRI
= aLastRI
->mParentReflowInput
;
973 if (aLastRI
!= &aReflowInput
) {
974 // Scrollbars need to be specifically excluded, if present, because they
975 // are outside the padding-edge. We need better APIs for getting the
976 // various boxes from a frame.
977 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(aLastRI
->mFrame
);
978 nsMargin
scrollbars(0, 0, 0, 0);
980 scrollbars
= scrollFrame
->GetDesiredScrollbarSizes(
981 aLastRI
->mFrame
->PresContext(), aLastRI
->mRenderingContext
);
982 if (!lastButOneRI
->mFlags
.mAssumingHScrollbar
) {
983 scrollbars
.top
= scrollbars
.bottom
= 0;
985 if (!lastButOneRI
->mFlags
.mAssumingVScrollbar
) {
986 scrollbars
.left
= scrollbars
.right
= 0;
989 // We found a reflow input for the outermost wrapping frame, so use
990 // its computed metrics if available, converted to our writing mode
991 WritingMode lastWM
= aLastRI
->GetWritingMode();
992 LogicalSize lastRISize
= aLastRI
->ComputedSize().ConvertTo(aWM
, lastWM
);
993 LogicalMargin lastRIPadding
=
994 aLastRI
->ComputedLogicalPadding().ConvertTo(aWM
, lastWM
);
995 LogicalMargin
logicalScrollbars(aWM
, scrollbars
);
996 if (lastRISize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
998 std::max(0, lastRISize
.ISize(aWM
) + lastRIPadding
.IStartEnd(aWM
) -
999 logicalScrollbars
.IStartEnd(aWM
));
1001 if (lastRISize
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1003 std::max(0, lastRISize
.BSize(aWM
) + lastRIPadding
.BStartEnd(aWM
) -
1004 logicalScrollbars
.BStartEnd(aWM
));
1013 * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1015 * This is used to determine whether to recurse into aFrame when applying
1016 * -webkit-line-clamp.
1018 static nsBlockFrame
* GetAsLineClampDescendant(nsIFrame
* aFrame
) {
1019 if (nsBlockFrame
* block
= do_QueryFrame(aFrame
)) {
1020 if (!block
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1028 * Iterator over all descendant inline line boxes, except for those that are
1029 * under an independent formatting context.
1031 class MOZ_RAII LineClampLineIterator
{
1033 explicit LineClampLineIterator(nsBlockFrame
* aFrame
)
1034 : mCur(aFrame
->LinesBegin()),
1035 mEnd(aFrame
->LinesEnd()),
1036 mCurrentFrame(mCur
== mEnd
? nullptr : aFrame
) {
1037 if (mCur
!= mEnd
&& !mCur
->IsInline()) {
1042 nsLineBox
* GetCurrentLine() { return mCurrentFrame
? mCur
.get() : nullptr; }
1043 nsBlockFrame
* GetCurrentFrame() { return mCurrentFrame
; }
1045 // Advances the iterator to the next line line.
1047 // Next() shouldn't be called once the iterator is at the end, which can be
1048 // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
1050 MOZ_ASSERT(mCur
!= mEnd
&& mCurrentFrame
,
1051 "Don't call Next() when the iterator is at the end");
1060 // Reached the end of the current block. Pop the parent off the
1061 // stack; if there isn't one, then we've reached the end.
1062 if (mStack
.IsEmpty()) {
1063 mCurrentFrame
= nullptr;
1066 auto entry
= mStack
.PopLastElement();
1067 mCurrentFrame
= entry
.first();
1068 mCur
= entry
.second();
1069 mEnd
= mCurrentFrame
->LinesEnd();
1070 } else if (mCur
->IsBlock()) {
1071 if (nsBlockFrame
* child
= GetAsLineClampDescendant(mCur
->mFirstChild
)) {
1072 nsBlockFrame::LineIterator next
= mCur
;
1074 mStack
.AppendElement(MakePair(mCurrentFrame
, next
));
1075 mCur
= child
->LinesBegin();
1076 mEnd
= child
->LinesEnd();
1077 mCurrentFrame
= child
;
1079 // Some kind of frame we shouldn't descend into.
1083 MOZ_ASSERT(mCur
->IsInline());
1089 // The current line within the current block.
1091 // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1093 nsBlockFrame::LineIterator mCur
;
1095 // The iterator end for the current block.
1096 nsBlockFrame::LineIterator mEnd
;
1098 // The current block.
1099 nsBlockFrame
* mCurrentFrame
;
1101 // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1103 AutoTArray
<Pair
<nsBlockFrame
*, nsBlockFrame::LineIterator
>, 8> mStack
;
1106 static bool ClearLineClampEllipsis(nsBlockFrame
* aFrame
) {
1107 if (!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
)) {
1108 for (nsIFrame
* f
: aFrame
->PrincipalChildList()) {
1109 if (nsBlockFrame
* child
= GetAsLineClampDescendant(f
)) {
1110 if (ClearLineClampEllipsis(child
)) {
1118 aFrame
->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1120 nsBlockFrame::LineIterator line
= aFrame
->LinesBegin();
1121 nsBlockFrame::LineIterator end
= aFrame
->LinesEnd();
1122 while (line
!= end
) {
1123 if (line
->HasLineClampEllipsis()) {
1124 line
->ClearHasLineClampEllipsis();
1130 MOZ_ASSERT_UNREACHABLE("expected to find a line with HasLineClampEllipsis");
1134 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1136 static bool IsLineClampItem(const ReflowInput
& aReflowInput
) {
1137 return aReflowInput
.mFlags
.mApplyLineClamp
||
1138 (aReflowInput
.mParentReflowInput
&&
1139 aReflowInput
.mParentReflowInput
->mFrame
->IsScrollFrame() &&
1140 aReflowInput
.mParentReflowInput
->mFlags
.mApplyLineClamp
);
1143 void nsBlockFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
1144 const ReflowInput
& aReflowInput
,
1145 nsReflowStatus
& aStatus
) {
1147 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1148 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
1149 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1153 IndentBy(stdout
, gNoiseIndent
);
1155 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1156 aReflowInput
.AvailableISize(), aReflowInput
.AvailableBSize(),
1157 aReflowInput
.ComputedISize(), aReflowInput
.ComputedBSize());
1159 AutoNoisyIndenter
indent(gNoisy
);
1160 PRTime start
= 0; // Initialize these variablies to silence the compiler.
1161 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
1162 if (gLameReflowMetrics
) {
1164 ctc
= nsLineBox::GetCtorCount();
1168 const ReflowInput
* reflowInput
= &aReflowInput
;
1169 WritingMode wm
= aReflowInput
.GetWritingMode();
1170 nscoord consumedBSize
= ConsumedBSize(wm
);
1171 nscoord effectiveComputedBSize
=
1172 GetEffectiveComputedBSize(aReflowInput
, consumedBSize
);
1173 Maybe
<ReflowInput
> mutableReflowInput
;
1174 // If we have non-auto block size, we're clipping our kids and we fit,
1175 // make sure our kids fit too.
1176 if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1177 aReflowInput
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
&&
1178 ShouldApplyOverflowClipping(this, aReflowInput
.mStyleDisplay
)) {
1179 LogicalMargin blockDirExtras
= aReflowInput
.ComputedLogicalBorderPadding();
1180 if (GetLogicalSkipSides().BStart()) {
1181 blockDirExtras
.BStart(wm
) = 0;
1183 // Block-end margin never causes us to create continuations, so we
1184 // don't need to worry about whether it fits in its entirety.
1185 blockDirExtras
.BStart(wm
) +=
1186 aReflowInput
.ComputedLogicalMargin().BStart(wm
);
1189 if (effectiveComputedBSize
+ blockDirExtras
.BStartEnd(wm
) <=
1190 aReflowInput
.AvailableBSize()) {
1191 mutableReflowInput
.emplace(aReflowInput
);
1192 mutableReflowInput
->AvailableBSize() = NS_UNCONSTRAINEDSIZE
;
1193 reflowInput
= mutableReflowInput
.ptr();
1197 // See comment below about oldSize. Use *only* for the
1198 // abs-pos-containing-block-size-change optimization!
1199 nsSize oldSize
= GetSize();
1201 // Should we create a float manager?
1202 nsAutoFloatManager
autoFloatManager(const_cast<ReflowInput
&>(*reflowInput
));
1204 // XXXldb If we start storing the float manager in the frame rather
1205 // than keeping it around only during reflow then we should create it
1206 // only when there are actually floats to manage. Otherwise things
1207 // like tables will gain significant bloat.
1208 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
1209 if (needFloatManager
) autoFloatManager
.CreateFloatManager(aPresContext
);
1211 // OK, some lines may be reflowed. Blow away any saved line cursor
1212 // because we may invalidate the nondecreasing
1213 // overflowArea.VisualOverflow().y/yMost invariant, and we may even
1214 // delete the line with the line cursor.
1217 if (IsFrameTreeTooDeep(*reflowInput
, aMetrics
, aStatus
)) {
1222 // Between when we drain pushed floats and when we complete reflow,
1223 // we're allowed to have multiple continuations of the same float on
1224 // our floats list, since a first-in-flow might get pushed to a later
1225 // continuation of its containing block. But it's not permitted
1226 // outside that time.
1227 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1230 // ALWAYS drain overflow. We never want to leave the previnflow's
1231 // overflow lines hanging around; block reflow depends on the
1232 // overflow line lists being cleared out between reflow passes.
1233 DrainOverflowLines();
1235 bool blockStartMarginRoot
, blockEndMarginRoot
;
1236 IsMarginRoot(&blockStartMarginRoot
, &blockEndMarginRoot
);
1238 // Cache the consumed height in the block reflow input so that we don't have
1239 // to continually recompute it.
1240 BlockReflowInput
state(*reflowInput
, aPresContext
, this, blockStartMarginRoot
,
1241 blockEndMarginRoot
, needFloatManager
, consumedBSize
);
1243 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
)
1244 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
1246 // Handle paginated overflow (see nsContainerFrame.h)
1247 nsOverflowAreas ocBounds
;
1248 nsReflowStatus ocStatus
;
1249 if (GetPrevInFlow()) {
1250 ReflowOverflowContainerChildren(aPresContext
, *reflowInput
, ocBounds
, 0,
1254 // Now that we're done cleaning up our overflow container lists, we can
1255 // give |state| its nsOverflowContinuationTracker.
1256 nsOverflowContinuationTracker
tracker(this, false);
1257 state
.mOverflowTracker
= &tracker
;
1259 // Drain & handle pushed floats
1260 DrainPushedFloats();
1261 nsOverflowAreas fcBounds
;
1262 nsReflowStatus fcStatus
;
1263 ReflowPushedFloats(state
, fcBounds
, fcStatus
);
1265 // If we're not dirty (which means we'll mark everything dirty later)
1266 // and our inline-size has changed, mark the lines dirty that we need to
1267 // mark dirty for a resize reflow.
1268 if (!(GetStateBits() & NS_FRAME_IS_DIRTY
) && reflowInput
->IsIResize()) {
1269 PrepareResizeReflow(state
);
1272 // The same for percentage text-indent, except conditioned on the
1274 if (!(GetStateBits() & NS_FRAME_IS_DIRTY
) && reflowInput
->mCBReflowInput
&&
1275 reflowInput
->mCBReflowInput
->IsIResize() &&
1276 reflowInput
->mStyleText
->mTextIndent
.HasPercent() && !mLines
.empty()) {
1277 mLines
.front()->MarkDirty();
1280 LazyMarkLinesDirty();
1282 RemoveStateBits(NS_FRAME_FIRST_REFLOW
);
1285 ReflowDirtyLines(state
);
1287 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1288 // this frame from a previous iteration of reflow, then we should not return
1289 // a status with IsFullyComplete() equals to true, since we actually have
1290 // overflow, it's just already been handled.
1292 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1293 // and reflow them, but just in case it does, this is a safety precaution so
1294 // we don't end up with a placeholder pointing to frames that have already
1295 // been deleted as part of removing our next-in-flow.
1296 if (state
.mReflowStatus
.IsFullyComplete()) {
1297 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1299 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1300 if (nif
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
) {
1301 state
.mReflowStatus
.SetOverflowIncomplete();
1303 state
.mReflowStatus
.SetIncomplete();
1308 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1312 state
.mReflowStatus
.MergeCompletionStatusFrom(ocStatus
);
1313 state
.mReflowStatus
.MergeCompletionStatusFrom(fcStatus
);
1315 // If we end in a BR with clear and affected floats continue,
1316 // we need to continue, too.
1317 if (NS_UNCONSTRAINEDSIZE
!= reflowInput
->AvailableBSize() &&
1318 state
.mReflowStatus
.IsComplete() &&
1319 state
.FloatManager()->ClearContinues(FindTrailingClear())) {
1320 state
.mReflowStatus
.SetIncomplete();
1323 if (!state
.mReflowStatus
.IsFullyComplete()) {
1324 if (HasOverflowLines() || HasPushedFloats()) {
1325 state
.mReflowStatus
.SetNextInFlowNeedsReflow();
1330 printf(": block is not fully complete\n");
1334 // Place the ::marker's frame if it is placed next to a block child.
1336 // According to the CSS2 spec, section 12.6.1, the ::marker's box
1337 // participates in the height calculation of the list-item box's
1340 // There are exactly two places a ::marker can be placed: near the
1341 // first or second line. It's only placed on the second line in a
1342 // rare case: an empty first line followed by a second line that
1343 // contains a block (example: <LI>\n<P>... ). This is where
1344 // the second case can happen.
1345 if (HasOutsideMarker() && !mLines
.empty() &&
1346 (mLines
.front()->IsBlock() ||
1347 (0 == mLines
.front()->BSize() && mLines
.front() != mLines
.back() &&
1348 mLines
.begin().next()->IsBlock()))) {
1349 // Reflow the ::marker's frame.
1350 ReflowOutput
reflowOutput(aReflowInput
);
1351 // XXX Use the entire line when we fix bug 25888.
1352 nsLayoutUtils::LinePosition position
;
1353 WritingMode wm
= aReflowInput
.GetWritingMode();
1355 nsLayoutUtils::GetFirstLinePosition(wm
, this, &position
);
1356 nscoord lineBStart
=
1357 havePosition
? position
.mBStart
1358 : reflowInput
->ComputedLogicalBorderPadding().BStart(wm
);
1359 nsIFrame
* marker
= GetOutsideMarker();
1360 ReflowOutsideMarker(marker
, state
, reflowOutput
, lineBStart
);
1361 NS_ASSERTION(!MarkerIsEmpty() || reflowOutput
.BSize(wm
) == 0,
1362 "empty ::marker frame took up space");
1364 if (havePosition
&& !MarkerIsEmpty()) {
1365 // We have some lines to align the ::marker with.
1367 // Doing the alignment using the baseline will also cater for
1368 // ::markers that are placed next to a child block (bug 92896)
1370 // Tall ::markers won't look particularly nice here...
1372 marker
->GetLogicalRect(wm
, reflowOutput
.PhysicalSize());
1373 const auto baselineGroup
= BaselineSharingGroup::First
;
1374 nscoord markerBaseline
;
1375 if (MOZ_UNLIKELY(wm
.IsOrthogonalTo(marker
->GetWritingMode()) ||
1376 !marker
->GetNaturalBaselineBOffset(wm
, baselineGroup
,
1377 &markerBaseline
))) {
1378 // ::marker has no baseline in this axis: align with its margin-box end.
1380 bbox
.BSize(wm
) + marker
->GetLogicalUsedMargin(wm
).BEnd(wm
);
1382 bbox
.BStart(wm
) = position
.mBaseline
- markerBaseline
;
1383 marker
->SetRect(wm
, bbox
, reflowOutput
.PhysicalSize());
1385 // Otherwise just leave the ::marker where it is, up against our
1386 // block-start padding.
1389 // Clear any existing -webkit-line-clamp ellipsis.
1390 if (IsLineClampItem(aReflowInput
)) {
1391 ClearLineClampEllipsis();
1396 // Compute our final size
1397 nscoord blockEndEdgeOfChildren
;
1398 ComputeFinalSize(*reflowInput
, state
, aMetrics
, &blockEndEdgeOfChildren
);
1400 // If the block direction is right-to-left, we need to update the bounds of
1401 // lines that were placed relative to mContainerSize during reflow, as
1402 // we typically do not know the true container size until we've reflowed all
1403 // its children. So we use a dummy mContainerSize during reflow (see
1404 // BlockReflowInput's constructor) and then fix up the positions of the
1405 // lines here, once the final block size is known.
1407 // Note that writing-mode:vertical-rl is the only case where the block
1408 // logical direction progresses in a negative physical direction, and
1409 // therefore block-dir coordinate conversion depends on knowing the width
1410 // of the coordinate space in order to translate between the logical and
1411 // physical origins.
1412 if (wm
.IsVerticalRL()) {
1413 nsSize containerSize
= aMetrics
.PhysicalSize();
1414 nscoord deltaX
= containerSize
.width
- state
.ContainerSize().width
;
1416 for (LineIterator line
= LinesBegin(), end
= LinesEnd(); line
!= end
;
1418 UpdateLineContainerSize(line
, containerSize
);
1420 for (nsIFrame
* f
: mFloats
) {
1421 nsPoint
physicalDelta(deltaX
, 0);
1422 f
->MovePositionBy(physicalDelta
);
1424 nsFrameList
* markerList
= GetOutsideMarkerList();
1426 nsPoint
physicalDelta(deltaX
, 0);
1427 for (nsIFrame
* f
: *markerList
) {
1428 f
->MovePositionBy(physicalDelta
);
1434 nsRect areaBounds
= nsRect(0, 0, aMetrics
.Width(), aMetrics
.Height());
1435 ComputeOverflowAreas(areaBounds
, reflowInput
->mStyleDisplay
,
1436 blockEndEdgeOfChildren
, aMetrics
.mOverflowAreas
);
1437 // Factor overflow container child bounds into the overflow area
1438 aMetrics
.mOverflowAreas
.UnionWith(ocBounds
);
1439 // Factor pushed float child bounds into the overflow area
1440 aMetrics
.mOverflowAreas
.UnionWith(fcBounds
);
1442 // Let the absolutely positioned container reflow any absolutely positioned
1443 // child frames that need to be reflowed, e.g., elements with a percentage
1444 // based width/height
1445 // We want to do this under either of two conditions:
1446 // 1. If we didn't do the incremental reflow above.
1447 // 2. If our size changed.
1448 // Even though it's the padding edge that's the containing block, we
1449 // can use our rect (the border edge) since if the border style
1450 // changed, the reflow would have been targeted at us so we'd satisfy
1452 // XXX checking oldSize is bogus, there are various reasons we might have
1453 // reflowed but our size might not have been changed to what we
1454 // asked for (e.g., we ended up being pushed to a new page)
1455 // When WillReflowAgainForClearance is true, we will reflow again without
1456 // resetting the size. Because of this, we must not reflow our abs-pos
1457 // children in that situation --- what we think is our "new size" will not be
1458 // our real new size. This also happens to be more efficient.
1459 WritingMode parentWM
= aMetrics
.GetWritingMode();
1460 if (HasAbsolutelyPositionedChildren()) {
1461 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1462 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1463 if (reflowInput
->WillReflowAgainForClearance() || haveInterrupt
) {
1464 // Make sure that when we reflow again we'll actually reflow all the abs
1465 // pos frames that might conceivably depend on our size (or all of them,
1466 // if we're dirty right now and interrupted; in that case we also need
1467 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1468 // better than that, because we don't really know what our size will be,
1469 // and it might in fact not change on the followup reflow!
1470 if (haveInterrupt
&& (GetStateBits() & NS_FRAME_IS_DIRTY
)) {
1471 absoluteContainer
->MarkAllFramesDirty();
1473 absoluteContainer
->MarkSizeDependentFramesDirty();
1475 if (haveInterrupt
) {
1476 // We're not going to reflow absolute frames; make sure to account for
1477 // their existing overflow areas, which is usually a side effect of this
1480 // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1481 // interrupt, can we just rely on it and unconditionally take the else
1482 // branch below? That's a bit more subtle / risky, since I don't see
1483 // what would reflow them in that case if they depended on our size.
1484 for (nsIFrame
* kid
= absoluteContainer
->GetChildList().FirstChild();
1485 kid
; kid
= kid
->GetNextSibling()) {
1486 ConsiderChildOverflow(aMetrics
.mOverflowAreas
, kid
);
1490 LogicalSize containingBlockSize
=
1491 CalculateContainingBlockSizeForAbsolutes(parentWM
, *reflowInput
,
1492 aMetrics
.Size(parentWM
));
1494 // Mark frames that depend on changes we just made to this frame as dirty:
1495 // Now we can assume that the padding edge hasn't moved.
1496 // We need to reflow the absolutes if one of them depends on
1497 // its placeholder position, or the containing block size in a
1498 // direction in which the containing block size might have
1501 // XXX "width" and "height" in this block will become ISize and BSize
1502 // when nsAbsoluteContainingBlock is logicalized
1503 bool cbWidthChanged
= aMetrics
.Width() != oldSize
.width
;
1504 bool isRoot
= !GetContent()->GetParent();
1505 // If isRoot and we have auto height, then we are the initial
1506 // containing block and the containing block height is the
1507 // viewport height, which can't change during incremental
1509 bool cbHeightChanged
=
1510 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== reflowInput
->ComputedHeight()) &&
1511 aMetrics
.Height() != oldSize
.height
;
1513 nsRect
containingBlock(nsPoint(0, 0),
1514 containingBlockSize
.GetPhysicalSize(parentWM
));
1515 AbsPosReflowFlags flags
= AbsPosReflowFlags::ConstrainHeight
;
1516 if (cbWidthChanged
) {
1517 flags
|= AbsPosReflowFlags::CBWidthChanged
;
1519 if (cbHeightChanged
) {
1520 flags
|= AbsPosReflowFlags::CBHeightChanged
;
1522 // Setup the line cursor here to optimize line searching for
1523 // calculating hypothetical position of absolutely-positioned
1524 // frames. The line cursor is immediately cleared afterward to
1525 // avoid affecting the display list generation.
1526 AutoLineCursorSetup
autoLineCursor(this);
1527 absoluteContainer
->Reflow(this, aPresContext
, *reflowInput
,
1528 state
.mReflowStatus
, containingBlock
, flags
,
1529 &aMetrics
.mOverflowAreas
);
1533 FinishAndStoreOverflow(&aMetrics
, reflowInput
->mStyleDisplay
);
1535 aStatus
= state
.mReflowStatus
;
1538 // Between when we drain pushed floats and when we complete reflow,
1539 // we're allowed to have multiple continuations of the same float on
1540 // our floats list, since a first-in-flow might get pushed to a later
1541 // continuation of its containing block. But it's not permitted
1542 // outside that time.
1543 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1546 IndentBy(stdout
, gNoiseIndent
);
1548 printf(": status=%s metrics=%d,%d carriedMargin=%d",
1549 ToString(aStatus
).c_str(), aMetrics
.ISize(parentWM
),
1550 aMetrics
.BSize(parentWM
), aMetrics
.mCarriedOutBEndMargin
.get());
1551 if (HasOverflowAreas()) {
1552 printf(" overflow-vis={%d,%d,%d,%d}", aMetrics
.VisualOverflow().x
,
1553 aMetrics
.VisualOverflow().y
, aMetrics
.VisualOverflow().width
,
1554 aMetrics
.VisualOverflow().height
);
1555 printf(" overflow-scr={%d,%d,%d,%d}", aMetrics
.ScrollableOverflow().x
,
1556 aMetrics
.ScrollableOverflow().y
,
1557 aMetrics
.ScrollableOverflow().width
,
1558 aMetrics
.ScrollableOverflow().height
);
1563 if (gLameReflowMetrics
) {
1564 PRTime end
= PR_Now();
1566 int32_t ectc
= nsLineBox::GetCtorCount();
1567 int32_t numLines
= mLines
.size();
1568 if (!numLines
) numLines
= 1;
1569 PRTime delta
, perLineDelta
, lines
;
1570 lines
= int64_t(numLines
);
1571 delta
= end
- start
;
1572 perLineDelta
= delta
/ lines
;
1577 ": %" PRId64
" elapsed (%" PRId64
1578 " per line) (%d lines; %d new lines)",
1579 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1580 printf("%s\n", buf
);
1584 NS_FRAME_SET_TRUNCATION(aStatus
, (*reflowInput
), aMetrics
);
1587 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
1588 LineIterator begin
= LinesBegin();
1589 LineIterator line
= LinesEnd();
1592 if (begin
== line
) {
1596 if (line
->BSize() != 0 || !line
->CachedIsEmpty()) {
1599 if (line
->HasClearance()) {
1606 static nsLineBox
* FindLineClampTarget(nsBlockFrame
*& aFrame
,
1607 uint32_t aLineNumber
) {
1608 MOZ_ASSERT(aLineNumber
> 0);
1609 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
1610 "Should have been removed earlier in nsBlockReflow::Reflow");
1612 nsLineBox
* target
= nullptr;
1613 nsBlockFrame
* targetFrame
= nullptr;
1614 bool foundFollowingLine
= false;
1616 LineClampLineIterator
iter(aFrame
);
1618 while (nsLineBox
* line
= iter
.GetCurrentLine()) {
1619 MOZ_ASSERT(!line
->HasLineClampEllipsis(),
1620 "Should have been removed earlier in nsBlockFrame::Reflow");
1621 MOZ_ASSERT(!iter
.GetCurrentFrame()->HasAnyStateBits(
1622 NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
1623 "Should have been removed earlier in nsBlockReflow::Reflow");
1625 // Don't count a line that only has collapsible white space (as might exist
1626 // after calling e.g. getBoxQuads).
1627 if (line
->IsEmpty()) {
1632 if (aLineNumber
== 0) {
1633 // We already previously found our target line, and now we have
1634 // confirmed that there is another line after it.
1635 foundFollowingLine
= true;
1639 if (--aLineNumber
== 0) {
1640 // This is our target line. Continue looping to confirm that we
1641 // have another line after us.
1643 targetFrame
= iter
.GetCurrentFrame();
1649 if (!foundFollowingLine
) {
1655 MOZ_ASSERT(targetFrame
);
1657 aFrame
= targetFrame
;
1661 static nscoord
ApplyLineClamp(const ReflowInput
& aReflowInput
,
1662 nsBlockFrame
* aFrame
, nscoord aContentBSize
) {
1663 // We only do the work of applying the -webkit-line-clamp value during the
1664 // measuring bsize reflow. Boxes affected by -webkit-line-clamp are always
1665 // inflexible, so we will never need to select a different line to place the
1666 // ellipsis on in the subsequent real reflow.
1667 if (!IsLineClampItem(aReflowInput
)) {
1668 return aContentBSize
;
1672 static_cast<nsFlexContainerFrame
*>(nsLayoutUtils::GetClosestFrameOfType(
1673 aFrame
, LayoutFrameType::FlexContainer
));
1674 MOZ_ASSERT(container
,
1675 "A flex item affected by -webkit-line-clamp must have an ancestor "
1678 uint32_t lineClamp
= container
->GetLineClampValue();
1679 if (lineClamp
== 0) {
1680 // -webkit-line-clamp is none or doesn't apply.
1681 return aContentBSize
;
1684 MOZ_ASSERT(container
->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX
),
1685 "Should only have an effective -webkit-line-clamp value if we "
1686 "are in a legacy flex container");
1688 nsBlockFrame
* frame
= aFrame
;
1689 nsLineBox
* line
= FindLineClampTarget(frame
, lineClamp
);
1691 // The number of lines did not exceed the -webkit-line-clamp value.
1692 return aContentBSize
;
1695 // Mark the line as having an ellipsis so that TextOverflow will render it.
1696 line
->SetHasLineClampEllipsis();
1697 frame
->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1698 container
->AddStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS
);
1700 // Translate the b-end edge of the line up to aFrame's space.
1701 nscoord edge
= line
->BEnd();
1702 for (nsIFrame
* f
= frame
; f
!= aFrame
; f
= f
->GetParent()) {
1704 f
->GetLogicalPosition(f
->GetParent()->GetSize()).B(f
->GetWritingMode());
1710 void nsBlockFrame::ComputeFinalSize(const ReflowInput
& aReflowInput
,
1711 BlockReflowInput
& aState
,
1712 ReflowOutput
& aMetrics
,
1713 nscoord
* aBEndEdgeOfChildren
) {
1714 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
1715 const LogicalMargin
& borderPadding
= aState
.BorderPadding();
1716 #ifdef NOISY_FINAL_SIZE
1718 printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
1719 aState
.mBCoord
, aState
.mFlags
.mIsBEndMarginRoot
? "yes" : "no",
1720 aState
.mPrevBEndMargin
.get(), borderPadding
.BStart(wm
),
1721 borderPadding
.BEnd(wm
));
1724 // Compute final inline size
1725 LogicalSize
finalSize(wm
);
1726 finalSize
.ISize(wm
) =
1727 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.IStart(wm
),
1728 aReflowInput
.ComputedISize()),
1729 borderPadding
.IEnd(wm
));
1731 // Return block-end margin information
1732 // rbs says he hit this assertion occasionally (see bug 86947), so
1733 // just set the margin to zero and we'll figure out why later
1734 // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
1735 // "someone else set the margin");
1736 nscoord nonCarriedOutBDirMargin
= 0;
1737 if (!aState
.mFlags
.mIsBEndMarginRoot
) {
1738 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1739 // line with clearance and a non-zero block-start margin and all
1740 // subsequent lines are empty, then we do not allow our children's
1741 // carried out block-end margin to be carried out of us and collapse
1742 // with our own block-end margin.
1743 if (CheckForCollapsedBEndMarginFromClearanceLine()) {
1744 // Convert the children's carried out margin to something that
1745 // we will include in our height
1746 nonCarriedOutBDirMargin
= aState
.mPrevBEndMargin
.get();
1747 aState
.mPrevBEndMargin
.Zero();
1749 aMetrics
.mCarriedOutBEndMargin
= aState
.mPrevBEndMargin
;
1751 aMetrics
.mCarriedOutBEndMargin
.Zero();
1754 nscoord blockEndEdgeOfChildren
= aState
.mBCoord
+ nonCarriedOutBDirMargin
;
1755 // Shrink wrap our height around our contents.
1756 if (aState
.mFlags
.mIsBEndMarginRoot
||
1757 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
1758 // When we are a block-end-margin root make sure that our last
1759 // childs block-end margin is fully applied. We also do this when
1760 // we have a computed height, since in that case the carried out
1761 // margin is not going to be applied anywhere, so we should note it
1762 // here to be included in the overflow area.
1763 // Apply the margin only if there's space for it.
1764 if (blockEndEdgeOfChildren
< aState
.mReflowInput
.AvailableBSize()) {
1765 // Truncate block-end margin if it doesn't fit to our available BSize.
1766 blockEndEdgeOfChildren
=
1767 std::min(blockEndEdgeOfChildren
+ aState
.mPrevBEndMargin
.get(),
1768 aState
.mReflowInput
.AvailableBSize());
1771 if (aState
.mFlags
.mBlockNeedsFloatManager
) {
1772 // Include the float manager's state to properly account for the
1773 // block-end margin of any floated elements; e.g., inside a table cell.
1774 nscoord floatHeight
=
1775 aState
.ClearFloats(blockEndEdgeOfChildren
, StyleClear::Both
, nullptr,
1776 nsFloatManager::DONT_CLEAR_PUSHED_FLOATS
);
1777 blockEndEdgeOfChildren
= std::max(blockEndEdgeOfChildren
, floatHeight
);
1780 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize() &&
1781 (!GetParent()->IsColumnSetFrame() ||
1782 aReflowInput
.mParentReflowInput
->AvailableBSize() ==
1783 NS_UNCONSTRAINEDSIZE
)) {
1784 ComputeFinalBSize(aReflowInput
, &aState
.mReflowStatus
,
1785 aState
.mBCoord
+ nonCarriedOutBDirMargin
, borderPadding
,
1786 finalSize
, aState
.ConsumedBSize());
1788 // Don't carry out a block-end margin when our BSize is fixed.
1789 aMetrics
.mCarriedOutBEndMargin
.Zero();
1790 } else if (!IsComboboxControlFrame() &&
1791 aReflowInput
.mStyleDisplay
->IsContainSize()) {
1792 // If we're size-containing and we don't have a specified size, then our
1793 // final size should actually be computed from only our border and padding,
1794 // as though we were empty.
1795 // Hence this case is a simplified version of the case below.
1797 // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
1798 // special case, because comboboxes implicitly honors the size-containment
1799 // behavior on its nsComboboxDisplayFrame child (which it shrinkwraps)
1800 // rather than on the nsComboboxControlFrame. (Moreover, the DisplayFrame
1801 // child doesn't even need any special content-size-ignoring behavior in
1802 // its reflow method, because that method just resolves "auto" BSize values
1803 // to one line-height rather than by measuring its contents' BSize.)
1804 nscoord contentBSize
= 0;
1805 nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(contentBSize
);
1806 aMetrics
.mCarriedOutBEndMargin
.Zero();
1807 autoBSize
+= borderPadding
.BStartEnd(wm
);
1808 finalSize
.BSize(wm
) = autoBSize
;
1809 } else if (aState
.mReflowStatus
.IsComplete()) {
1810 nscoord contentBSize
= blockEndEdgeOfChildren
- borderPadding
.BStart(wm
);
1811 nscoord lineClampedContentBSize
=
1812 ApplyLineClamp(aReflowInput
, this, contentBSize
);
1813 nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(lineClampedContentBSize
);
1814 if (autoBSize
!= contentBSize
) {
1815 // Our min-block-size, max-block-size, or -webkit-line-clamp value made
1816 // our bsize change. Don't carry out our kids' block-end margins.
1817 aMetrics
.mCarriedOutBEndMargin
.Zero();
1819 autoBSize
+= borderPadding
.BStart(wm
) + borderPadding
.BEnd(wm
);
1820 finalSize
.BSize(wm
) = autoBSize
;
1822 NS_ASSERTION(aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
1823 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
1824 finalSize
.BSize(wm
) =
1825 std::max(aState
.mBCoord
, aReflowInput
.AvailableBSize());
1826 if (aReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
1827 // This should never happen, but it does. See bug 414255
1828 finalSize
.BSize(wm
) = aState
.mBCoord
;
1832 if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
1833 if (aState
.mReflowStatus
.IsIncomplete()) {
1834 // Overflow containers can only be overflow complete.
1835 // Note that auto height overflow containers have no normal children
1836 NS_ASSERTION(finalSize
.BSize(wm
) == 0,
1837 "overflow containers must be zero-block-size");
1838 aState
.mReflowStatus
.SetOverflowIncomplete();
1840 } else if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1841 !aState
.mReflowStatus
.IsInlineBreakBefore() &&
1842 aState
.mReflowStatus
.IsComplete()) {
1843 // Currently only used for grid items, but could be used in other contexts.
1844 // The FragStretchBSizeProperty is our expected non-fragmented block-size
1845 // we should stretch to (for align-self:stretch etc). In some fragmentation
1846 // cases though, the last fragment (this frame since we're complete), needs
1847 // to have extra size applied because earlier fragments consumed too much of
1848 // our computed size due to overflowing their containing block. (E.g. this
1849 // ensures we fill the last row when a multi-row grid item is fragmented).
1851 nscoord bSize
= GetProperty(FragStretchBSizeProperty(), &found
);
1853 finalSize
.BSize(wm
) = std::max(bSize
, finalSize
.BSize(wm
));
1857 // Clamp the content size to fit within the margin-box clamp size, if any.
1858 if (MOZ_UNLIKELY(aReflowInput
.mFlags
.mBClampMarginBoxMinSize
) &&
1859 aState
.mReflowStatus
.IsComplete()) {
1861 nscoord cbSize
= GetProperty(BClampMarginBoxMinSizeProperty(), &found
);
1863 auto marginBoxBSize
= finalSize
.BSize(wm
) +
1864 aReflowInput
.ComputedLogicalMargin().BStartEnd(wm
);
1865 auto overflow
= marginBoxBSize
- cbSize
;
1867 auto contentBSize
= finalSize
.BSize(wm
) - borderPadding
.BStartEnd(wm
);
1868 auto newContentBSize
= std::max(nscoord(0), contentBSize
- overflow
);
1869 // XXXmats deal with percentages better somehow?
1870 finalSize
.BSize(wm
) -= contentBSize
- newContentBSize
;
1875 // Screen out negative block sizes --- can happen due to integer overflows :-(
1876 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
1877 *aBEndEdgeOfChildren
= blockEndEdgeOfChildren
;
1879 if (blockEndEdgeOfChildren
!= finalSize
.BSize(wm
) - borderPadding
.BEnd(wm
)) {
1880 SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren
);
1882 DeleteProperty(BlockEndEdgeOfChildrenProperty());
1885 aMetrics
.SetSize(wm
, finalSize
);
1888 if ((CRAZY_SIZE(aMetrics
.Width()) || CRAZY_SIZE(aMetrics
.Height())) &&
1889 !GetParent()->IsCrazySizeAssertSuppressed()) {
1891 printf(": WARNING: desired:%d,%d\n", aMetrics
.Width(), aMetrics
.Height());
1896 static void ConsiderBlockEndEdgeOfChildren(const WritingMode aWritingMode
,
1897 nscoord aBEndEdgeOfChildren
,
1898 nsOverflowAreas
& aOverflowAreas
,
1899 const nsStyleDisplay
* aDisplay
) {
1900 // Factor in the block-end edge of the children. Child frames will be added
1901 // to the overflow area as we iterate through the lines, but their margins
1902 // won't, so we need to account for block-end margins here.
1903 // REVIEW: For now, we do this for both visual and scrollable area,
1904 // although when we make scrollable overflow area not be a subset of
1905 // visual, we can change this.
1906 // XXX Currently, overflow areas are stored as physical rects, so we have
1907 // to handle writing modes explicitly here. If we change overflow rects
1908 // to be stored logically, this can be simplified again.
1909 if (aWritingMode
.IsVertical()) {
1910 if (aWritingMode
.IsVerticalLR()) {
1911 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
1912 if (!(aDisplay
->IsContainLayout() && otype
== eScrollableOverflow
)) {
1913 // Layout containment should force all overflow to be ink (visual)
1914 // overflow, so if we're layout-contained, we only add our children's
1915 // block-end edge to the ink (visual) overflow -- not to the
1916 // scrollable overflow.
1917 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
1918 o
.width
= std::max(o
.XMost(), aBEndEdgeOfChildren
) - o
.x
;
1922 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
1923 if (!(aDisplay
->IsContainLayout() && otype
== eScrollableOverflow
)) {
1924 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
1925 nscoord xmost
= o
.XMost();
1926 o
.x
= std::min(o
.x
, xmost
- aBEndEdgeOfChildren
);
1927 o
.width
= xmost
- o
.x
;
1932 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
1933 if (!(aDisplay
->IsContainLayout() && otype
== eScrollableOverflow
)) {
1934 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
1935 o
.height
= std::max(o
.YMost(), aBEndEdgeOfChildren
) - o
.y
;
1941 void nsBlockFrame::ComputeOverflowAreas(const nsRect
& aBounds
,
1942 const nsStyleDisplay
* aDisplay
,
1943 nscoord aBEndEdgeOfChildren
,
1944 nsOverflowAreas
& aOverflowAreas
) {
1945 // Compute the overflow areas of our children
1946 // XXX_perf: This can be done incrementally. It is currently one of
1947 // the things that makes incremental reflow O(N^2).
1948 nsOverflowAreas
areas(aBounds
, aBounds
);
1949 if (!ShouldApplyOverflowClipping(this, aDisplay
)) {
1950 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
1951 line
!= line_end
; ++line
) {
1952 if (aDisplay
->IsContainLayout()) {
1953 // If we have layout containment, we should only consider our child's
1954 // visual overflow, leaving the scrollable regions of the parent
1956 // Note: scrollable overflow is a subset of visual overflow,
1957 // so this has the same affect as unioning the child's visual and
1958 // scrollable overflow with its parent's visual overflow.
1959 nsRect childVisualRect
= line
->GetVisualOverflowArea();
1960 nsOverflowAreas childVisualArea
=
1961 nsOverflowAreas(childVisualRect
, nsRect());
1962 areas
.UnionWith(childVisualArea
);
1964 areas
.UnionWith(line
->GetOverflowAreas());
1968 // Factor an outside ::marker in; normally the ::marker will be factored
1969 // into the line-box's overflow areas. However, if the line is a block
1970 // line then it won't; if there are no lines, it won't. So just
1971 // factor it in anyway (it can't hurt if it was already done).
1972 // XXXldb Can we just fix GetOverflowArea instead?
1973 if (nsIFrame
* outsideMarker
= GetOutsideMarker()) {
1974 areas
.UnionAllWith(outsideMarker
->GetRect());
1977 ConsiderBlockEndEdgeOfChildren(GetWritingMode(), aBEndEdgeOfChildren
, areas
,
1981 #ifdef NOISY_COMBINED_AREA
1983 const nsRect
& vis
= areas
.VisualOverflow();
1984 printf(": VisualOverflowArea CA=%d,%d,%d,%d\n", vis
.x
, vis
.y
, vis
.width
,
1986 const nsRect
& scr
= areas
.ScrollableOverflow();
1987 printf(": ScrollableOverflowArea CA=%d,%d,%d,%d\n", scr
.x
, scr
.y
, scr
.width
,
1991 aOverflowAreas
= areas
;
1994 void nsBlockFrame::UnionChildOverflow(nsOverflowAreas
& aOverflowAreas
) {
1995 // We need to update the overflow areas of lines manually, as they
1996 // get cached and re-used otherwise. Lines aren't exposed as normal
1997 // frame children, so calling UnionChildOverflow alone will end up
1998 // using the old cached values.
1999 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2000 line
!= line_end
; ++line
) {
2001 nsRect bounds
= line
->GetPhysicalBounds();
2002 nsOverflowAreas
lineAreas(bounds
, bounds
);
2004 int32_t n
= line
->GetChildCount();
2005 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2006 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2007 ConsiderChildOverflow(lineAreas
, lineFrame
);
2010 // Consider the overflow areas of the floats attached to the line as well
2011 if (line
->HasFloats()) {
2012 for (nsFloatCache
* fc
= line
->GetFirstFloat(); fc
; fc
= fc
->Next()) {
2013 ConsiderChildOverflow(lineAreas
, fc
->mFloat
);
2017 line
->SetOverflowAreas(lineAreas
);
2018 aOverflowAreas
.UnionWith(lineAreas
);
2021 // Union with child frames, skipping the principal and float lists
2022 // since we already handled those using the line boxes.
2023 nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas
,
2024 {kPrincipalList
, kFloatList
});
2027 bool nsBlockFrame::ComputeCustomOverflow(nsOverflowAreas
& aOverflowAreas
) {
2029 nscoord blockEndEdgeOfChildren
=
2030 GetProperty(BlockEndEdgeOfChildrenProperty(), &found
);
2032 ConsiderBlockEndEdgeOfChildren(GetWritingMode(), blockEndEdgeOfChildren
,
2033 aOverflowAreas
, StyleDisplay());
2036 // Line cursor invariants depend on the overflow areas of the lines, so
2037 // we must clear the line cursor since those areas may have changed.
2039 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
2042 void nsBlockFrame::LazyMarkLinesDirty() {
2043 if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
) {
2044 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2045 line
!= line_end
; ++line
) {
2046 int32_t n
= line
->GetChildCount();
2047 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2048 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2049 if (NS_SUBTREE_DIRTY(lineFrame
)) {
2050 // NOTE: MarkLineDirty does more than just marking the line dirty.
2051 MarkLineDirty(line
, &mLines
);
2056 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
2060 void nsBlockFrame::MarkLineDirty(LineIterator aLine
,
2061 const nsLineList
* aLineList
) {
2064 aLine
->SetInvalidateTextRuns(true);
2067 IndentBy(stdout
, gNoiseIndent
);
2069 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
2073 // Mark previous line dirty if it's an inline line so that it can
2074 // maybe pullup something from the line just affected.
2075 // XXX We don't need to do this if aPrevLine ends in a break-after...
2076 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
2077 aLine
.prev()->IsInline()) {
2078 aLine
.prev()->MarkDirty();
2079 aLine
.prev()->SetInvalidateTextRuns(true);
2082 IndentBy(stdout
, gNoiseIndent
);
2084 printf(": mark prev-line %p dirty\n",
2085 static_cast<void*>(aLine
.prev().get()));
2092 * Test whether lines are certain to be aligned left so that we can make
2093 * resizing optimizations
2095 static inline bool IsAlignedLeft(uint8_t aAlignment
, uint8_t aDirection
,
2096 uint8_t aUnicodeBidi
, nsIFrame
* aFrame
) {
2097 return nsSVGUtils::IsInSVGTextSubtree(aFrame
) ||
2098 NS_STYLE_TEXT_ALIGN_LEFT
== aAlignment
||
2099 (((NS_STYLE_TEXT_ALIGN_START
== aAlignment
&&
2100 NS_STYLE_DIRECTION_LTR
== aDirection
) ||
2101 (NS_STYLE_TEXT_ALIGN_END
== aAlignment
&&
2102 NS_STYLE_DIRECTION_RTL
== aDirection
)) &&
2103 !(NS_STYLE_UNICODE_BIDI_PLAINTEXT
& aUnicodeBidi
));
2106 void nsBlockFrame::PrepareResizeReflow(BlockReflowInput
& aState
) {
2107 // See if we can try and avoid marking all the lines as dirty
2108 // FIXME(emilio): This should be writing-mode aware, I guess.
2109 bool tryAndSkipLines
=
2110 // The left content-edge must be a constant distance from the left
2112 !StylePadding()->mPadding
.Get(eSideLeft
).HasPercent();
2115 if (gDisableResizeOpt
) {
2116 tryAndSkipLines
= false;
2119 if (!tryAndSkipLines
) {
2120 IndentBy(stdout
, gNoiseIndent
);
2122 printf(": marking all lines dirty: availISize=%d\n",
2123 aState
.mReflowInput
.AvailableISize());
2128 if (tryAndSkipLines
) {
2129 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2130 nscoord newAvailISize
=
2131 aState
.mReflowInput
.ComputedLogicalBorderPadding().IStart(wm
) +
2132 aState
.mReflowInput
.ComputedISize();
2136 IndentBy(stdout
, gNoiseIndent
);
2138 printf(": trying to avoid marking all lines dirty\n");
2142 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2143 line
!= line_end
; ++line
) {
2144 // We let child blocks make their own decisions the same
2146 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2147 if (line
->IsBlock() || line
->HasFloats() ||
2148 (!isLastLine
&& !line
->HasBreakAfter()) ||
2149 ((isLastLine
|| !line
->IsLineWrapped())) ||
2150 line
->ResizeReflowOptimizationDisabled() ||
2151 line
->IsImpactedByFloat() || (line
->IEnd() > newAvailISize
)) {
2155 #ifdef REALLY_NOISY_REFLOW
2156 if (!line
->IsBlock()) {
2157 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2158 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
2162 if (gNoisyReflow
&& !line
->IsDirty()) {
2163 IndentBy(stdout
, gNoiseIndent
+ 1);
2165 "skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%s/%s "
2167 static_cast<void*>(line
.get()),
2169 (line
.next() != LinesEnd() ? line
.next().get() : nullptr)),
2170 line
->IsBlock() ? "block" : "inline",
2171 line
->HasBreakAfter() ? "has-break-after " : "",
2172 line
->HasFloats() ? "has-floats " : "",
2173 line
->IsImpactedByFloat() ? "impacted " : "",
2174 line
->BreakTypeToString(line
->GetBreakTypeBefore()),
2175 line
->BreakTypeToString(line
->GetBreakTypeAfter()), line
->IEnd());
2180 // Mark everything dirty
2181 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2182 line
!= line_end
; ++line
) {
2188 //----------------------------------------
2191 * Propagate reflow "damage" from from earlier lines to the current
2192 * line. The reflow damage comes from the following sources:
2193 * 1. The regions of float damage remembered during reflow.
2194 * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
2195 * float, either the previous reflow or now.
2197 * When entering this function, |aLine| is still at its old position and
2198 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2199 * doesn't get marked dirty and reflowed entirely).
2201 void nsBlockFrame::PropagateFloatDamage(BlockReflowInput
& aState
,
2203 nscoord aDeltaBCoord
) {
2204 nsFloatManager
* floatManager
= aState
.FloatManager();
2206 (aState
.mReflowInput
.mParentReflowInput
&&
2207 aState
.mReflowInput
.mParentReflowInput
->mFloatManager
== floatManager
) ||
2208 aState
.mReflowInput
.mBlockDelta
== 0,
2209 "Bad block delta passed in");
2211 // Check to see if there are any floats; if there aren't, there can't
2212 // be any float damage
2213 if (!floatManager
->HasAnyFloats()) return;
2215 // Check the damage region recorded in the float damage.
2216 if (floatManager
->HasFloatDamage()) {
2217 // Need to check mBounds *and* mCombinedArea to find intersections
2218 // with aLine's floats
2219 nscoord lineBCoordBefore
= aLine
->BStart() + aDeltaBCoord
;
2220 nscoord lineBCoordAfter
= lineBCoordBefore
+ aLine
->BSize();
2221 // Scrollable overflow should be sufficient for things that affect
2223 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2224 nsSize containerSize
= aState
.ContainerSize();
2225 LogicalRect overflow
=
2226 aLine
->GetOverflowArea(eScrollableOverflow
, wm
, containerSize
);
2227 nscoord lineBCoordCombinedBefore
= overflow
.BStart(wm
) + aDeltaBCoord
;
2228 nscoord lineBCoordCombinedAfter
=
2229 lineBCoordCombinedBefore
+ overflow
.BSize(wm
);
2232 floatManager
->IntersectsDamage(lineBCoordBefore
, lineBCoordAfter
) ||
2233 floatManager
->IntersectsDamage(lineBCoordCombinedBefore
,
2234 lineBCoordCombinedAfter
);
2241 // Check if the line is moving relative to the float manager
2242 if (aDeltaBCoord
+ aState
.mReflowInput
.mBlockDelta
!= 0) {
2243 if (aLine
->IsBlock()) {
2244 // Unconditionally reflow sliding blocks; we only really need to reflow
2245 // if there's a float impacting this block, but the current float manager
2246 // makes it difficult to check that. Therefore, we let the child block
2247 // decide what it needs to reflow.
2250 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
2251 nsFlowAreaRect floatAvailableSpace
=
2252 aState
.GetFloatAvailableSpaceForBSize(aLine
->BStart() + aDeltaBCoord
,
2253 aLine
->BSize(), nullptr);
2255 #ifdef REALLY_NOISY_REFLOW
2256 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2257 wasImpactedByFloat
, floatAvailableSpace
.HasFloats());
2260 // Mark the line dirty if it was or is affected by a float
2261 // We actually only really need to reflow if the amount of impact
2262 // changes, but that's not straightforward to check
2263 if (wasImpactedByFloat
|| floatAvailableSpace
.HasFloats()) {
2270 static bool LineHasClear(nsLineBox
* aLine
) {
2271 return aLine
->IsBlock()
2272 ? (aLine
->GetBreakTypeBefore() != StyleClear::None
||
2273 (aLine
->mFirstChild
->GetStateBits() &
2274 NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
2275 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
2276 : aLine
->HasFloatBreakAfter();
2280 * Reparent a whole list of floats from aOldParent to this block. The
2281 * floats might be taken from aOldParent's overflow list. They will be
2282 * removed from the list. They end up appended to our mFloats list.
2284 void nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
2285 nsBlockFrame
* aOldParent
,
2286 bool aReparentSiblings
) {
2288 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
2289 if (list
.NotEmpty()) {
2290 for (nsIFrame
* f
: list
) {
2291 MOZ_ASSERT(!(f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
),
2292 "CollectFloats should've removed that bit");
2293 ReparentFrame(f
, aOldParent
, this);
2295 mFloats
.AppendFrames(nullptr, list
);
2299 static void DumpLine(const BlockReflowInput
& aState
, nsLineBox
* aLine
,
2300 nscoord aDeltaBCoord
, int32_t aDeltaIndent
) {
2302 if (nsBlockFrame::gNoisyReflow
) {
2303 nsRect
ovis(aLine
->GetVisualOverflowArea());
2304 nsRect
oscr(aLine
->GetScrollableOverflowArea());
2305 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
2307 "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2308 "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2309 "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2310 static_cast<void*>(aLine
), aState
.mBCoord
,
2311 aLine
->IsDirty() ? "yes" : "no", aLine
->IStart(), aLine
->BStart(),
2312 aLine
->ISize(), aLine
->BSize(), ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
2313 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
, aDeltaBCoord
,
2314 aState
.mPrevBEndMargin
.get(), aLine
->GetChildCount());
2319 void nsBlockFrame::ReflowDirtyLines(BlockReflowInput
& aState
) {
2320 bool keepGoing
= true;
2321 bool repositionViews
= false; // should we really need this?
2322 bool foundAnyClears
= aState
.mFloatBreakType
!= StyleClear::None
;
2323 bool willReflowAgain
= false;
2327 IndentBy(stdout
, gNoiseIndent
);
2329 printf(": reflowing dirty lines");
2330 printf(" computedISize=%d\n", aState
.mReflowInput
.ComputedISize());
2332 AutoNoisyIndenter
indent(gNoisyReflow
);
2335 bool selfDirty
= (GetStateBits() & NS_FRAME_IS_DIRTY
) ||
2336 (aState
.mReflowInput
.IsBResize() &&
2337 (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE
));
2339 // Reflow our last line if our availableBSize has increased
2340 // so that we (and our last child) pull up content as necessary
2341 if (aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2343 aState
.mReflowInput
.AvailableBSize() >
2344 GetLogicalSize().BSize(aState
.mReflowInput
.GetWritingMode())) {
2345 LineIterator lastLine
= LinesEnd();
2346 if (lastLine
!= LinesBegin()) {
2348 lastLine
->MarkDirty();
2351 // the amount by which we will slide the current line if it is not
2353 nscoord deltaBCoord
= 0;
2355 // whether we did NOT reflow the previous line and thus we need to
2356 // recompute the carried out margin before the line if we want to
2357 // reflow it or if its previous margin is dirty
2358 bool needToRecoverState
= false;
2359 // Float continuations were reflowed in ReflowPushedFloats
2360 bool reflowedFloat
=
2361 mFloats
.NotEmpty() &&
2362 (mFloats
.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
);
2363 bool lastLineMovedUp
= false;
2364 // We save up information about BR-clearance here
2365 StyleClear inlineFloatBreakType
= aState
.mFloatBreakType
;
2367 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2369 // Reflow the lines that are already ours
2370 for (; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
2371 DumpLine(aState
, line
, deltaBCoord
, 0);
2373 AutoNoisyIndenter
indent2(gNoisyReflow
);
2376 if (selfDirty
) line
->MarkDirty();
2378 // This really sucks, but we have to look inside any blocks that have clear
2379 // elements inside them.
2380 // XXX what can we do smarter here?
2381 if (!line
->IsDirty() && line
->IsBlock() &&
2382 (line
->mFirstChild
->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
2386 nsIFrame
* replacedBlock
= nullptr;
2387 if (line
->IsBlock() &&
2388 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
2389 replacedBlock
= line
->mFirstChild
;
2392 // We have to reflow the line if it's a block whose clearance
2393 // might have changed, so detect that.
2394 if (!line
->IsDirty() &&
2395 (line
->GetBreakTypeBefore() != StyleClear::None
|| replacedBlock
)) {
2396 nscoord curBCoord
= aState
.mBCoord
;
2397 // See where we would be after applying any clearance due to
2399 if (inlineFloatBreakType
!= StyleClear::None
) {
2400 curBCoord
= aState
.ClearFloats(curBCoord
, inlineFloatBreakType
);
2403 nscoord newBCoord
= aState
.ClearFloats(
2404 curBCoord
, line
->GetBreakTypeBefore(), replacedBlock
);
2406 if (line
->HasClearance()) {
2407 // Reflow the line if it might not have clearance anymore.
2408 if (newBCoord
== curBCoord
2409 // aState.mBCoord is the clearance point which should be the
2410 // block-start border-edge of the block frame. If sliding the
2411 // block by deltaBCoord isn't going to put it in the predicted
2412 // position, then we'd better reflow the line.
2413 || newBCoord
!= line
->BStart() + deltaBCoord
) {
2417 // Reflow the line if the line might have clearance now.
2418 if (curBCoord
!= newBCoord
) {
2424 // We might have to reflow a line that is after a clearing BR.
2425 if (inlineFloatBreakType
!= StyleClear::None
) {
2426 aState
.mBCoord
= aState
.ClearFloats(aState
.mBCoord
, inlineFloatBreakType
);
2427 if (aState
.mBCoord
!= line
->BStart() + deltaBCoord
) {
2428 // SlideLine is not going to put the line where the clearance
2429 // put it. Reflow the line to be sure.
2432 inlineFloatBreakType
= StyleClear::None
;
2435 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
2436 if (previousMarginWasDirty
) {
2437 // If the previous margin is dirty, reflow the current line
2439 line
->ClearPreviousMarginDirty();
2440 } else if (line
->BEnd() + deltaBCoord
> aState
.mBEndEdge
) {
2441 // Lines that aren't dirty but get slid past our height constraint must
2446 // If we have a constrained height (i.e., breaking columns/pages),
2447 // and the distance to the bottom might have changed, then we need
2448 // to reflow any line that might have floats in it, both because the
2449 // breakpoints within those floats may have changed and because we
2450 // might have to push/pull the floats in their entirety.
2451 // FIXME: What about a deltaBCoord or block-size change that forces us to
2452 // push lines? Why does that work?
2453 if (!line
->IsDirty() &&
2454 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2455 (deltaBCoord
!= 0 || aState
.mReflowInput
.IsBResize() ||
2456 aState
.mReflowInput
.mFlags
.mMustReflowPlaceholders
) &&
2457 (line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed())) {
2461 if (!line
->IsDirty()) {
2462 // See if there's any reflow damage that requires that we mark the
2464 PropagateFloatDamage(aState
, line
, deltaBCoord
);
2467 // If the container size has changed, reset mContainerSize. If the
2468 // line's writing mode is not ltr, or if the line is not left-aligned, also
2469 // mark the line dirty.
2470 if (aState
.ContainerSize() != line
->mContainerSize
) {
2471 line
->mContainerSize
= aState
.ContainerSize();
2473 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow() &&
2474 NS_STYLE_TEXT_ALIGN_AUTO
== StyleText()->mTextAlignLast
;
2476 isLastLine
? StyleText()->mTextAlign
: StyleText()->mTextAlignLast
;
2478 if (line
->mWritingMode
.IsVertical() || !line
->mWritingMode
.IsBidiLTR() ||
2479 !IsAlignedLeft(align
,
2480 aState
.mReflowInput
.mStyleVisibility
->mDirection
,
2481 StyleTextReset()->mUnicodeBidi
, this)) {
2486 if (needToRecoverState
&& line
->IsDirty()) {
2487 // We need to reconstruct the block-end margin only if we didn't
2488 // reflow the previous line and we do need to reflow (or repair
2489 // the block-start position of) the next line.
2490 aState
.ReconstructMarginBefore(line
);
2493 bool reflowedPrevLine
= !needToRecoverState
;
2494 if (needToRecoverState
) {
2495 needToRecoverState
= false;
2497 // Update aState.mPrevChild as if we had reflowed all of the frames in
2499 if (line
->IsDirty()) {
2501 line
->mFirstChild
->GetPrevSibling() == line
.prev()->LastChild(),
2502 "unexpected line frames");
2503 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
2507 // Now repair the line and update |aState.mBCoord| by calling
2508 // |ReflowLine| or |SlideLine|.
2509 // If we're going to reflow everything again, then no need to reflow
2510 // the dirty line ... unless the line has floats, in which case we'd
2511 // better reflow it now to refresh its float cache, which may contain
2512 // dangling frame pointers! Ugh! This reflow of the line may be
2513 // incorrect because we skipped reflowing previous lines (e.g., floats
2514 // may be placed incorrectly), but that's OK because we'll mark the
2515 // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
2516 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
2517 lastLineMovedUp
= true;
2519 bool maybeReflowingForFirstTime
=
2520 line
->IStart() == 0 && line
->BStart() == 0 && line
->ISize() == 0 &&
2523 // Compute the dirty lines "before" BEnd, after factoring in
2524 // the running deltaBCoord value - the running value is implicit in
2526 nscoord oldB
= line
->BStart();
2527 nscoord oldBMost
= line
->BEnd();
2529 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
2530 "Don't reflow blocks while willReflowAgain is true, reflow "
2531 "of block abs-pos children depends on this");
2533 // Reflow the dirty line. If it's an incremental reflow, then force
2534 // it to invalidate the dirty area if necessary
2535 ReflowLine(aState
, line
, &keepGoing
);
2537 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
2539 willReflowAgain
= true;
2540 // Note that once we've entered this state, every line that gets here
2541 // (e.g. because it has floats) gets marked dirty and reflowed again.
2542 // in the next pass. This is important, see above.
2545 if (line
->HasFloats()) {
2546 reflowedFloat
= true;
2550 DumpLine(aState
, line
, deltaBCoord
, -1);
2551 if (0 == line
->GetChildCount()) {
2552 DeleteLine(aState
, line
, line_end
);
2557 // Test to see whether the margin that should be carried out
2558 // to the next line (NL) might have changed. In ReflowBlockFrame
2559 // we call nextLine->MarkPreviousMarginDirty if the block's
2560 // actual carried-out block-end margin changed. So here we only
2561 // need to worry about the following effects:
2562 // 1) the line was just created, and it might now be blocking
2563 // a carried-out block-end margin from previous lines that
2564 // used to reach NL from reaching NL
2565 // 2) the line used to be empty, and is now not empty,
2566 // thus blocking a carried-out block-end margin from previous lines
2567 // that used to reach NL from reaching NL
2568 // 3) the line wasn't empty, but now is, so a carried-out
2569 // block-end margin from previous lines that didn't used to reach NL
2571 // 4) the line might have changed in a way that affects NL's
2572 // ShouldApplyBStartMargin decision. The three things that matter
2573 // are the line's emptiness, its adjacency to the block-start edge of the
2574 // block, and whether it has clearance (the latter only matters if the
2575 // block was and is adjacent to the block-start and empty).
2577 // If the line is empty now, we can't reliably tell if the line was empty
2578 // before, so we just assume it was and do
2579 // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
2580 // redundant; if the line is empty now we don't need to check 4), but if
2581 // the line is not empty now and we're sure it wasn't empty before, any
2582 // adjacency and clearance changes are irrelevant to the result of
2583 // nextLine->ShouldApplyBStartMargin.
2584 if (line
.next() != LinesEnd()) {
2585 bool maybeWasEmpty
= oldB
== line
.next()->BStart();
2586 bool isEmpty
= line
->CachedIsEmpty();
2587 if (maybeReflowingForFirstTime
/*1*/ ||
2588 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
2589 line
.next()->MarkPreviousMarginDirty();
2590 // since it's marked dirty, nobody will care about |deltaBCoord|
2594 // If the line was just reflowed for the first time, then its
2595 // old mBounds cannot be trusted so this deltaBCoord computation is
2596 // bogus. But that's OK because we just did
2597 // MarkPreviousMarginDirty on the next line which will force it
2598 // to be reflowed, so this computation of deltaBCoord will not be
2600 deltaBCoord
= line
->BEnd() - oldBMost
;
2602 // Now do an interrupt check. We want to do this only in the case when we
2603 // actually reflow the line, so that if we get back in here we'll get
2604 // further on the reflow before interrupting.
2605 aState
.mPresContext
->CheckForInterrupt(this);
2607 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
2608 // Nop except for blocks (we don't create overflow container
2609 // continuations for any inlines atm), so only checking mFirstChild
2612 lastLineMovedUp
= deltaBCoord
< 0;
2614 if (deltaBCoord
!= 0)
2615 SlideLine(aState
, line
, deltaBCoord
);
2617 repositionViews
= true;
2619 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
2620 "Possibly stale float cache here!");
2621 if (willReflowAgain
&& line
->IsBlock()) {
2622 // If we're going to reflow everything again, and this line is a block,
2623 // then there is no need to recover float state. The line may contain
2624 // other lines with floats, but in that case RecoverStateFrom would only
2625 // add floats to the float manager. We don't need to do that because
2626 // everything's going to get reflowed again "for real". Calling
2627 // RecoverStateFrom in this situation could be lethal because the
2628 // block's descendant lines may have float caches containing dangling
2629 // frame pointers. Ugh!
2630 // If this line is inline, then we need to recover its state now
2631 // to make sure that we don't forget to move its floats by deltaBCoord.
2633 // XXX EVIL O(N^2) EVIL
2634 aState
.RecoverStateFrom(line
, deltaBCoord
);
2637 // Keep mBCoord up to date in case we're propagating reflow damage
2638 // and also because our final height may depend on it. If the
2639 // line is inlines, then only update mBCoord if the line is not
2640 // empty, because that's what PlaceLine does. (Empty blocks may
2641 // want to update mBCoord, e.g. if they have clearance.)
2642 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
2643 aState
.mBCoord
= line
->BEnd();
2646 needToRecoverState
= true;
2648 if (reflowedPrevLine
&& !line
->IsBlock() &&
2649 aState
.mPresContext
->HasPendingInterrupt()) {
2650 // Need to make sure to pull overflows from any prev-in-flows
2651 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
2652 inlineKid
= inlineKid
->PrincipalChildList().FirstChild()) {
2653 inlineKid
->PullOverflowsFromPrevInFlow();
2658 // Record if we need to clear floats before reflowing the next
2659 // line. Note that inlineFloatBreakType will be handled and
2660 // cleared before the next line is processed, so there is no
2661 // need to combine break types here.
2662 if (line
->HasFloatBreakAfter()) {
2663 inlineFloatBreakType
= line
->GetBreakTypeAfter();
2666 if (LineHasClear(line
.get())) {
2667 foundAnyClears
= true;
2670 DumpLine(aState
, line
, deltaBCoord
, -1);
2672 if (aState
.mPresContext
->HasPendingInterrupt()) {
2673 willReflowAgain
= true;
2674 // Another option here might be to leave |line| clean if
2675 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
2676 // that case the line really did reflow as it should have. Not sure
2677 // whether that would be safe, so doing this for now instead. Also not
2678 // sure whether we really want to mark all lines dirty after an
2679 // interrupt, but until we get better at propagating float damage we
2680 // really do need to do it this way; see comments inside MarkLineDirty.
2681 MarkLineDirtyForInterrupt(line
);
2685 // Handle BR-clearance from the last line of the block
2686 if (inlineFloatBreakType
!= StyleClear::None
) {
2687 aState
.mBCoord
= aState
.ClearFloats(aState
.mBCoord
, inlineFloatBreakType
);
2690 if (needToRecoverState
) {
2691 // Is this expensive?
2692 aState
.ReconstructMarginBefore(line
);
2694 // Update aState.mPrevChild as if we had reflowed all of the frames in
2696 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
2697 line
.prev()->LastChild(),
2698 "unexpected line frames");
2699 aState
.mPrevChild
= line
== line_end
? mFrames
.LastChild()
2700 : line
->mFirstChild
->GetPrevSibling();
2703 // Should we really have to do this?
2704 if (repositionViews
) nsContainerFrame::PlaceFrameView(this);
2706 // We can skip trying to pull up the next line if our height is constrained
2707 // (so we can report being incomplete) and there is no next in flow or we
2708 // were told not to or we know it will be futile, i.e.,
2709 // -- the next in flow is not changing
2710 // -- and we cannot have added more space for its first line to be
2712 // -- it's an incremental reflow of a descendant
2713 // -- and we didn't reflow any floats (so the available space
2715 // -- my chain of next-in-flows either has no first line, or its first
2716 // line isn't dirty.
2717 bool heightConstrained
=
2718 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
;
2719 bool skipPull
= willReflowAgain
&& heightConstrained
;
2720 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
2721 (aState
.mReflowInput
.mFlags
.mNextInFlowUntouched
&& !lastLineMovedUp
&&
2722 !(GetStateBits() & NS_FRAME_IS_DIRTY
) && !reflowedFloat
)) {
2723 // We'll place lineIter at the last line of this block, so that
2724 // nsBlockInFlowLineIterator::Next() will take us to the first
2725 // line of my next-in-flow-chain. (But first, check that I
2726 // have any lines -- if I don't, just bail out of this
2728 LineIterator lineIter
= this->LinesEnd();
2729 if (lineIter
!= this->LinesBegin()) {
2730 lineIter
--; // I have lines; step back from dummy iterator to last line.
2731 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
2733 // Check for next-in-flow-chain's first line.
2734 // (First, see if there is such a line, and second, see if it's clean)
2735 if (!bifLineIter
.Next() || !bifLineIter
.GetLine()->IsDirty()) {
2741 if (skipPull
&& aState
.mNextInFlow
) {
2742 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
2743 if (IS_TRUE_OVERFLOW_CONTAINER(aState
.mNextInFlow
))
2744 aState
.mReflowStatus
.SetOverflowIncomplete();
2746 aState
.mReflowStatus
.SetIncomplete();
2749 if (!skipPull
&& aState
.mNextInFlow
) {
2750 // Pull data from a next-in-flow if there's still room for more
2752 while (keepGoing
&& aState
.mNextInFlow
) {
2753 // Grab first line from our next-in-flow
2754 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
2755 nsLineBox
* pulledLine
;
2756 nsFrameList pulledFrames
;
2757 if (!nextInFlow
->mLines
.empty()) {
2758 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
, &pulledLine
,
2761 // Grab an overflow line if there are any
2762 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
2763 if (!overflowLines
) {
2764 aState
.mNextInFlow
=
2765 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
2769 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
2770 &pulledLine
, &pulledFrames
);
2772 nextInFlow
->DestroyOverflowLines();
2776 if (pulledFrames
.IsEmpty()) {
2777 // The line is empty. Try the next one.
2779 pulledLine
->GetChildCount() == 0 && !pulledLine
->mFirstChild
,
2781 nextInFlow
->FreeLineBox(pulledLine
);
2785 if (pulledLine
== nextInFlow
->GetLineCursor()) {
2786 nextInFlow
->ClearLineCursor();
2788 ReparentFrames(pulledFrames
, nextInFlow
, this);
2790 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
2791 "Unexpected last frame");
2792 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(),
2793 "should have a prevchild here");
2794 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
2795 "Incorrect aState.mPrevChild before inserting line at end");
2797 // Shift pulledLine's frames into our mFrames list.
2798 mFrames
.AppendFrames(nullptr, pulledFrames
);
2800 // Add line to our line list, and set its last child as our new prev-child
2801 line
= mLines
.before_insert(LinesEnd(), pulledLine
);
2802 aState
.mPrevChild
= mFrames
.LastChild();
2804 // Reparent floats whose placeholders are in the line.
2805 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
2807 DumpLine(aState
, pulledLine
, deltaBCoord
, 0);
2809 AutoNoisyIndenter
indent2(gNoisyReflow
);
2812 if (aState
.mPresContext
->HasPendingInterrupt()) {
2813 MarkLineDirtyForInterrupt(line
);
2815 // Now reflow it and any lines that it makes during it's reflow
2816 // (we have to loop here because reflowing the line may cause a new
2817 // line to be created; see SplitLine's callers for examples of
2818 // when this happens).
2819 while (line
!= LinesEnd()) {
2820 ReflowLine(aState
, line
, &keepGoing
);
2822 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
2825 aState
.mReflowStatus
.SetIncomplete();
2829 DumpLine(aState
, line
, deltaBCoord
, -1);
2831 if (0 == line
->GetChildCount()) {
2832 DeleteLine(aState
, line
, line_end
);
2837 if (LineHasClear(line
.get())) {
2838 foundAnyClears
= true;
2841 if (aState
.mPresContext
->CheckForInterrupt(this)) {
2842 MarkLineDirtyForInterrupt(line
);
2846 // If this is an inline frame then its time to stop
2848 aState
.AdvanceToNextLine();
2853 if (aState
.mReflowStatus
.IsIncomplete()) {
2854 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
2855 } // XXXfr shouldn't set this flag when nextinflow has no lines
2858 // Handle an odd-ball case: a list-item with no lines
2859 if (HasOutsideMarker() && mLines
.empty()) {
2860 ReflowOutput
metrics(aState
.mReflowInput
);
2861 nsIFrame
* marker
= GetOutsideMarker();
2862 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2863 ReflowOutsideMarker(
2864 marker
, aState
, metrics
,
2865 aState
.mReflowInput
.ComputedPhysicalBorderPadding().top
);
2866 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
2867 "empty ::marker frame took up space");
2869 if (!MarkerIsEmpty()) {
2870 // There are no lines so we have to fake up some y motion so that
2871 // we end up with *some* height.
2872 // (Note: if we're layout-contained, we have to be sure to leave our
2873 // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
2874 // because layout-contained frames have no baseline.)
2875 if (!aState
.mReflowInput
.mStyleDisplay
->IsContainLayout() &&
2876 metrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
2878 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2879 if (nsLayoutUtils::GetFirstLineBaseline(wm
, marker
, &ascent
)) {
2880 metrics
.SetBlockStartAscent(ascent
);
2882 metrics
.SetBlockStartAscent(metrics
.BSize(wm
));
2886 RefPtr
<nsFontMetrics
> fm
=
2887 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
2889 nscoord minAscent
= nsLayoutUtils::GetCenteredFontBaseline(
2890 fm
, aState
.mMinLineHeight
, wm
.IsLineInverted());
2891 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
2894 std::max(minAscent
, metrics
.BlockStartAscent()) +
2895 std::max(minDescent
, metrics
.BSize(wm
) - metrics
.BlockStartAscent());
2897 nscoord offset
= minAscent
- metrics
.BlockStartAscent();
2899 marker
->SetRect(marker
->GetRect() + nsPoint(0, offset
));
2904 if (foundAnyClears
) {
2905 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
2907 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
2912 VerifyOverflowSituation();
2914 IndentBy(stdout
, gNoiseIndent
- 1);
2916 printf(": done reflowing dirty lines (status=%s)\n",
2917 ToString(aState
.mReflowStatus
).c_str());
2922 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
) {
2925 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
2926 // marked the lines that need to be marked dirty based on our
2927 // vertical resize stuff. So we'll definitely reflow all those kids;
2928 // the only question is how they should behave.
2929 if (GetStateBits() & NS_FRAME_IS_DIRTY
) {
2930 // Mark all our child frames dirty so we make sure to reflow them
2932 int32_t n
= aLine
->GetChildCount();
2933 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
2934 f
= f
->GetNextSibling(), --n
) {
2935 f
->MarkSubtreeDirty();
2937 // And mark all the floats whose reflows we might be skipping dirty too.
2938 if (aLine
->HasFloats()) {
2939 for (nsFloatCache
* fc
= aLine
->GetFirstFloat(); fc
; fc
= fc
->Next()) {
2940 fc
->mFloat
->MarkSubtreeDirty();
2944 // Dirty all the descendant lines of block kids to handle float damage,
2945 // since our nsFloatManager will go away by the next time we're reflowing.
2946 // XXXbz Can we do something more like what PropagateFloatDamage does?
2947 // Would need to sort out the exact business with mBlockDelta for that....
2948 // This marks way too much dirty. If we ever make this better, revisit
2949 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
2950 nsBlockFrame
* bf
= do_QueryFrame(aLine
->mFirstChild
);
2952 MarkAllDescendantLinesDirty(bf
);
2957 void nsBlockFrame::DeleteLine(BlockReflowInput
& aState
,
2958 nsLineList::iterator aLine
,
2959 nsLineList::iterator aLineEnd
) {
2960 MOZ_ASSERT(0 == aLine
->GetChildCount(), "can't delete !empty line");
2961 if (0 == aLine
->GetChildCount()) {
2962 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
2963 "using function more generally than designed, "
2964 "but perhaps OK now");
2965 nsLineBox
* line
= aLine
;
2966 aLine
= mLines
.erase(aLine
);
2968 // Mark the previous margin of the next line dirty since we need to
2969 // recompute its top position.
2970 if (aLine
!= aLineEnd
) aLine
->MarkPreviousMarginDirty();
2975 * Reflow a line. The line will either contain a single block frame
2976 * or contain 1 or more inline frames. aKeepReflowGoing indicates
2977 * whether or not the caller should continue to reflow more lines.
2979 void nsBlockFrame::ReflowLine(BlockReflowInput
& aState
, LineIterator aLine
,
2980 bool* aKeepReflowGoing
) {
2981 MOZ_ASSERT(aLine
->GetChildCount(), "reflowing empty line");
2983 // Setup the line-layout for the new line
2984 aState
.mCurrentLine
= aLine
;
2985 aLine
->ClearDirty();
2986 aLine
->InvalidateCachedIsEmpty();
2987 aLine
->ClearHadFloatPushed();
2989 // Now that we know what kind of line we have, reflow it
2990 if (aLine
->IsBlock()) {
2991 ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
2993 aLine
->SetLineWrapped(false);
2994 ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
2996 // Store the line's float edges for overflow marker analysis if needed.
2997 aLine
->ClearFloatEdges();
2998 if (aState
.mFlags
.mCanHaveOverflowMarkers
) {
2999 WritingMode wm
= aLine
->mWritingMode
;
3000 nsFlowAreaRect r
= aState
.GetFloatAvailableSpaceForBSize(
3001 aLine
->BStart(), aLine
->BSize(), nullptr);
3002 if (r
.HasFloats()) {
3003 LogicalRect so
= aLine
->GetOverflowArea(eScrollableOverflow
, wm
,
3004 aLine
->mContainerSize
);
3005 nscoord s
= r
.mRect
.IStart(wm
);
3006 nscoord e
= r
.mRect
.IEnd(wm
);
3007 if (so
.IEnd(wm
) > e
|| so
.IStart(wm
) < s
) {
3008 // This line is overlapping a float - store the edges marking the area
3009 // between the floats for text-overflow analysis.
3010 aLine
->SetFloatEdges(s
, e
);
3017 nsIFrame
* nsBlockFrame::PullFrame(BlockReflowInput
& aState
,
3018 LineIterator aLine
) {
3019 // First check our remaining lines.
3020 if (LinesEnd() != aLine
.next()) {
3021 return PullFrameFrom(aLine
, this, aLine
.next());
3025 !GetOverflowLines(),
3026 "Our overflow lines should have been removed at the start of reflow");
3028 // Try each next-in-flow.
3029 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3030 while (nextInFlow
) {
3031 if (nextInFlow
->mLines
.empty()) {
3032 nextInFlow
->DrainSelfOverflowList();
3034 if (!nextInFlow
->mLines
.empty()) {
3035 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
3037 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3038 aState
.mNextInFlow
= nextInFlow
;
3044 nsIFrame
* nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
3045 nsBlockFrame
* aFromContainer
,
3046 nsLineList::iterator aFromLine
) {
3047 nsLineBox
* fromLine
= aFromLine
;
3048 MOZ_ASSERT(fromLine
, "bad line to pull from");
3049 MOZ_ASSERT(fromLine
->GetChildCount(), "empty line");
3050 MOZ_ASSERT(aLine
->GetChildCount(), "empty line");
3052 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
3053 "Disagreement about whether it's a block or not");
3055 if (fromLine
->IsBlock()) {
3056 // If our line is not empty and the child in aFromLine is a block
3057 // then we cannot pull up the frame into this line. In this case
3061 // Take frame from fromLine
3062 nsIFrame
* frame
= fromLine
->mFirstChild
;
3063 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
3065 if (aFromContainer
!= this) {
3066 // The frame is being pulled from a next-in-flow; therefore we
3067 // need to add it to our sibling list.
3068 MOZ_ASSERT(aLine
== mLines
.back());
3069 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
3070 "should only pull from first line");
3071 aFromContainer
->mFrames
.RemoveFrame(frame
);
3073 // When pushing and pulling frames we need to check for whether any
3074 // views need to be reparented.
3075 ReparentFrame(frame
, aFromContainer
, this);
3076 mFrames
.AppendFrame(nullptr, frame
);
3078 // The frame might have (or contain) floats that need to be brought
3079 // over too. (pass 'false' since there are no siblings to check)
3080 ReparentFloats(frame
, aFromContainer
, false);
3082 MOZ_ASSERT(aLine
== aFromLine
.prev());
3085 aLine
->NoteFrameAdded(frame
);
3086 fromLine
->NoteFrameRemoved(frame
);
3088 if (fromLine
->GetChildCount() > 0) {
3089 // Mark line dirty now that we pulled a child
3090 fromLine
->MarkDirty();
3091 fromLine
->mFirstChild
= newFirstChild
;
3093 // Free up the fromLine now that it's empty.
3094 // Its bounds might need to be redrawn, though.
3095 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
3096 aFromLine
.next()->MarkPreviousMarginDirty();
3098 aFromContainer
->mLines
.erase(aFromLine
);
3099 // aFromLine is now invalid
3100 aFromContainer
->FreeLineBox(fromLine
);
3105 VerifyOverflowSituation();
3111 void nsBlockFrame::SlideLine(BlockReflowInput
& aState
, nsLineBox
* aLine
,
3112 nscoord aDeltaBCoord
) {
3113 MOZ_ASSERT(aDeltaBCoord
!= 0, "why slide a line nowhere?");
3115 // Adjust line state
3116 aLine
->SlideBy(aDeltaBCoord
, aState
.ContainerSize());
3118 // Adjust the frames in the line
3119 MoveChildFramesOfLine(aLine
, aDeltaBCoord
);
3122 void nsBlockFrame::UpdateLineContainerSize(nsLineBox
* aLine
,
3123 const nsSize
& aNewContainerSize
) {
3124 if (aNewContainerSize
== aLine
->mContainerSize
) {
3128 // Adjust line state
3129 nsSize sizeDelta
= aLine
->UpdateContainerSize(aNewContainerSize
);
3131 // Changing container width only matters if writing mode is vertical-rl
3132 if (GetWritingMode().IsVerticalRL()) {
3133 MoveChildFramesOfLine(aLine
, sizeDelta
.width
);
3137 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox
* aLine
,
3138 nscoord aDeltaBCoord
) {
3139 // Adjust the frames in the line
3140 nsIFrame
* kid
= aLine
->mFirstChild
;
3145 WritingMode wm
= GetWritingMode();
3146 LogicalPoint
translation(wm
, 0, aDeltaBCoord
);
3148 if (aLine
->IsBlock()) {
3150 kid
->MovePositionBy(wm
, translation
);
3153 // Make sure the frame's view and any child views are updated
3154 nsContainerFrame::PlaceFrameView(kid
);
3156 // Adjust the block-dir coordinate of the frames in the line.
3157 // Note: we need to re-position views even if aDeltaBCoord is 0, because
3158 // one of our parent frames may have moved and so the view's position
3159 // relative to its parent may have changed.
3160 int32_t n
= aLine
->GetChildCount();
3163 kid
->MovePositionBy(wm
, translation
);
3165 // Make sure the frame's view and any child views are updated
3166 nsContainerFrame::PlaceFrameView(kid
);
3167 kid
= kid
->GetNextSibling();
3172 static inline bool IsNonAutoNonZeroBSize(const StyleSize
& aCoord
) {
3173 // The "extremum length" values (see ExtremumLength) were originally aimed at
3174 // inline-size (or width, as it was before logicalization). For now, let them
3175 // return false here, so we treat them like 'auto' pending a real
3176 // implementation. (See bug 1126420.)
3178 // FIXME (bug 567039, bug 527285)
3179 // This isn't correct for the 'fill' value, which should more
3180 // likely (but not necessarily, depending on the available space)
3181 // be returning true.
3182 if (aCoord
.IsAuto() || aCoord
.IsExtremumLength()) {
3185 if (aCoord
.IsLengthPercentage()) {
3186 // If we evaluate the length/percent/calc at a percentage basis of
3187 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3188 // length, percent, or combination thereof. Test > 0 so we clamp
3189 // negative calc() results to 0.
3190 return aCoord
.AsLengthPercentage().Resolve(nscoord_MAX
) > 0 ||
3191 aCoord
.AsLengthPercentage().Resolve(0) > 0;
3193 MOZ_ASSERT(false, "unexpected unit for height or min-height");
3198 bool nsBlockFrame::IsSelfEmpty() {
3199 // Blocks which are margin-roots (including inline-blocks) cannot be treated
3200 // as empty for margin-collapsing and other purposes. They're more like
3201 // replaced elements.
3202 if (GetStateBits() & NS_BLOCK_MARGIN_ROOT
) {
3206 WritingMode wm
= GetWritingMode();
3207 const nsStylePosition
* position
= StylePosition();
3209 if (IsNonAutoNonZeroBSize(position
->MinBSize(wm
)) ||
3210 IsNonAutoNonZeroBSize(position
->BSize(wm
))) {
3214 const nsStyleBorder
* border
= StyleBorder();
3215 const nsStylePadding
* padding
= StylePadding();
3217 if (border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBStart
)) !=
3219 border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBEnd
)) != 0 ||
3220 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBStart(wm
)) ||
3221 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBEnd(wm
))) {
3225 if (HasOutsideMarker() && !MarkerIsEmpty()) {
3232 bool nsBlockFrame::CachedIsEmpty() {
3233 if (!IsSelfEmpty()) {
3237 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
3238 line
!= line_end
; ++line
) {
3239 if (!line
->CachedIsEmpty()) return false;
3245 bool nsBlockFrame::IsEmpty() {
3246 if (!IsSelfEmpty()) {
3250 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
3251 line
!= line_end
; ++line
) {
3252 if (!line
->IsEmpty()) return false;
3258 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowInput
& aState
,
3260 nsIFrame
* aChildFrame
) {
3261 if (aState
.mFlags
.mShouldApplyBStartMargin
) {
3262 // Apply short-circuit check to avoid searching the line list
3266 if (!aState
.IsAdjacentWithTop() ||
3267 aChildFrame
->StyleBorder()->mBoxDecorationBreak
==
3268 StyleBoxDecorationBreak::Clone
) {
3269 // If we aren't at the start block-coordinate then something of non-zero
3270 // height must have been placed. Therefore the childs block-start margin
3272 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3276 // Determine if this line is "essentially" the first line
3277 LineIterator line
= LinesBegin();
3278 if (aState
.mFlags
.mHasLineAdjacentToTop
) {
3279 line
= aState
.mLineAdjacentToTop
;
3281 while (line
!= aLine
) {
3282 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
3283 // A line which precedes aLine is non-empty, or has clearance,
3284 // so therefore the block-start margin applies.
3285 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3288 // No need to apply the block-start margin if the line has floats. We
3289 // should collapse anyway (bug 44419)
3291 aState
.mFlags
.mHasLineAdjacentToTop
= true;
3292 aState
.mLineAdjacentToTop
= line
;
3295 // The line being reflowed is "essentially" the first line in the
3296 // block. Therefore its block-start margin will be collapsed by the
3297 // generational collapsing logic with its parent (us).
3301 void nsBlockFrame::ReflowBlockFrame(BlockReflowInput
& aState
,
3303 bool* aKeepReflowGoing
) {
3304 MOZ_ASSERT(*aKeepReflowGoing
, "bad caller");
3306 nsIFrame
* frame
= aLine
->mFirstChild
;
3308 NS_ASSERTION(false, "program error - unexpected empty line");
3312 // Prepare the block reflow engine
3313 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
3315 StyleClear breakType
= frame
->StyleDisplay()->mBreakType
;
3316 if (StyleClear::None
!= aState
.mFloatBreakType
) {
3318 nsLayoutUtils::CombineBreakType(breakType
, aState
.mFloatBreakType
);
3319 aState
.mFloatBreakType
= StyleClear::None
;
3322 // Clear past floats before the block if the clear style is not none
3323 aLine
->SetBreakTypeBefore(breakType
);
3325 // See if we should apply the block-start margin. If the block frame being
3326 // reflowed is a continuation (non-null prev-in-flow) then we don't
3327 // apply its block-start margin because it's not significant unless it has
3328 // 'box-decoration-break:clone'. Otherwise, dig deeper.
3329 bool applyBStartMargin
= (frame
->StyleBorder()->mBoxDecorationBreak
==
3330 StyleBoxDecorationBreak::Clone
||
3331 !frame
->GetPrevInFlow()) &&
3332 ShouldApplyBStartMargin(aState
, aLine
, frame
);
3333 if (applyBStartMargin
) {
3334 // The HasClearance setting is only valid if ShouldApplyBStartMargin
3335 // returned false (in which case the block-start margin-root set our
3336 // clearance flag). Otherwise clear it now. We'll set it later on
3337 // ourselves if necessary.
3338 aLine
->ClearHasClearance();
3340 bool treatWithClearance
= aLine
->HasClearance();
3342 bool mightClearFloats
= breakType
!= StyleClear::None
;
3343 nsIFrame
* replacedBlock
= nullptr;
3344 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
3345 mightClearFloats
= true;
3346 replacedBlock
= frame
;
3349 // If our block-start margin was counted as part of some parent's block-start
3350 // margin collapse, and we are being speculatively reflowed assuming this
3351 // frame DID NOT need clearance, then we need to check that
3353 if (!treatWithClearance
&& !applyBStartMargin
&& mightClearFloats
&&
3354 aState
.mReflowInput
.mDiscoveredClearance
) {
3355 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
3356 nscoord clearBCoord
=
3357 aState
.ClearFloats(curBCoord
, breakType
, replacedBlock
);
3358 if (clearBCoord
!= curBCoord
) {
3359 // Only record the first frame that requires clearance
3360 if (!*aState
.mReflowInput
.mDiscoveredClearance
) {
3361 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
3363 aState
.mPrevChild
= frame
;
3364 // Exactly what we do now is flexible since we'll definitely be
3369 if (treatWithClearance
) {
3370 applyBStartMargin
= true;
3373 nsIFrame
* clearanceFrame
= nullptr;
3374 nscoord startingBCoord
= aState
.mBCoord
;
3375 nsCollapsingMargin incomingMargin
= aState
.mPrevBEndMargin
;
3377 // Save the original position of the frame so that we can reposition
3378 // its view as needed.
3379 nsPoint originalPosition
= frame
->GetPosition();
3382 nscoord bStartMargin
= 0;
3383 bool mayNeedRetry
= false;
3384 bool clearedFloats
= false;
3385 if (applyBStartMargin
) {
3386 // Precompute the blocks block-start margin value so that we can get the
3387 // correct available space (there might be a float that's
3388 // already been placed below the aState.mPrevBEndMargin
3390 // Setup a reflowInput to get the style computed block-start margin
3391 // value. We'll use a reason of `resize' so that we don't fudge
3392 // any incremental reflow input.
3394 // The availSpace here is irrelevant to our needs - all we want
3395 // out if this setup is the block-start margin value which doesn't depend
3396 // on the childs available space.
3397 // XXX building a complete ReflowInput just to get the block-start
3398 // margin seems like a waste. And we do this for almost every block!
3399 WritingMode wm
= frame
->GetWritingMode();
3400 LogicalSize availSpace
= aState
.ContentSize(wm
);
3401 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, frame
,
3404 if (treatWithClearance
) {
3405 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
3406 aState
.mPrevBEndMargin
.Zero();
3409 // Now compute the collapsed margin-block-start value into
3410 // aState.mPrevBEndMargin, assuming that all child margins
3411 // collapse down to clearanceFrame.
3412 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
3413 clearanceFrame
, &mayNeedRetry
);
3415 // XXX optimization; we could check the collapsing children to see if they
3416 // are sure to require clearance, and so avoid retrying them
3418 if (clearanceFrame
) {
3419 // Don't allow retries on the second pass. The clearance decisions for
3420 // the blocks whose block-start margins collapse with ours are now
3422 mayNeedRetry
= false;
3425 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
3426 // We don't know if we need clearance and this is the first,
3427 // optimistic pass. So determine whether *this block* needs
3428 // clearance. Note that we do not allow the decision for whether
3429 // this block has clearance to change on the second pass; that
3430 // decision is only allowed to be made under the optimistic
3432 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
3433 nscoord clearBCoord
=
3434 aState
.ClearFloats(curBCoord
, breakType
, replacedBlock
);
3435 if (clearBCoord
!= curBCoord
) {
3436 // Looks like we need clearance and we didn't know about it already.
3437 // So recompute collapsed margin
3438 treatWithClearance
= true;
3439 // Remember this decision, needed for incremental reflow
3440 aLine
->SetHasClearance();
3442 // Apply incoming margins
3443 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
3444 aState
.mPrevBEndMargin
.Zero();
3446 // Compute the collapsed margin again, ignoring the incoming margin
3448 mayNeedRetry
= false;
3449 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
3450 clearanceFrame
, &mayNeedRetry
);
3454 // Temporarily advance the running block-direction value so that the
3455 // GetFloatAvailableSpace method will return the right available space.
3456 // This undone as soon as the horizontal margins are computed.
3457 bStartMargin
= aState
.mPrevBEndMargin
.get();
3459 if (treatWithClearance
) {
3460 nscoord currentBCoord
= aState
.mBCoord
;
3461 // advance mBCoord to the clear position.
3463 aState
.ClearFloats(aState
.mBCoord
, breakType
, replacedBlock
);
3465 clearedFloats
= aState
.mBCoord
!= currentBCoord
;
3467 // Compute clearance. It's the amount we need to add to the block-start
3468 // border-edge of the frame, after applying collapsed margins
3469 // from the frame and its children, to get it to line up with
3470 // the block-end of the floats. The former is
3471 // currentBCoord + bStartMargin, the latter is the current
3473 // Note that negative clearance is possible
3474 clearance
= aState
.mBCoord
- (currentBCoord
+ bStartMargin
);
3476 // Add clearance to our block-start margin while we compute available
3477 // space for the frame
3478 bStartMargin
+= clearance
;
3480 // Note that aState.mBCoord should stay where it is: at the block-start
3481 // border-edge of the frame
3483 // Advance aState.mBCoord to the block-start border-edge of the frame.
3484 aState
.mBCoord
+= bStartMargin
;
3488 aLine
->SetLineIsImpactedByFloat(false);
3490 // Here aState.mBCoord is the block-start border-edge of the block.
3491 // Compute the available space for the block
3492 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
3493 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3494 LogicalRect
availSpace(wm
);
3495 aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
3496 replacedBlock
!= nullptr, availSpace
);
3499 // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
3500 // is to some degree out of paranoia: if we reliably eat up block-start
3501 // margins at the top of the page as we ought to, it wouldn't be
3503 if ((!aState
.mReflowInput
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
3504 availSpace
.BSize(wm
) < 0) {
3505 // We know already that this child block won't fit on this
3506 // page/column due to the block-start margin or the clearance. So we
3507 // need to get out of here now. (If we don't, most blocks will handle
3508 // things fine, and report break-before, but zero-height blocks
3509 // won't, and will thus make their parent overly-large and force
3510 // *it* to be pushed in its entirety.)
3511 // Doing this means that we also don't need to worry about the
3512 // |availSpace.BSize(wm) += bStartMargin| below interacting with
3513 // pushed floats (which force nscoord_MAX clearance) to cause a
3514 // constrained block size to turn into an unconstrained one.
3515 aState
.mBCoord
= startingBCoord
;
3516 aState
.mPrevBEndMargin
= incomingMargin
;
3517 *aKeepReflowGoing
= false;
3518 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
3519 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
3521 PushLines(aState
, aLine
.prev());
3522 aState
.mReflowStatus
.SetIncomplete();
3527 // Now put the block-dir coordinate back to the start of the
3528 // block-start-margin + clearance.
3529 aState
.mBCoord
-= bStartMargin
;
3530 availSpace
.BStart(wm
) -= bStartMargin
;
3531 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.BSize(wm
)) {
3532 availSpace
.BSize(wm
) += bStartMargin
;
3535 // Construct the reflow input for the block.
3536 Maybe
<ReflowInput
> blockReflowInput
;
3537 Maybe
<LogicalSize
> cbSize
;
3538 LogicalSize availSize
= availSpace
.Size(wm
);
3539 if (Style()->GetPseudoType() == PseudoStyleType::columnContent
) {
3540 // Calculate the multicol containing block's block size so that the
3541 // children with percentage block size get correct percentage basis.
3542 const ReflowInput
* cbReflowInput
=
3543 StaticPrefs::layout_css_column_span_enabled()
3544 ? aState
.mReflowInput
.mParentReflowInput
->mCBReflowInput
3545 : aState
.mReflowInput
.mCBReflowInput
;
3546 MOZ_ASSERT(cbReflowInput
->mFrame
->StyleColumn()->IsColumnContainerStyle(),
3547 "Get unexpected reflow input of multicol containing block!");
3549 // Use column-width as the containing block's inline-size, i.e. the column
3550 // content's computed inline-size.
3551 cbSize
.emplace(LogicalSize(wm
, aState
.mReflowInput
.ComputedISize(),
3552 cbReflowInput
->ComputedBSize())
3553 .ConvertTo(frame
->GetWritingMode(), wm
));
3555 // If a ColumnSetWrapper is in a balancing column content, it may be
3556 // pushed or pulled back and forth between column contents. Always add
3557 // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
3558 // can have a chance to reflow under current block size constraint.
3559 if (aState
.mReflowInput
.mFlags
.mIsColumnBalancing
&&
3560 frame
->IsColumnSetWrapperFrame()) {
3561 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
3563 } else if (IsColumnSetWrapperFrame()) {
3564 // If we are reflowing our ColumnSet children, we want to apply our block
3565 // size constraint to the available block size when constructing reflow
3566 // input for ColumnSet so that ColumnSet can use it to compute its max
3567 // column block size.
3568 if (frame
->IsColumnSetFrame()) {
3569 if (availSize
.BSize(wm
) != NS_UNCONSTRAINEDSIZE
) {
3570 // If the available size is constrained, we need to subtract
3571 // ColumnSetWrapper's block-end border and padding.
3572 availSize
.BSize(wm
) -= aState
.BorderPadding().BEnd(wm
);
3575 nscoord contentBSize
= aState
.mReflowInput
.ComputedBSize();
3576 if (aState
.mReflowInput
.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE
) {
3578 std::min(contentBSize
, aState
.mReflowInput
.ComputedMaxBSize());
3580 if (contentBSize
!= NS_UNCONSTRAINEDSIZE
) {
3581 // ColumnSet is not the outermost frame in the column container, so it
3582 // cannot have any margin. We don't need to consider any margin that
3583 // can be generated by "box-decoration-break: clone" as we do in
3584 // BlockReflowInput::ComputeBlockAvailSpace().
3585 const nscoord availContentBSize
= std::max(
3586 0, contentBSize
- (aState
.mBCoord
- aState
.ContentBStart()));
3587 availSize
.BSize(wm
) =
3588 std::min(availSize
.BSize(wm
), availContentBSize
);
3593 blockReflowInput
.emplace(aState
.mPresContext
, aState
.mReflowInput
, frame
,
3594 availSize
.ConvertTo(frame
->GetWritingMode(), wm
),
3597 nsFloatManager::SavedState floatManagerState
;
3598 nsReflowStatus frameReflowStatus
;
3600 if (floatAvailableSpace
.HasFloats()) {
3601 // Set if floatAvailableSpace.HasFloats() is true for any
3602 // iteration of the loop.
3603 aLine
->SetLineIsImpactedByFloat(true);
3606 // We might need to store into mDiscoveredClearance later if it's
3607 // currently null; we want to overwrite any writes that
3608 // brc.ReflowBlock() below does, so we need to remember now
3609 // whether it's empty.
3610 const bool shouldStoreClearance
=
3611 aState
.mReflowInput
.mDiscoveredClearance
&&
3612 !*aState
.mReflowInput
.mDiscoveredClearance
;
3614 // Reflow the block into the available space
3615 if (mayNeedRetry
|| replacedBlock
) {
3616 aState
.FloatManager()->PushState(&floatManagerState
);
3620 blockReflowInput
->mDiscoveredClearance
= &clearanceFrame
;
3621 } else if (!applyBStartMargin
) {
3622 blockReflowInput
->mDiscoveredClearance
=
3623 aState
.mReflowInput
.mDiscoveredClearance
;
3626 frameReflowStatus
.Reset();
3627 brc
.ReflowBlock(availSpace
, applyBStartMargin
, aState
.mPrevBEndMargin
,
3628 clearance
, aState
.IsAdjacentWithTop(), aLine
.get(),
3629 *blockReflowInput
, frameReflowStatus
, aState
);
3631 // Now the block has a height. Using that height, get the
3632 // available space again and call ComputeBlockAvailSpace again.
3633 // If ComputeBlockAvailSpace gives a different result, we need to
3635 if (!replacedBlock
) {
3639 LogicalRect
oldFloatAvailableSpaceRect(floatAvailableSpace
.mRect
);
3640 floatAvailableSpace
= aState
.GetFloatAvailableSpaceForBSize(
3641 aState
.mBCoord
+ bStartMargin
, brc
.GetMetrics().BSize(wm
),
3642 &floatManagerState
);
3643 NS_ASSERTION(floatAvailableSpace
.mRect
.BStart(wm
) ==
3644 oldFloatAvailableSpaceRect
.BStart(wm
),
3646 // Restore the height to the position of the next band.
3647 floatAvailableSpace
.mRect
.BSize(wm
) =
3648 oldFloatAvailableSpaceRect
.BSize(wm
);
3649 // Determine whether the available space shrunk on either side,
3650 // because (the first time round) we now know the block's height,
3651 // and it may intersect additional floats, or (on later
3652 // iterations) because narrowing the width relative to the
3653 // previous time may cause the block to become taller. Note that
3654 // since we're reflowing the block, narrowing the width might also
3655 // make it shorter, so we must pass aCanGrow as true.
3656 if (!AvailableSpaceShrunk(wm
, oldFloatAvailableSpaceRect
,
3657 floatAvailableSpace
.mRect
, true)) {
3658 // The size and position we chose before are fine (i.e., they
3659 // don't cause intersecting with floats that requires a change
3660 // in size or position), so we're done.
3664 bool advanced
= false;
3665 if (!aState
.ReplacedBlockFitsInAvailSpace(replacedBlock
,
3666 floatAvailableSpace
)) {
3667 // Advance to the next band.
3668 nscoord newBCoord
= aState
.mBCoord
;
3669 if (aState
.AdvanceToNextBand(floatAvailableSpace
.mRect
, &newBCoord
)) {
3672 // ClearFloats might be able to advance us further once we're there.
3674 aState
.ClearFloats(newBCoord
, StyleClear::None
, replacedBlock
);
3675 // Start over with a new available space rect at the new height.
3676 floatAvailableSpace
= aState
.GetFloatAvailableSpaceWithState(
3677 aState
.mBCoord
, ShapeType::ShapeOutside
, &floatManagerState
);
3680 LogicalRect
oldAvailSpace(availSpace
);
3681 aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
3682 replacedBlock
!= nullptr, availSpace
);
3684 if (!advanced
&& availSpace
.IsEqualEdges(oldAvailSpace
)) {
3688 // We need another reflow.
3689 aState
.FloatManager()->PopState(&floatManagerState
);
3691 if (!treatWithClearance
&& !applyBStartMargin
&&
3692 aState
.mReflowInput
.mDiscoveredClearance
) {
3693 // We set shouldStoreClearance above to record only the first
3694 // frame that requires clearance.
3695 if (shouldStoreClearance
) {
3696 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
3698 aState
.mPrevChild
= frame
;
3699 // Exactly what we do now is flexible since we'll definitely be
3705 // We're pushing down the border-box, so we don't apply margin anymore.
3706 // This should never cause us to move up since the call to
3707 // GetFloatAvailableSpaceForBSize above included the margin.
3708 applyBStartMargin
= false;
3710 treatWithClearance
= true; // avoid hitting test above
3714 blockReflowInput
.reset();
3715 blockReflowInput
.emplace(
3716 aState
.mPresContext
, aState
.mReflowInput
, frame
,
3717 availSpace
.Size(wm
).ConvertTo(frame
->GetWritingMode(), wm
));
3720 if (mayNeedRetry
&& clearanceFrame
) {
3721 aState
.FloatManager()->PopState(&floatManagerState
);
3722 aState
.mBCoord
= startingBCoord
;
3723 aState
.mPrevBEndMargin
= incomingMargin
;
3727 aState
.mPrevChild
= frame
;
3729 if (blockReflowInput
->WillReflowAgainForClearance()) {
3730 // If an ancestor of ours is going to reflow for clearance, we
3731 // need to avoid calling PlaceBlock, because it unsets dirty bits
3732 // on the child block (both itself, and through its call to
3733 // nsFrame::DidReflow), and those dirty bits imply dirtiness for
3734 // all of the child block, including the lines it didn't reflow.
3735 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
3736 "we need to call PositionChildViews");
3740 #if defined(REFLOW_STATUS_COVERAGE)
3741 RecordReflowStatus(true, frameReflowStatus
);
3744 if (frameReflowStatus
.IsInlineBreakBefore()) {
3745 // None of the child block fits.
3746 *aKeepReflowGoing
= false;
3747 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
3748 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
3750 PushLines(aState
, aLine
.prev());
3751 aState
.mReflowStatus
.SetIncomplete();
3754 // Note: line-break-after a block is a nop
3756 // Try to place the child block.
3757 // Don't force the block to fit if we have positive clearance, because
3758 // pushing it to the next page would give it more room.
3759 // Don't force the block to fit if it's impacted by a float. If it is,
3760 // then pushing it to the next page would give it more room. Note that
3761 // isImpacted doesn't include impact from the block's own floats.
3762 bool forceFit
= aState
.IsAdjacentWithTop() && clearance
<= 0 &&
3763 !floatAvailableSpace
.HasFloats();
3764 nsCollapsingMargin collapsedBEndMargin
;
3765 nsOverflowAreas overflowAreas
;
3767 brc
.PlaceBlock(*blockReflowInput
, forceFit
, aLine
.get(),
3768 collapsedBEndMargin
, overflowAreas
, frameReflowStatus
);
3769 if (!frameReflowStatus
.IsFullyComplete() &&
3770 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
3771 *aKeepReflowGoing
= false;
3774 if (aLine
->SetCarriedOutBEndMargin(collapsedBEndMargin
)) {
3775 LineIterator nextLine
= aLine
;
3777 if (nextLine
!= LinesEnd()) {
3778 nextLine
->MarkPreviousMarginDirty();
3782 aLine
->SetOverflowAreas(overflowAreas
);
3783 if (*aKeepReflowGoing
) {
3784 // Some of the child block fit
3786 // Advance to new Y position
3787 nscoord newBCoord
= aLine
->BEnd();
3788 aState
.mBCoord
= newBCoord
;
3790 // Continue the block frame now if it didn't completely fit in
3791 // the available space.
3792 if (!frameReflowStatus
.IsFullyComplete()) {
3793 bool madeContinuation
= CreateContinuationFor(aState
, nullptr, frame
);
3795 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
3796 NS_ASSERTION(nextFrame
,
3797 "We're supposed to have a next-in-flow by now");
3799 if (frameReflowStatus
.IsIncomplete()) {
3800 // If nextFrame used to be an overflow container, make it a normal
3802 if (!madeContinuation
&&
3803 (NS_FRAME_IS_OVERFLOW_CONTAINER
& nextFrame
->GetStateBits())) {
3804 nsOverflowContinuationTracker::AutoFinish
fini(
3805 aState
.mOverflowTracker
, frame
);
3806 nsContainerFrame
* parent
= nextFrame
->GetParent();
3807 nsresult rv
= parent
->StealFrame(nextFrame
);
3808 if (NS_FAILED(rv
)) {
3811 if (parent
!= this) {
3812 ReparentFrame(nextFrame
, parent
, this);
3814 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
3815 madeContinuation
= true; // needs to be added to mLines
3816 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
3817 frameReflowStatus
.SetNextInFlowNeedsReflow();
3820 // Push continuation to a new line, but only if we actually made
3822 if (madeContinuation
) {
3823 nsLineBox
* line
= NewLineBox(nextFrame
, true);
3824 mLines
.after_insert(aLine
, line
);
3827 PushLines(aState
, aLine
);
3828 aState
.mReflowStatus
.SetIncomplete();
3830 // If we need to reflow the continuation of the block child,
3831 // then we'd better reflow our continuation
3832 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
3833 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
3834 // We also need to make that continuation's line dirty so
3835 // it gets reflowed when we reflow our next in flow. The
3836 // nif's line must always be either a line of the nif's
3837 // parent block (only if we didn't make a continuation) or
3838 // else one of our own overflow lines. In the latter case
3839 // the line is already marked dirty, so just handle the
3841 if (!madeContinuation
) {
3842 nsBlockFrame
* nifBlock
= do_QueryFrame(nextFrame
->GetParent());
3845 "A block's child's next in flow's parent must be a block!");
3846 for (LineIterator line
= nifBlock
->LinesBegin(),
3847 line_end
= nifBlock
->LinesEnd();
3848 line
!= line_end
; ++line
) {
3849 if (line
->Contains(nextFrame
)) {
3856 *aKeepReflowGoing
= false;
3858 // The block-end margin for a block is only applied on the last
3859 // flow block. Since we just continued the child block frame,
3860 // we know that line->mFirstChild is not the last flow block
3861 // therefore zero out the running margin value.
3862 #ifdef NOISY_BLOCK_DIR_MARGINS
3864 printf(": reflow incomplete, frame=");
3865 frame
->ListTag(stdout
);
3866 printf(" prevBEndMargin=%d, setting to zero\n",
3867 aState
.mPrevBEndMargin
.get());
3869 aState
.mPrevBEndMargin
.Zero();
3870 } else { // frame is complete but its overflow is not complete
3871 // Disconnect the next-in-flow and put it in our overflow tracker
3872 if (!madeContinuation
&&
3873 !(NS_FRAME_IS_OVERFLOW_CONTAINER
& nextFrame
->GetStateBits())) {
3874 // It already exists, but as a normal next-in-flow, so we need
3875 // to dig it out of the child lists.
3876 nsresult rv
= nextFrame
->GetParent()->StealFrame(nextFrame
);
3877 if (NS_FAILED(rv
)) {
3880 } else if (madeContinuation
) {
3881 mFrames
.RemoveFrame(nextFrame
);
3884 // Put it in our overflow list
3885 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
3886 aState
.mReflowStatus
.MergeCompletionStatusFrom(frameReflowStatus
);
3888 #ifdef NOISY_BLOCK_DIR_MARGINS
3890 printf(": reflow complete but overflow incomplete for ");
3891 frame
->ListTag(stdout
);
3892 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
3893 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
3895 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
3897 } else { // frame is fully complete
3898 #ifdef NOISY_BLOCK_DIR_MARGINS
3900 printf(": reflow complete for ");
3901 frame
->ListTag(stdout
);
3902 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
3903 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
3905 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
3907 #ifdef NOISY_BLOCK_DIR_MARGINS
3910 frame
->ListTag(stdout
);
3911 printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
3912 brc
.GetCarriedOutBEndMargin().get(), collapsedBEndMargin
.get(),
3913 aState
.mPrevBEndMargin
.get());
3916 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
3917 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
3918 // If it's our very first line *or* we're not at the top of the page
3919 // and we have page-break-inside:avoid, then we need to be pushed to
3920 // our parent's next-in-flow.
3921 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
3922 // When we reflow in the new position, we need to reflow this
3926 // Push the line that didn't fit and any lines that follow it
3927 // to our next-in-flow.
3928 PushLines(aState
, aLine
.prev());
3929 aState
.mReflowStatus
.SetIncomplete();
3933 break; // out of the reflow retry loop
3936 // Now that we've got its final position all figured out, position any child
3937 // views it may have. Note that the case when frame has a view got handled
3938 // by FinishReflowChild, but that function didn't have the coordinates needed
3939 // to correctly decide whether to reposition child views.
3940 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
3941 nsContainerFrame::PositionChildViews(frame
);
3949 void nsBlockFrame::ReflowInlineFrames(BlockReflowInput
& aState
,
3951 bool* aKeepReflowGoing
) {
3952 *aKeepReflowGoing
= true;
3954 aLine
->SetLineIsImpactedByFloat(false);
3956 // Setup initial coordinate system for reflowing the inline frames
3957 // into. Apply a previous block frame's block-end margin first.
3958 if (ShouldApplyBStartMargin(aState
, aLine
, aLine
->mFirstChild
)) {
3959 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
3961 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
3963 LineReflowStatus lineReflowStatus
;
3965 nscoord availableSpaceBSize
= 0;
3966 aState
.mLineBSize
.reset();
3968 bool allowPullUp
= true;
3969 nsIFrame
* forceBreakInFrame
= nullptr;
3970 int32_t forceBreakOffset
= -1;
3971 gfxBreakPriority forceBreakPriority
= gfxBreakPriority::eNoBreak
;
3973 nsFloatManager::SavedState floatManagerState
;
3974 aState
.FloatManager()->PushState(&floatManagerState
);
3976 // Once upon a time we allocated the first 30 nsLineLayout objects
3977 // on the stack, and then we switched to the heap. At that time
3978 // these objects were large (1100 bytes on a 32 bit system).
3979 // Then the nsLineLayout object was shrunk to 156 bytes by
3980 // removing some internal buffers. Given that it is so much
3981 // smaller, the complexity of 2 different ways of allocating
3982 // no longer makes sense. Now we always allocate on the stack.
3983 nsLineLayout
lineLayout(aState
.mPresContext
, aState
.FloatManager(),
3984 &aState
.mReflowInput
, &aLine
, nullptr);
3985 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
3986 if (forceBreakInFrame
) {
3987 lineLayout
.ForceBreakAtPosition(forceBreakInFrame
, forceBreakOffset
);
3989 DoReflowInlineFrames(aState
, lineLayout
, aLine
, floatAvailableSpace
,
3990 availableSpaceBSize
, &floatManagerState
,
3991 aKeepReflowGoing
, &lineReflowStatus
, allowPullUp
);
3992 lineLayout
.EndLineReflow();
3994 if (LineReflowStatus::RedoNoPull
== lineReflowStatus
||
3995 LineReflowStatus::RedoMoreFloats
== lineReflowStatus
||
3996 LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
3997 if (lineLayout
.NeedsBackup()) {
3998 NS_ASSERTION(!forceBreakInFrame
,
3999 "Backing up twice; this should never be necessary");
4000 // If there is no saved break position, then this will set
4001 // set forceBreakInFrame to null and we won't back up, which is
4003 forceBreakInFrame
= lineLayout
.GetLastOptionalBreakPosition(
4004 &forceBreakOffset
, &forceBreakPriority
);
4006 forceBreakInFrame
= nullptr;
4008 // restore the float manager state
4009 aState
.FloatManager()->PopState(&floatManagerState
);
4010 // Clear out float lists
4011 aState
.mCurrentLineFloats
.DeleteAll();
4012 aState
.mNoWrapFloats
.Clear();
4013 aState
.mBelowCurrentLineFloats
.DeleteAll();
4016 // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4017 allowPullUp
= false;
4018 } while (LineReflowStatus::RedoNoPull
== lineReflowStatus
);
4019 } while (LineReflowStatus::RedoMoreFloats
== lineReflowStatus
);
4020 } while (LineReflowStatus::RedoNextBand
== lineReflowStatus
);
4023 void nsBlockFrame::PushTruncatedLine(BlockReflowInput
& aState
,
4025 bool* aKeepReflowGoing
) {
4026 PushLines(aState
, aLine
.prev());
4027 *aKeepReflowGoing
= false;
4028 aState
.mReflowStatus
.SetIncomplete();
4031 void nsBlockFrame::DoReflowInlineFrames(
4032 BlockReflowInput
& aState
, nsLineLayout
& aLineLayout
, LineIterator aLine
,
4033 nsFlowAreaRect
& aFloatAvailableSpace
, nscoord
& aAvailableSpaceBSize
,
4034 nsFloatManager::SavedState
* aFloatStateBeforeLine
, bool* aKeepReflowGoing
,
4035 LineReflowStatus
* aLineReflowStatus
, bool aAllowPullUp
) {
4036 // Forget all of the floats on the line
4037 aLine
->FreeFloats(aState
.mFloatCacheFreeList
);
4038 aState
.mFloatOverflowAreas
.Clear();
4040 // We need to set this flag on the line if any of our reflow passes
4041 // are impacted by floats.
4042 if (aFloatAvailableSpace
.HasFloats()) aLine
->SetLineIsImpactedByFloat(true);
4043 #ifdef REALLY_NOISY_REFLOW
4044 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4045 aFloatAvailableSpace
.HasFloats());
4048 WritingMode outerWM
= aState
.mReflowInput
.GetWritingMode();
4049 WritingMode lineWM
= WritingModeForLine(outerWM
, aLine
->mFirstChild
);
4050 LogicalRect lineRect
= aFloatAvailableSpace
.mRect
.ConvertTo(
4051 lineWM
, outerWM
, aState
.ContainerSize());
4053 nscoord iStart
= lineRect
.IStart(lineWM
);
4054 nscoord availISize
= lineRect
.ISize(lineWM
);
4056 if (aState
.mFlags
.mHasUnconstrainedBSize
) {
4057 availBSize
= NS_UNCONSTRAINEDSIZE
;
4059 /* XXX get the height right! */
4060 availBSize
= lineRect
.BSize(lineWM
);
4063 // Make sure to enable resize optimization before we call BeginLineReflow
4064 // because it might get disabled there
4065 aLine
->EnableResizeReflowOptimization();
4067 aLineLayout
.BeginLineReflow(iStart
, aState
.mBCoord
, availISize
, availBSize
,
4068 aFloatAvailableSpace
.HasFloats(),
4069 false, /*XXX isTopOfPage*/
4070 lineWM
, aState
.mContainerSize
);
4072 aState
.mFlags
.mIsLineLayoutEmpty
= false;
4074 // XXX Unfortunately we need to know this before reflowing the first
4075 // inline frame in the line. FIX ME.
4076 if ((0 == aLineLayout
.GetLineNumber()) &&
4077 (NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) &&
4078 (NS_BLOCK_HAS_FIRST_LETTER_STYLE
& mState
)) {
4079 aLineLayout
.SetFirstLetterStyleOK(true);
4082 !((NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) && GetPrevContinuation()),
4083 "first letter child bit should only be on first continuation");
4085 // Reflow the frames that are already on the line first
4086 LineReflowStatus lineReflowStatus
= LineReflowStatus::OK
;
4088 nsIFrame
* frame
= aLine
->mFirstChild
;
4090 if (aFloatAvailableSpace
.HasFloats()) {
4091 // There is a soft break opportunity at the start of the line, because
4092 // we can always move this line down below float(s).
4093 if (aLineLayout
.NotifyOptionalBreakPosition(
4094 frame
, 0, true, gfxBreakPriority::eNormalBreak
)) {
4095 lineReflowStatus
= LineReflowStatus::RedoNextBand
;
4099 // need to repeatedly call GetChildCount here, because the child
4100 // count can change during the loop!
4102 LineReflowStatus::OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
4103 i
++, frame
= frame
->GetNextSibling()) {
4104 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4105 if (LineReflowStatus::OK
!= lineReflowStatus
) {
4106 // It is possible that one or more of next lines are empty
4107 // (because of DeleteNextInFlowChild). If so, delete them now
4108 // in case we are finished.
4110 while ((aLine
!= LinesEnd()) && (0 == aLine
->GetChildCount())) {
4111 // XXX Is this still necessary now that DeleteNextInFlowChild
4112 // uses DoRemoveFrame?
4113 nsLineBox
* toremove
= aLine
;
4114 aLine
= mLines
.erase(aLine
);
4115 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
4116 FreeLineBox(toremove
);
4120 NS_ASSERTION(lineReflowStatus
!= LineReflowStatus::Truncated
,
4121 "ReflowInlineFrame should never determine that a line "
4122 "needs to go to the next page/column");
4126 // Don't pull up new frames into lines with continuation placeholders
4128 // Pull frames and reflow them until we can't
4129 while (LineReflowStatus::OK
== lineReflowStatus
) {
4130 frame
= PullFrame(aState
, aLine
);
4135 while (LineReflowStatus::OK
== lineReflowStatus
) {
4136 int32_t oldCount
= aLine
->GetChildCount();
4137 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4138 if (aLine
->GetChildCount() != oldCount
) {
4139 // We just created a continuation for aFrame AND its going
4140 // to end up on this line (e.g. :first-letter
4141 // situation). Therefore we have to loop here before trying
4142 // to pull another frame.
4143 frame
= frame
->GetNextSibling();
4151 aState
.mFlags
.mIsLineLayoutEmpty
= aLineLayout
.LineIsEmpty();
4153 // We only need to backup if the line isn't going to be reflowed again anyway
4154 bool needsBackup
= aLineLayout
.NeedsBackup() &&
4155 (lineReflowStatus
== LineReflowStatus::Stop
||
4156 lineReflowStatus
== LineReflowStatus::OK
);
4157 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
4159 "We shouldn't be backing up more than once! "
4160 "Someone must have set a break opportunity beyond the available width, "
4161 "even though there were better break opportunities before it");
4162 needsBackup
= false;
4165 // We need to try backing up to before a text run
4166 // XXX It's possible, in fact not unusual, for the break opportunity to
4167 // already be the end of the line. We should detect that and optimize to not
4169 if (aLineLayout
.HasOptionalBreakPosition()) {
4171 lineReflowStatus
= LineReflowStatus::RedoNoPull
;
4174 // In case we reflow this line again, remember that we don't
4175 // need to force any breaking
4176 aLineLayout
.ClearOptionalBreakPosition();
4179 if (LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4180 // This happens only when we have a line that is impacted by
4181 // floats and the first element in the line doesn't fit with
4184 // If there's block space available, we either try to reflow the line
4185 // past the current band (if it's non-zero and the band definitely won't
4186 // widen around a shape-outside), otherwise we try one pixel down. If
4187 // there's no block space available, we push the line to the next
4190 NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.BSize(outerWM
),
4191 "unconstrained block size on totally empty line");
4193 // See the analogous code for blocks in BlockReflowInput::ClearFloats.
4194 nscoord bandBSize
= aFloatAvailableSpace
.mRect
.BSize(outerWM
);
4195 if (bandBSize
> 0 ||
4196 NS_UNCONSTRAINEDSIZE
== aState
.mReflowInput
.AvailableBSize()) {
4197 NS_ASSERTION(bandBSize
== 0 || aFloatAvailableSpace
.HasFloats(),
4198 "redo line on totally empty line with non-empty band...");
4199 // We should never hit this case if we've placed floats on the
4200 // line; if we have, then the GetFloatAvailableSpace call is wrong
4201 // and needs to happen after the caller pops the float manager
4203 aState
.FloatManager()->AssertStateMatches(aFloatStateBeforeLine
);
4205 if (!aFloatAvailableSpace
.MayWiden() && bandBSize
> 0) {
4206 // Move it down far enough to clear the current band.
4207 aState
.mBCoord
+= bandBSize
;
4209 // Move it down by one dev pixel.
4210 aState
.mBCoord
+= aState
.mPresContext
->DevPixelsToAppUnits(1);
4213 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
4215 // There's nowhere to retry placing the line, so we want to push
4216 // it to the next page/column where its contents can fit not
4218 lineReflowStatus
= LineReflowStatus::Truncated
;
4219 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4222 // XXX: a small optimization can be done here when paginating:
4223 // if the new Y coordinate is past the end of the block then
4224 // push the line and return now instead of later on after we are
4226 } else if (LineReflowStatus::Truncated
!= lineReflowStatus
&&
4227 LineReflowStatus::RedoNoPull
!= lineReflowStatus
) {
4228 // If we are propagating out a break-before status then there is
4229 // no point in placing the line.
4230 if (!aState
.mReflowStatus
.IsInlineBreakBefore()) {
4231 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
4232 aFloatAvailableSpace
, aAvailableSpaceBSize
,
4233 aKeepReflowGoing
)) {
4234 lineReflowStatus
= LineReflowStatus::RedoMoreFloats
;
4235 // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4242 printf("Line reflow status = %s\n",
4243 LineReflowStatusToString(lineReflowStatus
));
4247 if (aLineLayout
.GetDirtyNextLine()) {
4248 // aLine may have been pushed to the overflow lines.
4249 FrameLines
* overflowLines
= GetOverflowLines();
4250 // We can't just compare iterators front() to aLine here, since they may be
4251 // in different lists.
4252 bool pushedToOverflowLines
=
4253 overflowLines
&& overflowLines
->mLines
.front() == aLine
.get();
4254 if (pushedToOverflowLines
) {
4255 // aLine is stale, it's associated with the main line list but it should
4256 // be associated with the overflow line list now
4257 aLine
= overflowLines
->mLines
.begin();
4259 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
4260 if (iter
.Next() && iter
.GetLine()->IsInline()) {
4261 iter
.GetLine()->MarkDirty();
4262 if (iter
.GetContainer() != this) {
4263 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4268 *aLineReflowStatus
= lineReflowStatus
;
4272 * Reflow an inline frame. The reflow status is mapped from the frames
4273 * reflow status to the lines reflow status (not to our reflow status).
4274 * The line reflow status is simple: true means keep placing frames
4275 * on the line; false means don't (the line is done). If the line
4276 * has some sort of breaking affect then aLine's break-type will be set
4277 * to something other than StyleClear::None.
4279 void nsBlockFrame::ReflowInlineFrame(BlockReflowInput
& aState
,
4280 nsLineLayout
& aLineLayout
,
4281 LineIterator aLine
, nsIFrame
* aFrame
,
4282 LineReflowStatus
* aLineReflowStatus
) {
4284 *aLineReflowStatus
= LineReflowStatus::OK
;
4286 #ifdef NOISY_FIRST_LETTER
4288 printf(": reflowing ");
4289 aFrame
->ListTag(stdout
);
4290 printf(" reflowingFirstLetter=%s\n",
4291 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
4294 if (aFrame
->IsPlaceholderFrame()) {
4295 auto ph
= static_cast<nsPlaceholderFrame
*>(aFrame
);
4296 ph
->ForgetLineIsEmptySoFar();
4299 // Reflow the inline frame
4300 nsReflowStatus frameReflowStatus
;
4302 aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
, nullptr, pushedFrame
);
4304 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4305 aLineLayout
.SetDirtyNextLine();
4308 #ifdef REALLY_NOISY_REFLOW
4309 aFrame
->ListTag(stdout
);
4310 printf(": status=%s\n", ToString(frameReflowStatus
).c_str());
4313 #if defined(REFLOW_STATUS_COVERAGE)
4314 RecordReflowStatus(false, frameReflowStatus
);
4317 // Send post-reflow notification
4318 aState
.mPrevChild
= aFrame
;
4321 This is where we need to add logic to handle some odd behavior.
4322 For one thing, we should usually place at least one thing next
4323 to a left float, even when that float takes up all the width on a line.
4327 // Process the child frames reflow status. There are 5 cases:
4328 // complete, not-complete, break-before, break-after-complete,
4329 // break-after-not-complete. There are two situations: we are a
4330 // block or we are an inline. This makes a total of 10 cases
4331 // (fortunately, there is some overlap).
4332 aLine
->SetBreakTypeAfter(StyleClear::None
);
4333 if (frameReflowStatus
.IsInlineBreak() ||
4334 StyleClear::None
!= aState
.mFloatBreakType
) {
4335 // Always abort the line reflow (because a line break is the
4336 // minimal amount of break we do).
4337 *aLineReflowStatus
= LineReflowStatus::Stop
;
4339 // XXX what should aLine's break-type be set to in all these cases?
4340 StyleClear breakType
= frameReflowStatus
.BreakType();
4341 MOZ_ASSERT(StyleClear::None
!= breakType
||
4342 StyleClear::None
!= aState
.mFloatBreakType
,
4345 if (frameReflowStatus
.IsInlineBreakBefore()) {
4346 // Break-before cases.
4347 if (aFrame
== aLine
->mFirstChild
) {
4348 // If we break before the first frame on the line then we must
4349 // be trying to place content where there's no room (e.g. on a
4350 // line with wide floats). Inform the caller to reflow the
4351 // line after skipping past a float.
4352 *aLineReflowStatus
= LineReflowStatus::RedoNextBand
;
4354 // It's not the first child on this line so go ahead and split
4355 // the line. We will see the frame again on the next-line.
4356 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
4358 // If we're splitting the line because the frame didn't fit and it
4359 // was pushed, then mark the line as having word wrapped. We need to
4360 // know that if we're shrink wrapping our width
4362 aLine
->SetLineWrapped(true);
4366 // If a float split and its prev-in-flow was followed by a <BR>, then
4367 // combine the <BR>'s break type with the inline's break type (the inline
4368 // will be the very next frame after the split float).
4369 if (StyleClear::None
!= aState
.mFloatBreakType
) {
4371 nsLayoutUtils::CombineBreakType(breakType
, aState
.mFloatBreakType
);
4372 aState
.mFloatBreakType
= StyleClear::None
;
4374 // Break-after cases
4375 if (breakType
== StyleClear::Line
) {
4376 if (!aLineLayout
.GetLineEndsInBR()) {
4377 breakType
= StyleClear::None
;
4380 aLine
->SetBreakTypeAfter(breakType
);
4381 if (frameReflowStatus
.IsComplete()) {
4382 // Split line, but after the frame just reflowed
4383 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
4386 if (frameReflowStatus
.IsInlineBreakAfter() &&
4387 !aLineLayout
.GetLineEndsInBR()) {
4388 aLineLayout
.SetDirtyNextLine();
4394 if (!frameReflowStatus
.IsFullyComplete()) {
4395 // Create a continuation for the incomplete frame. Note that the
4396 // frame may already have a continuation.
4397 CreateContinuationFor(aState
, aLine
, aFrame
);
4399 // Remember that the line has wrapped
4400 if (!aLineLayout
.GetLineEndsInBR()) {
4401 aLine
->SetLineWrapped(true);
4404 // If we just ended a first-letter frame or reflowed a placeholder then
4405 // don't split the line and don't stop the line reflow...
4406 // But if we are going to stop anyways we'd better split the line.
4407 if ((!frameReflowStatus
.FirstLetterComplete() &&
4408 !aFrame
->IsPlaceholderFrame()) ||
4409 *aLineReflowStatus
== LineReflowStatus::Stop
) {
4410 // Split line after the current frame
4411 *aLineReflowStatus
= LineReflowStatus::Stop
;
4412 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
4418 bool nsBlockFrame::CreateContinuationFor(BlockReflowInput
& aState
,
4419 nsLineBox
* aLine
, nsIFrame
* aFrame
) {
4420 nsIFrame
* newFrame
= nullptr;
4422 if (!aFrame
->GetNextInFlow()) {
4423 newFrame
= aState
.mPresContext
->PresShell()
4424 ->FrameConstructor()
4425 ->CreateContinuingFrame(aState
.mPresContext
, aFrame
, this);
4427 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
4430 aLine
->NoteFrameAdded(newFrame
);
4439 void nsBlockFrame::SplitFloat(BlockReflowInput
& aState
, nsIFrame
* aFloat
,
4440 const nsReflowStatus
& aFloatStatus
) {
4441 MOZ_ASSERT(!aFloatStatus
.IsFullyComplete(),
4442 "why split the frame if it's fully complete?");
4443 MOZ_ASSERT(aState
.mBlock
== this);
4445 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
4447 nsContainerFrame
* oldParent
= nextInFlow
->GetParent();
4448 DebugOnly
<nsresult
> rv
= oldParent
->StealFrame(nextInFlow
);
4449 NS_ASSERTION(NS_SUCCEEDED(rv
), "StealFrame failed");
4450 if (oldParent
!= this) {
4451 ReparentFrame(nextInFlow
, oldParent
, this);
4453 if (!aFloatStatus
.IsOverflowIncomplete()) {
4454 nextInFlow
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4457 nextInFlow
= aState
.mPresContext
->PresShell()
4458 ->FrameConstructor()
4459 ->CreateContinuingFrame(aState
.mPresContext
, aFloat
, this);
4461 if (aFloatStatus
.IsOverflowIncomplete()) {
4462 nextInFlow
->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4465 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
4466 if (floatStyle
== StyleFloat::Left
) {
4467 aState
.FloatManager()->SetSplitLeftFloatAcrossBreak();
4469 MOZ_ASSERT(floatStyle
== StyleFloat::Right
, "Unexpected float side!");
4470 aState
.FloatManager()->SetSplitRightFloatAcrossBreak();
4473 aState
.AppendPushedFloatChain(nextInFlow
);
4474 aState
.mReflowStatus
.SetOverflowIncomplete();
4477 static nsFloatCache
* GetLastFloat(nsLineBox
* aLine
) {
4478 nsFloatCache
* fc
= aLine
->GetFirstFloat();
4479 while (fc
&& fc
->Next()) {
4485 static bool CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
,
4486 nsFloatCache
* aFC
) {
4487 if (!aFC
) return true;
4488 NS_ASSERTION(!aFC
->mFloat
->GetPrevContinuation(),
4489 "float in a line should never be a continuation");
4490 NS_ASSERTION(!(aFC
->mFloat
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
),
4491 "float in a line should never be a pushed float");
4492 nsIFrame
* ph
= aFC
->mFloat
->FirstInFlow()->GetPlaceholderFrame();
4493 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
4494 if (f
->GetParent() == aBlock
) return aLine
->Contains(f
);
4496 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
4500 void nsBlockFrame::SplitLine(BlockReflowInput
& aState
,
4501 nsLineLayout
& aLineLayout
, LineIterator aLine
,
4503 LineReflowStatus
* aLineReflowStatus
) {
4504 MOZ_ASSERT(aLine
->IsInline(), "illegal SplitLine on block line");
4507 aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
4508 MOZ_ASSERT(pushCount
>= 0, "bad push count");
4512 nsFrame::IndentBy(stdout
, gNoiseIndent
);
4513 printf("split line: from line=%p pushCount=%d aFrame=",
4514 static_cast<void*>(aLine
.get()), pushCount
);
4516 aFrame
->ListTag(stdout
);
4521 if (gReallyNoisyReflow
) {
4522 aLine
->List(stdout
, gNoiseIndent
+ 1);
4527 if (0 != pushCount
) {
4528 MOZ_ASSERT(aLine
->GetChildCount() > pushCount
, "bad push");
4529 MOZ_ASSERT(nullptr != aFrame
, "whoops");
4532 nsIFrame
* f
= aFrame
;
4533 int32_t count
= pushCount
;
4534 while (f
&& count
> 0) {
4535 f
= f
->GetNextSibling();
4538 NS_ASSERTION(count
== 0, "Not enough frames to push");
4542 // Put frames being split out into their own line
4543 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
4544 mLines
.after_insert(aLine
, newLine
);
4546 if (gReallyNoisyReflow
) {
4547 newLine
->List(stdout
, gNoiseIndent
+ 1);
4551 // Let line layout know that some frames are no longer part of its
4553 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
4555 // If floats have been placed whose placeholders have been pushed to the new
4556 // line, we need to reflow the old line again. We don't want to look at the
4557 // frames in the new line, because as a large paragraph is laid out the
4558 // we'd get O(N^2) performance. So instead we just check that the last
4559 // float and the last below-current-line float are still in aLine.
4560 if (!CheckPlaceholderInLine(this, aLine
, GetLastFloat(aLine
)) ||
4561 !CheckPlaceholderInLine(this, aLine
,
4562 aState
.mBelowCurrentLineFloats
.Tail())) {
4563 *aLineReflowStatus
= LineReflowStatus::RedoNoPull
;
4572 bool nsBlockFrame::IsLastLine(BlockReflowInput
& aState
, LineIterator aLine
) {
4573 while (++aLine
!= LinesEnd()) {
4574 // There is another line
4575 if (0 != aLine
->GetChildCount()) {
4576 // If the next line is a block line then this line is the last in a
4577 // group of inline lines.
4578 return aLine
->IsBlock();
4580 // The next line is empty, try the next one
4583 // XXX Not sure about this part
4584 // Try our next-in-flows lines to answer the question
4585 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*)GetNextInFlow();
4586 while (nullptr != nextInFlow
) {
4587 for (LineIterator line
= nextInFlow
->LinesBegin(),
4588 line_end
= nextInFlow
->LinesEnd();
4589 line
!= line_end
; ++line
) {
4590 if (0 != line
->GetChildCount()) return line
->IsBlock();
4592 nextInFlow
= (nsBlockFrame
*)nextInFlow
->GetNextInFlow();
4595 // This is the last line - so don't allow justification
4599 bool nsBlockFrame::PlaceLine(BlockReflowInput
& aState
,
4600 nsLineLayout
& aLineLayout
, LineIterator aLine
,
4601 nsFloatManager::SavedState
* aFloatStateBeforeLine
,
4602 nsFlowAreaRect
& aFlowArea
,
4603 nscoord
& aAvailableSpaceBSize
,
4604 bool* aKeepReflowGoing
) {
4605 // Try to position the floats in a nowrap context.
4606 aLineLayout
.FlushNoWrapFloats();
4608 // Trim extra white-space from the line before placing the frames
4609 aLineLayout
.TrimTrailingWhiteSpace();
4611 // Vertically align the frames on this line.
4613 // According to the CSS2 spec, section 12.6.1, the "marker" box
4614 // participates in the height calculation of the list-item box's
4617 // There are exactly two places a ::marker can be placed: near the
4618 // first or second line. It's only placed on the second line in a
4619 // rare case: when the first line is empty.
4620 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
4621 bool addedMarker
= false;
4622 if (HasOutsideMarker() &&
4623 ((aLine
== mLines
.front() &&
4624 (!aLineLayout
.IsZeroBSize() || (aLine
== mLines
.back()))) ||
4625 (mLines
.front() != mLines
.back() && 0 == mLines
.front()->BSize() &&
4626 aLine
== mLines
.begin().next()))) {
4627 ReflowOutput
metrics(aState
.mReflowInput
);
4628 nsIFrame
* marker
= GetOutsideMarker();
4629 ReflowOutsideMarker(marker
, aState
, metrics
, aState
.mBCoord
);
4630 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
4631 "empty ::marker frame took up space");
4632 aLineLayout
.AddMarkerFrame(marker
, metrics
);
4635 aLineLayout
.VerticalAlignLine();
4637 // We want to consider the floats in the current line when determining
4638 // whether the float available space is shrunk. If mLineBSize doesn't
4639 // exist, we are in the first pass trying to place the line. Calling
4640 // GetFloatAvailableSpace() like we did in BlockReflowInput::AddFloat()
4641 // for UpdateBand().
4643 // floatAvailableSpaceWithOldLineBSize is the float available space with
4644 // the old BSize, but including the floats that were added in this line.
4645 LogicalRect floatAvailableSpaceWithOldLineBSize
=
4646 aState
.mLineBSize
.isNothing()
4647 ? aState
.GetFloatAvailableSpace(aLine
->BStart()).mRect
4649 .GetFloatAvailableSpaceForBSize(
4650 aLine
->BStart(), aState
.mLineBSize
.value(), nullptr)
4653 // As we redo for floats, we can't reduce the amount of BSize we're
4655 aAvailableSpaceBSize
= std::max(aAvailableSpaceBSize
, aLine
->BSize());
4656 LogicalRect floatAvailableSpaceWithLineBSize
=
4658 .GetFloatAvailableSpaceForBSize(aLine
->BStart(), aAvailableSpaceBSize
,
4662 // If the available space between the floats is smaller now that we
4663 // know the BSize, return false (and cause another pass with
4664 // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
4665 // never decreases, which means that we can't reduce the set of floats
4666 // we intersect, which means that the available space cannot grow.
4667 if (AvailableSpaceShrunk(wm
, floatAvailableSpaceWithOldLineBSize
,
4668 floatAvailableSpaceWithLineBSize
, false)) {
4669 // Prepare data for redoing the line.
4670 aState
.mLineBSize
= Some(aLine
->BSize());
4672 // Since we want to redo the line, we update aFlowArea by using the
4673 // aFloatStateBeforeLine, which is the float manager's state before the
4675 LogicalRect
oldFloatAvailableSpace(aFlowArea
.mRect
);
4676 aFlowArea
= aState
.GetFloatAvailableSpaceForBSize(
4677 aLine
->BStart(), aAvailableSpaceBSize
, aFloatStateBeforeLine
);
4680 aFlowArea
.mRect
.BStart(wm
) == oldFloatAvailableSpace
.BStart(wm
),
4682 // Restore the BSize to the position of the next band.
4683 aFlowArea
.mRect
.BSize(wm
) = oldFloatAvailableSpace
.BSize(wm
);
4685 // Enforce both IStart() and IEnd() never move outwards to prevent
4686 // infinite grow-shrink loops.
4687 const nscoord iStartDiff
=
4688 aFlowArea
.mRect
.IStart(wm
) - oldFloatAvailableSpace
.IStart(wm
);
4689 const nscoord iEndDiff
=
4690 aFlowArea
.mRect
.IEnd(wm
) - oldFloatAvailableSpace
.IEnd(wm
);
4691 if (iStartDiff
< 0) {
4692 aFlowArea
.mRect
.IStart(wm
) -= iStartDiff
;
4693 aFlowArea
.mRect
.ISize(wm
) += iStartDiff
;
4696 aFlowArea
.mRect
.ISize(wm
) -= iEndDiff
;
4703 if (!GetParent()->IsCrazySizeAssertSuppressed()) {
4704 static nscoord lastHeight
= 0;
4705 if (CRAZY_SIZE(aLine
->BStart())) {
4706 lastHeight
= aLine
->BStart();
4707 if (abs(aLine
->BStart() - lastHeight
) > CRAZY_COORD
/ 10) {
4708 nsFrame::ListTag(stdout
);
4709 printf(": line=%p y=%d line.bounds.height=%d\n",
4710 static_cast<void*>(aLine
.get()), aLine
->BStart(),
4719 // Only block frames horizontally align their children because
4720 // inline frames "shrink-wrap" around their children (therefore
4721 // there is no extra horizontal space).
4722 const nsStyleText
* styleText
= StyleText();
4725 * text-align-last defaults to the same value as text-align when
4726 * text-align-last is set to auto (except when text-align is set to justify),
4727 * so in that case we don't need to set isLastLine.
4729 * In other words, isLastLine really means isLastLineAndWeCare.
4732 !nsSVGUtils::IsInSVGTextSubtree(this) &&
4733 ((NS_STYLE_TEXT_ALIGN_AUTO
!= styleText
->mTextAlignLast
||
4734 NS_STYLE_TEXT_ALIGN_JUSTIFY
== styleText
->mTextAlign
) &&
4735 (aLineLayout
.GetLineEndsInBR() || IsLastLine(aState
, aLine
)));
4737 aLineLayout
.TextAlignLine(aLine
, isLastLine
);
4739 // From here on, pfd->mBounds rectangles are incorrect because bidi
4740 // might have moved frames around!
4741 nsOverflowAreas overflowAreas
;
4742 aLineLayout
.RelativePositionFrames(overflowAreas
);
4743 aLine
->SetOverflowAreas(overflowAreas
);
4745 aLineLayout
.RemoveMarkerFrame(GetOutsideMarker());
4748 // Inline lines do not have margins themselves; however they are
4749 // impacted by prior block margins. If this line ends up having some
4750 // height then we zero out the previous block-end margin value that was
4751 // already applied to the line's starting Y coordinate. Otherwise we
4752 // leave it be so that the previous blocks block-end margin can be
4753 // collapsed with a block that follows.
4756 if (!aLine
->CachedIsEmpty()) {
4757 // This line has some height. Therefore the application of the
4758 // previous-bottom-margin should stick.
4759 aState
.mPrevBEndMargin
.Zero();
4760 newBCoord
= aLine
->BEnd();
4762 // Don't let the previous-bottom-margin value affect the newBCoord
4763 // coordinate (it was applied in ReflowInlineFrames speculatively)
4764 // since the line is empty.
4765 // We already called |ShouldApplyBStartMargin|, and if we applied it
4766 // then mShouldApplyBStartMargin is set.
4767 nscoord dy
= aState
.mFlags
.mShouldApplyBStartMargin
4768 ? -aState
.mPrevBEndMargin
.get()
4770 newBCoord
= aState
.mBCoord
+ dy
;
4773 if (!aState
.mReflowStatus
.IsFullyComplete() &&
4774 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4775 aLine
->AppendFloats(aState
.mCurrentLineFloats
);
4776 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4777 // Reflow the line again when we reflow at our new position.
4779 *aKeepReflowGoing
= false;
4783 // See if the line fit (our first line always does).
4784 if (mLines
.front() != aLine
&& newBCoord
> aState
.mBEndEdge
&&
4785 aState
.mBEndEdge
!= NS_UNCONSTRAINEDSIZE
) {
4786 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
4787 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4788 // All our content doesn't fit, start on the next page.
4789 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4790 *aKeepReflowGoing
= false;
4792 // Push aLine and all of its children and anything else that
4793 // follows to our next-in-flow.
4794 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4799 // Note that any early return before this update of aState.mBCoord
4800 // must either (a) return false or (b) set aKeepReflowGoing to false.
4801 // Otherwise we'll keep reflowing later lines at an incorrect
4802 // position, and we might not come back and clean up the damage later.
4803 aState
.mBCoord
= newBCoord
;
4805 // Add the already placed current-line floats to the line
4806 aLine
->AppendFloats(aState
.mCurrentLineFloats
);
4808 // Any below current line floats to place?
4809 if (aState
.mBelowCurrentLineFloats
.NotEmpty()) {
4810 // Reflow the below-current-line floats, which places on the line's
4812 aState
.PlaceBelowCurrentLineFloats(aLine
);
4815 // When a line has floats, factor them into the combined-area
4817 if (aLine
->HasFloats()) {
4818 // Combine the float combined area (stored in aState) and the
4819 // value computed by the line layout code.
4820 nsOverflowAreas lineOverflowAreas
;
4821 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
4822 nsRect
& o
= lineOverflowAreas
.Overflow(otype
);
4823 o
= aLine
->GetOverflowArea(otype
);
4824 #ifdef NOISY_COMBINED_AREA
4826 printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n", otype
,
4827 o
.x
, o
.y
, o
.width
, o
.height
,
4828 aState
.mFloatOverflowAreas
.Overflow(otype
).x
,
4829 aState
.mFloatOverflowAreas
.Overflow(otype
).y
,
4830 aState
.mFloatOverflowAreas
.Overflow(otype
).width
,
4831 aState
.mFloatOverflowAreas
.Overflow(otype
).height
);
4833 o
.UnionRect(aState
.mFloatOverflowAreas
.Overflow(otype
), o
);
4835 #ifdef NOISY_COMBINED_AREA
4836 printf(" ==> final lineCA=%d,%d,%d,%d\n", o
.x
, o
.y
, o
.width
, o
.height
);
4839 aLine
->SetOverflowAreas(lineOverflowAreas
);
4842 // Apply break-after clearing if necessary
4843 // This must stay in sync with |ReflowDirtyLines|.
4844 if (aLine
->HasFloatBreakAfter()) {
4846 aState
.ClearFloats(aState
.mBCoord
, aLine
->GetBreakTypeAfter());
4851 void nsBlockFrame::PushLines(BlockReflowInput
& aState
,
4852 nsLineList::iterator aLineBefore
) {
4853 // NOTE: aLineBefore is always a normal line, not an overflow line.
4854 // The following expression will assert otherwise.
4855 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
4857 nsLineList::iterator
overBegin(aLineBefore
.next());
4859 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
4860 bool firstLine
= overBegin
== LinesBegin();
4862 if (overBegin
!= LinesEnd()) {
4863 // Remove floats in the lines from mFloats
4865 CollectFloats(overBegin
->mFirstChild
, floats
, true);
4867 if (floats
.NotEmpty()) {
4869 for (nsIFrame
* f
: floats
) {
4870 MOZ_ASSERT(!(f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
),
4871 "CollectFloats should've removed that bit");
4874 // Push the floats onto the front of the overflow out-of-flows list
4875 nsAutoOOFFrameList
oofs(this);
4876 oofs
.mList
.InsertFrames(nullptr, nullptr, floats
);
4879 // overflow lines can already exist in some cases, in particular,
4880 // when shrinkwrapping and we discover that the shrinkwap causes
4881 // the height of some child block to grow which creates additional
4882 // overflowing content. In such cases we must prepend the new
4883 // overflow to the existing overflow.
4884 FrameLines
* overflowLines
= RemoveOverflowLines();
4885 if (!overflowLines
) {
4886 // XXXldb use presshell arena!
4887 overflowLines
= new FrameLines();
4889 if (overflowLines
) {
4890 nsIFrame
* lineBeforeLastFrame
;
4892 lineBeforeLastFrame
= nullptr; // removes all frames
4894 nsIFrame
* f
= overBegin
->mFirstChild
;
4895 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
4896 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
4897 "unexpected line frames");
4899 nsFrameList pushedFrames
= mFrames
.RemoveFramesAfter(lineBeforeLastFrame
);
4900 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr, pushedFrames
);
4902 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
4903 overBegin
, LinesEnd());
4904 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
4905 // this takes ownership but it won't delete it immediately so we
4906 // can keep using it.
4907 SetOverflowLines(overflowLines
);
4909 // Mark all the overflow lines dirty so that they get reflowed when
4910 // they are pulled up by our next-in-flow.
4912 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4913 for (LineIterator line
= overflowLines
->mLines
.begin(),
4914 line_end
= overflowLines
->mLines
.end();
4915 line
!= line_end
; ++line
) {
4917 line
->MarkPreviousMarginDirty();
4918 line
->SetBoundsEmpty();
4919 if (line
->HasFloats()) {
4920 line
->FreeFloats(aState
.mFloatCacheFreeList
);
4927 VerifyOverflowSituation();
4931 // The overflowLines property is stored as a pointer to a line list,
4932 // which must be deleted. However, the following functions all maintain
4933 // the invariant that the property is never set if the list is empty.
4935 bool nsBlockFrame::DrainOverflowLines() {
4937 VerifyOverflowSituation();
4940 // Steal the prev-in-flow's overflow lines and prepend them.
4941 bool didFindOverflow
= false;
4942 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
4944 prevBlock
->ClearLineCursor();
4945 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
4946 if (overflowLines
) {
4947 // Make all the frames on the overflow line list mine.
4948 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
4950 // Collect overflow containers from our [Excess]OverflowContainers lists
4951 // that are continuations from the frames we picked up from our
4952 // prev-in-flow. We'll append these to mFrames to ensure the continuations
4954 auto HasOverflowContainers
= [this]() -> bool {
4955 return GetPropTableFrames(OverflowContainersProperty()) ||
4956 GetPropTableFrames(ExcessOverflowContainersProperty());
4958 nsFrameList ocContinuations
;
4959 if (HasOverflowContainers()) {
4960 for (auto* f
: overflowLines
->mFrames
) {
4963 while (!done
&& (cont
= cont
->GetNextContinuation()) &&
4964 cont
->GetParent() == this) {
4965 bool onlyChild
= !cont
->GetPrevSibling() && !cont
->GetNextSibling();
4966 if (MaybeStealOverflowContainerFrame(cont
)) {
4967 cont
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4968 ocContinuations
.AppendFrame(nullptr, cont
);
4969 done
= onlyChild
&& !HasOverflowContainers();
4980 // Make the overflow out-of-flow frames mine too.
4981 nsAutoOOFFrameList
oofs(prevBlock
);
4982 if (oofs
.mList
.NotEmpty()) {
4983 // In case we own any next-in-flows of any of the drained frames, then
4984 // move those to the PushedFloat list.
4985 nsFrameList pushedFloats
;
4986 for (nsFrameList::Enumerator
e(oofs
.mList
); !e
.AtEnd(); e
.Next()) {
4987 nsIFrame
* nif
= e
.get()->GetNextInFlow();
4988 for (; nif
&& nif
->GetParent() == this; nif
= nif
->GetNextInFlow()) {
4989 MOZ_ASSERT(nif
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
));
4991 pushedFloats
.AppendFrame(nullptr, nif
);
4994 ReparentFrames(oofs
.mList
, prevBlock
, this);
4995 mFloats
.InsertFrames(nullptr, nullptr, oofs
.mList
);
4996 if (!pushedFloats
.IsEmpty()) {
4997 nsFrameList
* pf
= EnsurePushedFloats();
4998 pf
->InsertFrames(nullptr, nullptr, pushedFloats
);
5002 if (!mLines
.empty()) {
5003 // Remember to recompute the margins on the first line. This will
5004 // also recompute the correct deltaBCoord if necessary.
5005 mLines
.front()->MarkPreviousMarginDirty();
5007 // The overflow lines have already been marked dirty and their previous
5008 // margins marked dirty also.
5010 // Prepend the overflow frames/lines to our principal list.
5011 mFrames
.InsertFrames(nullptr, nullptr, overflowLines
->mFrames
);
5012 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
5013 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
5014 delete overflowLines
;
5015 AddFrames(ocContinuations
, mFrames
.LastChild());
5016 didFindOverflow
= true;
5020 // Now append our own overflow lines.
5021 return DrainSelfOverflowList() || didFindOverflow
;
5024 bool nsBlockFrame::DrainSelfOverflowList() {
5025 UniquePtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
5026 if (!ourOverflowLines
) {
5030 // No need to reparent frames in our own overflow lines/oofs, because they're
5031 // already ours. But we should put overflow floats back in mFloats.
5032 // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5034 nsAutoOOFFrameList
oofs(this);
5035 if (oofs
.mList
.NotEmpty()) {
5037 for (nsIFrame
* f
: oofs
.mList
) {
5038 MOZ_ASSERT(!(f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
),
5039 "CollectFloats should've removed that bit");
5042 // The overflow floats go after our regular floats.
5043 mFloats
.AppendFrames(nullptr, oofs
.mList
);
5046 if (!ourOverflowLines
->mLines
.empty()) {
5047 mFrames
.AppendFrames(nullptr, ourOverflowLines
->mFrames
);
5048 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
5052 VerifyOverflowSituation();
5058 * Pushed floats are floats whose placeholders are in a previous
5059 * continuation. They might themselves be next-continuations of a float
5060 * that partially fit in an earlier continuation, or they might be the
5061 * first continuation of a float that couldn't be placed at all.
5063 * Pushed floats live permanently at the beginning of a block's float
5064 * list, where they must live *before* any floats whose placeholders are
5067 * Temporarily, during reflow, they also live on the pushed floats list,
5068 * which only holds them between (a) when one continuation pushes them to
5069 * its pushed floats list because they don't fit and (b) when the next
5070 * continuation pulls them onto the beginning of its float list.
5072 * DrainPushedFloats sets up pushed floats the way we need them at the
5073 * start of reflow; they are then reflowed by ReflowPushedFloats (which
5074 * might push some of them on). Floats with placeholders in this block
5075 * are reflowed by (BlockReflowInput/nsLineLayout)::AddFloat, which
5076 * also maintains these invariants.
5078 * DrainSelfPushedFloats moves any pushed floats from this block's own
5079 * PushedFloats list back into mFloats. DrainPushedFloats additionally
5080 * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5082 void nsBlockFrame::DrainSelfPushedFloats() {
5083 // If we're getting reflowed multiple times without our
5084 // next-continuation being reflowed, we might need to pull back floats
5085 // that we just put in the list to be pushed to our next-in-flow.
5086 // We don't want to pull back any next-in-flows of floats on our own
5087 // float list, and we only need to pull back first-in-flows whose
5088 // placeholders were in earlier blocks (since first-in-flows whose
5089 // placeholders are in this block will get pulled appropriately by
5090 // AddFloat, and will then be more likely to be in the correct order).
5091 // FIXME: What if there's a continuation in our pushed floats list
5092 // whose prev-in-flow is in a previous continuation of this block
5093 // rather than this block? Might we need to pull it back so we don't
5094 // report ourselves complete?
5095 // FIXME: Maybe we should just pull all of them back?
5096 nsPresContext
* presContext
= PresContext();
5097 nsFrameList
* ourPushedFloats
= GetPushedFloats();
5098 if (ourPushedFloats
) {
5099 // When we pull back floats, we want to put them with the pushed
5100 // floats, which must live at the start of our float list, but we
5101 // want them at the end of those pushed floats.
5102 // FIXME: This isn't quite right! What if they're all pushed floats?
5103 nsIFrame
* insertionPrevSibling
= nullptr; /* beginning of list */
5104 for (nsIFrame
* f
= mFloats
.FirstChild();
5105 f
&& (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
);
5106 f
= f
->GetNextSibling()) {
5107 insertionPrevSibling
= f
;
5110 for (nsIFrame
*f
= ourPushedFloats
->LastChild(), *next
; f
; f
= next
) {
5111 next
= f
->GetPrevSibling();
5113 if (f
->GetPrevContinuation()) {
5116 nsPlaceholderFrame
* placeholder
= f
->GetPlaceholderFrame();
5117 nsIFrame
* floatOriginalParent
=
5118 presContext
->PresShell()
5119 ->FrameConstructor()
5120 ->GetFloatContainingBlock(placeholder
);
5121 if (floatOriginalParent
!= this) {
5122 // This is a first continuation that was pushed from one of our
5123 // previous continuations. Take it out of the pushed floats
5124 // list and put it in our floats list, before any of our
5125 // floats, but after other pushed floats.
5126 ourPushedFloats
->RemoveFrame(f
);
5127 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
5132 if (ourPushedFloats
->IsEmpty()) {
5133 RemovePushedFloats()->Delete(presContext
->PresShell());
5138 void nsBlockFrame::DrainPushedFloats() {
5139 DrainSelfPushedFloats();
5141 // After our prev-in-flow has completed reflow, it may have a pushed
5142 // floats list, containing floats that we need to own. Take these.
5143 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5145 AutoFrameListPtr
list(PresContext(), prevBlock
->RemovePushedFloats());
5146 if (list
&& list
->NotEmpty()) {
5147 mFloats
.InsertFrames(this, nullptr, *list
);
5152 nsBlockFrame::FrameLines
* nsBlockFrame::GetOverflowLines() const {
5153 if (!HasOverflowLines()) {
5156 FrameLines
* prop
= GetProperty(OverflowLinesProperty());
5158 prop
&& !prop
->mLines
.empty() &&
5159 prop
->mLines
.front()->GetChildCount() == 0
5160 ? prop
->mFrames
.IsEmpty()
5161 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5162 "value should always be stored and non-empty when state set");
5166 nsBlockFrame::FrameLines
* nsBlockFrame::RemoveOverflowLines() {
5167 if (!HasOverflowLines()) {
5170 FrameLines
* prop
= RemoveProperty(OverflowLinesProperty());
5172 prop
&& !prop
->mLines
.empty() &&
5173 prop
->mLines
.front()->GetChildCount() == 0
5174 ? prop
->mFrames
.IsEmpty()
5175 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5176 "value should always be stored and non-empty when state set");
5177 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5181 void nsBlockFrame::DestroyOverflowLines() {
5182 NS_ASSERTION(HasOverflowLines(), "huh?");
5183 FrameLines
* prop
= RemoveProperty(OverflowLinesProperty());
5184 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
5185 "value should always be stored but empty when destroying");
5186 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5190 // This takes ownership of aOverflowLines.
5191 // XXX We should allocate overflowLines from presShell arena!
5192 void nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
) {
5193 NS_ASSERTION(aOverflowLines
, "null lines");
5194 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
5195 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
5196 aOverflowLines
->mFrames
.FirstChild(),
5197 "invalid overflow lines / frames");
5198 NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES
),
5199 "Overwriting existing overflow lines");
5201 // Verify that we won't overwrite an existing overflow list
5202 NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5203 SetProperty(OverflowLinesProperty(), aOverflowLines
);
5204 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5207 nsFrameList
* nsBlockFrame::GetOverflowOutOfFlows() const {
5208 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5211 nsFrameList
* result
= GetPropTableFrames(OverflowOutOfFlowsProperty());
5212 NS_ASSERTION(result
, "value should always be non-empty when state set");
5216 // This takes ownership of the frames
5217 void nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList
& aList
,
5218 nsFrameList
* aPropValue
) {
5220 !!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) == !!aPropValue
,
5221 "state does not match value");
5223 if (aList
.IsEmpty()) {
5224 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5227 nsFrameList
* list
= RemovePropTableFrames(OverflowOutOfFlowsProperty());
5228 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
5230 list
->Delete(PresShell());
5231 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5232 } else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) {
5233 NS_ASSERTION(aPropValue
== GetPropTableFrames(OverflowOutOfFlowsProperty()),
5234 "prop value mismatch");
5235 *aPropValue
= aList
;
5237 SetPropTableFrames(new (PresShell()) nsFrameList(aList
),
5238 OverflowOutOfFlowsProperty());
5239 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5243 nsIFrame
* nsBlockFrame::GetInsideMarker() const {
5244 if (!HasInsideMarker()) {
5247 NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5248 nsIFrame
* frame
= GetProperty(InsideMarkerProperty());
5249 NS_ASSERTION(frame
, "bogus inside ::marker frame");
5253 nsIFrame
* nsBlockFrame::GetOutsideMarker() const {
5254 nsFrameList
* list
= GetOutsideMarkerList();
5255 return list
? list
->FirstChild() : nullptr;
5258 nsFrameList
* nsBlockFrame::GetOutsideMarkerList() const {
5259 if (!HasOutsideMarker()) {
5262 NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5263 nsFrameList
* list
= GetProperty(OutsideMarkerProperty());
5264 NS_ASSERTION(list
&& list
->GetLength() == 1, "bogus outside ::marker list");
5268 nsFrameList
* nsBlockFrame::GetPushedFloats() const {
5269 if (!HasPushedFloats()) {
5272 nsFrameList
* result
= GetProperty(PushedFloatProperty());
5273 NS_ASSERTION(result
, "value should always be non-empty when state set");
5277 nsFrameList
* nsBlockFrame::EnsurePushedFloats() {
5278 nsFrameList
* result
= GetPushedFloats();
5279 if (result
) return result
;
5281 result
= new (PresShell()) nsFrameList
;
5282 SetProperty(PushedFloatProperty(), result
);
5283 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5288 nsFrameList
* nsBlockFrame::RemovePushedFloats() {
5289 if (!HasPushedFloats()) {
5292 nsFrameList
* result
= RemoveProperty(PushedFloatProperty());
5293 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5294 NS_ASSERTION(result
, "value should always be non-empty when state set");
5298 //////////////////////////////////////////////////////////////////////
5299 // Frame list manipulation routines
5301 void nsBlockFrame::AppendFrames(ChildListID aListID
, nsFrameList
& aFrameList
) {
5302 if (aFrameList
.IsEmpty()) {
5305 if (aListID
!= kPrincipalList
) {
5306 if (kFloatList
== aListID
) {
5307 DrainSelfPushedFloats(); // ensure the last frame is in mFloats
5308 mFloats
.AppendFrames(nullptr, aFrameList
);
5311 MOZ_ASSERT(kNoReflowPrincipalList
== aListID
, "unexpected child list");
5314 // Find the proper last-child for where the append should go
5315 nsIFrame
* lastKid
= mFrames
.LastChild();
5317 (mLines
.empty() ? nullptr : mLines
.back()->LastChild()) == lastKid
,
5318 "out-of-sync mLines / mFrames");
5320 #ifdef NOISY_REFLOW_REASON
5322 printf(": append ");
5323 for (nsIFrame
* frame
: aFrameList
) {
5324 frame
->ListTag(out
);
5328 lastKid
->ListTag(stdout
);
5333 if (nsSVGUtils::IsInSVGTextSubtree(this)) {
5334 MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
5335 "unexpected block frame in SVG text");
5336 // Workaround for bug 1399425 in case this bit has been removed from the
5337 // SVGTextFrame just before the parser adds more descendant nodes.
5338 GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
);
5341 AddFrames(aFrameList
, lastKid
);
5342 if (aListID
!= kNoReflowPrincipalList
) {
5343 PresShell()->FrameNeedsReflow(
5344 this, IntrinsicDirty::TreeChange
,
5345 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5349 void nsBlockFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
5350 nsFrameList
& aFrameList
) {
5351 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
5352 "inserting after sibling frame with different parent");
5354 if (aListID
!= kPrincipalList
) {
5355 if (kFloatList
== aListID
) {
5356 DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
5357 mFloats
.InsertFrames(this, aPrevFrame
, aFrameList
);
5360 MOZ_ASSERT(kNoReflowPrincipalList
== aListID
, "unexpected child list");
5363 #ifdef NOISY_REFLOW_REASON
5365 printf(": insert ");
5366 for (nsIFrame
* frame
: aFrameList
) {
5367 frame
->ListTag(out
);
5371 aPrevFrame
->ListTag(stdout
);
5376 AddFrames(aFrameList
, aPrevFrame
);
5377 if (aListID
!= kNoReflowPrincipalList
) {
5378 PresShell()->FrameNeedsReflow(
5379 this, IntrinsicDirty::TreeChange
,
5380 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5384 void nsBlockFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
5385 #ifdef NOISY_REFLOW_REASON
5387 printf(": remove ");
5388 aOldFrame
->ListTag(stdout
);
5392 if (aListID
== kPrincipalList
) {
5393 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
5394 DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
5396 MarkSameFloatManagerLinesDirty(this);
5398 } else if (kFloatList
== aListID
) {
5399 // Make sure to mark affected lines dirty for the float frame
5400 // we are removing; this way is a bit messy, but so is the rest of the code.
5402 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
5403 "RemoveFrame should not be called on pushed floats.");
5404 for (nsIFrame
* f
= aOldFrame
;
5405 f
&& !(f
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
);
5406 f
= f
->GetNextContinuation()) {
5407 MarkSameFloatManagerLinesDirty(
5408 static_cast<nsBlockFrame
*>(f
->GetParent()));
5410 DoRemoveOutOfFlowFrame(aOldFrame
);
5411 } else if (kNoReflowPrincipalList
== aListID
) {
5412 // Skip the call to |FrameNeedsReflow| below by returning now.
5413 DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
5416 MOZ_CRASH("unexpected child list");
5419 PresShell()->FrameNeedsReflow(
5420 this, IntrinsicDirty::TreeChange
,
5421 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5424 static bool ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
) {
5425 LayoutFrameType type
= aLastFrame
->Type();
5426 if (type
== LayoutFrameType::Br
) {
5429 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
5430 if (type
== LayoutFrameType::Text
&&
5431 !(aLastFrame
->GetStateBits() & TEXT_OFFSETS_NEED_FIXING
)) {
5432 return aLastFrame
->HasSignificantTerminalNewline();
5437 void nsBlockFrame::AddFrames(nsFrameList
& aFrameList
, nsIFrame
* aPrevSibling
) {
5438 // Clear our line cursor, since our lines may change.
5441 if (aFrameList
.IsEmpty()) {
5445 // Attempt to find the line that contains the previous sibling
5446 nsLineList
* lineList
= &mLines
;
5447 nsFrameList
* frames
= &mFrames
;
5448 nsLineList::iterator prevSibLine
= lineList
->end();
5449 int32_t prevSiblingIndex
= -1;
5451 // XXX_perf This is technically O(N^2) in some cases, but by using
5452 // RFind instead of Find, we make it O(N) in the most common case,
5453 // which is appending content.
5455 // Find the line that contains the previous sibling
5456 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
5457 prevSibLine
, mFrames
.LastChild(),
5458 &prevSiblingIndex
)) {
5459 // Not in mLines - try overflow lines.
5460 FrameLines
* overflowLines
= GetOverflowLines();
5462 if (overflowLines
) {
5463 prevSibLine
= overflowLines
->mLines
.end();
5464 prevSiblingIndex
= -1;
5465 found
= nsLineBox::RFindLineContaining(
5466 aPrevSibling
, overflowLines
->mLines
.begin(), prevSibLine
,
5467 overflowLines
->mFrames
.LastChild(), &prevSiblingIndex
);
5469 if (MOZ_LIKELY(found
)) {
5470 lineList
= &overflowLines
->mLines
;
5471 frames
= &overflowLines
->mFrames
;
5473 // Note: defensive code! RFindLineContaining must not return
5474 // false in this case, so if it does...
5475 MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
5476 aPrevSibling
= nullptr;
5477 prevSibLine
= lineList
->end();
5482 // Find the frame following aPrevSibling so that we can join up the
5483 // two lists of frames.
5485 // Split line containing aPrevSibling in two if the insertion
5486 // point is somewhere in the middle of the line.
5487 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
5489 // Split the line in two where the frame(s) are being inserted.
5491 NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
5492 lineList
->after_insert(prevSibLine
, line
);
5493 // Mark prevSibLine dirty and as needing textrun invalidation, since
5494 // we may be breaking up text in the line. Its previous line may also
5495 // need to be invalidated because it may be able to pull some text up.
5496 MarkLineDirty(prevSibLine
, lineList
);
5497 // The new line will also need its textruns recomputed because of the
5500 line
->SetInvalidateTextRuns(true);
5502 } else if (!lineList
->empty()) {
5503 lineList
->front()->MarkDirty();
5504 lineList
->front()->SetInvalidateTextRuns(true);
5506 const nsFrameList::Slice
& newFrames
=
5507 frames
->InsertFrames(nullptr, aPrevSibling
, aFrameList
);
5509 // Walk through the new frames being added and update the line data
5510 // structures to fit.
5511 for (nsFrameList::Enumerator
e(newFrames
); !e
.AtEnd(); e
.Next()) {
5512 nsIFrame
* newFrame
= e
.get();
5513 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
5514 "Unexpected aPrevSibling");
5516 !newFrame
->IsPlaceholderFrame() ||
5517 (!newFrame
->IsAbsolutelyPositioned() && !newFrame
->IsFloating()),
5518 "Placeholders should not float or be positioned");
5520 bool isBlock
= newFrame
->IsBlockOutside();
5522 // If the frame is a block frame, or if there is no previous line or if the
5523 // previous line is a block line we need to make a new line. We also make
5524 // a new line, as an optimization, in the two cases we know we'll need it:
5525 // if the previous line ended with a <br>, or if it has significant
5526 // whitespace and ended in a newline.
5527 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
5528 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
5529 // Create a new line for the frame and add its line to the line
5531 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
5532 if (prevSibLine
!= lineList
->end()) {
5533 // Append new line after prevSibLine
5534 lineList
->after_insert(prevSibLine
, line
);
5537 // New line is going before the other lines
5538 lineList
->push_front(line
);
5539 prevSibLine
= lineList
->begin();
5542 prevSibLine
->NoteFrameAdded(newFrame
);
5543 // We're adding inline content to prevSibLine, so we need to mark it
5544 // dirty, ensure its textruns are recomputed, and possibly do the same
5545 // to its previous line since that line may be able to pull content up.
5546 MarkLineDirty(prevSibLine
, lineList
);
5549 aPrevSibling
= newFrame
;
5553 MOZ_ASSERT(aFrameList
.IsEmpty());
5558 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
) {
5559 // Find which line contains the float, so we can update
5561 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
5562 for (; line
!= line_end
; ++line
) {
5563 if (line
->IsInline() && line
->RemoveFloat(aFloat
)) {
5569 void nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
5571 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
5572 // frame list properties.
5573 if (!mFloats
.ContainsFrame(aFloat
)) {
5575 (GetOverflowOutOfFlows() &&
5576 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
5577 (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat
)),
5578 "aFloat is not our child or on an unexpected frame list");
5582 if (mFloats
.StartRemoveFrame(aFloat
)) {
5586 nsFrameList
* list
= GetPushedFloats();
5587 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
5589 // XXXmats not yet - need to investigate BlockReflowInput::mPushedFloats
5590 // first so we don't leave it pointing to a deleted list.
5591 if (list
->IsEmpty()) {
5592 delete RemovePushedFloats();
5599 nsAutoOOFFrameList
oofs(this);
5600 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
5606 void nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame
* aFrame
) {
5607 // The containing block is always the parent of aFrame.
5608 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
5610 // Remove aFrame from the appropriate list.
5611 if (aFrame
->IsAbsolutelyPositioned()) {
5612 // This also deletes the next-in-flows
5613 block
->GetAbsoluteContainingBlock()->RemoveFrame(block
, kAbsoluteList
,
5616 // First remove aFrame's next-in-flows.
5617 nsIFrame
* nif
= aFrame
->GetNextInFlow();
5619 nif
->GetParent()->DeleteNextInFlowChild(nif
, false);
5621 // Now remove aFrame from its child list and Destroy it.
5622 block
->RemoveFloatFromFloatCache(aFrame
);
5623 block
->RemoveFloat(aFrame
);
5629 * This helps us iterate over the list of all normal + overflow lines
5631 void nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
5632 nsLineList::iterator
* aStartIterator
,
5633 nsLineList::iterator
* aEndIterator
,
5634 bool* aInOverflowLines
,
5635 FrameLines
** aOverflowLines
) {
5636 if (*aIterator
== *aEndIterator
) {
5637 if (!*aInOverflowLines
) {
5638 // Try the overflow lines
5639 *aInOverflowLines
= true;
5640 FrameLines
* lines
= GetOverflowLines();
5642 *aStartIterator
= lines
->mLines
.begin();
5643 *aIterator
= *aStartIterator
;
5644 *aEndIterator
= lines
->mLines
.end();
5645 *aOverflowLines
= lines
;
5651 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5653 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
) {
5654 // This will assert if aLine isn't in mLines of aFrame:
5655 DebugOnly
<bool> check
= aLine
== mFrame
->LinesBegin();
5658 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5663 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
5664 : &aFrame
->mLines
) {}
5666 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5667 bool* aFoundValidLine
)
5668 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
5669 mLine
= aFrame
->LinesBegin();
5670 *aFoundValidLine
= FindValidLine();
5673 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState
& aRestyleState
) {
5674 nsIFrame
* letterFrame
= GetFirstLetter();
5679 // Figure out what the right style parent is. This needs to match
5680 // nsCSSFrameConstructor::CreateLetterFrame.
5681 nsIFrame
* inFlowFrame
= letterFrame
;
5682 if (inFlowFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) {
5683 inFlowFrame
= inFlowFrame
->GetPlaceholderFrame();
5685 nsIFrame
* styleParent
= CorrectStyleParentFrame(inFlowFrame
->GetParent(),
5686 PseudoStyleType::firstLetter
);
5687 ComputedStyle
* parentStyle
= styleParent
->Style();
5688 RefPtr
<ComputedStyle
> firstLetterStyle
=
5689 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
5690 *mContent
->AsElement(), PseudoStyleType::firstLetter
, parentStyle
);
5691 // Note that we don't need to worry about changehints for the continuation
5692 // styles: those will be handled by the styleParent already.
5693 RefPtr
<ComputedStyle
> continuationStyle
=
5694 aRestyleState
.StyleSet().ResolveStyleForFirstLetterContinuation(
5696 UpdateStyleOfOwnedChildFrame(letterFrame
, firstLetterStyle
, aRestyleState
,
5697 Some(continuationStyle
.get()));
5699 // We also want to update the style on the textframe inside the first-letter.
5700 // We don't need to compute a changehint for this, though, since any changes
5701 // to it are handled by the first-letter anyway.
5702 nsIFrame
* textFrame
= letterFrame
->PrincipalChildList().FirstChild();
5703 RefPtr
<ComputedStyle
> firstTextStyle
=
5704 aRestyleState
.StyleSet().ResolveStyleForText(textFrame
->GetContent(),
5706 textFrame
->SetComputedStyle(firstTextStyle
);
5708 // We don't need to update style for textFrame's continuations: it's already
5709 // set up to inherit from parentStyle, which is what we want.
5712 static nsIFrame
* FindChildContaining(nsBlockFrame
* aFrame
,
5713 nsIFrame
* aFindFrame
) {
5714 NS_ASSERTION(aFrame
, "must have frame");
5717 nsIFrame
* block
= aFrame
;
5719 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
5721 block
= block
->GetNextContinuation();
5723 if (!child
) return nullptr;
5724 if (!(child
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) break;
5725 aFindFrame
= child
->GetPlaceholderFrame();
5731 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
5732 nsIFrame
* aFindFrame
,
5733 bool* aFoundValidLine
)
5734 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
5735 *aFoundValidLine
= false;
5737 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
5740 LineIterator line_end
= aFrame
->LinesEnd();
5741 mLine
= aFrame
->LinesBegin();
5742 if (mLine
!= line_end
&& mLine
.next() == line_end
&&
5743 !aFrame
->HasOverflowLines()) {
5744 // The block has a single line - that must be it!
5745 *aFoundValidLine
= true;
5749 // Try to use the cursor if it exists, otherwise fall back to the first line
5750 if (nsLineBox
* const cursor
= aFrame
->GetLineCursor()) {
5752 // Perform a simultaneous forward and reverse search starting from the
5754 nsBlockFrame::LineIterator line
= aFrame
->LinesBeginFrom(cursor
);
5755 nsBlockFrame::ReverseLineIterator rline
= aFrame
->LinesRBeginFrom(cursor
);
5756 nsBlockFrame::ReverseLineIterator rline_end
= aFrame
->LinesREnd();
5757 // rline is positioned on the line containing 'cursor', so it's not
5758 // rline_end. So we can safely increment it (i.e. move it to one line
5759 // earlier) to start searching there.
5761 while (line
!= line_end
|| rline
!= rline_end
) {
5762 if (line
!= line_end
) {
5763 if (line
->Contains(child
)) {
5769 if (rline
!= rline_end
) {
5770 if (rline
->Contains(child
)) {
5777 if (mLine
!= line_end
) {
5778 *aFoundValidLine
= true;
5779 if (mLine
!= cursor
) {
5780 aFrame
->SetProperty(nsBlockFrame::LineCursorProperty(), mLine
);
5785 for (mLine
= aFrame
->LinesBegin(); mLine
!= line_end
; ++mLine
) {
5786 if (mLine
->Contains(child
)) {
5787 *aFoundValidLine
= true;
5792 // Didn't find the line
5793 MOZ_ASSERT(mLine
== line_end
, "mLine should be line_end at this point");
5795 // If we reach here, it means that we have not been able to find the
5796 // desired frame in our in-flow lines. So we should start looking at
5797 // our overflow lines. In order to do that, we set mLine to the end
5798 // iterator so that FindValidLine starts to look at overflow lines,
5801 if (!FindValidLine()) return;
5804 if (mLine
->Contains(child
)) {
5805 *aFoundValidLine
= true;
5811 nsBlockFrame::LineIterator
nsBlockInFlowLineIterator::End() {
5812 return mLineList
->end();
5815 bool nsBlockInFlowLineIterator::IsLastLineInList() {
5816 LineIterator end
= End();
5817 return mLine
!= end
&& mLine
.next() == end
;
5820 bool nsBlockInFlowLineIterator::Next() {
5822 return FindValidLine();
5825 bool nsBlockInFlowLineIterator::Prev() {
5826 LineIterator begin
= mLineList
->begin();
5827 if (mLine
!= begin
) {
5831 bool currentlyInOverflowLines
= GetInOverflow();
5833 if (currentlyInOverflowLines
) {
5834 mLineList
= &mFrame
->mLines
;
5835 mLine
= mLineList
->end();
5836 if (mLine
!= mLineList
->begin()) {
5841 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
5842 if (!mFrame
) return false;
5843 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
5844 if (overflowLines
) {
5845 mLineList
= &overflowLines
->mLines
;
5846 mLine
= mLineList
->end();
5847 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
5852 currentlyInOverflowLines
= !currentlyInOverflowLines
;
5856 bool nsBlockInFlowLineIterator::FindValidLine() {
5857 LineIterator end
= mLineList
->end();
5858 if (mLine
!= end
) return true;
5859 bool currentlyInOverflowLines
= GetInOverflow();
5861 if (currentlyInOverflowLines
) {
5862 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
5863 if (!mFrame
) return false;
5864 mLineList
= &mFrame
->mLines
;
5865 mLine
= mLineList
->begin();
5866 if (mLine
!= mLineList
->end()) return true;
5868 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
5869 if (overflowLines
) {
5870 mLineList
= &overflowLines
->mLines
;
5871 mLine
= mLineList
->begin();
5872 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
5876 currentlyInOverflowLines
= !currentlyInOverflowLines
;
5880 // This function removes aDeletedFrame and all its continuations. It
5881 // is optimized for deleting a whole series of frames. The easy
5882 // implementation would invoke itself recursively on
5883 // aDeletedFrame->GetNextContinuation, then locate the line containing
5884 // aDeletedFrame and remove aDeletedFrame from that line. But here we
5885 // start by locating aDeletedFrame and then scanning from that point
5886 // on looking for continuations.
5887 void nsBlockFrame::DoRemoveFrameInternal(nsIFrame
* aDeletedFrame
,
5889 PostDestroyData
& aPostDestroyData
) {
5890 // Clear our line cursor, since our lines may change.
5893 if (aDeletedFrame
->GetStateBits() &
5894 (NS_FRAME_OUT_OF_FLOW
| NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
5895 if (!aDeletedFrame
->GetPrevInFlow()) {
5896 NS_ASSERTION(aDeletedFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
5897 "Expected out-of-flow frame");
5898 DoRemoveOutOfFlowFrame(aDeletedFrame
);
5900 nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame
,
5901 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
5906 // Find the line that contains deletedFrame
5907 nsLineList::iterator line_start
= mLines
.begin(), line_end
= mLines
.end();
5908 nsLineList::iterator line
= line_start
;
5909 FrameLines
* overflowLines
= nullptr;
5910 bool searchingOverflowList
= false;
5911 // Make sure we look in the overflow lines even if the normal line
5913 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
5915 while (line
!= line_end
) {
5916 if (line
->Contains(aDeletedFrame
)) {
5920 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
5924 if (line
== line_end
) {
5925 NS_ERROR("can't find deleted frame in lines");
5929 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
5930 if (line
!= line_start
) {
5931 line
.prev()->MarkDirty();
5932 line
.prev()->SetInvalidateTextRuns(true);
5933 } else if (searchingOverflowList
&& !mLines
.empty()) {
5934 mLines
.back()->MarkDirty();
5935 mLines
.back()->SetInvalidateTextRuns(true);
5939 while (line
!= line_end
&& aDeletedFrame
) {
5940 NS_ASSERTION(this == aDeletedFrame
->GetParent(), "messed up delete code");
5941 NS_ASSERTION(line
->Contains(aDeletedFrame
), "frame not in line");
5943 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
5945 line
->SetInvalidateTextRuns(true);
5948 // If the frame being deleted is the last one on the line then
5949 // optimize away the line->Contains(next-in-flow) call below.
5950 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
5951 if (!isLastFrameOnLine
) {
5952 LineIterator next
= line
.next();
5953 nsIFrame
* lastFrame
=
5955 ? next
->mFirstChild
->GetPrevSibling()
5956 : (searchingOverflowList
? overflowLines
->mFrames
.LastChild()
5957 : mFrames
.LastChild());
5958 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
5959 "unexpected line frames");
5960 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
5963 // Remove aDeletedFrame from the line
5964 if (line
->mFirstChild
== aDeletedFrame
) {
5965 // We should be setting this to null if aDeletedFrame
5966 // is the only frame on the line. HOWEVER in that case
5967 // we will be removing the line anyway, see below.
5968 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
5971 // Hmm, this won't do anything if we're removing a frame in the first
5972 // overflow line... Hopefully doesn't matter
5974 if (line
!= line_end
&& !line
->IsBlock()) {
5975 // Since we just removed a frame that follows some inline
5976 // frames, we need to reflow the previous line.
5981 // Take aDeletedFrame out of the sibling list. Note that
5982 // prevSibling will only be nullptr when we are deleting the very
5983 // first frame in the main or overflow list.
5984 if (searchingOverflowList
) {
5985 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
5987 mFrames
.RemoveFrame(aDeletedFrame
);
5990 // Update the child count of the line to be accurate
5991 line
->NoteFrameRemoved(aDeletedFrame
);
5993 // Destroy frame; capture its next continuation first in case we need
5994 // to destroy that too.
5995 nsIFrame
* deletedNextContinuation
=
5996 (aFlags
& REMOVE_FIXED_CONTINUATIONS
)
5997 ? aDeletedFrame
->GetNextContinuation()
5998 : aDeletedFrame
->GetNextInFlow();
5999 #ifdef NOISY_REMOVE_FRAME
6000 printf("DoRemoveFrame: %s line=%p frame=",
6001 searchingOverflowList
? "overflow" : "normal", line
.get());
6002 aDeletedFrame
->ListTag(stdout
);
6003 printf(" prevSibling=%p deletedNextContinuation=%p\n",
6004 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
6007 // If next-in-flow is an overflow container, must remove it first.
6008 if (deletedNextContinuation
&& deletedNextContinuation
->GetStateBits() &
6009 NS_FRAME_IS_OVERFLOW_CONTAINER
) {
6010 deletedNextContinuation
->GetParent()->DeleteNextInFlowChild(
6011 deletedNextContinuation
, false);
6012 deletedNextContinuation
= nullptr;
6015 aDeletedFrame
->DestroyFrom(aDeletedFrame
, aPostDestroyData
);
6016 aDeletedFrame
= deletedNextContinuation
;
6018 bool haveAdvancedToNextLine
= false;
6019 // If line is empty, remove it now.
6020 if (0 == line
->GetChildCount()) {
6021 #ifdef NOISY_REMOVE_FRAME
6022 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6023 searchingOverflowList
? "overflow" : "normal", line
.get());
6025 nsLineBox
* cur
= line
;
6026 if (!searchingOverflowList
) {
6027 line
= mLines
.erase(line
);
6028 // Invalidate the space taken up by the line.
6029 // XXX We need to do this if we're removing a frame as a result of
6030 // a call to RemoveFrame(), but we may not need to do this in all
6032 #ifdef NOISY_BLOCK_INVALIDATE
6033 nsRect
visOverflow(cur
->GetVisualOverflowArea());
6034 printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, visOverflow
.x
,
6035 visOverflow
.y
, visOverflow
.width
, visOverflow
.height
);
6038 line
= overflowLines
->mLines
.erase(line
);
6039 if (overflowLines
->mLines
.empty()) {
6040 DestroyOverflowLines();
6041 overflowLines
= nullptr;
6042 // We just invalidated our iterators. Since we were in
6043 // the overflow lines list, which is now empty, set them
6044 // so we're at the end of the regular line list.
6045 line_start
= mLines
.begin();
6046 line_end
= mLines
.end();
6052 // If we're removing a line, ReflowDirtyLines isn't going to
6053 // know that it needs to slide lines unless something is marked
6054 // dirty. So mark the previous margin of the next line dirty if
6056 if (line
!= line_end
) {
6057 line
->MarkPreviousMarginDirty();
6059 haveAdvancedToNextLine
= true;
6061 // Make the line that just lost a frame dirty, and advance to
6063 if (!deletedNextContinuation
|| isLastFrameOnLine
||
6064 !line
->Contains(deletedNextContinuation
)) {
6067 haveAdvancedToNextLine
= true;
6071 if (deletedNextContinuation
) {
6072 // See if we should keep looking in the current flow's line list.
6073 if (deletedNextContinuation
->GetParent() != this) {
6074 // The deceased frames continuation is not a child of the
6075 // current block. So break out of the loop so that we advance
6076 // to the next parent.
6078 // If we have a continuation in a different block then all bets are
6079 // off regarding whether we are deleting frames without actual content,
6080 // so don't propagate FRAMES_ARE_EMPTY any further.
6081 aFlags
&= ~FRAMES_ARE_EMPTY
;
6085 // If we advanced to the next line then check if we should switch to the
6086 // overflow line list.
6087 if (haveAdvancedToNextLine
) {
6088 if (line
!= line_end
&& !searchingOverflowList
&&
6089 !line
->Contains(deletedNextContinuation
)) {
6090 // We have advanced to the next *normal* line but the next-in-flow
6091 // is not there - force a switch to the overflow line list.
6095 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6097 #ifdef NOISY_REMOVE_FRAME
6098 printf("DoRemoveFrame: now on %s line=%p\n",
6099 searchingOverflowList
? "overflow" : "normal", line
.get());
6105 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
6106 line
.next()->MarkDirty();
6107 line
.next()->SetInvalidateTextRuns(true);
6112 VerifyOverflowSituation();
6115 // Advance to next flow block if the frame has more continuations.
6116 if (!aDeletedFrame
) {
6119 nsBlockFrame
* nextBlock
= do_QueryFrame(aDeletedFrame
->GetParent());
6120 NS_ASSERTION(nextBlock
, "Our child's continuation's parent is not a block?");
6121 uint32_t flags
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
);
6122 nextBlock
->DoRemoveFrameInternal(aDeletedFrame
, flags
, aPostDestroyData
);
6125 static bool FindBlockLineFor(nsIFrame
* aChild
, nsLineList::iterator aBegin
,
6126 nsLineList::iterator aEnd
,
6127 nsLineList::iterator
* aResult
) {
6128 MOZ_ASSERT(aChild
->IsBlockOutside());
6129 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6130 MOZ_ASSERT(line
->GetChildCount() > 0);
6131 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
6132 MOZ_ASSERT(line
->GetChildCount() == 1);
6140 static bool FindInlineLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6141 nsLineList::iterator aBegin
,
6142 nsLineList::iterator aEnd
,
6143 nsLineList::iterator
* aResult
) {
6144 MOZ_ASSERT(!aChild
->IsBlockOutside());
6145 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6146 MOZ_ASSERT(line
->GetChildCount() > 0);
6147 if (!line
->IsBlock()) {
6148 // Optimize by comparing the line's last child first.
6149 nsLineList::iterator next
= line
.next();
6150 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
6151 : next
->mFirstChild
->GetPrevSibling()) ||
6152 line
->Contains(aChild
)) {
6161 static bool FindLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6162 nsLineList::iterator aBegin
, nsLineList::iterator aEnd
,
6163 nsLineList::iterator
* aResult
) {
6164 return aChild
->IsBlockOutside()
6165 ? FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
)
6166 : FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
6169 nsresult
nsBlockFrame::StealFrame(nsIFrame
* aChild
) {
6170 MOZ_ASSERT(aChild
->GetParent() == this);
6172 if ((aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) && aChild
->IsFloating()) {
6173 RemoveFloat(aChild
);
6177 if (MaybeStealOverflowContainerFrame(aChild
)) {
6181 MOZ_ASSERT(!(aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
));
6183 nsLineList::iterator line
;
6184 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
6185 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
6187 FrameLines
* overflowLines
= GetOverflowLines();
6188 DebugOnly
<bool> found
;
6189 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
6190 overflowLines
->mLines
.begin(),
6191 overflowLines
->mLines
.end(), &line
);
6193 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
6194 overflowLines
->mLines
);
6195 if (overflowLines
->mLines
.empty()) {
6196 DestroyOverflowLines();
6203 void nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
,
6204 nsLineList::iterator aLine
,
6205 nsFrameList
& aFrameList
,
6206 nsLineList
& aLineList
) {
6207 aFrameList
.RemoveFrame(aChild
);
6208 if (aChild
== aLine
->mFirstChild
) {
6209 aLine
->mFirstChild
= aChild
->GetNextSibling();
6211 aLine
->NoteFrameRemoved(aChild
);
6212 if (aLine
->GetChildCount() > 0) {
6215 // The line became empty - destroy it.
6216 nsLineBox
* lineBox
= aLine
;
6217 aLine
= aLineList
.erase(aLine
);
6218 if (aLine
!= aLineList
.end()) {
6219 aLine
->MarkPreviousMarginDirty();
6221 FreeLineBox(lineBox
);
6225 void nsBlockFrame::DeleteNextInFlowChild(nsIFrame
* aNextInFlow
,
6226 bool aDeletingEmptyFrames
) {
6227 MOZ_ASSERT(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
6229 if (aNextInFlow
->GetStateBits() &
6230 (NS_FRAME_OUT_OF_FLOW
| NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6231 nsContainerFrame::DeleteNextInFlowChild(aNextInFlow
, aDeletingEmptyFrames
);
6234 if (aDeletingEmptyFrames
) {
6235 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
6238 DoRemoveFrame(aNextInFlow
, aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
6242 const nsStyleText
* nsBlockFrame::StyleTextForLineLayout() {
6243 // Return the pointer to an unmodified style text
6247 ////////////////////////////////////////////////////////////////////////
6250 LogicalRect
nsBlockFrame::AdjustFloatAvailableSpace(
6251 BlockReflowInput
& aState
, const LogicalRect
& aFloatAvailableSpace
,
6252 nsIFrame
* aFloatFrame
) {
6253 // Compute the available inline size. By default, assume the inline
6254 // size of the containing block.
6256 const nsStyleDisplay
* floatDisplay
= aFloatFrame
->StyleDisplay();
6257 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
6259 if (mozilla::StyleDisplay::Table
!= floatDisplay
->mDisplay
||
6260 eCompatibility_NavQuirks
!= aState
.mPresContext
->CompatibilityMode()) {
6261 availISize
= aState
.ContentISize();
6263 // This quirk matches the one in BlockReflowInput::FlowAndPlaceFloat
6264 // give tables only the available space
6265 // if they can shrink we may not be constrained to place
6266 // them in the next line
6267 availISize
= aFloatAvailableSpace
.ISize(wm
);
6270 nscoord availBSize
= NS_UNCONSTRAINEDSIZE
== aState
.ContentBSize()
6271 ? NS_UNCONSTRAINEDSIZE
6272 : std::max(0, aState
.ContentBEnd() - aState
.mBCoord
);
6274 if (availBSize
!= NS_UNCONSTRAINEDSIZE
&&
6275 !aState
.mFlags
.mFloatFragmentsInsideColumnEnabled
&&
6276 nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::ColumnSet
)) {
6277 // Tell the float it has unrestricted block-size, so it won't break.
6278 // If the float doesn't actually fit in the column it will fail to be
6279 // placed, and either move to the block-start of the next column or just
6281 availBSize
= NS_UNCONSTRAINEDSIZE
;
6284 return LogicalRect(wm
, aState
.ContentIStart(), aState
.ContentBStart(),
6285 availISize
, availBSize
);
6288 nscoord
nsBlockFrame::ComputeFloatISize(BlockReflowInput
& aState
,
6289 const LogicalRect
& aFloatAvailableSpace
,
6291 MOZ_ASSERT(aFloat
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
6292 "aFloat must be an out-of-flow frame");
6294 // Reflow the float.
6295 LogicalRect availSpace
=
6296 AdjustFloatAvailableSpace(aState
, aFloatAvailableSpace
, aFloat
);
6298 WritingMode blockWM
= aState
.mReflowInput
.GetWritingMode();
6299 WritingMode floatWM
= aFloat
->GetWritingMode();
6300 ReflowInput
floatRS(aState
.mPresContext
, aState
.mReflowInput
, aFloat
,
6301 availSpace
.Size(blockWM
).ConvertTo(floatWM
, blockWM
));
6303 return floatRS
.ComputedSizeWithMarginBorderPadding(blockWM
).ISize(blockWM
);
6306 void nsBlockFrame::ReflowFloat(BlockReflowInput
& aState
,
6307 const LogicalRect
& aAdjustedAvailableSpace
,
6308 nsIFrame
* aFloat
, LogicalMargin
& aFloatMargin
,
6309 LogicalMargin
& aFloatOffsets
,
6310 bool aFloatPushedDown
,
6311 nsReflowStatus
& aReflowStatus
) {
6312 MOZ_ASSERT(aFloat
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
6313 "aFloat must be an out-of-flow frame");
6315 // Reflow the float.
6316 aReflowStatus
.Reset();
6318 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
6320 printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n", aFloat
,
6321 this, aAdjustedAvailableSpace
.IStart(wm
),
6322 aAdjustedAvailableSpace
.BStart(wm
), aAdjustedAvailableSpace
.ISize(wm
),
6323 aAdjustedAvailableSpace
.BSize(wm
));
6326 ReflowInput
floatRS(
6327 aState
.mPresContext
, aState
.mReflowInput
, aFloat
,
6328 aAdjustedAvailableSpace
.Size(wm
).ConvertTo(aFloat
->GetWritingMode(), wm
));
6330 // Normally the mIsTopOfPage state is copied from the parent reflow
6331 // input. However, when reflowing a float, if we've placed other
6332 // floats that force this float *down* or *narrower*, we should unset
6333 // the mIsTopOfPage state.
6334 // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
6335 // variable below, which has the exact same effect. Perhaps it should
6336 // be merged into that, except that the test for narrowing here is not
6337 // about adjacency with the top, so it seems misleading.
6338 if (floatRS
.mFlags
.mIsTopOfPage
&&
6339 (aFloatPushedDown
||
6340 aAdjustedAvailableSpace
.ISize(wm
) != aState
.ContentISize())) {
6341 floatRS
.mFlags
.mIsTopOfPage
= false;
6344 // Setup a block reflow context to reflow the float.
6345 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
6348 bool isAdjacentWithTop
= aState
.IsAdjacentWithTop();
6350 nsIFrame
* clearanceFrame
= nullptr;
6352 nsCollapsingMargin margin
;
6353 bool mayNeedRetry
= false;
6354 floatRS
.mDiscoveredClearance
= nullptr;
6355 // Only first in flow gets a block-start margin.
6356 if (!aFloat
->GetPrevInFlow()) {
6357 brc
.ComputeCollapsedBStartMargin(floatRS
, &margin
, clearanceFrame
,
6360 if (mayNeedRetry
&& !clearanceFrame
) {
6361 floatRS
.mDiscoveredClearance
= &clearanceFrame
;
6362 // We don't need to push the float manager state because the the block
6363 // has its own float manager that will be destroyed and recreated
6367 brc
.ReflowBlock(aAdjustedAvailableSpace
, true, margin
, 0, isAdjacentWithTop
,
6368 nullptr, floatRS
, aReflowStatus
, aState
);
6369 } while (clearanceFrame
);
6371 if (!aReflowStatus
.IsFullyComplete() && ShouldAvoidBreakInside(floatRS
)) {
6372 aReflowStatus
.SetInlineLineBreakBeforeAndReset();
6373 } else if (aReflowStatus
.IsIncomplete() &&
6374 (NS_UNCONSTRAINEDSIZE
== aAdjustedAvailableSpace
.BSize(wm
))) {
6375 // An incomplete reflow status means we should split the float
6376 // if the height is constrained (bug 145305).
6377 aReflowStatus
.Reset();
6380 if (aReflowStatus
.NextInFlowNeedsReflow()) {
6381 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
6384 if (aFloat
->IsLetterFrame()) {
6385 // We never split floating first letters; an incomplete state for
6386 // such frames simply means that there is more content to be
6387 // reflowed on the line.
6388 if (aReflowStatus
.IsIncomplete()) aReflowStatus
.Reset();
6391 // Capture the margin and offsets information for the caller
6393 // float margins don't collapse
6394 floatRS
.ComputedLogicalMargin().ConvertTo(wm
, floatRS
.GetWritingMode());
6396 floatRS
.ComputedLogicalOffsets().ConvertTo(wm
, floatRS
.GetWritingMode());
6398 const ReflowOutput
& metrics
= brc
.GetMetrics();
6400 // Set the rect, make sure the view is properly sized and positioned,
6401 // and tell the frame we're done reflowing it
6402 // XXXldb This seems like the wrong place to be doing this -- shouldn't
6403 // we be doing this in BlockReflowInput::FlowAndPlaceFloat after
6404 // we've positioned the float, and shouldn't we be doing the equivalent
6405 // of |PlaceFrameView| here?
6406 WritingMode metricsWM
= metrics
.GetWritingMode();
6407 aFloat
->SetSize(metricsWM
, metrics
.Size(metricsWM
));
6408 if (aFloat
->HasView()) {
6409 nsContainerFrame::SyncFrameViewAfterReflow(
6410 aState
.mPresContext
, aFloat
, aFloat
->GetView(),
6411 metrics
.VisualOverflow(), NS_FRAME_NO_MOVE_VIEW
);
6413 // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same
6415 aFloat
->DidReflow(aState
.mPresContext
, &floatRS
);
6418 printf("end ReflowFloat %p, sized to %d,%d\n", aFloat
, metrics
.Width(),
6423 StyleClear
nsBlockFrame::FindTrailingClear() {
6424 // find the break type of the last line
6425 for (nsIFrame
* b
= this; b
; b
= b
->GetPrevInFlow()) {
6426 nsBlockFrame
* block
= static_cast<nsBlockFrame
*>(b
);
6427 LineIterator endLine
= block
->LinesEnd();
6428 if (endLine
!= block
->LinesBegin()) {
6430 return endLine
->GetBreakTypeAfter();
6433 return StyleClear::None
;
6436 void nsBlockFrame::ReflowPushedFloats(BlockReflowInput
& aState
,
6437 nsOverflowAreas
& aOverflowAreas
,
6438 nsReflowStatus
& aStatus
) {
6439 // Pushed floats live at the start of our float list; see comment
6440 // above nsBlockFrame::DrainPushedFloats.
6441 nsIFrame
* f
= mFloats
.FirstChild();
6442 nsIFrame
* prev
= nullptr;
6443 while (f
&& (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
)) {
6444 MOZ_ASSERT(prev
== f
->GetPrevSibling());
6445 // When we push a first-continuation float in a non-initial reflow,
6446 // it's possible that we end up with two continuations with the same
6447 // parent. This happens if, on the previous reflow of the block or
6448 // a previous reflow of the line containing the block, the float was
6449 // split between continuations A and B of the parent, but on the
6450 // current reflow, none of the float can fit in A.
6452 // When this happens, we might even have the two continuations
6453 // out-of-order due to the management of the pushed floats. In
6454 // particular, if the float's placeholder was in a pushed line that
6455 // we reflowed before it was pushed, and we split the float during
6456 // that reflow, we might have the continuation of the float before
6457 // the float itself. (In the general case, however, it's correct
6458 // for floats in the pushed floats list to come before floats
6459 // anchored in pushed lines; however, in this case it's wrong. We
6460 // should probably find a way to fix it somehow, since it leads to
6461 // incorrect layout in some cases.)
6463 // When we have these out-of-order continuations, we might hit the
6464 // next-continuation before the previous-continuation. When that
6465 // happens, just push it. When we reflow the next continuation,
6466 // we'll either pull all of its content back and destroy it (by
6467 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
6468 // pull it out of its current position and push it again (and
6469 // potentially repeat this cycle for the next continuation, although
6470 // hopefully then they'll be in the right order).
6472 // We should also need this code for the in-order case if the first
6473 // continuation of a float gets moved across more than one
6474 // continuation of the containing block. In this case we'd manage
6475 // to push the second continuation without this check, but not the
6477 nsIFrame
* prevContinuation
= f
->GetPrevContinuation();
6478 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
6479 mFloats
.RemoveFrame(f
);
6480 aState
.AppendPushedFloatChain(f
);
6481 f
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
6485 // Always call FlowAndPlaceFloat; we might need to place this float
6486 // if didn't belong to this block the last time it was reflowed.
6487 aState
.FlowAndPlaceFloat(f
);
6488 ConsiderChildOverflow(aOverflowAreas
, f
);
6490 nsIFrame
* next
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
6492 // We didn't push |f| so its next-sibling is next.
6493 next
= f
->GetNextSibling();
6495 } // else: we did push |f| so |prev|'s new next-sibling is next.
6499 // If there are continued floats, then we may need to continue BR clearance
6500 if (0 != aState
.ClearFloats(0, StyleClear::Both
)) {
6501 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
6503 aState
.mFloatBreakType
= prevBlock
->FindTrailingClear();
6508 void nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
, WritingMode aWM
,
6509 const nsSize
& aContainerSize
) {
6510 // Recover our own floats
6511 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
6512 // belong to our next-in-flow
6513 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
;
6514 f
= f
->GetNextSibling()) {
6515 LogicalRect region
= nsFloatManager::GetRegionFor(aWM
, f
, aContainerSize
);
6516 aFloatManager
.AddFloat(f
, region
, aWM
, aContainerSize
);
6517 if (!stop
&& f
->GetNextInFlow()) stop
= f
->GetNextInFlow();
6520 // Recurse into our overflow container children
6521 for (nsIFrame
* oc
= GetChildList(kOverflowContainersList
).FirstChild(); oc
;
6522 oc
= oc
->GetNextSibling()) {
6523 RecoverFloatsFor(oc
, aFloatManager
, aWM
, aContainerSize
);
6526 // Recurse into our normal children
6527 for (nsBlockFrame::LineIterator line
= LinesBegin(); line
!= LinesEnd();
6529 if (line
->IsBlock()) {
6530 RecoverFloatsFor(line
->mFirstChild
, aFloatManager
, aWM
, aContainerSize
);
6535 void nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
6536 nsFloatManager
& aFloatManager
,
6538 const nsSize
& aContainerSize
) {
6539 MOZ_ASSERT(aFrame
, "null frame");
6541 // Only blocks have floats
6542 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
6543 // Don't recover any state inside a block that has its own float manager
6544 // (we don't currently have any blocks like this, though, thanks to our
6545 // use of extra frames for 'overflow')
6546 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
6547 // If the element is relatively positioned, then adjust x and y
6548 // accordingly so that we consider relatively positioned frames
6549 // at their original position.
6551 LogicalRect
rect(aWM
, block
->GetNormalRect(), aContainerSize
);
6552 nscoord lineLeft
= rect
.LineLeft(aWM
, aContainerSize
);
6553 nscoord blockStart
= rect
.BStart(aWM
);
6554 aFloatManager
.Translate(lineLeft
, blockStart
);
6555 block
->RecoverFloats(aFloatManager
, aWM
, aContainerSize
);
6556 aFloatManager
.Translate(-lineLeft
, -blockStart
);
6560 //////////////////////////////////////////////////////////////////////
6561 // Painting, event handling
6564 static void ComputeVisualOverflowArea(nsLineList
& aLines
, nscoord aWidth
,
6565 nscoord aHeight
, nsRect
& aResult
) {
6566 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
6567 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
6568 line
!= line_end
; ++line
) {
6569 // Compute min and max x/y values for the reflowed frame's
6571 nsRect
visOverflow(line
->GetVisualOverflowArea());
6572 nscoord x
= visOverflow
.x
;
6573 nscoord y
= visOverflow
.y
;
6574 nscoord xmost
= x
+ visOverflow
.width
;
6575 nscoord ymost
= y
+ visOverflow
.height
;
6592 aResult
.width
= xb
- xa
;
6593 aResult
.height
= yb
- ya
;
6598 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
6599 if (nsBlockFrame::gNoisyDamageRepair
) {
6600 nsFrame::IndentBy(stdout
, aDepth
+ 1);
6601 nsRect lineArea
= aLine
->GetVisualOverflowArea();
6602 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6603 aDrawn
? "draw" : "skip", static_cast<void*>(aLine
), aLine
->IStart(),
6604 aLine
->BStart(), aLine
->ISize(), aLine
->BSize(), lineArea
.x
,
6605 lineArea
.y
, lineArea
.width
, lineArea
.height
);
6610 static void DisplayLine(nsDisplayListBuilder
* aBuilder
,
6611 nsBlockFrame::LineIterator
& aLine
,
6612 const bool aLineInLine
, const nsDisplayListSet
& aLists
,
6613 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
,
6614 uint32_t aLineNumberForTextOverflow
, int32_t aDepth
,
6615 int32_t& aDrawnLines
) {
6617 if (nsBlockFrame::gLamePaintMetrics
) {
6620 const bool intersect
=
6621 aLine
->GetVisualOverflowArea().Intersects(aBuilder
->GetDirtyRect());
6622 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
6625 // Collect our line's display items in a temporary nsDisplayListCollection,
6626 // so that we can apply any "text-overflow" clipping to the entire collection
6627 // without affecting previous lines.
6628 nsDisplayListCollection
collection(aBuilder
);
6630 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
6631 // Inline-level child backgrounds go on the regular child content list.
6632 nsDisplayListSet
childLists(
6634 aLineInLine
? collection
.Content() : collection
.BlockBorderBackgrounds());
6636 uint32_t flags
= aLineInLine
? nsIFrame::DISPLAY_CHILD_INLINE
: 0;
6638 nsIFrame
* kid
= aLine
->mFirstChild
;
6639 int32_t n
= aLine
->GetChildCount();
6641 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, childLists
, flags
);
6642 kid
= kid
->GetNextSibling();
6645 if (aTextOverflow
&& aLineInLine
) {
6646 aTextOverflow
->ProcessLine(collection
, aLine
.get(),
6647 aLineNumberForTextOverflow
);
6650 collection
.MoveTo(aLists
);
6653 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
6654 const nsDisplayListSet
& aLists
) {
6655 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
6658 if (gNoisyDamageRepair
) {
6659 nsRect dirty
= aBuilder
->GetDirtyRect();
6662 ::ComputeVisualOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
6663 nsFrame::IndentBy(stdout
, depth
);
6665 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6666 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
, dirty
.x
, dirty
.y
,
6667 dirty
.width
, dirty
.height
, ca
.x
, ca
.y
, ca
.width
, ca
.height
);
6669 PRTime start
= 0; // Initialize these variables to silence the compiler.
6670 if (gLamePaintMetrics
) {
6676 // TODO(heycam): Should we boost the load priority of any shape-outside
6677 // images using CATEGORY_DISPLAY, now that this block is being displayed?
6678 // We don't have a float manager here.
6680 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
6682 if (GetPrevInFlow()) {
6683 DisplayOverflowContainers(aBuilder
, aLists
);
6684 for (nsIFrame
* f
: mFloats
) {
6685 if (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
)
6686 BuildDisplayListForChild(aBuilder
, f
, aLists
);
6690 aBuilder
->MarkFramesForDisplayList(this, mFloats
);
6692 if (HasOutsideMarker()) {
6693 // Display outside ::marker manually.
6694 BuildDisplayListForChild(aBuilder
, GetOutsideMarker(), aLists
);
6697 // Prepare for text-overflow processing.
6698 Maybe
<TextOverflow
> textOverflow
=
6699 TextOverflow::WillProcessLines(aBuilder
, this);
6701 const bool hasDescendantPlaceHolders
=
6702 (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
6703 ForceDescendIntoIfVisible() || aBuilder
->GetIncludeAllOutOfFlows();
6705 const auto ShouldDescendIntoLine
= [&](const nsRect
& aLineArea
) -> bool {
6706 // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
6707 // some frame trees, building display list for child lines can change it.
6709 const bool descendAlways
=
6710 (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
6711 aBuilder
->GetIncludeAllOutOfFlows();
6713 return descendAlways
|| aLineArea
.Intersects(aBuilder
->GetDirtyRect()) ||
6714 (ForceDescendIntoIfVisible() &&
6715 aLineArea
.Intersects(aBuilder
->GetVisibleRect()));
6718 // Don't use the line cursor if we might have a descendant placeholder ...
6719 // it might skip lines that contain placeholders but don't themselves
6720 // intersect with the dirty area.
6721 // In particular, we really want to check ShouldDescendIntoFrame()
6722 // on all our child frames, but that might be expensive. So we
6723 // approximate it by checking it on |this|; if it's true for any
6724 // frame in our child list, it's also true for |this|.
6725 // Also skip the cursor if we're creating text overflow markers,
6726 // since we need to know what line number we're up to in order
6727 // to generate unique display item keys.
6728 nsLineBox
* cursor
= (hasDescendantPlaceHolders
|| textOverflow
.isSome())
6730 : GetFirstLineContaining(aBuilder
->GetDirtyRect().y
);
6731 LineIterator line_end
= LinesEnd();
6733 TextOverflow
* textOverflowPtr
= textOverflow
.ptrOr(nullptr);
6736 for (LineIterator line
= mLines
.begin(cursor
); line
!= line_end
; ++line
) {
6737 const nsRect lineArea
= line
->GetVisualOverflowArea();
6738 if (!lineArea
.IsEmpty()) {
6739 // Because we have a cursor, the combinedArea.ys are non-decreasing.
6740 // Once we've passed aDirtyRect.YMost(), we can never see it again.
6741 if (lineArea
.y
>= aBuilder
->GetDirtyRect().YMost()) {
6744 MOZ_ASSERT(textOverflow
.isNothing());
6746 if (ShouldDescendIntoLine(lineArea
)) {
6747 DisplayLine(aBuilder
, line
, line
->IsInline(), aLists
, this, nullptr,
6748 0, depth
, drawnLines
);
6753 bool nonDecreasingYs
= true;
6754 uint32_t lineCount
= 0;
6755 nscoord lastY
= INT32_MIN
;
6756 nscoord lastYMost
= INT32_MIN
;
6757 for (LineIterator line
= LinesBegin(); line
!= line_end
; ++line
) {
6758 const nsRect lineArea
= line
->GetVisualOverflowArea();
6759 const bool lineInLine
= line
->IsInline();
6761 if ((lineInLine
&& textOverflowPtr
) || ShouldDescendIntoLine(lineArea
)) {
6762 DisplayLine(aBuilder
, line
, lineInLine
, aLists
, this, textOverflowPtr
,
6763 lineCount
, depth
, drawnLines
);
6766 if (!lineArea
.IsEmpty()) {
6767 if (lineArea
.y
< lastY
|| lineArea
.YMost() < lastYMost
) {
6768 nonDecreasingYs
= false;
6771 lastYMost
= lineArea
.YMost();
6776 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
6781 if (textOverflow
.isSome()) {
6782 // Put any text-overflow:ellipsis markers on top of the non-positioned
6783 // content of the block's lines. (If we ever start sorting the Content()
6784 // list this will end up in the wrong place.)
6785 aLists
.Content()->AppendToTop(&textOverflow
->GetMarkers());
6789 if (gLamePaintMetrics
) {
6790 PRTime end
= PR_Now();
6792 int32_t numLines
= mLines
.size();
6793 if (!numLines
) numLines
= 1;
6794 PRTime lines
, deltaPerLine
, delta
;
6795 lines
= int64_t(numLines
);
6796 delta
= end
- start
;
6797 deltaPerLine
= delta
/ lines
;
6802 ": %" PRId64
" elapsed (%" PRId64
6803 " per line) lines=%d drawn=%d skip=%d",
6804 delta
, deltaPerLine
, numLines
, drawnLines
,
6805 numLines
- drawnLines
);
6806 printf("%s\n", buf
);
6811 #ifdef ACCESSIBILITY
6812 a11y::AccType
nsBlockFrame::AccessibleType() {
6813 if (IsTableCaption()) {
6814 return GetRect().IsEmpty() ? a11y::eNoType
: a11y::eHTMLCaptionType
;
6817 // block frame may be for <hr>
6818 if (mContent
->IsHTMLElement(nsGkAtoms::hr
)) {
6819 return a11y::eHTMLHRType
;
6822 if (!HasMarker() || !PresContext()) {
6823 // XXXsmaug What if we're in the shadow dom?
6824 if (!mContent
->GetParent()) {
6825 // Don't create accessible objects for the root content node, they are
6826 // redundant with the nsDocAccessible object created with the document
6828 return a11y::eNoType
;
6831 if (mContent
== mContent
->OwnerDoc()->GetBody()) {
6832 // Don't create accessible objects for the body, they are redundant with
6833 // the nsDocAccessible object created with the document node
6834 return a11y::eNoType
;
6837 // Not a list item with a ::marker, treat as normal HTML container.
6838 return a11y::eHyperTextType
;
6841 // Create special list item accessible since we have a ::marker.
6842 return a11y::eHTMLLiType
;
6846 void nsBlockFrame::ClearLineCursor() {
6847 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
)) {
6851 DeleteProperty(LineCursorProperty());
6852 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
6855 void nsBlockFrame::SetupLineCursor() {
6856 if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
|| mLines
.empty()) {
6860 SetProperty(LineCursorProperty(), mLines
.front());
6861 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
6864 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
) {
6865 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
)) {
6869 nsLineBox
* property
= GetProperty(LineCursorProperty());
6870 LineIterator cursor
= mLines
.begin(property
);
6871 nsRect cursorArea
= cursor
->GetVisualOverflowArea();
6873 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
) &&
6874 cursor
!= mLines
.front()) {
6875 cursor
= cursor
.prev();
6876 cursorArea
= cursor
->GetVisualOverflowArea();
6878 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
) &&
6879 cursor
!= mLines
.back()) {
6880 cursor
= cursor
.next();
6881 cursorArea
= cursor
->GetVisualOverflowArea();
6884 if (cursor
.get() != property
) {
6885 SetProperty(LineCursorProperty(), cursor
.get());
6888 return cursor
.get();
6892 void nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
) {
6893 // See if the child is absolutely positioned
6894 if (aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
&&
6895 aChild
->IsAbsolutelyPositioned()) {
6897 } else if (aChild
== GetOutsideMarker()) {
6898 // The ::marker lives in the first line, unless the first line has
6899 // height 0 and there is a second line, in which case it lives
6900 // in the second line.
6901 LineIterator markerLine
= LinesBegin();
6902 if (markerLine
!= LinesEnd() && markerLine
->BSize() == 0 &&
6903 markerLine
!= mLines
.back()) {
6904 markerLine
= markerLine
.next();
6907 if (markerLine
!= LinesEnd()) {
6908 MarkLineDirty(markerLine
, &mLines
);
6910 // otherwise we have an empty line list, and ReflowDirtyLines
6911 // will handle reflowing the ::marker.
6913 // Note that we should go through our children to mark lines dirty
6914 // before the next reflow. Doing it now could make things O(N^2)
6915 // since finding the right line is O(N).
6916 // We don't need to worry about marking lines on the overflow list
6917 // as dirty; we're guaranteed to reflow them if we take them off the
6919 // However, we might have gotten a float, in which case we need to
6920 // reflow the line containing its placeholder. So find the
6921 // ancestor-or-self of the placeholder that's a child of the block,
6922 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
6923 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6924 // We need to take some care to handle the case where a float is in
6925 // a different continuation than its placeholder, including marking
6926 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6927 if (!(aChild
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
6928 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
6930 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
6931 nsIFrame
* thisFC
= FirstContinuation();
6932 nsIFrame
* placeholderPath
= aChild
->GetPlaceholderFrame();
6933 // SVG code sometimes sends FrameNeedsReflow notifications during
6934 // frame destruction, leading to null placeholders, but we're safe
6936 if (placeholderPath
) {
6938 nsIFrame
* parent
= placeholderPath
->GetParent();
6939 if (parent
->GetContent() == mContent
&&
6940 parent
->FirstContinuation() == thisFC
) {
6941 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
6944 placeholderPath
= parent
;
6946 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
6951 nsContainerFrame::ChildIsDirty(aChild
);
6954 void nsBlockFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
6955 nsIFrame
* aPrevInFlow
) {
6957 // Copy over the inherited block frame bits from the prev-in-flow.
6958 RemoveStateBits(NS_BLOCK_FLAGS_MASK
);
6959 AddStateBits(aPrevInFlow
->GetStateBits() &
6960 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
6963 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
6966 aPrevInFlow
->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION
) {
6967 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
6970 // A display:flow-root box establishes a block formatting context.
6971 // If a box has a different block flow direction than its containing block:
6973 // If the box is a block container, then it establishes a new block
6974 // formatting context.
6975 // (http://dev.w3.org/csswg/css-writing-modes/#block-flow)
6977 // If the box has contain: paint or contain:layout (or contain:strict),
6978 // then it should also establish a formatting context.
6980 // Per spec, a column-span always establishes a new block formatting context.
6981 if (StyleDisplay()->mDisplay
== mozilla::StyleDisplay::FlowRoot
||
6982 (GetParent() && StyleVisibility()->mWritingMode
!=
6983 GetParent()->StyleVisibility()->mWritingMode
) ||
6984 StyleDisplay()->IsContainPaint() || StyleDisplay()->IsContainLayout() ||
6985 (StaticPrefs::layout_css_column_span_enabled() && IsColumnSpan())) {
6986 AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
6989 if ((GetStateBits() &
6990 (NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) ==
6991 (NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) {
6992 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
6996 void nsBlockFrame::SetInitialChildList(ChildListID aListID
,
6997 nsFrameList
& aChildList
) {
6998 if (kFloatList
== aListID
) {
6999 mFloats
.SetFrames(aChildList
);
7000 } else if (kPrincipalList
== aListID
) {
7002 // The only times a block that is an anonymous box is allowed to have a
7003 // first-letter frame are when it's the block inside a non-anonymous cell,
7004 // the block inside a fieldset, button or column set, or a scrolled content
7005 // block, except for <select>. Note that this means that blocks which are
7006 // the anonymous block in {ib} splits do NOT get first-letter frames.
7007 // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7009 auto pseudo
= Style()->GetPseudoType();
7010 bool haveFirstLetterStyle
=
7011 (pseudo
== PseudoStyleType::NotPseudo
||
7012 (pseudo
== PseudoStyleType::cellContent
&&
7013 !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7014 pseudo
== PseudoStyleType::fieldsetContent
||
7015 pseudo
== PseudoStyleType::buttonContent
||
7016 pseudo
== PseudoStyleType::columnContent
||
7017 (pseudo
== PseudoStyleType::scrolledContent
&&
7018 !GetParent()->IsListControlFrame()) ||
7019 pseudo
== PseudoStyleType::mozSVGText
) &&
7020 !IsComboboxControlFrame() && !IsFrameOfType(eMathML
) &&
7021 !IsColumnSetWrapperFrame() &&
7022 RefPtr
<ComputedStyle
>(GetFirstLetterStyle(PresContext())) != nullptr;
7023 NS_ASSERTION(haveFirstLetterStyle
==
7024 ((mState
& NS_BLOCK_HAS_FIRST_LETTER_STYLE
) != 0),
7025 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7028 AddFrames(aChildList
, nullptr);
7030 nsContainerFrame::SetInitialChildList(aListID
, aChildList
);
7034 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame
* aMarkerFrame
) {
7035 MOZ_ASSERT(aMarkerFrame
);
7036 MOZ_ASSERT((GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_MARKER
|
7037 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
)) == 0,
7038 "How can we have a ::marker frame already?");
7040 if (StyleList()->mListStylePosition
== NS_STYLE_LIST_STYLE_POSITION_INSIDE
) {
7041 SetProperty(InsideMarkerProperty(), aMarkerFrame
);
7042 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
);
7044 SetProperty(OutsideMarkerProperty(),
7045 new (PresShell()) nsFrameList(aMarkerFrame
, aMarkerFrame
));
7046 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
7050 bool nsBlockFrame::MarkerIsEmpty() const {
7051 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->mDisplay
==
7052 mozilla::StyleDisplay::ListItem
&&
7054 "should only care when we have an outside ::marker");
7055 nsIFrame
* marker
= GetMarker();
7056 const nsStyleList
* list
= marker
->StyleList();
7057 return list
->mCounterStyle
.IsNone() && !list
->GetListStyleImage() &&
7058 marker
->StyleContent()->ContentCount() == 0;
7061 #ifdef ACCESSIBILITY
7062 void nsBlockFrame::GetSpokenMarkerText(nsAString
& aText
) const {
7063 const nsStyleList
* myList
= StyleList();
7064 if (myList
->GetListStyleImage()) {
7065 aText
.Assign(kDiscCharacter
);
7068 if (nsIFrame
* marker
= GetMarker()) {
7069 if (nsBulletFrame
* bullet
= do_QueryFrame(marker
)) {
7070 bullet
->GetSpokenText(aText
);
7073 marker
->GetContent()->GetTextContent(aText
, err
);
7085 void nsBlockFrame::ReflowOutsideMarker(nsIFrame
* aMarkerFrame
,
7086 BlockReflowInput
& aState
,
7087 ReflowOutput
& aMetrics
,
7089 const ReflowInput
& ri
= aState
.mReflowInput
;
7091 WritingMode markerWM
= aMarkerFrame
->GetWritingMode();
7092 LogicalSize
availSize(markerWM
);
7093 // Make up an inline-size since it doesn't really matter (XXX).
7094 availSize
.ISize(markerWM
) = aState
.ContentISize();
7095 availSize
.BSize(markerWM
) = NS_UNCONSTRAINEDSIZE
;
7097 ReflowInput
reflowInput(aState
.mPresContext
, ri
, aMarkerFrame
, availSize
,
7098 Nothing(), ReflowInput::COMPUTE_SIZE_SHRINK_WRAP
);
7099 nsReflowStatus status
;
7100 aMarkerFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowInput
, status
);
7102 // Get the float available space using our saved state from before we
7103 // started reflowing the block, so that we ignore any floats inside
7105 // FIXME: aLineTop isn't actually set correctly by some callers, since
7106 // they reposition the line.
7107 LogicalRect floatAvailSpace
=
7109 .GetFloatAvailableSpaceWithState(aLineTop
, ShapeType::ShapeOutside
,
7110 &aState
.mFloatManagerStateBefore
)
7112 // FIXME (bug 25888): need to check the entire region that the first
7113 // line overlaps, not just the top pixel.
7115 // Place the ::marker now. We want to place the ::marker relative to the
7116 // border-box of the associated block (using the right/left margin of
7117 // the ::marker frame as separation). However, if a line box would be
7118 // displaced by floats that are *outside* the associated block, we
7119 // want to displace it by the same amount. That is, we act as though
7120 // the edge of the floats is the content-edge of the block, and place
7121 // the ::marker at a position offset from there by the block's padding,
7122 // the block's border, and the ::marker frame's margin.
7124 // IStart from floatAvailSpace gives us the content/float start edge
7125 // in the current writing mode. Then we subtract out the start
7126 // border/padding and the ::marker's width and margin to offset the position.
7127 WritingMode wm
= ri
.GetWritingMode();
7128 // Get the ::marker's margin, converted to our writing mode so that we can
7129 // combine it with other logical values here.
7130 LogicalMargin markerMargin
=
7131 reflowInput
.ComputedLogicalMargin().ConvertTo(wm
, markerWM
);
7132 nscoord iStart
= floatAvailSpace
.IStart(wm
) -
7133 ri
.ComputedLogicalBorderPadding().IStart(wm
) -
7134 markerMargin
.IEnd(wm
) - aMetrics
.ISize(wm
);
7136 // Approximate the ::marker's position; vertical alignment will provide
7137 // the final vertical location. We pass our writing-mode here, because
7138 // it may be different from the ::marker frame's mode.
7139 nscoord bStart
= floatAvailSpace
.BStart(wm
);
7140 aMarkerFrame
->SetRect(
7142 LogicalRect(wm
, iStart
, bStart
, aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)),
7143 aState
.ContainerSize());
7144 aMarkerFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowInput
);
7147 // This is used to scan frames for any float placeholders, add their
7148 // floats to the list represented by aList, and remove the
7149 // floats from whatever list they might be in. We don't search descendants
7150 // that are float containing blocks. Floats that or not children of 'this'
7151 // are ignored (they are not added to aList).
7152 void nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
7153 bool aCollectSiblings
) {
7155 // Don't descend into float containing blocks.
7156 if (!aFrame
->IsFloatContainingBlock()) {
7157 nsIFrame
* outOfFlowFrame
=
7158 aFrame
->IsPlaceholderFrame()
7159 ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame
)
7161 while (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
7162 RemoveFloat(outOfFlowFrame
);
7163 // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
7164 // the PushedFloats list.
7165 outOfFlowFrame
->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
7166 aList
.AppendFrame(nullptr, outOfFlowFrame
);
7167 outOfFlowFrame
= outOfFlowFrame
->GetNextInFlow();
7168 // FIXME: By not pulling floats whose parent is one of our
7169 // later siblings, are we risking the pushed floats getting
7171 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
7174 DoCollectFloats(aFrame
->PrincipalChildList().FirstChild(), aList
, true);
7175 DoCollectFloats(aFrame
->GetChildList(kOverflowList
).FirstChild(), aList
,
7178 if (!aCollectSiblings
) break;
7179 aFrame
= aFrame
->GetNextSibling();
7183 void nsBlockFrame::CheckFloats(BlockReflowInput
& aState
) {
7185 // If any line is still dirty, that must mean we're going to reflow this
7186 // block again soon (e.g. because we bailed out after noticing that
7187 // clearance was imposed), so don't worry if the floats are out of sync.
7188 bool anyLineDirty
= false;
7190 // Check that the float list is what we would have built
7191 AutoTArray
<nsIFrame
*, 8> lineFloats
;
7192 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
7193 line
!= line_end
; ++line
) {
7194 if (line
->HasFloats()) {
7195 nsFloatCache
* fc
= line
->GetFirstFloat();
7197 lineFloats
.AppendElement(fc
->mFloat
);
7201 if (line
->IsDirty()) {
7202 anyLineDirty
= true;
7206 AutoTArray
<nsIFrame
*, 8> storedFloats
;
7209 for (nsIFrame
* f
: mFloats
) {
7210 if (f
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
) continue;
7211 storedFloats
.AppendElement(f
);
7212 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
7218 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) &&
7221 "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
7223 # if defined(DEBUG_roc)
7224 nsFrame::RootFrameList(PresContext(), stdout
, 0);
7225 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
7226 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
7228 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
7229 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
7235 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
7236 if (oofs
&& oofs
->NotEmpty()) {
7237 // Floats that were pushed should be removed from our float
7238 // manager. Otherwise the float manager's YMost or XMost might
7239 // be larger than necessary, causing this block to get an
7240 // incorrect desired height (or width). Some of these floats
7241 // may not actually have been added to the float manager because
7242 // they weren't reflowed before being pushed; that's OK,
7243 // RemoveRegions will ignore them. It is safe to do this here
7244 // because we know from here on the float manager will only be
7245 // used for its XMost and YMost, not to place new floats and
7247 aState
.FloatManager()->RemoveTrailingRegions(oofs
->FirstChild());
7251 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot
,
7252 bool* aBEndMarginRoot
) {
7253 if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT
)) {
7254 nsIFrame
* parent
= GetParent();
7255 if (!parent
|| parent
->IsFloatContainingBlock()) {
7256 *aBStartMarginRoot
= false;
7257 *aBEndMarginRoot
= false;
7260 if (parent
->IsColumnSetFrame()) {
7261 *aBStartMarginRoot
= GetPrevInFlow() == nullptr;
7262 *aBEndMarginRoot
= GetNextInFlow() == nullptr;
7267 *aBStartMarginRoot
= true;
7268 *aBEndMarginRoot
= true;
7272 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
) {
7273 MOZ_ASSERT(aBlock
, "Must have a frame");
7274 NS_ASSERTION(aBlock
->IsBlockFrameOrSubclass(), "aBlock must be a block");
7276 nsIFrame
* parent
= aBlock
->GetParent();
7277 return (aBlock
->GetStateBits() & NS_BLOCK_FLOAT_MGR
) ||
7278 (parent
&& !parent
->IsFloatContainingBlock());
7282 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
) {
7283 return aFrame
->IsBlockFrameOrSubclass() &&
7284 !aFrame
->IsFrameOfType(nsIFrame::eReplaced
) &&
7285 !(aFrame
->GetStateBits() & NS_BLOCK_FLOAT_MGR
);
7288 // Note that this width can vary based on the vertical position.
7289 // However, the cases where it varies are the cases where the width fits
7290 // in the available space given, which means that variation shouldn't
7293 nsBlockFrame::ReplacedElementISizeToClear
nsBlockFrame::ISizeToClearPastFloats(
7294 const BlockReflowInput
& aState
, const LogicalRect
& aFloatAvailableSpace
,
7296 nscoord inlineStartOffset
, inlineEndOffset
;
7297 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
7298 SizeComputationInput
offsetState(aFrame
,
7299 aState
.mReflowInput
.mRenderingContext
, wm
,
7300 aState
.mContentArea
.ISize(wm
));
7302 ReplacedElementISizeToClear result
;
7303 aState
.ComputeReplacedBlockOffsetsForFloats(
7304 aFrame
, aFloatAvailableSpace
, inlineStartOffset
, inlineEndOffset
);
7305 nscoord availISize
=
7306 aState
.mContentArea
.ISize(wm
) - inlineStartOffset
- inlineEndOffset
;
7308 // We actually don't want the min width here; see bug 427782; we only
7309 // want to displace if the width won't compute to a value small enough
7311 // All we really need here is the result of ComputeSize, and we
7312 // could *almost* get that from an SizeComputationInput, except for the
7314 WritingMode frWM
= aFrame
->GetWritingMode();
7315 LogicalSize availSpace
=
7316 LogicalSize(wm
, availISize
, NS_UNCONSTRAINEDSIZE
).ConvertTo(frWM
, wm
);
7317 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, aFrame
,
7319 result
.borderBoxISize
=
7320 reflowInput
.ComputedSizeWithBorderPadding().ConvertTo(wm
, frWM
).ISize(wm
);
7321 // Use the margins from offsetState rather than reflowInput so that
7322 // they aren't reduced by ignoring margins in overconstrained cases.
7323 LogicalMargin computedMargin
=
7324 offsetState
.ComputedLogicalMargin().ConvertTo(wm
, frWM
);
7325 result
.marginIStart
= computedMargin
.IStart(wm
);
7330 nsBlockFrame
* nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
) {
7331 nsBlockFrame
* block
= nullptr;
7332 while (aCandidate
) {
7333 block
= do_QueryFrame(aCandidate
);
7335 // yay, candidate is a block!
7338 // Not a block. Check its parent next.
7339 aCandidate
= aCandidate
->GetParent();
7341 MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
7345 void nsBlockFrame::ComputeFinalBSize(const ReflowInput
& aReflowInput
,
7346 nsReflowStatus
* aStatus
,
7347 nscoord aContentBSize
,
7348 const LogicalMargin
& aBorderPadding
,
7349 LogicalSize
& aFinalSize
,
7350 nscoord aConsumed
) {
7351 WritingMode wm
= aReflowInput
.GetWritingMode();
7352 // Figure out how much of the computed block-size should be
7353 // applied to this frame.
7354 const nscoord computedBSizeLeftOver
=
7355 GetEffectiveComputedBSize(aReflowInput
, aConsumed
);
7356 NS_ASSERTION(!(IS_TRUE_OVERFLOW_CONTAINER(this) && computedBSizeLeftOver
),
7357 "overflow container must not have computedBSizeLeftOver");
7359 aFinalSize
.BSize(wm
) = NSCoordSaturatingAdd(
7360 NSCoordSaturatingAdd(aBorderPadding
.BStart(wm
), computedBSizeLeftOver
),
7361 aBorderPadding
.BEnd(wm
));
7363 if (aStatus
->IsIncomplete() &&
7364 aFinalSize
.BSize(wm
) <= aReflowInput
.AvailableBSize()) {
7365 // We used up all of our element's remaining computed block-size on this
7366 // page/column, but we're incomplete. Set status to complete except for
7368 aStatus
->SetOverflowIncomplete();
7372 if (aStatus
->IsComplete()) {
7373 if (computedBSizeLeftOver
> 0 &&
7374 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize() &&
7375 aFinalSize
.BSize(wm
) > aReflowInput
.AvailableBSize()) {
7376 if (ShouldAvoidBreakInside(aReflowInput
)) {
7377 aStatus
->SetInlineLineBreakBeforeAndReset();
7381 // Our leftover block-size does not fit into the available block-size.
7382 // Change aStatus to incomplete to let the logic at the end of this method
7383 // calculate the correct block-size.
7384 aStatus
->SetIncomplete();
7385 if (!GetNextInFlow()) {
7386 aStatus
->SetNextInFlowNeedsReflow();
7391 if (aStatus
->IsIncomplete()) {
7392 MOZ_ASSERT(aFinalSize
.BSize(wm
) > aReflowInput
.AvailableBSize(),
7393 "We should be overflow incomplete and should've returned "
7394 "in early if-branch!");
7396 // Use the current block-size; continuations will take up the rest.
7397 // Do extend the block-size to at least consume the available block-size,
7398 // otherwise our left/right borders (for example) won't extend all the way
7400 aFinalSize
.BSize(wm
) =
7401 std::max(aReflowInput
.AvailableBSize(), aContentBSize
);
7402 // ... but don't take up more block size than is available
7403 aFinalSize
.BSize(wm
) =
7404 std::min(aFinalSize
.BSize(wm
),
7405 aBorderPadding
.BStart(wm
) + computedBSizeLeftOver
);
7406 // XXX It's pretty wrong that our bottom border still gets drawn on
7407 // on its own on the last-in-flow, even if we ran out of height
7408 // here. We need GetSkipSides to check whether we ran out of content
7409 // height in the current frame, not whether it's last-in-flow.
7411 // XXX aBorderPadding.BEnd(wm) is not considered here, so
7412 // "box-decoration-break: clone" may not render correctly.
7416 nsresult
nsBlockFrame::ResolveBidi() {
7417 NS_ASSERTION(!GetPrevInFlow(),
7418 "ResolveBidi called on non-first continuation");
7420 nsPresContext
* presContext
= PresContext();
7421 if (!presContext
->BidiEnabled()) {
7425 return nsBidiPresUtils::Resolve(this);
7428 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState
& aRestyleState
) {
7429 // first-letter needs to be updated before first-line, because first-line can
7430 // change the style of the first-letter.
7431 if (HasFirstLetterChild()) {
7432 UpdateFirstLetterStyle(aRestyleState
);
7435 if (nsIFrame
* firstLineFrame
= GetFirstLineFrame()) {
7436 nsIFrame
* styleParent
= CorrectStyleParentFrame(firstLineFrame
->GetParent(),
7437 PseudoStyleType::firstLine
);
7439 ComputedStyle
* parentStyle
= styleParent
->Style();
7440 RefPtr
<ComputedStyle
> firstLineStyle
=
7441 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
7442 *mContent
->AsElement(), PseudoStyleType::firstLine
, parentStyle
);
7444 // FIXME(bz): Can we make first-line continuations be non-inheriting anon
7446 RefPtr
<ComputedStyle
> continuationStyle
=
7447 aRestyleState
.StyleSet().ResolveInheritingAnonymousBoxStyle(
7448 PseudoStyleType::mozLineFrame
, parentStyle
);
7450 UpdateStyleOfOwnedChildFrame(firstLineFrame
, firstLineStyle
, aRestyleState
,
7451 Some(continuationStyle
.get()));
7453 // We also want to update the styles of the first-line's descendants. We
7454 // don't need to compute a changehint for this, though, since any changes to
7455 // them are handled by the first-line anyway.
7456 RestyleManager
* manager
= PresContext()->RestyleManager();
7457 for (nsIFrame
* kid
: firstLineFrame
->PrincipalChildList()) {
7458 manager
->ReparentComputedStyleForFirstLine(kid
);
7463 nsIFrame
* nsBlockFrame::GetFirstLetter() const {
7464 if (!(GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
7465 // Certainly no first-letter frame.
7469 return GetProperty(FirstLetterProperty());
7472 nsIFrame
* nsBlockFrame::GetFirstLineFrame() const {
7473 nsIFrame
* maybeFirstLine
= PrincipalChildList().FirstChild();
7474 if (maybeFirstLine
&& maybeFirstLine
->IsLineFrame()) {
7475 return maybeFirstLine
;
7482 void nsBlockFrame::VerifyLines(bool aFinalCheckOK
) {
7483 if (!gVerifyLines
) {
7486 if (mLines
.empty()) {
7490 nsLineBox
* cursor
= GetLineCursor();
7492 // Add up the counts on each line. Also validate that IsFirstLine is
7495 LineIterator line
, line_end
;
7496 for (line
= LinesBegin(), line_end
= LinesEnd(); line
!= line_end
; ++line
) {
7497 if (line
== cursor
) {
7500 if (aFinalCheckOK
) {
7501 MOZ_ASSERT(line
->GetChildCount(), "empty line");
7502 if (line
->IsBlock()) {
7503 NS_ASSERTION(1 == line
->GetChildCount(), "bad first line");
7506 count
+= line
->GetChildCount();
7509 // Then count the frames
7510 int32_t frameCount
= 0;
7511 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
7514 frame
= frame
->GetNextSibling();
7516 NS_ASSERTION(count
== frameCount
, "bad line list");
7518 // Next: test that each line has right number of frames on it
7519 for (line
= LinesBegin(), line_end
= LinesEnd(); line
!= line_end
;) {
7520 count
= line
->GetChildCount();
7521 frame
= line
->mFirstChild
;
7522 while (--count
>= 0) {
7523 frame
= frame
->GetNextSibling();
7526 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
7527 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
7532 FrameLines
* overflowLines
= GetOverflowLines();
7533 if (overflowLines
) {
7534 LineIterator line
= overflowLines
->mLines
.begin();
7535 LineIterator line_end
= overflowLines
->mLines
.end();
7536 for (; line
!= line_end
; ++line
) {
7537 if (line
== cursor
) {
7544 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
7547 void nsBlockFrame::VerifyOverflowSituation() {
7548 // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
7549 nsFrameList
* oofs
= GetOverflowOutOfFlows();
7551 for (nsFrameList::Enumerator
e(*oofs
); !e
.AtEnd(); e
.Next()) {
7552 nsIFrame
* nif
= e
.get()->GetNextInFlow();
7554 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
7558 // Pushed floats must not have a next-in-flow in mFloats or mFrames.
7559 oofs
= GetPushedFloats();
7561 for (nsFrameList::Enumerator
e(*oofs
); !e
.AtEnd(); e
.Next()) {
7562 nsIFrame
* nif
= e
.get()->GetNextInFlow();
7564 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
7568 // A child float next-in-flow's parent must be |this| or a next-in-flow of
7569 // |this|. Later next-in-flows must have the same or later parents.
7570 nsIFrame::ChildListID childLists
[] = {nsIFrame::kFloatList
,
7571 nsIFrame::kPushedFloatsList
};
7572 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
7573 nsFrameList
children(GetChildList(childLists
[i
]));
7574 for (nsFrameList::Enumerator
e(children
); !e
.AtEnd(); e
.Next()) {
7575 nsIFrame
* parent
= this;
7576 nsIFrame
* nif
= e
.get()->GetNextInFlow();
7577 for (; nif
; nif
= nif
->GetNextInFlow()) {
7579 for (nsIFrame
* p
= parent
; p
; p
= p
->GetNextInFlow()) {
7580 if (nif
->GetParent() == p
) {
7588 "next-in-flow is a child of parent earlier in the frame tree?");
7593 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
7595 FrameLines
* overflowLines
= flow
->GetOverflowLines();
7596 if (overflowLines
) {
7597 NS_ASSERTION(!overflowLines
->mLines
.empty(),
7598 "should not be empty if present");
7599 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
7600 "bad overflow lines");
7601 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
7602 overflowLines
->mFrames
.FirstChild(),
7603 "bad overflow frames / lines");
7605 nsLineBox
* cursor
= flow
->GetLineCursor();
7607 LineIterator line
= flow
->LinesBegin();
7608 LineIterator line_end
= flow
->LinesEnd();
7609 for (; line
!= line_end
&& line
!= cursor
; ++line
)
7611 if (line
== line_end
&& overflowLines
) {
7612 line
= overflowLines
->mLines
.begin();
7613 line_end
= overflowLines
->mLines
.end();
7614 for (; line
!= line_end
&& line
!= cursor
; ++line
)
7617 MOZ_ASSERT(line
!= line_end
, "stale LineCursorProperty");
7619 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
7623 int32_t nsBlockFrame::GetDepth() const {
7625 nsIFrame
* parent
= GetParent();
7627 parent
= parent
->GetParent();
7633 already_AddRefed
<ComputedStyle
> nsBlockFrame::GetFirstLetterStyle(
7634 nsPresContext
* aPresContext
) {
7635 return aPresContext
->StyleSet()->ProbePseudoElementStyle(
7636 *mContent
->AsElement(), PseudoStyleType::firstLetter
, Style());