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/AppUnits.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/Maybe.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/StaticPrefs_browser.h"
22 #include "mozilla/StaticPrefs_layout.h"
23 #include "mozilla/SVGUtils.h"
24 #include "mozilla/ToString.h"
25 #include "mozilla/UniquePtr.h"
29 #include "nsCSSRendering.h"
30 #include "nsAbsoluteContainingBlock.h"
31 #include "nsBlockReflowContext.h"
32 #include "BlockReflowState.h"
33 #include "nsFontMetrics.h"
34 #include "nsGenericHTMLElement.h"
35 #include "nsLineBox.h"
36 #include "nsLineLayout.h"
37 #include "nsPlaceholderFrame.h"
38 #include "nsStyleConsts.h"
39 #include "nsFrameManager.h"
40 #include "nsPresContext.h"
41 #include "nsPresContextInlines.h"
42 #include "nsHTMLParts.h"
43 #include "nsGkAtoms.h"
44 #include "nsAttrValueInlines.h"
45 #include "mozilla/Sprintf.h"
46 #include "nsFloatManager.h"
49 #include "nsIScrollableFrame.h"
51 #include "nsLayoutUtils.h"
52 #include "nsDisplayList.h"
53 #include "nsCSSAnonBoxes.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "TextOverflow.h"
56 #include "nsIFrameInlines.h"
57 #include "CounterStyleManager.h"
58 #include "mozilla/dom/HTMLDetailsElement.h"
59 #include "mozilla/dom/HTMLSummaryElement.h"
60 #include "mozilla/dom/Selection.h"
61 #include "mozilla/PresShell.h"
62 #include "mozilla/RestyleManager.h"
63 #include "mozilla/ServoStyleSet.h"
64 #include "mozilla/Telemetry.h"
65 #include "nsFlexContainerFrame.h"
67 #include "nsBidiPresUtils.h"
71 static const int MIN_LINES_NEEDING_CURSOR
= 20;
73 using namespace mozilla
;
74 using namespace mozilla::css
;
75 using namespace mozilla::dom
;
76 using namespace mozilla::layout
;
77 using AbsPosReflowFlags
= nsAbsoluteContainingBlock::AbsPosReflowFlags
;
78 using ClearFloatsResult
= BlockReflowState::ClearFloatsResult
;
79 using ShapeType
= nsFloatManager::ShapeType
;
81 static void MarkAllDescendantLinesDirty(nsBlockFrame
* aBlock
) {
82 for (auto& line
: aBlock
->Lines()) {
84 nsBlockFrame
* bf
= do_QueryFrame(line
.mFirstChild
);
86 MarkAllDescendantLinesDirty(bf
);
93 static void MarkSameFloatManagerLinesDirty(nsBlockFrame
* aBlock
) {
94 nsBlockFrame
* blockWithFloatMgr
= aBlock
;
95 while (!blockWithFloatMgr
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
)) {
96 nsBlockFrame
* bf
= do_QueryFrame(blockWithFloatMgr
->GetParent());
100 blockWithFloatMgr
= bf
;
103 // Mark every line at and below the line where the float was
104 // dirty, and mark their lines dirty too. We could probably do
105 // something more efficient --- e.g., just dirty the lines that intersect
106 // the float vertically.
107 MarkAllDescendantLinesDirty(blockWithFloatMgr
);
111 * Returns true if aFrame is a block that has one or more float children.
113 static bool BlockHasAnyFloats(nsIFrame
* aFrame
) {
114 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
118 if (block
->GetChildList(FrameChildListID::Float
).FirstChild()) {
122 for (const auto& line
: block
->Lines()) {
123 if (line
.IsBlock() && BlockHasAnyFloats(line
.mFirstChild
)) {
131 * Determines whether the given frame is visible or has
132 * visible children that participate in the same line. Frames
133 * that are not line participants do not have their
136 static bool FrameHasVisibleInlineContent(nsIFrame
* aFrame
) {
137 MOZ_ASSERT(aFrame
, "Frame argument cannot be null");
139 if (aFrame
->StyleVisibility()->IsVisible()) {
143 if (aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
)) {
144 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
145 if (kid
->StyleVisibility()->IsVisible() ||
146 FrameHasVisibleInlineContent(kid
)) {
155 * Determines whether any of the frames descended from the
156 * given line have inline content with 'visibility: visible'.
157 * This function calls FrameHasVisibleInlineContent to process
158 * each frame in the line's child list.
160 static bool LineHasVisibleInlineContent(nsLineBox
* aLine
) {
161 nsIFrame
* kid
= aLine
->mFirstChild
;
162 int32_t n
= aLine
->GetChildCount();
164 if (FrameHasVisibleInlineContent(kid
)) {
168 kid
= kid
->GetNextSibling();
175 * Iterates through the frame's in-flow children and
176 * unions the ink overflow of all text frames which
177 * participate in the line aFrame belongs to.
178 * If a child of aFrame is not a text frame,
179 * we recurse with the child as the aFrame argument.
180 * If aFrame isn't a line participant, we skip it entirely
181 * and return an empty rect.
182 * The resulting nsRect is offset relative to the parent of aFrame.
184 static nsRect
GetFrameTextArea(nsIFrame
* aFrame
,
185 nsDisplayListBuilder
* aBuilder
) {
187 if (const nsTextFrame
* textFrame
= do_QueryFrame(aFrame
)) {
188 if (!textFrame
->IsEntirelyWhitespace()) {
189 textArea
= aFrame
->InkOverflowRect();
191 } else if (aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
)) {
192 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
193 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
194 textArea
.OrWith(kidTextArea
);
197 // add aFrame's position to keep textArea relative to aFrame's parent
198 return textArea
+ aFrame
->GetPosition();
202 * Iterates through the line's children and
203 * unions the ink overflow of all text frames.
204 * GetFrameTextArea unions and returns the ink overflow
205 * from all line-participating text frames within the given child.
206 * The nsRect returned from GetLineTextArea is offset
207 * relative to the given line.
209 static nsRect
GetLineTextArea(nsLineBox
* aLine
,
210 nsDisplayListBuilder
* aBuilder
) {
212 nsIFrame
* kid
= aLine
->mFirstChild
;
213 int32_t n
= aLine
->GetChildCount();
215 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
216 textArea
.OrWith(kidTextArea
);
217 kid
= kid
->GetNextSibling();
224 * Starting with aFrame, iterates upward through parent frames and checks for
225 * non-transparent background colors. If one is found, we use that as our
226 * backplate color. Otheriwse, we use the default background color from
227 * our high contrast theme.
229 static nscolor
GetBackplateColor(nsIFrame
* aFrame
) {
230 nsPresContext
* pc
= aFrame
->PresContext();
231 nscolor currentBackgroundColor
= NS_TRANSPARENT
;
232 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent()) {
233 // NOTE(emilio): We assume themed frames (frame->IsThemed()) have correct
234 // background-color information so as to compute the right backplate color.
236 // This holds because HTML widgets with author-specified backgrounds or
237 // borders disable theming. So as long as the UA-specified background colors
238 // match the actual theme (which they should because we always use system
239 // colors with the non-native theme, and native system colors should also
240 // match the native theme), then we're alright and we should compute an
241 // appropriate backplate color.
242 const auto* style
= frame
->Style();
243 if (style
->StyleBackground()->IsTransparent(style
)) {
246 bool drawImage
= false, drawColor
= false;
247 nscolor backgroundColor
= nsCSSRendering::DetermineBackgroundColor(
248 pc
, style
, frame
, drawImage
, drawColor
);
249 if (!drawColor
&& !drawImage
) {
252 if (NS_GET_A(backgroundColor
) == 0) {
253 // Even if there's a background image, if there's no background color we
254 // keep going up the frame tree, see bug 1723938.
257 if (NS_GET_A(currentBackgroundColor
) == 0) {
258 // Try to avoid somewhat expensive math in the common case.
259 currentBackgroundColor
= backgroundColor
;
261 currentBackgroundColor
=
262 NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
264 if (NS_GET_A(currentBackgroundColor
) == 0xff) {
265 // If fully opaque, we're done, otherwise keep going up blending with our
267 return currentBackgroundColor
;
270 nscolor backgroundColor
= aFrame
->PresContext()->DefaultBackgroundColor();
271 if (NS_GET_A(currentBackgroundColor
) == 0) {
272 return backgroundColor
;
274 return NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
278 # include "nsBlockDebugFlags.h"
280 bool nsBlockFrame::gLamePaintMetrics
;
281 bool nsBlockFrame::gLameReflowMetrics
;
282 bool nsBlockFrame::gNoisy
;
283 bool nsBlockFrame::gNoisyDamageRepair
;
284 bool nsBlockFrame::gNoisyIntrinsic
;
285 bool nsBlockFrame::gNoisyReflow
;
286 bool nsBlockFrame::gReallyNoisyReflow
;
287 bool nsBlockFrame::gNoisyFloatManager
;
288 bool nsBlockFrame::gVerifyLines
;
289 bool nsBlockFrame::gDisableResizeOpt
;
291 int32_t nsBlockFrame::gNoiseIndent
;
293 struct BlockDebugFlags
{
298 static const BlockDebugFlags gFlags
[] = {
299 {"reflow", &nsBlockFrame::gNoisyReflow
},
300 {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow
},
301 {"intrinsic", &nsBlockFrame::gNoisyIntrinsic
},
302 {"float-manager", &nsBlockFrame::gNoisyFloatManager
},
303 {"verify-lines", &nsBlockFrame::gVerifyLines
},
304 {"damage-repair", &nsBlockFrame::gNoisyDamageRepair
},
305 {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics
},
306 {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics
},
307 {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt
},
309 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
311 static void ShowDebugFlags() {
312 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
313 const BlockDebugFlags
* bdf
= gFlags
;
314 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
315 for (; bdf
< end
; bdf
++) {
316 printf(" %s\n", bdf
->name
);
318 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
319 printf("names (no whitespace)\n");
322 void nsBlockFrame::InitDebugFlags() {
323 static bool firstTime
= true;
326 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
330 char* cm
= strchr(flags
, ',');
336 const BlockDebugFlags
* bdf
= gFlags
;
337 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
338 for (; bdf
< end
; bdf
++) {
339 if (nsCRT::strcasecmp(bdf
->name
, flags
) == 0) {
341 printf("nsBlockFrame: setting %s debug flag on\n", bdf
->name
);
366 //----------------------------------------------------------------------
368 // Debugging support code
371 const char* nsBlockFrame::kReflowCommandType
[] = {
372 "ContentChanged", "StyleChanged", "ReflowDirty", "Timeout", "UserDefined",
375 const char* nsBlockFrame::LineReflowStatusToString(
376 LineReflowStatus aLineReflowStatus
) const {
377 switch (aLineReflowStatus
) {
378 case LineReflowStatus::OK
:
379 return "LINE_REFLOW_OK";
380 case LineReflowStatus::Stop
:
381 return "LINE_REFLOW_STOP";
382 case LineReflowStatus::RedoNoPull
:
383 return "LINE_REFLOW_REDO_NO_PULL";
384 case LineReflowStatus::RedoMoreFloats
:
385 return "LINE_REFLOW_REDO_MORE_FLOATS";
386 case LineReflowStatus::RedoNextBand
:
387 return "LINE_REFLOW_REDO_NEXT_BAND";
388 case LineReflowStatus::Truncated
:
389 return "LINE_REFLOW_TRUNCATED";
396 #ifdef REFLOW_STATUS_COVERAGE
397 static void RecordReflowStatus(bool aChildIsBlock
,
398 const nsReflowStatus
& aFrameReflowStatus
) {
399 static uint32_t record
[2];
402 // 1: child-is-inline
404 if (!aChildIsBlock
) {
408 // Compute new status
409 uint32_t newS
= record
[index
];
410 if (aFrameReflowStatus
.IsInlineBreak()) {
411 if (aFrameReflowStatus
.IsInlineBreakBefore()) {
413 } else if (aFrameReflowStatus
.IsIncomplete()) {
418 } else if (aFrameReflowStatus
.IsIncomplete()) {
424 // Log updates to the status that yield different values
425 if (record
[index
] != newS
) {
426 record
[index
] = newS
;
427 printf("record(%d): %02x %02x\n", index
, record
[0], record
[1]);
432 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty
,
433 nsBlockFrame::FrameLines
)
434 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty
)
435 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty
)
436 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty
)
437 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty
, nsIFrame
)
438 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty
, nscoord
)
440 //----------------------------------------------------------------------
442 nsBlockFrame
* NS_NewBlockFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
443 return new (aPresShell
) nsBlockFrame(aStyle
, aPresShell
->GetPresContext());
446 nsBlockFrame
* NS_NewBlockFormattingContext(PresShell
* aPresShell
,
447 ComputedStyle
* aComputedStyle
) {
448 nsBlockFrame
* blockFrame
= NS_NewBlockFrame(aPresShell
, aComputedStyle
);
449 blockFrame
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
453 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame
)
455 nsBlockFrame::~nsBlockFrame() = default;
457 void nsBlockFrame::AddSizeOfExcludingThisForTree(
458 nsWindowSizes
& aWindowSizes
) const {
459 nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes
);
461 // Add the size of any nsLineBox::mFrames hashtables we might have:
462 for (const auto& line
: Lines()) {
463 line
.AddSizeOfExcludingThis(aWindowSizes
);
465 const FrameLines
* overflowLines
= GetOverflowLines();
467 ConstLineIterator line
= overflowLines
->mLines
.begin(),
468 line_end
= overflowLines
->mLines
.end();
469 for (; line
!= line_end
; ++line
) {
470 line
->AddSizeOfExcludingThis(aWindowSizes
);
475 void nsBlockFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
476 PostDestroyData
& aPostDestroyData
) {
478 DestroyAbsoluteFrames(aDestructRoot
, aPostDestroyData
);
479 mFloats
.DestroyFramesFrom(aDestructRoot
, aPostDestroyData
);
480 nsPresContext
* presContext
= PresContext();
481 mozilla::PresShell
* presShell
= presContext
->PresShell();
482 nsLineBox::DeleteLineList(presContext
, mLines
, aDestructRoot
, &mFrames
,
485 if (HasPushedFloats()) {
486 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
487 PushedFloatProperty());
488 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
491 // destroy overflow lines now
492 FrameLines
* overflowLines
= RemoveOverflowLines();
494 nsLineBox::DeleteLineList(presContext
, overflowLines
->mLines
, aDestructRoot
,
495 &overflowLines
->mFrames
, aPostDestroyData
);
496 delete overflowLines
;
499 if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
500 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
501 OverflowOutOfFlowsProperty());
502 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
505 if (HasOutsideMarker()) {
506 SafelyDestroyFrameListProp(aDestructRoot
, aPostDestroyData
, presShell
,
507 OutsideMarkerProperty());
508 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
511 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
515 nsILineIterator
* nsBlockFrame::GetLineIterator() {
516 nsLineIterator
* iter
= GetProperty(LineIteratorProperty());
518 const nsStyleVisibility
* visibility
= StyleVisibility();
519 iter
= new nsLineIterator(mLines
,
520 visibility
->mDirection
== StyleDirection::Rtl
);
521 SetProperty(LineIteratorProperty(), iter
);
526 NS_QUERYFRAME_HEAD(nsBlockFrame
)
527 NS_QUERYFRAME_ENTRY(nsBlockFrame
)
528 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
530 #ifdef DEBUG_FRAME_DUMP
531 void nsBlockFrame::List(FILE* out
, const char* aPrefix
,
532 ListFlags aFlags
) const {
534 ListGeneric(str
, aPrefix
, aFlags
);
536 fprintf_stderr(out
, "%s <\n", str
.get());
538 nsCString
pfx(aPrefix
);
542 if (!mLines
.empty()) {
543 ConstLineIterator line
= LinesBegin(), line_end
= LinesEnd();
544 for (; line
!= line_end
; ++line
) {
545 line
->List(out
, pfx
.get(), aFlags
);
549 // Output the overflow lines.
550 const FrameLines
* overflowLines
= GetOverflowLines();
551 if (overflowLines
&& !overflowLines
->mLines
.empty()) {
552 fprintf_stderr(out
, "%sOverflow-lines %p/%p <\n", pfx
.get(), overflowLines
,
553 &overflowLines
->mFrames
);
554 nsCString
nestedPfx(pfx
);
556 ConstLineIterator line
= overflowLines
->mLines
.begin(),
557 line_end
= overflowLines
->mLines
.end();
558 for (; line
!= line_end
; ++line
) {
559 line
->List(out
, nestedPfx
.get(), aFlags
);
561 fprintf_stderr(out
, "%s>\n", pfx
.get());
564 // skip the principal list - we printed the lines above
565 // skip the overflow list - we printed the overflow lines above
566 ChildListIDs skip
= {FrameChildListID::Principal
, FrameChildListID::Overflow
};
567 ListChildLists(out
, pfx
.get(), aFlags
, skip
);
569 fprintf_stderr(out
, "%s>\n", aPrefix
);
572 nsresult
nsBlockFrame::GetFrameName(nsAString
& aResult
) const {
573 return MakeFrameName(u
"Block"_ns
, aResult
);
577 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
578 bool aRebuildDisplayItems
) {
579 if (SVGUtils::IsInSVGTextSubtree(this)) {
580 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
581 "unexpected block frame in SVG text");
582 GetParent()->InvalidateFrame();
585 nsContainerFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
588 void nsBlockFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
589 uint32_t aDisplayItemKey
,
590 bool aRebuildDisplayItems
) {
591 if (SVGUtils::IsInSVGTextSubtree(this)) {
592 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
593 "unexpected block frame in SVG text");
594 GetParent()->InvalidateFrame();
597 nsContainerFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
598 aRebuildDisplayItems
);
601 nscoord
nsBlockFrame::GetLogicalBaseline(WritingMode aWM
) const {
602 auto lastBaseline
= BaselineBOffset(aWM
, BaselineSharingGroup::Last
,
603 AlignmentContext::Inline
);
604 return BSize(aWM
) - lastBaseline
;
607 bool nsBlockFrame::GetNaturalBaselineBOffset(
608 mozilla::WritingMode aWM
, BaselineSharingGroup aBaselineGroup
,
609 nscoord
* aBaseline
) const {
610 if (StyleDisplay()->IsContainLayout()) {
614 if (aBaselineGroup
== BaselineSharingGroup::First
) {
615 return nsLayoutUtils::GetFirstLineBaseline(aWM
, this, aBaseline
);
618 for (ConstReverseLineIterator line
= LinesRBegin(), line_end
= LinesREnd();
619 line
!= line_end
; ++line
) {
620 if (line
->IsBlock()) {
622 nsIFrame
* kid
= line
->mFirstChild
;
623 if (!aWM
.IsOrthogonalTo(kid
->GetWritingMode()) &&
624 kid
->GetVerticalAlignBaseline(aWM
, &offset
)) {
625 // Ignore relative positioning for baseline calculations.
626 const nsSize
& sz
= line
->mContainerSize
;
627 offset
+= kid
->GetLogicalNormalPosition(aWM
, sz
).B(aWM
);
628 *aBaseline
= BSize(aWM
) - offset
;
632 // XXX Is this the right test? We have some bogus empty lines
633 // floating around, but IsEmpty is perhaps too weak.
634 if (line
->BSize() != 0 || !line
->IsEmpty()) {
635 *aBaseline
= BSize(aWM
) - (line
->BStart() + line
->GetLogicalAscent());
643 nscoord
nsBlockFrame::GetCaretBaseline() const {
644 nsRect contentRect
= GetContentRect();
645 nsMargin bp
= GetUsedBorderAndPadding();
647 if (!mLines
.empty()) {
648 ConstLineIterator line
= LinesBegin();
649 if (!line
->IsEmpty()) {
650 if (line
->IsBlock()) {
651 return bp
.top
+ line
->mFirstChild
->GetCaretBaseline();
653 return line
->BStart() + line
->GetLogicalAscent();
657 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
658 RefPtr
<nsFontMetrics
> fm
=
659 nsLayoutUtils::GetFontMetricsForFrame(this, inflation
);
660 nscoord lineHeight
= ReflowInput::CalcLineHeight(
661 GetContent(), Style(), PresContext(), contentRect
.height
, inflation
);
662 const WritingMode wm
= GetWritingMode();
663 return nsLayoutUtils::GetCenteredFontBaseline(fm
, lineHeight
,
664 wm
.IsLineInverted()) +
668 /////////////////////////////////////////////////////////////////////////////
669 // Child frame enumeration
671 const nsFrameList
& nsBlockFrame::GetChildList(ChildListID aListID
) const {
673 case FrameChildListID::Principal
:
675 case FrameChildListID::Overflow
: {
676 FrameLines
* overflowLines
= GetOverflowLines();
677 return overflowLines
? overflowLines
->mFrames
: nsFrameList::EmptyList();
679 case FrameChildListID::Float
:
681 case FrameChildListID::OverflowOutOfFlow
: {
682 const nsFrameList
* list
= GetOverflowOutOfFlows();
683 return list
? *list
: nsFrameList::EmptyList();
685 case FrameChildListID::PushedFloats
: {
686 const nsFrameList
* list
= GetPushedFloats();
687 return list
? *list
: nsFrameList::EmptyList();
689 case FrameChildListID::Bullet
: {
690 const nsFrameList
* list
= GetOutsideMarkerList();
691 return list
? *list
: nsFrameList::EmptyList();
694 return nsContainerFrame::GetChildList(aListID
);
698 void nsBlockFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const {
699 nsContainerFrame::GetChildLists(aLists
);
700 FrameLines
* overflowLines
= GetOverflowLines();
702 overflowLines
->mFrames
.AppendIfNonempty(aLists
, FrameChildListID::Overflow
);
704 const nsFrameList
* list
= GetOverflowOutOfFlows();
706 list
->AppendIfNonempty(aLists
, FrameChildListID::OverflowOutOfFlow
);
708 mFloats
.AppendIfNonempty(aLists
, FrameChildListID::Float
);
709 list
= GetOutsideMarkerList();
711 list
->AppendIfNonempty(aLists
, FrameChildListID::Bullet
);
713 list
= GetPushedFloats();
715 list
->AppendIfNonempty(aLists
, FrameChildListID::PushedFloats
);
720 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
723 * Remove the first line from aFromLines and adjust the associated frame list
724 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
725 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
726 * that were extracted from the head of aFromFrames.
727 * aFromLines must contain at least one line, the line may be empty.
728 * @return true if aFromLines becomes empty
730 static bool RemoveFirstLine(nsLineList
& aFromLines
, nsFrameList
& aFromFrames
,
731 nsLineBox
** aOutLine
, nsFrameList
* aOutFrames
) {
732 nsLineList_iterator removedLine
= aFromLines
.begin();
733 *aOutLine
= removedLine
;
734 nsLineList_iterator next
= aFromLines
.erase(removedLine
);
735 bool isLastLine
= next
== aFromLines
.end();
736 nsIFrame
* firstFrameInNextLine
= isLastLine
? nullptr : next
->mFirstChild
;
737 *aOutFrames
= aFromFrames
.TakeFramesBefore(firstFrameInNextLine
);
741 //////////////////////////////////////////////////////////////////////
745 void nsBlockFrame::MarkIntrinsicISizesDirty() {
746 nsBlockFrame
* dirtyBlock
= static_cast<nsBlockFrame
*>(FirstContinuation());
747 dirtyBlock
->mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
748 dirtyBlock
->mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
749 if (!HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
750 for (nsIFrame
* frame
= dirtyBlock
; frame
;
751 frame
= frame
->GetNextContinuation()) {
752 frame
->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
756 nsContainerFrame::MarkIntrinsicISizesDirty();
759 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
760 nsPresContext
* presContext
= PresContext();
761 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext
)) {
764 bool inflationEnabled
= !presContext
->mInflationDisabledForShrinkWrap
;
765 if (inflationEnabled
!= HasAnyStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
)) {
766 mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
767 mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
768 if (inflationEnabled
) {
769 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
771 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
777 nscoord
nsBlockFrame::GetMinISize(gfxContext
* aRenderingContext
) {
778 nsIFrame
* firstInFlow
= FirstContinuation();
779 if (firstInFlow
!= this) {
780 return firstInFlow
->GetMinISize(aRenderingContext
);
783 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
785 CheckIntrinsicCacheAgainstShrinkWrapState();
787 if (mCachedMinISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
788 return mCachedMinISize
;
791 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
792 mCachedMinISize
= *containISize
;
793 return mCachedMinISize
;
797 if (gNoisyIntrinsic
) {
798 IndentBy(stdout
, gNoiseIndent
);
800 printf(": GetMinISize\n");
802 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
805 for (nsBlockFrame
* curFrame
= this; curFrame
;
806 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
807 curFrame
->LazyMarkLinesDirty();
810 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
811 PresContext()->BidiEnabled()) {
815 const bool whiteSpaceCanWrap
= StyleText()->WhiteSpaceCanWrapStyle();
816 InlineMinISizeData data
;
817 for (nsBlockFrame
* curFrame
= this; curFrame
;
818 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
819 for (LineIterator line
= curFrame
->LinesBegin(),
820 line_end
= curFrame
->LinesEnd();
821 line
!= line_end
; ++line
) {
823 if (gNoisyIntrinsic
) {
824 IndentBy(stdout
, gNoiseIndent
);
825 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
826 line
->IsEmpty() ? ", empty" : "");
828 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
830 if (line
->IsBlock()) {
832 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
833 aRenderingContext
, line
->mFirstChild
, IntrinsicISizeType::MinISize
);
836 if (!curFrame
->GetPrevContinuation() &&
837 line
== curFrame
->LinesBegin()) {
838 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
841 data
.SetLineContainer(curFrame
);
842 nsIFrame
* kid
= line
->mFirstChild
;
843 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
844 ++i
, kid
= kid
->GetNextSibling()) {
845 kid
->AddInlineMinISize(aRenderingContext
, &data
);
846 if (whiteSpaceCanWrap
&& data
.mTrailingWhitespace
) {
847 data
.OptionallyBreak();
852 if (gNoisyIntrinsic
) {
853 IndentBy(stdout
, gNoiseIndent
);
854 printf("min: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
862 mCachedMinISize
= data
.mPrevLines
;
863 return mCachedMinISize
;
867 nscoord
nsBlockFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
868 nsIFrame
* firstInFlow
= FirstContinuation();
869 if (firstInFlow
!= this) {
870 return firstInFlow
->GetPrefISize(aRenderingContext
);
873 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
875 CheckIntrinsicCacheAgainstShrinkWrapState();
877 if (mCachedPrefISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
878 return mCachedPrefISize
;
881 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
882 mCachedPrefISize
= *containISize
;
883 return mCachedPrefISize
;
887 if (gNoisyIntrinsic
) {
888 IndentBy(stdout
, gNoiseIndent
);
890 printf(": GetPrefISize\n");
892 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
895 for (nsBlockFrame
* curFrame
= this; curFrame
;
896 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
897 curFrame
->LazyMarkLinesDirty();
900 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
901 PresContext()->BidiEnabled()) {
904 InlinePrefISizeData data
;
905 for (nsBlockFrame
* curFrame
= this; curFrame
;
906 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
907 for (LineIterator line
= curFrame
->LinesBegin(),
908 line_end
= curFrame
->LinesEnd();
909 line
!= line_end
; ++line
) {
911 if (gNoisyIntrinsic
) {
912 IndentBy(stdout
, gNoiseIndent
);
913 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
914 line
->IsEmpty() ? ", empty" : "");
916 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
918 if (line
->IsBlock()) {
919 StyleClear clearType
;
920 if (!data
.mLineIsEmpty
|| BlockCanIntersectFloats(line
->mFirstChild
)) {
921 clearType
= StyleClear::Both
;
923 clearType
= line
->mFirstChild
->StyleDisplay()->mClear
;
925 data
.ForceBreak(clearType
);
926 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
927 aRenderingContext
, line
->mFirstChild
,
928 IntrinsicISizeType::PrefISize
);
931 if (!curFrame
->GetPrevContinuation() &&
932 line
== curFrame
->LinesBegin()) {
933 nscoord indent
= StyleText()->mTextIndent
.Resolve(0);
934 data
.mCurrentLine
+= indent
;
935 // XXXmats should the test below be indent > 0?
936 if (indent
!= nscoord(0)) {
937 data
.mLineIsEmpty
= false;
941 data
.SetLineContainer(curFrame
);
942 nsIFrame
* kid
= line
->mFirstChild
;
943 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
944 ++i
, kid
= kid
->GetNextSibling()) {
945 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
949 if (gNoisyIntrinsic
) {
950 IndentBy(stdout
, gNoiseIndent
);
951 printf("pref: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
959 mCachedPrefISize
= data
.mPrevLines
;
960 return mCachedPrefISize
;
963 nsRect
nsBlockFrame::ComputeTightBounds(DrawTarget
* aDrawTarget
) const {
965 if (Style()->HasTextDecorationLines()) {
966 return InkOverflowRect();
968 return ComputeSimpleTightBounds(aDrawTarget
);
972 nsresult
nsBlockFrame::GetPrefWidthTightBounds(gfxContext
* aRenderingContext
,
973 nscoord
* aX
, nscoord
* aXMost
) {
974 nsIFrame
* firstInFlow
= FirstContinuation();
975 if (firstInFlow
!= this) {
976 return firstInFlow
->GetPrefWidthTightBounds(aRenderingContext
, aX
, aXMost
);
983 InlinePrefISizeData data
;
984 for (nsBlockFrame
* curFrame
= this; curFrame
;
985 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
986 for (LineIterator line
= curFrame
->LinesBegin(),
987 line_end
= curFrame
->LinesEnd();
988 line
!= line_end
; ++line
) {
989 nscoord childX
, childXMost
;
990 if (line
->IsBlock()) {
992 rv
= line
->mFirstChild
->GetPrefWidthTightBounds(aRenderingContext
,
993 &childX
, &childXMost
);
994 NS_ENSURE_SUCCESS(rv
, rv
);
995 *aX
= std::min(*aX
, childX
);
996 *aXMost
= std::max(*aXMost
, childXMost
);
998 if (!curFrame
->GetPrevContinuation() &&
999 line
== curFrame
->LinesBegin()) {
1000 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
1003 data
.SetLineContainer(curFrame
);
1004 nsIFrame
* kid
= line
->mFirstChild
;
1005 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
1006 ++i
, kid
= kid
->GetNextSibling()) {
1007 rv
= kid
->GetPrefWidthTightBounds(aRenderingContext
, &childX
,
1009 NS_ENSURE_SUCCESS(rv
, rv
);
1010 *aX
= std::min(*aX
, data
.mCurrentLine
+ childX
);
1011 *aXMost
= std::max(*aXMost
, data
.mCurrentLine
+ childXMost
);
1012 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1023 * Return whether aNewAvailableSpace is smaller *on either side*
1024 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
1025 * if we need to redo layout on an line, replaced block, or block
1026 * formatting context, because its height (which we used to compute
1027 * aNewAvailableSpace) caused it to intersect additional floats.
1029 static bool AvailableSpaceShrunk(WritingMode aWM
,
1030 const LogicalRect
& aOldAvailableSpace
,
1031 const LogicalRect
& aNewAvailableSpace
,
1032 bool aCanGrow
/* debug-only */) {
1033 if (aNewAvailableSpace
.ISize(aWM
) == 0) {
1034 // Positions are not significant if the inline size is zero.
1035 return aOldAvailableSpace
.ISize(aWM
) != 0;
1039 aNewAvailableSpace
.IStart(aWM
) <= aOldAvailableSpace
.IStart(aWM
) ||
1040 aNewAvailableSpace
.IEnd(aWM
) <= aOldAvailableSpace
.IEnd(aWM
),
1041 "available space should not shrink on the start side and "
1042 "grow on the end side");
1044 aNewAvailableSpace
.IStart(aWM
) >= aOldAvailableSpace
.IStart(aWM
) ||
1045 aNewAvailableSpace
.IEnd(aWM
) >= aOldAvailableSpace
.IEnd(aWM
),
1046 "available space should not grow on the start side and "
1047 "shrink on the end side");
1050 aOldAvailableSpace
.IStart(aWM
) <= aNewAvailableSpace
.IStart(aWM
) &&
1051 aOldAvailableSpace
.IEnd(aWM
) >= aNewAvailableSpace
.IEnd(aWM
),
1052 "available space should never grow");
1054 // Have we shrunk on either side?
1055 return aNewAvailableSpace
.IStart(aWM
) > aOldAvailableSpace
.IStart(aWM
) ||
1056 aNewAvailableSpace
.IEnd(aWM
) < aOldAvailableSpace
.IEnd(aWM
);
1059 static LogicalSize
CalculateContainingBlockSizeForAbsolutes(
1060 WritingMode aWM
, const ReflowInput
& aReflowInput
, LogicalSize aFrameSize
) {
1061 // The issue here is that for a 'height' of 'auto' the reflow input
1062 // code won't know how to calculate the containing block height
1063 // because it's calculated bottom up. So we use our own computed
1064 // size as the dimensions.
1065 nsIFrame
* frame
= aReflowInput
.mFrame
;
1067 LogicalSize
cbSize(aFrameSize
);
1068 // Containing block is relative to the padding edge
1069 const LogicalMargin border
= aReflowInput
.ComputedLogicalBorder(aWM
);
1070 cbSize
.ISize(aWM
) -= border
.IStartEnd(aWM
);
1071 cbSize
.BSize(aWM
) -= border
.BStartEnd(aWM
);
1073 if (frame
->GetParent()->GetContent() != frame
->GetContent() ||
1074 frame
->GetParent()->IsCanvasFrame()) {
1078 // We are a wrapped frame for the content (and the wrapper is not the
1079 // canvas frame, whose size is not meaningful here).
1080 // Use the container's dimensions, if they have been precomputed.
1081 // XXX This is a hack! We really should be waiting until the outermost
1082 // frame is fully reflowed and using the resulting dimensions, even
1083 // if they're intrinsic.
1084 // In fact we should be attaching absolute children to the outermost
1085 // frame and not always sticking them in block frames.
1087 // First, find the reflow input for the outermost frame for this content.
1088 const ReflowInput
* lastRI
= &aReflowInput
;
1089 DebugOnly
<const ReflowInput
*> lastButOneRI
= &aReflowInput
;
1090 while (lastRI
->mParentReflowInput
&&
1091 lastRI
->mParentReflowInput
->mFrame
->GetContent() ==
1092 frame
->GetContent()) {
1093 lastButOneRI
= lastRI
;
1094 lastRI
= lastRI
->mParentReflowInput
;
1097 if (lastRI
== &aReflowInput
) {
1101 // For scroll containers, we can just use cbSize (which is the padding-box
1102 // size of the scrolled-content frame).
1103 if (nsIScrollableFrame
* scrollFrame
= do_QueryFrame(lastRI
->mFrame
)) {
1104 // Assert that we're not missing any frames between the abspos containing
1105 // block and the scroll container.
1107 Unused
<< scrollFrame
;
1108 MOZ_ASSERT(lastButOneRI
== &aReflowInput
);
1112 // Same for fieldsets, where the inner anonymous frame has the correct padding
1113 // area with the legend taken into account.
1114 if (lastRI
->mFrame
->IsFieldSetFrame()) {
1118 // We found a reflow input for the outermost wrapping frame, so use
1119 // its computed metrics if available, converted to our writing mode
1120 const LogicalSize lastRISize
= lastRI
->ComputedSize(aWM
);
1121 const LogicalMargin lastRIPadding
= lastRI
->ComputedLogicalPadding(aWM
);
1122 if (lastRISize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1124 std::max(0, lastRISize
.ISize(aWM
) + lastRIPadding
.IStartEnd(aWM
));
1126 if (lastRISize
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1128 std::max(0, lastRISize
.BSize(aWM
) + lastRIPadding
.BStartEnd(aWM
));
1135 * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1137 * This is used to determine whether to recurse into aFrame when applying
1138 * -webkit-line-clamp.
1140 static const nsBlockFrame
* GetAsLineClampDescendant(const nsIFrame
* aFrame
) {
1141 if (const nsBlockFrame
* block
= do_QueryFrame(aFrame
)) {
1142 if (!block
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1149 static nsBlockFrame
* GetAsLineClampDescendant(nsIFrame
* aFrame
) {
1150 return const_cast<nsBlockFrame
*>(
1151 GetAsLineClampDescendant(const_cast<const nsIFrame
*>(aFrame
)));
1154 static bool IsLineClampRoot(const nsBlockFrame
* aFrame
) {
1155 if (!aFrame
->StyleDisplay()->mWebkitLineClamp
) {
1159 if (!aFrame
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1163 if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled()) {
1167 // For now, -webkit-box is the only thing allowed to be a line-clamp root.
1168 // Ideally we'd just make this work everywhere, but for now we're carrying
1169 // this forward as a limitation on the legacy -webkit-line-clamp feature,
1170 // since relaxing this limitation might create webcompat trouble.
1171 auto origDisplay
= [&] {
1172 if (aFrame
->Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
1173 // If we're the anonymous block inside the scroll frame, we need to look
1174 // at the original display of our parent frame.
1175 MOZ_ASSERT(aFrame
->GetParent());
1176 const auto& parentDisp
= *aFrame
->GetParent()->StyleDisplay();
1177 MOZ_ASSERT(parentDisp
.mWebkitLineClamp
==
1178 aFrame
->StyleDisplay()->mWebkitLineClamp
,
1179 ":-moz-scrolled-content should inherit -webkit-line-clamp, "
1180 "via rule in UA stylesheet");
1181 return parentDisp
.mOriginalDisplay
;
1183 return aFrame
->StyleDisplay()->mOriginalDisplay
;
1185 return nsStyleDisplay::DisplayInside(origDisplay
) ==
1186 StyleDisplayInside::WebkitBox
;
1189 bool nsBlockFrame::IsInLineClampContext() const {
1190 if (IsLineClampRoot(this)) {
1193 const nsBlockFrame
* cur
= this;
1194 while (GetAsLineClampDescendant(cur
)) {
1195 cur
= do_QueryFrame(cur
->GetParent());
1199 if (IsLineClampRoot(cur
)) {
1207 * Iterator over all descendant inline line boxes, except for those that are
1208 * under an independent formatting context.
1210 class MOZ_RAII LineClampLineIterator
{
1212 explicit LineClampLineIterator(nsBlockFrame
* aFrame
)
1213 : mCur(aFrame
->LinesBegin()),
1214 mEnd(aFrame
->LinesEnd()),
1215 mCurrentFrame(mCur
== mEnd
? nullptr : aFrame
) {
1216 if (mCur
!= mEnd
&& !mCur
->IsInline()) {
1221 nsLineBox
* GetCurrentLine() { return mCurrentFrame
? mCur
.get() : nullptr; }
1222 nsBlockFrame
* GetCurrentFrame() { return mCurrentFrame
; }
1224 // Advances the iterator to the next line line.
1226 // Next() shouldn't be called once the iterator is at the end, which can be
1227 // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
1229 MOZ_ASSERT(mCur
!= mEnd
&& mCurrentFrame
,
1230 "Don't call Next() when the iterator is at the end");
1239 // Reached the end of the current block. Pop the parent off the
1240 // stack; if there isn't one, then we've reached the end.
1241 if (mStack
.IsEmpty()) {
1242 mCurrentFrame
= nullptr;
1245 auto entry
= mStack
.PopLastElement();
1246 mCurrentFrame
= entry
.first
;
1247 mCur
= entry
.second
;
1248 mEnd
= mCurrentFrame
->LinesEnd();
1249 } else if (mCur
->IsBlock()) {
1250 if (nsBlockFrame
* child
= GetAsLineClampDescendant(mCur
->mFirstChild
)) {
1251 nsBlockFrame::LineIterator next
= mCur
;
1253 mStack
.AppendElement(std::make_pair(mCurrentFrame
, next
));
1254 mCur
= child
->LinesBegin();
1255 mEnd
= child
->LinesEnd();
1256 mCurrentFrame
= child
;
1258 // Some kind of frame we shouldn't descend into.
1262 MOZ_ASSERT(mCur
->IsInline());
1268 // The current line within the current block.
1270 // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1272 nsBlockFrame::LineIterator mCur
;
1274 // The iterator end for the current block.
1275 nsBlockFrame::LineIterator mEnd
;
1277 // The current block.
1278 nsBlockFrame
* mCurrentFrame
;
1280 // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1282 AutoTArray
<std::pair
<nsBlockFrame
*, nsBlockFrame::LineIterator
>, 8> mStack
;
1285 static bool ClearLineClampEllipsis(nsBlockFrame
* aFrame
) {
1286 if (!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
)) {
1287 for (nsIFrame
* f
: aFrame
->PrincipalChildList()) {
1288 if (nsBlockFrame
* child
= GetAsLineClampDescendant(f
)) {
1289 if (ClearLineClampEllipsis(child
)) {
1297 aFrame
->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1299 for (auto& line
: aFrame
->Lines()) {
1300 if (line
.HasLineClampEllipsis()) {
1301 line
.ClearHasLineClampEllipsis();
1306 // We didn't find a line with the ellipsis; it must have been deleted already.
1310 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1312 void nsBlockFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
1313 const ReflowInput
& aReflowInput
,
1314 nsReflowStatus
& aStatus
) {
1315 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
1316 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1321 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1322 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
1323 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1327 IndentBy(stdout
, gNoiseIndent
);
1329 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1330 aReflowInput
.AvailableISize(), aReflowInput
.AvailableBSize(),
1331 aReflowInput
.ComputedISize(), aReflowInput
.ComputedBSize());
1333 AutoNoisyIndenter
indent(gNoisy
);
1334 PRTime start
= 0; // Initialize these variablies to silence the compiler.
1335 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
1336 if (gLameReflowMetrics
) {
1338 ctc
= nsLineBox::GetCtorCount();
1342 // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
1343 // max-block-size because both affect the children's available block-size.
1344 if (IsColumnSetWrapperFrame()) {
1345 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
);
1348 const ReflowInput
* reflowInput
= &aReflowInput
;
1349 WritingMode wm
= aReflowInput
.GetWritingMode();
1350 const nscoord consumedBSize
= CalcAndCacheConsumedBSize();
1351 const nscoord effectiveContentBoxBSize
=
1352 GetEffectiveComputedBSize(aReflowInput
, consumedBSize
);
1353 Maybe
<ReflowInput
> mutableReflowInput
;
1354 // If we have non-auto block size, we're clipping our kids and we fit,
1355 // make sure our kids fit too.
1356 const PhysicalAxes physicalBlockAxis
=
1357 wm
.IsVertical() ? PhysicalAxes::Horizontal
: PhysicalAxes::Vertical
;
1358 if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1359 aReflowInput
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
&&
1360 (ShouldApplyOverflowClipping(aReflowInput
.mStyleDisplay
) &
1361 physicalBlockAxis
)) {
1362 LogicalMargin blockDirExtras
=
1363 aReflowInput
.ComputedLogicalBorderPadding(wm
);
1364 if (GetLogicalSkipSides().BStart()) {
1365 blockDirExtras
.BStart(wm
) = 0;
1367 // Block-end margin never causes us to create continuations, so we
1368 // don't need to worry about whether it fits in its entirety.
1369 blockDirExtras
.BStart(wm
) +=
1370 aReflowInput
.ComputedLogicalMargin(wm
).BStart(wm
);
1373 if (effectiveContentBoxBSize
+ blockDirExtras
.BStartEnd(wm
) <=
1374 aReflowInput
.AvailableBSize()) {
1375 mutableReflowInput
.emplace(aReflowInput
);
1376 mutableReflowInput
->SetAvailableBSize(NS_UNCONSTRAINEDSIZE
);
1377 reflowInput
= mutableReflowInput
.ptr();
1381 // See comment below about oldSize. Use *only* for the
1382 // abs-pos-containing-block-size-change optimization!
1383 nsSize oldSize
= GetSize();
1385 // Should we create a float manager?
1386 nsAutoFloatManager
autoFloatManager(const_cast<ReflowInput
&>(*reflowInput
));
1388 // XXXldb If we start storing the float manager in the frame rather
1389 // than keeping it around only during reflow then we should create it
1390 // only when there are actually floats to manage. Otherwise things
1391 // like tables will gain significant bloat.
1392 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
1393 if (needFloatManager
) {
1394 autoFloatManager
.CreateFloatManager(aPresContext
);
1397 // OK, some lines may be reflowed. Blow away any saved line cursor
1398 // because we may invalidate the nondecreasing
1399 // overflowArea.InkOverflow().y/yMost invariant, and we may even
1400 // delete the line with the line cursor.
1403 if (IsFrameTreeTooDeep(*reflowInput
, aMetrics
, aStatus
)) {
1408 // Between when we drain pushed floats and when we complete reflow,
1409 // we're allowed to have multiple continuations of the same float on
1410 // our floats list, since a first-in-flow might get pushed to a later
1411 // continuation of its containing block. But it's not permitted
1412 // outside that time.
1413 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1416 // ALWAYS drain overflow. We never want to leave the previnflow's
1417 // overflow lines hanging around; block reflow depends on the
1418 // overflow line lists being cleared out between reflow passes.
1419 DrainOverflowLines();
1421 bool blockStartMarginRoot
, blockEndMarginRoot
;
1422 IsMarginRoot(&blockStartMarginRoot
, &blockEndMarginRoot
);
1424 BlockReflowState
state(*reflowInput
, aPresContext
, this, blockStartMarginRoot
,
1425 blockEndMarginRoot
, needFloatManager
, consumedBSize
,
1426 effectiveContentBoxBSize
);
1428 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
1429 PresContext()->BidiEnabled()) {
1430 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
1433 // Handle paginated overflow (see nsContainerFrame.h)
1434 OverflowAreas ocBounds
;
1435 nsReflowStatus ocStatus
;
1436 if (GetPrevInFlow()) {
1437 ReflowOverflowContainerChildren(
1438 aPresContext
, *reflowInput
, ocBounds
, ReflowChildFlags::Default
,
1439 ocStatus
, DefaultChildFrameMerge
, Some(state
.ContainerSize()));
1442 // Now that we're done cleaning up our overflow container lists, we can
1443 // give |state| its nsOverflowContinuationTracker.
1444 nsOverflowContinuationTracker
tracker(this, false);
1445 state
.mOverflowTracker
= &tracker
;
1447 // Drain & handle pushed floats
1448 DrainPushedFloats();
1449 OverflowAreas fcBounds
;
1450 ReflowPushedFloats(state
, fcBounds
);
1452 // If we're not dirty (which means we'll mark everything dirty later)
1453 // and our inline-size has changed, mark the lines dirty that we need to
1454 // mark dirty for a resize reflow.
1455 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && reflowInput
->IsIResize()) {
1456 PrepareResizeReflow(state
);
1459 // The same for percentage text-indent, except conditioned on the
1461 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && reflowInput
->mCBReflowInput
&&
1462 reflowInput
->mCBReflowInput
->IsIResize() &&
1463 StyleText()->mTextIndent
.HasPercent() && !mLines
.empty()) {
1464 mLines
.front()->MarkDirty();
1467 LazyMarkLinesDirty();
1470 ReflowDirtyLines(state
);
1472 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1473 // this frame from a previous iteration of reflow, then we should not return
1474 // a status with IsFullyComplete() equals to true, since we actually have
1475 // overflow, it's just already been handled.
1477 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1478 // and reflow them, but just in case it does, this is a safety precaution so
1479 // we don't end up with a placeholder pointing to frames that have already
1480 // been deleted as part of removing our next-in-flow.
1481 if (state
.mReflowStatus
.IsFullyComplete()) {
1482 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1484 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1485 if (nif
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
1486 state
.mReflowStatus
.SetOverflowIncomplete();
1488 state
.mReflowStatus
.SetIncomplete();
1493 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1497 state
.mReflowStatus
.MergeCompletionStatusFrom(ocStatus
);
1499 // If we end in a BR with clear and affected floats continue,
1500 // we need to continue, too.
1501 if (NS_UNCONSTRAINEDSIZE
!= reflowInput
->AvailableBSize() &&
1502 state
.mReflowStatus
.IsComplete() &&
1503 state
.FloatManager()->ClearContinues(FindTrailingClear())) {
1504 state
.mReflowStatus
.SetIncomplete();
1507 if (!state
.mReflowStatus
.IsFullyComplete()) {
1508 if (HasOverflowLines() || HasPushedFloats()) {
1509 state
.mReflowStatus
.SetNextInFlowNeedsReflow();
1514 printf(": block is not fully complete\n");
1518 // Place the ::marker's frame if it is placed next to a block child.
1520 // According to the CSS2 spec, section 12.6.1, the ::marker's box
1521 // participates in the height calculation of the list-item box's
1524 // There are exactly two places a ::marker can be placed: near the
1525 // first or second line. It's only placed on the second line in a
1526 // rare case: an empty first line followed by a second line that
1527 // contains a block (example: <LI>\n<P>... ). This is where
1528 // the second case can happen.
1529 if (HasOutsideMarker() && !mLines
.empty() &&
1530 (mLines
.front()->IsBlock() ||
1531 (0 == mLines
.front()->BSize() && mLines
.front() != mLines
.back() &&
1532 mLines
.begin().next()->IsBlock()))) {
1533 // Reflow the ::marker's frame.
1534 ReflowOutput
reflowOutput(aReflowInput
);
1535 // XXX Use the entire line when we fix bug 25888.
1536 nsLayoutUtils::LinePosition position
;
1537 WritingMode wm
= aReflowInput
.GetWritingMode();
1539 nsLayoutUtils::GetFirstLinePosition(wm
, this, &position
);
1540 nscoord lineBStart
=
1541 havePosition
? position
.mBStart
1542 : reflowInput
->ComputedLogicalBorderPadding(wm
).BStart(wm
);
1543 nsIFrame
* marker
= GetOutsideMarker();
1544 ReflowOutsideMarker(marker
, state
, reflowOutput
, lineBStart
);
1545 NS_ASSERTION(!MarkerIsEmpty() || reflowOutput
.BSize(wm
) == 0,
1546 "empty ::marker frame took up space");
1548 if (havePosition
&& !MarkerIsEmpty()) {
1549 // We have some lines to align the ::marker with.
1551 // Doing the alignment using the baseline will also cater for
1552 // ::markers that are placed next to a child block (bug 92896)
1554 // Tall ::markers won't look particularly nice here...
1556 marker
->GetLogicalRect(wm
, reflowOutput
.PhysicalSize());
1557 const auto baselineGroup
= BaselineSharingGroup::First
;
1558 nscoord markerBaseline
;
1559 if (MOZ_UNLIKELY(wm
.IsOrthogonalTo(marker
->GetWritingMode()) ||
1560 !marker
->GetNaturalBaselineBOffset(wm
, baselineGroup
,
1561 &markerBaseline
))) {
1562 // ::marker has no baseline in this axis: align with its margin-box end.
1564 bbox
.BSize(wm
) + marker
->GetLogicalUsedMargin(wm
).BEnd(wm
);
1566 bbox
.BStart(wm
) = position
.mBaseline
- markerBaseline
;
1567 marker
->SetRect(wm
, bbox
, reflowOutput
.PhysicalSize());
1569 // Otherwise just leave the ::marker where it is, up against our
1570 // block-start padding.
1573 // Clear any existing -webkit-line-clamp ellipsis.
1574 if (aReflowInput
.mStyleDisplay
->mWebkitLineClamp
) {
1575 ClearLineClampEllipsis();
1580 // Compute our final size
1581 nscoord blockEndEdgeOfChildren
;
1582 ComputeFinalSize(*reflowInput
, state
, aMetrics
, &blockEndEdgeOfChildren
);
1584 // If the block direction is right-to-left, we need to update the bounds of
1585 // lines that were placed relative to mContainerSize during reflow, as
1586 // we typically do not know the true container size until we've reflowed all
1587 // its children. So we use a dummy mContainerSize during reflow (see
1588 // BlockReflowState's constructor) and then fix up the positions of the
1589 // lines here, once the final block size is known.
1591 // Note that writing-mode:vertical-rl is the only case where the block
1592 // logical direction progresses in a negative physical direction, and
1593 // therefore block-dir coordinate conversion depends on knowing the width
1594 // of the coordinate space in order to translate between the logical and
1595 // physical origins.
1596 if (wm
.IsVerticalRL()) {
1597 nsSize containerSize
= aMetrics
.PhysicalSize();
1598 nscoord deltaX
= containerSize
.width
- state
.ContainerSize().width
;
1600 // We compute our lines and markers' overflow areas later in
1601 // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
1603 const nsPoint
physicalDelta(deltaX
, 0);
1604 for (auto& line
: Lines()) {
1605 UpdateLineContainerSize(&line
, containerSize
);
1608 for (nsIFrame
* f
: mFloats
) {
1609 f
->MovePositionBy(physicalDelta
);
1610 ConsiderChildOverflow(fcBounds
, f
);
1612 nsFrameList
* markerList
= GetOutsideMarkerList();
1614 for (nsIFrame
* f
: *markerList
) {
1615 f
->MovePositionBy(physicalDelta
);
1618 if (nsFrameList
* overflowContainers
= GetOverflowContainers()) {
1620 for (nsIFrame
* f
: *overflowContainers
) {
1621 f
->MovePositionBy(physicalDelta
);
1622 ConsiderChildOverflow(ocBounds
, f
);
1628 aMetrics
.SetOverflowAreasToDesiredBounds();
1629 ComputeOverflowAreas(aMetrics
.mOverflowAreas
, blockEndEdgeOfChildren
,
1630 reflowInput
->mStyleDisplay
);
1631 // Factor overflow container child bounds into the overflow area
1632 aMetrics
.mOverflowAreas
.UnionWith(ocBounds
);
1633 // Factor pushed float child bounds into the overflow area
1634 aMetrics
.mOverflowAreas
.UnionWith(fcBounds
);
1636 // Let the absolutely positioned container reflow any absolutely positioned
1637 // child frames that need to be reflowed, e.g., elements with a percentage
1638 // based width/height
1639 // We want to do this under either of two conditions:
1640 // 1. If we didn't do the incremental reflow above.
1641 // 2. If our size changed.
1642 // Even though it's the padding edge that's the containing block, we
1643 // can use our rect (the border edge) since if the border style
1644 // changed, the reflow would have been targeted at us so we'd satisfy
1646 // XXX checking oldSize is bogus, there are various reasons we might have
1647 // reflowed but our size might not have been changed to what we
1648 // asked for (e.g., we ended up being pushed to a new page)
1649 // When WillReflowAgainForClearance is true, we will reflow again without
1650 // resetting the size. Because of this, we must not reflow our abs-pos
1651 // children in that situation --- what we think is our "new size" will not be
1652 // our real new size. This also happens to be more efficient.
1653 WritingMode parentWM
= aMetrics
.GetWritingMode();
1654 if (HasAbsolutelyPositionedChildren()) {
1655 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1656 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1657 if (reflowInput
->WillReflowAgainForClearance() || haveInterrupt
) {
1658 // Make sure that when we reflow again we'll actually reflow all the abs
1659 // pos frames that might conceivably depend on our size (or all of them,
1660 // if we're dirty right now and interrupted; in that case we also need
1661 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1662 // better than that, because we don't really know what our size will be,
1663 // and it might in fact not change on the followup reflow!
1664 if (haveInterrupt
&& HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
1665 absoluteContainer
->MarkAllFramesDirty();
1667 absoluteContainer
->MarkSizeDependentFramesDirty();
1669 if (haveInterrupt
) {
1670 // We're not going to reflow absolute frames; make sure to account for
1671 // their existing overflow areas, which is usually a side effect of this
1674 // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1675 // interrupt, can we just rely on it and unconditionally take the else
1676 // branch below? That's a bit more subtle / risky, since I don't see
1677 // what would reflow them in that case if they depended on our size.
1678 for (nsIFrame
* kid
= absoluteContainer
->GetChildList().FirstChild();
1679 kid
; kid
= kid
->GetNextSibling()) {
1680 ConsiderChildOverflow(aMetrics
.mOverflowAreas
, kid
);
1684 LogicalSize containingBlockSize
=
1685 CalculateContainingBlockSizeForAbsolutes(parentWM
, *reflowInput
,
1686 aMetrics
.Size(parentWM
));
1688 // Mark frames that depend on changes we just made to this frame as dirty:
1689 // Now we can assume that the padding edge hasn't moved.
1690 // We need to reflow the absolutes if one of them depends on
1691 // its placeholder position, or the containing block size in a
1692 // direction in which the containing block size might have
1695 // XXX "width" and "height" in this block will become ISize and BSize
1696 // when nsAbsoluteContainingBlock is logicalized
1697 bool cbWidthChanged
= aMetrics
.Width() != oldSize
.width
;
1698 bool isRoot
= !GetContent()->GetParent();
1699 // If isRoot and we have auto height, then we are the initial
1700 // containing block and the containing block height is the
1701 // viewport height, which can't change during incremental
1703 bool cbHeightChanged
=
1704 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== reflowInput
->ComputedHeight()) &&
1705 aMetrics
.Height() != oldSize
.height
;
1707 nsRect
containingBlock(nsPoint(0, 0),
1708 containingBlockSize
.GetPhysicalSize(parentWM
));
1709 AbsPosReflowFlags flags
= AbsPosReflowFlags::ConstrainHeight
;
1710 if (cbWidthChanged
) {
1711 flags
|= AbsPosReflowFlags::CBWidthChanged
;
1713 if (cbHeightChanged
) {
1714 flags
|= AbsPosReflowFlags::CBHeightChanged
;
1716 // Setup the line cursor here to optimize line searching for
1717 // calculating hypothetical position of absolutely-positioned
1719 SetupLineCursorForQuery();
1720 absoluteContainer
->Reflow(this, aPresContext
, *reflowInput
,
1721 state
.mReflowStatus
, containingBlock
, flags
,
1722 &aMetrics
.mOverflowAreas
);
1726 FinishAndStoreOverflow(&aMetrics
, reflowInput
->mStyleDisplay
);
1728 aStatus
= state
.mReflowStatus
;
1731 // Between when we drain pushed floats and when we complete reflow,
1732 // we're allowed to have multiple continuations of the same float on
1733 // our floats list, since a first-in-flow might get pushed to a later
1734 // continuation of its containing block. But it's not permitted
1735 // outside that time.
1736 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1739 IndentBy(stdout
, gNoiseIndent
);
1741 printf(": status=%s metrics=%d,%d carriedMargin=%d",
1742 ToString(aStatus
).c_str(), aMetrics
.ISize(parentWM
),
1743 aMetrics
.BSize(parentWM
), aMetrics
.mCarriedOutBEndMargin
.get());
1744 if (HasOverflowAreas()) {
1745 printf(" overflow-vis={%d,%d,%d,%d}", aMetrics
.InkOverflow().x
,
1746 aMetrics
.InkOverflow().y
, aMetrics
.InkOverflow().width
,
1747 aMetrics
.InkOverflow().height
);
1748 printf(" overflow-scr={%d,%d,%d,%d}", aMetrics
.ScrollableOverflow().x
,
1749 aMetrics
.ScrollableOverflow().y
,
1750 aMetrics
.ScrollableOverflow().width
,
1751 aMetrics
.ScrollableOverflow().height
);
1756 if (gLameReflowMetrics
) {
1757 PRTime end
= PR_Now();
1759 int32_t ectc
= nsLineBox::GetCtorCount();
1760 int32_t numLines
= mLines
.size();
1764 PRTime delta
, perLineDelta
, lines
;
1765 lines
= int64_t(numLines
);
1766 delta
= end
- start
;
1767 perLineDelta
= delta
/ lines
;
1772 ": %" PRId64
" elapsed (%" PRId64
1773 " per line) (%d lines; %d new lines)",
1774 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1775 printf("%s\n", buf
);
1780 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
1781 for (auto& line
: Reversed(Lines())) {
1782 if (0 != line
.BSize() || !line
.CachedIsEmpty()) {
1785 if (line
.HasClearance()) {
1792 static nsLineBox
* FindLineClampTarget(nsBlockFrame
*& aFrame
,
1793 StyleLineClamp aLineNumber
) {
1794 MOZ_ASSERT(aLineNumber
> 0);
1795 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
1796 "Should have been removed earlier in nsBlockReflow::Reflow");
1798 nsLineBox
* target
= nullptr;
1799 nsBlockFrame
* targetFrame
= nullptr;
1800 bool foundFollowingLine
= false;
1802 LineClampLineIterator
iter(aFrame
);
1804 while (nsLineBox
* line
= iter
.GetCurrentLine()) {
1805 MOZ_ASSERT(!line
->HasLineClampEllipsis(),
1806 "Should have been removed earlier in nsBlockFrame::Reflow");
1807 MOZ_ASSERT(!iter
.GetCurrentFrame()->HasAnyStateBits(
1808 NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
1809 "Should have been removed earlier in nsBlockReflow::Reflow");
1811 // Don't count a line that only has collapsible white space (as might exist
1812 // after calling e.g. getBoxQuads).
1813 if (line
->IsEmpty()) {
1818 if (aLineNumber
== 0) {
1819 // We already previously found our target line, and now we have
1820 // confirmed that there is another line after it.
1821 foundFollowingLine
= true;
1825 if (--aLineNumber
== 0) {
1826 // This is our target line. Continue looping to confirm that we
1827 // have another line after us.
1829 targetFrame
= iter
.GetCurrentFrame();
1835 if (!foundFollowingLine
) {
1841 MOZ_ASSERT(targetFrame
);
1843 aFrame
= targetFrame
;
1847 static nscoord
ApplyLineClamp(const ReflowInput
& aReflowInput
,
1848 nsBlockFrame
* aFrame
,
1849 nscoord aContentBlockEndEdge
) {
1850 if (!IsLineClampRoot(aFrame
)) {
1851 return aContentBlockEndEdge
;
1853 auto lineClamp
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
1854 nsBlockFrame
* frame
= aFrame
;
1855 nsLineBox
* line
= FindLineClampTarget(frame
, lineClamp
);
1857 // The number of lines did not exceed the -webkit-line-clamp value.
1858 return aContentBlockEndEdge
;
1861 // Mark the line as having an ellipsis so that TextOverflow will render it.
1862 line
->SetHasLineClampEllipsis();
1863 frame
->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1865 // Translate the b-end edge of the line up to aFrame's space.
1866 nscoord edge
= line
->BEnd();
1867 for (nsIFrame
* f
= frame
; f
!= aFrame
; f
= f
->GetParent()) {
1869 f
->GetLogicalPosition(f
->GetParent()->GetSize()).B(f
->GetWritingMode());
1875 void nsBlockFrame::ComputeFinalSize(const ReflowInput
& aReflowInput
,
1876 BlockReflowState
& aState
,
1877 ReflowOutput
& aMetrics
,
1878 nscoord
* aBEndEdgeOfChildren
) {
1879 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
1880 const LogicalMargin
& borderPadding
= aState
.BorderPadding();
1881 #ifdef NOISY_FINAL_SIZE
1883 printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
1884 aState
.mBCoord
, aState
.mFlags
.mIsBEndMarginRoot
? "yes" : "no",
1885 aState
.mPrevBEndMargin
.get(), borderPadding
.BStart(wm
),
1886 borderPadding
.BEnd(wm
));
1889 // Compute final inline size
1890 LogicalSize
finalSize(wm
);
1891 finalSize
.ISize(wm
) =
1892 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.IStart(wm
),
1893 aReflowInput
.ComputedISize()),
1894 borderPadding
.IEnd(wm
));
1896 // Return block-end margin information
1897 // rbs says he hit this assertion occasionally (see bug 86947), so
1898 // just set the margin to zero and we'll figure out why later
1899 // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
1900 // "someone else set the margin");
1901 nscoord nonCarriedOutBDirMargin
= 0;
1902 if (!aState
.mFlags
.mIsBEndMarginRoot
) {
1903 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1904 // line with clearance and a non-zero block-start margin and all
1905 // subsequent lines are empty, then we do not allow our children's
1906 // carried out block-end margin to be carried out of us and collapse
1907 // with our own block-end margin.
1908 if (CheckForCollapsedBEndMarginFromClearanceLine()) {
1909 // Convert the children's carried out margin to something that
1910 // we will include in our height
1911 nonCarriedOutBDirMargin
= aState
.mPrevBEndMargin
.get();
1912 aState
.mPrevBEndMargin
.Zero();
1914 aMetrics
.mCarriedOutBEndMargin
= aState
.mPrevBEndMargin
;
1916 aMetrics
.mCarriedOutBEndMargin
.Zero();
1919 nscoord blockEndEdgeOfChildren
= aState
.mBCoord
+ nonCarriedOutBDirMargin
;
1920 // Shrink wrap our height around our contents.
1921 if (aState
.mFlags
.mIsBEndMarginRoot
||
1922 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
1923 // When we are a block-end-margin root make sure that our last
1924 // child's block-end margin is fully applied. We also do this when
1925 // we have a computed height, since in that case the carried out
1926 // margin is not going to be applied anywhere, so we should note it
1927 // here to be included in the overflow area.
1928 // Apply the margin only if there's space for it.
1929 if (blockEndEdgeOfChildren
< aState
.mReflowInput
.AvailableBSize()) {
1930 // Truncate block-end margin if it doesn't fit to our available BSize.
1931 blockEndEdgeOfChildren
=
1932 std::min(blockEndEdgeOfChildren
+ aState
.mPrevBEndMargin
.get(),
1933 aState
.mReflowInput
.AvailableBSize());
1936 if (aState
.mFlags
.mBlockNeedsFloatManager
) {
1937 // Include the float manager's state to properly account for the
1938 // block-end margin of any floated elements; e.g., inside a table cell.
1940 // Note: The block coordinate returned by ClearFloats is always greater than
1941 // or equal to blockEndEdgeOfChildren.
1942 std::tie(blockEndEdgeOfChildren
, std::ignore
) =
1943 aState
.ClearFloats(blockEndEdgeOfChildren
, StyleClear::Both
);
1946 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
1947 // Note: We don't use blockEndEdgeOfChildren because it includes the
1949 const nscoord contentBSizeWithBStartBP
=
1950 aState
.mBCoord
+ nonCarriedOutBDirMargin
;
1952 // We don't care about ApplyLineClamp's return value (the line-clamped
1953 // content BSize) in this explicit-BSize codepath, but we do still need to
1954 // call ApplyLineClamp for ellipsis markers to be placed as-needed.
1955 ApplyLineClamp(aState
.mReflowInput
, this, contentBSizeWithBStartBP
);
1957 finalSize
.BSize(wm
) = ComputeFinalBSize(aState
, contentBSizeWithBStartBP
);
1959 // If the content block-size is larger than the effective computed
1960 // block-size, we extend the block-size to contain all the content.
1961 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
1962 if (aReflowInput
.ShouldApplyAutomaticMinimumOnBlockAxis()) {
1963 // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
1964 // the content's block-size plus our border and padding..
1965 finalSize
.BSize(wm
) =
1966 std::max(finalSize
.BSize(wm
),
1967 contentBSizeWithBStartBP
+ borderPadding
.BEnd(wm
));
1970 // Don't carry out a block-end margin when our BSize is fixed.
1972 // Note: this also includes the case that aReflowInput.ComputedBSize() is
1973 // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
1974 // is replaced by the block size from aspect-ratio and inline size.
1975 aMetrics
.mCarriedOutBEndMargin
.Zero();
1977 Maybe
<nscoord
> containBSize
= ContainIntrinsicBSize(
1978 IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE
: 0);
1979 if (containBSize
&& *containBSize
!= NS_UNCONSTRAINEDSIZE
) {
1980 // If we're size-containing in block axis and we don't have a specified
1981 // block size, then our final size should actually be computed from only
1982 // our border, padding and contain-intrinsic-block-size, ignoring the
1983 // actual contents. Hence this case is a simplified version of the case
1986 // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
1987 // special case when it has 'contain-intrinsic-block-size: none', because
1988 // comboboxes implicitly honors the size-containment behavior on its
1989 // nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the
1990 // nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even
1991 // need any special content-size-ignoring behavior in its reflow method,
1992 // because that method just resolves "auto" BSize values to one
1993 // line-height rather than by measuring its contents' BSize.)
1994 nscoord contentBSize
= *containBSize
;
1996 aReflowInput
.ApplyMinMaxBSize(contentBSize
, aState
.mConsumedBSize
);
1997 aMetrics
.mCarriedOutBEndMargin
.Zero();
1998 autoBSize
+= borderPadding
.BStartEnd(wm
);
1999 finalSize
.BSize(wm
) = autoBSize
;
2000 } else if (aState
.mReflowStatus
.IsInlineBreakBefore()) {
2001 // Our parent is expected to push this frame to the next page/column so
2002 // what size we set here doesn't really matter.
2003 finalSize
.BSize(wm
) = aReflowInput
.AvailableBSize();
2004 } else if (aState
.mReflowStatus
.IsComplete()) {
2005 const nscoord lineClampedContentBlockEndEdge
=
2006 ApplyLineClamp(aReflowInput
, this, blockEndEdgeOfChildren
);
2008 const nscoord bpBStart
= borderPadding
.BStart(wm
);
2009 const nscoord contentBSize
= blockEndEdgeOfChildren
- bpBStart
;
2010 const nscoord lineClampedContentBSize
=
2011 lineClampedContentBlockEndEdge
- bpBStart
;
2013 const nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(
2014 lineClampedContentBSize
, aState
.mConsumedBSize
);
2015 if (autoBSize
!= contentBSize
) {
2016 // Our min-block-size, max-block-size, or -webkit-line-clamp value made
2017 // our bsize change. Don't carry out our kids' block-end margins.
2018 aMetrics
.mCarriedOutBEndMargin
.Zero();
2020 nscoord bSize
= autoBSize
+ borderPadding
.BStartEnd(wm
);
2021 if (MOZ_UNLIKELY(autoBSize
> contentBSize
&&
2022 bSize
> aReflowInput
.AvailableBSize() &&
2023 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
)) {
2024 // Applying `min-size` made us overflow our available size.
2025 // Clamp it and report that we're Incomplete, or BreakBefore if we have
2026 // 'break-inside: avoid' that is applicable.
2027 bSize
= aReflowInput
.AvailableBSize();
2028 if (ShouldAvoidBreakInside(aReflowInput
)) {
2029 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
2031 aState
.mReflowStatus
.SetIncomplete();
2034 finalSize
.BSize(wm
) = bSize
;
2037 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
2038 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
2039 nscoord bSize
= std::max(aState
.mBCoord
, aReflowInput
.AvailableBSize());
2040 if (aReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
2041 // This should never happen, but it does. See bug 414255
2042 bSize
= aState
.mBCoord
;
2044 const nscoord maxBSize
= aReflowInput
.ComputedMaxBSize();
2045 if (maxBSize
!= NS_UNCONSTRAINEDSIZE
&&
2046 aState
.mConsumedBSize
+ bSize
- borderPadding
.BStart(wm
) > maxBSize
) {
2047 nscoord bEnd
= std::max(0, maxBSize
- aState
.mConsumedBSize
) +
2048 borderPadding
.BStart(wm
);
2049 // Note that |borderPadding| has GetSkipSides applied, so we ask
2050 // aReflowInput for the actual value we'd use on a last fragment here:
2051 bEnd
+= aReflowInput
.ComputedLogicalBorderPadding(wm
).BEnd(wm
);
2052 if (bEnd
<= aReflowInput
.AvailableBSize()) {
2053 // We actually fit after applying `max-size` so we should be
2054 // Overflow-Incomplete instead.
2056 aState
.mReflowStatus
.SetOverflowIncomplete();
2059 finalSize
.BSize(wm
) = bSize
;
2063 if (IsTrueOverflowContainer()) {
2064 if (aState
.mReflowStatus
.IsIncomplete()) {
2065 // Overflow containers can only be overflow complete.
2066 // Note that auto height overflow containers have no normal children
2067 NS_ASSERTION(finalSize
.BSize(wm
) == 0,
2068 "overflow containers must be zero-block-size");
2069 aState
.mReflowStatus
.SetOverflowIncomplete();
2071 } else if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2072 !aState
.mReflowStatus
.IsInlineBreakBefore() &&
2073 aState
.mReflowStatus
.IsComplete()) {
2074 // Currently only used for grid items, but could be used in other contexts.
2075 // The FragStretchBSizeProperty is our expected non-fragmented block-size
2076 // we should stretch to (for align-self:stretch etc). In some fragmentation
2077 // cases though, the last fragment (this frame since we're complete), needs
2078 // to have extra size applied because earlier fragments consumed too much of
2079 // our computed size due to overflowing their containing block. (E.g. this
2080 // ensures we fill the last row when a multi-row grid item is fragmented).
2082 nscoord bSize
= GetProperty(FragStretchBSizeProperty(), &found
);
2084 finalSize
.BSize(wm
) = std::max(bSize
, finalSize
.BSize(wm
));
2088 // Clamp the content size to fit within the margin-box clamp size, if any.
2089 if (MOZ_UNLIKELY(aReflowInput
.mComputeSizeFlags
.contains(
2090 ComputeSizeFlag::BClampMarginBoxMinSize
)) &&
2091 aState
.mReflowStatus
.IsComplete()) {
2093 nscoord cbSize
= GetProperty(BClampMarginBoxMinSizeProperty(), &found
);
2095 auto marginBoxBSize
=
2096 finalSize
.BSize(wm
) +
2097 aReflowInput
.ComputedLogicalMargin(wm
).BStartEnd(wm
);
2098 auto overflow
= marginBoxBSize
- cbSize
;
2100 auto contentBSize
= finalSize
.BSize(wm
) - borderPadding
.BStartEnd(wm
);
2101 auto newContentBSize
= std::max(nscoord(0), contentBSize
- overflow
);
2102 // XXXmats deal with percentages better somehow?
2103 finalSize
.BSize(wm
) -= contentBSize
- newContentBSize
;
2108 // Screen out negative block sizes --- can happen due to integer overflows :-(
2109 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
2110 *aBEndEdgeOfChildren
= blockEndEdgeOfChildren
;
2112 if (blockEndEdgeOfChildren
!= finalSize
.BSize(wm
) - borderPadding
.BEnd(wm
)) {
2113 SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren
);
2115 RemoveProperty(BlockEndEdgeOfChildrenProperty());
2118 aMetrics
.SetSize(wm
, finalSize
);
2121 if ((ABSURD_SIZE(aMetrics
.Width()) || ABSURD_SIZE(aMetrics
.Height())) &&
2122 !GetParent()->IsAbsurdSizeAssertSuppressed()) {
2124 printf(": WARNING: desired:%d,%d\n", aMetrics
.Width(), aMetrics
.Height());
2129 void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
2130 OverflowAreas
& aOverflowAreas
, nscoord aBEndEdgeOfChildren
,
2131 const nsStyleDisplay
* aDisplay
) const {
2132 const auto wm
= GetWritingMode();
2134 // Factor in the block-end edge of the children. Child frames will be added
2135 // to the overflow area as we iterate through the lines, but their margins
2136 // won't, so we need to account for block-end margins here.
2137 // REVIEW: For now, we do this for both visual and scrollable area,
2138 // although when we make scrollable overflow area not be a subset of
2139 // visual, we can change this.
2141 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
2142 // If we are a scrolled inner frame, add our block-end padding to our
2143 // children's block-end edge.
2145 // Note: aBEndEdgeOfChildren already includes our own block-start padding
2146 // because it is relative to our block-start edge of our border-box, which
2147 // is the same as our padding-box here.
2148 MOZ_ASSERT(GetLogicalUsedBorderAndPadding(wm
) == GetLogicalUsedPadding(wm
),
2149 "A scrolled inner frame shouldn't have any border!");
2150 aBEndEdgeOfChildren
+= GetLogicalUsedPadding(wm
).BEnd(wm
);
2153 // XXX Currently, overflow areas are stored as physical rects, so we have
2154 // to handle writing modes explicitly here. If we change overflow rects
2155 // to be stored logically, this can be simplified again.
2156 if (wm
.IsVertical()) {
2157 if (wm
.IsVerticalLR()) {
2158 for (const auto otype
: AllOverflowTypes()) {
2159 if (!(aDisplay
->IsContainLayout() &&
2160 otype
== OverflowType::Scrollable
)) {
2161 // Layout containment should force all overflow to be ink (visual)
2162 // overflow, so if we're layout-contained, we only add our children's
2163 // block-end edge to the ink (visual) overflow -- not to the
2164 // scrollable overflow.
2165 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2166 o
.width
= std::max(o
.XMost(), aBEndEdgeOfChildren
) - o
.x
;
2170 for (const auto otype
: AllOverflowTypes()) {
2171 if (!(aDisplay
->IsContainLayout() &&
2172 otype
== OverflowType::Scrollable
)) {
2173 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2174 nscoord xmost
= o
.XMost();
2175 o
.x
= std::min(o
.x
, xmost
- aBEndEdgeOfChildren
);
2176 o
.width
= xmost
- o
.x
;
2181 for (const auto otype
: AllOverflowTypes()) {
2182 if (!(aDisplay
->IsContainLayout() && otype
== OverflowType::Scrollable
)) {
2183 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2184 o
.height
= std::max(o
.YMost(), aBEndEdgeOfChildren
) - o
.y
;
2190 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas
& aOverflowAreas
,
2191 nscoord aBEndEdgeOfChildren
,
2192 const nsStyleDisplay
* aDisplay
) const {
2193 // XXX_perf: This can be done incrementally. It is currently one of
2194 // the things that makes incremental reflow O(N^2).
2195 auto overflowClipAxes
= ShouldApplyOverflowClipping(aDisplay
);
2196 auto overflowClipMargin
= OverflowClipMargin(overflowClipAxes
);
2197 if (overflowClipAxes
== PhysicalAxes::Both
&&
2198 overflowClipMargin
== nsSize()) {
2202 // We rely here on our caller having called SetOverflowAreasToDesiredBounds().
2203 nsRect frameBounds
= aOverflowAreas
.ScrollableOverflow();
2205 for (const auto& line
: Lines()) {
2206 if (aDisplay
->IsContainLayout()) {
2207 // If we have layout containment, we should only consider our child's
2208 // ink overflow, leaving the scrollable regions of the parent
2210 // Note: scrollable overflow is a subset of ink overflow,
2211 // so this has the same affect as unioning the child's visual and
2212 // scrollable overflow with its parent's ink overflow.
2213 nsRect childVisualRect
= line
.InkOverflowRect();
2214 OverflowAreas childVisualArea
= OverflowAreas(childVisualRect
, nsRect());
2215 aOverflowAreas
.UnionWith(childVisualArea
);
2217 aOverflowAreas
.UnionWith(line
.GetOverflowAreas());
2221 // Factor an outside ::marker in; normally the ::marker will be factored
2222 // into the line-box's overflow areas. However, if the line is a block
2223 // line then it won't; if there are no lines, it won't. So just
2224 // factor it in anyway (it can't hurt if it was already done).
2225 // XXXldb Can we just fix GetOverflowArea instead?
2226 if (nsIFrame
* outsideMarker
= GetOutsideMarker()) {
2227 aOverflowAreas
.UnionAllWith(outsideMarker
->GetRect());
2230 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, aBEndEdgeOfChildren
, aDisplay
);
2232 if (overflowClipAxes
!= PhysicalAxes::None
) {
2233 aOverflowAreas
.ApplyClipping(frameBounds
, overflowClipAxes
,
2234 overflowClipMargin
);
2237 #ifdef NOISY_OVERFLOW_AREAS
2238 printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
2239 ToString(aOverflowAreas
.InkOverflow()).c_str(),
2240 ToString(aOverflowAreas
.ScrollableOverflow()).c_str());
2244 void nsBlockFrame::UnionChildOverflow(OverflowAreas
& aOverflowAreas
) {
2245 // We need to update the overflow areas of lines manually, as they
2246 // get cached and re-used otherwise. Lines aren't exposed as normal
2247 // frame children, so calling UnionChildOverflow alone will end up
2248 // using the old cached values.
2249 for (auto& line
: Lines()) {
2250 nsRect bounds
= line
.GetPhysicalBounds();
2251 OverflowAreas
lineAreas(bounds
, bounds
);
2253 int32_t n
= line
.GetChildCount();
2254 for (nsIFrame
* lineFrame
= line
.mFirstChild
; n
> 0;
2255 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2256 ConsiderChildOverflow(lineAreas
, lineFrame
);
2259 // Consider the overflow areas of the floats attached to the line as well
2260 if (line
.HasFloats()) {
2261 for (nsIFrame
* f
: line
.Floats()) {
2262 ConsiderChildOverflow(lineAreas
, f
);
2266 line
.SetOverflowAreas(lineAreas
);
2267 aOverflowAreas
.UnionWith(lineAreas
);
2270 // Union with child frames, skipping the principal and float lists
2271 // since we already handled those using the line boxes.
2272 nsLayoutUtils::UnionChildOverflow(
2273 this, aOverflowAreas
,
2274 {FrameChildListID::Principal
, FrameChildListID::Float
});
2277 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas
& aOverflowAreas
) {
2279 nscoord blockEndEdgeOfChildren
=
2280 GetProperty(BlockEndEdgeOfChildrenProperty(), &found
);
2282 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, blockEndEdgeOfChildren
,
2286 // Line cursor invariants depend on the overflow areas of the lines, so
2287 // we must clear the line cursor since those areas may have changed.
2289 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
2292 void nsBlockFrame::LazyMarkLinesDirty() {
2293 if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
)) {
2294 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2295 line
!= line_end
; ++line
) {
2296 int32_t n
= line
->GetChildCount();
2297 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2298 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2299 if (lineFrame
->IsSubtreeDirty()) {
2300 // NOTE: MarkLineDirty does more than just marking the line dirty.
2301 MarkLineDirty(line
, &mLines
);
2306 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
2310 void nsBlockFrame::MarkLineDirty(LineIterator aLine
,
2311 const nsLineList
* aLineList
) {
2314 aLine
->SetInvalidateTextRuns(true);
2317 IndentBy(stdout
, gNoiseIndent
);
2319 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
2323 // Mark previous line dirty if it's an inline line so that it can
2324 // maybe pullup something from the line just affected.
2325 // XXX We don't need to do this if aPrevLine ends in a break-after...
2326 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
2327 aLine
.prev()->IsInline()) {
2328 aLine
.prev()->MarkDirty();
2329 aLine
.prev()->SetInvalidateTextRuns(true);
2332 IndentBy(stdout
, gNoiseIndent
);
2334 printf(": mark prev-line %p dirty\n",
2335 static_cast<void*>(aLine
.prev().get()));
2342 * Test whether lines are certain to be aligned left so that we can make
2343 * resizing optimizations
2345 static inline bool IsAlignedLeft(StyleTextAlign aAlignment
,
2346 StyleDirection aDirection
,
2347 uint8_t aUnicodeBidi
, nsIFrame
* aFrame
) {
2348 return SVGUtils::IsInSVGTextSubtree(aFrame
) ||
2349 StyleTextAlign::Left
== aAlignment
||
2350 (((StyleTextAlign::Start
== aAlignment
&&
2351 StyleDirection::Ltr
== aDirection
) ||
2352 (StyleTextAlign::End
== aAlignment
&&
2353 StyleDirection::Rtl
== aDirection
)) &&
2354 !(NS_STYLE_UNICODE_BIDI_PLAINTEXT
& aUnicodeBidi
));
2357 void nsBlockFrame::PrepareResizeReflow(BlockReflowState
& aState
) {
2358 // See if we can try and avoid marking all the lines as dirty
2359 // FIXME(emilio): This should be writing-mode aware, I guess.
2360 bool tryAndSkipLines
=
2361 // The left content-edge must be a constant distance from the left
2363 !StylePadding()->mPadding
.Get(eSideLeft
).HasPercent();
2366 if (gDisableResizeOpt
) {
2367 tryAndSkipLines
= false;
2370 if (!tryAndSkipLines
) {
2371 IndentBy(stdout
, gNoiseIndent
);
2373 printf(": marking all lines dirty: availISize=%d\n",
2374 aState
.mReflowInput
.AvailableISize());
2379 if (tryAndSkipLines
) {
2380 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2381 nscoord newAvailISize
=
2382 aState
.mReflowInput
.ComputedLogicalBorderPadding(wm
).IStart(wm
) +
2383 aState
.mReflowInput
.ComputedISize();
2387 IndentBy(stdout
, gNoiseIndent
);
2389 printf(": trying to avoid marking all lines dirty\n");
2393 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2394 line
!= line_end
; ++line
) {
2395 // We let child blocks make their own decisions the same
2397 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2398 if (line
->IsBlock() || line
->HasFloats() ||
2399 (!isLastLine
&& !line
->HasForcedLineBreakAfter()) ||
2400 ((isLastLine
|| !line
->IsLineWrapped())) ||
2401 line
->ResizeReflowOptimizationDisabled() ||
2402 line
->IsImpactedByFloat() || (line
->IEnd() > newAvailISize
)) {
2406 #ifdef REALLY_NOISY_REFLOW
2407 if (!line
->IsBlock()) {
2408 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2409 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
2413 if (gNoisyReflow
&& !line
->IsDirty()) {
2414 IndentBy(stdout
, gNoiseIndent
+ 1);
2416 "skipped: line=%p next=%p %s %s%s%s clearTypeBefore/After=%s/%s "
2418 static_cast<void*>(line
.get()),
2420 (line
.next() != LinesEnd() ? line
.next().get() : nullptr)),
2421 line
->IsBlock() ? "block" : "inline",
2422 line
->HasForcedLineBreakAfter() ? "has-break-after " : "",
2423 line
->HasFloats() ? "has-floats " : "",
2424 line
->IsImpactedByFloat() ? "impacted " : "",
2425 line
->StyleClearToString(line
->FloatClearTypeBefore()),
2426 line
->StyleClearToString(line
->FloatClearTypeAfter()),
2432 // Mark everything dirty
2433 for (auto& line
: Lines()) {
2439 //----------------------------------------
2442 * Propagate reflow "damage" from from earlier lines to the current
2443 * line. The reflow damage comes from the following sources:
2444 * 1. The regions of float damage remembered during reflow.
2445 * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
2446 * float, either the previous reflow or now.
2448 * When entering this function, |aLine| is still at its old position and
2449 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2450 * doesn't get marked dirty and reflowed entirely).
2452 void nsBlockFrame::PropagateFloatDamage(BlockReflowState
& aState
,
2454 nscoord aDeltaBCoord
) {
2455 nsFloatManager
* floatManager
= aState
.FloatManager();
2457 (aState
.mReflowInput
.mParentReflowInput
&&
2458 aState
.mReflowInput
.mParentReflowInput
->mFloatManager
== floatManager
) ||
2459 aState
.mReflowInput
.mBlockDelta
== 0,
2460 "Bad block delta passed in");
2462 // Check to see if there are any floats; if there aren't, there can't
2463 // be any float damage
2464 if (!floatManager
->HasAnyFloats()) {
2468 // Check the damage region recorded in the float damage.
2469 if (floatManager
->HasFloatDamage()) {
2470 // Need to check mBounds *and* mCombinedArea to find intersections
2471 // with aLine's floats
2472 nscoord lineBCoordBefore
= aLine
->BStart() + aDeltaBCoord
;
2473 nscoord lineBCoordAfter
= lineBCoordBefore
+ aLine
->BSize();
2474 // Scrollable overflow should be sufficient for things that affect
2476 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2477 nsSize containerSize
= aState
.ContainerSize();
2478 LogicalRect overflow
=
2479 aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
, containerSize
);
2480 nscoord lineBCoordCombinedBefore
= overflow
.BStart(wm
) + aDeltaBCoord
;
2481 nscoord lineBCoordCombinedAfter
=
2482 lineBCoordCombinedBefore
+ overflow
.BSize(wm
);
2485 floatManager
->IntersectsDamage(lineBCoordBefore
, lineBCoordAfter
) ||
2486 floatManager
->IntersectsDamage(lineBCoordCombinedBefore
,
2487 lineBCoordCombinedAfter
);
2494 // Check if the line is moving relative to the float manager
2495 if (aDeltaBCoord
+ aState
.mReflowInput
.mBlockDelta
!= 0) {
2496 if (aLine
->IsBlock()) {
2497 // Unconditionally reflow sliding blocks; we only really need to reflow
2498 // if there's a float impacting this block, but the current float manager
2499 // makes it difficult to check that. Therefore, we let the child block
2500 // decide what it needs to reflow.
2503 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
2504 nsFlowAreaRect floatAvailableSpace
=
2505 aState
.GetFloatAvailableSpaceForBSize(aLine
->BStart() + aDeltaBCoord
,
2506 aLine
->BSize(), nullptr);
2508 #ifdef REALLY_NOISY_REFLOW
2509 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2510 wasImpactedByFloat
, floatAvailableSpace
.HasFloats());
2513 // Mark the line dirty if it was or is affected by a float
2514 // We actually only really need to reflow if the amount of impact
2515 // changes, but that's not straightforward to check
2516 if (wasImpactedByFloat
|| floatAvailableSpace
.HasFloats()) {
2523 static bool LineHasClear(nsLineBox
* aLine
) {
2524 return aLine
->IsBlock()
2525 ? (aLine
->HasForcedLineBreakBefore() ||
2526 aLine
->mFirstChild
->HasAnyStateBits(
2527 NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
2528 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
2529 : aLine
->HasFloatClearTypeAfter();
2533 * Reparent a whole list of floats from aOldParent to this block. The
2534 * floats might be taken from aOldParent's overflow list. They will be
2535 * removed from the list. They end up appended to our mFloats list.
2537 void nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
2538 nsBlockFrame
* aOldParent
,
2539 bool aReparentSiblings
) {
2541 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
2542 if (list
.NotEmpty()) {
2543 for (nsIFrame
* f
: list
) {
2544 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
2545 "CollectFloats should've removed that bit");
2546 ReparentFrame(f
, aOldParent
, this);
2548 mFloats
.AppendFrames(nullptr, std::move(list
));
2552 static void DumpLine(const BlockReflowState
& aState
, nsLineBox
* aLine
,
2553 nscoord aDeltaBCoord
, int32_t aDeltaIndent
) {
2555 if (nsBlockFrame::gNoisyReflow
) {
2556 nsRect
ovis(aLine
->InkOverflowRect());
2557 nsRect
oscr(aLine
->ScrollableOverflowRect());
2558 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
2560 "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2561 "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2562 "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2563 static_cast<void*>(aLine
), aState
.mBCoord
,
2564 aLine
->IsDirty() ? "yes" : "no", aLine
->IStart(), aLine
->BStart(),
2565 aLine
->ISize(), aLine
->BSize(), ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
2566 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
, aDeltaBCoord
,
2567 aState
.mPrevBEndMargin
.get(), aLine
->GetChildCount());
2572 static bool LinesAreEmpty(const nsLineList
& aList
) {
2573 for (const auto& line
: aList
) {
2574 if (!line
.IsEmpty()) {
2581 void nsBlockFrame::ReflowDirtyLines(BlockReflowState
& aState
) {
2582 bool keepGoing
= true;
2583 bool repositionViews
= false; // should we really need this?
2584 bool foundAnyClears
= aState
.mTrailingClearFromPIF
!= StyleClear::None
;
2585 bool willReflowAgain
= false;
2589 IndentBy(stdout
, gNoiseIndent
);
2591 printf(": reflowing dirty lines");
2592 printf(" computedISize=%d\n", aState
.mReflowInput
.ComputedISize());
2594 AutoNoisyIndenter
indent(gNoisyReflow
);
2597 bool selfDirty
= HasAnyStateBits(NS_FRAME_IS_DIRTY
) ||
2598 (aState
.mReflowInput
.IsBResize() &&
2599 HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
));
2601 // Reflow our last line if our availableBSize has increased
2602 // so that we (and our last child) pull up content as necessary
2603 if (aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2605 aState
.mReflowInput
.AvailableBSize() >
2606 GetLogicalSize().BSize(aState
.mReflowInput
.GetWritingMode())) {
2607 LineIterator lastLine
= LinesEnd();
2608 if (lastLine
!= LinesBegin()) {
2610 lastLine
->MarkDirty();
2613 // the amount by which we will slide the current line if it is not
2615 nscoord deltaBCoord
= 0;
2617 // whether we did NOT reflow the previous line and thus we need to
2618 // recompute the carried out margin before the line if we want to
2619 // reflow it or if its previous margin is dirty
2620 bool needToRecoverState
= false;
2621 // Float continuations were reflowed in ReflowPushedFloats
2622 bool reflowedFloat
=
2623 mFloats
.NotEmpty() &&
2624 mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
2625 bool lastLineMovedUp
= false;
2626 // We save up information about BR-clearance here
2627 StyleClear inlineFloatClearType
= aState
.mTrailingClearFromPIF
;
2629 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2631 // Determine if children of this frame could have breaks between them for
2634 // We need to check for paginated layout, the named-page pref, and if the
2635 // available block-size is constrained.
2637 // Note that we need to check for paginated layout as named-pages are only
2638 // used during paginated reflow. We need to additionally check for
2639 // unconstrained block-size to avoid introducing fragmentation breaks during
2640 // "measuring" reflows within an overall paginated reflow, and to avoid
2641 // fragmentation in monolithic containers like 'inline-block'.
2643 // Because we can only break for named pages using Class A breakpoints, we
2644 // also need to check that the block flow direction of the containing frame
2645 // of these items (which is this block) is parallel to that of this page.
2646 // See: https://www.w3.org/TR/css-break-3/#btw-blocks
2647 const nsPresContext
* const presCtx
= aState
.mPresContext
;
2648 const bool canBreakForPageNames
=
2649 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2650 presCtx
->IsPaginated() && StaticPrefs::layout_css_named_pages_enabled() &&
2651 presCtx
->GetPresShell()->GetRootFrame()->GetWritingMode().IsVertical() ==
2652 GetWritingMode().IsVertical();
2654 // Reflow the lines that are already ours
2655 for (; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
2656 DumpLine(aState
, line
, deltaBCoord
, 0);
2658 AutoNoisyIndenter
indent2(gNoisyReflow
);
2665 // This really sucks, but we have to look inside any blocks that have clear
2666 // elements inside them.
2667 // XXX what can we do smarter here?
2668 if (!line
->IsDirty() && line
->IsBlock() &&
2669 line
->mFirstChild
->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
2673 nsIFrame
* floatAvoidingBlock
= nullptr;
2674 if (line
->IsBlock() &&
2675 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
2676 floatAvoidingBlock
= line
->mFirstChild
;
2679 // We have to reflow the line if it's a block whose clearance
2680 // might have changed, so detect that.
2681 if (!line
->IsDirty() &&
2682 (line
->HasForcedLineBreakBefore() || floatAvoidingBlock
)) {
2683 nscoord curBCoord
= aState
.mBCoord
;
2684 // See where we would be after applying any clearance due to
2686 if (inlineFloatClearType
!= StyleClear::None
) {
2687 std::tie(curBCoord
, std::ignore
) =
2688 aState
.ClearFloats(curBCoord
, inlineFloatClearType
);
2691 auto [newBCoord
, result
] = aState
.ClearFloats(
2692 curBCoord
, line
->FloatClearTypeBefore(), floatAvoidingBlock
);
2694 if (line
->HasClearance()) {
2695 // Reflow the line if it might not have clearance anymore.
2696 if (result
== ClearFloatsResult::BCoordNoChange
2697 // aState.mBCoord is the clearance point which should be the
2698 // block-start border-edge of the block frame. If sliding the
2699 // block by deltaBCoord isn't going to put it in the predicted
2700 // position, then we'd better reflow the line.
2701 || newBCoord
!= line
->BStart() + deltaBCoord
) {
2705 // Reflow the line if the line might have clearance now.
2706 if (result
!= ClearFloatsResult::BCoordNoChange
) {
2712 // We might have to reflow a line that is after a clearing BR.
2713 if (inlineFloatClearType
!= StyleClear::None
) {
2714 std::tie(aState
.mBCoord
, std::ignore
) =
2715 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
2716 if (aState
.mBCoord
!= line
->BStart() + deltaBCoord
) {
2717 // SlideLine is not going to put the line where the clearance
2718 // put it. Reflow the line to be sure.
2721 inlineFloatClearType
= StyleClear::None
;
2724 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
2725 if (previousMarginWasDirty
) {
2726 // If the previous margin is dirty, reflow the current line
2728 line
->ClearPreviousMarginDirty();
2729 } else if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
2730 const nscoord scrollableOverflowBEnd
=
2731 LogicalRect(line
->mWritingMode
, line
->ScrollableOverflowRect(),
2732 line
->mContainerSize
)
2733 .BEnd(line
->mWritingMode
);
2734 if (scrollableOverflowBEnd
+ deltaBCoord
> aState
.ContentBEnd()) {
2735 // Lines that aren't dirty but get slid past our available block-size
2736 // constraint must be reflowed.
2741 if (!line
->IsDirty()) {
2742 const bool isPaginated
=
2743 // Last column can be reflowed unconstrained during column balancing.
2744 // Hence the additional NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR bit check
2745 // as a fail-safe fallback.
2746 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
||
2747 HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR
) ||
2748 // Table can also be reflowed unconstrained during printing.
2749 aState
.mPresContext
->IsPaginated();
2751 // We are in a paginated context, i.e. in columns or pages.
2752 const bool mayContainFloats
=
2753 line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed();
2754 if (mayContainFloats
) {
2755 // The following if-else conditions check whether this line -- which
2756 // might have floats in its subtree, or has floats as direct children,
2757 // or had floats pushed -- needs to be reflowed.
2758 if (deltaBCoord
!= 0 || aState
.mReflowInput
.IsBResize()) {
2759 // The distance to the block-end edge might have changed. Reflow the
2760 // line both because the breakpoints within its floats may have
2761 // changed and because we might have to push/pull the floats in
2764 } else if (HasPushedFloats()) {
2765 // We had pushed floats which haven't been drained by our
2766 // next-in-flow, which means our parent is currently reflowing us
2767 // again due to clearance without creating a next-in-flow for us.
2768 // Reflow the line to redo the floats split logic to correctly set
2769 // our reflow status.
2771 } else if (aState
.mReflowInput
.mFlags
.mMustReflowPlaceholders
) {
2772 // Reflow the line (that may containing a float's placeholder frame)
2773 // if our parent tells us to do so.
2775 } else if (aState
.mReflowInput
.mFlags
.mMovedBlockFragments
) {
2776 // Our parent's line containing us moved to a different fragment.
2777 // Reflow the line because the decision about whether the float fits
2778 // may be different in a different fragment.
2785 if (!line
->IsDirty()) {
2786 // See if there's any reflow damage that requires that we mark the
2788 PropagateFloatDamage(aState
, line
, deltaBCoord
);
2791 // If the container size has changed, reset mContainerSize. If the
2792 // line's writing mode is not ltr, or if the line is not left-aligned, also
2793 // mark the line dirty.
2794 if (aState
.ContainerSize() != line
->mContainerSize
) {
2795 line
->mContainerSize
= aState
.ContainerSize();
2797 const bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2798 const auto align
= isLastLine
? StyleText()->TextAlignForLastLine()
2799 : StyleText()->mTextAlign
;
2800 if (line
->mWritingMode
.IsVertical() || line
->mWritingMode
.IsBidiRTL() ||
2801 !IsAlignedLeft(align
, StyleVisibility()->mDirection
,
2802 StyleTextReset()->mUnicodeBidi
, this)) {
2807 // Check for a page break caused by CSS named pages.
2809 // We should break for named pages when two frames meet at a class A
2810 // breakpoint, where the first frame has a different end page value to the
2811 // second frame's start page value. canBreakForPageNames is true iff
2812 // children of this frame can form class A breakpoints, and that we are not
2813 // in a measurement reflow or in a monolithic container such as
2816 // We specifically do not want to cause a page-break for named pages when
2817 // we are at the top of a page. This would otherwise happen when the
2818 // previous sibling is an nsPageBreakFrame, or all previous siblings on the
2819 // current page are zero-height. The latter may not be per-spec, but is
2820 // compatible with Chrome's implementation of named pages.
2821 const nsAtom
* nextPageName
= nullptr;
2822 bool shouldBreakForPageName
= false;
2823 if (canBreakForPageNames
&& (!aState
.mReflowInput
.mFlags
.mIsTopOfPage
||
2824 !aState
.IsAdjacentWithBStart())) {
2825 const nsIFrame
* const frame
= line
->mFirstChild
;
2826 if (const nsIFrame
* const prevFrame
= frame
->GetPrevSibling()) {
2827 if (!frame
->IsPlaceholderFrame() && !prevFrame
->IsPlaceholderFrame()) {
2828 nextPageName
= frame
->GetStartPageValue();
2829 if (nextPageName
!= prevFrame
->GetEndPageValue()) {
2830 shouldBreakForPageName
= true;
2837 if (needToRecoverState
&& line
->IsDirty()) {
2838 // We need to reconstruct the block-end margin only if we didn't
2839 // reflow the previous line and we do need to reflow (or repair
2840 // the block-start position of) the next line.
2841 aState
.ReconstructMarginBefore(line
);
2844 bool reflowedPrevLine
= !needToRecoverState
;
2845 if (needToRecoverState
) {
2846 needToRecoverState
= false;
2848 // Update aState.mPrevChild as if we had reflowed all of the frames in
2850 if (line
->IsDirty()) {
2852 line
->mFirstChild
->GetPrevSibling() == line
.prev()->LastChild(),
2853 "unexpected line frames");
2854 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
2858 // Now repair the line and update |aState.mBCoord| by calling
2859 // |ReflowLine| or |SlideLine|.
2860 // If we're going to reflow everything again, then no need to reflow
2861 // the dirty line ... unless the line has floats, in which case we'd
2862 // better reflow it now to refresh its float cache, which may contain
2863 // dangling frame pointers! Ugh! This reflow of the line may be
2864 // incorrect because we skipped reflowing previous lines (e.g., floats
2865 // may be placed incorrectly), but that's OK because we'll mark the
2866 // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
2867 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
2868 lastLineMovedUp
= true;
2870 bool maybeReflowingForFirstTime
=
2871 line
->IStart() == 0 && line
->BStart() == 0 && line
->ISize() == 0 &&
2874 // Compute the dirty lines "before" BEnd, after factoring in
2875 // the running deltaBCoord value - the running value is implicit in
2877 nscoord oldB
= line
->BStart();
2878 nscoord oldBMost
= line
->BEnd();
2880 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
2881 "Don't reflow blocks while willReflowAgain is true, reflow "
2882 "of block abs-pos children depends on this");
2884 if (shouldBreakForPageName
) {
2885 // Immediately fragment for page-name. It is possible we could break
2886 // out of the loop right here, but this should make it more similar to
2887 // what happens when reflow causes fragmentation.
2888 PushTruncatedLine(aState
, line
, &keepGoing
);
2889 PresShell()->FrameConstructor()->SetNextPageContentFramePageName(
2890 nextPageName
? nextPageName
: GetAutoPageValue());
2892 // Reflow the dirty line. If it's an incremental reflow, then force
2893 // it to invalidate the dirty area if necessary
2894 ReflowLine(aState
, line
, &keepGoing
);
2897 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
2899 willReflowAgain
= true;
2900 // Note that once we've entered this state, every line that gets here
2901 // (e.g. because it has floats) gets marked dirty and reflowed again.
2902 // in the next pass. This is important, see above.
2905 if (line
->HasFloats()) {
2906 reflowedFloat
= true;
2910 DumpLine(aState
, line
, deltaBCoord
, -1);
2911 if (0 == line
->GetChildCount()) {
2912 DeleteLine(aState
, line
, line_end
);
2917 // Test to see whether the margin that should be carried out
2918 // to the next line (NL) might have changed. In ReflowBlockFrame
2919 // we call nextLine->MarkPreviousMarginDirty if the block's
2920 // actual carried-out block-end margin changed. So here we only
2921 // need to worry about the following effects:
2922 // 1) the line was just created, and it might now be blocking
2923 // a carried-out block-end margin from previous lines that
2924 // used to reach NL from reaching NL
2925 // 2) the line used to be empty, and is now not empty,
2926 // thus blocking a carried-out block-end margin from previous lines
2927 // that used to reach NL from reaching NL
2928 // 3) the line wasn't empty, but now is, so a carried-out
2929 // block-end margin from previous lines that didn't used to reach NL
2931 // 4) the line might have changed in a way that affects NL's
2932 // ShouldApplyBStartMargin decision. The three things that matter
2933 // are the line's emptiness, its adjacency to the block-start edge of the
2934 // block, and whether it has clearance (the latter only matters if the
2935 // block was and is adjacent to the block-start and empty).
2937 // If the line is empty now, we can't reliably tell if the line was empty
2938 // before, so we just assume it was and do
2939 // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
2940 // redundant; if the line is empty now we don't need to check 4), but if
2941 // the line is not empty now and we're sure it wasn't empty before, any
2942 // adjacency and clearance changes are irrelevant to the result of
2943 // nextLine->ShouldApplyBStartMargin.
2944 if (line
.next() != LinesEnd()) {
2945 bool maybeWasEmpty
= oldB
== line
.next()->BStart();
2946 bool isEmpty
= line
->CachedIsEmpty();
2947 if (maybeReflowingForFirstTime
/*1*/ ||
2948 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
2949 line
.next()->MarkPreviousMarginDirty();
2950 // since it's marked dirty, nobody will care about |deltaBCoord|
2954 // If the line was just reflowed for the first time, then its
2955 // old mBounds cannot be trusted so this deltaBCoord computation is
2956 // bogus. But that's OK because we just did
2957 // MarkPreviousMarginDirty on the next line which will force it
2958 // to be reflowed, so this computation of deltaBCoord will not be
2960 deltaBCoord
= line
->BEnd() - oldBMost
;
2962 // Now do an interrupt check. We want to do this only in the case when we
2963 // actually reflow the line, so that if we get back in here we'll get
2964 // further on the reflow before interrupting.
2965 aState
.mPresContext
->CheckForInterrupt(this);
2967 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
2968 // Nop except for blocks (we don't create overflow container
2969 // continuations for any inlines atm), so only checking mFirstChild
2972 lastLineMovedUp
= deltaBCoord
< 0;
2974 if (deltaBCoord
!= 0) {
2975 SlideLine(aState
, line
, deltaBCoord
);
2977 repositionViews
= true;
2980 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
2981 "Possibly stale float cache here!");
2982 if (willReflowAgain
&& line
->IsBlock()) {
2983 // If we're going to reflow everything again, and this line is a block,
2984 // then there is no need to recover float state. The line may contain
2985 // other lines with floats, but in that case RecoverStateFrom would only
2986 // add floats to the float manager. We don't need to do that because
2987 // everything's going to get reflowed again "for real". Calling
2988 // RecoverStateFrom in this situation could be lethal because the
2989 // block's descendant lines may have float caches containing dangling
2990 // frame pointers. Ugh!
2991 // If this line is inline, then we need to recover its state now
2992 // to make sure that we don't forget to move its floats by deltaBCoord.
2994 // XXX EVIL O(N^2) EVIL
2995 aState
.RecoverStateFrom(line
, deltaBCoord
);
2998 // Keep mBCoord up to date in case we're propagating reflow damage
2999 // and also because our final height may depend on it. If the
3000 // line is inlines, then only update mBCoord if the line is not
3001 // empty, because that's what PlaceLine does. (Empty blocks may
3002 // want to update mBCoord, e.g. if they have clearance.)
3003 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
3004 aState
.mBCoord
= line
->BEnd();
3007 needToRecoverState
= true;
3009 if (reflowedPrevLine
&& !line
->IsBlock() &&
3010 aState
.mPresContext
->HasPendingInterrupt()) {
3011 // Need to make sure to pull overflows from any prev-in-flows
3012 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
3013 inlineKid
= inlineKid
->PrincipalChildList().FirstChild()) {
3014 inlineKid
->PullOverflowsFromPrevInFlow();
3019 // Record if we need to clear floats before reflowing the next
3020 // line. Note that inlineFloatClearType will be handled and
3021 // cleared before the next line is processed, so there is no
3022 // need to combine break types here.
3023 if (line
->HasFloatClearTypeAfter()) {
3024 inlineFloatClearType
= line
->FloatClearTypeAfter();
3027 if (LineHasClear(line
.get())) {
3028 foundAnyClears
= true;
3031 DumpLine(aState
, line
, deltaBCoord
, -1);
3033 if (aState
.mPresContext
->HasPendingInterrupt()) {
3034 willReflowAgain
= true;
3035 // Another option here might be to leave |line| clean if
3036 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
3037 // that case the line really did reflow as it should have. Not sure
3038 // whether that would be safe, so doing this for now instead. Also not
3039 // sure whether we really want to mark all lines dirty after an
3040 // interrupt, but until we get better at propagating float damage we
3041 // really do need to do it this way; see comments inside MarkLineDirty.
3042 MarkLineDirtyForInterrupt(line
);
3046 // Handle BR-clearance from the last line of the block
3047 if (inlineFloatClearType
!= StyleClear::None
) {
3048 std::tie(aState
.mBCoord
, std::ignore
) =
3049 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
3052 if (needToRecoverState
) {
3053 // Is this expensive?
3054 aState
.ReconstructMarginBefore(line
);
3056 // Update aState.mPrevChild as if we had reflowed all of the frames in
3058 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
3059 line
.prev()->LastChild(),
3060 "unexpected line frames");
3061 aState
.mPrevChild
= line
== line_end
? mFrames
.LastChild()
3062 : line
->mFirstChild
->GetPrevSibling();
3065 // Should we really have to do this?
3066 if (repositionViews
) {
3067 nsContainerFrame::PlaceFrameView(this);
3070 // We can skip trying to pull up the next line if our height is constrained
3071 // (so we can report being incomplete) and there is no next in flow or we
3072 // were told not to or we know it will be futile, i.e.,
3073 // -- the next in flow is not changing
3074 // -- and we cannot have added more space for its first line to be
3076 // -- it's an incremental reflow of a descendant
3077 // -- and we didn't reflow any floats (so the available space
3079 // -- my chain of next-in-flows either has no first line, or its first
3080 // line isn't dirty.
3081 bool heightConstrained
=
3082 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
;
3083 bool skipPull
= willReflowAgain
&& heightConstrained
;
3084 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
3085 (aState
.mReflowInput
.mFlags
.mNextInFlowUntouched
&& !lastLineMovedUp
&&
3086 !HasAnyStateBits(NS_FRAME_IS_DIRTY
) && !reflowedFloat
)) {
3087 // We'll place lineIter at the last line of this block, so that
3088 // nsBlockInFlowLineIterator::Next() will take us to the first
3089 // line of my next-in-flow-chain. (But first, check that I
3090 // have any lines -- if I don't, just bail out of this
3092 LineIterator lineIter
= this->LinesEnd();
3093 if (lineIter
!= this->LinesBegin()) {
3094 lineIter
--; // I have lines; step back from dummy iterator to last line.
3095 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
3097 // Check for next-in-flow-chain's first line.
3098 // (First, see if there is such a line, and second, see if it's clean)
3099 if (!bifLineIter
.Next() || !bifLineIter
.GetLine()->IsDirty()) {
3105 if (skipPull
&& aState
.mNextInFlow
) {
3106 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
3107 if (aState
.mNextInFlow
->IsTrueOverflowContainer()) {
3108 aState
.mReflowStatus
.SetOverflowIncomplete();
3110 aState
.mReflowStatus
.SetIncomplete();
3114 if (!skipPull
&& aState
.mNextInFlow
) {
3115 // Pull data from a next-in-flow if there's still room for more
3117 while (keepGoing
&& aState
.mNextInFlow
) {
3118 // Grab first line from our next-in-flow
3119 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3120 nsLineBox
* pulledLine
;
3121 nsFrameList pulledFrames
;
3122 if (!nextInFlow
->mLines
.empty()) {
3123 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
, &pulledLine
,
3126 // Grab an overflow line if there are any
3127 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
3128 if (!overflowLines
) {
3129 aState
.mNextInFlow
=
3130 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3134 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
3135 &pulledLine
, &pulledFrames
);
3137 nextInFlow
->DestroyOverflowLines();
3141 if (pulledFrames
.IsEmpty()) {
3142 // The line is empty. Try the next one.
3144 pulledLine
->GetChildCount() == 0 && !pulledLine
->mFirstChild
,
3146 nextInFlow
->FreeLineBox(pulledLine
);
3150 if (nextInFlow
->MaybeHasLineCursor()) {
3151 if (pulledLine
== nextInFlow
->GetLineCursorForDisplay()) {
3152 nextInFlow
->ClearLineCursorForDisplay();
3154 if (pulledLine
== nextInFlow
->GetLineCursorForQuery()) {
3155 nextInFlow
->ClearLineCursorForQuery();
3158 ReparentFrames(pulledFrames
, nextInFlow
, this);
3159 pulledLine
->SetMovedFragments();
3161 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
3162 "Unexpected last frame");
3163 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(),
3164 "should have a prevchild here");
3165 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
3166 "Incorrect aState.mPrevChild before inserting line at end");
3168 // Shift pulledLine's frames into our mFrames list.
3169 mFrames
.AppendFrames(nullptr, std::move(pulledFrames
));
3171 // Add line to our line list, and set its last child as our new prev-child
3172 line
= mLines
.before_insert(LinesEnd(), pulledLine
);
3173 aState
.mPrevChild
= mFrames
.LastChild();
3175 // Reparent floats whose placeholders are in the line.
3176 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
3178 DumpLine(aState
, pulledLine
, deltaBCoord
, 0);
3180 AutoNoisyIndenter
indent2(gNoisyReflow
);
3183 if (aState
.mPresContext
->HasPendingInterrupt()) {
3184 MarkLineDirtyForInterrupt(line
);
3186 // Now reflow it and any lines that it makes during it's reflow
3187 // (we have to loop here because reflowing the line may cause a new
3188 // line to be created; see SplitLine's callers for examples of
3189 // when this happens).
3190 while (line
!= LinesEnd()) {
3191 ReflowLine(aState
, line
, &keepGoing
);
3193 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3196 aState
.mReflowStatus
.SetIncomplete();
3200 DumpLine(aState
, line
, deltaBCoord
, -1);
3202 if (0 == line
->GetChildCount()) {
3203 DeleteLine(aState
, line
, line_end
);
3208 if (LineHasClear(line
.get())) {
3209 foundAnyClears
= true;
3212 if (aState
.mPresContext
->CheckForInterrupt(this)) {
3213 MarkLineDirtyForInterrupt(line
);
3217 // If this is an inline frame then its time to stop
3219 aState
.AdvanceToNextLine();
3224 if (aState
.mReflowStatus
.IsIncomplete()) {
3225 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
3226 } // XXXfr shouldn't set this flag when nextinflow has no lines
3229 // Handle an odd-ball case: a list-item with no lines
3230 if (mLines
.empty() && HasOutsideMarker()) {
3231 ReflowOutput
metrics(aState
.mReflowInput
);
3232 nsIFrame
* marker
= GetOutsideMarker();
3233 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3234 ReflowOutsideMarker(
3235 marker
, aState
, metrics
,
3236 aState
.mReflowInput
.ComputedPhysicalBorderPadding().top
);
3237 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
3238 "empty ::marker frame took up space");
3240 if (!MarkerIsEmpty()) {
3241 // There are no lines so we have to fake up some y motion so that
3242 // we end up with *some* height.
3243 // (Note: if we're layout-contained, we have to be sure to leave our
3244 // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
3245 // because layout-contained frames have no baseline.)
3246 if (!aState
.mReflowInput
.mStyleDisplay
->IsContainLayout() &&
3247 metrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
3249 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3250 if (nsLayoutUtils::GetFirstLineBaseline(wm
, marker
, &ascent
)) {
3251 metrics
.SetBlockStartAscent(ascent
);
3253 metrics
.SetBlockStartAscent(metrics
.BSize(wm
));
3257 RefPtr
<nsFontMetrics
> fm
=
3258 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
3260 nscoord minAscent
= nsLayoutUtils::GetCenteredFontBaseline(
3261 fm
, aState
.mMinLineHeight
, wm
.IsLineInverted());
3262 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
3265 std::max(minAscent
, metrics
.BlockStartAscent()) +
3266 std::max(minDescent
, metrics
.BSize(wm
) - metrics
.BlockStartAscent());
3268 nscoord offset
= minAscent
- metrics
.BlockStartAscent();
3270 marker
->SetRect(marker
->GetRect() + nsPoint(0, offset
));
3275 if (LinesAreEmpty(mLines
) && ShouldHaveLineIfEmpty()) {
3276 aState
.mBCoord
+= aState
.mMinLineHeight
;
3279 if (foundAnyClears
) {
3280 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3282 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3287 VerifyOverflowSituation();
3289 IndentBy(stdout
, gNoiseIndent
- 1);
3291 printf(": done reflowing dirty lines (status=%s)\n",
3292 ToString(aState
.mReflowStatus
).c_str());
3297 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
) {
3300 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
3301 // marked the lines that need to be marked dirty based on our
3302 // vertical resize stuff. So we'll definitely reflow all those kids;
3303 // the only question is how they should behave.
3304 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
3305 // Mark all our child frames dirty so we make sure to reflow them
3307 int32_t n
= aLine
->GetChildCount();
3308 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
3309 f
= f
->GetNextSibling(), --n
) {
3310 f
->MarkSubtreeDirty();
3312 // And mark all the floats whose reflows we might be skipping dirty too.
3313 if (aLine
->HasFloats()) {
3314 for (nsIFrame
* f
: aLine
->Floats()) {
3315 f
->MarkSubtreeDirty();
3319 // Dirty all the descendant lines of block kids to handle float damage,
3320 // since our nsFloatManager will go away by the next time we're reflowing.
3321 // XXXbz Can we do something more like what PropagateFloatDamage does?
3322 // Would need to sort out the exact business with mBlockDelta for that....
3323 // This marks way too much dirty. If we ever make this better, revisit
3324 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
3325 nsBlockFrame
* bf
= do_QueryFrame(aLine
->mFirstChild
);
3327 MarkAllDescendantLinesDirty(bf
);
3332 void nsBlockFrame::DeleteLine(BlockReflowState
& aState
,
3333 nsLineList::iterator aLine
,
3334 nsLineList::iterator aLineEnd
) {
3335 MOZ_ASSERT(0 == aLine
->GetChildCount(), "can't delete !empty line");
3336 if (0 == aLine
->GetChildCount()) {
3337 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
3338 "using function more generally than designed, "
3339 "but perhaps OK now");
3340 nsLineBox
* line
= aLine
;
3341 aLine
= mLines
.erase(aLine
);
3343 // Mark the previous margin of the next line dirty since we need to
3344 // recompute its top position.
3345 if (aLine
!= aLineEnd
) {
3346 aLine
->MarkPreviousMarginDirty();
3352 * Reflow a line. The line will either contain a single block frame
3353 * or contain 1 or more inline frames. aKeepReflowGoing indicates
3354 * whether or not the caller should continue to reflow more lines.
3356 void nsBlockFrame::ReflowLine(BlockReflowState
& aState
, LineIterator aLine
,
3357 bool* aKeepReflowGoing
) {
3358 MOZ_ASSERT(aLine
->GetChildCount(), "reflowing empty line");
3360 // Setup the line-layout for the new line
3361 aState
.mCurrentLine
= aLine
;
3362 aLine
->ClearDirty();
3363 aLine
->InvalidateCachedIsEmpty();
3364 aLine
->ClearHadFloatPushed();
3366 // If this line contains a single block that is hidden by `content-visibility`
3367 // don't reflow the line. If this line contains inlines and the first one is
3368 // hidden by `content-visibility`, all of them are, so avoid reflow in that
3370 nsIFrame
* firstChild
= aLine
->mFirstChild
;
3371 if (firstChild
->IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
3375 // Now that we know what kind of line we have, reflow it
3376 if (aLine
->IsBlock()) {
3377 ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
3379 aLine
->SetLineWrapped(false);
3380 ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
3382 // Store the line's float edges for overflow marker analysis if needed.
3383 aLine
->ClearFloatEdges();
3384 if (aState
.mFlags
.mCanHaveOverflowMarkers
) {
3385 WritingMode wm
= aLine
->mWritingMode
;
3386 nsFlowAreaRect r
= aState
.GetFloatAvailableSpaceForBSize(
3387 aLine
->BStart(), aLine
->BSize(), nullptr);
3388 if (r
.HasFloats()) {
3389 LogicalRect so
= aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
,
3390 aLine
->mContainerSize
);
3391 nscoord s
= r
.mRect
.IStart(wm
);
3392 nscoord e
= r
.mRect
.IEnd(wm
);
3393 if (so
.IEnd(wm
) > e
|| so
.IStart(wm
) < s
) {
3394 // This line is overlapping a float - store the edges marking the area
3395 // between the floats for text-overflow analysis.
3396 aLine
->SetFloatEdges(s
, e
);
3402 aLine
->ClearMovedFragments();
3405 nsIFrame
* nsBlockFrame::PullFrame(BlockReflowState
& aState
,
3406 LineIterator aLine
) {
3407 // First check our remaining lines.
3408 if (LinesEnd() != aLine
.next()) {
3409 return PullFrameFrom(aLine
, this, aLine
.next());
3413 !GetOverflowLines(),
3414 "Our overflow lines should have been removed at the start of reflow");
3416 // Try each next-in-flow.
3417 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3418 while (nextInFlow
) {
3419 if (nextInFlow
->mLines
.empty()) {
3420 nextInFlow
->DrainSelfOverflowList();
3422 if (!nextInFlow
->mLines
.empty()) {
3423 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
3425 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3426 aState
.mNextInFlow
= nextInFlow
;
3432 nsIFrame
* nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
3433 nsBlockFrame
* aFromContainer
,
3434 nsLineList::iterator aFromLine
) {
3435 nsLineBox
* fromLine
= aFromLine
;
3436 MOZ_ASSERT(fromLine
, "bad line to pull from");
3437 MOZ_ASSERT(fromLine
->GetChildCount(), "empty line");
3438 MOZ_ASSERT(aLine
->GetChildCount(), "empty line");
3440 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
3441 "Disagreement about whether it's a block or not");
3443 if (fromLine
->IsBlock()) {
3444 // If our line is not empty and the child in aFromLine is a block
3445 // then we cannot pull up the frame into this line. In this case
3449 // Take frame from fromLine
3450 nsIFrame
* frame
= fromLine
->mFirstChild
;
3451 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
3453 if (aFromContainer
!= this) {
3454 // The frame is being pulled from a next-in-flow; therefore we
3455 // need to add it to our sibling list.
3456 MOZ_ASSERT(aLine
== mLines
.back());
3457 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
3458 "should only pull from first line");
3459 aFromContainer
->mFrames
.RemoveFrame(frame
);
3461 // When pushing and pulling frames we need to check for whether any
3462 // views need to be reparented.
3463 ReparentFrame(frame
, aFromContainer
, this);
3464 mFrames
.AppendFrame(nullptr, frame
);
3466 // The frame might have (or contain) floats that need to be brought
3467 // over too. (pass 'false' since there are no siblings to check)
3468 ReparentFloats(frame
, aFromContainer
, false);
3470 MOZ_ASSERT(aLine
== aFromLine
.prev());
3473 aLine
->NoteFrameAdded(frame
);
3474 fromLine
->NoteFrameRemoved(frame
);
3476 if (fromLine
->GetChildCount() > 0) {
3477 // Mark line dirty now that we pulled a child
3478 fromLine
->MarkDirty();
3479 fromLine
->mFirstChild
= newFirstChild
;
3481 // Free up the fromLine now that it's empty.
3482 // Its bounds might need to be redrawn, though.
3483 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
3484 aFromLine
.next()->MarkPreviousMarginDirty();
3486 aFromContainer
->mLines
.erase(aFromLine
);
3487 // aFromLine is now invalid
3488 aFromContainer
->FreeLineBox(fromLine
);
3493 VerifyOverflowSituation();
3499 void nsBlockFrame::SlideLine(BlockReflowState
& aState
, nsLineBox
* aLine
,
3500 nscoord aDeltaBCoord
) {
3501 MOZ_ASSERT(aDeltaBCoord
!= 0, "why slide a line nowhere?");
3503 // Adjust line state
3504 aLine
->SlideBy(aDeltaBCoord
, aState
.ContainerSize());
3506 // Adjust the frames in the line
3507 MoveChildFramesOfLine(aLine
, aDeltaBCoord
);
3510 void nsBlockFrame::UpdateLineContainerSize(nsLineBox
* aLine
,
3511 const nsSize
& aNewContainerSize
) {
3512 if (aNewContainerSize
== aLine
->mContainerSize
) {
3516 // Adjust line state
3517 nsSize sizeDelta
= aLine
->UpdateContainerSize(aNewContainerSize
);
3519 // Changing container width only matters if writing mode is vertical-rl
3520 if (GetWritingMode().IsVerticalRL()) {
3521 MoveChildFramesOfLine(aLine
, sizeDelta
.width
);
3525 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox
* aLine
,
3526 nscoord aDeltaBCoord
) {
3527 // Adjust the frames in the line
3528 nsIFrame
* kid
= aLine
->mFirstChild
;
3533 WritingMode wm
= GetWritingMode();
3534 LogicalPoint
translation(wm
, 0, aDeltaBCoord
);
3536 if (aLine
->IsBlock()) {
3538 kid
->MovePositionBy(wm
, translation
);
3541 // Make sure the frame's view and any child views are updated
3542 nsContainerFrame::PlaceFrameView(kid
);
3544 // Adjust the block-dir coordinate of the frames in the line.
3545 // Note: we need to re-position views even if aDeltaBCoord is 0, because
3546 // one of our parent frames may have moved and so the view's position
3547 // relative to its parent may have changed.
3548 int32_t n
= aLine
->GetChildCount();
3551 kid
->MovePositionBy(wm
, translation
);
3553 // Make sure the frame's view and any child views are updated
3554 nsContainerFrame::PlaceFrameView(kid
);
3555 kid
= kid
->GetNextSibling();
3560 static inline bool IsNonAutoNonZeroBSize(const StyleSize
& aCoord
) {
3561 // The "extremum length" values (see ExtremumLength) were originally aimed at
3562 // inline-size (or width, as it was before logicalization). For now, let them
3563 // return false here, so we treat them like 'auto' pending a real
3564 // implementation. (See bug 1126420.)
3566 // FIXME (bug 567039, bug 527285) This isn't correct for the 'fill' value,
3567 // which should more likely (but not necessarily, depending on the available
3568 // space) be returning true.
3569 if (aCoord
.BehavesLikeInitialValueOnBlockAxis()) {
3572 MOZ_ASSERT(aCoord
.IsLengthPercentage());
3573 // If we evaluate the length/percent/calc at a percentage basis of
3574 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3575 // length, percent, or combination thereof. Test > 0 so we clamp
3576 // negative calc() results to 0.
3577 return aCoord
.AsLengthPercentage().Resolve(nscoord_MAX
) > 0 ||
3578 aCoord
.AsLengthPercentage().Resolve(0) > 0;
3582 bool nsBlockFrame::IsSelfEmpty() {
3583 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
3587 // Blocks which are margin-roots (including inline-blocks) cannot be treated
3588 // as empty for margin-collapsing and other purposes. They're more like
3589 // replaced elements.
3590 if (HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
3594 WritingMode wm
= GetWritingMode();
3595 const nsStylePosition
* position
= StylePosition();
3597 if (IsNonAutoNonZeroBSize(position
->MinBSize(wm
)) ||
3598 IsNonAutoNonZeroBSize(position
->BSize(wm
))) {
3602 // FIXME: Bug 1646100 - Take intrinsic size into account.
3603 // FIXME: Handle the case that both inline and block sizes are auto.
3604 // https://github.com/w3c/csswg-drafts/issues/5060.
3605 // Note: block-size could be zero or auto/intrinsic keywords here.
3606 if (position
->BSize(wm
).BehavesLikeInitialValueOnBlockAxis() &&
3607 position
->mAspectRatio
.HasFiniteRatio()) {
3611 const nsStyleBorder
* border
= StyleBorder();
3612 const nsStylePadding
* padding
= StylePadding();
3614 if (border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBStart
)) !=
3616 border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBEnd
)) != 0 ||
3617 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBStart(wm
)) ||
3618 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBEnd(wm
))) {
3622 if (HasOutsideMarker() && !MarkerIsEmpty()) {
3629 bool nsBlockFrame::CachedIsEmpty() {
3630 if (!IsSelfEmpty()) {
3633 for (auto& line
: mLines
) {
3634 if (!line
.CachedIsEmpty()) {
3641 bool nsBlockFrame::IsEmpty() {
3642 if (!IsSelfEmpty()) {
3646 return LinesAreEmpty(mLines
);
3649 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState
& aState
,
3651 if (aLine
->mFirstChild
->IsPageBreakFrame()) {
3652 // A page break frame consumes margins adjacent to it.
3653 // https://drafts.csswg.org/css-break/#break-margins
3657 if (aState
.mFlags
.mShouldApplyBStartMargin
) {
3658 // Apply short-circuit check to avoid searching the line list
3662 if (!aState
.IsAdjacentWithBStart()) {
3663 // If we aren't at the start block-coordinate then something of non-zero
3664 // height must have been placed. Therefore the childs block-start margin
3666 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3670 // Determine if this line is "essentially" the first line
3671 LineIterator line
= LinesBegin();
3672 if (aState
.mFlags
.mHasLineAdjacentToTop
) {
3673 line
= aState
.mLineAdjacentToTop
;
3675 while (line
!= aLine
) {
3676 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
3677 // A line which precedes aLine is non-empty, or has clearance,
3678 // so therefore the block-start margin applies.
3679 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3682 // No need to apply the block-start margin if the line has floats. We
3683 // should collapse anyway (bug 44419)
3685 aState
.mFlags
.mHasLineAdjacentToTop
= true;
3686 aState
.mLineAdjacentToTop
= line
;
3689 // The line being reflowed is "essentially" the first line in the
3690 // block. Therefore its block-start margin will be collapsed by the
3691 // generational collapsing logic with its parent (us).
3695 void nsBlockFrame::ReflowBlockFrame(BlockReflowState
& aState
,
3697 bool* aKeepReflowGoing
) {
3698 MOZ_ASSERT(*aKeepReflowGoing
, "bad caller");
3700 nsIFrame
* frame
= aLine
->mFirstChild
;
3702 NS_ASSERTION(false, "program error - unexpected empty line");
3706 // Prepare the block reflow engine
3707 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
3709 StyleClear clearType
= frame
->StyleDisplay()->mClear
;
3710 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
3711 clearType
= nsLayoutUtils::CombineClearType(clearType
,
3712 aState
.mTrailingClearFromPIF
);
3713 aState
.mTrailingClearFromPIF
= StyleClear::None
;
3716 // Clear past floats before the block if the clear style is not none
3717 aLine
->ClearForcedLineBreak();
3718 if (clearType
!= StyleClear::None
) {
3719 aLine
->SetForcedLineBreakBefore(clearType
);
3722 // See if we should apply the block-start margin. If the block frame being
3723 // reflowed is a continuation, then we don't apply its block-start margin
3724 // because it's not significant. Otherwise, dig deeper.
3725 bool applyBStartMargin
=
3726 !frame
->GetPrevContinuation() && ShouldApplyBStartMargin(aState
, aLine
);
3727 if (applyBStartMargin
) {
3728 // The HasClearance setting is only valid if ShouldApplyBStartMargin
3729 // returned false (in which case the block-start margin-root set our
3730 // clearance flag). Otherwise clear it now. We'll set it later on
3731 // ourselves if necessary.
3732 aLine
->ClearHasClearance();
3734 bool treatWithClearance
= aLine
->HasClearance();
3736 bool mightClearFloats
= clearType
!= StyleClear::None
;
3737 nsIFrame
* floatAvoidingBlock
= nullptr;
3738 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
3739 mightClearFloats
= true;
3740 floatAvoidingBlock
= frame
;
3743 // If our block-start margin was counted as part of some parent's block-start
3744 // margin collapse, and we are being speculatively reflowed assuming this
3745 // frame DID NOT need clearance, then we need to check that
3747 if (!treatWithClearance
&& !applyBStartMargin
&& mightClearFloats
&&
3748 aState
.mReflowInput
.mDiscoveredClearance
) {
3749 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
3750 if (auto [clearBCoord
, result
] =
3751 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
3752 result
!= ClearFloatsResult::BCoordNoChange
) {
3753 Unused
<< clearBCoord
;
3755 // Only record the first frame that requires clearance
3756 if (!*aState
.mReflowInput
.mDiscoveredClearance
) {
3757 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
3759 aState
.mPrevChild
= frame
;
3760 // Exactly what we do now is flexible since we'll definitely be
3765 if (treatWithClearance
) {
3766 applyBStartMargin
= true;
3769 nsIFrame
* clearanceFrame
= nullptr;
3770 const nscoord startingBCoord
= aState
.mBCoord
;
3771 const nsCollapsingMargin incomingMargin
= aState
.mPrevBEndMargin
;
3773 // Save the original position of the frame so that we can reposition
3774 // its view as needed.
3775 nsPoint originalPosition
= frame
->GetPosition();
3778 nscoord bStartMargin
= 0;
3779 bool mayNeedRetry
= false;
3780 bool clearedFloats
= false;
3781 bool clearedPushedOrSplitFloat
= false;
3782 if (applyBStartMargin
) {
3783 // Precompute the blocks block-start margin value so that we can get the
3784 // correct available space (there might be a float that's
3785 // already been placed below the aState.mPrevBEndMargin
3787 // Setup a reflowInput to get the style computed block-start margin
3788 // value. We'll use a reason of `resize' so that we don't fudge
3789 // any incremental reflow input.
3791 // The availSpace here is irrelevant to our needs - all we want
3792 // out if this setup is the block-start margin value which doesn't depend
3793 // on the childs available space.
3794 // XXX building a complete ReflowInput just to get the block-start
3795 // margin seems like a waste. And we do this for almost every block!
3796 WritingMode wm
= frame
->GetWritingMode();
3797 LogicalSize availSpace
= aState
.ContentSize(wm
);
3798 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, frame
,
3801 if (treatWithClearance
) {
3802 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
3803 aState
.mPrevBEndMargin
.Zero();
3806 // Now compute the collapsed margin-block-start value into
3807 // aState.mPrevBEndMargin, assuming that all child margins
3808 // collapse down to clearanceFrame.
3809 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
3810 clearanceFrame
, &mayNeedRetry
);
3812 // XXX optimization; we could check the collapsing children to see if they
3813 // are sure to require clearance, and so avoid retrying them
3815 if (clearanceFrame
) {
3816 // Don't allow retries on the second pass. The clearance decisions for
3817 // the blocks whose block-start margins collapse with ours are now
3819 mayNeedRetry
= false;
3822 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
3823 // We don't know if we need clearance and this is the first,
3824 // optimistic pass. So determine whether *this block* needs
3825 // clearance. Note that we do not allow the decision for whether
3826 // this block has clearance to change on the second pass; that
3827 // decision is only allowed to be made under the optimistic
3829 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
3830 if (auto [clearBCoord
, result
] =
3831 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
3832 result
!= ClearFloatsResult::BCoordNoChange
) {
3833 Unused
<< clearBCoord
;
3835 // Looks like we need clearance and we didn't know about it already.
3836 // So recompute collapsed margin
3837 treatWithClearance
= true;
3838 // Remember this decision, needed for incremental reflow
3839 aLine
->SetHasClearance();
3841 // Apply incoming margins
3842 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
3843 aState
.mPrevBEndMargin
.Zero();
3845 // Compute the collapsed margin again, ignoring the incoming margin
3847 mayNeedRetry
= false;
3848 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
3849 clearanceFrame
, &mayNeedRetry
);
3853 // Temporarily advance the running block-direction value so that the
3854 // GetFloatAvailableSpace method will return the right available space.
3855 // This undone as soon as the horizontal margins are computed.
3856 bStartMargin
= aState
.mPrevBEndMargin
.get();
3858 if (treatWithClearance
) {
3859 nscoord currentBCoord
= aState
.mBCoord
;
3860 // advance mBCoord to the clear position.
3861 auto [clearBCoord
, result
] =
3862 aState
.ClearFloats(aState
.mBCoord
, clearType
, floatAvoidingBlock
);
3863 aState
.mBCoord
= clearBCoord
;
3865 clearedFloats
= result
!= ClearFloatsResult::BCoordNoChange
;
3866 clearedPushedOrSplitFloat
=
3867 result
== ClearFloatsResult::FloatsPushedOrSplit
;
3869 // Compute clearance. It's the amount we need to add to the block-start
3870 // border-edge of the frame, after applying collapsed margins
3871 // from the frame and its children, to get it to line up with
3872 // the block-end of the floats. The former is
3873 // currentBCoord + bStartMargin, the latter is the current
3875 // Note that negative clearance is possible
3876 clearance
= aState
.mBCoord
- (currentBCoord
+ bStartMargin
);
3878 // Add clearance to our block-start margin while we compute available
3879 // space for the frame
3880 bStartMargin
+= clearance
;
3882 // Note that aState.mBCoord should stay where it is: at the block-start
3883 // border-edge of the frame
3885 // Advance aState.mBCoord to the block-start border-edge of the frame.
3886 aState
.mBCoord
+= bStartMargin
;
3890 aLine
->SetLineIsImpactedByFloat(false);
3892 // Here aState.mBCoord is the block-start border-edge of the block.
3893 // Compute the available space for the block
3894 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
3895 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3896 LogicalRect availSpace
= aState
.ComputeBlockAvailSpace(
3897 frame
, floatAvailableSpace
, (floatAvoidingBlock
));
3900 // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
3901 // is to some degree out of paranoia: if we reliably eat up block-start
3902 // margins at the top of the page as we ought to, it wouldn't be
3904 if ((!aState
.mReflowInput
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
3905 (availSpace
.BSize(wm
) < 0 || clearedPushedOrSplitFloat
)) {
3906 // We know already that this child block won't fit on this
3907 // page/column due to the block-start margin or the clearance. So we
3908 // need to get out of here now. (If we don't, most blocks will handle
3909 // things fine, and report break-before, but zero-height blocks
3910 // won't, and will thus make their parent overly-large and force
3911 // *it* to be pushed in its entirety.)
3912 aState
.mBCoord
= startingBCoord
;
3913 aState
.mPrevBEndMargin
= incomingMargin
;
3914 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
3915 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
3917 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
3922 // Now put the block-dir coordinate back to the start of the
3923 // block-start-margin + clearance.
3924 aState
.mBCoord
-= bStartMargin
;
3925 availSpace
.BStart(wm
) -= bStartMargin
;
3926 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.BSize(wm
)) {
3927 availSpace
.BSize(wm
) += bStartMargin
;
3930 // Construct the reflow input for the block.
3931 Maybe
<ReflowInput
> childReflowInput
;
3932 Maybe
<LogicalSize
> cbSize
;
3933 LogicalSize availSize
= availSpace
.Size(wm
);
3934 bool columnSetWrapperHasNoBSizeLeft
= false;
3935 if (Style()->GetPseudoType() == PseudoStyleType::columnContent
) {
3936 // Calculate the multicol containing block's block size so that the
3937 // children with percentage block size get correct percentage basis.
3938 const ReflowInput
* cbReflowInput
=
3939 aState
.mReflowInput
.mParentReflowInput
->mCBReflowInput
;
3940 MOZ_ASSERT(cbReflowInput
->mFrame
->StyleColumn()->IsColumnContainerStyle(),
3941 "Get unexpected reflow input of multicol containing block!");
3943 // Use column-width as the containing block's inline-size, i.e. the column
3944 // content's computed inline-size.
3945 cbSize
.emplace(LogicalSize(wm
, aState
.mReflowInput
.ComputedISize(),
3946 cbReflowInput
->ComputedBSize())
3947 .ConvertTo(frame
->GetWritingMode(), wm
));
3949 // If a ColumnSetWrapper is in a balancing column content, it may be
3950 // pushed or pulled back and forth between column contents. Always add
3951 // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
3952 // can have a chance to reflow under current block size constraint.
3953 if (aState
.mReflowInput
.mFlags
.mIsColumnBalancing
&&
3954 frame
->IsColumnSetWrapperFrame()) {
3955 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
3957 } else if (IsColumnSetWrapperFrame()) {
3958 // If we are reflowing our ColumnSet children, we want to apply our block
3959 // size constraint to the available block size when constructing reflow
3960 // input for ColumnSet so that ColumnSet can use it to compute its max
3961 // column block size.
3962 if (frame
->IsColumnSetFrame()) {
3963 nscoord contentBSize
= aState
.mReflowInput
.ComputedBSize();
3964 if (aState
.mReflowInput
.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE
) {
3966 std::min(contentBSize
, aState
.mReflowInput
.ComputedMaxBSize());
3968 if (contentBSize
!= NS_UNCONSTRAINEDSIZE
) {
3969 // To get the remaining content block-size, subtract the content
3970 // block-size consumed by our previous continuations.
3971 contentBSize
-= aState
.mConsumedBSize
;
3973 // ColumnSet is not the outermost frame in the column container, so it
3974 // cannot have any margin. We don't need to consider any margin that
3975 // can be generated by "box-decoration-break: clone" as we do in
3976 // BlockReflowState::ComputeBlockAvailSpace().
3977 const nscoord availContentBSize
= std::max(
3978 0, contentBSize
- (aState
.mBCoord
- aState
.ContentBStart()));
3979 if (availSize
.BSize(wm
) >= availContentBSize
) {
3980 availSize
.BSize(wm
) = availContentBSize
;
3981 columnSetWrapperHasNoBSizeLeft
= true;
3987 childReflowInput
.emplace(aState
.mPresContext
, aState
.mReflowInput
, frame
,
3988 availSize
.ConvertTo(frame
->GetWritingMode(), wm
),
3991 childReflowInput
->mFlags
.mColumnSetWrapperHasNoBSizeLeft
=
3992 columnSetWrapperHasNoBSizeLeft
;
3994 if (aLine
->MovedFragments()) {
3995 // We only need to set this the first reflow, since if we reflow
3996 // again (and replace childReflowInput) we'll be reflowing it
3997 // again in the same fragment as the previous time.
3998 childReflowInput
->mFlags
.mMovedBlockFragments
= true;
4001 nsFloatManager::SavedState floatManagerState
;
4002 nsReflowStatus frameReflowStatus
;
4004 if (floatAvailableSpace
.HasFloats()) {
4005 // Set if floatAvailableSpace.HasFloats() is true for any
4006 // iteration of the loop.
4007 aLine
->SetLineIsImpactedByFloat(true);
4010 // We might need to store into mDiscoveredClearance later if it's
4011 // currently null; we want to overwrite any writes that
4012 // brc.ReflowBlock() below does, so we need to remember now
4013 // whether it's empty.
4014 const bool shouldStoreClearance
=
4015 aState
.mReflowInput
.mDiscoveredClearance
&&
4016 !*aState
.mReflowInput
.mDiscoveredClearance
;
4018 // Reflow the block into the available space
4019 if (mayNeedRetry
|| floatAvoidingBlock
) {
4020 aState
.FloatManager()->PushState(&floatManagerState
);
4024 childReflowInput
->mDiscoveredClearance
= &clearanceFrame
;
4025 } else if (!applyBStartMargin
) {
4026 childReflowInput
->mDiscoveredClearance
=
4027 aState
.mReflowInput
.mDiscoveredClearance
;
4030 frameReflowStatus
.Reset();
4031 brc
.ReflowBlock(availSpace
, applyBStartMargin
, aState
.mPrevBEndMargin
,
4032 clearance
, aLine
.get(), *childReflowInput
,
4033 frameReflowStatus
, aState
);
4035 if (frameReflowStatus
.IsInlineBreakBefore()) {
4036 // No need to retry this loop if there is a break opportunity before the
4041 // Now the block has a height. Using that height, get the
4042 // available space again and call ComputeBlockAvailSpace again.
4043 // If ComputeBlockAvailSpace gives a different result, we need to
4045 if (!floatAvoidingBlock
) {
4049 LogicalRect
oldFloatAvailableSpaceRect(floatAvailableSpace
.mRect
);
4050 floatAvailableSpace
= aState
.GetFloatAvailableSpaceForBSize(
4051 aState
.mBCoord
+ bStartMargin
, brc
.GetMetrics().BSize(wm
),
4052 &floatManagerState
);
4053 NS_ASSERTION(floatAvailableSpace
.mRect
.BStart(wm
) ==
4054 oldFloatAvailableSpaceRect
.BStart(wm
),
4056 // Restore the height to the position of the next band.
4057 floatAvailableSpace
.mRect
.BSize(wm
) =
4058 oldFloatAvailableSpaceRect
.BSize(wm
);
4059 // Determine whether the available space shrunk on either side,
4060 // because (the first time round) we now know the block's height,
4061 // and it may intersect additional floats, or (on later
4062 // iterations) because narrowing the width relative to the
4063 // previous time may cause the block to become taller. Note that
4064 // since we're reflowing the block, narrowing the width might also
4065 // make it shorter, so we must pass aCanGrow as true.
4066 if (!AvailableSpaceShrunk(wm
, oldFloatAvailableSpaceRect
,
4067 floatAvailableSpace
.mRect
, true)) {
4068 // The size and position we chose before are fine (i.e., they
4069 // don't cause intersecting with floats that requires a change
4070 // in size or position), so we're done.
4074 bool advanced
= false;
4075 if (!aState
.FloatAvoidingBlockFitsInAvailSpace(floatAvoidingBlock
,
4076 floatAvailableSpace
)) {
4077 // Advance to the next band.
4078 nscoord newBCoord
= aState
.mBCoord
;
4079 if (aState
.AdvanceToNextBand(floatAvailableSpace
.mRect
, &newBCoord
)) {
4082 // ClearFloats might be able to advance us further once we're there.
4083 std::tie(aState
.mBCoord
, std::ignore
) =
4084 aState
.ClearFloats(newBCoord
, StyleClear::None
, floatAvoidingBlock
);
4086 // Start over with a new available space rect at the new height.
4087 floatAvailableSpace
= aState
.GetFloatAvailableSpaceWithState(
4088 aState
.mBCoord
, ShapeType::ShapeOutside
, &floatManagerState
);
4091 const LogicalRect oldAvailSpace
= availSpace
;
4092 availSpace
= aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
4093 (floatAvoidingBlock
));
4095 if (!advanced
&& availSpace
.IsEqualEdges(oldAvailSpace
)) {
4099 // We need another reflow.
4100 aState
.FloatManager()->PopState(&floatManagerState
);
4102 if (!treatWithClearance
&& !applyBStartMargin
&&
4103 aState
.mReflowInput
.mDiscoveredClearance
) {
4104 // We set shouldStoreClearance above to record only the first
4105 // frame that requires clearance.
4106 if (shouldStoreClearance
) {
4107 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4109 aState
.mPrevChild
= frame
;
4110 // Exactly what we do now is flexible since we'll definitely be
4116 // We're pushing down the border-box, so we don't apply margin anymore.
4117 // This should never cause us to move up since the call to
4118 // GetFloatAvailableSpaceForBSize above included the margin.
4119 applyBStartMargin
= false;
4121 treatWithClearance
= true; // avoid hitting test above
4125 childReflowInput
.reset();
4126 childReflowInput
.emplace(
4127 aState
.mPresContext
, aState
.mReflowInput
, frame
,
4128 availSpace
.Size(wm
).ConvertTo(frame
->GetWritingMode(), wm
));
4131 if (mayNeedRetry
&& clearanceFrame
) {
4132 // Found a clearance frame, so we need to reflow |frame| a second time.
4133 // Restore the states and start over again.
4134 aState
.FloatManager()->PopState(&floatManagerState
);
4135 aState
.mBCoord
= startingBCoord
;
4136 aState
.mPrevBEndMargin
= incomingMargin
;
4140 aState
.mPrevChild
= frame
;
4142 if (childReflowInput
->WillReflowAgainForClearance()) {
4143 // If an ancestor of ours is going to reflow for clearance, we
4144 // need to avoid calling PlaceBlock, because it unsets dirty bits
4145 // on the child block (both itself, and through its call to
4146 // nsIFrame::DidReflow), and those dirty bits imply dirtiness for
4147 // all of the child block, including the lines it didn't reflow.
4148 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
4149 "we need to call PositionChildViews");
4153 #if defined(REFLOW_STATUS_COVERAGE)
4154 RecordReflowStatus(true, frameReflowStatus
);
4157 if (frameReflowStatus
.IsInlineBreakBefore()) {
4158 // None of the child block fits.
4159 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4160 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4162 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4165 // Note: line-break-after a block is a nop
4167 // Try to place the child block.
4168 // Don't force the block to fit if we have positive clearance, because
4169 // pushing it to the next page would give it more room.
4170 // Don't force the block to fit if it's impacted by a float. If it is,
4171 // then pushing it to the next page would give it more room. Note that
4172 // isImpacted doesn't include impact from the block's own floats.
4173 bool forceFit
= aState
.IsAdjacentWithBStart() && clearance
<= 0 &&
4174 !floatAvailableSpace
.HasFloats();
4175 nsCollapsingMargin collapsedBEndMargin
;
4176 OverflowAreas overflowAreas
;
4178 brc
.PlaceBlock(*childReflowInput
, forceFit
, aLine
.get(),
4179 collapsedBEndMargin
, overflowAreas
, frameReflowStatus
);
4180 if (!frameReflowStatus
.IsFullyComplete() &&
4181 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4182 *aKeepReflowGoing
= false;
4186 if (aLine
->SetCarriedOutBEndMargin(collapsedBEndMargin
)) {
4187 LineIterator nextLine
= aLine
;
4189 if (nextLine
!= LinesEnd()) {
4190 nextLine
->MarkPreviousMarginDirty();
4194 aLine
->SetOverflowAreas(overflowAreas
);
4195 if (*aKeepReflowGoing
) {
4196 // Some of the child block fit
4198 // Advance to new Y position
4199 nscoord newBCoord
= aLine
->BEnd();
4200 aState
.mBCoord
= newBCoord
;
4202 // Continue the block frame now if it didn't completely fit in
4203 // the available space.
4204 if (!frameReflowStatus
.IsFullyComplete()) {
4205 bool madeContinuation
= CreateContinuationFor(aState
, nullptr, frame
);
4207 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
4208 NS_ASSERTION(nextFrame
,
4209 "We're supposed to have a next-in-flow by now");
4211 if (frameReflowStatus
.IsIncomplete()) {
4212 // If nextFrame used to be an overflow container, make it a normal
4214 if (!madeContinuation
&&
4215 nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4216 nsOverflowContinuationTracker::AutoFinish
fini(
4217 aState
.mOverflowTracker
, frame
);
4218 nsContainerFrame
* parent
= nextFrame
->GetParent();
4219 parent
->StealFrame(nextFrame
);
4220 if (parent
!= this) {
4221 ReparentFrame(nextFrame
, parent
, this);
4223 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
4224 madeContinuation
= true; // needs to be added to mLines
4225 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4226 frameReflowStatus
.SetNextInFlowNeedsReflow();
4229 // Push continuation to a new line, but only if we actually made
4231 if (madeContinuation
) {
4232 nsLineBox
* line
= NewLineBox(nextFrame
, true);
4233 mLines
.after_insert(aLine
, line
);
4236 PushTruncatedLine(aState
, aLine
.next(), aKeepReflowGoing
);
4238 // If we need to reflow the continuation of the block child,
4239 // then we'd better reflow our continuation
4240 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4241 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4242 // We also need to make that continuation's line dirty so
4243 // it gets reflowed when we reflow our next in flow. The
4244 // nif's line must always be either a line of the nif's
4245 // parent block (only if we didn't make a continuation) or
4246 // else one of our own overflow lines. In the latter case
4247 // the line is already marked dirty, so just handle the
4249 if (!madeContinuation
) {
4250 nsBlockFrame
* nifBlock
= do_QueryFrame(nextFrame
->GetParent());
4253 "A block's child's next in flow's parent must be a block!");
4254 for (auto& line
: nifBlock
->Lines()) {
4255 if (line
.Contains(nextFrame
)) {
4263 // The block-end margin for a block is only applied on the last
4264 // flow block. Since we just continued the child block frame,
4265 // we know that line->mFirstChild is not the last flow block
4266 // therefore zero out the running margin value.
4267 #ifdef NOISY_BLOCK_DIR_MARGINS
4269 printf(": reflow incomplete, frame=");
4270 frame
->ListTag(stdout
);
4271 printf(" prevBEndMargin=%d, setting to zero\n",
4272 aState
.mPrevBEndMargin
.get());
4274 aState
.mPrevBEndMargin
.Zero();
4275 } else { // frame is complete but its overflow is not complete
4276 // Disconnect the next-in-flow and put it in our overflow tracker
4277 if (!madeContinuation
&&
4278 !nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4279 // It already exists, but as a normal next-in-flow, so we need
4280 // to dig it out of the child lists.
4281 nextFrame
->GetParent()->StealFrame(nextFrame
);
4282 } else if (madeContinuation
) {
4283 mFrames
.RemoveFrame(nextFrame
);
4286 // Put it in our overflow list
4287 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
4288 aState
.mReflowStatus
.MergeCompletionStatusFrom(frameReflowStatus
);
4290 #ifdef NOISY_BLOCK_DIR_MARGINS
4292 printf(": reflow complete but overflow incomplete for ");
4293 frame
->ListTag(stdout
);
4294 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4295 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4297 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4299 } else { // frame is fully complete
4300 #ifdef NOISY_BLOCK_DIR_MARGINS
4302 printf(": reflow complete for ");
4303 frame
->ListTag(stdout
);
4304 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4305 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4307 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4309 #ifdef NOISY_BLOCK_DIR_MARGINS
4312 frame
->ListTag(stdout
);
4313 printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
4314 brc
.GetCarriedOutBEndMargin().get(), collapsedBEndMargin
.get(),
4315 aState
.mPrevBEndMargin
.get());
4318 if (!frameReflowStatus
.IsFullyComplete()) {
4319 // The frame reported an incomplete status, but then it also didn't
4320 // fit. This means we need to reflow it again so that it can
4321 // (again) report the incomplete status.
4322 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4325 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
4326 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4327 // If it's our very first line *or* we're not at the top of the page
4328 // and we have page-break-inside:avoid, then we need to be pushed to
4329 // our parent's next-in-flow.
4330 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4332 // Push the line that didn't fit and any lines that follow it
4333 // to our next-in-flow.
4334 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4338 break; // out of the reflow retry loop
4341 // Now that we've got its final position all figured out, position any child
4342 // views it may have. Note that the case when frame has a view got handled
4343 // by FinishReflowChild, but that function didn't have the coordinates needed
4344 // to correctly decide whether to reposition child views.
4345 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
4346 nsContainerFrame::PositionChildViews(frame
);
4354 void nsBlockFrame::ReflowInlineFrames(BlockReflowState
& aState
,
4356 bool* aKeepReflowGoing
) {
4357 *aKeepReflowGoing
= true;
4359 aLine
->SetLineIsImpactedByFloat(false);
4361 // Setup initial coordinate system for reflowing the inline frames
4362 // into. Apply a previous block frame's block-end margin first.
4363 if (ShouldApplyBStartMargin(aState
, aLine
)) {
4364 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4366 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4368 LineReflowStatus lineReflowStatus
;
4370 nscoord availableSpaceBSize
= 0;
4371 aState
.mLineBSize
.reset();
4373 bool allowPullUp
= true;
4374 nsIFrame
* forceBreakInFrame
= nullptr;
4375 int32_t forceBreakOffset
= -1;
4376 gfxBreakPriority forceBreakPriority
= gfxBreakPriority::eNoBreak
;
4378 nsFloatManager::SavedState floatManagerState
;
4379 aState
.FloatManager()->PushState(&floatManagerState
);
4381 // Once upon a time we allocated the first 30 nsLineLayout objects
4382 // on the stack, and then we switched to the heap. At that time
4383 // these objects were large (1100 bytes on a 32 bit system).
4384 // Then the nsLineLayout object was shrunk to 156 bytes by
4385 // removing some internal buffers. Given that it is so much
4386 // smaller, the complexity of 2 different ways of allocating
4387 // no longer makes sense. Now we always allocate on the stack.
4388 nsLineLayout
lineLayout(aState
.mPresContext
, aState
.FloatManager(),
4389 aState
.mReflowInput
, &aLine
, nullptr);
4390 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
4391 if (forceBreakInFrame
) {
4392 lineLayout
.ForceBreakAtPosition(forceBreakInFrame
, forceBreakOffset
);
4394 DoReflowInlineFrames(aState
, lineLayout
, aLine
, floatAvailableSpace
,
4395 availableSpaceBSize
, &floatManagerState
,
4396 aKeepReflowGoing
, &lineReflowStatus
, allowPullUp
);
4397 lineLayout
.EndLineReflow();
4399 if (LineReflowStatus::RedoNoPull
== lineReflowStatus
||
4400 LineReflowStatus::RedoMoreFloats
== lineReflowStatus
||
4401 LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4402 if (lineLayout
.NeedsBackup()) {
4403 NS_ASSERTION(!forceBreakInFrame
,
4404 "Backing up twice; this should never be necessary");
4405 // If there is no saved break position, then this will set
4406 // set forceBreakInFrame to null and we won't back up, which is
4408 forceBreakInFrame
= lineLayout
.GetLastOptionalBreakPosition(
4409 &forceBreakOffset
, &forceBreakPriority
);
4411 forceBreakInFrame
= nullptr;
4413 // restore the float manager state
4414 aState
.FloatManager()->PopState(&floatManagerState
);
4415 // Clear out float lists
4416 aState
.mCurrentLineFloats
.Clear();
4417 aState
.mBelowCurrentLineFloats
.Clear();
4418 aState
.mNoWrapFloats
.Clear();
4421 // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4422 allowPullUp
= false;
4423 } while (LineReflowStatus::RedoNoPull
== lineReflowStatus
);
4424 } while (LineReflowStatus::RedoMoreFloats
== lineReflowStatus
);
4425 } while (LineReflowStatus::RedoNextBand
== lineReflowStatus
);
4428 void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState
& aState
,
4430 bool* aKeepReflowGoing
) {
4431 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4432 // Reflow the line again when we reflow at our new position.
4434 *aKeepReflowGoing
= false;
4437 void nsBlockFrame::PushTruncatedLine(BlockReflowState
& aState
,
4439 bool* aKeepReflowGoing
) {
4440 PushLines(aState
, aLine
.prev());
4441 *aKeepReflowGoing
= false;
4442 aState
.mReflowStatus
.SetIncomplete();
4445 void nsBlockFrame::DoReflowInlineFrames(
4446 BlockReflowState
& aState
, nsLineLayout
& aLineLayout
, LineIterator aLine
,
4447 nsFlowAreaRect
& aFloatAvailableSpace
, nscoord
& aAvailableSpaceBSize
,
4448 nsFloatManager::SavedState
* aFloatStateBeforeLine
, bool* aKeepReflowGoing
,
4449 LineReflowStatus
* aLineReflowStatus
, bool aAllowPullUp
) {
4450 // Forget all of the floats on the line
4451 aLine
->ClearFloats();
4452 aState
.mFloatOverflowAreas
.Clear();
4454 // We need to set this flag on the line if any of our reflow passes
4455 // are impacted by floats.
4456 if (aFloatAvailableSpace
.HasFloats()) {
4457 aLine
->SetLineIsImpactedByFloat(true);
4459 #ifdef REALLY_NOISY_REFLOW
4460 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4461 aFloatAvailableSpace
.HasFloats());
4464 WritingMode outerWM
= aState
.mReflowInput
.GetWritingMode();
4465 WritingMode lineWM
= WritingModeForLine(outerWM
, aLine
->mFirstChild
);
4466 LogicalRect lineRect
= aFloatAvailableSpace
.mRect
.ConvertTo(
4467 lineWM
, outerWM
, aState
.ContainerSize());
4469 nscoord iStart
= lineRect
.IStart(lineWM
);
4470 nscoord availISize
= lineRect
.ISize(lineWM
);
4472 if (aState
.mReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
4473 availBSize
= NS_UNCONSTRAINEDSIZE
;
4475 /* XXX get the height right! */
4476 availBSize
= lineRect
.BSize(lineWM
);
4479 // Make sure to enable resize optimization before we call BeginLineReflow
4480 // because it might get disabled there
4481 aLine
->EnableResizeReflowOptimization();
4483 aLineLayout
.BeginLineReflow(iStart
, aState
.mBCoord
, availISize
, availBSize
,
4484 aFloatAvailableSpace
.HasFloats(),
4485 false, /*XXX isTopOfPage*/
4486 lineWM
, aState
.mContainerSize
);
4488 aState
.mFlags
.mIsLineLayoutEmpty
= false;
4490 // XXX Unfortunately we need to know this before reflowing the first
4491 // inline frame in the line. FIX ME.
4492 if ((0 == aLineLayout
.GetLineNumber()) &&
4493 (NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) &&
4494 (NS_BLOCK_HAS_FIRST_LETTER_STYLE
& mState
)) {
4495 aLineLayout
.SetFirstLetterStyleOK(true);
4498 !((NS_BLOCK_HAS_FIRST_LETTER_CHILD
& mState
) && GetPrevContinuation()),
4499 "first letter child bit should only be on first continuation");
4501 // Reflow the frames that are already on the line first
4502 LineReflowStatus lineReflowStatus
= LineReflowStatus::OK
;
4504 nsIFrame
* frame
= aLine
->mFirstChild
;
4506 if (aFloatAvailableSpace
.HasFloats()) {
4507 // There is a soft break opportunity at the start of the line, because
4508 // we can always move this line down below float(s).
4509 if (aLineLayout
.NotifyOptionalBreakPosition(
4510 frame
, 0, true, gfxBreakPriority::eNormalBreak
)) {
4511 lineReflowStatus
= LineReflowStatus::RedoNextBand
;
4515 // need to repeatedly call GetChildCount here, because the child
4516 // count can change during the loop!
4518 LineReflowStatus::OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
4519 i
++, frame
= frame
->GetNextSibling()) {
4520 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4521 if (LineReflowStatus::OK
!= lineReflowStatus
) {
4522 // It is possible that one or more of next lines are empty
4523 // (because of DeleteNextInFlowChild). If so, delete them now
4524 // in case we are finished.
4526 while ((aLine
!= LinesEnd()) && (0 == aLine
->GetChildCount())) {
4527 // XXX Is this still necessary now that DeleteNextInFlowChild
4528 // uses DoRemoveFrame?
4529 nsLineBox
* toremove
= aLine
;
4530 aLine
= mLines
.erase(aLine
);
4531 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
4532 FreeLineBox(toremove
);
4536 NS_ASSERTION(lineReflowStatus
!= LineReflowStatus::Truncated
,
4537 "ReflowInlineFrame should never determine that a line "
4538 "needs to go to the next page/column");
4542 // Don't pull up new frames into lines with continuation placeholders
4544 // Pull frames and reflow them until we can't
4545 while (LineReflowStatus::OK
== lineReflowStatus
) {
4546 frame
= PullFrame(aState
, aLine
);
4551 while (LineReflowStatus::OK
== lineReflowStatus
) {
4552 int32_t oldCount
= aLine
->GetChildCount();
4553 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4554 if (aLine
->GetChildCount() != oldCount
) {
4555 // We just created a continuation for aFrame AND its going
4556 // to end up on this line (e.g. :first-letter
4557 // situation). Therefore we have to loop here before trying
4558 // to pull another frame.
4559 frame
= frame
->GetNextSibling();
4567 aState
.mFlags
.mIsLineLayoutEmpty
= aLineLayout
.LineIsEmpty();
4569 // We only need to backup if the line isn't going to be reflowed again anyway
4570 bool needsBackup
= aLineLayout
.NeedsBackup() &&
4571 (lineReflowStatus
== LineReflowStatus::Stop
||
4572 lineReflowStatus
== LineReflowStatus::OK
);
4573 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
4575 "We shouldn't be backing up more than once! "
4576 "Someone must have set a break opportunity beyond the available width, "
4577 "even though there were better break opportunities before it");
4578 needsBackup
= false;
4581 // We need to try backing up to before a text run
4582 // XXX It's possible, in fact not unusual, for the break opportunity to
4583 // already be the end of the line. We should detect that and optimize to not
4585 if (aLineLayout
.HasOptionalBreakPosition()) {
4587 lineReflowStatus
= LineReflowStatus::RedoNoPull
;
4590 // In case we reflow this line again, remember that we don't
4591 // need to force any breaking
4592 aLineLayout
.ClearOptionalBreakPosition();
4595 if (LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4596 // This happens only when we have a line that is impacted by
4597 // floats and the first element in the line doesn't fit with
4600 // If there's block space available, we either try to reflow the line
4601 // past the current band (if it's non-zero and the band definitely won't
4602 // widen around a shape-outside), otherwise we try one pixel down. If
4603 // there's no block space available, we push the line to the next
4606 NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.BSize(outerWM
),
4607 "unconstrained block size on totally empty line");
4609 // See the analogous code for blocks in BlockReflowState::ClearFloats.
4610 nscoord bandBSize
= aFloatAvailableSpace
.mRect
.BSize(outerWM
);
4611 if (bandBSize
> 0 ||
4612 NS_UNCONSTRAINEDSIZE
== aState
.mReflowInput
.AvailableBSize()) {
4613 NS_ASSERTION(bandBSize
== 0 || aFloatAvailableSpace
.HasFloats(),
4614 "redo line on totally empty line with non-empty band...");
4615 // We should never hit this case if we've placed floats on the
4616 // line; if we have, then the GetFloatAvailableSpace call is wrong
4617 // and needs to happen after the caller pops the float manager
4619 aState
.FloatManager()->AssertStateMatches(aFloatStateBeforeLine
);
4621 if (!aFloatAvailableSpace
.MayWiden() && bandBSize
> 0) {
4622 // Move it down far enough to clear the current band.
4623 aState
.mBCoord
+= bandBSize
;
4625 // Move it down by one dev pixel.
4626 aState
.mBCoord
+= aState
.mPresContext
->DevPixelsToAppUnits(1);
4629 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
4631 // There's nowhere to retry placing the line, so we want to push
4632 // it to the next page/column where its contents can fit not
4634 lineReflowStatus
= LineReflowStatus::Truncated
;
4635 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4638 // XXX: a small optimization can be done here when paginating:
4639 // if the new Y coordinate is past the end of the block then
4640 // push the line and return now instead of later on after we are
4642 } else if (LineReflowStatus::Truncated
!= lineReflowStatus
&&
4643 LineReflowStatus::RedoNoPull
!= lineReflowStatus
) {
4644 // If we are propagating out a break-before status then there is
4645 // no point in placing the line.
4646 if (!aState
.mReflowStatus
.IsInlineBreakBefore()) {
4647 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
4648 aFloatAvailableSpace
, aAvailableSpaceBSize
,
4649 aKeepReflowGoing
)) {
4650 lineReflowStatus
= LineReflowStatus::RedoMoreFloats
;
4651 // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4658 printf("Line reflow status = %s\n",
4659 LineReflowStatusToString(lineReflowStatus
));
4663 if (aLineLayout
.GetDirtyNextLine()) {
4664 // aLine may have been pushed to the overflow lines.
4665 FrameLines
* overflowLines
= GetOverflowLines();
4666 // We can't just compare iterators front() to aLine here, since they may be
4667 // in different lists.
4668 bool pushedToOverflowLines
=
4669 overflowLines
&& overflowLines
->mLines
.front() == aLine
.get();
4670 if (pushedToOverflowLines
) {
4671 // aLine is stale, it's associated with the main line list but it should
4672 // be associated with the overflow line list now
4673 aLine
= overflowLines
->mLines
.begin();
4675 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
4676 if (iter
.Next() && iter
.GetLine()->IsInline()) {
4677 iter
.GetLine()->MarkDirty();
4678 if (iter
.GetContainer() != this) {
4679 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4684 *aLineReflowStatus
= lineReflowStatus
;
4688 * Reflow an inline frame. The reflow status is mapped from the frames
4689 * reflow status to the lines reflow status (not to our reflow status).
4690 * The line reflow status is simple: true means keep placing frames
4691 * on the line; false means don't (the line is done). If the line
4692 * has some sort of breaking affect then aLine's break-type will be set
4693 * to something other than StyleClear::None.
4695 void nsBlockFrame::ReflowInlineFrame(BlockReflowState
& aState
,
4696 nsLineLayout
& aLineLayout
,
4697 LineIterator aLine
, nsIFrame
* aFrame
,
4698 LineReflowStatus
* aLineReflowStatus
) {
4700 *aLineReflowStatus
= LineReflowStatus::OK
;
4702 #ifdef NOISY_FIRST_LETTER
4704 printf(": reflowing ");
4705 aFrame
->ListTag(stdout
);
4706 printf(" reflowingFirstLetter=%s\n",
4707 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
4710 if (aFrame
->IsPlaceholderFrame()) {
4711 auto ph
= static_cast<nsPlaceholderFrame
*>(aFrame
);
4712 ph
->ForgetLineIsEmptySoFar();
4715 // Reflow the inline frame
4716 nsReflowStatus frameReflowStatus
;
4718 aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
, nullptr, pushedFrame
);
4720 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4721 aLineLayout
.SetDirtyNextLine();
4724 #ifdef REALLY_NOISY_REFLOW
4725 aFrame
->ListTag(stdout
);
4726 printf(": status=%s\n", ToString(frameReflowStatus
).c_str());
4729 #if defined(REFLOW_STATUS_COVERAGE)
4730 RecordReflowStatus(false, frameReflowStatus
);
4733 // Send post-reflow notification
4734 aState
.mPrevChild
= aFrame
;
4737 This is where we need to add logic to handle some odd behavior.
4738 For one thing, we should usually place at least one thing next
4739 to a left float, even when that float takes up all the width on a line.
4743 // Process the child frames reflow status. There are 5 cases:
4744 // complete, not-complete, break-before, break-after-complete,
4745 // break-after-not-complete. There are two situations: we are a
4746 // block or we are an inline. This makes a total of 10 cases
4747 // (fortunately, there is some overlap).
4748 aLine
->ClearForcedLineBreak();
4749 if (frameReflowStatus
.IsInlineBreak() ||
4750 aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
4751 // Always abort the line reflow (because a line break is the
4752 // minimal amount of break we do).
4753 *aLineReflowStatus
= LineReflowStatus::Stop
;
4755 // XXX what should aLine's break-type be set to in all these cases?
4756 if (frameReflowStatus
.IsInlineBreakBefore()) {
4757 // Break-before cases.
4758 if (aFrame
== aLine
->mFirstChild
) {
4759 // If we break before the first frame on the line then we must
4760 // be trying to place content where there's no room (e.g. on a
4761 // line with wide floats). Inform the caller to reflow the
4762 // line after skipping past a float.
4763 *aLineReflowStatus
= LineReflowStatus::RedoNextBand
;
4765 // It's not the first child on this line so go ahead and split
4766 // the line. We will see the frame again on the next-line.
4767 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
4769 // If we're splitting the line because the frame didn't fit and it
4770 // was pushed, then mark the line as having word wrapped. We need to
4771 // know that if we're shrink wrapping our width
4773 aLine
->SetLineWrapped(true);
4777 MOZ_ASSERT(frameReflowStatus
.IsInlineBreakAfter() ||
4778 aState
.mTrailingClearFromPIF
!= StyleClear::None
,
4779 "We should've handled inline break-before in the if-branch!");
4781 // If a float split and its prev-in-flow was followed by a <BR>, then
4782 // combine the <BR>'s float clear type with the inline's float clear type
4783 // (the inline will be the very next frame after the split float).
4784 StyleClear clearType
= frameReflowStatus
.FloatClearType();
4785 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
4786 clearType
= nsLayoutUtils::CombineClearType(
4787 clearType
, aState
.mTrailingClearFromPIF
);
4788 aState
.mTrailingClearFromPIF
= StyleClear::None
;
4790 // Break-after cases
4791 if (clearType
!= StyleClear::None
|| aLineLayout
.GetLineEndsInBR()) {
4792 aLine
->SetForcedLineBreakAfter(clearType
);
4794 if (frameReflowStatus
.IsComplete()) {
4795 // Split line, but after the frame just reflowed
4796 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
4799 if (frameReflowStatus
.IsInlineBreakAfter() &&
4800 !aLineLayout
.GetLineEndsInBR()) {
4801 aLineLayout
.SetDirtyNextLine();
4807 if (!frameReflowStatus
.IsFullyComplete()) {
4808 // Create a continuation for the incomplete frame. Note that the
4809 // frame may already have a continuation.
4810 CreateContinuationFor(aState
, aLine
, aFrame
);
4812 // Remember that the line has wrapped
4813 if (!aLineLayout
.GetLineEndsInBR()) {
4814 aLine
->SetLineWrapped(true);
4817 // If we just ended a first-letter frame or reflowed a placeholder then
4818 // don't split the line and don't stop the line reflow...
4819 // But if we are going to stop anyways we'd better split the line.
4820 if ((!frameReflowStatus
.FirstLetterComplete() &&
4821 !aFrame
->IsPlaceholderFrame()) ||
4822 *aLineReflowStatus
== LineReflowStatus::Stop
) {
4823 // Split line after the current frame
4824 *aLineReflowStatus
= LineReflowStatus::Stop
;
4825 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
4831 bool nsBlockFrame::CreateContinuationFor(BlockReflowState
& aState
,
4832 nsLineBox
* aLine
, nsIFrame
* aFrame
) {
4833 nsIFrame
* newFrame
= nullptr;
4835 if (!aFrame
->GetNextInFlow()) {
4837 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame
, this);
4839 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
4842 aLine
->NoteFrameAdded(newFrame
);
4851 void nsBlockFrame::SplitFloat(BlockReflowState
& aState
, nsIFrame
* aFloat
,
4852 const nsReflowStatus
& aFloatStatus
) {
4853 MOZ_ASSERT(!aFloatStatus
.IsFullyComplete(),
4854 "why split the frame if it's fully complete?");
4855 MOZ_ASSERT(aState
.mBlock
== this);
4857 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
4859 nsContainerFrame
* oldParent
= nextInFlow
->GetParent();
4860 oldParent
->StealFrame(nextInFlow
);
4861 if (oldParent
!= this) {
4862 ReparentFrame(nextInFlow
, oldParent
, this);
4864 if (!aFloatStatus
.IsOverflowIncomplete()) {
4865 nextInFlow
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4869 PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat
, this);
4871 if (aFloatStatus
.IsOverflowIncomplete()) {
4872 nextInFlow
->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4875 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
4876 if (floatStyle
== StyleFloat::Left
) {
4877 aState
.FloatManager()->SetSplitLeftFloatAcrossBreak();
4879 MOZ_ASSERT(floatStyle
== StyleFloat::Right
, "Unexpected float side!");
4880 aState
.FloatManager()->SetSplitRightFloatAcrossBreak();
4883 aState
.AppendPushedFloatChain(nextInFlow
);
4884 if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_FLOAT_MGR
)) ||
4885 MOZ_UNLIKELY(IsTrueOverflowContainer())) {
4886 aState
.mReflowStatus
.SetOverflowIncomplete();
4888 aState
.mReflowStatus
.SetIncomplete();
4892 static bool CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
,
4897 NS_ASSERTION(!aFloat
->GetPrevContinuation(),
4898 "float in a line should never be a continuation");
4899 NS_ASSERTION(!aFloat
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
4900 "float in a line should never be a pushed float");
4901 nsIFrame
* ph
= aFloat
->FirstInFlow()->GetPlaceholderFrame();
4902 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
4903 if (f
->GetParent() == aBlock
) {
4904 return aLine
->Contains(f
);
4907 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
4911 void nsBlockFrame::SplitLine(BlockReflowState
& aState
,
4912 nsLineLayout
& aLineLayout
, LineIterator aLine
,
4914 LineReflowStatus
* aLineReflowStatus
) {
4915 MOZ_ASSERT(aLine
->IsInline(), "illegal SplitLine on block line");
4918 aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
4919 MOZ_ASSERT(pushCount
>= 0, "bad push count");
4923 nsIFrame::IndentBy(stdout
, gNoiseIndent
);
4924 printf("split line: from line=%p pushCount=%d aFrame=",
4925 static_cast<void*>(aLine
.get()), pushCount
);
4927 aFrame
->ListTag(stdout
);
4932 if (gReallyNoisyReflow
) {
4933 aLine
->List(stdout
, gNoiseIndent
+ 1);
4938 if (0 != pushCount
) {
4939 MOZ_ASSERT(aLine
->GetChildCount() > pushCount
, "bad push");
4940 MOZ_ASSERT(nullptr != aFrame
, "whoops");
4943 nsIFrame
* f
= aFrame
;
4944 int32_t count
= pushCount
;
4945 while (f
&& count
> 0) {
4946 f
= f
->GetNextSibling();
4949 NS_ASSERTION(count
== 0, "Not enough frames to push");
4953 // Put frames being split out into their own line
4954 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
4955 mLines
.after_insert(aLine
, newLine
);
4957 if (gReallyNoisyReflow
) {
4958 newLine
->List(stdout
, gNoiseIndent
+ 1);
4962 // Let line layout know that some frames are no longer part of its
4964 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
4966 // If floats have been placed whose placeholders have been pushed to the new
4967 // line, we need to reflow the old line again. We don't want to look at the
4968 // frames in the new line, because as a large paragraph is laid out the
4969 // we'd get O(N^2) performance. So instead we just check that the last
4970 // float and the last below-current-line float are still in aLine.
4971 if (!CheckPlaceholderInLine(
4973 aLine
->HasFloats() ? aLine
->Floats().LastElement() : nullptr) ||
4974 !CheckPlaceholderInLine(
4976 aState
.mBelowCurrentLineFloats
.SafeLastElement(nullptr))) {
4977 *aLineReflowStatus
= LineReflowStatus::RedoNoPull
;
4986 bool nsBlockFrame::IsLastLine(BlockReflowState
& aState
, LineIterator aLine
) {
4987 while (++aLine
!= LinesEnd()) {
4988 // There is another line
4989 if (0 != aLine
->GetChildCount()) {
4990 // If the next line is a block line then this line is the last in a
4991 // group of inline lines.
4992 return aLine
->IsBlock();
4994 // The next line is empty, try the next one
4997 // XXX Not sure about this part
4998 // Try our next-in-flows lines to answer the question
4999 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*)GetNextInFlow();
5000 while (nullptr != nextInFlow
) {
5001 for (const auto& line
: nextInFlow
->Lines()) {
5002 if (0 != line
.GetChildCount()) {
5003 return line
.IsBlock();
5006 nextInFlow
= (nsBlockFrame
*)nextInFlow
->GetNextInFlow();
5009 // This is the last line - so don't allow justification
5013 bool nsBlockFrame::PlaceLine(BlockReflowState
& aState
,
5014 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5015 nsFloatManager::SavedState
* aFloatStateBeforeLine
,
5016 nsFlowAreaRect
& aFlowArea
,
5017 nscoord
& aAvailableSpaceBSize
,
5018 bool* aKeepReflowGoing
) {
5019 // Try to position the floats in a nowrap context.
5020 aLineLayout
.FlushNoWrapFloats();
5022 // Trim extra white-space from the line before placing the frames
5023 aLineLayout
.TrimTrailingWhiteSpace();
5025 // Vertically align the frames on this line.
5027 // According to the CSS2 spec, section 12.6.1, the "marker" box
5028 // participates in the height calculation of the list-item box's
5031 // There are exactly two places a ::marker can be placed: near the
5032 // first or second line. It's only placed on the second line in a
5033 // rare case: when the first line is empty.
5034 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
5035 bool addedMarker
= false;
5036 if (HasOutsideMarker() &&
5037 ((aLine
== mLines
.front() &&
5038 (!aLineLayout
.IsZeroBSize() || (aLine
== mLines
.back()))) ||
5039 (mLines
.front() != mLines
.back() && 0 == mLines
.front()->BSize() &&
5040 aLine
== mLines
.begin().next()))) {
5041 ReflowOutput
metrics(aState
.mReflowInput
);
5042 nsIFrame
* marker
= GetOutsideMarker();
5043 ReflowOutsideMarker(marker
, aState
, metrics
, aState
.mBCoord
);
5044 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
5045 "empty ::marker frame took up space");
5046 aLineLayout
.AddMarkerFrame(marker
, metrics
);
5049 aLineLayout
.VerticalAlignLine();
5051 // We want to consider the floats in the current line when determining
5052 // whether the float available space is shrunk. If mLineBSize doesn't
5053 // exist, we are in the first pass trying to place the line. Calling
5054 // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
5055 // for UpdateBand().
5057 // floatAvailableSpaceWithOldLineBSize is the float available space with
5058 // the old BSize, but including the floats that were added in this line.
5059 LogicalRect floatAvailableSpaceWithOldLineBSize
=
5060 aState
.mLineBSize
.isNothing()
5061 ? aState
.GetFloatAvailableSpace(aLine
->BStart()).mRect
5063 .GetFloatAvailableSpaceForBSize(
5064 aLine
->BStart(), aState
.mLineBSize
.value(), nullptr)
5067 // As we redo for floats, we can't reduce the amount of BSize we're
5069 aAvailableSpaceBSize
= std::max(aAvailableSpaceBSize
, aLine
->BSize());
5070 LogicalRect floatAvailableSpaceWithLineBSize
=
5072 .GetFloatAvailableSpaceForBSize(aLine
->BStart(), aAvailableSpaceBSize
,
5076 // If the available space between the floats is smaller now that we
5077 // know the BSize, return false (and cause another pass with
5078 // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
5079 // never decreases, which means that we can't reduce the set of floats
5080 // we intersect, which means that the available space cannot grow.
5081 if (AvailableSpaceShrunk(wm
, floatAvailableSpaceWithOldLineBSize
,
5082 floatAvailableSpaceWithLineBSize
, false)) {
5083 // Prepare data for redoing the line.
5084 aState
.mLineBSize
= Some(aLine
->BSize());
5086 // Since we want to redo the line, we update aFlowArea by using the
5087 // aFloatStateBeforeLine, which is the float manager's state before the
5089 LogicalRect
oldFloatAvailableSpace(aFlowArea
.mRect
);
5090 aFlowArea
= aState
.GetFloatAvailableSpaceForBSize(
5091 aLine
->BStart(), aAvailableSpaceBSize
, aFloatStateBeforeLine
);
5094 aFlowArea
.mRect
.BStart(wm
) == oldFloatAvailableSpace
.BStart(wm
),
5096 // Restore the BSize to the position of the next band.
5097 aFlowArea
.mRect
.BSize(wm
) = oldFloatAvailableSpace
.BSize(wm
);
5099 // Enforce both IStart() and IEnd() never move outwards to prevent
5100 // infinite grow-shrink loops.
5101 const nscoord iStartDiff
=
5102 aFlowArea
.mRect
.IStart(wm
) - oldFloatAvailableSpace
.IStart(wm
);
5103 const nscoord iEndDiff
=
5104 aFlowArea
.mRect
.IEnd(wm
) - oldFloatAvailableSpace
.IEnd(wm
);
5105 if (iStartDiff
< 0) {
5106 aFlowArea
.mRect
.IStart(wm
) -= iStartDiff
;
5107 aFlowArea
.mRect
.ISize(wm
) += iStartDiff
;
5110 aFlowArea
.mRect
.ISize(wm
) -= iEndDiff
;
5117 if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
5118 static nscoord lastHeight
= 0;
5119 if (ABSURD_SIZE(aLine
->BStart())) {
5120 lastHeight
= aLine
->BStart();
5121 if (abs(aLine
->BStart() - lastHeight
) > ABSURD_COORD
/ 10) {
5122 nsIFrame::ListTag(stdout
);
5123 printf(": line=%p y=%d line.bounds.height=%d\n",
5124 static_cast<void*>(aLine
.get()), aLine
->BStart(),
5133 // Only block frames horizontally align their children because
5134 // inline frames "shrink-wrap" around their children (therefore
5135 // there is no extra horizontal space).
5136 const nsStyleText
* styleText
= StyleText();
5139 * We don't care checking for IsLastLine properly if we don't care (if it
5140 * can't change the used text-align value for the line).
5142 * In other words, isLastLine really means isLastLineAndWeCare.
5144 const bool isLastLine
=
5145 !SVGUtils::IsInSVGTextSubtree(this) &&
5146 styleText
->TextAlignForLastLine() != styleText
->mTextAlign
&&
5147 (aLineLayout
.GetLineEndsInBR() || IsLastLine(aState
, aLine
));
5149 aLineLayout
.TextAlignLine(aLine
, isLastLine
);
5151 // From here on, pfd->mBounds rectangles are incorrect because bidi
5152 // might have moved frames around!
5153 OverflowAreas overflowAreas
;
5154 aLineLayout
.RelativePositionFrames(overflowAreas
);
5155 aLine
->SetOverflowAreas(overflowAreas
);
5157 aLineLayout
.RemoveMarkerFrame(GetOutsideMarker());
5160 // Inline lines do not have margins themselves; however they are
5161 // impacted by prior block margins. If this line ends up having some
5162 // height then we zero out the previous block-end margin value that was
5163 // already applied to the line's starting Y coordinate. Otherwise we
5164 // leave it be so that the previous blocks block-end margin can be
5165 // collapsed with a block that follows.
5168 if (!aLine
->CachedIsEmpty()) {
5169 // This line has some height. Therefore the application of the
5170 // previous-bottom-margin should stick.
5171 aState
.mPrevBEndMargin
.Zero();
5172 newBCoord
= aLine
->BEnd();
5174 // Don't let the previous-bottom-margin value affect the newBCoord
5175 // coordinate (it was applied in ReflowInlineFrames speculatively)
5176 // since the line is empty.
5177 // We already called |ShouldApplyBStartMargin|, and if we applied it
5178 // then mShouldApplyBStartMargin is set.
5179 nscoord dy
= aState
.mFlags
.mShouldApplyBStartMargin
5180 ? -aState
.mPrevBEndMargin
.get()
5182 newBCoord
= aState
.mBCoord
+ dy
;
5185 if (!aState
.mReflowStatus
.IsFullyComplete() &&
5186 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5187 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5188 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5192 // See if the line fit (our first line always does).
5193 if (mLines
.front() != aLine
&&
5194 aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
&&
5195 newBCoord
> aState
.ContentBEnd()) {
5196 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
5197 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5198 // All our content doesn't fit, start on the next page.
5199 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5201 // Push aLine and all of its children and anything else that
5202 // follows to our next-in-flow.
5203 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
5208 // Note that any early return before this update of aState.mBCoord
5209 // must either (a) return false or (b) set aKeepReflowGoing to false.
5210 // Otherwise we'll keep reflowing later lines at an incorrect
5211 // position, and we might not come back and clean up the damage later.
5212 aState
.mBCoord
= newBCoord
;
5214 // Add the already placed current-line floats to the line
5215 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5217 // Any below current line floats to place?
5218 if (!aState
.mBelowCurrentLineFloats
.IsEmpty()) {
5219 // Reflow the below-current-line floats, which places on the line's
5221 aState
.PlaceBelowCurrentLineFloats(aLine
);
5224 // When a line has floats, factor them into the overflow areas computations.
5225 if (aLine
->HasFloats()) {
5226 // Union the float overflow areas (stored in aState) and the value computed
5227 // by the line layout code.
5228 OverflowAreas lineOverflowAreas
= aState
.mFloatOverflowAreas
;
5229 lineOverflowAreas
.UnionWith(aLine
->GetOverflowAreas());
5230 aLine
->SetOverflowAreas(lineOverflowAreas
);
5232 #ifdef NOISY_OVERFLOW_AREAS
5233 printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
5234 ListTag().get(), aLine
.get(),
5235 ToString(aLine
->InkOverflowRect()).c_str(),
5236 ToString(aLine
->ScrollableOverflowRect()).c_str());
5240 // Apply break-after clearing if necessary
5241 // This must stay in sync with |ReflowDirtyLines|.
5242 if (aLine
->HasFloatClearTypeAfter()) {
5243 std::tie(aState
.mBCoord
, std::ignore
) =
5244 aState
.ClearFloats(aState
.mBCoord
, aLine
->FloatClearTypeAfter());
5249 void nsBlockFrame::PushLines(BlockReflowState
& aState
,
5250 nsLineList::iterator aLineBefore
) {
5251 // NOTE: aLineBefore is always a normal line, not an overflow line.
5252 // The following expression will assert otherwise.
5253 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
5255 nsLineList::iterator
overBegin(aLineBefore
.next());
5257 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
5258 bool firstLine
= overBegin
== LinesBegin();
5260 if (overBegin
!= LinesEnd()) {
5261 // Remove floats in the lines from mFloats
5263 CollectFloats(overBegin
->mFirstChild
, floats
, true);
5265 if (floats
.NotEmpty()) {
5267 for (nsIFrame
* f
: floats
) {
5268 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5269 "CollectFloats should've removed that bit");
5272 // Push the floats onto the front of the overflow out-of-flows list
5273 nsAutoOOFFrameList
oofs(this);
5274 oofs
.mList
.InsertFrames(nullptr, nullptr, std::move(floats
));
5277 // overflow lines can already exist in some cases, in particular,
5278 // when shrinkwrapping and we discover that the shrinkwap causes
5279 // the height of some child block to grow which creates additional
5280 // overflowing content. In such cases we must prepend the new
5281 // overflow to the existing overflow.
5282 FrameLines
* overflowLines
= RemoveOverflowLines();
5283 if (!overflowLines
) {
5284 // XXXldb use presshell arena!
5285 overflowLines
= new FrameLines();
5287 if (overflowLines
) {
5288 nsIFrame
* lineBeforeLastFrame
;
5290 lineBeforeLastFrame
= nullptr; // removes all frames
5292 nsIFrame
* f
= overBegin
->mFirstChild
;
5293 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
5294 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
5295 "unexpected line frames");
5297 nsFrameList pushedFrames
= mFrames
.TakeFramesAfter(lineBeforeLastFrame
);
5298 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr,
5299 std::move(pushedFrames
));
5301 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
5302 overBegin
, LinesEnd());
5303 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
5304 // this takes ownership but it won't delete it immediately so we
5305 // can keep using it.
5306 SetOverflowLines(overflowLines
);
5308 // Mark all the overflow lines dirty so that they get reflowed when
5309 // they are pulled up by our next-in-flow.
5311 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
5312 for (LineIterator line
= overflowLines
->mLines
.begin(),
5313 line_end
= overflowLines
->mLines
.end();
5314 line
!= line_end
; ++line
) {
5316 line
->MarkPreviousMarginDirty();
5317 line
->SetMovedFragments();
5318 line
->SetBoundsEmpty();
5319 if (line
->HasFloats()) {
5320 line
->ClearFloats();
5327 VerifyOverflowSituation();
5331 // The overflowLines property is stored as a pointer to a line list,
5332 // which must be deleted. However, the following functions all maintain
5333 // the invariant that the property is never set if the list is empty.
5335 bool nsBlockFrame::DrainOverflowLines() {
5337 VerifyOverflowSituation();
5340 // Steal the prev-in-flow's overflow lines and prepend them.
5341 bool didFindOverflow
= false;
5342 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5344 prevBlock
->ClearLineCursors();
5345 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
5346 if (overflowLines
) {
5347 // Make all the frames on the overflow line list mine.
5348 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
5350 // Collect overflow containers from our OverflowContainers list that are
5351 // continuations from the frames we picked up from our prev-in-flow, then
5352 // prepend those to ExcessOverflowContainers to ensure the continuations
5354 if (GetOverflowContainers()) {
5355 nsFrameList ocContinuations
;
5356 for (auto* f
: overflowLines
->mFrames
) {
5359 while (!done
&& (cont
= cont
->GetNextContinuation()) &&
5360 cont
->GetParent() == this) {
5361 bool onlyChild
= !cont
->GetPrevSibling() && !cont
->GetNextSibling();
5362 if (cont
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
) &&
5363 TryRemoveFrame(OverflowContainersProperty(), cont
)) {
5364 ocContinuations
.AppendFrame(nullptr, cont
);
5374 if (!ocContinuations
.IsEmpty()) {
5375 if (nsFrameList
* eoc
= GetExcessOverflowContainers()) {
5376 eoc
->InsertFrames(nullptr, nullptr, std::move(ocContinuations
));
5378 SetExcessOverflowContainers(std::move(ocContinuations
));
5383 // Make the overflow out-of-flow frames mine too.
5384 nsAutoOOFFrameList
oofs(prevBlock
);
5385 if (oofs
.mList
.NotEmpty()) {
5386 // In case we own any next-in-flows of any of the drained frames, then
5387 // move those to the PushedFloat list.
5388 nsFrameList pushedFloats
;
5389 for (nsIFrame
* f
: oofs
.mList
) {
5390 nsIFrame
* nif
= f
->GetNextInFlow();
5391 for (; nif
&& nif
->GetParent() == this; nif
= nif
->GetNextInFlow()) {
5392 MOZ_ASSERT(nif
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
));
5394 pushedFloats
.AppendFrame(nullptr, nif
);
5397 ReparentFrames(oofs
.mList
, prevBlock
, this);
5398 mFloats
.InsertFrames(nullptr, nullptr, std::move(oofs
.mList
));
5399 if (!pushedFloats
.IsEmpty()) {
5400 nsFrameList
* pf
= EnsurePushedFloats();
5401 pf
->InsertFrames(nullptr, nullptr, std::move(pushedFloats
));
5405 if (!mLines
.empty()) {
5406 // Remember to recompute the margins on the first line. This will
5407 // also recompute the correct deltaBCoord if necessary.
5408 mLines
.front()->MarkPreviousMarginDirty();
5410 // The overflow lines have already been marked dirty and their previous
5411 // margins marked dirty also.
5413 // Prepend the overflow frames/lines to our principal list.
5414 mFrames
.InsertFrames(nullptr, nullptr, std::move(overflowLines
->mFrames
));
5415 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
5416 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
5417 delete overflowLines
;
5418 didFindOverflow
= true;
5422 // Now append our own overflow lines.
5423 return DrainSelfOverflowList() || didFindOverflow
;
5426 bool nsBlockFrame::DrainSelfOverflowList() {
5427 UniquePtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
5428 if (!ourOverflowLines
) {
5432 // No need to reparent frames in our own overflow lines/oofs, because they're
5433 // already ours. But we should put overflow floats back in mFloats.
5434 // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5436 nsAutoOOFFrameList
oofs(this);
5437 if (oofs
.mList
.NotEmpty()) {
5439 for (nsIFrame
* f
: oofs
.mList
) {
5440 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5441 "CollectFloats should've removed that bit");
5444 // The overflow floats go after our regular floats.
5445 mFloats
.AppendFrames(nullptr, std::move(oofs
).mList
);
5448 if (!ourOverflowLines
->mLines
.empty()) {
5449 mFrames
.AppendFrames(nullptr, std::move(ourOverflowLines
->mFrames
));
5450 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
5454 VerifyOverflowSituation();
5460 * Pushed floats are floats whose placeholders are in a previous
5461 * continuation. They might themselves be next-continuations of a float
5462 * that partially fit in an earlier continuation, or they might be the
5463 * first continuation of a float that couldn't be placed at all.
5465 * Pushed floats live permanently at the beginning of a block's float
5466 * list, where they must live *before* any floats whose placeholders are
5469 * Temporarily, during reflow, they also live on the pushed floats list,
5470 * which only holds them between (a) when one continuation pushes them to
5471 * its pushed floats list because they don't fit and (b) when the next
5472 * continuation pulls them onto the beginning of its float list.
5474 * DrainPushedFloats sets up pushed floats the way we need them at the
5475 * start of reflow; they are then reflowed by ReflowPushedFloats (which
5476 * might push some of them on). Floats with placeholders in this block
5477 * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
5478 * also maintains these invariants.
5480 * DrainSelfPushedFloats moves any pushed floats from this block's own
5481 * PushedFloats list back into mFloats. DrainPushedFloats additionally
5482 * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5484 void nsBlockFrame::DrainSelfPushedFloats() {
5485 // If we're getting reflowed multiple times without our
5486 // next-continuation being reflowed, we might need to pull back floats
5487 // that we just put in the list to be pushed to our next-in-flow.
5488 // We don't want to pull back any next-in-flows of floats on our own
5489 // float list, and we only need to pull back first-in-flows whose
5490 // placeholders were in earlier blocks (since first-in-flows whose
5491 // placeholders are in this block will get pulled appropriately by
5492 // AddFloat, and will then be more likely to be in the correct order).
5493 // FIXME: What if there's a continuation in our pushed floats list
5494 // whose prev-in-flow is in a previous continuation of this block
5495 // rather than this block? Might we need to pull it back so we don't
5496 // report ourselves complete?
5497 // FIXME: Maybe we should just pull all of them back?
5498 nsPresContext
* presContext
= PresContext();
5499 nsFrameList
* ourPushedFloats
= GetPushedFloats();
5500 if (ourPushedFloats
) {
5501 // When we pull back floats, we want to put them with the pushed
5502 // floats, which must live at the start of our float list, but we
5503 // want them at the end of those pushed floats.
5504 // FIXME: This isn't quite right! What if they're all pushed floats?
5505 nsIFrame
* insertionPrevSibling
= nullptr; /* beginning of list */
5506 for (nsIFrame
* f
= mFloats
.FirstChild();
5507 f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
5508 f
= f
->GetNextSibling()) {
5509 insertionPrevSibling
= f
;
5512 for (nsIFrame
*f
= ourPushedFloats
->LastChild(), *next
; f
; f
= next
) {
5513 next
= f
->GetPrevSibling();
5515 if (f
->GetPrevContinuation()) {
5518 nsPlaceholderFrame
* placeholder
= f
->GetPlaceholderFrame();
5519 nsIFrame
* floatOriginalParent
=
5520 presContext
->PresShell()
5521 ->FrameConstructor()
5522 ->GetFloatContainingBlock(placeholder
);
5523 if (floatOriginalParent
!= this) {
5524 // This is a first continuation that was pushed from one of our
5525 // previous continuations. Take it out of the pushed floats
5526 // list and put it in our floats list, before any of our
5527 // floats, but after other pushed floats.
5528 ourPushedFloats
->RemoveFrame(f
);
5529 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
5534 if (ourPushedFloats
->IsEmpty()) {
5535 RemovePushedFloats()->Delete(presContext
->PresShell());
5540 void nsBlockFrame::DrainPushedFloats() {
5541 DrainSelfPushedFloats();
5543 // After our prev-in-flow has completed reflow, it may have a pushed
5544 // floats list, containing floats that we need to own. Take these.
5545 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5547 AutoFrameListPtr
list(PresContext(), prevBlock
->RemovePushedFloats());
5548 if (list
&& list
->NotEmpty()) {
5549 mFloats
.InsertFrames(this, nullptr, std::move(*list
));
5554 nsBlockFrame::FrameLines
* nsBlockFrame::GetOverflowLines() const {
5555 if (!HasOverflowLines()) {
5558 FrameLines
* prop
= GetProperty(OverflowLinesProperty());
5560 prop
&& !prop
->mLines
.empty() &&
5561 prop
->mLines
.front()->GetChildCount() == 0
5562 ? prop
->mFrames
.IsEmpty()
5563 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5564 "value should always be stored and non-empty when state set");
5568 nsBlockFrame::FrameLines
* nsBlockFrame::RemoveOverflowLines() {
5569 if (!HasOverflowLines()) {
5572 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5574 prop
&& !prop
->mLines
.empty() &&
5575 prop
->mLines
.front()->GetChildCount() == 0
5576 ? prop
->mFrames
.IsEmpty()
5577 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5578 "value should always be stored and non-empty when state set");
5579 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5583 void nsBlockFrame::DestroyOverflowLines() {
5584 NS_ASSERTION(HasOverflowLines(), "huh?");
5585 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5586 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
5587 "value should always be stored but empty when destroying");
5588 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5592 // This takes ownership of aOverflowLines.
5593 // XXX We should allocate overflowLines from presShell arena!
5594 void nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
) {
5595 NS_ASSERTION(aOverflowLines
, "null lines");
5596 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
5597 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
5598 aOverflowLines
->mFrames
.FirstChild(),
5599 "invalid overflow lines / frames");
5600 NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
),
5601 "Overwriting existing overflow lines");
5603 // Verify that we won't overwrite an existing overflow list
5604 NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5605 SetProperty(OverflowLinesProperty(), aOverflowLines
);
5606 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5609 nsFrameList
* nsBlockFrame::GetOverflowOutOfFlows() const {
5610 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5613 nsFrameList
* result
= GetProperty(OverflowOutOfFlowsProperty());
5614 NS_ASSERTION(result
, "value should always be non-empty when state set");
5618 void nsBlockFrame::SetOverflowOutOfFlows(nsFrameList
&& aList
,
5619 nsFrameList
* aPropValue
) {
5621 HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) == !!aPropValue
,
5622 "state does not match value");
5624 if (aList
.IsEmpty()) {
5625 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5628 nsFrameList
* list
= TakeProperty(OverflowOutOfFlowsProperty());
5629 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
5631 list
->Delete(PresShell());
5632 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5633 } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5634 NS_ASSERTION(aPropValue
== GetProperty(OverflowOutOfFlowsProperty()),
5635 "prop value mismatch");
5636 *aPropValue
= std::move(aList
);
5638 SetProperty(OverflowOutOfFlowsProperty(),
5639 new (PresShell()) nsFrameList(std::move(aList
)));
5640 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5644 nsIFrame
* nsBlockFrame::GetInsideMarker() const {
5645 if (!HasInsideMarker()) {
5648 NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5649 nsIFrame
* frame
= GetProperty(InsideMarkerProperty());
5650 NS_ASSERTION(frame
, "bogus inside ::marker frame");
5654 nsIFrame
* nsBlockFrame::GetOutsideMarker() const {
5655 nsFrameList
* list
= GetOutsideMarkerList();
5656 return list
? list
->FirstChild() : nullptr;
5659 nsFrameList
* nsBlockFrame::GetOutsideMarkerList() const {
5660 if (!HasOutsideMarker()) {
5663 NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5664 nsFrameList
* list
= GetProperty(OutsideMarkerProperty());
5665 NS_ASSERTION(list
&& list
->GetLength() == 1, "bogus outside ::marker list");
5669 nsFrameList
* nsBlockFrame::GetPushedFloats() const {
5670 if (!HasPushedFloats()) {
5673 nsFrameList
* result
= GetProperty(PushedFloatProperty());
5674 NS_ASSERTION(result
, "value should always be non-empty when state set");
5678 nsFrameList
* nsBlockFrame::EnsurePushedFloats() {
5679 nsFrameList
* result
= GetPushedFloats();
5684 result
= new (PresShell()) nsFrameList
;
5685 SetProperty(PushedFloatProperty(), result
);
5686 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5691 nsFrameList
* nsBlockFrame::RemovePushedFloats() {
5692 if (!HasPushedFloats()) {
5695 nsFrameList
* result
= TakeProperty(PushedFloatProperty());
5696 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5697 NS_ASSERTION(result
, "value should always be non-empty when state set");
5701 //////////////////////////////////////////////////////////////////////
5702 // Frame list manipulation routines
5704 void nsBlockFrame::AppendFrames(ChildListID aListID
, nsFrameList
&& aFrameList
) {
5705 if (aFrameList
.IsEmpty()) {
5708 if (aListID
!= FrameChildListID::Principal
) {
5709 if (FrameChildListID::Float
== aListID
) {
5710 DrainSelfPushedFloats(); // ensure the last frame is in mFloats
5711 mFloats
.AppendFrames(nullptr, std::move(aFrameList
));
5714 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
5715 "unexpected child list");
5718 // Find the proper last-child for where the append should go
5719 nsIFrame
* lastKid
= mFrames
.LastChild();
5721 (mLines
.empty() ? nullptr : mLines
.back()->LastChild()) == lastKid
,
5722 "out-of-sync mLines / mFrames");
5724 #ifdef NOISY_REFLOW_REASON
5726 printf(": append ");
5727 for (nsIFrame
* frame
: aFrameList
) {
5728 frame
->ListTag(out
);
5732 lastKid
->ListTag(stdout
);
5737 if (SVGUtils::IsInSVGTextSubtree(this)) {
5738 MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
5739 "unexpected block frame in SVG text");
5740 // Workaround for bug 1399425 in case this bit has been removed from the
5741 // SVGTextFrame just before the parser adds more descendant nodes.
5742 GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
);
5745 AddFrames(std::move(aFrameList
), lastKid
, nullptr);
5746 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
5747 PresShell()->FrameNeedsReflow(
5748 this, IntrinsicDirty::TreeChange
,
5749 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5753 void nsBlockFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
5754 const nsLineList::iterator
* aPrevFrameLine
,
5755 nsFrameList
&& aFrameList
) {
5756 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
5757 "inserting after sibling frame with different parent");
5759 if (aListID
!= FrameChildListID::Principal
) {
5760 if (FrameChildListID::Float
== aListID
) {
5761 DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
5762 mFloats
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
5765 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
5766 "unexpected child list");
5769 #ifdef NOISY_REFLOW_REASON
5771 printf(": insert ");
5772 for (nsIFrame
* frame
: aFrameList
) {
5773 frame
->ListTag(out
);
5777 aPrevFrame
->ListTag(stdout
);
5782 AddFrames(std::move(aFrameList
), aPrevFrame
, aPrevFrameLine
);
5783 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
5784 PresShell()->FrameNeedsReflow(
5785 this, IntrinsicDirty::TreeChange
,
5786 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5790 void nsBlockFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
5791 #ifdef NOISY_REFLOW_REASON
5793 printf(": remove ");
5794 aOldFrame
->ListTag(stdout
);
5798 if (aListID
== FrameChildListID::Principal
) {
5799 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
5800 DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
5802 MarkSameFloatManagerLinesDirty(this);
5804 } else if (FrameChildListID::Float
== aListID
) {
5805 // Make sure to mark affected lines dirty for the float frame
5806 // we are removing; this way is a bit messy, but so is the rest of the code.
5808 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
5809 "RemoveFrame should not be called on pushed floats.");
5810 for (nsIFrame
* f
= aOldFrame
;
5811 f
&& !f
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5812 f
= f
->GetNextContinuation()) {
5813 MarkSameFloatManagerLinesDirty(
5814 static_cast<nsBlockFrame
*>(f
->GetParent()));
5816 DoRemoveOutOfFlowFrame(aOldFrame
);
5817 } else if (FrameChildListID::NoReflowPrincipal
== aListID
) {
5818 // Skip the call to |FrameNeedsReflow| below by returning now.
5819 DoRemoveFrame(aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
5822 MOZ_CRASH("unexpected child list");
5825 PresShell()->FrameNeedsReflow(
5826 this, IntrinsicDirty::TreeChange
,
5827 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
5830 static bool ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
) {
5831 LayoutFrameType type
= aLastFrame
->Type();
5832 if (type
== LayoutFrameType::Br
) {
5835 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
5836 if (type
== LayoutFrameType::Text
&&
5837 !aLastFrame
->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING
)) {
5838 return aLastFrame
->HasSignificantTerminalNewline();
5843 void nsBlockFrame::AddFrames(nsFrameList
&& aFrameList
, nsIFrame
* aPrevSibling
,
5844 const nsLineList::iterator
* aPrevSiblingLine
) {
5845 // Clear our line cursor, since our lines may change.
5848 if (aFrameList
.IsEmpty()) {
5852 // Attempt to find the line that contains the previous sibling
5853 nsLineList
* lineList
= &mLines
;
5854 nsFrameList
* frames
= &mFrames
;
5855 nsLineList::iterator prevSibLine
;
5856 int32_t prevSiblingIndex
;
5857 if (aPrevSiblingLine
) {
5858 MOZ_ASSERT(aPrevSibling
);
5859 prevSibLine
= *aPrevSiblingLine
;
5860 FrameLines
* overflowLines
= GetOverflowLines();
5861 MOZ_ASSERT(prevSibLine
.IsInSameList(mLines
.begin()) ||
5863 prevSibLine
.IsInSameList(overflowLines
->mLines
.begin())),
5864 "must be one of our line lists");
5865 if (overflowLines
) {
5866 // We need to find out which list it's actually in. Assume that
5867 // *if* we have overflow lines, that our primary lines aren't
5868 // huge, but our overflow lines might be.
5869 nsLineList::iterator line
= mLines
.begin(), lineEnd
= mLines
.end();
5870 while (line
!= lineEnd
) {
5871 if (line
== prevSibLine
) {
5876 if (line
== lineEnd
) {
5877 // By elimination, the line must be in our overflow lines.
5878 lineList
= &overflowLines
->mLines
;
5879 frames
= &overflowLines
->mFrames
;
5883 nsLineList::iterator nextLine
= prevSibLine
.next();
5884 nsIFrame
* lastFrameInLine
= nextLine
== lineList
->end()
5885 ? frames
->LastChild()
5886 : nextLine
->mFirstChild
->GetPrevSibling();
5887 prevSiblingIndex
= prevSibLine
->RIndexOf(aPrevSibling
, lastFrameInLine
);
5888 MOZ_ASSERT(prevSiblingIndex
>= 0,
5889 "aPrevSibling must be in aPrevSiblingLine");
5891 prevSibLine
= lineList
->end();
5892 prevSiblingIndex
= -1;
5894 // XXX_perf This is technically O(N^2) in some cases, but by using
5895 // RFind instead of Find, we make it O(N) in the most common case,
5896 // which is appending content.
5898 // Find the line that contains the previous sibling
5899 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
5900 prevSibLine
, mFrames
.LastChild(),
5901 &prevSiblingIndex
)) {
5902 // Not in mLines - try overflow lines.
5903 FrameLines
* overflowLines
= GetOverflowLines();
5905 if (overflowLines
) {
5906 prevSibLine
= overflowLines
->mLines
.end();
5907 prevSiblingIndex
= -1;
5908 found
= nsLineBox::RFindLineContaining(
5909 aPrevSibling
, overflowLines
->mLines
.begin(), prevSibLine
,
5910 overflowLines
->mFrames
.LastChild(), &prevSiblingIndex
);
5912 if (MOZ_LIKELY(found
)) {
5913 lineList
= &overflowLines
->mLines
;
5914 frames
= &overflowLines
->mFrames
;
5916 // Note: defensive code! RFindLineContaining must not return
5917 // false in this case, so if it does...
5918 MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
5919 aPrevSibling
= nullptr;
5920 prevSibLine
= lineList
->end();
5926 // Find the frame following aPrevSibling so that we can join up the
5927 // two lists of frames.
5929 // Split line containing aPrevSibling in two if the insertion
5930 // point is somewhere in the middle of the line.
5931 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
5933 // Split the line in two where the frame(s) are being inserted.
5935 NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
5936 lineList
->after_insert(prevSibLine
, line
);
5937 // Mark prevSibLine dirty and as needing textrun invalidation, since
5938 // we may be breaking up text in the line. Its previous line may also
5939 // need to be invalidated because it may be able to pull some text up.
5940 MarkLineDirty(prevSibLine
, lineList
);
5941 // The new line will also need its textruns recomputed because of the
5944 line
->SetInvalidateTextRuns(true);
5946 } else if (!lineList
->empty()) {
5947 lineList
->front()->MarkDirty();
5948 lineList
->front()->SetInvalidateTextRuns(true);
5950 const nsFrameList::Slice
& newFrames
=
5951 frames
->InsertFrames(nullptr, aPrevSibling
, std::move(aFrameList
));
5953 // Walk through the new frames being added and update the line data
5954 // structures to fit.
5955 for (nsIFrame
* newFrame
: newFrames
) {
5956 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
5957 "Unexpected aPrevSibling");
5959 !newFrame
->IsPlaceholderFrame() ||
5960 (!newFrame
->IsAbsolutelyPositioned() && !newFrame
->IsFloating()),
5961 "Placeholders should not float or be positioned");
5963 bool isBlock
= newFrame
->IsBlockOutside();
5965 // If the frame is a block frame, or if there is no previous line or if the
5966 // previous line is a block line we need to make a new line. We also make
5967 // a new line, as an optimization, in the two cases we know we'll need it:
5968 // if the previous line ended with a <br>, or if it has significant
5969 // whitespace and ended in a newline.
5970 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
5971 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
5972 // Create a new line for the frame and add its line to the line
5974 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
5975 if (prevSibLine
!= lineList
->end()) {
5976 // Append new line after prevSibLine
5977 lineList
->after_insert(prevSibLine
, line
);
5980 // New line is going before the other lines
5981 lineList
->push_front(line
);
5982 prevSibLine
= lineList
->begin();
5985 prevSibLine
->NoteFrameAdded(newFrame
);
5986 // We're adding inline content to prevSibLine, so we need to mark it
5987 // dirty, ensure its textruns are recomputed, and possibly do the same
5988 // to its previous line since that line may be able to pull content up.
5989 MarkLineDirty(prevSibLine
, lineList
);
5992 aPrevSibling
= newFrame
;
5996 MOZ_ASSERT(aFrameList
.IsEmpty());
6001 nsContainerFrame
* nsBlockFrame::GetRubyContentPseudoFrame() {
6002 auto* firstChild
= PrincipalChildList().FirstChild();
6003 if (firstChild
&& firstChild
->IsRubyFrame() &&
6004 firstChild
->Style()->GetPseudoType() ==
6005 mozilla::PseudoStyleType::blockRubyContent
) {
6006 return static_cast<nsContainerFrame
*>(firstChild
);
6011 nsContainerFrame
* nsBlockFrame::GetContentInsertionFrame() {
6012 // 'display:block ruby' use the inner (Ruby) frame for insertions.
6013 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6014 return rubyContentPseudoFrame
;
6019 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
6020 nsTArray
<OwnedAnonBox
>& aResult
) {
6021 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6022 aResult
.AppendElement(OwnedAnonBox(rubyContentPseudoFrame
));
6026 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
) {
6027 // Find which line contains the float, so we can update
6029 for (auto& line
: Lines()) {
6030 if (line
.IsInline() && line
.RemoveFloat(aFloat
)) {
6036 void nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
6038 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
6039 // frame list properties.
6040 if (!mFloats
.ContainsFrame(aFloat
)) {
6042 (GetOverflowOutOfFlows() &&
6043 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
6044 (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat
)),
6045 "aFloat is not our child or on an unexpected frame list");
6049 if (mFloats
.StartRemoveFrame(aFloat
)) {
6053 nsFrameList
* list
= GetPushedFloats();
6054 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
6056 // XXXmats not yet - need to investigate BlockReflowState::mPushedFloats
6057 // first so we don't leave it pointing to a deleted list.
6058 if (list
->IsEmpty()) {
6059 delete RemovePushedFloats();
6066 nsAutoOOFFrameList
oofs(this);
6067 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
6073 void nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame
* aFrame
) {
6074 // The containing block is always the parent of aFrame.
6075 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
6077 // Remove aFrame from the appropriate list.
6078 if (aFrame
->IsAbsolutelyPositioned()) {
6079 // This also deletes the next-in-flows
6080 block
->GetAbsoluteContainingBlock()->RemoveFrame(
6081 block
, FrameChildListID::Absolute
, aFrame
);
6083 // First remove aFrame's next-in-flows.
6084 nsIFrame
* nif
= aFrame
->GetNextInFlow();
6086 nif
->GetParent()->DeleteNextInFlowChild(nif
, false);
6088 // Now remove aFrame from its child list and Destroy it.
6089 block
->RemoveFloatFromFloatCache(aFrame
);
6090 block
->RemoveFloat(aFrame
);
6096 * This helps us iterate over the list of all normal + overflow lines
6098 void nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
6099 nsLineList::iterator
* aStartIterator
,
6100 nsLineList::iterator
* aEndIterator
,
6101 bool* aInOverflowLines
,
6102 FrameLines
** aOverflowLines
) {
6103 if (*aIterator
== *aEndIterator
) {
6104 if (!*aInOverflowLines
) {
6105 // Try the overflow lines
6106 *aInOverflowLines
= true;
6107 FrameLines
* lines
= GetOverflowLines();
6109 *aStartIterator
= lines
->mLines
.begin();
6110 *aIterator
= *aStartIterator
;
6111 *aEndIterator
= lines
->mLines
.end();
6112 *aOverflowLines
= lines
;
6118 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6120 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
) {
6121 // This will assert if aLine isn't in mLines of aFrame:
6122 DebugOnly
<bool> check
= aLine
== mFrame
->LinesBegin();
6125 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6130 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
6131 : &aFrame
->mLines
) {}
6133 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6134 bool* aFoundValidLine
)
6135 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6136 mLine
= aFrame
->LinesBegin();
6137 *aFoundValidLine
= FindValidLine();
6140 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState
& aRestyleState
) {
6141 nsIFrame
* letterFrame
= GetFirstLetter();
6146 // Figure out what the right style parent is. This needs to match
6147 // nsCSSFrameConstructor::CreateLetterFrame.
6148 nsIFrame
* inFlowFrame
= letterFrame
;
6149 if (inFlowFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6150 inFlowFrame
= inFlowFrame
->GetPlaceholderFrame();
6152 nsIFrame
* styleParent
= CorrectStyleParentFrame(inFlowFrame
->GetParent(),
6153 PseudoStyleType::firstLetter
);
6154 ComputedStyle
* parentStyle
= styleParent
->Style();
6155 RefPtr
<ComputedStyle
> firstLetterStyle
=
6156 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
6157 *mContent
->AsElement(), PseudoStyleType::firstLetter
, parentStyle
);
6158 // Note that we don't need to worry about changehints for the continuation
6159 // styles: those will be handled by the styleParent already.
6160 RefPtr
<ComputedStyle
> continuationStyle
=
6161 aRestyleState
.StyleSet().ResolveStyleForFirstLetterContinuation(
6163 UpdateStyleOfOwnedChildFrame(letterFrame
, firstLetterStyle
, aRestyleState
,
6164 Some(continuationStyle
.get()));
6166 // We also want to update the style on the textframe inside the first-letter.
6167 // We don't need to compute a changehint for this, though, since any changes
6168 // to it are handled by the first-letter anyway.
6169 nsIFrame
* textFrame
= letterFrame
->PrincipalChildList().FirstChild();
6170 RefPtr
<ComputedStyle
> firstTextStyle
=
6171 aRestyleState
.StyleSet().ResolveStyleForText(textFrame
->GetContent(),
6173 textFrame
->SetComputedStyle(firstTextStyle
);
6175 // We don't need to update style for textFrame's continuations: it's already
6176 // set up to inherit from parentStyle, which is what we want.
6179 static nsIFrame
* FindChildContaining(nsBlockFrame
* aFrame
,
6180 nsIFrame
* aFindFrame
) {
6181 NS_ASSERTION(aFrame
, "must have frame");
6184 nsIFrame
* block
= aFrame
;
6186 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
6190 block
= block
->GetNextContinuation();
6195 if (!child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6198 aFindFrame
= child
->GetPlaceholderFrame();
6204 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6205 nsIFrame
* aFindFrame
,
6206 bool* aFoundValidLine
)
6207 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6208 *aFoundValidLine
= false;
6210 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
6215 LineIterator line_end
= aFrame
->LinesEnd();
6216 mLine
= aFrame
->LinesBegin();
6217 if (mLine
!= line_end
&& mLine
.next() == line_end
&&
6218 !aFrame
->HasOverflowLines()) {
6219 // The block has a single line - that must be it!
6220 *aFoundValidLine
= true;
6224 // Try to use the cursor if it exists, otherwise fall back to the first line
6225 if (nsLineBox
* const cursor
= aFrame
->GetLineCursorForQuery()) {
6227 // Perform a simultaneous forward and reverse search starting from the
6229 nsBlockFrame::LineIterator line
= aFrame
->LinesBeginFrom(cursor
);
6230 nsBlockFrame::ReverseLineIterator rline
= aFrame
->LinesRBeginFrom(cursor
);
6231 nsBlockFrame::ReverseLineIterator rline_end
= aFrame
->LinesREnd();
6232 // rline is positioned on the line containing 'cursor', so it's not
6233 // rline_end. So we can safely increment it (i.e. move it to one line
6234 // earlier) to start searching there.
6236 while (line
!= line_end
|| rline
!= rline_end
) {
6237 if (line
!= line_end
) {
6238 if (line
->Contains(child
)) {
6244 if (rline
!= rline_end
) {
6245 if (rline
->Contains(child
)) {
6252 if (mLine
!= line_end
) {
6253 *aFoundValidLine
= true;
6254 if (mLine
!= cursor
) {
6255 aFrame
->SetProperty(nsBlockFrame::LineCursorPropertyQuery(), mLine
);
6260 for (mLine
= aFrame
->LinesBegin(); mLine
!= line_end
; ++mLine
) {
6261 if (mLine
->Contains(child
)) {
6262 *aFoundValidLine
= true;
6267 // Didn't find the line
6268 MOZ_ASSERT(mLine
== line_end
, "mLine should be line_end at this point");
6270 // If we reach here, it means that we have not been able to find the
6271 // desired frame in our in-flow lines. So we should start looking at
6272 // our overflow lines. In order to do that, we set mLine to the end
6273 // iterator so that FindValidLine starts to look at overflow lines,
6276 if (!FindValidLine()) {
6281 if (mLine
->Contains(child
)) {
6282 *aFoundValidLine
= true;
6288 nsBlockFrame::LineIterator
nsBlockInFlowLineIterator::End() {
6289 return mLineList
->end();
6292 bool nsBlockInFlowLineIterator::IsLastLineInList() {
6293 LineIterator end
= End();
6294 return mLine
!= end
&& mLine
.next() == end
;
6297 bool nsBlockInFlowLineIterator::Next() {
6299 return FindValidLine();
6302 bool nsBlockInFlowLineIterator::Prev() {
6303 LineIterator begin
= mLineList
->begin();
6304 if (mLine
!= begin
) {
6308 bool currentlyInOverflowLines
= GetInOverflow();
6310 if (currentlyInOverflowLines
) {
6311 mLineList
= &mFrame
->mLines
;
6312 mLine
= mLineList
->end();
6313 if (mLine
!= mLineList
->begin()) {
6318 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
6322 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6323 if (overflowLines
) {
6324 mLineList
= &overflowLines
->mLines
;
6325 mLine
= mLineList
->end();
6326 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
6331 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6335 bool nsBlockInFlowLineIterator::FindValidLine() {
6336 LineIterator end
= mLineList
->end();
6340 bool currentlyInOverflowLines
= GetInOverflow();
6342 if (currentlyInOverflowLines
) {
6343 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
6347 mLineList
= &mFrame
->mLines
;
6348 mLine
= mLineList
->begin();
6349 if (mLine
!= mLineList
->end()) {
6353 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6354 if (overflowLines
) {
6355 mLineList
= &overflowLines
->mLines
;
6356 mLine
= mLineList
->begin();
6357 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
6361 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6365 // This function removes aDeletedFrame and all its continuations. It
6366 // is optimized for deleting a whole series of frames. The easy
6367 // implementation would invoke itself recursively on
6368 // aDeletedFrame->GetNextContinuation, then locate the line containing
6369 // aDeletedFrame and remove aDeletedFrame from that line. But here we
6370 // start by locating aDeletedFrame and then scanning from that point
6371 // on looking for continuations.
6372 void nsBlockFrame::DoRemoveFrameInternal(nsIFrame
* aDeletedFrame
,
6374 PostDestroyData
& aPostDestroyData
) {
6375 // Clear our line cursor, since our lines may change.
6378 if (aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6379 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6380 if (!aDeletedFrame
->GetPrevInFlow()) {
6381 NS_ASSERTION(aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6382 "Expected out-of-flow frame");
6383 DoRemoveOutOfFlowFrame(aDeletedFrame
);
6385 nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame
,
6386 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
6391 // Find the line that contains deletedFrame
6392 nsLineList::iterator line_start
= mLines
.begin(), line_end
= mLines
.end();
6393 nsLineList::iterator line
= line_start
;
6394 FrameLines
* overflowLines
= nullptr;
6395 bool searchingOverflowList
= false;
6396 // Make sure we look in the overflow lines even if the normal line
6398 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6400 while (line
!= line_end
) {
6401 if (line
->Contains(aDeletedFrame
)) {
6405 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6409 if (line
== line_end
) {
6410 NS_ERROR("can't find deleted frame in lines");
6414 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6415 if (line
!= line_start
) {
6416 line
.prev()->MarkDirty();
6417 line
.prev()->SetInvalidateTextRuns(true);
6418 } else if (searchingOverflowList
&& !mLines
.empty()) {
6419 mLines
.back()->MarkDirty();
6420 mLines
.back()->SetInvalidateTextRuns(true);
6424 while (line
!= line_end
&& aDeletedFrame
) {
6425 MOZ_ASSERT(this == aDeletedFrame
->GetParent(), "messed up delete code");
6426 MOZ_ASSERT(line
->Contains(aDeletedFrame
), "frame not in line");
6428 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6430 line
->SetInvalidateTextRuns(true);
6433 // If the frame being deleted is the last one on the line then
6434 // optimize away the line->Contains(next-in-flow) call below.
6435 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
6436 if (!isLastFrameOnLine
) {
6437 LineIterator next
= line
.next();
6438 nsIFrame
* lastFrame
=
6440 ? next
->mFirstChild
->GetPrevSibling()
6441 : (searchingOverflowList
? overflowLines
->mFrames
.LastChild()
6442 : mFrames
.LastChild());
6443 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
6444 "unexpected line frames");
6445 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
6448 // Remove aDeletedFrame from the line
6449 if (line
->mFirstChild
== aDeletedFrame
) {
6450 // We should be setting this to null if aDeletedFrame
6451 // is the only frame on the line. HOWEVER in that case
6452 // we will be removing the line anyway, see below.
6453 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
6456 // Hmm, this won't do anything if we're removing a frame in the first
6457 // overflow line... Hopefully doesn't matter
6459 if (line
!= line_end
&& !line
->IsBlock()) {
6460 // Since we just removed a frame that follows some inline
6461 // frames, we need to reflow the previous line.
6466 // Take aDeletedFrame out of the sibling list. Note that
6467 // prevSibling will only be nullptr when we are deleting the very
6468 // first frame in the main or overflow list.
6469 if (searchingOverflowList
) {
6470 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
6472 mFrames
.RemoveFrame(aDeletedFrame
);
6475 // Update the child count of the line to be accurate
6476 line
->NoteFrameRemoved(aDeletedFrame
);
6478 // Destroy frame; capture its next continuation first in case we need
6479 // to destroy that too.
6480 nsIFrame
* deletedNextContinuation
=
6481 (aFlags
& REMOVE_FIXED_CONTINUATIONS
)
6482 ? aDeletedFrame
->GetNextContinuation()
6483 : aDeletedFrame
->GetNextInFlow();
6484 #ifdef NOISY_REMOVE_FRAME
6485 printf("DoRemoveFrame: %s line=%p frame=",
6486 searchingOverflowList
? "overflow" : "normal", line
.get());
6487 aDeletedFrame
->ListTag(stdout
);
6488 printf(" prevSibling=%p deletedNextContinuation=%p\n",
6489 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
6492 // If next-in-flow is an overflow container, must remove it first.
6493 if (deletedNextContinuation
&& deletedNextContinuation
->HasAnyStateBits(
6494 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6495 deletedNextContinuation
->GetParent()->DeleteNextInFlowChild(
6496 deletedNextContinuation
, false);
6497 deletedNextContinuation
= nullptr;
6500 aDeletedFrame
->DestroyFrom(aDeletedFrame
, aPostDestroyData
);
6501 aDeletedFrame
= deletedNextContinuation
;
6503 bool haveAdvancedToNextLine
= false;
6504 // If line is empty, remove it now.
6505 if (0 == line
->GetChildCount()) {
6506 #ifdef NOISY_REMOVE_FRAME
6507 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6508 searchingOverflowList
? "overflow" : "normal", line
.get());
6510 nsLineBox
* cur
= line
;
6511 if (!searchingOverflowList
) {
6512 line
= mLines
.erase(line
);
6513 // Invalidate the space taken up by the line.
6514 // XXX We need to do this if we're removing a frame as a result of
6515 // a call to RemoveFrame(), but we may not need to do this in all
6517 #ifdef NOISY_BLOCK_INVALIDATE
6518 nsRect
inkOverflow(cur
->InkOverflowRect());
6519 printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow
.x
,
6520 inkOverflow
.y
, inkOverflow
.width
, inkOverflow
.height
);
6523 line
= overflowLines
->mLines
.erase(line
);
6524 if (overflowLines
->mLines
.empty()) {
6525 DestroyOverflowLines();
6526 overflowLines
= nullptr;
6527 // We just invalidated our iterators. Since we were in
6528 // the overflow lines list, which is now empty, set them
6529 // so we're at the end of the regular line list.
6530 line_start
= mLines
.begin();
6531 line_end
= mLines
.end();
6537 // If we're removing a line, ReflowDirtyLines isn't going to
6538 // know that it needs to slide lines unless something is marked
6539 // dirty. So mark the previous margin of the next line dirty if
6541 if (line
!= line_end
) {
6542 line
->MarkPreviousMarginDirty();
6544 haveAdvancedToNextLine
= true;
6546 // Make the line that just lost a frame dirty, and advance to
6548 if (!deletedNextContinuation
|| isLastFrameOnLine
||
6549 !line
->Contains(deletedNextContinuation
)) {
6552 haveAdvancedToNextLine
= true;
6556 if (deletedNextContinuation
) {
6557 // See if we should keep looking in the current flow's line list.
6558 if (deletedNextContinuation
->GetParent() != this) {
6559 // The deceased frames continuation is not a child of the
6560 // current block. So break out of the loop so that we advance
6561 // to the next parent.
6563 // If we have a continuation in a different block then all bets are
6564 // off regarding whether we are deleting frames without actual content,
6565 // so don't propagate FRAMES_ARE_EMPTY any further.
6566 aFlags
&= ~FRAMES_ARE_EMPTY
;
6570 // If we advanced to the next line then check if we should switch to the
6571 // overflow line list.
6572 if (haveAdvancedToNextLine
) {
6573 if (line
!= line_end
&& !searchingOverflowList
&&
6574 !line
->Contains(deletedNextContinuation
)) {
6575 // We have advanced to the next *normal* line but the next-in-flow
6576 // is not there - force a switch to the overflow line list.
6580 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6582 #ifdef NOISY_REMOVE_FRAME
6583 printf("DoRemoveFrame: now on %s line=%p\n",
6584 searchingOverflowList
? "overflow" : "normal", line
.get());
6590 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
6591 line
.next()->MarkDirty();
6592 line
.next()->SetInvalidateTextRuns(true);
6597 VerifyOverflowSituation();
6600 // Advance to next flow block if the frame has more continuations.
6601 if (!aDeletedFrame
) {
6604 nsBlockFrame
* nextBlock
= do_QueryFrame(aDeletedFrame
->GetParent());
6605 NS_ASSERTION(nextBlock
, "Our child's continuation's parent is not a block?");
6606 uint32_t flags
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
);
6607 nextBlock
->DoRemoveFrameInternal(aDeletedFrame
, flags
, aPostDestroyData
);
6610 static bool FindBlockLineFor(nsIFrame
* aChild
, nsLineList::iterator aBegin
,
6611 nsLineList::iterator aEnd
,
6612 nsLineList::iterator
* aResult
) {
6613 MOZ_ASSERT(aChild
->IsBlockOutside());
6614 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6615 MOZ_ASSERT(line
->GetChildCount() > 0);
6616 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
6617 MOZ_ASSERT(line
->GetChildCount() == 1);
6625 static bool FindInlineLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6626 nsLineList::iterator aBegin
,
6627 nsLineList::iterator aEnd
,
6628 nsLineList::iterator
* aResult
) {
6629 MOZ_ASSERT(!aChild
->IsBlockOutside());
6630 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6631 MOZ_ASSERT(line
->GetChildCount() > 0);
6632 if (!line
->IsBlock()) {
6633 // Optimize by comparing the line's last child first.
6634 nsLineList::iterator next
= line
.next();
6635 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
6636 : next
->mFirstChild
->GetPrevSibling()) ||
6637 line
->Contains(aChild
)) {
6646 static bool FindLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6647 nsLineList::iterator aBegin
, nsLineList::iterator aEnd
,
6648 nsLineList::iterator
* aResult
) {
6649 return aChild
->IsBlockOutside()
6650 ? FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
)
6651 : FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
6654 void nsBlockFrame::StealFrame(nsIFrame
* aChild
) {
6655 MOZ_ASSERT(aChild
->GetParent() == this);
6657 if (aChild
->IsFloating()) {
6658 RemoveFloat(aChild
);
6662 if (MaybeStealOverflowContainerFrame(aChild
)) {
6666 MOZ_ASSERT(!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
6668 nsLineList::iterator line
;
6669 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
6670 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
6672 FrameLines
* overflowLines
= GetOverflowLines();
6673 DebugOnly
<bool> found
;
6674 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
6675 overflowLines
->mLines
.begin(),
6676 overflowLines
->mLines
.end(), &line
);
6677 MOZ_ASSERT(found
, "Why can't we find aChild in our overflow lines?");
6678 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
6679 overflowLines
->mLines
);
6680 if (overflowLines
->mLines
.empty()) {
6681 DestroyOverflowLines();
6686 void nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
,
6687 nsLineList::iterator aLine
,
6688 nsFrameList
& aFrameList
,
6689 nsLineList
& aLineList
) {
6690 aFrameList
.RemoveFrame(aChild
);
6691 if (aChild
== aLine
->mFirstChild
) {
6692 aLine
->mFirstChild
= aChild
->GetNextSibling();
6694 aLine
->NoteFrameRemoved(aChild
);
6695 if (aLine
->GetChildCount() > 0) {
6698 // The line became empty - destroy it.
6699 nsLineBox
* lineBox
= aLine
;
6700 aLine
= aLineList
.erase(aLine
);
6701 if (aLine
!= aLineList
.end()) {
6702 aLine
->MarkPreviousMarginDirty();
6704 FreeLineBox(lineBox
);
6708 void nsBlockFrame::DeleteNextInFlowChild(nsIFrame
* aNextInFlow
,
6709 bool aDeletingEmptyFrames
) {
6710 MOZ_ASSERT(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
6712 if (aNextInFlow
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6713 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6714 nsContainerFrame::DeleteNextInFlowChild(aNextInFlow
, aDeletingEmptyFrames
);
6717 if (aDeletingEmptyFrames
) {
6718 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
6721 DoRemoveFrame(aNextInFlow
, aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
6725 const nsStyleText
* nsBlockFrame::StyleTextForLineLayout() {
6726 // Return the pointer to an unmodified style text
6730 void nsBlockFrame::ReflowFloat(BlockReflowState
& aState
, ReflowInput
& aFloatRI
,
6732 nsReflowStatus
& aReflowStatus
) {
6733 MOZ_ASSERT(aReflowStatus
.IsEmpty(),
6734 "Caller should pass a fresh reflow status!");
6735 MOZ_ASSERT(aFloat
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6736 "aFloat must be an out-of-flow frame");
6738 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
6740 // Setup a block reflow context to reflow the float.
6741 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
6743 nsIFrame
* clearanceFrame
= nullptr;
6745 nsCollapsingMargin margin
;
6746 bool mayNeedRetry
= false;
6747 aFloatRI
.mDiscoveredClearance
= nullptr;
6748 // Only first in flow gets a block-start margin.
6749 if (!aFloat
->GetPrevInFlow()) {
6750 brc
.ComputeCollapsedBStartMargin(aFloatRI
, &margin
, clearanceFrame
,
6753 if (mayNeedRetry
&& !clearanceFrame
) {
6754 aFloatRI
.mDiscoveredClearance
= &clearanceFrame
;
6755 // We don't need to push the float manager state because the the block
6756 // has its own float manager that will be destroyed and recreated
6760 // When reflowing a float, aSpace argument doesn't matter because we pass
6761 // nullptr to aLine and we don't call nsBlockReflowContext::PlaceBlock()
6763 brc
.ReflowBlock(LogicalRect(wm
), true, margin
, 0, nullptr, aFloatRI
,
6764 aReflowStatus
, aState
);
6765 } while (clearanceFrame
);
6767 if (aFloat
->IsLetterFrame()) {
6768 // We never split floating first letters; an incomplete status for such
6769 // frames simply means that there is more content to be reflowed on the
6771 if (aReflowStatus
.IsIncomplete()) {
6772 aReflowStatus
.Reset();
6776 NS_ASSERTION(aReflowStatus
.IsFullyComplete() ||
6777 aFloatRI
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
6778 "The status can only be incomplete or overflow-incomplete if "
6779 "the available block-size is constrained!");
6781 if (aReflowStatus
.NextInFlowNeedsReflow()) {
6782 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
6785 const ReflowOutput
& metrics
= brc
.GetMetrics();
6787 // Set the rect, make sure the view is properly sized and positioned,
6788 // and tell the frame we're done reflowing it
6789 // XXXldb This seems like the wrong place to be doing this -- shouldn't
6790 // we be doing this in BlockReflowState::FlowAndPlaceFloat after
6791 // we've positioned the float, and shouldn't we be doing the equivalent
6792 // of |PlaceFrameView| here?
6793 WritingMode metricsWM
= metrics
.GetWritingMode();
6794 aFloat
->SetSize(metricsWM
, metrics
.Size(metricsWM
));
6795 if (aFloat
->HasView()) {
6796 nsContainerFrame::SyncFrameViewAfterReflow(
6797 aState
.mPresContext
, aFloat
, aFloat
->GetView(), metrics
.InkOverflow(),
6798 ReflowChildFlags::NoMoveView
);
6800 aFloat
->DidReflow(aState
.mPresContext
, &aFloatRI
);
6803 StyleClear
nsBlockFrame::FindTrailingClear() {
6804 for (nsBlockFrame
* b
= this; b
;
6805 b
= static_cast<nsBlockFrame
*>(b
->GetPrevInFlow())) {
6806 auto endLine
= b
->LinesRBegin();
6807 if (endLine
!= b
->LinesREnd()) {
6808 return endLine
->FloatClearTypeAfter();
6811 return StyleClear::None
;
6814 void nsBlockFrame::ReflowPushedFloats(BlockReflowState
& aState
,
6815 OverflowAreas
& aOverflowAreas
) {
6816 // Pushed floats live at the start of our float list; see comment
6817 // above nsBlockFrame::DrainPushedFloats.
6818 nsIFrame
* f
= mFloats
.FirstChild();
6819 nsIFrame
* prev
= nullptr;
6820 while (f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
6821 MOZ_ASSERT(prev
== f
->GetPrevSibling());
6822 // When we push a first-continuation float in a non-initial reflow,
6823 // it's possible that we end up with two continuations with the same
6824 // parent. This happens if, on the previous reflow of the block or
6825 // a previous reflow of the line containing the block, the float was
6826 // split between continuations A and B of the parent, but on the
6827 // current reflow, none of the float can fit in A.
6829 // When this happens, we might even have the two continuations
6830 // out-of-order due to the management of the pushed floats. In
6831 // particular, if the float's placeholder was in a pushed line that
6832 // we reflowed before it was pushed, and we split the float during
6833 // that reflow, we might have the continuation of the float before
6834 // the float itself. (In the general case, however, it's correct
6835 // for floats in the pushed floats list to come before floats
6836 // anchored in pushed lines; however, in this case it's wrong. We
6837 // should probably find a way to fix it somehow, since it leads to
6838 // incorrect layout in some cases.)
6840 // When we have these out-of-order continuations, we might hit the
6841 // next-continuation before the previous-continuation. When that
6842 // happens, just push it. When we reflow the next continuation,
6843 // we'll either pull all of its content back and destroy it (by
6844 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
6845 // pull it out of its current position and push it again (and
6846 // potentially repeat this cycle for the next continuation, although
6847 // hopefully then they'll be in the right order).
6849 // We should also need this code for the in-order case if the first
6850 // continuation of a float gets moved across more than one
6851 // continuation of the containing block. In this case we'd manage
6852 // to push the second continuation without this check, but not the
6854 nsIFrame
* prevContinuation
= f
->GetPrevContinuation();
6855 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
6856 mFloats
.RemoveFrame(f
);
6857 aState
.AppendPushedFloatChain(f
);
6858 f
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
6862 // Always call FlowAndPlaceFloat; we might need to place this float if it
6863 // didn't belong to this block the last time it was reflowed. Note that if
6864 // the float doesn't get placed, we don't consider its overflow areas.
6865 // (Not-getting-placed means it didn't fit and we pushed it instead of
6866 // placing it, and its position could be stale.)
6867 if (aState
.FlowAndPlaceFloat(f
) ==
6868 BlockReflowState::PlaceFloatResult::Placed
) {
6869 ConsiderChildOverflow(aOverflowAreas
, f
);
6872 nsIFrame
* next
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
6874 // We didn't push |f| so its next-sibling is next.
6875 next
= f
->GetNextSibling();
6877 } // else: we did push |f| so |prev|'s new next-sibling is next.
6881 // If there are pushed or split floats, then we may need to continue BR
6883 if (auto [bCoord
, result
] = aState
.ClearFloats(0, StyleClear::Both
);
6884 result
!= ClearFloatsResult::BCoordNoChange
) {
6886 if (auto* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow())) {
6887 aState
.mTrailingClearFromPIF
= prevBlock
->FindTrailingClear();
6892 void nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
, WritingMode aWM
,
6893 const nsSize
& aContainerSize
) {
6894 // Recover our own floats
6895 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
6896 // belong to our next-in-flow
6897 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
;
6898 f
= f
->GetNextSibling()) {
6899 LogicalRect region
= nsFloatManager::GetRegionFor(aWM
, f
, aContainerSize
);
6900 aFloatManager
.AddFloat(f
, region
, aWM
, aContainerSize
);
6901 if (!stop
&& f
->GetNextInFlow()) {
6902 stop
= f
->GetNextInFlow();
6906 // Recurse into our overflow container children
6908 GetChildList(FrameChildListID::OverflowContainers
).FirstChild();
6909 oc
; oc
= oc
->GetNextSibling()) {
6910 RecoverFloatsFor(oc
, aFloatManager
, aWM
, aContainerSize
);
6913 // Recurse into our normal children
6914 for (const auto& line
: Lines()) {
6915 if (line
.IsBlock()) {
6916 RecoverFloatsFor(line
.mFirstChild
, aFloatManager
, aWM
, aContainerSize
);
6921 void nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
6922 nsFloatManager
& aFloatManager
,
6924 const nsSize
& aContainerSize
) {
6925 MOZ_ASSERT(aFrame
, "null frame");
6927 // Only blocks have floats
6928 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
6929 // Don't recover any state inside a block that has its own float manager
6930 // (we don't currently have any blocks like this, though, thanks to our
6931 // use of extra frames for 'overflow')
6932 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
6933 // If the element is relatively positioned, then adjust x and y
6934 // accordingly so that we consider relatively positioned frames
6935 // at their original position.
6937 LogicalRect
rect(aWM
, block
->GetNormalRect(), aContainerSize
);
6938 nscoord lineLeft
= rect
.LineLeft(aWM
, aContainerSize
);
6939 nscoord blockStart
= rect
.BStart(aWM
);
6940 aFloatManager
.Translate(lineLeft
, blockStart
);
6941 block
->RecoverFloats(aFloatManager
, aWM
, aContainerSize
);
6942 aFloatManager
.Translate(-lineLeft
, -blockStart
);
6946 bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
6947 if (!mFloats
.IsEmpty()) {
6948 // If we have pushed floats, then they should be at the beginning of our
6950 if (mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
6956 // Double-check the above assertion that pushed floats should be at the
6957 // beginning of our floats list.
6958 for (nsIFrame
* f
: mFloats
) {
6959 NS_ASSERTION(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
6960 "pushed floats must be at the beginning of the float list");
6964 // We may have a pending push of pushed floats too:
6965 if (HasPushedFloats()) {
6966 // XXX we can return 'true' here once we make HasPushedFloats
6967 // not lie. (see nsBlockFrame::RemoveFloat)
6968 auto* pushedFloats
= GetPushedFloats();
6969 return pushedFloats
&& !pushedFloats
->IsEmpty();
6974 //////////////////////////////////////////////////////////////////////
6975 // Painting, event handling
6978 static void ComputeInkOverflowArea(nsLineList
& aLines
, nscoord aWidth
,
6979 nscoord aHeight
, nsRect
& aResult
) {
6980 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
6981 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
6982 line
!= line_end
; ++line
) {
6983 // Compute min and max x/y values for the reflowed frame's
6985 nsRect
inkOverflow(line
->InkOverflowRect());
6986 nscoord x
= inkOverflow
.x
;
6987 nscoord y
= inkOverflow
.y
;
6988 nscoord xmost
= x
+ inkOverflow
.width
;
6989 nscoord ymost
= y
+ inkOverflow
.height
;
7006 aResult
.width
= xb
- xa
;
7007 aResult
.height
= yb
- ya
;
7012 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
7013 if (nsBlockFrame::gNoisyDamageRepair
) {
7014 nsIFrame::IndentBy(stdout
, aDepth
+ 1);
7015 nsRect lineArea
= aLine
->InkOverflowRect();
7016 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7017 aDrawn
? "draw" : "skip", static_cast<void*>(aLine
), aLine
->IStart(),
7018 aLine
->BStart(), aLine
->ISize(), aLine
->BSize(), lineArea
.x
,
7019 lineArea
.y
, lineArea
.width
, lineArea
.height
);
7024 static void DisplayLine(nsDisplayListBuilder
* aBuilder
,
7025 nsBlockFrame::LineIterator
& aLine
,
7026 const bool aLineInLine
, const nsDisplayListSet
& aLists
,
7027 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
,
7028 uint32_t aLineNumberForTextOverflow
, int32_t aDepth
,
7029 int32_t& aDrawnLines
) {
7031 if (nsBlockFrame::gLamePaintMetrics
) {
7034 const bool intersect
=
7035 aLine
->InkOverflowRect().Intersects(aBuilder
->GetDirtyRect());
7036 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
7039 // Collect our line's display items in a temporary nsDisplayListCollection,
7040 // so that we can apply any "text-overflow" clipping to the entire collection
7041 // without affecting previous lines.
7042 nsDisplayListCollection
collection(aBuilder
);
7044 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
7045 // Inline-level child backgrounds go on the regular child content list.
7046 nsDisplayListSet
childLists(
7048 aLineInLine
? collection
.Content() : collection
.BlockBorderBackgrounds());
7052 ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline
)
7053 : nsIFrame::DisplayChildFlags();
7055 nsIFrame
* kid
= aLine
->mFirstChild
;
7056 int32_t n
= aLine
->GetChildCount();
7058 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, childLists
, flags
);
7059 kid
= kid
->GetNextSibling();
7062 if (aTextOverflow
&& aLineInLine
) {
7063 aTextOverflow
->ProcessLine(collection
, aLine
.get(),
7064 aLineNumberForTextOverflow
);
7067 collection
.MoveTo(aLists
);
7070 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
7071 const nsDisplayListSet
& aLists
) {
7072 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
7075 if (gNoisyDamageRepair
) {
7076 nsRect dirty
= aBuilder
->GetDirtyRect();
7079 ::ComputeInkOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
7080 nsIFrame::IndentBy(stdout
, depth
);
7082 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7083 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
, dirty
.x
, dirty
.y
,
7084 dirty
.width
, dirty
.height
, ca
.x
, ca
.y
, ca
.width
, ca
.height
);
7086 PRTime start
= 0; // Initialize these variables to silence the compiler.
7087 if (gLamePaintMetrics
) {
7093 // TODO(heycam): Should we boost the load priority of any shape-outside
7094 // images using CATEGORY_DISPLAY, now that this block is being displayed?
7095 // We don't have a float manager here.
7097 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
7099 if (GetPrevInFlow()) {
7100 DisplayOverflowContainers(aBuilder
, aLists
);
7101 for (nsIFrame
* f
: mFloats
) {
7102 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7103 BuildDisplayListForChild(aBuilder
, f
, aLists
);
7108 aBuilder
->MarkFramesForDisplayList(this, mFloats
);
7110 if (HasOutsideMarker()) {
7111 // Display outside ::marker manually.
7112 BuildDisplayListForChild(aBuilder
, GetOutsideMarker(), aLists
);
7115 // Prepare for text-overflow processing.
7116 Maybe
<TextOverflow
> textOverflow
=
7117 TextOverflow::WillProcessLines(aBuilder
, this);
7119 const bool hasDescendantPlaceHolders
=
7120 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7121 ForceDescendIntoIfVisible() || aBuilder
->GetIncludeAllOutOfFlows();
7123 const auto ShouldDescendIntoLine
= [&](const nsRect
& aLineArea
) -> bool {
7124 // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
7125 // some frame trees, building display list for child lines can change it.
7127 const bool descendAlways
=
7128 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7129 aBuilder
->GetIncludeAllOutOfFlows();
7131 return descendAlways
|| aLineArea
.Intersects(aBuilder
->GetDirtyRect()) ||
7132 (ForceDescendIntoIfVisible() &&
7133 aLineArea
.Intersects(aBuilder
->GetVisibleRect()));
7136 Maybe
<nscolor
> backplateColor
;
7138 // We'll try to draw an accessibility backplate behind text (to ensure it's
7139 // readable over any possible background-images), if all of the following
7141 // (A) the backplate feature is preffed on
7142 // (B) we are not honoring the document colors
7143 if (StaticPrefs::browser_display_permit_backplate() &&
7144 PresContext()->ForcingColors() && !IsComboboxControlFrame()) {
7145 backplateColor
.emplace(GetBackplateColor(this));
7148 // Don't use the line cursor if we might have a descendant placeholder ...
7149 // it might skip lines that contain placeholders but don't themselves
7150 // intersect with the dirty area.
7151 // In particular, we really want to check ShouldDescendIntoFrame()
7152 // on all our child frames, but that might be expensive. So we
7153 // approximate it by checking it on |this|; if it's true for any
7154 // frame in our child list, it's also true for |this|.
7155 // Also skip the cursor if we're creating text overflow markers,
7156 // since we need to know what line number we're up to in order
7157 // to generate unique display item keys.
7158 // Lastly, the cursor should be skipped if we're drawing
7159 // backplates behind text. When backplating we consider consecutive
7160 // runs of text as a whole, which requires we iterate through all lines
7161 // to find our backplate size.
7163 (hasDescendantPlaceHolders
|| textOverflow
.isSome() || backplateColor
)
7165 : GetFirstLineContaining(aBuilder
->GetDirtyRect().y
);
7166 LineIterator line_end
= LinesEnd();
7168 TextOverflow
* textOverflowPtr
= textOverflow
.ptrOr(nullptr);
7171 for (LineIterator line
= mLines
.begin(cursor
); line
!= line_end
; ++line
) {
7172 const nsRect lineArea
= line
->InkOverflowRect();
7173 if (!lineArea
.IsEmpty()) {
7174 // Because we have a cursor, the combinedArea.ys are non-decreasing.
7175 // Once we've passed aDirtyRect.YMost(), we can never see it again.
7176 if (lineArea
.y
>= aBuilder
->GetDirtyRect().YMost()) {
7179 MOZ_ASSERT(textOverflow
.isNothing());
7181 if (ShouldDescendIntoLine(lineArea
)) {
7182 DisplayLine(aBuilder
, line
, line
->IsInline(), aLists
, this, nullptr,
7183 0, depth
, drawnLines
);
7188 bool nonDecreasingYs
= true;
7189 uint32_t lineCount
= 0;
7190 nscoord lastY
= INT32_MIN
;
7191 nscoord lastYMost
= INT32_MIN
;
7193 // A frame's display list cannot contain more than one copy of a
7194 // given display item unless the items are uniquely identifiable.
7195 // Because backplate occasionally requires multiple
7196 // SolidColor items, we use an index (backplateIndex) to maintain
7197 // uniqueness among them. Note this is a mapping of index to
7198 // item, and the mapping is stable even if the dirty rect changes.
7199 uint16_t backplateIndex
= 0;
7200 nsRect curBackplateArea
;
7202 auto AddBackplate
= [&]() {
7203 aLists
.BorderBackground()->AppendNewToTopWithIndex
<nsDisplaySolidColor
>(
7204 aBuilder
, this, backplateIndex
, curBackplateArea
,
7205 backplateColor
.value());
7208 for (LineIterator line
= LinesBegin(); line
!= line_end
; ++line
) {
7209 const nsRect lineArea
= line
->InkOverflowRect();
7210 const bool lineInLine
= line
->IsInline();
7212 if ((lineInLine
&& textOverflowPtr
) || ShouldDescendIntoLine(lineArea
)) {
7213 DisplayLine(aBuilder
, line
, lineInLine
, aLists
, this, textOverflowPtr
,
7214 lineCount
, depth
, drawnLines
);
7217 if (!lineInLine
&& !curBackplateArea
.IsEmpty()) {
7218 // If we have encountered a non-inline line but were previously
7219 // forming a backplate, we should add the backplate to the display
7220 // list as-is and render future backplates disjointly.
7221 MOZ_ASSERT(backplateColor
,
7222 "if this master switch is off, curBackplateArea "
7223 "must be empty and we shouldn't get here");
7226 curBackplateArea
= nsRect();
7229 if (!lineArea
.IsEmpty()) {
7230 if (lineArea
.y
< lastY
|| lineArea
.YMost() < lastYMost
) {
7231 nonDecreasingYs
= false;
7234 lastYMost
= lineArea
.YMost();
7235 if (lineInLine
&& backplateColor
&& LineHasVisibleInlineContent(line
)) {
7236 nsRect lineBackplate
= GetLineTextArea(line
, aBuilder
) +
7237 aBuilder
->ToReferenceFrame(this);
7238 if (curBackplateArea
.IsEmpty()) {
7239 curBackplateArea
= lineBackplate
;
7241 curBackplateArea
.OrWith(lineBackplate
);
7248 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
7249 SetupLineCursorForDisplay();
7252 if (!curBackplateArea
.IsEmpty()) {
7257 if (textOverflow
.isSome()) {
7258 // Put any text-overflow:ellipsis markers on top of the non-positioned
7259 // content of the block's lines. (If we ever start sorting the Content()
7260 // list this will end up in the wrong place.)
7261 aLists
.Content()->AppendToTop(&textOverflow
->GetMarkers());
7265 if (gLamePaintMetrics
) {
7266 PRTime end
= PR_Now();
7268 int32_t numLines
= mLines
.size();
7272 PRTime lines
, deltaPerLine
, delta
;
7273 lines
= int64_t(numLines
);
7274 delta
= end
- start
;
7275 deltaPerLine
= delta
/ lines
;
7280 ": %" PRId64
" elapsed (%" PRId64
7281 " per line) lines=%d drawn=%d skip=%d",
7282 delta
, deltaPerLine
, numLines
, drawnLines
,
7283 numLines
- drawnLines
);
7284 printf("%s\n", buf
);
7289 #ifdef ACCESSIBILITY
7290 a11y::AccType
nsBlockFrame::AccessibleType() {
7291 if (IsTableCaption()) {
7292 return GetRect().IsEmpty() ? a11y::eNoType
: a11y::eHTMLCaptionType
;
7295 // block frame may be for <hr>
7296 if (mContent
->IsHTMLElement(nsGkAtoms::hr
)) {
7297 return a11y::eHTMLHRType
;
7300 if (!HasMarker() || !PresContext()) {
7301 // XXXsmaug What if we're in the shadow dom?
7302 if (!mContent
->GetParent()) {
7303 // Don't create accessible objects for the root content node, they are
7304 // redundant with the nsDocAccessible object created with the document
7306 return a11y::eNoType
;
7309 if (mContent
== mContent
->OwnerDoc()->GetBody()) {
7310 // Don't create accessible objects for the body, they are redundant with
7311 // the nsDocAccessible object created with the document node
7312 return a11y::eNoType
;
7315 // Not a list item with a ::marker, treat as normal HTML container.
7316 return a11y::eHyperTextType
;
7319 // Create special list item accessible since we have a ::marker.
7320 return a11y::eHTMLLiType
;
7324 void nsBlockFrame::SetupLineCursorForDisplay() {
7325 if (mLines
.empty() || HasProperty(LineCursorPropertyDisplay())) {
7329 SetProperty(LineCursorPropertyDisplay(), mLines
.front());
7330 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7333 void nsBlockFrame::SetupLineCursorForQuery() {
7334 if (mLines
.empty() || HasProperty(LineCursorPropertyQuery())) {
7338 SetProperty(LineCursorPropertyQuery(), mLines
.front());
7339 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7342 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
) {
7343 // Although this looks like a "querying" method, it is used by the
7344 // display-list building code, so uses the Display cursor.
7345 nsLineBox
* property
= GetLineCursorForDisplay();
7349 LineIterator cursor
= mLines
.begin(property
);
7350 nsRect cursorArea
= cursor
->InkOverflowRect();
7352 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
) &&
7353 cursor
!= mLines
.front()) {
7354 cursor
= cursor
.prev();
7355 cursorArea
= cursor
->InkOverflowRect();
7357 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
) &&
7358 cursor
!= mLines
.back()) {
7359 cursor
= cursor
.next();
7360 cursorArea
= cursor
->InkOverflowRect();
7363 if (cursor
.get() != property
) {
7364 SetProperty(LineCursorPropertyDisplay(), cursor
.get());
7367 return cursor
.get();
7371 void nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
) {
7372 // See if the child is absolutely positioned
7373 if (aChild
->IsAbsolutelyPositioned()) {
7375 } else if (aChild
== GetOutsideMarker()) {
7376 // The ::marker lives in the first line, unless the first line has
7377 // height 0 and there is a second line, in which case it lives
7378 // in the second line.
7379 LineIterator markerLine
= LinesBegin();
7380 if (markerLine
!= LinesEnd() && markerLine
->BSize() == 0 &&
7381 markerLine
!= mLines
.back()) {
7382 markerLine
= markerLine
.next();
7385 if (markerLine
!= LinesEnd()) {
7386 MarkLineDirty(markerLine
, &mLines
);
7388 // otherwise we have an empty line list, and ReflowDirtyLines
7389 // will handle reflowing the ::marker.
7391 // Note that we should go through our children to mark lines dirty
7392 // before the next reflow. Doing it now could make things O(N^2)
7393 // since finding the right line is O(N).
7394 // We don't need to worry about marking lines on the overflow list
7395 // as dirty; we're guaranteed to reflow them if we take them off the
7397 // However, we might have gotten a float, in which case we need to
7398 // reflow the line containing its placeholder. So find the
7399 // ancestor-or-self of the placeholder that's a child of the block,
7400 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
7401 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7402 // We need to take some care to handle the case where a float is in
7403 // a different continuation than its placeholder, including marking
7404 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7405 if (!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
7406 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7408 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
7409 nsIFrame
* thisFC
= FirstContinuation();
7410 nsIFrame
* placeholderPath
= aChild
->GetPlaceholderFrame();
7411 // SVG code sometimes sends FrameNeedsReflow notifications during
7412 // frame destruction, leading to null placeholders, but we're safe
7414 if (placeholderPath
) {
7416 nsIFrame
* parent
= placeholderPath
->GetParent();
7417 if (parent
->GetContent() == mContent
&&
7418 parent
->FirstContinuation() == thisFC
) {
7419 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7422 placeholderPath
= parent
;
7424 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
7429 nsContainerFrame::ChildIsDirty(aChild
);
7432 void nsBlockFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
7433 nsIFrame
* aPrevInFlow
) {
7434 // These are all the block specific frame bits, they are copied from
7435 // the prev-in-flow to a newly created next-in-flow, except for the
7436 // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
7437 constexpr nsFrameState NS_BLOCK_FLAGS_MASK
=
7438 NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
|
7439 NS_BLOCK_CLIP_PAGINATED_OVERFLOW
| NS_BLOCK_HAS_FIRST_LETTER_STYLE
|
7440 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7441 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7443 // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
7444 // by default. They should only be set on the first-in-flow.
7445 constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK
=
7446 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7447 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7450 // Copy over the inherited block frame bits from the prev-in-flow.
7451 RemoveStateBits(NS_BLOCK_FLAGS_MASK
);
7452 AddStateBits(aPrevInFlow
->GetStateBits() &
7453 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
7456 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
7459 aPrevInFlow
->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
7460 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
7463 // A display:flow-root box establishes a block formatting context.
7465 // If a box has a different writing-mode value than its containing block:
7467 // If the box is a block container, then it establishes a new block
7468 // formatting context.
7469 // (https://drafts.csswg.org/css-writing-modes/#block-flow)
7471 // If the box has contain: paint or contain:layout (or contain:strict),
7472 // then it should also establish a formatting context.
7474 // Per spec, a column-span always establishes a new block formatting context.
7475 if (StyleDisplay()->mDisplay
== mozilla::StyleDisplay::FlowRoot
||
7477 (GetWritingMode().GetBlockDir() !=
7478 GetParent()->GetWritingMode().GetBlockDir() ||
7479 GetWritingMode().IsVerticalSideways() !=
7480 GetParent()->GetWritingMode().IsVerticalSideways())) ||
7481 StyleDisplay()->IsContainPaint() || StyleDisplay()->IsContainLayout() ||
7483 AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7486 if (HasAllStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) {
7487 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
7491 void nsBlockFrame::SetInitialChildList(ChildListID aListID
,
7492 nsFrameList
&& aChildList
) {
7493 if (FrameChildListID::Float
== aListID
) {
7494 mFloats
= std::move(aChildList
);
7495 } else if (FrameChildListID::Principal
== aListID
) {
7497 // The only times a block that is an anonymous box is allowed to have a
7498 // first-letter frame are when it's the block inside a non-anonymous cell,
7499 // the block inside a fieldset, button or column set, or a scrolled content
7500 // block, except for <select>. Note that this means that blocks which are
7501 // the anonymous block in {ib} splits do NOT get first-letter frames.
7502 // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7504 auto pseudo
= Style()->GetPseudoType();
7505 bool haveFirstLetterStyle
=
7506 (pseudo
== PseudoStyleType::NotPseudo
||
7507 (pseudo
== PseudoStyleType::cellContent
&&
7508 !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7509 pseudo
== PseudoStyleType::fieldsetContent
||
7510 pseudo
== PseudoStyleType::buttonContent
||
7511 pseudo
== PseudoStyleType::columnContent
||
7512 (pseudo
== PseudoStyleType::scrolledContent
&&
7513 !GetParent()->IsListControlFrame()) ||
7514 pseudo
== PseudoStyleType::mozSVGText
) &&
7515 !IsComboboxControlFrame() && !IsFrameOfType(eMathML
) &&
7516 !IsColumnSetWrapperFrame() &&
7517 RefPtr
<ComputedStyle
>(GetFirstLetterStyle(PresContext())) != nullptr;
7518 NS_ASSERTION(haveFirstLetterStyle
==
7519 ((mState
& NS_BLOCK_HAS_FIRST_LETTER_STYLE
) != 0),
7520 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7523 AddFrames(std::move(aChildList
), nullptr, nullptr);
7525 nsContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
7529 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame
* aMarkerFrame
) {
7530 MOZ_ASSERT(aMarkerFrame
);
7531 MOZ_ASSERT(!HasAnyStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
|
7532 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
),
7533 "How can we have a ::marker frame already?");
7535 if (StyleList()->mListStylePosition
== NS_STYLE_LIST_STYLE_POSITION_INSIDE
) {
7536 SetProperty(InsideMarkerProperty(), aMarkerFrame
);
7537 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
);
7539 if (nsBlockFrame
* marker
= do_QueryFrame(aMarkerFrame
)) {
7540 // An outside ::marker needs to be an independent formatting context
7541 // to avoid being influenced by the float manager etc.
7542 marker
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7544 SetProperty(OutsideMarkerProperty(),
7545 new (PresShell()) nsFrameList(aMarkerFrame
, aMarkerFrame
));
7546 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
7550 bool nsBlockFrame::MarkerIsEmpty() const {
7551 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
7553 "should only care when we have an outside ::marker");
7554 nsIFrame
* marker
= GetMarker();
7555 const nsStyleList
* list
= marker
->StyleList();
7556 return marker
->StyleContent()->mContent
.IsNone() ||
7557 (list
->mCounterStyle
.IsNone() && list
->mListStyleImage
.IsNone() &&
7558 marker
->StyleContent()->ContentCount() == 0);
7561 void nsBlockFrame::ReflowOutsideMarker(nsIFrame
* aMarkerFrame
,
7562 BlockReflowState
& aState
,
7563 ReflowOutput
& aMetrics
,
7565 const ReflowInput
& ri
= aState
.mReflowInput
;
7567 WritingMode markerWM
= aMarkerFrame
->GetWritingMode();
7568 LogicalSize
availSize(markerWM
);
7569 // Make up an inline-size since it doesn't really matter (XXX).
7570 availSize
.ISize(markerWM
) = aState
.ContentISize();
7571 availSize
.BSize(markerWM
) = NS_UNCONSTRAINEDSIZE
;
7573 ReflowInput
reflowInput(aState
.mPresContext
, ri
, aMarkerFrame
, availSize
,
7574 Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap
});
7575 nsReflowStatus status
;
7576 aMarkerFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowInput
, status
);
7578 // Get the float available space using our saved state from before we
7579 // started reflowing the block, so that we ignore any floats inside
7581 // FIXME: aLineTop isn't actually set correctly by some callers, since
7582 // they reposition the line.
7583 LogicalRect floatAvailSpace
=
7585 .GetFloatAvailableSpaceWithState(aLineTop
, ShapeType::ShapeOutside
,
7586 &aState
.mFloatManagerStateBefore
)
7588 // FIXME (bug 25888): need to check the entire region that the first
7589 // line overlaps, not just the top pixel.
7591 // Place the ::marker now. We want to place the ::marker relative to the
7592 // border-box of the associated block (using the right/left margin of
7593 // the ::marker frame as separation). However, if a line box would be
7594 // displaced by floats that are *outside* the associated block, we
7595 // want to displace it by the same amount. That is, we act as though
7596 // the edge of the floats is the content-edge of the block, and place
7597 // the ::marker at a position offset from there by the block's padding,
7598 // the block's border, and the ::marker frame's margin.
7600 // IStart from floatAvailSpace gives us the content/float start edge
7601 // in the current writing mode. Then we subtract out the start
7602 // border/padding and the ::marker's width and margin to offset the position.
7603 WritingMode wm
= ri
.GetWritingMode();
7604 // Get the ::marker's margin, converted to our writing mode so that we can
7605 // combine it with other logical values here.
7606 LogicalMargin markerMargin
= reflowInput
.ComputedLogicalMargin(wm
);
7607 nscoord iStart
= floatAvailSpace
.IStart(wm
) -
7608 ri
.ComputedLogicalBorderPadding(wm
).IStart(wm
) -
7609 markerMargin
.IEnd(wm
) - aMetrics
.ISize(wm
);
7611 // Approximate the ::marker's position; vertical alignment will provide
7612 // the final vertical location. We pass our writing-mode here, because
7613 // it may be different from the ::marker frame's mode.
7614 nscoord bStart
= floatAvailSpace
.BStart(wm
);
7615 aMarkerFrame
->SetRect(
7617 LogicalRect(wm
, iStart
, bStart
, aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)),
7618 aState
.ContainerSize());
7619 aMarkerFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowInput
);
7622 // This is used to scan frames for any float placeholders, add their
7623 // floats to the list represented by aList, and remove the
7624 // floats from whatever list they might be in. We don't search descendants
7625 // that are float containing blocks. Floats that or not children of 'this'
7626 // are ignored (they are not added to aList).
7627 void nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
7628 bool aCollectSiblings
) {
7630 // Don't descend into float containing blocks.
7631 if (!aFrame
->IsFloatContainingBlock()) {
7632 nsIFrame
* outOfFlowFrame
=
7633 aFrame
->IsPlaceholderFrame()
7634 ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame
)
7636 while (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
7637 RemoveFloat(outOfFlowFrame
);
7638 // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
7639 // the PushedFloats list.
7640 outOfFlowFrame
->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
7641 aList
.AppendFrame(nullptr, outOfFlowFrame
);
7642 outOfFlowFrame
= outOfFlowFrame
->GetNextInFlow();
7643 // FIXME: By not pulling floats whose parent is one of our
7644 // later siblings, are we risking the pushed floats getting
7646 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
7649 DoCollectFloats(aFrame
->PrincipalChildList().FirstChild(), aList
, true);
7651 aFrame
->GetChildList(FrameChildListID::Overflow
).FirstChild(), aList
,
7654 if (!aCollectSiblings
) {
7657 aFrame
= aFrame
->GetNextSibling();
7661 void nsBlockFrame::CheckFloats(BlockReflowState
& aState
) {
7663 // If any line is still dirty, that must mean we're going to reflow this
7664 // block again soon (e.g. because we bailed out after noticing that
7665 // clearance was imposed), so don't worry if the floats are out of sync.
7666 bool anyLineDirty
= false;
7668 // Check that the float list is what we would have built
7669 AutoTArray
<nsIFrame
*, 8> lineFloats
;
7670 for (auto& line
: Lines()) {
7671 if (line
.HasFloats()) {
7672 lineFloats
.AppendElements(line
.Floats());
7674 if (line
.IsDirty()) {
7675 anyLineDirty
= true;
7679 AutoTArray
<nsIFrame
*, 8> storedFloats
;
7682 for (nsIFrame
* f
: mFloats
) {
7683 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7686 storedFloats
.AppendElement(f
);
7687 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
7693 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) &&
7696 "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
7698 # if defined(DEBUG_roc)
7699 nsIFrame::RootFrameList(PresContext(), stdout
, 0);
7700 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
7701 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
7703 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
7704 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
7710 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
7711 if (oofs
&& oofs
->NotEmpty()) {
7712 // Floats that were pushed should be removed from our float
7713 // manager. Otherwise the float manager's YMost or XMost might
7714 // be larger than necessary, causing this block to get an
7715 // incorrect desired height (or width). Some of these floats
7716 // may not actually have been added to the float manager because
7717 // they weren't reflowed before being pushed; that's OK,
7718 // RemoveRegions will ignore them. It is safe to do this here
7719 // because we know from here on the float manager will only be
7720 // used for its XMost and YMost, not to place new floats and
7722 aState
.FloatManager()->RemoveTrailingRegions(oofs
->FirstChild());
7726 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot
,
7727 bool* aBEndMarginRoot
) {
7728 nsIFrame
* parent
= GetParent();
7729 if (!HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
7730 if (!parent
|| parent
->IsFloatContainingBlock()) {
7731 *aBStartMarginRoot
= false;
7732 *aBEndMarginRoot
= false;
7737 if (parent
&& parent
->IsColumnSetFrame()) {
7738 // The first column is a start margin root and the last column is an end
7739 // margin root. (If the column-set is split by a column-span:all box then
7740 // the first and last column in each column-set fragment are margin roots.)
7741 *aBStartMarginRoot
= GetPrevInFlow() == nullptr;
7742 *aBEndMarginRoot
= GetNextInFlow() == nullptr;
7746 *aBStartMarginRoot
= true;
7747 *aBEndMarginRoot
= true;
7751 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
) {
7752 MOZ_ASSERT(aBlock
, "Must have a frame");
7753 NS_ASSERTION(aBlock
->IsBlockFrameOrSubclass(), "aBlock must be a block");
7755 nsIFrame
* parent
= aBlock
->GetParent();
7756 return aBlock
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
) ||
7757 (parent
&& !parent
->IsFloatContainingBlock());
7761 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
) {
7762 return aFrame
->IsBlockFrameOrSubclass() &&
7763 !aFrame
->IsFrameOfType(nsIFrame::eReplaced
) &&
7764 !aFrame
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
);
7767 // Note that this width can vary based on the vertical position.
7768 // However, the cases where it varies are the cases where the width fits
7769 // in the available space given, which means that variation shouldn't
7772 nsBlockFrame::FloatAvoidingISizeToClear
nsBlockFrame::ISizeToClearPastFloats(
7773 const BlockReflowState
& aState
, const LogicalRect
& aFloatAvailableSpace
,
7774 nsIFrame
* aFloatAvoidingBlock
) {
7775 nscoord inlineStartOffset
, inlineEndOffset
;
7776 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
7778 FloatAvoidingISizeToClear result
;
7779 aState
.ComputeFloatAvoidingOffsets(aFloatAvoidingBlock
, aFloatAvailableSpace
,
7780 inlineStartOffset
, inlineEndOffset
);
7781 nscoord availISize
=
7782 aState
.mContentArea
.ISize(wm
) - inlineStartOffset
- inlineEndOffset
;
7784 // We actually don't want the min width here; see bug 427782; we only
7785 // want to displace if the width won't compute to a value small enough
7787 // All we really need here is the result of ComputeSize, and we
7788 // could *almost* get that from an SizeComputationInput, except for the
7790 WritingMode frWM
= aFloatAvoidingBlock
->GetWritingMode();
7791 LogicalSize availSpace
=
7792 LogicalSize(wm
, availISize
, NS_UNCONSTRAINEDSIZE
).ConvertTo(frWM
, wm
);
7793 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
,
7794 aFloatAvoidingBlock
, availSpace
);
7795 result
.borderBoxISize
=
7796 reflowInput
.ComputedSizeWithBorderPadding(wm
).ISize(wm
);
7798 // Use the margins from sizingInput rather than reflowInput so that
7799 // they aren't reduced by ignoring margins in overconstrained cases.
7800 SizeComputationInput
sizingInput(aFloatAvoidingBlock
,
7801 aState
.mReflowInput
.mRenderingContext
, wm
,
7802 aState
.mContentArea
.ISize(wm
));
7803 const LogicalMargin computedMargin
= sizingInput
.ComputedLogicalMargin(wm
);
7805 nscoord marginISize
= computedMargin
.IStartEnd(wm
);
7806 const auto& iSize
= reflowInput
.mStylePosition
->ISize(wm
);
7807 if (marginISize
< 0 && (iSize
.IsAuto() || iSize
.IsMozAvailable())) {
7808 // If we get here, floatAvoidingBlock has a negative amount of inline-axis
7809 // margin and an 'auto' (or ~equivalently, -moz-available) inline
7810 // size. Under these circumstances, we use the margin to establish a
7811 // (positive) minimum size for the border-box, in order to satisfy the
7812 // equation in CSS2 10.3.3. That equation essentially simplifies to the
7815 // iSize of margins + iSize of borderBox = iSize of containingBlock
7817 // ...where "iSize of borderBox" is the sum of floatAvoidingBlock's
7818 // inline-axis components of border, padding, and {width,height}.
7820 // Right now, in the above equation, "iSize of margins" is the only term
7821 // that we know for sure. (And we also know that it's negative, since we
7822 // got here.) The other terms are as-yet unresolved, since the frame has an
7823 // 'auto' iSize, and since we aren't yet sure if we'll clear this frame
7824 // beyond floats or place it alongside them.
7826 // However: we *do* know that the equation's "iSize of containingBlock"
7827 // term *must* be non-negative, since boxes' widths and heights generally
7828 // can't be negative in CSS. To satisfy that requirement, we can then
7829 // infer that the equation's "iSize of borderBox" term *must* be large
7830 // enough to cancel out the (known-to-be-negative) "iSize of margins"
7831 // term. Therefore, marginISize value (negated to make it positive)
7832 // establishes a lower-bound for how much inline-axis space our border-box
7833 // will really require in order to fit alongside any floats.
7835 // XXXdholbert This explanation is admittedly a bit hand-wavy and may not
7836 // precisely match what any particular spec requires. It's the best
7837 // reasoning I could come up with to explain engines' behavior. Also, our
7838 // behavior with -moz-available doesn't seem particularly correct here, per
7839 // bug 1767217, though that's probably due to a bug elsewhere in our float
7841 result
.borderBoxISize
= std::max(result
.borderBoxISize
, -marginISize
);
7844 result
.marginIStart
= computedMargin
.IStart(wm
);
7849 nsBlockFrame
* nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
) {
7850 nsBlockFrame
* block
= nullptr;
7851 while (aCandidate
) {
7852 block
= do_QueryFrame(aCandidate
);
7854 // yay, candidate is a block!
7857 // Not a block. Check its parent next.
7858 aCandidate
= aCandidate
->GetParent();
7860 MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
7864 nscoord
nsBlockFrame::ComputeFinalBSize(BlockReflowState
& aState
,
7865 nscoord aBEndEdgeOfChildren
) {
7866 const WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
7868 const nscoord effectiveContentBoxBSize
=
7869 GetEffectiveComputedBSize(aState
.mReflowInput
, aState
.mConsumedBSize
);
7870 const nscoord blockStartBP
= aState
.BorderPadding().BStart(wm
);
7871 const nscoord blockEndBP
= aState
.BorderPadding().BEnd(wm
);
7874 !IsTrueOverflowContainer() || (effectiveContentBoxBSize
== 0 &&
7875 blockStartBP
== 0 && blockEndBP
== 0),
7876 "An overflow container's effective content-box block-size, block-start "
7877 "BP, and block-end BP should all be zero!");
7879 const nscoord effectiveContentBoxBSizeWithBStartBP
=
7880 NSCoordSaturatingAdd(blockStartBP
, effectiveContentBoxBSize
);
7881 const nscoord effectiveBorderBoxBSize
=
7882 NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP
, blockEndBP
);
7884 if (HasColumnSpanSiblings()) {
7885 MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
7886 "Frame constructor should've created column-span siblings!");
7888 // If a block is split by any column-spans, we calculate the final
7889 // block-size by shrinkwrapping our children's block-size for all the
7890 // fragments except for those after the final column-span, but we should
7891 // take no more than our effective border-box block-size. If there's any
7892 // leftover block-size, our next continuations will take up rest.
7894 // We don't need to adjust aBri.mReflowStatus because our children's status
7895 // is the same as ours.
7896 return std::min(effectiveBorderBoxBSize
, aBEndEdgeOfChildren
);
7899 const nscoord availBSize
= aState
.mReflowInput
.AvailableBSize();
7900 if (availBSize
== NS_UNCONSTRAINEDSIZE
) {
7901 return effectiveBorderBoxBSize
;
7904 // Save our children's reflow status.
7905 const bool isChildStatusComplete
= aState
.mReflowStatus
.IsComplete();
7906 if (isChildStatusComplete
&& effectiveContentBoxBSize
> 0 &&
7907 effectiveBorderBoxBSize
> availBSize
&&
7908 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
7909 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
7910 return effectiveBorderBoxBSize
;
7913 const bool isBDBClone
=
7914 aState
.mReflowInput
.mStyleBorder
->mBoxDecorationBreak
==
7915 StyleBoxDecorationBreak::Clone
;
7917 // The maximum value our content-box block-size can take within the given
7918 // available block-size.
7919 const nscoord maxContentBoxBSize
= aState
.ContentBSize();
7921 // The block-end edge of our content-box (relative to this frame's origin) if
7922 // we consumed the maximum block-size available to us (maxContentBoxBSize).
7923 const nscoord maxContentBoxBEnd
= aState
.ContentBEnd();
7925 // These variables are uninitialized intentionally so that the compiler can
7926 // check they are assigned in every if-else branch below.
7927 nscoord finalContentBoxBSizeWithBStartBP
;
7928 bool isOurStatusComplete
;
7930 if (effectiveBorderBoxBSize
<= availBSize
) {
7931 // Our effective border-box block-size can fit in the available block-size,
7932 // so we are complete.
7933 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
7934 isOurStatusComplete
= true;
7935 } else if (effectiveContentBoxBSizeWithBStartBP
<= maxContentBoxBEnd
) {
7936 // Note: The following assertion should generally hold because, for
7937 // box-decoration-break:clone, this "else if" branch is mathematically
7938 // equivalent to the initial "if".
7939 NS_ASSERTION(!isBDBClone
,
7940 "This else-if branch is handling a situation that's specific "
7941 "to box-decoration-break:slice, i.e. a case when we can skip "
7942 "our block-end border and padding!");
7944 // Our effective content-box block-size plus the block-start border and
7945 // padding can fit in the available block-size, but it cannot fit after
7946 // adding the block-end border and padding. Thus, we need a continuation
7947 // (unless we already weren't asking for any block-size, in which case we
7948 // stay complete to avoid looping forever).
7949 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
7950 isOurStatusComplete
= effectiveContentBoxBSize
== 0;
7952 // We aren't going to be able to fit our content-box in the space available
7953 // to it, which means we'll probably call ourselves incomplete to request a
7954 // continuation. But before making that decision, we check for certain
7955 // conditions which would force us to overflow beyond the available space --
7956 // these might result in us actually being complete if we're forced to
7957 // overflow far enough.
7958 if (MOZ_UNLIKELY(aState
.mReflowInput
.mFlags
.mIsTopOfPage
&& isBDBClone
&&
7959 maxContentBoxBSize
<= 0 &&
7960 aBEndEdgeOfChildren
== blockStartBP
)) {
7961 // In this rare case, we are at the top of page/column, we have
7962 // box-decoration-break:clone and zero available block-size for our
7963 // content-box (e.g. our own block-start border and padding already exceed
7964 // the available block-size), and we didn't lay out any child to consume
7965 // our content-box block-size. To ensure we make progress (avoid looping
7966 // forever), use 1px as our content-box block-size regardless of our
7967 // effective content-box block-size, in the spirit of
7968 // https://drafts.csswg.org/css-break/#breaking-rules.
7969 finalContentBoxBSizeWithBStartBP
= blockStartBP
+ AppUnitsPerCSSPixel();
7970 isOurStatusComplete
= effectiveContentBoxBSize
<= AppUnitsPerCSSPixel();
7971 } else if (aBEndEdgeOfChildren
> maxContentBoxBEnd
) {
7972 // We have a unbreakable child whose block-end edge exceeds the available
7973 // block-size for children.
7974 if (aBEndEdgeOfChildren
>= effectiveContentBoxBSizeWithBStartBP
) {
7975 // The unbreakable child's block-end edge forces us to consume all of
7976 // our effective content-box block-size.
7977 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
7979 // Even though we've consumed all of our effective content-box
7980 // block-size, we may still need to report an incomplete status in order
7981 // to get another continuation, which will be responsible for laying out
7982 // & drawing our block-end border & padding. But if we have no such
7983 // border & padding, or if we're forced to apply that border & padding
7984 // on this frame due to box-decoration-break:clone, then we don't need
7985 // to bother with that additional continuation.
7986 isOurStatusComplete
= (isBDBClone
|| blockEndBP
== 0);
7988 // The unbreakable child's block-end edge doesn't force us to consume
7989 // all of our effective content-box block-size.
7990 finalContentBoxBSizeWithBStartBP
= aBEndEdgeOfChildren
;
7991 isOurStatusComplete
= false;
7994 // The children's block-end edge can fit in the content-box space that we
7995 // have available for it. Consume all the space that is available so that
7996 // our inline-start/inline-end borders extend all the way to the block-end
7997 // edge of column/page.
7998 finalContentBoxBSizeWithBStartBP
= maxContentBoxBEnd
;
7999 isOurStatusComplete
= false;
8003 nscoord finalBorderBoxBSize
= finalContentBoxBSizeWithBStartBP
;
8004 if (isOurStatusComplete
) {
8005 finalBorderBoxBSize
= NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8006 if (isChildStatusComplete
) {
8007 // We want to use children's reflow status as ours, which can be overflow
8008 // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
8010 aState
.mReflowStatus
.SetOverflowIncomplete();
8013 NS_ASSERTION(!IsTrueOverflowContainer(),
8014 "An overflow container should always be complete because of "
8015 "its zero border-box block-size!");
8017 finalBorderBoxBSize
=
8018 NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8020 aState
.mReflowStatus
.SetIncomplete();
8021 if (!GetNextInFlow()) {
8022 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
8026 return finalBorderBoxBSize
;
8029 nsresult
nsBlockFrame::ResolveBidi() {
8030 NS_ASSERTION(!GetPrevInFlow(),
8031 "ResolveBidi called on non-first continuation");
8032 MOZ_ASSERT(PresContext()->BidiEnabled());
8033 return nsBidiPresUtils::Resolve(this);
8036 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState
& aRestyleState
) {
8037 // first-letter needs to be updated before first-line, because first-line can
8038 // change the style of the first-letter.
8039 if (HasFirstLetterChild()) {
8040 UpdateFirstLetterStyle(aRestyleState
);
8043 if (nsIFrame
* firstLineFrame
= GetFirstLineFrame()) {
8044 nsIFrame
* styleParent
= CorrectStyleParentFrame(firstLineFrame
->GetParent(),
8045 PseudoStyleType::firstLine
);
8047 ComputedStyle
* parentStyle
= styleParent
->Style();
8048 RefPtr
<ComputedStyle
> firstLineStyle
=
8049 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
8050 *mContent
->AsElement(), PseudoStyleType::firstLine
, parentStyle
);
8052 // FIXME(bz): Can we make first-line continuations be non-inheriting anon
8054 RefPtr
<ComputedStyle
> continuationStyle
=
8055 aRestyleState
.StyleSet().ResolveInheritingAnonymousBoxStyle(
8056 PseudoStyleType::mozLineFrame
, parentStyle
);
8058 UpdateStyleOfOwnedChildFrame(firstLineFrame
, firstLineStyle
, aRestyleState
,
8059 Some(continuationStyle
.get()));
8061 // We also want to update the styles of the first-line's descendants. We
8062 // don't need to compute a changehint for this, though, since any changes to
8063 // them are handled by the first-line anyway.
8064 RestyleManager
* manager
= PresContext()->RestyleManager();
8065 for (nsIFrame
* kid
: firstLineFrame
->PrincipalChildList()) {
8066 manager
->ReparentComputedStyleForFirstLine(kid
);
8071 nsIFrame
* nsBlockFrame::GetFirstLetter() const {
8072 if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
8073 // Certainly no first-letter frame.
8077 return GetProperty(FirstLetterProperty());
8080 nsIFrame
* nsBlockFrame::GetFirstLineFrame() const {
8081 nsIFrame
* maybeFirstLine
= PrincipalChildList().FirstChild();
8082 if (maybeFirstLine
&& maybeFirstLine
->IsLineFrame()) {
8083 return maybeFirstLine
;
8090 void nsBlockFrame::VerifyLines(bool aFinalCheckOK
) {
8091 if (!gVerifyLines
) {
8094 if (mLines
.empty()) {
8098 nsLineBox
* cursor
= GetLineCursorForQuery();
8100 // Add up the counts on each line. Also validate that IsFirstLine is
8103 for (const auto& line
: Lines()) {
8104 if (&line
== cursor
) {
8107 if (aFinalCheckOK
) {
8108 MOZ_ASSERT(line
.GetChildCount(), "empty line");
8109 if (line
.IsBlock()) {
8110 NS_ASSERTION(1 == line
.GetChildCount(), "bad first line");
8113 count
+= line
.GetChildCount();
8116 // Then count the frames
8117 int32_t frameCount
= 0;
8118 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
8121 frame
= frame
->GetNextSibling();
8123 NS_ASSERTION(count
== frameCount
, "bad line list");
8125 // Next: test that each line has right number of frames on it
8126 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
8127 line
!= line_end
;) {
8128 count
= line
->GetChildCount();
8129 frame
= line
->mFirstChild
;
8130 while (--count
>= 0) {
8131 frame
= frame
->GetNextSibling();
8134 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
8135 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
8140 FrameLines
* overflowLines
= GetOverflowLines();
8141 if (overflowLines
) {
8142 LineIterator line
= overflowLines
->mLines
.begin();
8143 LineIterator line_end
= overflowLines
->mLines
.end();
8144 for (; line
!= line_end
; ++line
) {
8145 if (line
== cursor
) {
8152 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
8155 void nsBlockFrame::VerifyOverflowSituation() {
8156 // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
8157 nsFrameList
* oofs
= GetOverflowOutOfFlows();
8159 for (nsIFrame
* f
: *oofs
) {
8160 nsIFrame
* nif
= f
->GetNextInFlow();
8162 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8166 // Pushed floats must not have a next-in-flow in mFloats or mFrames.
8167 oofs
= GetPushedFloats();
8169 for (nsIFrame
* f
: *oofs
) {
8170 nsIFrame
* nif
= f
->GetNextInFlow();
8172 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8176 // A child float next-in-flow's parent must be |this| or a next-in-flow of
8177 // |this|. Later next-in-flows must have the same or later parents.
8178 ChildListID childLists
[] = {FrameChildListID::Float
,
8179 FrameChildListID::PushedFloats
};
8180 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
8181 const nsFrameList
& children
= GetChildList(childLists
[i
]);
8182 for (nsIFrame
* f
: children
) {
8183 nsIFrame
* parent
= this;
8184 nsIFrame
* nif
= f
->GetNextInFlow();
8185 for (; nif
; nif
= nif
->GetNextInFlow()) {
8187 for (nsIFrame
* p
= parent
; p
; p
= p
->GetNextInFlow()) {
8188 if (nif
->GetParent() == p
) {
8196 "next-in-flow is a child of parent earlier in the frame tree?");
8201 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
8203 FrameLines
* overflowLines
= flow
->GetOverflowLines();
8204 if (overflowLines
) {
8205 NS_ASSERTION(!overflowLines
->mLines
.empty(),
8206 "should not be empty if present");
8207 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
8208 "bad overflow lines");
8209 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
8210 overflowLines
->mFrames
.FirstChild(),
8211 "bad overflow frames / lines");
8213 auto checkCursor
= [&](nsLineBox
* cursor
) -> bool {
8217 LineIterator line
= flow
->LinesBegin();
8218 LineIterator line_end
= flow
->LinesEnd();
8219 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8221 if (line
== line_end
&& overflowLines
) {
8222 line
= overflowLines
->mLines
.begin();
8223 line_end
= overflowLines
->mLines
.end();
8224 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8227 return line
!= line_end
;
8229 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForDisplay()),
8230 "stale LineCursorPropertyDisplay");
8231 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForQuery()),
8232 "stale LineCursorPropertyQuery");
8233 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
8237 int32_t nsBlockFrame::GetDepth() const {
8239 nsIFrame
* parent
= GetParent();
8241 parent
= parent
->GetParent();
8247 already_AddRefed
<ComputedStyle
> nsBlockFrame::GetFirstLetterStyle(
8248 nsPresContext
* aPresContext
) {
8249 return aPresContext
->StyleSet()->ProbePseudoElementStyle(
8250 *mContent
->AsElement(), PseudoStyleType::firstLetter
, Style());