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/Baseline.h"
18 #include "mozilla/ComputedStyle.h"
19 #include "mozilla/DebugOnly.h"
20 #include "mozilla/Maybe.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/StaticPrefs_browser.h"
23 #include "mozilla/StaticPrefs_layout.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
->IsLineParticipant()) {
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
->IsLineParticipant()) {
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::Destroy(DestroyContext
& aContext
) {
477 DestroyAbsoluteFrames(aContext
);
478 mFloats
.DestroyFrames(aContext
);
479 nsPresContext
* presContext
= PresContext();
480 mozilla::PresShell
* presShell
= presContext
->PresShell();
481 nsLineBox::DeleteLineList(presContext
, mLines
, &mFrames
, aContext
);
483 if (HasPushedFloats()) {
484 SafelyDestroyFrameListProp(aContext
, presShell
, PushedFloatProperty());
485 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
488 // destroy overflow lines now
489 FrameLines
* overflowLines
= RemoveOverflowLines();
491 nsLineBox::DeleteLineList(presContext
, overflowLines
->mLines
,
492 &overflowLines
->mFrames
, aContext
);
493 delete overflowLines
;
496 if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
497 SafelyDestroyFrameListProp(aContext
, presShell
,
498 OverflowOutOfFlowsProperty());
499 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
502 if (HasOutsideMarker()) {
503 SafelyDestroyFrameListProp(aContext
, presShell
, OutsideMarkerProperty());
504 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
507 nsContainerFrame::Destroy(aContext
);
511 nsILineIterator
* nsBlockFrame::GetLineIterator() {
512 nsLineIterator
* iter
= GetProperty(LineIteratorProperty());
514 const nsStyleVisibility
* visibility
= StyleVisibility();
515 iter
= new nsLineIterator(mLines
,
516 visibility
->mDirection
== StyleDirection::Rtl
);
517 SetProperty(LineIteratorProperty(), iter
);
522 NS_QUERYFRAME_HEAD(nsBlockFrame
)
523 NS_QUERYFRAME_ENTRY(nsBlockFrame
)
524 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
526 #ifdef DEBUG_FRAME_DUMP
527 void nsBlockFrame::List(FILE* out
, const char* aPrefix
,
528 ListFlags aFlags
) const {
530 ListGeneric(str
, aPrefix
, aFlags
);
532 fprintf_stderr(out
, "%s <\n", str
.get());
534 nsCString
pfx(aPrefix
);
538 if (!mLines
.empty()) {
539 ConstLineIterator line
= LinesBegin(), line_end
= LinesEnd();
540 for (; line
!= line_end
; ++line
) {
541 line
->List(out
, pfx
.get(), aFlags
);
545 // Output the overflow lines.
546 const FrameLines
* overflowLines
= GetOverflowLines();
547 if (overflowLines
&& !overflowLines
->mLines
.empty()) {
548 fprintf_stderr(out
, "%sOverflow-lines %p/%p <\n", pfx
.get(), overflowLines
,
549 &overflowLines
->mFrames
);
550 nsCString
nestedPfx(pfx
);
552 ConstLineIterator line
= overflowLines
->mLines
.begin(),
553 line_end
= overflowLines
->mLines
.end();
554 for (; line
!= line_end
; ++line
) {
555 line
->List(out
, nestedPfx
.get(), aFlags
);
557 fprintf_stderr(out
, "%s>\n", pfx
.get());
560 // skip the principal list - we printed the lines above
561 // skip the overflow list - we printed the overflow lines above
562 ChildListIDs skip
= {FrameChildListID::Principal
, FrameChildListID::Overflow
};
563 ListChildLists(out
, pfx
.get(), aFlags
, skip
);
565 fprintf_stderr(out
, "%s>\n", aPrefix
);
568 nsresult
nsBlockFrame::GetFrameName(nsAString
& aResult
) const {
569 return MakeFrameName(u
"Block"_ns
, aResult
);
573 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
574 bool aRebuildDisplayItems
) {
575 if (IsInSVGTextSubtree()) {
576 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
577 "unexpected block frame in SVG text");
578 GetParent()->InvalidateFrame();
581 nsContainerFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
584 void nsBlockFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
585 uint32_t aDisplayItemKey
,
586 bool aRebuildDisplayItems
) {
587 if (IsInSVGTextSubtree()) {
588 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
589 "unexpected block frame in SVG text");
590 GetParent()->InvalidateFrame();
593 nsContainerFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
594 aRebuildDisplayItems
);
597 nscoord
nsBlockFrame::SynthesizeFallbackBaseline(
598 WritingMode aWM
, BaselineSharingGroup aBaselineGroup
) const {
599 return Baseline::SynthesizeBOffsetFromMarginBox(this, aWM
, aBaselineGroup
);
602 template <typename LineIteratorType
>
603 Maybe
<nscoord
> nsBlockFrame::GetBaselineBOffset(
604 LineIteratorType aStart
, LineIteratorType aEnd
, WritingMode aWM
,
605 BaselineSharingGroup aBaselineGroup
,
606 BaselineExportContext aExportContext
) const {
607 MOZ_ASSERT((std::is_same_v
<LineIteratorType
, ConstLineIterator
> &&
608 aBaselineGroup
== BaselineSharingGroup::First
) ||
609 (std::is_same_v
<LineIteratorType
, ConstReverseLineIterator
> &&
610 aBaselineGroup
== BaselineSharingGroup::Last
),
611 "Iterator direction must match baseline sharing group.");
612 for (auto line
= aStart
; line
!= aEnd
; ++line
) {
613 if (!line
->IsBlock()) {
614 // XXX Is this the right test? We have some bogus empty lines
615 // floating around, but IsEmpty is perhaps too weak.
616 if (line
->BSize() != 0 || !line
->IsEmpty()) {
617 const auto ascent
= line
->BStart() + line
->GetLogicalAscent();
618 if (aBaselineGroup
== BaselineSharingGroup::Last
) {
619 return Some(BSize(aWM
) - ascent
);
625 nsIFrame
* kid
= line
->mFirstChild
;
626 if (aWM
.IsOrthogonalTo(kid
->GetWritingMode())) {
629 if (aExportContext
== BaselineExportContext::LineLayout
&&
630 kid
->IsTableWrapperFrame()) {
631 // `<table>` in inline-block context does not export any baseline.
634 const auto kidBaselineGroup
=
635 aExportContext
== BaselineExportContext::LineLayout
636 ? kid
->GetDefaultBaselineSharingGroup()
638 const auto kidBaseline
=
639 kid
->GetNaturalBaselineBOffset(aWM
, kidBaselineGroup
, aExportContext
);
643 auto result
= *kidBaseline
;
644 if (kidBaselineGroup
== BaselineSharingGroup::Last
) {
645 result
= kid
->BSize(aWM
) - result
;
647 // Ignore relative positioning for baseline calculations.
648 const nsSize
& sz
= line
->mContainerSize
;
649 result
+= kid
->GetLogicalNormalPosition(aWM
, sz
).B(aWM
);
650 if (aBaselineGroup
== BaselineSharingGroup::Last
) {
651 return Some(BSize(aWM
) - result
);
658 Maybe
<nscoord
> nsBlockFrame::GetNaturalBaselineBOffset(
659 WritingMode aWM
, BaselineSharingGroup aBaselineGroup
,
660 BaselineExportContext aExportContext
) const {
661 if (StyleDisplay()->IsContainLayout()) {
665 if (aBaselineGroup
== BaselineSharingGroup::First
) {
666 return GetBaselineBOffset(LinesBegin(), LinesEnd(), aWM
, aBaselineGroup
,
670 return GetBaselineBOffset(LinesRBegin(), LinesREnd(), aWM
, aBaselineGroup
,
674 nscoord
nsBlockFrame::GetCaretBaseline() const {
675 nsRect contentRect
= GetContentRect();
676 nsMargin bp
= GetUsedBorderAndPadding();
678 if (!mLines
.empty()) {
679 ConstLineIterator line
= LinesBegin();
680 if (!line
->IsEmpty()) {
681 if (line
->IsBlock()) {
682 return bp
.top
+ line
->mFirstChild
->GetCaretBaseline();
684 return line
->BStart() + line
->GetLogicalAscent();
688 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
689 RefPtr
<nsFontMetrics
> fm
=
690 nsLayoutUtils::GetFontMetricsForFrame(this, inflation
);
691 nscoord lineHeight
= ReflowInput::CalcLineHeight(
692 *Style(), PresContext(), GetContent(), contentRect
.height
, inflation
);
693 const WritingMode wm
= GetWritingMode();
694 return nsLayoutUtils::GetCenteredFontBaseline(fm
, lineHeight
,
695 wm
.IsLineInverted()) +
699 /////////////////////////////////////////////////////////////////////////////
700 // Child frame enumeration
702 const nsFrameList
& nsBlockFrame::GetChildList(ChildListID aListID
) const {
704 case FrameChildListID::Principal
:
706 case FrameChildListID::Overflow
: {
707 FrameLines
* overflowLines
= GetOverflowLines();
708 return overflowLines
? overflowLines
->mFrames
: nsFrameList::EmptyList();
710 case FrameChildListID::Float
:
712 case FrameChildListID::OverflowOutOfFlow
: {
713 const nsFrameList
* list
= GetOverflowOutOfFlows();
714 return list
? *list
: nsFrameList::EmptyList();
716 case FrameChildListID::PushedFloats
: {
717 const nsFrameList
* list
= GetPushedFloats();
718 return list
? *list
: nsFrameList::EmptyList();
720 case FrameChildListID::Bullet
: {
721 const nsFrameList
* list
= GetOutsideMarkerList();
722 return list
? *list
: nsFrameList::EmptyList();
725 return nsContainerFrame::GetChildList(aListID
);
729 void nsBlockFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const {
730 nsContainerFrame::GetChildLists(aLists
);
731 FrameLines
* overflowLines
= GetOverflowLines();
733 overflowLines
->mFrames
.AppendIfNonempty(aLists
, FrameChildListID::Overflow
);
735 const nsFrameList
* list
= GetOverflowOutOfFlows();
737 list
->AppendIfNonempty(aLists
, FrameChildListID::OverflowOutOfFlow
);
739 mFloats
.AppendIfNonempty(aLists
, FrameChildListID::Float
);
740 list
= GetOutsideMarkerList();
742 list
->AppendIfNonempty(aLists
, FrameChildListID::Bullet
);
744 list
= GetPushedFloats();
746 list
->AppendIfNonempty(aLists
, FrameChildListID::PushedFloats
);
751 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
754 * Remove the first line from aFromLines and adjust the associated frame list
755 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
756 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
757 * that were extracted from the head of aFromFrames.
758 * aFromLines must contain at least one line, the line may be empty.
759 * @return true if aFromLines becomes empty
761 static bool RemoveFirstLine(nsLineList
& aFromLines
, nsFrameList
& aFromFrames
,
762 nsLineBox
** aOutLine
, nsFrameList
* aOutFrames
) {
763 nsLineList_iterator removedLine
= aFromLines
.begin();
764 *aOutLine
= removedLine
;
765 nsLineList_iterator next
= aFromLines
.erase(removedLine
);
766 bool isLastLine
= next
== aFromLines
.end();
767 nsIFrame
* firstFrameInNextLine
= isLastLine
? nullptr : next
->mFirstChild
;
768 *aOutFrames
= aFromFrames
.TakeFramesBefore(firstFrameInNextLine
);
772 //////////////////////////////////////////////////////////////////////
776 void nsBlockFrame::MarkIntrinsicISizesDirty() {
777 nsBlockFrame
* dirtyBlock
= static_cast<nsBlockFrame
*>(FirstContinuation());
778 dirtyBlock
->mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
779 dirtyBlock
->mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
780 if (!HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
781 for (nsIFrame
* frame
= dirtyBlock
; frame
;
782 frame
= frame
->GetNextContinuation()) {
783 frame
->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
787 nsContainerFrame::MarkIntrinsicISizesDirty();
790 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
791 nsPresContext
* presContext
= PresContext();
792 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext
)) {
795 bool inflationEnabled
= !presContext
->mInflationDisabledForShrinkWrap
;
796 if (inflationEnabled
!= HasAnyStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
)) {
797 mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
798 mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
799 if (inflationEnabled
) {
800 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
802 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
807 // Whether this line is indented by the text-indent amount.
808 bool nsBlockFrame::TextIndentAppliesTo(const LineIterator
& aLine
) const {
809 const auto& textIndent
= StyleText()->mTextIndent
;
811 bool isFirstLineOrAfterHardBreak
= [&] {
812 if (aLine
!= LinesBegin()) {
813 // If not the first line of the block, but 'each-line' is in effect,
814 // check if the previous line was not wrapped.
815 return textIndent
.each_line
&& !aLine
.prev()->IsLineWrapped();
817 if (nsBlockFrame
* prevBlock
= do_QueryFrame(GetPrevInFlow())) {
818 // There's a prev-in-flow, so this only counts as a first-line if
819 // 'each-line' and the prev-in-flow's last line was not wrapped.
820 return textIndent
.each_line
&&
821 (prevBlock
->Lines().empty() ||
822 !prevBlock
->LinesEnd().prev()->IsLineWrapped());
827 // The 'hanging' option inverts which lines are/aren't indented.
828 return isFirstLineOrAfterHardBreak
!= textIndent
.hanging
;
832 nscoord
nsBlockFrame::GetMinISize(gfxContext
* aRenderingContext
) {
833 nsIFrame
* firstInFlow
= FirstContinuation();
834 if (firstInFlow
!= this) {
835 return firstInFlow
->GetMinISize(aRenderingContext
);
838 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
840 CheckIntrinsicCacheAgainstShrinkWrapState();
842 if (mCachedMinISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
843 return mCachedMinISize
;
846 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
847 mCachedMinISize
= *containISize
;
848 return mCachedMinISize
;
852 if (gNoisyIntrinsic
) {
853 IndentBy(stdout
, gNoiseIndent
);
855 printf(": GetMinISize\n");
857 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
860 for (nsBlockFrame
* curFrame
= this; curFrame
;
861 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
862 curFrame
->LazyMarkLinesDirty();
865 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
866 PresContext()->BidiEnabled()) {
870 const bool whiteSpaceCanWrap
= StyleText()->WhiteSpaceCanWrapStyle();
871 InlineMinISizeData data
;
872 for (nsBlockFrame
* curFrame
= this; curFrame
;
873 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
874 for (LineIterator line
= curFrame
->LinesBegin(),
875 line_end
= curFrame
->LinesEnd();
876 line
!= line_end
; ++line
) {
878 if (gNoisyIntrinsic
) {
879 IndentBy(stdout
, gNoiseIndent
);
880 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
881 line
->IsEmpty() ? ", empty" : "");
883 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
885 if (line
->IsBlock()) {
887 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
888 aRenderingContext
, line
->mFirstChild
, IntrinsicISizeType::MinISize
);
891 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
892 data
.mCurrentLine
+= StyleText()->mTextIndent
.length
.Resolve(0);
895 data
.SetLineContainer(curFrame
);
896 nsIFrame
* kid
= line
->mFirstChild
;
897 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
898 ++i
, kid
= kid
->GetNextSibling()) {
899 kid
->AddInlineMinISize(aRenderingContext
, &data
);
900 if (whiteSpaceCanWrap
&& data
.mTrailingWhitespace
) {
901 data
.OptionallyBreak();
906 if (gNoisyIntrinsic
) {
907 IndentBy(stdout
, gNoiseIndent
);
908 printf("min: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
916 mCachedMinISize
= data
.mPrevLines
;
917 return mCachedMinISize
;
921 nscoord
nsBlockFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
922 nsIFrame
* firstInFlow
= FirstContinuation();
923 if (firstInFlow
!= this) {
924 return firstInFlow
->GetPrefISize(aRenderingContext
);
927 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
929 CheckIntrinsicCacheAgainstShrinkWrapState();
931 if (mCachedPrefISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
932 return mCachedPrefISize
;
935 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
936 mCachedPrefISize
= *containISize
;
937 return mCachedPrefISize
;
941 if (gNoisyIntrinsic
) {
942 IndentBy(stdout
, gNoiseIndent
);
944 printf(": GetPrefISize\n");
946 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
949 for (nsBlockFrame
* curFrame
= this; curFrame
;
950 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
951 curFrame
->LazyMarkLinesDirty();
954 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
955 PresContext()->BidiEnabled()) {
958 InlinePrefISizeData data
;
959 for (nsBlockFrame
* curFrame
= this; curFrame
;
960 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
961 for (LineIterator line
= curFrame
->LinesBegin(),
962 line_end
= curFrame
->LinesEnd();
963 line
!= line_end
; ++line
) {
965 if (gNoisyIntrinsic
) {
966 IndentBy(stdout
, gNoiseIndent
);
967 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
968 line
->IsEmpty() ? ", empty" : "");
970 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
972 if (line
->IsBlock()) {
973 StyleClear clearType
;
974 if (!data
.mLineIsEmpty
|| BlockCanIntersectFloats(line
->mFirstChild
)) {
975 clearType
= StyleClear::Both
;
977 clearType
= line
->mFirstChild
->StyleDisplay()->mClear
;
979 data
.ForceBreak(clearType
);
980 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
981 aRenderingContext
, line
->mFirstChild
,
982 IntrinsicISizeType::PrefISize
);
985 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
986 nscoord indent
= StyleText()->mTextIndent
.length
.Resolve(0);
987 data
.mCurrentLine
+= indent
;
988 // XXXmats should the test below be indent > 0?
989 if (indent
!= nscoord(0)) {
990 data
.mLineIsEmpty
= false;
994 data
.SetLineContainer(curFrame
);
995 nsIFrame
* kid
= line
->mFirstChild
;
996 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
997 ++i
, kid
= kid
->GetNextSibling()) {
998 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1002 if (gNoisyIntrinsic
) {
1003 IndentBy(stdout
, gNoiseIndent
);
1004 printf("pref: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
1012 mCachedPrefISize
= data
.mPrevLines
;
1013 return mCachedPrefISize
;
1016 nsRect
nsBlockFrame::ComputeTightBounds(DrawTarget
* aDrawTarget
) const {
1018 if (Style()->HasTextDecorationLines()) {
1019 return InkOverflowRect();
1021 return ComputeSimpleTightBounds(aDrawTarget
);
1025 nsresult
nsBlockFrame::GetPrefWidthTightBounds(gfxContext
* aRenderingContext
,
1026 nscoord
* aX
, nscoord
* aXMost
) {
1027 nsIFrame
* firstInFlow
= FirstContinuation();
1028 if (firstInFlow
!= this) {
1029 return firstInFlow
->GetPrefWidthTightBounds(aRenderingContext
, aX
, aXMost
);
1036 InlinePrefISizeData data
;
1037 for (nsBlockFrame
* curFrame
= this; curFrame
;
1038 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
1039 for (LineIterator line
= curFrame
->LinesBegin(),
1040 line_end
= curFrame
->LinesEnd();
1041 line
!= line_end
; ++line
) {
1042 nscoord childX
, childXMost
;
1043 if (line
->IsBlock()) {
1045 rv
= line
->mFirstChild
->GetPrefWidthTightBounds(aRenderingContext
,
1046 &childX
, &childXMost
);
1047 NS_ENSURE_SUCCESS(rv
, rv
);
1048 *aX
= std::min(*aX
, childX
);
1049 *aXMost
= std::max(*aXMost
, childXMost
);
1051 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
1052 data
.mCurrentLine
+= StyleText()->mTextIndent
.length
.Resolve(0);
1055 data
.SetLineContainer(curFrame
);
1056 nsIFrame
* kid
= line
->mFirstChild
;
1057 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
1058 ++i
, kid
= kid
->GetNextSibling()) {
1059 rv
= kid
->GetPrefWidthTightBounds(aRenderingContext
, &childX
,
1061 NS_ENSURE_SUCCESS(rv
, rv
);
1062 *aX
= std::min(*aX
, data
.mCurrentLine
+ childX
);
1063 *aXMost
= std::max(*aXMost
, data
.mCurrentLine
+ childXMost
);
1064 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1075 * Return whether aNewAvailableSpace is smaller *on either side*
1076 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
1077 * if we need to redo layout on an line, replaced block, or block
1078 * formatting context, because its height (which we used to compute
1079 * aNewAvailableSpace) caused it to intersect additional floats.
1081 static bool AvailableSpaceShrunk(WritingMode aWM
,
1082 const LogicalRect
& aOldAvailableSpace
,
1083 const LogicalRect
& aNewAvailableSpace
,
1084 bool aCanGrow
/* debug-only */) {
1085 if (aNewAvailableSpace
.ISize(aWM
) == 0) {
1086 // Positions are not significant if the inline size is zero.
1087 return aOldAvailableSpace
.ISize(aWM
) != 0;
1091 aNewAvailableSpace
.IStart(aWM
) <= aOldAvailableSpace
.IStart(aWM
) ||
1092 aNewAvailableSpace
.IEnd(aWM
) <= aOldAvailableSpace
.IEnd(aWM
),
1093 "available space should not shrink on the start side and "
1094 "grow on the end side");
1096 aNewAvailableSpace
.IStart(aWM
) >= aOldAvailableSpace
.IStart(aWM
) ||
1097 aNewAvailableSpace
.IEnd(aWM
) >= aOldAvailableSpace
.IEnd(aWM
),
1098 "available space should not grow on the start side and "
1099 "shrink on the end side");
1102 aOldAvailableSpace
.IStart(aWM
) <= aNewAvailableSpace
.IStart(aWM
) &&
1103 aOldAvailableSpace
.IEnd(aWM
) >= aNewAvailableSpace
.IEnd(aWM
),
1104 "available space should never grow");
1106 // Have we shrunk on either side?
1107 return aNewAvailableSpace
.IStart(aWM
) > aOldAvailableSpace
.IStart(aWM
) ||
1108 aNewAvailableSpace
.IEnd(aWM
) < aOldAvailableSpace
.IEnd(aWM
);
1111 static LogicalSize
CalculateContainingBlockSizeForAbsolutes(
1112 WritingMode aWM
, const ReflowInput
& aReflowInput
, LogicalSize aFrameSize
) {
1113 // The issue here is that for a 'height' of 'auto' the reflow input
1114 // code won't know how to calculate the containing block height
1115 // because it's calculated bottom up. So we use our own computed
1116 // size as the dimensions.
1117 nsIFrame
* frame
= aReflowInput
.mFrame
;
1119 LogicalSize
cbSize(aFrameSize
);
1120 // Containing block is relative to the padding edge
1121 const LogicalMargin border
= aReflowInput
.ComputedLogicalBorder(aWM
);
1122 cbSize
.ISize(aWM
) -= border
.IStartEnd(aWM
);
1123 cbSize
.BSize(aWM
) -= border
.BStartEnd(aWM
);
1125 if (frame
->GetParent()->GetContent() != frame
->GetContent() ||
1126 frame
->GetParent()->IsCanvasFrame()) {
1130 // We are a wrapped frame for the content (and the wrapper is not the
1131 // canvas frame, whose size is not meaningful here).
1132 // Use the container's dimensions, if they have been precomputed.
1133 // XXX This is a hack! We really should be waiting until the outermost
1134 // frame is fully reflowed and using the resulting dimensions, even
1135 // if they're intrinsic.
1136 // In fact we should be attaching absolute children to the outermost
1137 // frame and not always sticking them in block frames.
1139 // First, find the reflow input for the outermost frame for this content.
1140 const ReflowInput
* lastRI
= &aReflowInput
;
1141 DebugOnly
<const ReflowInput
*> lastButOneRI
= &aReflowInput
;
1142 while (lastRI
->mParentReflowInput
&&
1143 lastRI
->mParentReflowInput
->mFrame
->GetContent() ==
1144 frame
->GetContent()) {
1145 lastButOneRI
= lastRI
;
1146 lastRI
= lastRI
->mParentReflowInput
;
1149 if (lastRI
== &aReflowInput
) {
1153 // For scroll containers, we can just use cbSize (which is the padding-box
1154 // size of the scrolled-content frame).
1155 if (nsIScrollableFrame
* scrollFrame
= do_QueryFrame(lastRI
->mFrame
)) {
1156 // Assert that we're not missing any frames between the abspos containing
1157 // block and the scroll container.
1159 Unused
<< scrollFrame
;
1160 MOZ_ASSERT(lastButOneRI
== &aReflowInput
);
1164 // Same for fieldsets, where the inner anonymous frame has the correct padding
1165 // area with the legend taken into account.
1166 if (lastRI
->mFrame
->IsFieldSetFrame()) {
1170 // We found a reflow input for the outermost wrapping frame, so use
1171 // its computed metrics if available, converted to our writing mode
1172 const LogicalSize lastRISize
= lastRI
->ComputedSize(aWM
);
1173 const LogicalMargin lastRIPadding
= lastRI
->ComputedLogicalPadding(aWM
);
1174 if (lastRISize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1176 std::max(0, lastRISize
.ISize(aWM
) + lastRIPadding
.IStartEnd(aWM
));
1178 if (lastRISize
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1180 std::max(0, lastRISize
.BSize(aWM
) + lastRIPadding
.BStartEnd(aWM
));
1187 * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1189 * This is used to determine whether to recurse into aFrame when applying
1190 * -webkit-line-clamp.
1192 static const nsBlockFrame
* GetAsLineClampDescendant(const nsIFrame
* aFrame
) {
1193 if (const nsBlockFrame
* block
= do_QueryFrame(aFrame
)) {
1194 if (!block
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1201 static nsBlockFrame
* GetAsLineClampDescendant(nsIFrame
* aFrame
) {
1202 return const_cast<nsBlockFrame
*>(
1203 GetAsLineClampDescendant(const_cast<const nsIFrame
*>(aFrame
)));
1206 static bool IsLineClampRoot(const nsBlockFrame
* aFrame
) {
1207 if (!aFrame
->StyleDisplay()->mWebkitLineClamp
) {
1211 if (!aFrame
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1215 if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled()) {
1219 // For now, -webkit-box is the only thing allowed to be a line-clamp root.
1220 // Ideally we'd just make this work everywhere, but for now we're carrying
1221 // this forward as a limitation on the legacy -webkit-line-clamp feature,
1222 // since relaxing this limitation might create webcompat trouble.
1223 auto origDisplay
= [&] {
1224 if (aFrame
->Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
1225 // If we're the anonymous block inside the scroll frame, we need to look
1226 // at the original display of our parent frame.
1227 MOZ_ASSERT(aFrame
->GetParent());
1228 const auto& parentDisp
= *aFrame
->GetParent()->StyleDisplay();
1229 MOZ_ASSERT(parentDisp
.mWebkitLineClamp
==
1230 aFrame
->StyleDisplay()->mWebkitLineClamp
,
1231 ":-moz-scrolled-content should inherit -webkit-line-clamp, "
1232 "via rule in UA stylesheet");
1233 return parentDisp
.mOriginalDisplay
;
1235 return aFrame
->StyleDisplay()->mOriginalDisplay
;
1237 return origDisplay
.Inside() == StyleDisplayInside::WebkitBox
;
1240 bool nsBlockFrame::IsInLineClampContext() const {
1241 if (IsLineClampRoot(this)) {
1244 const nsBlockFrame
* cur
= this;
1245 while (GetAsLineClampDescendant(cur
)) {
1246 cur
= do_QueryFrame(cur
->GetParent());
1250 if (IsLineClampRoot(cur
)) {
1258 * Iterator over all descendant inline line boxes, except for those that are
1259 * under an independent formatting context.
1261 class MOZ_RAII LineClampLineIterator
{
1263 explicit LineClampLineIterator(nsBlockFrame
* aFrame
)
1264 : mCur(aFrame
->LinesBegin()),
1265 mEnd(aFrame
->LinesEnd()),
1266 mCurrentFrame(mCur
== mEnd
? nullptr : aFrame
) {
1267 if (mCur
!= mEnd
&& !mCur
->IsInline()) {
1272 nsLineBox
* GetCurrentLine() { return mCurrentFrame
? mCur
.get() : nullptr; }
1273 nsBlockFrame
* GetCurrentFrame() { return mCurrentFrame
; }
1275 // Advances the iterator to the next line line.
1277 // Next() shouldn't be called once the iterator is at the end, which can be
1278 // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
1280 MOZ_ASSERT(mCur
!= mEnd
&& mCurrentFrame
,
1281 "Don't call Next() when the iterator is at the end");
1290 // Reached the end of the current block. Pop the parent off the
1291 // stack; if there isn't one, then we've reached the end.
1292 if (mStack
.IsEmpty()) {
1293 mCurrentFrame
= nullptr;
1296 auto entry
= mStack
.PopLastElement();
1297 mCurrentFrame
= entry
.first
;
1298 mCur
= entry
.second
;
1299 mEnd
= mCurrentFrame
->LinesEnd();
1300 } else if (mCur
->IsBlock()) {
1301 if (nsBlockFrame
* child
= GetAsLineClampDescendant(mCur
->mFirstChild
)) {
1302 nsBlockFrame::LineIterator next
= mCur
;
1304 mStack
.AppendElement(std::make_pair(mCurrentFrame
, next
));
1305 mCur
= child
->LinesBegin();
1306 mEnd
= child
->LinesEnd();
1307 mCurrentFrame
= child
;
1309 // Some kind of frame we shouldn't descend into.
1313 MOZ_ASSERT(mCur
->IsInline());
1319 // The current line within the current block.
1321 // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1323 nsBlockFrame::LineIterator mCur
;
1325 // The iterator end for the current block.
1326 nsBlockFrame::LineIterator mEnd
;
1328 // The current block.
1329 nsBlockFrame
* mCurrentFrame
;
1331 // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1333 AutoTArray
<std::pair
<nsBlockFrame
*, nsBlockFrame::LineIterator
>, 8> mStack
;
1336 static bool ClearLineClampEllipsis(nsBlockFrame
* aFrame
) {
1337 if (!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
)) {
1338 for (nsIFrame
* f
: aFrame
->PrincipalChildList()) {
1339 if (nsBlockFrame
* child
= GetAsLineClampDescendant(f
)) {
1340 if (ClearLineClampEllipsis(child
)) {
1348 aFrame
->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1350 for (auto& line
: aFrame
->Lines()) {
1351 if (line
.HasLineClampEllipsis()) {
1352 line
.ClearHasLineClampEllipsis();
1357 // We didn't find a line with the ellipsis; it must have been deleted already.
1361 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1363 void nsBlockFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
1364 const ReflowInput
& aReflowInput
,
1365 nsReflowStatus
& aStatus
) {
1366 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
1367 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1372 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1373 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
1374 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1378 IndentBy(stdout
, gNoiseIndent
);
1380 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1381 aReflowInput
.AvailableISize(), aReflowInput
.AvailableBSize(),
1382 aReflowInput
.ComputedISize(), aReflowInput
.ComputedBSize());
1384 AutoNoisyIndenter
indent(gNoisy
);
1385 PRTime start
= 0; // Initialize these variablies to silence the compiler.
1386 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
1387 if (gLameReflowMetrics
) {
1389 ctc
= nsLineBox::GetCtorCount();
1393 // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
1394 // max-block-size because both affect the children's available block-size.
1395 if (IsColumnSetWrapperFrame()) {
1396 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
);
1399 Maybe
<nscoord
> restoreReflowInputAvailBSize
;
1400 auto MaybeRestore
= MakeScopeExit([&] {
1401 if (MOZ_UNLIKELY(restoreReflowInputAvailBSize
)) {
1402 const_cast<ReflowInput
&>(aReflowInput
)
1403 .SetAvailableBSize(*restoreReflowInputAvailBSize
);
1407 WritingMode wm
= aReflowInput
.GetWritingMode();
1408 const nscoord consumedBSize
= CalcAndCacheConsumedBSize();
1409 const nscoord effectiveContentBoxBSize
=
1410 GetEffectiveComputedBSize(aReflowInput
, consumedBSize
);
1411 // If we have non-auto block size, we're clipping our kids and we fit,
1412 // make sure our kids fit too.
1413 const PhysicalAxes physicalBlockAxis
=
1414 wm
.IsVertical() ? PhysicalAxes::Horizontal
: PhysicalAxes::Vertical
;
1415 if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1416 aReflowInput
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
&&
1417 (ShouldApplyOverflowClipping(aReflowInput
.mStyleDisplay
) &
1418 physicalBlockAxis
)) {
1419 LogicalMargin blockDirExtras
=
1420 aReflowInput
.ComputedLogicalBorderPadding(wm
);
1421 if (GetLogicalSkipSides().BStart()) {
1422 blockDirExtras
.BStart(wm
) = 0;
1424 // Block-end margin never causes us to create continuations, so we
1425 // don't need to worry about whether it fits in its entirety.
1426 blockDirExtras
.BStart(wm
) +=
1427 aReflowInput
.ComputedLogicalMargin(wm
).BStart(wm
);
1430 if (effectiveContentBoxBSize
+ blockDirExtras
.BStartEnd(wm
) <=
1431 aReflowInput
.AvailableBSize()) {
1432 restoreReflowInputAvailBSize
.emplace(aReflowInput
.AvailableBSize());
1433 const_cast<ReflowInput
&>(aReflowInput
)
1434 .SetAvailableBSize(NS_UNCONSTRAINEDSIZE
);
1438 if (IsFrameTreeTooDeep(aReflowInput
, aMetrics
, aStatus
)) {
1442 // OK, some lines may be reflowed. Blow away any saved line cursor
1443 // because we may invalidate the nondecreasing
1444 // overflowArea.InkOverflow().y/yMost invariant, and we may even
1445 // delete the line with the line cursor.
1448 // See comment below about oldSize. Use *only* for the
1449 // abs-pos-containing-block-size-change optimization!
1450 nsSize oldSize
= GetSize();
1452 // Should we create a float manager?
1453 nsAutoFloatManager
autoFloatManager(const_cast<ReflowInput
&>(aReflowInput
));
1455 // XXXldb If we start storing the float manager in the frame rather
1456 // than keeping it around only during reflow then we should create it
1457 // only when there are actually floats to manage. Otherwise things
1458 // like tables will gain significant bloat.
1459 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
1460 if (needFloatManager
) {
1461 autoFloatManager
.CreateFloatManager(aPresContext
);
1464 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
1465 PresContext()->BidiEnabled()) {
1466 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
1469 // Whether to apply text-wrap: balance behavior.
1470 bool tryBalance
= StyleText()->mTextWrap
== StyleTextWrap::Balance
&&
1471 !GetPrevContinuation();
1473 // Struct used to hold the "target" number of lines or clamp position to
1474 // maintain when doing text-wrap: balance.
1475 struct BalanceTarget
{
1476 // If line-clamp is in effect, mContent and mOffset indicate the starting
1477 // position of the first line after the clamp limit. If line-clamp is not
1478 // in use, mContent is null and mOffset is the total number of lines that
1479 // the block must contain.
1480 nsIContent
* mContent
= nullptr;
1481 int32_t mOffset
= -1;
1483 bool operator==(const BalanceTarget
& aOther
) const {
1484 return mContent
== aOther
.mContent
&& mOffset
== aOther
.mOffset
;
1486 bool operator!=(const BalanceTarget
& aOther
) const {
1487 return !(*this == aOther
);
1491 BalanceTarget balanceTarget
;
1493 // Helpers for text-wrap: balance implementation:
1495 // Count the number of lines in the mLines list, but return -1 (to suppress
1496 // balancing) instead if the count is going to exceed aLimit, or if we
1497 // encounter a block.
1498 auto countLinesUpTo
= [&](int32_t aLimit
) -> int32_t {
1500 for (auto iter
= mLines
.begin(); iter
!= mLines
.end(); ++iter
) {
1501 if (++n
> aLimit
|| iter
->IsBlock()) {
1508 // Return a BalanceTarget record representing the position at which line-clamp
1509 // will take effect for the current line list. Only to be used when there are
1510 // enough lines that the clamp will apply.
1511 auto getClampPosition
= [&](uint32_t aClampCount
) -> BalanceTarget
{
1512 MOZ_ASSERT(aClampCount
< mLines
.size());
1513 auto iter
= mLines
.begin();
1514 for (uint32_t i
= 0; i
< aClampCount
; i
++) {
1517 nsIFrame
* firstChild
= iter
->mFirstChild
;
1519 return BalanceTarget
{};
1521 nsIContent
* content
= firstChild
->GetContent();
1523 return BalanceTarget
{};
1526 if (firstChild
->IsTextFrame()) {
1527 auto* textFrame
= static_cast<nsTextFrame
*>(firstChild
);
1528 offset
= textFrame
->GetContentOffset();
1530 return BalanceTarget
{content
, offset
};
1533 // "balancing" is implemented by shortening the effective inline-size of the
1534 // lines, so that content will tend to be pushed down to fill later lines of
1535 // the block. `balanceInset` is the current amount of "inset" to apply, and
1536 // `balanceStep` is the increment to adjust it by for the next iteration.
1537 nscoord balanceStep
= 0;
1539 // text-wrap: balance loop, executed only once if balancing is not required.
1540 nsReflowStatus reflowStatus
;
1541 TrialReflowState
trialState(consumedBSize
, effectiveContentBoxBSize
,
1544 // Save the initial floatManager state for repeated trial reflows.
1545 // We'll restore (and re-save) the initial state each time we repeat the
1547 nsFloatManager::SavedState floatManagerState
;
1548 aReflowInput
.mFloatManager
->PushState(&floatManagerState
);
1550 aMetrics
= ReflowOutput(aMetrics
.GetWritingMode());
1552 TrialReflow(aPresContext
, aMetrics
, aReflowInput
, trialState
);
1554 // Do we need to start a `text-wrap: balance` iteration?
1557 // Don't try to balance an incomplete block.
1558 if (!reflowStatus
.IsFullyComplete()) {
1561 balanceTarget
.mOffset
=
1562 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1563 if (balanceTarget
.mOffset
< 2) {
1564 // If there are less than 2 lines, or the number exceeds the limit,
1565 // no balancing is needed; just break from the balance loop.
1568 // Initialize the amount of inset to try, and the iteration step size.
1569 balanceStep
= aReflowInput
.ComputedISize() / balanceTarget
.mOffset
;
1570 trialState
.ResetForBalance(balanceStep
);
1573 // If -webkit-line-clamp is in effect, then we need to maintain the
1574 // content location at which clamping occurs, rather than the total
1575 // number of lines in the block.
1576 if (StaticPrefs::layout_css_text_wrap_balance_after_clamp_enabled() &&
1577 IsLineClampRoot(this)) {
1578 uint32_t lineClampCount
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
1579 if (uint32_t(balanceTarget
.mOffset
) > lineClampCount
) {
1580 auto t
= getClampPosition(lineClampCount
);
1587 // Restore initial floatManager state for a new trial with updated inset.
1588 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1592 // Helper to determine whether the current trial succeeded (i.e. was able
1593 // to fit the content into the expected number of lines).
1594 auto trialSucceeded
= [&]() -> bool {
1595 if (!reflowStatus
.IsFullyComplete()) {
1598 if (balanceTarget
.mContent
) {
1599 auto t
= getClampPosition(aReflowInput
.mStyleDisplay
->mWebkitLineClamp
);
1600 return t
== balanceTarget
;
1603 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1604 return numLines
== balanceTarget
.mOffset
;
1607 // If we're in the process of a balance operation, check whether we've
1608 // inset by too much and either increase or reduce the inset for the next
1610 if (balanceStep
> 0) {
1611 if (trialSucceeded()) {
1612 trialState
.ResetForBalance(balanceStep
);
1614 trialState
.ResetForBalance(-balanceStep
);
1618 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1622 // If we were attempting to balance, check whether the final iteration was
1623 // successful, and if not, back up by one step.
1624 if (balanceTarget
.mOffset
>= 0) {
1625 if (trialSucceeded()) {
1628 trialState
.ResetForBalance(-1);
1630 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1634 // If we reach here, no balancing was required, so just exit; we don't
1635 // reset (pop) the floatManager state because this is the reflow we're
1636 // going to keep. So the saved state is just dropped.
1638 } // End of text-wrap: balance retry loop
1640 // If the block direction is right-to-left, we need to update the bounds of
1641 // lines that were placed relative to mContainerSize during reflow, as
1642 // we typically do not know the true container size until we've reflowed all
1643 // its children. So we use a dummy mContainerSize during reflow (see
1644 // BlockReflowState's constructor) and then fix up the positions of the
1645 // lines here, once the final block size is known.
1647 // Note that writing-mode:vertical-rl is the only case where the block
1648 // logical direction progresses in a negative physical direction, and
1649 // therefore block-dir coordinate conversion depends on knowing the width
1650 // of the coordinate space in order to translate between the logical and
1651 // physical origins.
1652 if (aReflowInput
.GetWritingMode().IsVerticalRL()) {
1653 nsSize containerSize
= aMetrics
.PhysicalSize();
1654 nscoord deltaX
= containerSize
.width
- trialState
.mContainerWidth
;
1656 // We compute our lines and markers' overflow areas later in
1657 // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
1659 const nsPoint
physicalDelta(deltaX
, 0);
1660 for (auto& line
: Lines()) {
1661 UpdateLineContainerSize(&line
, containerSize
);
1663 trialState
.mFcBounds
.Clear();
1664 for (nsIFrame
* f
: mFloats
) {
1665 f
->MovePositionBy(physicalDelta
);
1666 ConsiderChildOverflow(trialState
.mFcBounds
, f
);
1668 nsFrameList
* markerList
= GetOutsideMarkerList();
1670 for (nsIFrame
* f
: *markerList
) {
1671 f
->MovePositionBy(physicalDelta
);
1674 if (nsFrameList
* overflowContainers
= GetOverflowContainers()) {
1675 trialState
.mOcBounds
.Clear();
1676 for (nsIFrame
* f
: *overflowContainers
) {
1677 f
->MovePositionBy(physicalDelta
);
1678 ConsiderChildOverflow(trialState
.mOcBounds
, f
);
1684 aMetrics
.SetOverflowAreasToDesiredBounds();
1685 ComputeOverflowAreas(aMetrics
.mOverflowAreas
,
1686 trialState
.mBlockEndEdgeOfChildren
,
1687 aReflowInput
.mStyleDisplay
);
1688 // Factor overflow container child bounds into the overflow area
1689 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mOcBounds
);
1690 // Factor pushed float child bounds into the overflow area
1691 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mFcBounds
);
1693 // Let the absolutely positioned container reflow any absolutely positioned
1694 // child frames that need to be reflowed, e.g., elements with a percentage
1695 // based width/height
1696 // We want to do this under either of two conditions:
1697 // 1. If we didn't do the incremental reflow above.
1698 // 2. If our size changed.
1699 // Even though it's the padding edge that's the containing block, we
1700 // can use our rect (the border edge) since if the border style
1701 // changed, the reflow would have been targeted at us so we'd satisfy
1703 // XXX checking oldSize is bogus, there are various reasons we might have
1704 // reflowed but our size might not have been changed to what we
1705 // asked for (e.g., we ended up being pushed to a new page)
1706 // When WillReflowAgainForClearance is true, we will reflow again without
1707 // resetting the size. Because of this, we must not reflow our abs-pos
1708 // children in that situation --- what we think is our "new size" will not be
1709 // our real new size. This also happens to be more efficient.
1710 WritingMode parentWM
= aMetrics
.GetWritingMode();
1711 if (HasAbsolutelyPositionedChildren()) {
1712 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1713 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1714 if (aReflowInput
.WillReflowAgainForClearance() || haveInterrupt
) {
1715 // Make sure that when we reflow again we'll actually reflow all the abs
1716 // pos frames that might conceivably depend on our size (or all of them,
1717 // if we're dirty right now and interrupted; in that case we also need
1718 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1719 // better than that, because we don't really know what our size will be,
1720 // and it might in fact not change on the followup reflow!
1721 if (haveInterrupt
&& HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
1722 absoluteContainer
->MarkAllFramesDirty();
1724 absoluteContainer
->MarkSizeDependentFramesDirty();
1726 if (haveInterrupt
) {
1727 // We're not going to reflow absolute frames; make sure to account for
1728 // their existing overflow areas, which is usually a side effect of this
1731 // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1732 // interrupt, can we just rely on it and unconditionally take the else
1733 // branch below? That's a bit more subtle / risky, since I don't see
1734 // what would reflow them in that case if they depended on our size.
1735 for (nsIFrame
* kid
= absoluteContainer
->GetChildList().FirstChild();
1736 kid
; kid
= kid
->GetNextSibling()) {
1737 ConsiderChildOverflow(aMetrics
.mOverflowAreas
, kid
);
1741 LogicalSize containingBlockSize
=
1742 CalculateContainingBlockSizeForAbsolutes(parentWM
, aReflowInput
,
1743 aMetrics
.Size(parentWM
));
1745 // Mark frames that depend on changes we just made to this frame as dirty:
1746 // Now we can assume that the padding edge hasn't moved.
1747 // We need to reflow the absolutes if one of them depends on
1748 // its placeholder position, or the containing block size in a
1749 // direction in which the containing block size might have
1752 // XXX "width" and "height" in this block will become ISize and BSize
1753 // when nsAbsoluteContainingBlock is logicalized
1754 bool cbWidthChanged
= aMetrics
.Width() != oldSize
.width
;
1755 bool isRoot
= !GetContent()->GetParent();
1756 // If isRoot and we have auto height, then we are the initial
1757 // containing block and the containing block height is the
1758 // viewport height, which can't change during incremental
1760 bool cbHeightChanged
=
1761 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedHeight()) &&
1762 aMetrics
.Height() != oldSize
.height
;
1764 nsRect
containingBlock(nsPoint(0, 0),
1765 containingBlockSize
.GetPhysicalSize(parentWM
));
1766 AbsPosReflowFlags flags
= AbsPosReflowFlags::ConstrainHeight
;
1767 if (cbWidthChanged
) {
1768 flags
|= AbsPosReflowFlags::CBWidthChanged
;
1770 if (cbHeightChanged
) {
1771 flags
|= AbsPosReflowFlags::CBHeightChanged
;
1773 // Setup the line cursor here to optimize line searching for
1774 // calculating hypothetical position of absolutely-positioned
1776 SetupLineCursorForQuery();
1777 absoluteContainer
->Reflow(this, aPresContext
, aReflowInput
, reflowStatus
,
1778 containingBlock
, flags
,
1779 &aMetrics
.mOverflowAreas
);
1783 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1785 aStatus
= reflowStatus
;
1788 // Between when we drain pushed floats and when we complete reflow,
1789 // we're allowed to have multiple continuations of the same float on
1790 // our floats list, since a first-in-flow might get pushed to a later
1791 // continuation of its containing block. But it's not permitted
1792 // outside that time.
1793 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1796 IndentBy(stdout
, gNoiseIndent
);
1798 printf(": status=%s metrics=%d,%d carriedMargin=%d",
1799 ToString(aStatus
).c_str(), aMetrics
.ISize(parentWM
),
1800 aMetrics
.BSize(parentWM
), aMetrics
.mCarriedOutBEndMargin
.get());
1801 if (HasOverflowAreas()) {
1802 printf(" overflow-vis={%d,%d,%d,%d}", aMetrics
.InkOverflow().x
,
1803 aMetrics
.InkOverflow().y
, aMetrics
.InkOverflow().width
,
1804 aMetrics
.InkOverflow().height
);
1805 printf(" overflow-scr={%d,%d,%d,%d}", aMetrics
.ScrollableOverflow().x
,
1806 aMetrics
.ScrollableOverflow().y
,
1807 aMetrics
.ScrollableOverflow().width
,
1808 aMetrics
.ScrollableOverflow().height
);
1813 if (gLameReflowMetrics
) {
1814 PRTime end
= PR_Now();
1816 int32_t ectc
= nsLineBox::GetCtorCount();
1817 int32_t numLines
= mLines
.size();
1821 PRTime delta
, perLineDelta
, lines
;
1822 lines
= int64_t(numLines
);
1823 delta
= end
- start
;
1824 perLineDelta
= delta
/ lines
;
1829 ": %" PRId64
" elapsed (%" PRId64
1830 " per line) (%d lines; %d new lines)",
1831 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1832 printf("%s\n", buf
);
1837 nsReflowStatus
nsBlockFrame::TrialReflow(nsPresContext
* aPresContext
,
1838 ReflowOutput
& aMetrics
,
1839 const ReflowInput
& aReflowInput
,
1840 TrialReflowState
& aTrialState
) {
1842 // Between when we drain pushed floats and when we complete reflow,
1843 // we're allowed to have multiple continuations of the same float on
1844 // our floats list, since a first-in-flow might get pushed to a later
1845 // continuation of its containing block. But it's not permitted
1846 // outside that time.
1847 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1850 // ALWAYS drain overflow. We never want to leave the previnflow's
1851 // overflow lines hanging around; block reflow depends on the
1852 // overflow line lists being cleared out between reflow passes.
1853 DrainOverflowLines();
1855 bool blockStartMarginRoot
, blockEndMarginRoot
;
1856 IsMarginRoot(&blockStartMarginRoot
, &blockEndMarginRoot
);
1858 BlockReflowState
state(aReflowInput
, aPresContext
, this, blockStartMarginRoot
,
1859 blockEndMarginRoot
, aTrialState
.mNeedFloatManager
,
1860 aTrialState
.mConsumedBSize
,
1861 aTrialState
.mEffectiveContentBoxBSize
,
1862 aTrialState
.mInset
);
1864 // Handle paginated overflow (see nsContainerFrame.h)
1865 nsReflowStatus ocStatus
;
1866 if (GetPrevInFlow()) {
1867 ReflowOverflowContainerChildren(
1868 aPresContext
, aReflowInput
, aTrialState
.mOcBounds
,
1869 ReflowChildFlags::Default
, ocStatus
, DefaultChildFrameMerge
,
1870 Some(state
.ContainerSize()));
1873 // Now that we're done cleaning up our overflow container lists, we can
1874 // give |state| its nsOverflowContinuationTracker.
1875 nsOverflowContinuationTracker
tracker(this, false);
1876 state
.mOverflowTracker
= &tracker
;
1878 // Drain & handle pushed floats
1879 DrainPushedFloats();
1880 ReflowPushedFloats(state
, aTrialState
.mFcBounds
);
1882 // If we're not dirty (which means we'll mark everything dirty later)
1883 // and our inline-size has changed, mark the lines dirty that we need to
1884 // mark dirty for a resize reflow.
1885 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.IsIResize()) {
1886 PrepareResizeReflow(state
);
1889 // The same for percentage text-indent, except conditioned on the
1891 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.mCBReflowInput
&&
1892 aReflowInput
.mCBReflowInput
->IsIResize() &&
1893 StyleText()->mTextIndent
.length
.HasPercent() && !mLines
.empty()) {
1894 mLines
.front()->MarkDirty();
1897 // For text-wrap:balance trials, we need to reflow all the lines even if
1898 // they're not all "dirty".
1899 if (aTrialState
.mBalancing
) {
1900 MarkAllDescendantLinesDirty(this);
1902 LazyMarkLinesDirty();
1906 ReflowDirtyLines(state
);
1908 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1909 // this frame from a previous iteration of reflow, then we should not return
1910 // a status with IsFullyComplete() equals to true, since we actually have
1911 // overflow, it's just already been handled.
1913 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1914 // and reflow them, but just in case it does, this is a safety precaution so
1915 // we don't end up with a placeholder pointing to frames that have already
1916 // been deleted as part of removing our next-in-flow.
1917 if (state
.mReflowStatus
.IsFullyComplete()) {
1918 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1920 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1921 if (nif
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
1922 state
.mReflowStatus
.SetOverflowIncomplete();
1924 state
.mReflowStatus
.SetIncomplete();
1929 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1933 state
.mReflowStatus
.MergeCompletionStatusFrom(ocStatus
);
1935 // If we end in a BR with clear and affected floats continue,
1936 // we need to continue, too.
1937 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize() &&
1938 state
.mReflowStatus
.IsComplete() &&
1939 state
.FloatManager()->ClearContinues(FindTrailingClear())) {
1940 state
.mReflowStatus
.SetIncomplete();
1943 if (!state
.mReflowStatus
.IsFullyComplete()) {
1944 if (HasOverflowLines() || HasPushedFloats()) {
1945 state
.mReflowStatus
.SetNextInFlowNeedsReflow();
1950 printf(": block is not fully complete\n");
1954 // Place the ::marker's frame if it is placed next to a block child.
1956 // According to the CSS2 spec, section 12.6.1, the ::marker's box
1957 // participates in the height calculation of the list-item box's
1960 // There are exactly two places a ::marker can be placed: near the
1961 // first or second line. It's only placed on the second line in a
1962 // rare case: an empty first line followed by a second line that
1963 // contains a block (example: <LI>\n<P>... ). This is where
1964 // the second case can happen.
1965 if (HasOutsideMarker() && !mLines
.empty() &&
1966 (mLines
.front()->IsBlock() ||
1967 (0 == mLines
.front()->BSize() && mLines
.front() != mLines
.back() &&
1968 mLines
.begin().next()->IsBlock()))) {
1969 // Reflow the ::marker's frame.
1970 ReflowOutput
reflowOutput(aReflowInput
);
1971 // XXX Use the entire line when we fix bug 25888.
1972 nsLayoutUtils::LinePosition position
;
1973 WritingMode wm
= aReflowInput
.GetWritingMode();
1975 nsLayoutUtils::GetFirstLinePosition(wm
, this, &position
);
1976 nscoord lineBStart
=
1977 havePosition
? position
.mBStart
1978 : aReflowInput
.ComputedLogicalBorderPadding(wm
).BStart(wm
);
1979 nsIFrame
* marker
= GetOutsideMarker();
1980 ReflowOutsideMarker(marker
, state
, reflowOutput
, lineBStart
);
1981 NS_ASSERTION(!MarkerIsEmpty() || reflowOutput
.BSize(wm
) == 0,
1982 "empty ::marker frame took up space");
1984 if (havePosition
&& !MarkerIsEmpty()) {
1985 // We have some lines to align the ::marker with.
1987 // Doing the alignment using the baseline will also cater for
1988 // ::markers that are placed next to a child block (bug 92896)
1990 // Tall ::markers won't look particularly nice here...
1992 marker
->GetLogicalRect(wm
, reflowOutput
.PhysicalSize());
1993 const auto baselineGroup
= BaselineSharingGroup::First
;
1994 Maybe
<nscoord
> result
;
1995 if (MOZ_LIKELY(!wm
.IsOrthogonalTo(marker
->GetWritingMode()))) {
1996 result
= marker
->GetNaturalBaselineBOffset(
1997 wm
, baselineGroup
, BaselineExportContext::LineLayout
);
1999 const auto markerBaseline
= result
.valueOrFrom([bbox
, wm
, marker
]() {
2000 return bbox
.BSize(wm
) + marker
->GetLogicalUsedMargin(wm
).BEnd(wm
);
2002 bbox
.BStart(wm
) = position
.mBaseline
- markerBaseline
;
2003 marker
->SetRect(wm
, bbox
, reflowOutput
.PhysicalSize());
2005 // Otherwise just leave the ::marker where it is, up against our
2006 // block-start padding.
2009 // Clear any existing -webkit-line-clamp ellipsis.
2010 if (aReflowInput
.mStyleDisplay
->mWebkitLineClamp
) {
2011 ClearLineClampEllipsis();
2016 // Compute our final size (for this trial layout)
2017 aTrialState
.mBlockEndEdgeOfChildren
=
2018 ComputeFinalSize(aReflowInput
, state
, aMetrics
);
2019 aTrialState
.mContainerWidth
= state
.ContainerSize().width
;
2021 return state
.mReflowStatus
;
2024 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
2025 for (auto& line
: Reversed(Lines())) {
2026 if (0 != line
.BSize() || !line
.CachedIsEmpty()) {
2029 if (line
.HasClearance()) {
2036 static nsLineBox
* FindLineClampTarget(nsBlockFrame
*& aFrame
,
2037 StyleLineClamp aLineNumber
) {
2038 MOZ_ASSERT(aLineNumber
> 0);
2039 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2040 "Should have been removed earlier in nsBlockReflow::Reflow");
2042 nsLineBox
* target
= nullptr;
2043 nsBlockFrame
* targetFrame
= nullptr;
2044 bool foundFollowingLine
= false;
2046 LineClampLineIterator
iter(aFrame
);
2048 while (nsLineBox
* line
= iter
.GetCurrentLine()) {
2049 MOZ_ASSERT(!line
->HasLineClampEllipsis(),
2050 "Should have been removed earlier in nsBlockFrame::Reflow");
2051 MOZ_ASSERT(!iter
.GetCurrentFrame()->HasAnyStateBits(
2052 NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2053 "Should have been removed earlier in nsBlockReflow::Reflow");
2055 // Don't count a line that only has collapsible white space (as might exist
2056 // after calling e.g. getBoxQuads).
2057 if (line
->IsEmpty()) {
2062 if (aLineNumber
== 0) {
2063 // We already previously found our target line, and now we have
2064 // confirmed that there is another line after it.
2065 foundFollowingLine
= true;
2069 if (--aLineNumber
== 0) {
2070 // This is our target line. Continue looping to confirm that we
2071 // have another line after us.
2073 targetFrame
= iter
.GetCurrentFrame();
2079 if (!foundFollowingLine
) {
2085 MOZ_ASSERT(targetFrame
);
2087 aFrame
= targetFrame
;
2091 static nscoord
ApplyLineClamp(const ReflowInput
& aReflowInput
,
2092 nsBlockFrame
* aFrame
,
2093 nscoord aContentBlockEndEdge
) {
2094 if (!IsLineClampRoot(aFrame
)) {
2095 return aContentBlockEndEdge
;
2097 auto lineClamp
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
2098 nsBlockFrame
* frame
= aFrame
;
2099 nsLineBox
* line
= FindLineClampTarget(frame
, lineClamp
);
2101 // The number of lines did not exceed the -webkit-line-clamp value.
2102 return aContentBlockEndEdge
;
2105 // Mark the line as having an ellipsis so that TextOverflow will render it.
2106 line
->SetHasLineClampEllipsis();
2107 frame
->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
2109 // Translate the b-end edge of the line up to aFrame's space.
2110 nscoord edge
= line
->BEnd();
2111 for (nsIFrame
* f
= frame
; f
!= aFrame
; f
= f
->GetParent()) {
2113 f
->GetLogicalPosition(f
->GetParent()->GetSize()).B(f
->GetWritingMode());
2119 nscoord
nsBlockFrame::ComputeFinalSize(const ReflowInput
& aReflowInput
,
2120 BlockReflowState
& aState
,
2121 ReflowOutput
& aMetrics
) {
2122 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2123 const LogicalMargin
& borderPadding
= aState
.BorderPadding();
2124 #ifdef NOISY_FINAL_SIZE
2126 printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
2127 aState
.mBCoord
, aState
.mFlags
.mIsBEndMarginRoot
? "yes" : "no",
2128 aState
.mPrevBEndMargin
.get(), borderPadding
.BStart(wm
),
2129 borderPadding
.BEnd(wm
));
2132 // Compute final inline size
2133 LogicalSize
finalSize(wm
);
2134 finalSize
.ISize(wm
) =
2135 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.IStart(wm
),
2136 aReflowInput
.ComputedISize()),
2137 borderPadding
.IEnd(wm
));
2139 // Return block-end margin information
2140 // rbs says he hit this assertion occasionally (see bug 86947), so
2141 // just set the margin to zero and we'll figure out why later
2142 // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
2143 // "someone else set the margin");
2144 nscoord nonCarriedOutBDirMargin
= 0;
2145 if (!aState
.mFlags
.mIsBEndMarginRoot
) {
2146 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
2147 // line with clearance and a non-zero block-start margin and all
2148 // subsequent lines are empty, then we do not allow our children's
2149 // carried out block-end margin to be carried out of us and collapse
2150 // with our own block-end margin.
2151 if (CheckForCollapsedBEndMarginFromClearanceLine()) {
2152 // Convert the children's carried out margin to something that
2153 // we will include in our height
2154 nonCarriedOutBDirMargin
= aState
.mPrevBEndMargin
.get();
2155 aState
.mPrevBEndMargin
.Zero();
2157 aMetrics
.mCarriedOutBEndMargin
= aState
.mPrevBEndMargin
;
2159 aMetrics
.mCarriedOutBEndMargin
.Zero();
2162 nscoord blockEndEdgeOfChildren
= aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2163 // Shrink wrap our height around our contents.
2164 if (aState
.mFlags
.mIsBEndMarginRoot
||
2165 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2166 // When we are a block-end-margin root make sure that our last
2167 // child's block-end margin is fully applied. We also do this when
2168 // we have a computed height, since in that case the carried out
2169 // margin is not going to be applied anywhere, so we should note it
2170 // here to be included in the overflow area.
2171 // Apply the margin only if there's space for it.
2172 if (blockEndEdgeOfChildren
< aState
.mReflowInput
.AvailableBSize()) {
2173 // Truncate block-end margin if it doesn't fit to our available BSize.
2174 blockEndEdgeOfChildren
=
2175 std::min(blockEndEdgeOfChildren
+ aState
.mPrevBEndMargin
.get(),
2176 aState
.mReflowInput
.AvailableBSize());
2179 if (aState
.mFlags
.mBlockNeedsFloatManager
) {
2180 // Include the float manager's state to properly account for the
2181 // block-end margin of any floated elements; e.g., inside a table cell.
2183 // Note: The block coordinate returned by ClearFloats is always greater than
2184 // or equal to blockEndEdgeOfChildren.
2185 std::tie(blockEndEdgeOfChildren
, std::ignore
) =
2186 aState
.ClearFloats(blockEndEdgeOfChildren
, StyleClear::Both
);
2189 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2190 // Note: We don't use blockEndEdgeOfChildren because it includes the
2192 const nscoord contentBSizeWithBStartBP
=
2193 aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2195 // We don't care about ApplyLineClamp's return value (the line-clamped
2196 // content BSize) in this explicit-BSize codepath, but we do still need to
2197 // call ApplyLineClamp for ellipsis markers to be placed as-needed.
2198 ApplyLineClamp(aState
.mReflowInput
, this, contentBSizeWithBStartBP
);
2200 finalSize
.BSize(wm
) = ComputeFinalBSize(aState
, contentBSizeWithBStartBP
);
2202 // If the content block-size is larger than the effective computed
2203 // block-size, we extend the block-size to contain all the content.
2204 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
2205 if (aReflowInput
.ShouldApplyAutomaticMinimumOnBlockAxis()) {
2206 // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
2207 // the content's block-size plus our border and padding..
2208 finalSize
.BSize(wm
) =
2209 std::max(finalSize
.BSize(wm
),
2210 contentBSizeWithBStartBP
+ borderPadding
.BEnd(wm
));
2213 // Don't carry out a block-end margin when our BSize is fixed.
2215 // Note: this also includes the case that aReflowInput.ComputedBSize() is
2216 // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
2217 // is replaced by the block size from aspect-ratio and inline size.
2218 aMetrics
.mCarriedOutBEndMargin
.Zero();
2220 Maybe
<nscoord
> containBSize
= ContainIntrinsicBSize(
2221 IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE
: 0);
2222 if (containBSize
&& *containBSize
!= NS_UNCONSTRAINEDSIZE
) {
2223 // If we're size-containing in block axis and we don't have a specified
2224 // block size, then our final size should actually be computed from only
2225 // our border, padding and contain-intrinsic-block-size, ignoring the
2226 // actual contents. Hence this case is a simplified version of the case
2229 // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
2230 // special case when it has 'contain-intrinsic-block-size: none', because
2231 // comboboxes implicitly honors the size-containment behavior on its
2232 // nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the
2233 // nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even
2234 // need any special content-size-ignoring behavior in its reflow method,
2235 // because that method just resolves "auto" BSize values to one
2236 // line-height rather than by measuring its contents' BSize.)
2237 nscoord contentBSize
= *containBSize
;
2239 aReflowInput
.ApplyMinMaxBSize(contentBSize
, aState
.mConsumedBSize
);
2240 aMetrics
.mCarriedOutBEndMargin
.Zero();
2241 autoBSize
+= borderPadding
.BStartEnd(wm
);
2242 finalSize
.BSize(wm
) = autoBSize
;
2243 } else if (aState
.mReflowStatus
.IsInlineBreakBefore()) {
2244 // Our parent is expected to push this frame to the next page/column so
2245 // what size we set here doesn't really matter.
2246 finalSize
.BSize(wm
) = aReflowInput
.AvailableBSize();
2247 } else if (aState
.mReflowStatus
.IsComplete()) {
2248 const nscoord lineClampedContentBlockEndEdge
=
2249 ApplyLineClamp(aReflowInput
, this, blockEndEdgeOfChildren
);
2251 const nscoord bpBStart
= borderPadding
.BStart(wm
);
2252 const nscoord contentBSize
= blockEndEdgeOfChildren
- bpBStart
;
2253 const nscoord lineClampedContentBSize
=
2254 lineClampedContentBlockEndEdge
- bpBStart
;
2256 const nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(
2257 lineClampedContentBSize
, aState
.mConsumedBSize
);
2258 if (autoBSize
!= contentBSize
) {
2259 // Our min-block-size, max-block-size, or -webkit-line-clamp value made
2260 // our bsize change. Don't carry out our kids' block-end margins.
2261 aMetrics
.mCarriedOutBEndMargin
.Zero();
2263 nscoord bSize
= autoBSize
+ borderPadding
.BStartEnd(wm
);
2264 if (MOZ_UNLIKELY(autoBSize
> contentBSize
&&
2265 bSize
> aReflowInput
.AvailableBSize() &&
2266 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
)) {
2267 // Applying `min-size` made us overflow our available size.
2268 // Clamp it and report that we're Incomplete, or BreakBefore if we have
2269 // 'break-inside: avoid' that is applicable.
2270 bSize
= aReflowInput
.AvailableBSize();
2271 if (ShouldAvoidBreakInside(aReflowInput
)) {
2272 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
2274 aState
.mReflowStatus
.SetIncomplete();
2277 finalSize
.BSize(wm
) = bSize
;
2280 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
2281 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
2282 nscoord bSize
= std::max(aState
.mBCoord
, aReflowInput
.AvailableBSize());
2283 if (aReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
2284 // This should never happen, but it does. See bug 414255
2285 bSize
= aState
.mBCoord
;
2287 const nscoord maxBSize
= aReflowInput
.ComputedMaxBSize();
2288 if (maxBSize
!= NS_UNCONSTRAINEDSIZE
&&
2289 aState
.mConsumedBSize
+ bSize
- borderPadding
.BStart(wm
) > maxBSize
) {
2290 // Compute this fragment's block-size, with the max-block-size
2291 // constraint taken into consideration.
2292 const nscoord clampedBSizeWithoutEndBP
=
2293 std::max(0, maxBSize
- aState
.mConsumedBSize
) +
2294 borderPadding
.BStart(wm
);
2295 const nscoord clampedBSize
=
2296 clampedBSizeWithoutEndBP
+ borderPadding
.BEnd(wm
);
2297 if (clampedBSize
<= aReflowInput
.AvailableBSize()) {
2298 // We actually fit after applying `max-size` so we should be
2299 // Overflow-Incomplete instead.
2300 bSize
= clampedBSize
;
2301 aState
.mReflowStatus
.SetOverflowIncomplete();
2303 // We cannot fit after applying `max-size` with our block-end BP, so
2304 // we should draw it in our next continuation.
2305 bSize
= clampedBSizeWithoutEndBP
;
2308 finalSize
.BSize(wm
) = bSize
;
2312 if (IsTrueOverflowContainer()) {
2313 if (aState
.mReflowStatus
.IsIncomplete()) {
2314 // Overflow containers can only be overflow complete.
2315 // Note that auto height overflow containers have no normal children
2316 NS_ASSERTION(finalSize
.BSize(wm
) == 0,
2317 "overflow containers must be zero-block-size");
2318 aState
.mReflowStatus
.SetOverflowIncomplete();
2320 } else if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2321 !aState
.mReflowStatus
.IsInlineBreakBefore() &&
2322 aState
.mReflowStatus
.IsComplete()) {
2323 // Currently only used for grid items, but could be used in other contexts.
2324 // The FragStretchBSizeProperty is our expected non-fragmented block-size
2325 // we should stretch to (for align-self:stretch etc). In some fragmentation
2326 // cases though, the last fragment (this frame since we're complete), needs
2327 // to have extra size applied because earlier fragments consumed too much of
2328 // our computed size due to overflowing their containing block. (E.g. this
2329 // ensures we fill the last row when a multi-row grid item is fragmented).
2331 nscoord bSize
= GetProperty(FragStretchBSizeProperty(), &found
);
2333 finalSize
.BSize(wm
) = std::max(bSize
, finalSize
.BSize(wm
));
2337 // Clamp the content size to fit within the margin-box clamp size, if any.
2338 if (MOZ_UNLIKELY(aReflowInput
.mComputeSizeFlags
.contains(
2339 ComputeSizeFlag::BClampMarginBoxMinSize
)) &&
2340 aState
.mReflowStatus
.IsComplete()) {
2342 nscoord cbSize
= GetProperty(BClampMarginBoxMinSizeProperty(), &found
);
2344 auto marginBoxBSize
=
2345 finalSize
.BSize(wm
) +
2346 aReflowInput
.ComputedLogicalMargin(wm
).BStartEnd(wm
);
2347 auto overflow
= marginBoxBSize
- cbSize
;
2349 auto contentBSize
= finalSize
.BSize(wm
) - borderPadding
.BStartEnd(wm
);
2350 auto newContentBSize
= std::max(nscoord(0), contentBSize
- overflow
);
2351 // XXXmats deal with percentages better somehow?
2352 finalSize
.BSize(wm
) -= contentBSize
- newContentBSize
;
2357 // Screen out negative block sizes --- can happen due to integer overflows :-(
2358 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
2360 if (blockEndEdgeOfChildren
!= finalSize
.BSize(wm
) - borderPadding
.BEnd(wm
)) {
2361 SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren
);
2363 RemoveProperty(BlockEndEdgeOfChildrenProperty());
2366 aMetrics
.SetSize(wm
, finalSize
);
2369 if ((ABSURD_SIZE(aMetrics
.Width()) || ABSURD_SIZE(aMetrics
.Height())) &&
2370 !GetParent()->IsAbsurdSizeAssertSuppressed()) {
2372 printf(": WARNING: desired:%d,%d\n", aMetrics
.Width(), aMetrics
.Height());
2376 return blockEndEdgeOfChildren
;
2379 void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
2380 OverflowAreas
& aOverflowAreas
, nscoord aBEndEdgeOfChildren
,
2381 const nsStyleDisplay
* aDisplay
) const {
2382 const auto wm
= GetWritingMode();
2384 // Factor in the block-end edge of the children. Child frames will be added
2385 // to the overflow area as we iterate through the lines, but their margins
2386 // won't, so we need to account for block-end margins here.
2387 // REVIEW: For now, we do this for both visual and scrollable area,
2388 // although when we make scrollable overflow area not be a subset of
2389 // visual, we can change this.
2391 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
2392 // If we are a scrolled inner frame, add our block-end padding to our
2393 // children's block-end edge.
2395 // Note: aBEndEdgeOfChildren already includes our own block-start padding
2396 // because it is relative to our block-start edge of our border-box, which
2397 // is the same as our padding-box here.
2398 MOZ_ASSERT(GetLogicalUsedBorderAndPadding(wm
) == GetLogicalUsedPadding(wm
),
2399 "A scrolled inner frame shouldn't have any border!");
2400 aBEndEdgeOfChildren
+= GetLogicalUsedPadding(wm
).BEnd(wm
);
2403 // XXX Currently, overflow areas are stored as physical rects, so we have
2404 // to handle writing modes explicitly here. If we change overflow rects
2405 // to be stored logically, this can be simplified again.
2406 if (wm
.IsVertical()) {
2407 if (wm
.IsVerticalLR()) {
2408 for (const auto otype
: AllOverflowTypes()) {
2409 if (!(aDisplay
->IsContainLayout() &&
2410 otype
== OverflowType::Scrollable
)) {
2411 // Layout containment should force all overflow to be ink (visual)
2412 // overflow, so if we're layout-contained, we only add our children's
2413 // block-end edge to the ink (visual) overflow -- not to the
2414 // scrollable overflow.
2415 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2416 o
.width
= std::max(o
.XMost(), aBEndEdgeOfChildren
) - o
.x
;
2420 for (const auto otype
: AllOverflowTypes()) {
2421 if (!(aDisplay
->IsContainLayout() &&
2422 otype
== OverflowType::Scrollable
)) {
2423 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2424 nscoord xmost
= o
.XMost();
2425 o
.x
= std::min(o
.x
, xmost
- aBEndEdgeOfChildren
);
2426 o
.width
= xmost
- o
.x
;
2431 for (const auto otype
: AllOverflowTypes()) {
2432 if (!(aDisplay
->IsContainLayout() && otype
== OverflowType::Scrollable
)) {
2433 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2434 o
.height
= std::max(o
.YMost(), aBEndEdgeOfChildren
) - o
.y
;
2440 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas
& aOverflowAreas
,
2441 nscoord aBEndEdgeOfChildren
,
2442 const nsStyleDisplay
* aDisplay
) const {
2443 // XXX_perf: This can be done incrementally. It is currently one of
2444 // the things that makes incremental reflow O(N^2).
2445 auto overflowClipAxes
= ShouldApplyOverflowClipping(aDisplay
);
2446 auto overflowClipMargin
= OverflowClipMargin(overflowClipAxes
);
2447 if (overflowClipAxes
== PhysicalAxes::Both
&&
2448 overflowClipMargin
== nsSize()) {
2452 // We rely here on our caller having called SetOverflowAreasToDesiredBounds().
2453 nsRect frameBounds
= aOverflowAreas
.ScrollableOverflow();
2455 for (const auto& line
: Lines()) {
2456 if (aDisplay
->IsContainLayout()) {
2457 // If we have layout containment, we should only consider our child's
2458 // ink overflow, leaving the scrollable regions of the parent
2460 // Note: scrollable overflow is a subset of ink overflow,
2461 // so this has the same affect as unioning the child's visual and
2462 // scrollable overflow with its parent's ink overflow.
2463 nsRect childVisualRect
= line
.InkOverflowRect();
2464 OverflowAreas childVisualArea
= OverflowAreas(childVisualRect
, nsRect());
2465 aOverflowAreas
.UnionWith(childVisualArea
);
2467 aOverflowAreas
.UnionWith(line
.GetOverflowAreas());
2471 // Factor an outside ::marker in; normally the ::marker will be factored
2472 // into the line-box's overflow areas. However, if the line is a block
2473 // line then it won't; if there are no lines, it won't. So just
2474 // factor it in anyway (it can't hurt if it was already done).
2475 // XXXldb Can we just fix GetOverflowArea instead?
2476 if (nsIFrame
* outsideMarker
= GetOutsideMarker()) {
2477 aOverflowAreas
.UnionAllWith(outsideMarker
->GetRect());
2480 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, aBEndEdgeOfChildren
, aDisplay
);
2482 if (overflowClipAxes
!= PhysicalAxes::None
) {
2483 aOverflowAreas
.ApplyClipping(frameBounds
, overflowClipAxes
,
2484 overflowClipMargin
);
2487 #ifdef NOISY_OVERFLOW_AREAS
2488 printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
2489 ToString(aOverflowAreas
.InkOverflow()).c_str(),
2490 ToString(aOverflowAreas
.ScrollableOverflow()).c_str());
2494 void nsBlockFrame::UnionChildOverflow(OverflowAreas
& aOverflowAreas
) {
2495 // We need to update the overflow areas of lines manually, as they
2496 // get cached and re-used otherwise. Lines aren't exposed as normal
2497 // frame children, so calling UnionChildOverflow alone will end up
2498 // using the old cached values.
2499 for (auto& line
: Lines()) {
2500 nsRect bounds
= line
.GetPhysicalBounds();
2501 OverflowAreas
lineAreas(bounds
, bounds
);
2503 int32_t n
= line
.GetChildCount();
2504 for (nsIFrame
* lineFrame
= line
.mFirstChild
; n
> 0;
2505 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2506 ConsiderChildOverflow(lineAreas
, lineFrame
);
2509 // Consider the overflow areas of the floats attached to the line as well
2510 if (line
.HasFloats()) {
2511 for (nsIFrame
* f
: line
.Floats()) {
2512 ConsiderChildOverflow(lineAreas
, f
);
2516 line
.SetOverflowAreas(lineAreas
);
2517 aOverflowAreas
.UnionWith(lineAreas
);
2520 // Union with child frames, skipping the principal and float lists
2521 // since we already handled those using the line boxes.
2522 nsLayoutUtils::UnionChildOverflow(
2523 this, aOverflowAreas
,
2524 {FrameChildListID::Principal
, FrameChildListID::Float
});
2527 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas
& aOverflowAreas
) {
2529 nscoord blockEndEdgeOfChildren
=
2530 GetProperty(BlockEndEdgeOfChildrenProperty(), &found
);
2532 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, blockEndEdgeOfChildren
,
2536 // Line cursor invariants depend on the overflow areas of the lines, so
2537 // we must clear the line cursor since those areas may have changed.
2539 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
2542 void nsBlockFrame::LazyMarkLinesDirty() {
2543 if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
)) {
2544 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2545 line
!= line_end
; ++line
) {
2546 int32_t n
= line
->GetChildCount();
2547 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2548 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2549 if (lineFrame
->IsSubtreeDirty()) {
2550 // NOTE: MarkLineDirty does more than just marking the line dirty.
2551 MarkLineDirty(line
, &mLines
);
2556 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
2560 void nsBlockFrame::MarkLineDirty(LineIterator aLine
,
2561 const nsLineList
* aLineList
) {
2564 aLine
->SetInvalidateTextRuns(true);
2567 IndentBy(stdout
, gNoiseIndent
);
2569 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
2573 // Mark previous line dirty if it's an inline line so that it can
2574 // maybe pullup something from the line just affected.
2575 // XXX We don't need to do this if aPrevLine ends in a break-after...
2576 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
2577 aLine
.prev()->IsInline()) {
2578 aLine
.prev()->MarkDirty();
2579 aLine
.prev()->SetInvalidateTextRuns(true);
2582 IndentBy(stdout
, gNoiseIndent
);
2584 printf(": mark prev-line %p dirty\n",
2585 static_cast<void*>(aLine
.prev().get()));
2592 * Test whether lines are certain to be aligned left so that we can make
2593 * resizing optimizations
2595 static inline bool IsAlignedLeft(StyleTextAlign aAlignment
,
2596 StyleDirection aDirection
,
2597 StyleUnicodeBidi aUnicodeBidi
,
2599 return aFrame
->IsInSVGTextSubtree() || StyleTextAlign::Left
== aAlignment
||
2600 (((StyleTextAlign::Start
== aAlignment
&&
2601 StyleDirection::Ltr
== aDirection
) ||
2602 (StyleTextAlign::End
== aAlignment
&&
2603 StyleDirection::Rtl
== aDirection
)) &&
2604 aUnicodeBidi
!= StyleUnicodeBidi::Plaintext
);
2607 void nsBlockFrame::PrepareResizeReflow(BlockReflowState
& aState
) {
2608 // See if we can try and avoid marking all the lines as dirty
2609 // FIXME(emilio): This should be writing-mode aware, I guess.
2610 bool tryAndSkipLines
=
2611 // The left content-edge must be a constant distance from the left
2613 !StylePadding()->mPadding
.Get(eSideLeft
).HasPercent();
2616 if (gDisableResizeOpt
) {
2617 tryAndSkipLines
= false;
2620 if (!tryAndSkipLines
) {
2621 IndentBy(stdout
, gNoiseIndent
);
2623 printf(": marking all lines dirty: availISize=%d\n",
2624 aState
.mReflowInput
.AvailableISize());
2629 if (tryAndSkipLines
) {
2630 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2631 nscoord newAvailISize
=
2632 aState
.mReflowInput
.ComputedLogicalBorderPadding(wm
).IStart(wm
) +
2633 aState
.mReflowInput
.ComputedISize();
2637 IndentBy(stdout
, gNoiseIndent
);
2639 printf(": trying to avoid marking all lines dirty\n");
2643 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2644 line
!= line_end
; ++line
) {
2645 // We let child blocks make their own decisions the same
2647 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2648 if (line
->IsBlock() || line
->HasFloats() ||
2649 (!isLastLine
&& !line
->HasForcedLineBreakAfter()) ||
2650 ((isLastLine
|| !line
->IsLineWrapped())) ||
2651 line
->ResizeReflowOptimizationDisabled() ||
2652 line
->IsImpactedByFloat() || (line
->IEnd() > newAvailISize
)) {
2656 #ifdef REALLY_NOISY_REFLOW
2657 if (!line
->IsBlock()) {
2658 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2659 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
2663 if (gNoisyReflow
&& !line
->IsDirty()) {
2664 IndentBy(stdout
, gNoiseIndent
+ 1);
2666 "skipped: line=%p next=%p %s %s%s%s clearTypeBefore/After=%s/%s "
2668 static_cast<void*>(line
.get()),
2670 (line
.next() != LinesEnd() ? line
.next().get() : nullptr)),
2671 line
->IsBlock() ? "block" : "inline",
2672 line
->HasForcedLineBreakAfter() ? "has-break-after " : "",
2673 line
->HasFloats() ? "has-floats " : "",
2674 line
->IsImpactedByFloat() ? "impacted " : "",
2675 line
->StyleClearToString(line
->FloatClearTypeBefore()),
2676 line
->StyleClearToString(line
->FloatClearTypeAfter()),
2682 // Mark everything dirty
2683 for (auto& line
: Lines()) {
2689 //----------------------------------------
2692 * Propagate reflow "damage" from from earlier lines to the current
2693 * line. The reflow damage comes from the following sources:
2694 * 1. The regions of float damage remembered during reflow.
2695 * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
2696 * float, either the previous reflow or now.
2698 * When entering this function, |aLine| is still at its old position and
2699 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2700 * doesn't get marked dirty and reflowed entirely).
2702 void nsBlockFrame::PropagateFloatDamage(BlockReflowState
& aState
,
2704 nscoord aDeltaBCoord
) {
2705 nsFloatManager
* floatManager
= aState
.FloatManager();
2707 (aState
.mReflowInput
.mParentReflowInput
&&
2708 aState
.mReflowInput
.mParentReflowInput
->mFloatManager
== floatManager
) ||
2709 aState
.mReflowInput
.mBlockDelta
== 0,
2710 "Bad block delta passed in");
2712 // Check to see if there are any floats; if there aren't, there can't
2713 // be any float damage
2714 if (!floatManager
->HasAnyFloats()) {
2718 // Check the damage region recorded in the float damage.
2719 if (floatManager
->HasFloatDamage()) {
2720 // Need to check mBounds *and* mCombinedArea to find intersections
2721 // with aLine's floats
2722 nscoord lineBCoordBefore
= aLine
->BStart() + aDeltaBCoord
;
2723 nscoord lineBCoordAfter
= lineBCoordBefore
+ aLine
->BSize();
2724 // Scrollable overflow should be sufficient for things that affect
2726 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2727 nsSize containerSize
= aState
.ContainerSize();
2728 LogicalRect overflow
=
2729 aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
, containerSize
);
2730 nscoord lineBCoordCombinedBefore
= overflow
.BStart(wm
) + aDeltaBCoord
;
2731 nscoord lineBCoordCombinedAfter
=
2732 lineBCoordCombinedBefore
+ overflow
.BSize(wm
);
2735 floatManager
->IntersectsDamage(lineBCoordBefore
, lineBCoordAfter
) ||
2736 floatManager
->IntersectsDamage(lineBCoordCombinedBefore
,
2737 lineBCoordCombinedAfter
);
2744 // Check if the line is moving relative to the float manager
2745 if (aDeltaBCoord
+ aState
.mReflowInput
.mBlockDelta
!= 0) {
2746 if (aLine
->IsBlock()) {
2747 // Unconditionally reflow sliding blocks; we only really need to reflow
2748 // if there's a float impacting this block, but the current float manager
2749 // makes it difficult to check that. Therefore, we let the child block
2750 // decide what it needs to reflow.
2753 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
2754 nsFlowAreaRect floatAvailableSpace
=
2755 aState
.GetFloatAvailableSpaceForBSize(aLine
->BStart() + aDeltaBCoord
,
2756 aLine
->BSize(), nullptr);
2758 #ifdef REALLY_NOISY_REFLOW
2759 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2760 wasImpactedByFloat
, floatAvailableSpace
.HasFloats());
2763 // Mark the line dirty if it was or is affected by a float
2764 // We actually only really need to reflow if the amount of impact
2765 // changes, but that's not straightforward to check
2766 if (wasImpactedByFloat
|| floatAvailableSpace
.HasFloats()) {
2773 static bool LineHasClear(nsLineBox
* aLine
) {
2774 return aLine
->IsBlock()
2775 ? (aLine
->HasForcedLineBreakBefore() ||
2776 aLine
->mFirstChild
->HasAnyStateBits(
2777 NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
2778 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
2779 : aLine
->HasFloatClearTypeAfter();
2783 * Reparent a whole list of floats from aOldParent to this block. The
2784 * floats might be taken from aOldParent's overflow list. They will be
2785 * removed from the list. They end up appended to our mFloats list.
2787 void nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
2788 nsBlockFrame
* aOldParent
,
2789 bool aReparentSiblings
) {
2791 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
2792 if (list
.NotEmpty()) {
2793 for (nsIFrame
* f
: list
) {
2794 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
2795 "CollectFloats should've removed that bit");
2796 ReparentFrame(f
, aOldParent
, this);
2798 mFloats
.AppendFrames(nullptr, std::move(list
));
2802 static void DumpLine(const BlockReflowState
& aState
, nsLineBox
* aLine
,
2803 nscoord aDeltaBCoord
, int32_t aDeltaIndent
) {
2805 if (nsBlockFrame::gNoisyReflow
) {
2806 nsRect
ovis(aLine
->InkOverflowRect());
2807 nsRect
oscr(aLine
->ScrollableOverflowRect());
2808 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
2810 "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2811 "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2812 "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2813 static_cast<void*>(aLine
), aState
.mBCoord
,
2814 aLine
->IsDirty() ? "yes" : "no", aLine
->IStart(), aLine
->BStart(),
2815 aLine
->ISize(), aLine
->BSize(), ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
2816 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
, aDeltaBCoord
,
2817 aState
.mPrevBEndMargin
.get(), aLine
->GetChildCount());
2822 static bool LinesAreEmpty(const nsLineList
& aList
) {
2823 for (const auto& line
: aList
) {
2824 if (!line
.IsEmpty()) {
2831 void nsBlockFrame::ReflowDirtyLines(BlockReflowState
& aState
) {
2832 bool keepGoing
= true;
2833 bool repositionViews
= false; // should we really need this?
2834 bool foundAnyClears
= aState
.mTrailingClearFromPIF
!= StyleClear::None
;
2835 bool willReflowAgain
= false;
2839 IndentBy(stdout
, gNoiseIndent
);
2841 printf(": reflowing dirty lines");
2842 printf(" computedISize=%d\n", aState
.mReflowInput
.ComputedISize());
2844 AutoNoisyIndenter
indent(gNoisyReflow
);
2847 bool selfDirty
= HasAnyStateBits(NS_FRAME_IS_DIRTY
) ||
2848 (aState
.mReflowInput
.IsBResize() &&
2849 HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
));
2851 // Reflow our last line if our availableBSize has increased
2852 // so that we (and our last child) pull up content as necessary
2853 if (aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2855 aState
.mReflowInput
.AvailableBSize() >
2856 GetLogicalSize().BSize(aState
.mReflowInput
.GetWritingMode())) {
2857 LineIterator lastLine
= LinesEnd();
2858 if (lastLine
!= LinesBegin()) {
2860 lastLine
->MarkDirty();
2863 // the amount by which we will slide the current line if it is not
2865 nscoord deltaBCoord
= 0;
2867 // whether we did NOT reflow the previous line and thus we need to
2868 // recompute the carried out margin before the line if we want to
2869 // reflow it or if its previous margin is dirty
2870 bool needToRecoverState
= false;
2871 // Float continuations were reflowed in ReflowPushedFloats
2872 bool reflowedFloat
=
2873 mFloats
.NotEmpty() &&
2874 mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
2875 bool lastLineMovedUp
= false;
2876 // We save up information about BR-clearance here
2877 StyleClear inlineFloatClearType
= aState
.mTrailingClearFromPIF
;
2879 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2881 // Determine if children of this frame could have breaks between them for
2884 // We need to check for paginated layout, the named-page pref, and if the
2885 // available block-size is constrained.
2887 // Note that we need to check for paginated layout as named-pages are only
2888 // used during paginated reflow. We need to additionally check for
2889 // unconstrained block-size to avoid introducing fragmentation breaks during
2890 // "measuring" reflows within an overall paginated reflow, and to avoid
2891 // fragmentation in monolithic containers like 'inline-block'.
2893 // Because we can only break for named pages using Class A breakpoints, we
2894 // also need to check that the block flow direction of the containing frame
2895 // of these items (which is this block) is parallel to that of this page.
2896 // See: https://www.w3.org/TR/css-break-3/#btw-blocks
2897 const nsPresContext
* const presCtx
= aState
.mPresContext
;
2898 const bool canBreakForPageNames
=
2899 aState
.mReflowInput
.mFlags
.mCanHaveClassABreakpoints
&&
2900 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2901 presCtx
->GetPresShell()->GetRootFrame()->GetWritingMode().IsVertical() ==
2902 GetWritingMode().IsVertical();
2904 // ReflowInput.mFlags.mCanHaveClassABreakpoints should respect the named
2905 // pages pref and presCtx->IsPaginated, so we did not explicitly check these
2906 // above when setting canBreakForPageNames.
2907 if (canBreakForPageNames
) {
2908 MOZ_ASSERT(presCtx
->IsPaginated(),
2909 "canBreakForPageNames should not be set during non-paginated "
2913 // Reflow the lines that are already ours
2914 for (; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
2915 DumpLine(aState
, line
, deltaBCoord
, 0);
2917 AutoNoisyIndenter
indent2(gNoisyReflow
);
2924 // This really sucks, but we have to look inside any blocks that have clear
2925 // elements inside them.
2926 // XXX what can we do smarter here?
2927 if (!line
->IsDirty() && line
->IsBlock() &&
2928 line
->mFirstChild
->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
2932 nsIFrame
* floatAvoidingBlock
= nullptr;
2933 if (line
->IsBlock() &&
2934 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
2935 floatAvoidingBlock
= line
->mFirstChild
;
2938 // We have to reflow the line if it's a block whose clearance
2939 // might have changed, so detect that.
2940 if (!line
->IsDirty() &&
2941 (line
->HasForcedLineBreakBefore() || floatAvoidingBlock
)) {
2942 nscoord curBCoord
= aState
.mBCoord
;
2943 // See where we would be after applying any clearance due to
2945 if (inlineFloatClearType
!= StyleClear::None
) {
2946 std::tie(curBCoord
, std::ignore
) =
2947 aState
.ClearFloats(curBCoord
, inlineFloatClearType
);
2950 auto [newBCoord
, result
] = aState
.ClearFloats(
2951 curBCoord
, line
->FloatClearTypeBefore(), floatAvoidingBlock
);
2953 if (line
->HasClearance()) {
2954 // Reflow the line if it might not have clearance anymore.
2955 if (result
== ClearFloatsResult::BCoordNoChange
2956 // aState.mBCoord is the clearance point which should be the
2957 // block-start border-edge of the block frame. If sliding the
2958 // block by deltaBCoord isn't going to put it in the predicted
2959 // position, then we'd better reflow the line.
2960 || newBCoord
!= line
->BStart() + deltaBCoord
) {
2964 // Reflow the line if the line might have clearance now.
2965 if (result
!= ClearFloatsResult::BCoordNoChange
) {
2971 // We might have to reflow a line that is after a clearing BR.
2972 if (inlineFloatClearType
!= StyleClear::None
) {
2973 std::tie(aState
.mBCoord
, std::ignore
) =
2974 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
2975 if (aState
.mBCoord
!= line
->BStart() + deltaBCoord
) {
2976 // SlideLine is not going to put the line where the clearance
2977 // put it. Reflow the line to be sure.
2980 inlineFloatClearType
= StyleClear::None
;
2983 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
2984 if (previousMarginWasDirty
) {
2985 // If the previous margin is dirty, reflow the current line
2987 line
->ClearPreviousMarginDirty();
2988 } else if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
2989 const nscoord scrollableOverflowBEnd
=
2990 LogicalRect(line
->mWritingMode
, line
->ScrollableOverflowRect(),
2991 line
->mContainerSize
)
2992 .BEnd(line
->mWritingMode
);
2993 if (scrollableOverflowBEnd
+ deltaBCoord
> aState
.ContentBEnd()) {
2994 // Lines that aren't dirty but get slid past our available block-size
2995 // constraint must be reflowed.
3000 if (!line
->IsDirty()) {
3001 const bool isPaginated
=
3002 // Last column can be reflowed unconstrained during column balancing.
3003 // Hence the additional NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR bit check
3004 // as a fail-safe fallback.
3005 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
||
3006 HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR
) ||
3007 // Table can also be reflowed unconstrained during printing.
3008 aState
.mPresContext
->IsPaginated();
3010 // We are in a paginated context, i.e. in columns or pages.
3011 const bool mayContainFloats
=
3012 line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed();
3013 if (mayContainFloats
) {
3014 // The following if-else conditions check whether this line -- which
3015 // might have floats in its subtree, or has floats as direct children,
3016 // or had floats pushed -- needs to be reflowed.
3017 if (deltaBCoord
!= 0 || aState
.mReflowInput
.IsBResize()) {
3018 // The distance to the block-end edge might have changed. Reflow the
3019 // line both because the breakpoints within its floats may have
3020 // changed and because we might have to push/pull the floats in
3023 } else if (HasPushedFloats()) {
3024 // We had pushed floats which haven't been drained by our
3025 // next-in-flow, which means our parent is currently reflowing us
3026 // again due to clearance without creating a next-in-flow for us.
3027 // Reflow the line to redo the floats split logic to correctly set
3028 // our reflow status.
3030 } else if (aState
.mReflowInput
.mFlags
.mMustReflowPlaceholders
) {
3031 // Reflow the line (that may containing a float's placeholder frame)
3032 // if our parent tells us to do so.
3034 } else if (aState
.mReflowInput
.mFlags
.mMovedBlockFragments
) {
3035 // Our parent's line containing us moved to a different fragment.
3036 // Reflow the line because the decision about whether the float fits
3037 // may be different in a different fragment.
3044 if (!line
->IsDirty()) {
3045 // See if there's any reflow damage that requires that we mark the
3047 PropagateFloatDamage(aState
, line
, deltaBCoord
);
3050 // If the container size has changed, reset mContainerSize. If the
3051 // line's writing mode is not ltr, or if the line is not left-aligned, also
3052 // mark the line dirty.
3053 if (aState
.ContainerSize() != line
->mContainerSize
) {
3054 line
->mContainerSize
= aState
.ContainerSize();
3056 const bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
3057 const auto align
= isLastLine
? StyleText()->TextAlignForLastLine()
3058 : StyleText()->mTextAlign
;
3059 if (line
->mWritingMode
.IsVertical() || line
->mWritingMode
.IsBidiRTL() ||
3060 !IsAlignedLeft(align
, StyleVisibility()->mDirection
,
3061 StyleTextReset()->mUnicodeBidi
, this)) {
3066 // Check for a page break caused by CSS named pages.
3068 // We should break for named pages when two frames meet at a class A
3069 // breakpoint, where the first frame has a different end page value to the
3070 // second frame's start page value. canBreakForPageNames is true iff
3071 // children of this frame can form class A breakpoints, and that we are not
3072 // in a measurement reflow or in a monolithic container such as
3075 // We specifically do not want to cause a page-break for named pages when
3076 // we are at the top of a page. This would otherwise happen when the
3077 // previous sibling is an nsPageBreakFrame, or all previous siblings on the
3078 // current page are zero-height. The latter may not be per-spec, but is
3079 // compatible with Chrome's implementation of named pages.
3080 const nsAtom
* nextPageName
= nullptr;
3081 bool shouldBreakForPageName
= false;
3082 if (canBreakForPageNames
&& (!aState
.mReflowInput
.mFlags
.mIsTopOfPage
||
3083 !aState
.IsAdjacentWithBStart())) {
3084 const nsIFrame
* const frame
= line
->mFirstChild
;
3085 if (!frame
->IsPlaceholderFrame() && !frame
->IsPageBreakFrame()) {
3086 nextPageName
= frame
->GetStartPageValue();
3087 // Walk back to the last frame that isn't a placeholder.
3088 const nsIFrame
* prevFrame
= frame
->GetPrevSibling();
3089 while (prevFrame
&& prevFrame
->IsPlaceholderFrame()) {
3090 prevFrame
= prevFrame
->GetPrevSibling();
3092 if (prevFrame
&& prevFrame
->GetEndPageValue() != nextPageName
) {
3093 shouldBreakForPageName
= true;
3099 if (needToRecoverState
&& line
->IsDirty()) {
3100 // We need to reconstruct the block-end margin only if we didn't
3101 // reflow the previous line and we do need to reflow (or repair
3102 // the block-start position of) the next line.
3103 aState
.ReconstructMarginBefore(line
);
3106 bool reflowedPrevLine
= !needToRecoverState
;
3107 if (needToRecoverState
) {
3108 needToRecoverState
= false;
3110 // Update aState.mPrevChild as if we had reflowed all of the frames in
3112 if (line
->IsDirty()) {
3114 line
->mFirstChild
->GetPrevSibling() == line
.prev()->LastChild(),
3115 "unexpected line frames");
3116 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
3120 // Now repair the line and update |aState.mBCoord| by calling
3121 // |ReflowLine| or |SlideLine|.
3122 // If we're going to reflow everything again, then no need to reflow
3123 // the dirty line ... unless the line has floats, in which case we'd
3124 // better reflow it now to refresh its float cache, which may contain
3125 // dangling frame pointers! Ugh! This reflow of the line may be
3126 // incorrect because we skipped reflowing previous lines (e.g., floats
3127 // may be placed incorrectly), but that's OK because we'll mark the
3128 // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
3129 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
3130 lastLineMovedUp
= true;
3132 bool maybeReflowingForFirstTime
=
3133 line
->IStart() == 0 && line
->BStart() == 0 && line
->ISize() == 0 &&
3136 // Compute the dirty lines "before" BEnd, after factoring in
3137 // the running deltaBCoord value - the running value is implicit in
3139 nscoord oldB
= line
->BStart();
3140 nscoord oldBMost
= line
->BEnd();
3142 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
3143 "Don't reflow blocks while willReflowAgain is true, reflow "
3144 "of block abs-pos children depends on this");
3146 if (shouldBreakForPageName
) {
3147 // Immediately fragment for page-name. It is possible we could break
3148 // out of the loop right here, but this should make it more similar to
3149 // what happens when reflow causes fragmentation.
3150 PushTruncatedLine(aState
, line
, &keepGoing
);
3151 PresShell()->FrameConstructor()->SetNextPageContentFramePageName(
3152 nextPageName
? nextPageName
: GetAutoPageValue());
3154 // Reflow the dirty line. If it's an incremental reflow, then force
3155 // it to invalidate the dirty area if necessary
3156 ReflowLine(aState
, line
, &keepGoing
);
3159 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3161 willReflowAgain
= true;
3162 // Note that once we've entered this state, every line that gets here
3163 // (e.g. because it has floats) gets marked dirty and reflowed again.
3164 // in the next pass. This is important, see above.
3167 if (line
->HasFloats()) {
3168 reflowedFloat
= true;
3172 DumpLine(aState
, line
, deltaBCoord
, -1);
3173 if (0 == line
->GetChildCount()) {
3174 DeleteLine(aState
, line
, line_end
);
3179 // Test to see whether the margin that should be carried out
3180 // to the next line (NL) might have changed. In ReflowBlockFrame
3181 // we call nextLine->MarkPreviousMarginDirty if the block's
3182 // actual carried-out block-end margin changed. So here we only
3183 // need to worry about the following effects:
3184 // 1) the line was just created, and it might now be blocking
3185 // a carried-out block-end margin from previous lines that
3186 // used to reach NL from reaching NL
3187 // 2) the line used to be empty, and is now not empty,
3188 // thus blocking a carried-out block-end margin from previous lines
3189 // that used to reach NL from reaching NL
3190 // 3) the line wasn't empty, but now is, so a carried-out
3191 // block-end margin from previous lines that didn't used to reach NL
3193 // 4) the line might have changed in a way that affects NL's
3194 // ShouldApplyBStartMargin decision. The three things that matter
3195 // are the line's emptiness, its adjacency to the block-start edge of the
3196 // block, and whether it has clearance (the latter only matters if the
3197 // block was and is adjacent to the block-start and empty).
3199 // If the line is empty now, we can't reliably tell if the line was empty
3200 // before, so we just assume it was and do
3201 // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
3202 // redundant; if the line is empty now we don't need to check 4), but if
3203 // the line is not empty now and we're sure it wasn't empty before, any
3204 // adjacency and clearance changes are irrelevant to the result of
3205 // nextLine->ShouldApplyBStartMargin.
3206 if (line
.next() != LinesEnd()) {
3207 bool maybeWasEmpty
= oldB
== line
.next()->BStart();
3208 bool isEmpty
= line
->CachedIsEmpty();
3209 if (maybeReflowingForFirstTime
/*1*/ ||
3210 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
3211 line
.next()->MarkPreviousMarginDirty();
3212 // since it's marked dirty, nobody will care about |deltaBCoord|
3216 // If the line was just reflowed for the first time, then its
3217 // old mBounds cannot be trusted so this deltaBCoord computation is
3218 // bogus. But that's OK because we just did
3219 // MarkPreviousMarginDirty on the next line which will force it
3220 // to be reflowed, so this computation of deltaBCoord will not be
3222 deltaBCoord
= line
->BEnd() - oldBMost
;
3224 // Now do an interrupt check. We want to do this only in the case when we
3225 // actually reflow the line, so that if we get back in here we'll get
3226 // further on the reflow before interrupting.
3227 aState
.mPresContext
->CheckForInterrupt(this);
3229 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
3230 // Nop except for blocks (we don't create overflow container
3231 // continuations for any inlines atm), so only checking mFirstChild
3234 lastLineMovedUp
= deltaBCoord
< 0;
3236 if (deltaBCoord
!= 0) {
3237 SlideLine(aState
, line
, deltaBCoord
);
3239 repositionViews
= true;
3242 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
3243 "Possibly stale float cache here!");
3244 if (willReflowAgain
&& line
->IsBlock()) {
3245 // If we're going to reflow everything again, and this line is a block,
3246 // then there is no need to recover float state. The line may contain
3247 // other lines with floats, but in that case RecoverStateFrom would only
3248 // add floats to the float manager. We don't need to do that because
3249 // everything's going to get reflowed again "for real". Calling
3250 // RecoverStateFrom in this situation could be lethal because the
3251 // block's descendant lines may have float caches containing dangling
3252 // frame pointers. Ugh!
3253 // If this line is inline, then we need to recover its state now
3254 // to make sure that we don't forget to move its floats by deltaBCoord.
3256 // XXX EVIL O(N^2) EVIL
3257 aState
.RecoverStateFrom(line
, deltaBCoord
);
3260 // Keep mBCoord up to date in case we're propagating reflow damage
3261 // and also because our final height may depend on it. If the
3262 // line is inlines, then only update mBCoord if the line is not
3263 // empty, because that's what PlaceLine does. (Empty blocks may
3264 // want to update mBCoord, e.g. if they have clearance.)
3265 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
3266 aState
.mBCoord
= line
->BEnd();
3269 needToRecoverState
= true;
3271 if (reflowedPrevLine
&& !line
->IsBlock() &&
3272 aState
.mPresContext
->HasPendingInterrupt()) {
3273 // Need to make sure to pull overflows from any prev-in-flows
3274 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
3275 inlineKid
= inlineKid
->PrincipalChildList().FirstChild()) {
3276 inlineKid
->PullOverflowsFromPrevInFlow();
3281 // Record if we need to clear floats before reflowing the next
3282 // line. Note that inlineFloatClearType will be handled and
3283 // cleared before the next line is processed, so there is no
3284 // need to combine break types here.
3285 if (line
->HasFloatClearTypeAfter()) {
3286 inlineFloatClearType
= line
->FloatClearTypeAfter();
3289 if (LineHasClear(line
.get())) {
3290 foundAnyClears
= true;
3293 DumpLine(aState
, line
, deltaBCoord
, -1);
3295 if (aState
.mPresContext
->HasPendingInterrupt()) {
3296 willReflowAgain
= true;
3297 // Another option here might be to leave |line| clean if
3298 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
3299 // that case the line really did reflow as it should have. Not sure
3300 // whether that would be safe, so doing this for now instead. Also not
3301 // sure whether we really want to mark all lines dirty after an
3302 // interrupt, but until we get better at propagating float damage we
3303 // really do need to do it this way; see comments inside MarkLineDirty.
3304 MarkLineDirtyForInterrupt(line
);
3308 // Handle BR-clearance from the last line of the block
3309 if (inlineFloatClearType
!= StyleClear::None
) {
3310 std::tie(aState
.mBCoord
, std::ignore
) =
3311 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
3314 if (needToRecoverState
) {
3315 // Is this expensive?
3316 aState
.ReconstructMarginBefore(line
);
3318 // Update aState.mPrevChild as if we had reflowed all of the frames in
3320 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
3321 line
.prev()->LastChild(),
3322 "unexpected line frames");
3323 aState
.mPrevChild
= line
== line_end
? mFrames
.LastChild()
3324 : line
->mFirstChild
->GetPrevSibling();
3327 // Should we really have to do this?
3328 if (repositionViews
) {
3329 nsContainerFrame::PlaceFrameView(this);
3332 // We can skip trying to pull up the next line if our height is constrained
3333 // (so we can report being incomplete) and there is no next in flow or we
3334 // were told not to or we know it will be futile, i.e.,
3335 // -- the next in flow is not changing
3336 // -- and we cannot have added more space for its first line to be
3338 // -- it's an incremental reflow of a descendant
3339 // -- and we didn't reflow any floats (so the available space
3341 // -- my chain of next-in-flows either has no first line, or its first
3342 // line isn't dirty.
3343 bool heightConstrained
=
3344 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
;
3345 bool skipPull
= willReflowAgain
&& heightConstrained
;
3346 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
3347 (aState
.mReflowInput
.mFlags
.mNextInFlowUntouched
&& !lastLineMovedUp
&&
3348 !HasAnyStateBits(NS_FRAME_IS_DIRTY
) && !reflowedFloat
)) {
3349 // We'll place lineIter at the last line of this block, so that
3350 // nsBlockInFlowLineIterator::Next() will take us to the first
3351 // line of my next-in-flow-chain. (But first, check that I
3352 // have any lines -- if I don't, just bail out of this
3354 LineIterator lineIter
= this->LinesEnd();
3355 if (lineIter
!= this->LinesBegin()) {
3356 lineIter
--; // I have lines; step back from dummy iterator to last line.
3357 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
3359 // Check for next-in-flow-chain's first line.
3360 // (First, see if there is such a line, and second, see if it's clean)
3361 if (!bifLineIter
.Next() || !bifLineIter
.GetLine()->IsDirty()) {
3367 if (skipPull
&& aState
.mNextInFlow
) {
3368 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
3369 if (aState
.mNextInFlow
->IsTrueOverflowContainer()) {
3370 aState
.mReflowStatus
.SetOverflowIncomplete();
3372 aState
.mReflowStatus
.SetIncomplete();
3376 if (!skipPull
&& aState
.mNextInFlow
) {
3377 // Pull data from a next-in-flow if there's still room for more
3379 while (keepGoing
&& aState
.mNextInFlow
) {
3380 // Grab first line from our next-in-flow
3381 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3382 nsLineBox
* pulledLine
;
3383 nsFrameList pulledFrames
;
3384 if (!nextInFlow
->mLines
.empty()) {
3385 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
, &pulledLine
,
3388 // Grab an overflow line if there are any
3389 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
3390 if (!overflowLines
) {
3391 aState
.mNextInFlow
=
3392 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3396 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
3397 &pulledLine
, &pulledFrames
);
3399 nextInFlow
->DestroyOverflowLines();
3403 if (pulledFrames
.IsEmpty()) {
3404 // The line is empty. Try the next one.
3406 pulledLine
->GetChildCount() == 0 && !pulledLine
->mFirstChild
,
3408 nextInFlow
->FreeLineBox(pulledLine
);
3412 if (nextInFlow
->MaybeHasLineCursor()) {
3413 if (pulledLine
== nextInFlow
->GetLineCursorForDisplay()) {
3414 nextInFlow
->ClearLineCursorForDisplay();
3416 if (pulledLine
== nextInFlow
->GetLineCursorForQuery()) {
3417 nextInFlow
->ClearLineCursorForQuery();
3420 ReparentFrames(pulledFrames
, nextInFlow
, this);
3421 pulledLine
->SetMovedFragments();
3423 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
3424 "Unexpected last frame");
3425 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(),
3426 "should have a prevchild here");
3427 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
3428 "Incorrect aState.mPrevChild before inserting line at end");
3430 // Shift pulledLine's frames into our mFrames list.
3431 mFrames
.AppendFrames(nullptr, std::move(pulledFrames
));
3433 // Add line to our line list, and set its last child as our new prev-child
3434 line
= mLines
.before_insert(LinesEnd(), pulledLine
);
3435 aState
.mPrevChild
= mFrames
.LastChild();
3437 // Reparent floats whose placeholders are in the line.
3438 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
3440 DumpLine(aState
, pulledLine
, deltaBCoord
, 0);
3442 AutoNoisyIndenter
indent2(gNoisyReflow
);
3445 if (aState
.mPresContext
->HasPendingInterrupt()) {
3446 MarkLineDirtyForInterrupt(line
);
3448 // Now reflow it and any lines that it makes during it's reflow
3449 // (we have to loop here because reflowing the line may cause a new
3450 // line to be created; see SplitLine's callers for examples of
3451 // when this happens).
3452 while (line
!= LinesEnd()) {
3453 ReflowLine(aState
, line
, &keepGoing
);
3455 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3458 aState
.mReflowStatus
.SetIncomplete();
3462 DumpLine(aState
, line
, deltaBCoord
, -1);
3464 if (0 == line
->GetChildCount()) {
3465 DeleteLine(aState
, line
, line_end
);
3470 if (LineHasClear(line
.get())) {
3471 foundAnyClears
= true;
3474 if (aState
.mPresContext
->CheckForInterrupt(this)) {
3475 MarkLineDirtyForInterrupt(line
);
3479 // If this is an inline frame then its time to stop
3481 aState
.AdvanceToNextLine();
3486 if (aState
.mReflowStatus
.IsIncomplete()) {
3487 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
3488 } // XXXfr shouldn't set this flag when nextinflow has no lines
3491 // Handle an odd-ball case: a list-item with no lines
3492 if (mLines
.empty() && HasOutsideMarker()) {
3493 ReflowOutput
metrics(aState
.mReflowInput
);
3494 nsIFrame
* marker
= GetOutsideMarker();
3495 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3496 ReflowOutsideMarker(
3497 marker
, aState
, metrics
,
3498 aState
.mReflowInput
.ComputedPhysicalBorderPadding().top
);
3499 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
3500 "empty ::marker frame took up space");
3502 if (!MarkerIsEmpty()) {
3503 // There are no lines so we have to fake up some y motion so that
3504 // we end up with *some* height.
3505 // (Note: if we're layout-contained, we have to be sure to leave our
3506 // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
3507 // because layout-contained frames have no baseline.)
3508 if (!aState
.mReflowInput
.mStyleDisplay
->IsContainLayout() &&
3509 metrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
3511 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3512 if (nsLayoutUtils::GetFirstLineBaseline(wm
, marker
, &ascent
)) {
3513 metrics
.SetBlockStartAscent(ascent
);
3515 metrics
.SetBlockStartAscent(metrics
.BSize(wm
));
3519 RefPtr
<nsFontMetrics
> fm
=
3520 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
3522 nscoord minAscent
= nsLayoutUtils::GetCenteredFontBaseline(
3523 fm
, aState
.mMinLineHeight
, wm
.IsLineInverted());
3524 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
3527 std::max(minAscent
, metrics
.BlockStartAscent()) +
3528 std::max(minDescent
, metrics
.BSize(wm
) - metrics
.BlockStartAscent());
3530 nscoord offset
= minAscent
- metrics
.BlockStartAscent();
3532 marker
->SetRect(marker
->GetRect() + nsPoint(0, offset
));
3537 if (LinesAreEmpty(mLines
) && ShouldHaveLineIfEmpty()) {
3538 aState
.mBCoord
+= aState
.mMinLineHeight
;
3541 if (foundAnyClears
) {
3542 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3544 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3549 VerifyOverflowSituation();
3551 IndentBy(stdout
, gNoiseIndent
- 1);
3553 printf(": done reflowing dirty lines (status=%s)\n",
3554 ToString(aState
.mReflowStatus
).c_str());
3559 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
) {
3562 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
3563 // marked the lines that need to be marked dirty based on our
3564 // vertical resize stuff. So we'll definitely reflow all those kids;
3565 // the only question is how they should behave.
3566 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
3567 // Mark all our child frames dirty so we make sure to reflow them
3569 int32_t n
= aLine
->GetChildCount();
3570 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
3571 f
= f
->GetNextSibling(), --n
) {
3572 f
->MarkSubtreeDirty();
3574 // And mark all the floats whose reflows we might be skipping dirty too.
3575 if (aLine
->HasFloats()) {
3576 for (nsIFrame
* f
: aLine
->Floats()) {
3577 f
->MarkSubtreeDirty();
3581 // Dirty all the descendant lines of block kids to handle float damage,
3582 // since our nsFloatManager will go away by the next time we're reflowing.
3583 // XXXbz Can we do something more like what PropagateFloatDamage does?
3584 // Would need to sort out the exact business with mBlockDelta for that....
3585 // This marks way too much dirty. If we ever make this better, revisit
3586 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
3587 nsBlockFrame
* bf
= do_QueryFrame(aLine
->mFirstChild
);
3589 MarkAllDescendantLinesDirty(bf
);
3594 void nsBlockFrame::DeleteLine(BlockReflowState
& aState
,
3595 nsLineList::iterator aLine
,
3596 nsLineList::iterator aLineEnd
) {
3597 MOZ_ASSERT(0 == aLine
->GetChildCount(), "can't delete !empty line");
3598 if (0 == aLine
->GetChildCount()) {
3599 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
3600 "using function more generally than designed, "
3601 "but perhaps OK now");
3602 nsLineBox
* line
= aLine
;
3603 aLine
= mLines
.erase(aLine
);
3605 // Mark the previous margin of the next line dirty since we need to
3606 // recompute its top position.
3607 if (aLine
!= aLineEnd
) {
3608 aLine
->MarkPreviousMarginDirty();
3614 * Reflow a line. The line will either contain a single block frame
3615 * or contain 1 or more inline frames. aKeepReflowGoing indicates
3616 * whether or not the caller should continue to reflow more lines.
3618 void nsBlockFrame::ReflowLine(BlockReflowState
& aState
, LineIterator aLine
,
3619 bool* aKeepReflowGoing
) {
3620 MOZ_ASSERT(aLine
->GetChildCount(), "reflowing empty line");
3622 // Setup the line-layout for the new line
3623 aState
.mCurrentLine
= aLine
;
3624 aLine
->ClearDirty();
3625 aLine
->InvalidateCachedIsEmpty();
3626 aLine
->ClearHadFloatPushed();
3628 // If this line contains a single block that is hidden by `content-visibility`
3629 // don't reflow the line. If this line contains inlines and the first one is
3630 // hidden by `content-visibility`, all of them are, so avoid reflow in that
3632 // For frames that own anonymous children, even the first child is hidden by
3633 // `content-visibility`, there could be some anonymous children need reflow,
3634 // so we don't skip reflow this line.
3635 nsIFrame
* firstChild
= aLine
->mFirstChild
;
3636 if (firstChild
->IsHiddenByContentVisibilityOfInFlowParentForLayout() &&
3637 !HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES
)) {
3641 // Now that we know what kind of line we have, reflow it
3642 if (aLine
->IsBlock()) {
3643 ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
3645 aLine
->SetLineWrapped(false);
3646 ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
3648 // Store the line's float edges for overflow marker analysis if needed.
3649 aLine
->ClearFloatEdges();
3650 if (aState
.mFlags
.mCanHaveOverflowMarkers
) {
3651 WritingMode wm
= aLine
->mWritingMode
;
3652 nsFlowAreaRect r
= aState
.GetFloatAvailableSpaceForBSize(
3653 aLine
->BStart(), aLine
->BSize(), nullptr);
3654 if (r
.HasFloats()) {
3655 LogicalRect so
= aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
,
3656 aLine
->mContainerSize
);
3657 nscoord s
= r
.mRect
.IStart(wm
);
3658 nscoord e
= r
.mRect
.IEnd(wm
);
3659 if (so
.IEnd(wm
) > e
|| so
.IStart(wm
) < s
) {
3660 // This line is overlapping a float - store the edges marking the area
3661 // between the floats for text-overflow analysis.
3662 aLine
->SetFloatEdges(s
, e
);
3668 aLine
->ClearMovedFragments();
3671 nsIFrame
* nsBlockFrame::PullFrame(BlockReflowState
& aState
,
3672 LineIterator aLine
) {
3673 // First check our remaining lines.
3674 if (LinesEnd() != aLine
.next()) {
3675 return PullFrameFrom(aLine
, this, aLine
.next());
3679 !GetOverflowLines(),
3680 "Our overflow lines should have been removed at the start of reflow");
3682 // Try each next-in-flow.
3683 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3684 while (nextInFlow
) {
3685 if (nextInFlow
->mLines
.empty()) {
3686 nextInFlow
->DrainSelfOverflowList();
3688 if (!nextInFlow
->mLines
.empty()) {
3689 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
3691 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3692 aState
.mNextInFlow
= nextInFlow
;
3698 nsIFrame
* nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
3699 nsBlockFrame
* aFromContainer
,
3700 nsLineList::iterator aFromLine
) {
3701 nsLineBox
* fromLine
= aFromLine
;
3702 MOZ_ASSERT(fromLine
, "bad line to pull from");
3703 MOZ_ASSERT(fromLine
->GetChildCount(), "empty line");
3704 MOZ_ASSERT(aLine
->GetChildCount(), "empty line");
3705 MOZ_ASSERT(!HasProperty(LineIteratorProperty()),
3706 "Shouldn't have line iterators mid-reflow");
3708 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
3709 "Disagreement about whether it's a block or not");
3711 if (fromLine
->IsBlock()) {
3712 // If our line is not empty and the child in aFromLine is a block
3713 // then we cannot pull up the frame into this line. In this case
3717 // Take frame from fromLine
3718 nsIFrame
* frame
= fromLine
->mFirstChild
;
3719 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
3721 if (aFromContainer
!= this) {
3722 // The frame is being pulled from a next-in-flow; therefore we need to add
3723 // it to our sibling list.
3724 MOZ_ASSERT(aLine
== mLines
.back());
3725 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
3726 "should only pull from first line");
3727 aFromContainer
->mFrames
.RemoveFrame(frame
);
3729 // When pushing and pulling frames we need to check for whether any
3730 // views need to be reparented.
3731 ReparentFrame(frame
, aFromContainer
, this);
3732 mFrames
.AppendFrame(nullptr, frame
);
3734 // The frame might have (or contain) floats that need to be brought
3735 // over too. (pass 'false' since there are no siblings to check)
3736 ReparentFloats(frame
, aFromContainer
, false);
3738 MOZ_ASSERT(aLine
== aFromLine
.prev());
3741 aLine
->NoteFrameAdded(frame
);
3742 fromLine
->NoteFrameRemoved(frame
);
3744 if (fromLine
->GetChildCount() > 0) {
3745 // Mark line dirty now that we pulled a child
3746 fromLine
->MarkDirty();
3747 fromLine
->mFirstChild
= newFirstChild
;
3749 // Free up the fromLine now that it's empty.
3750 // Its bounds might need to be redrawn, though.
3751 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
3752 aFromLine
.next()->MarkPreviousMarginDirty();
3754 aFromContainer
->mLines
.erase(aFromLine
);
3755 // aFromLine is now invalid
3756 aFromContainer
->FreeLineBox(fromLine
);
3761 VerifyOverflowSituation();
3767 void nsBlockFrame::SlideLine(BlockReflowState
& aState
, nsLineBox
* aLine
,
3768 nscoord aDeltaBCoord
) {
3769 MOZ_ASSERT(aDeltaBCoord
!= 0, "why slide a line nowhere?");
3771 // Adjust line state
3772 aLine
->SlideBy(aDeltaBCoord
, aState
.ContainerSize());
3774 // Adjust the frames in the line
3775 MoveChildFramesOfLine(aLine
, aDeltaBCoord
);
3778 void nsBlockFrame::UpdateLineContainerSize(nsLineBox
* aLine
,
3779 const nsSize
& aNewContainerSize
) {
3780 if (aNewContainerSize
== aLine
->mContainerSize
) {
3784 // Adjust line state
3785 nsSize sizeDelta
= aLine
->UpdateContainerSize(aNewContainerSize
);
3787 // Changing container width only matters if writing mode is vertical-rl
3788 if (GetWritingMode().IsVerticalRL()) {
3789 MoveChildFramesOfLine(aLine
, sizeDelta
.width
);
3793 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox
* aLine
,
3794 nscoord aDeltaBCoord
) {
3795 // Adjust the frames in the line
3796 nsIFrame
* kid
= aLine
->mFirstChild
;
3801 WritingMode wm
= GetWritingMode();
3802 LogicalPoint
translation(wm
, 0, aDeltaBCoord
);
3804 if (aLine
->IsBlock()) {
3806 kid
->MovePositionBy(wm
, translation
);
3809 // Make sure the frame's view and any child views are updated
3810 nsContainerFrame::PlaceFrameView(kid
);
3812 // Adjust the block-dir coordinate of the frames in the line.
3813 // Note: we need to re-position views even if aDeltaBCoord is 0, because
3814 // one of our parent frames may have moved and so the view's position
3815 // relative to its parent may have changed.
3816 int32_t n
= aLine
->GetChildCount();
3819 kid
->MovePositionBy(wm
, translation
);
3821 // Make sure the frame's view and any child views are updated
3822 nsContainerFrame::PlaceFrameView(kid
);
3823 kid
= kid
->GetNextSibling();
3828 static inline bool IsNonAutoNonZeroBSize(const StyleSize
& aCoord
) {
3829 // The "extremum length" values (see ExtremumLength) were originally aimed at
3830 // inline-size (or width, as it was before logicalization). For now, let them
3831 // return false here, so we treat them like 'auto' pending a real
3832 // implementation. (See bug 1126420.)
3834 // FIXME (bug 567039, bug 527285) This isn't correct for the 'fill' value,
3835 // which should more likely (but not necessarily, depending on the available
3836 // space) be returning true.
3837 if (aCoord
.BehavesLikeInitialValueOnBlockAxis()) {
3840 MOZ_ASSERT(aCoord
.IsLengthPercentage());
3841 // If we evaluate the length/percent/calc at a percentage basis of
3842 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3843 // length, percent, or combination thereof. Test > 0 so we clamp
3844 // negative calc() results to 0.
3845 return aCoord
.AsLengthPercentage().Resolve(nscoord_MAX
) > 0 ||
3846 aCoord
.AsLengthPercentage().Resolve(0) > 0;
3850 bool nsBlockFrame::IsSelfEmpty() {
3851 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
3855 // Blocks which are margin-roots (including inline-blocks) cannot be treated
3856 // as empty for margin-collapsing and other purposes. They're more like
3857 // replaced elements.
3858 if (HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
3862 WritingMode wm
= GetWritingMode();
3863 const nsStylePosition
* position
= StylePosition();
3865 if (IsNonAutoNonZeroBSize(position
->MinBSize(wm
)) ||
3866 IsNonAutoNonZeroBSize(position
->BSize(wm
))) {
3870 // FIXME: Bug 1646100 - Take intrinsic size into account.
3871 // FIXME: Handle the case that both inline and block sizes are auto.
3872 // https://github.com/w3c/csswg-drafts/issues/5060.
3873 // Note: block-size could be zero or auto/intrinsic keywords here.
3874 if (position
->BSize(wm
).BehavesLikeInitialValueOnBlockAxis() &&
3875 position
->mAspectRatio
.HasFiniteRatio()) {
3879 const nsStyleBorder
* border
= StyleBorder();
3880 const nsStylePadding
* padding
= StylePadding();
3882 if (border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBStart
)) !=
3884 border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBEnd
)) != 0 ||
3885 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBStart(wm
)) ||
3886 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBEnd(wm
))) {
3890 if (HasOutsideMarker() && !MarkerIsEmpty()) {
3897 bool nsBlockFrame::CachedIsEmpty() {
3898 if (!IsSelfEmpty()) {
3901 for (auto& line
: mLines
) {
3902 if (!line
.CachedIsEmpty()) {
3909 bool nsBlockFrame::IsEmpty() {
3910 if (!IsSelfEmpty()) {
3914 return LinesAreEmpty(mLines
);
3917 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState
& aState
,
3919 if (aLine
->mFirstChild
->IsPageBreakFrame()) {
3920 // A page break frame consumes margins adjacent to it.
3921 // https://drafts.csswg.org/css-break/#break-margins
3925 if (aState
.mFlags
.mShouldApplyBStartMargin
) {
3926 // Apply short-circuit check to avoid searching the line list
3930 if (!aState
.IsAdjacentWithBStart()) {
3931 // If we aren't at the start block-coordinate then something of non-zero
3932 // height must have been placed. Therefore the childs block-start margin
3934 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3938 // Determine if this line is "essentially" the first line
3939 LineIterator line
= LinesBegin();
3940 if (aState
.mFlags
.mHasLineAdjacentToTop
) {
3941 line
= aState
.mLineAdjacentToTop
;
3943 while (line
!= aLine
) {
3944 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
3945 // A line which precedes aLine is non-empty, or has clearance,
3946 // so therefore the block-start margin applies.
3947 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3950 // No need to apply the block-start margin if the line has floats. We
3951 // should collapse anyway (bug 44419)
3953 aState
.mFlags
.mHasLineAdjacentToTop
= true;
3954 aState
.mLineAdjacentToTop
= line
;
3957 // The line being reflowed is "essentially" the first line in the
3958 // block. Therefore its block-start margin will be collapsed by the
3959 // generational collapsing logic with its parent (us).
3963 void nsBlockFrame::ReflowBlockFrame(BlockReflowState
& aState
,
3965 bool* aKeepReflowGoing
) {
3966 MOZ_ASSERT(*aKeepReflowGoing
, "bad caller");
3968 nsIFrame
* frame
= aLine
->mFirstChild
;
3970 NS_ASSERTION(false, "program error - unexpected empty line");
3974 // If the previous frame was a page-break-frame, then preemptively push this
3975 // frame to the next page.
3976 // This is primarily important for the placeholders for abspos frames, which
3977 // measure as zero height and then would be placed on this page.
3978 if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
3979 const nsIFrame
* const prev
= frame
->GetPrevSibling();
3980 if (prev
&& prev
->IsPageBreakFrame()) {
3981 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
3986 // Prepare the block reflow engine
3987 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
3989 StyleClear clearType
= frame
->StyleDisplay()->mClear
;
3990 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
3991 clearType
= nsLayoutUtils::CombineClearType(clearType
,
3992 aState
.mTrailingClearFromPIF
);
3993 aState
.mTrailingClearFromPIF
= StyleClear::None
;
3996 // Clear past floats before the block if the clear style is not none
3997 aLine
->ClearForcedLineBreak();
3998 if (clearType
!= StyleClear::None
) {
3999 aLine
->SetForcedLineBreakBefore(clearType
);
4002 // See if we should apply the block-start margin. If the block frame being
4003 // reflowed is a continuation, then we don't apply its block-start margin
4004 // because it's not significant. Otherwise, dig deeper.
4005 bool applyBStartMargin
=
4006 !frame
->GetPrevContinuation() && ShouldApplyBStartMargin(aState
, aLine
);
4007 if (applyBStartMargin
) {
4008 // The HasClearance setting is only valid if ShouldApplyBStartMargin
4009 // returned false (in which case the block-start margin-root set our
4010 // clearance flag). Otherwise clear it now. We'll set it later on
4011 // ourselves if necessary.
4012 aLine
->ClearHasClearance();
4014 bool treatWithClearance
= aLine
->HasClearance();
4016 bool mightClearFloats
= clearType
!= StyleClear::None
;
4017 nsIFrame
* floatAvoidingBlock
= nullptr;
4018 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
4019 mightClearFloats
= true;
4020 floatAvoidingBlock
= frame
;
4023 // If our block-start margin was counted as part of some parent's block-start
4024 // margin collapse, and we are being speculatively reflowed assuming this
4025 // frame DID NOT need clearance, then we need to check that
4027 if (!treatWithClearance
&& !applyBStartMargin
&& mightClearFloats
&&
4028 aState
.mReflowInput
.mDiscoveredClearance
) {
4029 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4030 if (auto [clearBCoord
, result
] =
4031 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4032 result
!= ClearFloatsResult::BCoordNoChange
) {
4033 Unused
<< clearBCoord
;
4035 // Only record the first frame that requires clearance
4036 if (!*aState
.mReflowInput
.mDiscoveredClearance
) {
4037 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4039 aState
.mPrevChild
= frame
;
4040 // Exactly what we do now is flexible since we'll definitely be
4045 if (treatWithClearance
) {
4046 applyBStartMargin
= true;
4049 nsIFrame
* clearanceFrame
= nullptr;
4050 const nscoord startingBCoord
= aState
.mBCoord
;
4051 const nsCollapsingMargin incomingMargin
= aState
.mPrevBEndMargin
;
4053 // Save the original position of the frame so that we can reposition
4054 // its view as needed.
4055 nsPoint originalPosition
= frame
->GetPosition();
4058 nscoord bStartMargin
= 0;
4059 bool mayNeedRetry
= false;
4060 bool clearedFloats
= false;
4061 bool clearedPushedOrSplitFloat
= false;
4062 if (applyBStartMargin
) {
4063 // Precompute the blocks block-start margin value so that we can get the
4064 // correct available space (there might be a float that's
4065 // already been placed below the aState.mPrevBEndMargin
4067 // Setup a reflowInput to get the style computed block-start margin
4068 // value. We'll use a reason of `resize' so that we don't fudge
4069 // any incremental reflow input.
4071 // The availSpace here is irrelevant to our needs - all we want
4072 // out if this setup is the block-start margin value which doesn't depend
4073 // on the childs available space.
4074 // XXX building a complete ReflowInput just to get the block-start
4075 // margin seems like a waste. And we do this for almost every block!
4076 WritingMode wm
= frame
->GetWritingMode();
4077 LogicalSize availSpace
= aState
.ContentSize(wm
);
4078 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4081 if (treatWithClearance
) {
4082 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4083 aState
.mPrevBEndMargin
.Zero();
4086 // Now compute the collapsed margin-block-start value into
4087 // aState.mPrevBEndMargin, assuming that all child margins
4088 // collapse down to clearanceFrame.
4089 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4090 clearanceFrame
, &mayNeedRetry
);
4092 // XXX optimization; we could check the collapsing children to see if they
4093 // are sure to require clearance, and so avoid retrying them
4095 if (clearanceFrame
) {
4096 // Don't allow retries on the second pass. The clearance decisions for
4097 // the blocks whose block-start margins collapse with ours are now
4099 mayNeedRetry
= false;
4102 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
4103 // We don't know if we need clearance and this is the first,
4104 // optimistic pass. So determine whether *this block* needs
4105 // clearance. Note that we do not allow the decision for whether
4106 // this block has clearance to change on the second pass; that
4107 // decision is only allowed to be made under the optimistic
4109 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4110 if (auto [clearBCoord
, result
] =
4111 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4112 result
!= ClearFloatsResult::BCoordNoChange
) {
4113 Unused
<< clearBCoord
;
4115 // Looks like we need clearance and we didn't know about it already.
4116 // So recompute collapsed margin
4117 treatWithClearance
= true;
4118 // Remember this decision, needed for incremental reflow
4119 aLine
->SetHasClearance();
4121 // Apply incoming margins
4122 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4123 aState
.mPrevBEndMargin
.Zero();
4125 // Compute the collapsed margin again, ignoring the incoming margin
4127 mayNeedRetry
= false;
4128 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4129 clearanceFrame
, &mayNeedRetry
);
4133 // Temporarily advance the running block-direction value so that the
4134 // GetFloatAvailableSpace method will return the right available space.
4135 // This undone as soon as the horizontal margins are computed.
4136 bStartMargin
= aState
.mPrevBEndMargin
.get();
4138 if (treatWithClearance
) {
4139 nscoord currentBCoord
= aState
.mBCoord
;
4140 // advance mBCoord to the clear position.
4141 auto [clearBCoord
, result
] =
4142 aState
.ClearFloats(aState
.mBCoord
, clearType
, floatAvoidingBlock
);
4143 aState
.mBCoord
= clearBCoord
;
4145 clearedFloats
= result
!= ClearFloatsResult::BCoordNoChange
;
4146 clearedPushedOrSplitFloat
=
4147 result
== ClearFloatsResult::FloatsPushedOrSplit
;
4149 // Compute clearance. It's the amount we need to add to the block-start
4150 // border-edge of the frame, after applying collapsed margins
4151 // from the frame and its children, to get it to line up with
4152 // the block-end of the floats. The former is
4153 // currentBCoord + bStartMargin, the latter is the current
4155 // Note that negative clearance is possible
4156 clearance
= aState
.mBCoord
- (currentBCoord
+ bStartMargin
);
4158 // Add clearance to our block-start margin while we compute available
4159 // space for the frame
4160 bStartMargin
+= clearance
;
4162 // Note that aState.mBCoord should stay where it is: at the block-start
4163 // border-edge of the frame
4165 // Advance aState.mBCoord to the block-start border-edge of the frame.
4166 aState
.mBCoord
+= bStartMargin
;
4170 aLine
->SetLineIsImpactedByFloat(false);
4172 // Here aState.mBCoord is the block-start border-edge of the block.
4173 // Compute the available space for the block
4174 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4175 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
4176 LogicalRect availSpace
= aState
.ComputeBlockAvailSpace(
4177 frame
, floatAvailableSpace
, (floatAvoidingBlock
));
4180 // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
4181 // is to some degree out of paranoia: if we reliably eat up block-start
4182 // margins at the top of the page as we ought to, it wouldn't be
4184 if ((!aState
.mReflowInput
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
4185 (availSpace
.BSize(wm
) < 0 || clearedPushedOrSplitFloat
)) {
4186 // We know already that this child block won't fit on this
4187 // page/column due to the block-start margin or the clearance. So we
4188 // need to get out of here now. (If we don't, most blocks will handle
4189 // things fine, and report break-before, but zero-height blocks
4190 // won't, and will thus make their parent overly-large and force
4191 // *it* to be pushed in its entirety.)
4192 aState
.mBCoord
= startingBCoord
;
4193 aState
.mPrevBEndMargin
= incomingMargin
;
4194 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4195 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4197 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4202 // Now put the block-dir coordinate back to the start of the
4203 // block-start-margin + clearance.
4204 aState
.mBCoord
-= bStartMargin
;
4205 availSpace
.BStart(wm
) -= bStartMargin
;
4206 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.BSize(wm
)) {
4207 availSpace
.BSize(wm
) += bStartMargin
;
4210 // Construct the reflow input for the block.
4211 Maybe
<ReflowInput
> childReflowInput
;
4212 Maybe
<LogicalSize
> cbSize
;
4213 LogicalSize availSize
= availSpace
.Size(wm
);
4214 bool columnSetWrapperHasNoBSizeLeft
= false;
4215 if (Style()->GetPseudoType() == PseudoStyleType::columnContent
) {
4216 // Calculate the multicol containing block's block size so that the
4217 // children with percentage block size get correct percentage basis.
4218 const ReflowInput
* cbReflowInput
=
4219 aState
.mReflowInput
.mParentReflowInput
->mCBReflowInput
;
4220 MOZ_ASSERT(cbReflowInput
->mFrame
->StyleColumn()->IsColumnContainerStyle(),
4221 "Get unexpected reflow input of multicol containing block!");
4223 // Use column-width as the containing block's inline-size, i.e. the column
4224 // content's computed inline-size.
4225 cbSize
.emplace(LogicalSize(wm
, aState
.mReflowInput
.ComputedISize(),
4226 cbReflowInput
->ComputedBSize())
4227 .ConvertTo(frame
->GetWritingMode(), wm
));
4229 // If a ColumnSetWrapper is in a balancing column content, it may be
4230 // pushed or pulled back and forth between column contents. Always add
4231 // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
4232 // can have a chance to reflow under current block size constraint.
4233 if (aState
.mReflowInput
.mFlags
.mIsColumnBalancing
&&
4234 frame
->IsColumnSetWrapperFrame()) {
4235 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4237 } else if (IsColumnSetWrapperFrame()) {
4238 // If we are reflowing our ColumnSet children, we want to apply our block
4239 // size constraint to the available block size when constructing reflow
4240 // input for ColumnSet so that ColumnSet can use it to compute its max
4241 // column block size.
4242 if (frame
->IsColumnSetFrame()) {
4243 nscoord contentBSize
= aState
.mReflowInput
.ComputedBSize();
4244 if (aState
.mReflowInput
.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE
) {
4246 std::min(contentBSize
, aState
.mReflowInput
.ComputedMaxBSize());
4248 if (contentBSize
!= NS_UNCONSTRAINEDSIZE
) {
4249 // To get the remaining content block-size, subtract the content
4250 // block-size consumed by our previous continuations.
4251 contentBSize
-= aState
.mConsumedBSize
;
4253 // ColumnSet is not the outermost frame in the column container, so it
4254 // cannot have any margin. We don't need to consider any margin that
4255 // can be generated by "box-decoration-break: clone" as we do in
4256 // BlockReflowState::ComputeBlockAvailSpace().
4257 const nscoord availContentBSize
= std::max(
4258 0, contentBSize
- (aState
.mBCoord
- aState
.ContentBStart()));
4259 if (availSize
.BSize(wm
) >= availContentBSize
) {
4260 availSize
.BSize(wm
) = availContentBSize
;
4261 columnSetWrapperHasNoBSizeLeft
= true;
4267 childReflowInput
.emplace(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4268 availSize
.ConvertTo(frame
->GetWritingMode(), wm
),
4271 childReflowInput
->mFlags
.mColumnSetWrapperHasNoBSizeLeft
=
4272 columnSetWrapperHasNoBSizeLeft
;
4274 if (aLine
->MovedFragments()) {
4275 // We only need to set this the first reflow, since if we reflow
4276 // again (and replace childReflowInput) we'll be reflowing it
4277 // again in the same fragment as the previous time.
4278 childReflowInput
->mFlags
.mMovedBlockFragments
= true;
4281 nsFloatManager::SavedState floatManagerState
;
4282 nsReflowStatus frameReflowStatus
;
4284 if (floatAvailableSpace
.HasFloats()) {
4285 // Set if floatAvailableSpace.HasFloats() is true for any
4286 // iteration of the loop.
4287 aLine
->SetLineIsImpactedByFloat(true);
4290 // We might need to store into mDiscoveredClearance later if it's
4291 // currently null; we want to overwrite any writes that
4292 // brc.ReflowBlock() below does, so we need to remember now
4293 // whether it's empty.
4294 const bool shouldStoreClearance
=
4295 aState
.mReflowInput
.mDiscoveredClearance
&&
4296 !*aState
.mReflowInput
.mDiscoveredClearance
;
4298 // Reflow the block into the available space
4299 if (mayNeedRetry
|| floatAvoidingBlock
) {
4300 aState
.FloatManager()->PushState(&floatManagerState
);
4304 childReflowInput
->mDiscoveredClearance
= &clearanceFrame
;
4305 } else if (!applyBStartMargin
) {
4306 childReflowInput
->mDiscoveredClearance
=
4307 aState
.mReflowInput
.mDiscoveredClearance
;
4310 frameReflowStatus
.Reset();
4311 brc
.ReflowBlock(availSpace
, applyBStartMargin
, aState
.mPrevBEndMargin
,
4312 clearance
, aLine
.get(), *childReflowInput
,
4313 frameReflowStatus
, aState
);
4315 if (frameReflowStatus
.IsInlineBreakBefore()) {
4316 // No need to retry this loop if there is a break opportunity before the
4321 // Now the block has a height. Using that height, get the
4322 // available space again and call ComputeBlockAvailSpace again.
4323 // If ComputeBlockAvailSpace gives a different result, we need to
4325 if (!floatAvoidingBlock
) {
4329 LogicalRect
oldFloatAvailableSpaceRect(floatAvailableSpace
.mRect
);
4330 floatAvailableSpace
= aState
.GetFloatAvailableSpaceForBSize(
4331 aState
.mBCoord
+ bStartMargin
, brc
.GetMetrics().BSize(wm
),
4332 &floatManagerState
);
4333 NS_ASSERTION(floatAvailableSpace
.mRect
.BStart(wm
) ==
4334 oldFloatAvailableSpaceRect
.BStart(wm
),
4336 // Restore the height to the position of the next band.
4337 floatAvailableSpace
.mRect
.BSize(wm
) =
4338 oldFloatAvailableSpaceRect
.BSize(wm
);
4339 // Determine whether the available space shrunk on either side,
4340 // because (the first time round) we now know the block's height,
4341 // and it may intersect additional floats, or (on later
4342 // iterations) because narrowing the width relative to the
4343 // previous time may cause the block to become taller. Note that
4344 // since we're reflowing the block, narrowing the width might also
4345 // make it shorter, so we must pass aCanGrow as true.
4346 if (!AvailableSpaceShrunk(wm
, oldFloatAvailableSpaceRect
,
4347 floatAvailableSpace
.mRect
, true)) {
4348 // The size and position we chose before are fine (i.e., they
4349 // don't cause intersecting with floats that requires a change
4350 // in size or position), so we're done.
4354 bool advanced
= false;
4355 if (!aState
.FloatAvoidingBlockFitsInAvailSpace(floatAvoidingBlock
,
4356 floatAvailableSpace
)) {
4357 // Advance to the next band.
4358 nscoord newBCoord
= aState
.mBCoord
;
4359 if (aState
.AdvanceToNextBand(floatAvailableSpace
.mRect
, &newBCoord
)) {
4362 // ClearFloats might be able to advance us further once we're there.
4363 std::tie(aState
.mBCoord
, std::ignore
) =
4364 aState
.ClearFloats(newBCoord
, StyleClear::None
, floatAvoidingBlock
);
4366 // Start over with a new available space rect at the new height.
4367 floatAvailableSpace
= aState
.GetFloatAvailableSpaceWithState(
4368 aState
.mBCoord
, ShapeType::ShapeOutside
, &floatManagerState
);
4371 const LogicalRect oldAvailSpace
= availSpace
;
4372 availSpace
= aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
4373 (floatAvoidingBlock
));
4375 if (!advanced
&& availSpace
.IsEqualEdges(oldAvailSpace
)) {
4379 // We need another reflow.
4380 aState
.FloatManager()->PopState(&floatManagerState
);
4382 if (!treatWithClearance
&& !applyBStartMargin
&&
4383 aState
.mReflowInput
.mDiscoveredClearance
) {
4384 // We set shouldStoreClearance above to record only the first
4385 // frame that requires clearance.
4386 if (shouldStoreClearance
) {
4387 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4389 aState
.mPrevChild
= frame
;
4390 // Exactly what we do now is flexible since we'll definitely be
4396 // We're pushing down the border-box, so we don't apply margin anymore.
4397 // This should never cause us to move up since the call to
4398 // GetFloatAvailableSpaceForBSize above included the margin.
4399 applyBStartMargin
= false;
4401 treatWithClearance
= true; // avoid hitting test above
4405 childReflowInput
.reset();
4406 childReflowInput
.emplace(
4407 aState
.mPresContext
, aState
.mReflowInput
, frame
,
4408 availSpace
.Size(wm
).ConvertTo(frame
->GetWritingMode(), wm
));
4411 if (mayNeedRetry
&& clearanceFrame
) {
4412 // Found a clearance frame, so we need to reflow |frame| a second time.
4413 // Restore the states and start over again.
4414 aState
.FloatManager()->PopState(&floatManagerState
);
4415 aState
.mBCoord
= startingBCoord
;
4416 aState
.mPrevBEndMargin
= incomingMargin
;
4420 aState
.mPrevChild
= frame
;
4422 if (childReflowInput
->WillReflowAgainForClearance()) {
4423 // If an ancestor of ours is going to reflow for clearance, we
4424 // need to avoid calling PlaceBlock, because it unsets dirty bits
4425 // on the child block (both itself, and through its call to
4426 // nsIFrame::DidReflow), and those dirty bits imply dirtiness for
4427 // all of the child block, including the lines it didn't reflow.
4428 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
4429 "we need to call PositionChildViews");
4433 #if defined(REFLOW_STATUS_COVERAGE)
4434 RecordReflowStatus(true, frameReflowStatus
);
4437 if (frameReflowStatus
.IsInlineBreakBefore()) {
4438 // None of the child block fits.
4439 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4440 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4442 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4445 // Note: line-break-after a block is a nop
4447 // Try to place the child block.
4448 // Don't force the block to fit if we have positive clearance, because
4449 // pushing it to the next page would give it more room.
4450 // Don't force the block to fit if it's impacted by a float. If it is,
4451 // then pushing it to the next page would give it more room. Note that
4452 // isImpacted doesn't include impact from the block's own floats.
4453 bool forceFit
= aState
.IsAdjacentWithBStart() && clearance
<= 0 &&
4454 !floatAvailableSpace
.HasFloats();
4455 nsCollapsingMargin collapsedBEndMargin
;
4456 OverflowAreas overflowAreas
;
4458 brc
.PlaceBlock(*childReflowInput
, forceFit
, aLine
.get(),
4459 collapsedBEndMargin
, overflowAreas
, frameReflowStatus
);
4460 if (!frameReflowStatus
.IsFullyComplete() &&
4461 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4462 *aKeepReflowGoing
= false;
4466 if (aLine
->SetCarriedOutBEndMargin(collapsedBEndMargin
)) {
4467 LineIterator nextLine
= aLine
;
4469 if (nextLine
!= LinesEnd()) {
4470 nextLine
->MarkPreviousMarginDirty();
4474 aLine
->SetOverflowAreas(overflowAreas
);
4475 if (*aKeepReflowGoing
) {
4476 // Some of the child block fit
4478 // Advance to new Y position
4479 nscoord newBCoord
= aLine
->BEnd();
4480 aState
.mBCoord
= newBCoord
;
4482 // Continue the block frame now if it didn't completely fit in
4483 // the available space.
4484 if (!frameReflowStatus
.IsFullyComplete()) {
4485 bool madeContinuation
= CreateContinuationFor(aState
, nullptr, frame
);
4487 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
4488 NS_ASSERTION(nextFrame
,
4489 "We're supposed to have a next-in-flow by now");
4491 if (frameReflowStatus
.IsIncomplete()) {
4492 // If nextFrame used to be an overflow container, make it a normal
4494 if (!madeContinuation
&&
4495 nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4496 nsOverflowContinuationTracker::AutoFinish
fini(
4497 aState
.mOverflowTracker
, frame
);
4498 nsContainerFrame
* parent
= nextFrame
->GetParent();
4499 parent
->StealFrame(nextFrame
);
4500 if (parent
!= this) {
4501 ReparentFrame(nextFrame
, parent
, this);
4503 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
4504 madeContinuation
= true; // needs to be added to mLines
4505 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4506 frameReflowStatus
.SetNextInFlowNeedsReflow();
4509 // Push continuation to a new line, but only if we actually made
4511 if (madeContinuation
) {
4512 nsLineBox
* line
= NewLineBox(nextFrame
, true);
4513 mLines
.after_insert(aLine
, line
);
4516 PushTruncatedLine(aState
, aLine
.next(), aKeepReflowGoing
);
4518 // If we need to reflow the continuation of the block child,
4519 // then we'd better reflow our continuation
4520 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4521 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4522 // We also need to make that continuation's line dirty so
4523 // it gets reflowed when we reflow our next in flow. The
4524 // nif's line must always be either a line of the nif's
4525 // parent block (only if we didn't make a continuation) or
4526 // else one of our own overflow lines. In the latter case
4527 // the line is already marked dirty, so just handle the
4529 if (!madeContinuation
) {
4530 nsBlockFrame
* nifBlock
= do_QueryFrame(nextFrame
->GetParent());
4533 "A block's child's next in flow's parent must be a block!");
4534 for (auto& line
: nifBlock
->Lines()) {
4535 if (line
.Contains(nextFrame
)) {
4543 // The block-end margin for a block is only applied on the last
4544 // flow block. Since we just continued the child block frame,
4545 // we know that line->mFirstChild is not the last flow block
4546 // therefore zero out the running margin value.
4547 #ifdef NOISY_BLOCK_DIR_MARGINS
4549 printf(": reflow incomplete, frame=");
4550 frame
->ListTag(stdout
);
4551 printf(" prevBEndMargin=%d, setting to zero\n",
4552 aState
.mPrevBEndMargin
.get());
4554 aState
.mPrevBEndMargin
.Zero();
4555 } else { // frame is complete but its overflow is not complete
4556 // Disconnect the next-in-flow and put it in our overflow tracker
4557 if (!madeContinuation
&&
4558 !nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4559 // It already exists, but as a normal next-in-flow, so we need
4560 // to dig it out of the child lists.
4561 nextFrame
->GetParent()->StealFrame(nextFrame
);
4562 } else if (madeContinuation
) {
4563 mFrames
.RemoveFrame(nextFrame
);
4566 // Put it in our overflow list
4567 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
4568 aState
.mReflowStatus
.MergeCompletionStatusFrom(frameReflowStatus
);
4570 #ifdef NOISY_BLOCK_DIR_MARGINS
4572 printf(": reflow complete but overflow incomplete for ");
4573 frame
->ListTag(stdout
);
4574 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4575 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4577 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4579 } else { // frame is fully complete
4580 #ifdef NOISY_BLOCK_DIR_MARGINS
4582 printf(": reflow complete for ");
4583 frame
->ListTag(stdout
);
4584 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4585 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4587 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4589 #ifdef NOISY_BLOCK_DIR_MARGINS
4592 frame
->ListTag(stdout
);
4593 printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
4594 brc
.GetCarriedOutBEndMargin().get(), collapsedBEndMargin
.get(),
4595 aState
.mPrevBEndMargin
.get());
4598 if (!frameReflowStatus
.IsFullyComplete()) {
4599 // The frame reported an incomplete status, but then it also didn't
4600 // fit. This means we need to reflow it again so that it can
4601 // (again) report the incomplete status.
4602 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4605 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
4606 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4607 // If it's our very first line *or* we're not at the top of the page
4608 // and we have page-break-inside:avoid, then we need to be pushed to
4609 // our parent's next-in-flow.
4610 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4612 // Push the line that didn't fit and any lines that follow it
4613 // to our next-in-flow.
4614 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4618 break; // out of the reflow retry loop
4621 // Now that we've got its final position all figured out, position any child
4622 // views it may have. Note that the case when frame has a view got handled
4623 // by FinishReflowChild, but that function didn't have the coordinates needed
4624 // to correctly decide whether to reposition child views.
4625 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
4626 nsContainerFrame::PositionChildViews(frame
);
4634 void nsBlockFrame::ReflowInlineFrames(BlockReflowState
& aState
,
4636 bool* aKeepReflowGoing
) {
4637 *aKeepReflowGoing
= true;
4639 aLine
->SetLineIsImpactedByFloat(false);
4641 // Setup initial coordinate system for reflowing the inline frames
4642 // into. Apply a previous block frame's block-end margin first.
4643 if (ShouldApplyBStartMargin(aState
, aLine
)) {
4644 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4646 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4648 LineReflowStatus lineReflowStatus
;
4650 nscoord availableSpaceBSize
= 0;
4651 aState
.mLineBSize
.reset();
4653 bool allowPullUp
= true;
4654 nsIFrame
* forceBreakInFrame
= nullptr;
4655 int32_t forceBreakOffset
= -1;
4656 gfxBreakPriority forceBreakPriority
= gfxBreakPriority::eNoBreak
;
4658 nsFloatManager::SavedState floatManagerState
;
4659 aState
.FloatManager()->PushState(&floatManagerState
);
4661 // Once upon a time we allocated the first 30 nsLineLayout objects
4662 // on the stack, and then we switched to the heap. At that time
4663 // these objects were large (1100 bytes on a 32 bit system).
4664 // Then the nsLineLayout object was shrunk to 156 bytes by
4665 // removing some internal buffers. Given that it is so much
4666 // smaller, the complexity of 2 different ways of allocating
4667 // no longer makes sense. Now we always allocate on the stack.
4668 nsLineLayout
lineLayout(aState
.mPresContext
, aState
.FloatManager(),
4669 aState
.mReflowInput
, &aLine
, nullptr);
4670 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
4671 if (forceBreakInFrame
) {
4672 lineLayout
.ForceBreakAtPosition(forceBreakInFrame
, forceBreakOffset
);
4674 DoReflowInlineFrames(aState
, lineLayout
, aLine
, floatAvailableSpace
,
4675 availableSpaceBSize
, &floatManagerState
,
4676 aKeepReflowGoing
, &lineReflowStatus
, allowPullUp
);
4677 lineLayout
.EndLineReflow();
4679 if (LineReflowStatus::RedoNoPull
== lineReflowStatus
||
4680 LineReflowStatus::RedoMoreFloats
== lineReflowStatus
||
4681 LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4682 if (lineLayout
.NeedsBackup()) {
4683 NS_ASSERTION(!forceBreakInFrame
,
4684 "Backing up twice; this should never be necessary");
4685 // If there is no saved break position, then this will set
4686 // set forceBreakInFrame to null and we won't back up, which is
4688 forceBreakInFrame
= lineLayout
.GetLastOptionalBreakPosition(
4689 &forceBreakOffset
, &forceBreakPriority
);
4691 forceBreakInFrame
= nullptr;
4693 // restore the float manager state
4694 aState
.FloatManager()->PopState(&floatManagerState
);
4695 // Clear out float lists
4696 aState
.mCurrentLineFloats
.Clear();
4697 aState
.mBelowCurrentLineFloats
.Clear();
4698 aState
.mNoWrapFloats
.Clear();
4701 // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4702 allowPullUp
= false;
4703 } while (LineReflowStatus::RedoNoPull
== lineReflowStatus
);
4704 } while (LineReflowStatus::RedoMoreFloats
== lineReflowStatus
);
4705 } while (LineReflowStatus::RedoNextBand
== lineReflowStatus
);
4708 void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState
& aState
,
4710 bool* aKeepReflowGoing
) {
4711 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4712 // Reflow the line again when we reflow at our new position.
4714 *aKeepReflowGoing
= false;
4717 void nsBlockFrame::PushTruncatedLine(BlockReflowState
& aState
,
4719 bool* aKeepReflowGoing
) {
4720 PushLines(aState
, aLine
.prev());
4721 *aKeepReflowGoing
= false;
4722 aState
.mReflowStatus
.SetIncomplete();
4725 void nsBlockFrame::DoReflowInlineFrames(
4726 BlockReflowState
& aState
, nsLineLayout
& aLineLayout
, LineIterator aLine
,
4727 nsFlowAreaRect
& aFloatAvailableSpace
, nscoord
& aAvailableSpaceBSize
,
4728 nsFloatManager::SavedState
* aFloatStateBeforeLine
, bool* aKeepReflowGoing
,
4729 LineReflowStatus
* aLineReflowStatus
, bool aAllowPullUp
) {
4730 // Forget all of the floats on the line
4731 aLine
->ClearFloats();
4732 aState
.mFloatOverflowAreas
.Clear();
4734 // We need to set this flag on the line if any of our reflow passes
4735 // are impacted by floats.
4736 if (aFloatAvailableSpace
.HasFloats()) {
4737 aLine
->SetLineIsImpactedByFloat(true);
4739 #ifdef REALLY_NOISY_REFLOW
4740 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4741 aFloatAvailableSpace
.HasFloats());
4744 WritingMode outerWM
= aState
.mReflowInput
.GetWritingMode();
4745 WritingMode lineWM
= WritingModeForLine(outerWM
, aLine
->mFirstChild
);
4746 LogicalRect lineRect
= aFloatAvailableSpace
.mRect
.ConvertTo(
4747 lineWM
, outerWM
, aState
.ContainerSize());
4749 nscoord iStart
= lineRect
.IStart(lineWM
);
4750 nscoord availISize
= lineRect
.ISize(lineWM
);
4752 if (aState
.mReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
4753 availBSize
= NS_UNCONSTRAINEDSIZE
;
4755 /* XXX get the height right! */
4756 availBSize
= lineRect
.BSize(lineWM
);
4759 // Make sure to enable resize optimization before we call BeginLineReflow
4760 // because it might get disabled there
4761 aLine
->EnableResizeReflowOptimization();
4763 aLineLayout
.BeginLineReflow(
4764 iStart
, aState
.mBCoord
, availISize
, availBSize
,
4765 aFloatAvailableSpace
.HasFloats(), false, /*XXX isTopOfPage*/
4766 lineWM
, aState
.mContainerSize
, aState
.mInsetForBalance
);
4768 aState
.mFlags
.mIsLineLayoutEmpty
= false;
4770 // XXX Unfortunately we need to know this before reflowing the first
4771 // inline frame in the line. FIX ME.
4772 if (0 == aLineLayout
.GetLineNumber() &&
4773 HasAllStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
4774 NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
4775 aLineLayout
.SetFirstLetterStyleOK(true);
4777 NS_ASSERTION(!(HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
) &&
4778 GetPrevContinuation()),
4779 "first letter child bit should only be on first continuation");
4781 // Reflow the frames that are already on the line first
4782 LineReflowStatus lineReflowStatus
= LineReflowStatus::OK
;
4784 nsIFrame
* frame
= aLine
->mFirstChild
;
4786 if (aFloatAvailableSpace
.HasFloats()) {
4787 // There is a soft break opportunity at the start of the line, because
4788 // we can always move this line down below float(s).
4789 if (aLineLayout
.NotifyOptionalBreakPosition(
4790 frame
, 0, true, gfxBreakPriority::eNormalBreak
)) {
4791 lineReflowStatus
= LineReflowStatus::RedoNextBand
;
4795 // need to repeatedly call GetChildCount here, because the child
4796 // count can change during the loop!
4798 LineReflowStatus::OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
4799 i
++, frame
= frame
->GetNextSibling()) {
4800 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4801 if (LineReflowStatus::OK
!= lineReflowStatus
) {
4802 // It is possible that one or more of next lines are empty
4803 // (because of DeleteNextInFlowChild). If so, delete them now
4804 // in case we are finished.
4806 while ((aLine
!= LinesEnd()) && (0 == aLine
->GetChildCount())) {
4807 // XXX Is this still necessary now that DeleteNextInFlowChild
4808 // uses DoRemoveFrame?
4809 nsLineBox
* toremove
= aLine
;
4810 aLine
= mLines
.erase(aLine
);
4811 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
4812 FreeLineBox(toremove
);
4816 NS_ASSERTION(lineReflowStatus
!= LineReflowStatus::Truncated
,
4817 "ReflowInlineFrame should never determine that a line "
4818 "needs to go to the next page/column");
4822 // Don't pull up new frames into lines with continuation placeholders
4824 // Pull frames and reflow them until we can't
4825 while (LineReflowStatus::OK
== lineReflowStatus
) {
4826 frame
= PullFrame(aState
, aLine
);
4831 while (LineReflowStatus::OK
== lineReflowStatus
) {
4832 int32_t oldCount
= aLine
->GetChildCount();
4833 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4834 if (aLine
->GetChildCount() != oldCount
) {
4835 // We just created a continuation for aFrame AND its going
4836 // to end up on this line (e.g. :first-letter
4837 // situation). Therefore we have to loop here before trying
4838 // to pull another frame.
4839 frame
= frame
->GetNextSibling();
4847 aState
.mFlags
.mIsLineLayoutEmpty
= aLineLayout
.LineIsEmpty();
4849 // We only need to backup if the line isn't going to be reflowed again anyway
4850 bool needsBackup
= aLineLayout
.NeedsBackup() &&
4851 (lineReflowStatus
== LineReflowStatus::Stop
||
4852 lineReflowStatus
== LineReflowStatus::OK
);
4853 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
4855 "We shouldn't be backing up more than once! "
4856 "Someone must have set a break opportunity beyond the available width, "
4857 "even though there were better break opportunities before it");
4858 needsBackup
= false;
4861 // We need to try backing up to before a text run
4862 // XXX It's possible, in fact not unusual, for the break opportunity to
4863 // already be the end of the line. We should detect that and optimize to not
4865 if (aLineLayout
.HasOptionalBreakPosition()) {
4867 lineReflowStatus
= LineReflowStatus::RedoNoPull
;
4870 // In case we reflow this line again, remember that we don't
4871 // need to force any breaking
4872 aLineLayout
.ClearOptionalBreakPosition();
4875 if (LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4876 // This happens only when we have a line that is impacted by
4877 // floats and the first element in the line doesn't fit with
4880 // If there's block space available, we either try to reflow the line
4881 // past the current band (if it's non-zero and the band definitely won't
4882 // widen around a shape-outside), otherwise we try one pixel down. If
4883 // there's no block space available, we push the line to the next
4886 NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.BSize(outerWM
),
4887 "unconstrained block size on totally empty line");
4889 // See the analogous code for blocks in BlockReflowState::ClearFloats.
4890 nscoord bandBSize
= aFloatAvailableSpace
.mRect
.BSize(outerWM
);
4891 if (bandBSize
> 0 ||
4892 NS_UNCONSTRAINEDSIZE
== aState
.mReflowInput
.AvailableBSize()) {
4893 NS_ASSERTION(bandBSize
== 0 || aFloatAvailableSpace
.HasFloats(),
4894 "redo line on totally empty line with non-empty band...");
4895 // We should never hit this case if we've placed floats on the
4896 // line; if we have, then the GetFloatAvailableSpace call is wrong
4897 // and needs to happen after the caller pops the float manager
4899 aState
.FloatManager()->AssertStateMatches(aFloatStateBeforeLine
);
4901 if (!aFloatAvailableSpace
.MayWiden() && bandBSize
> 0) {
4902 // Move it down far enough to clear the current band.
4903 aState
.mBCoord
+= bandBSize
;
4905 // Move it down by one dev pixel.
4906 aState
.mBCoord
+= aState
.mPresContext
->DevPixelsToAppUnits(1);
4909 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
4911 // There's nowhere to retry placing the line, so we want to push
4912 // it to the next page/column where its contents can fit not
4914 lineReflowStatus
= LineReflowStatus::Truncated
;
4915 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4918 // XXX: a small optimization can be done here when paginating:
4919 // if the new Y coordinate is past the end of the block then
4920 // push the line and return now instead of later on after we are
4922 } else if (LineReflowStatus::Truncated
!= lineReflowStatus
&&
4923 LineReflowStatus::RedoNoPull
!= lineReflowStatus
) {
4924 // If we are propagating out a break-before status then there is
4925 // no point in placing the line.
4926 if (!aState
.mReflowStatus
.IsInlineBreakBefore()) {
4927 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
4928 aFloatAvailableSpace
, aAvailableSpaceBSize
,
4929 aKeepReflowGoing
)) {
4930 lineReflowStatus
= LineReflowStatus::RedoMoreFloats
;
4931 // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4938 printf("Line reflow status = %s\n",
4939 LineReflowStatusToString(lineReflowStatus
));
4943 if (aLineLayout
.GetDirtyNextLine()) {
4944 // aLine may have been pushed to the overflow lines.
4945 FrameLines
* overflowLines
= GetOverflowLines();
4946 // We can't just compare iterators front() to aLine here, since they may be
4947 // in different lists.
4948 bool pushedToOverflowLines
=
4949 overflowLines
&& overflowLines
->mLines
.front() == aLine
.get();
4950 if (pushedToOverflowLines
) {
4951 // aLine is stale, it's associated with the main line list but it should
4952 // be associated with the overflow line list now
4953 aLine
= overflowLines
->mLines
.begin();
4955 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
4956 if (iter
.Next() && iter
.GetLine()->IsInline()) {
4957 iter
.GetLine()->MarkDirty();
4958 if (iter
.GetContainer() != this) {
4959 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4964 *aLineReflowStatus
= lineReflowStatus
;
4968 * Reflow an inline frame. The reflow status is mapped from the frames
4969 * reflow status to the lines reflow status (not to our reflow status).
4970 * The line reflow status is simple: true means keep placing frames
4971 * on the line; false means don't (the line is done). If the line
4972 * has some sort of breaking affect then aLine's break-type will be set
4973 * to something other than StyleClear::None.
4975 void nsBlockFrame::ReflowInlineFrame(BlockReflowState
& aState
,
4976 nsLineLayout
& aLineLayout
,
4977 LineIterator aLine
, nsIFrame
* aFrame
,
4978 LineReflowStatus
* aLineReflowStatus
) {
4980 *aLineReflowStatus
= LineReflowStatus::OK
;
4982 #ifdef NOISY_FIRST_LETTER
4984 printf(": reflowing ");
4985 aFrame
->ListTag(stdout
);
4986 printf(" reflowingFirstLetter=%s\n",
4987 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
4990 if (aFrame
->IsPlaceholderFrame()) {
4991 auto ph
= static_cast<nsPlaceholderFrame
*>(aFrame
);
4992 ph
->ForgetLineIsEmptySoFar();
4995 // Reflow the inline frame
4996 nsReflowStatus frameReflowStatus
;
4998 aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
, nullptr, pushedFrame
);
5000 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
5001 aLineLayout
.SetDirtyNextLine();
5004 #ifdef REALLY_NOISY_REFLOW
5005 aFrame
->ListTag(stdout
);
5006 printf(": status=%s\n", ToString(frameReflowStatus
).c_str());
5009 #if defined(REFLOW_STATUS_COVERAGE)
5010 RecordReflowStatus(false, frameReflowStatus
);
5013 // Send post-reflow notification
5014 aState
.mPrevChild
= aFrame
;
5017 This is where we need to add logic to handle some odd behavior.
5018 For one thing, we should usually place at least one thing next
5019 to a left float, even when that float takes up all the width on a line.
5023 // Process the child frames reflow status. There are 5 cases:
5024 // complete, not-complete, break-before, break-after-complete,
5025 // break-after-not-complete. There are two situations: we are a
5026 // block or we are an inline. This makes a total of 10 cases
5027 // (fortunately, there is some overlap).
5028 aLine
->ClearForcedLineBreak();
5029 if (frameReflowStatus
.IsInlineBreak() ||
5030 aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5031 // Always abort the line reflow (because a line break is the
5032 // minimal amount of break we do).
5033 *aLineReflowStatus
= LineReflowStatus::Stop
;
5035 // XXX what should aLine's break-type be set to in all these cases?
5036 if (frameReflowStatus
.IsInlineBreakBefore()) {
5037 // Break-before cases.
5038 if (aFrame
== aLine
->mFirstChild
) {
5039 // If we break before the first frame on the line then we must
5040 // be trying to place content where there's no room (e.g. on a
5041 // line with wide floats). Inform the caller to reflow the
5042 // line after skipping past a float.
5043 *aLineReflowStatus
= LineReflowStatus::RedoNextBand
;
5045 // It's not the first child on this line so go ahead and split
5046 // the line. We will see the frame again on the next-line.
5047 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
5049 // If we're splitting the line because the frame didn't fit and it
5050 // was pushed, then mark the line as having word wrapped. We need to
5051 // know that if we're shrink wrapping our width
5053 aLine
->SetLineWrapped(true);
5057 MOZ_ASSERT(frameReflowStatus
.IsInlineBreakAfter() ||
5058 aState
.mTrailingClearFromPIF
!= StyleClear::None
,
5059 "We should've handled inline break-before in the if-branch!");
5061 // If a float split and its prev-in-flow was followed by a <BR>, then
5062 // combine the <BR>'s float clear type with the inline's float clear type
5063 // (the inline will be the very next frame after the split float).
5064 StyleClear clearType
= frameReflowStatus
.FloatClearType();
5065 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5066 clearType
= nsLayoutUtils::CombineClearType(
5067 clearType
, aState
.mTrailingClearFromPIF
);
5068 aState
.mTrailingClearFromPIF
= StyleClear::None
;
5070 // Break-after cases
5071 if (clearType
!= StyleClear::None
|| aLineLayout
.GetLineEndsInBR()) {
5072 aLine
->SetForcedLineBreakAfter(clearType
);
5074 if (frameReflowStatus
.IsComplete()) {
5075 // Split line, but after the frame just reflowed
5076 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5079 if (frameReflowStatus
.IsInlineBreakAfter() &&
5080 !aLineLayout
.GetLineEndsInBR()) {
5081 aLineLayout
.SetDirtyNextLine();
5087 if (!frameReflowStatus
.IsFullyComplete()) {
5088 // Create a continuation for the incomplete frame. Note that the
5089 // frame may already have a continuation.
5090 CreateContinuationFor(aState
, aLine
, aFrame
);
5092 // Remember that the line has wrapped
5093 if (!aLineLayout
.GetLineEndsInBR()) {
5094 aLine
->SetLineWrapped(true);
5097 // If we just ended a first-letter frame or reflowed a placeholder then
5098 // don't split the line and don't stop the line reflow...
5099 // But if we are going to stop anyways we'd better split the line.
5100 if ((!frameReflowStatus
.FirstLetterComplete() &&
5101 !aFrame
->IsPlaceholderFrame()) ||
5102 *aLineReflowStatus
== LineReflowStatus::Stop
) {
5103 // Split line after the current frame
5104 *aLineReflowStatus
= LineReflowStatus::Stop
;
5105 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5111 bool nsBlockFrame::CreateContinuationFor(BlockReflowState
& aState
,
5112 nsLineBox
* aLine
, nsIFrame
* aFrame
) {
5113 nsIFrame
* newFrame
= nullptr;
5115 if (!aFrame
->GetNextInFlow()) {
5117 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame
, this);
5119 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
5122 aLine
->NoteFrameAdded(newFrame
);
5131 void nsBlockFrame::SplitFloat(BlockReflowState
& aState
, nsIFrame
* aFloat
,
5132 const nsReflowStatus
& aFloatStatus
) {
5133 MOZ_ASSERT(!aFloatStatus
.IsFullyComplete(),
5134 "why split the frame if it's fully complete?");
5135 MOZ_ASSERT(aState
.mBlock
== this);
5137 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
5139 nsContainerFrame
* oldParent
= nextInFlow
->GetParent();
5140 oldParent
->StealFrame(nextInFlow
);
5141 if (oldParent
!= this) {
5142 ReparentFrame(nextInFlow
, oldParent
, this);
5144 if (!aFloatStatus
.IsOverflowIncomplete()) {
5145 nextInFlow
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5149 PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat
, this);
5151 if (aFloatStatus
.IsOverflowIncomplete()) {
5152 nextInFlow
->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5155 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
5156 if (floatStyle
== StyleFloat::Left
) {
5157 aState
.FloatManager()->SetSplitLeftFloatAcrossBreak();
5159 MOZ_ASSERT(floatStyle
== StyleFloat::Right
, "Unexpected float side!");
5160 aState
.FloatManager()->SetSplitRightFloatAcrossBreak();
5163 aState
.AppendPushedFloatChain(nextInFlow
);
5164 if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_FLOAT_MGR
)) ||
5165 MOZ_UNLIKELY(IsTrueOverflowContainer())) {
5166 aState
.mReflowStatus
.SetOverflowIncomplete();
5168 aState
.mReflowStatus
.SetIncomplete();
5172 static bool CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
,
5177 NS_ASSERTION(!aFloat
->GetPrevContinuation(),
5178 "float in a line should never be a continuation");
5179 NS_ASSERTION(!aFloat
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5180 "float in a line should never be a pushed float");
5181 nsIFrame
* ph
= aFloat
->FirstInFlow()->GetPlaceholderFrame();
5182 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
5183 if (f
->GetParent() == aBlock
) {
5184 return aLine
->Contains(f
);
5187 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
5191 void nsBlockFrame::SplitLine(BlockReflowState
& aState
,
5192 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5194 LineReflowStatus
* aLineReflowStatus
) {
5195 MOZ_ASSERT(aLine
->IsInline(), "illegal SplitLine on block line");
5198 aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
5199 MOZ_ASSERT(pushCount
>= 0, "bad push count");
5203 nsIFrame::IndentBy(stdout
, gNoiseIndent
);
5204 printf("split line: from line=%p pushCount=%d aFrame=",
5205 static_cast<void*>(aLine
.get()), pushCount
);
5207 aFrame
->ListTag(stdout
);
5212 if (gReallyNoisyReflow
) {
5213 aLine
->List(stdout
, gNoiseIndent
+ 1);
5218 if (0 != pushCount
) {
5219 MOZ_ASSERT(aLine
->GetChildCount() > pushCount
, "bad push");
5220 MOZ_ASSERT(nullptr != aFrame
, "whoops");
5223 nsIFrame
* f
= aFrame
;
5224 int32_t count
= pushCount
;
5225 while (f
&& count
> 0) {
5226 f
= f
->GetNextSibling();
5229 NS_ASSERTION(count
== 0, "Not enough frames to push");
5233 // Put frames being split out into their own line
5234 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
5235 mLines
.after_insert(aLine
, newLine
);
5237 if (gReallyNoisyReflow
) {
5238 newLine
->List(stdout
, gNoiseIndent
+ 1);
5242 // Let line layout know that some frames are no longer part of its
5244 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
5246 // If floats have been placed whose placeholders have been pushed to the new
5247 // line, we need to reflow the old line again. We don't want to look at the
5248 // frames in the new line, because as a large paragraph is laid out the
5249 // we'd get O(N^2) performance. So instead we just check that the last
5250 // float and the last below-current-line float are still in aLine.
5251 if (!CheckPlaceholderInLine(
5253 aLine
->HasFloats() ? aLine
->Floats().LastElement() : nullptr) ||
5254 !CheckPlaceholderInLine(
5256 aState
.mBelowCurrentLineFloats
.SafeLastElement(nullptr))) {
5257 *aLineReflowStatus
= LineReflowStatus::RedoNoPull
;
5266 bool nsBlockFrame::IsLastLine(BlockReflowState
& aState
, LineIterator aLine
) {
5267 while (++aLine
!= LinesEnd()) {
5268 // There is another line
5269 if (0 != aLine
->GetChildCount()) {
5270 // If the next line is a block line then this line is the last in a
5271 // group of inline lines.
5272 return aLine
->IsBlock();
5274 // The next line is empty, try the next one
5277 // Try our next-in-flows lines to answer the question
5278 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*)GetNextInFlow();
5279 while (nullptr != nextInFlow
) {
5280 for (const auto& line
: nextInFlow
->Lines()) {
5281 if (0 != line
.GetChildCount()) {
5282 return line
.IsBlock();
5285 nextInFlow
= (nsBlockFrame
*)nextInFlow
->GetNextInFlow();
5288 // This is the last line - so don't allow justification
5292 bool nsBlockFrame::PlaceLine(BlockReflowState
& aState
,
5293 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5294 nsFloatManager::SavedState
* aFloatStateBeforeLine
,
5295 nsFlowAreaRect
& aFlowArea
,
5296 nscoord
& aAvailableSpaceBSize
,
5297 bool* aKeepReflowGoing
) {
5298 // Try to position the floats in a nowrap context.
5299 aLineLayout
.FlushNoWrapFloats();
5301 // Trim extra white-space from the line before placing the frames
5302 aLineLayout
.TrimTrailingWhiteSpace();
5304 // Vertically align the frames on this line.
5306 // According to the CSS2 spec, section 12.6.1, the "marker" box
5307 // participates in the height calculation of the list-item box's
5310 // There are exactly two places a ::marker can be placed: near the
5311 // first or second line. It's only placed on the second line in a
5312 // rare case: when the first line is empty.
5313 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
5314 bool addedMarker
= false;
5315 if (HasOutsideMarker() &&
5316 ((aLine
== mLines
.front() &&
5317 (!aLineLayout
.IsZeroBSize() || (aLine
== mLines
.back()))) ||
5318 (mLines
.front() != mLines
.back() && 0 == mLines
.front()->BSize() &&
5319 aLine
== mLines
.begin().next()))) {
5320 ReflowOutput
metrics(aState
.mReflowInput
);
5321 nsIFrame
* marker
= GetOutsideMarker();
5322 ReflowOutsideMarker(marker
, aState
, metrics
, aState
.mBCoord
);
5323 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
5324 "empty ::marker frame took up space");
5325 aLineLayout
.AddMarkerFrame(marker
, metrics
);
5328 aLineLayout
.VerticalAlignLine();
5330 // We want to consider the floats in the current line when determining
5331 // whether the float available space is shrunk. If mLineBSize doesn't
5332 // exist, we are in the first pass trying to place the line. Calling
5333 // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
5334 // for UpdateBand().
5336 // floatAvailableSpaceWithOldLineBSize is the float available space with
5337 // the old BSize, but including the floats that were added in this line.
5338 LogicalRect floatAvailableSpaceWithOldLineBSize
=
5339 aState
.mLineBSize
.isNothing()
5340 ? aState
.GetFloatAvailableSpace(aLine
->BStart()).mRect
5342 .GetFloatAvailableSpaceForBSize(
5343 aLine
->BStart(), aState
.mLineBSize
.value(), nullptr)
5346 // As we redo for floats, we can't reduce the amount of BSize we're
5348 aAvailableSpaceBSize
= std::max(aAvailableSpaceBSize
, aLine
->BSize());
5349 LogicalRect floatAvailableSpaceWithLineBSize
=
5351 .GetFloatAvailableSpaceForBSize(aLine
->BStart(), aAvailableSpaceBSize
,
5355 // If the available space between the floats is smaller now that we
5356 // know the BSize, return false (and cause another pass with
5357 // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
5358 // never decreases, which means that we can't reduce the set of floats
5359 // we intersect, which means that the available space cannot grow.
5360 if (AvailableSpaceShrunk(wm
, floatAvailableSpaceWithOldLineBSize
,
5361 floatAvailableSpaceWithLineBSize
, false)) {
5362 // Prepare data for redoing the line.
5363 aState
.mLineBSize
= Some(aLine
->BSize());
5365 // Since we want to redo the line, we update aFlowArea by using the
5366 // aFloatStateBeforeLine, which is the float manager's state before the
5368 LogicalRect
oldFloatAvailableSpace(aFlowArea
.mRect
);
5369 aFlowArea
= aState
.GetFloatAvailableSpaceForBSize(
5370 aLine
->BStart(), aAvailableSpaceBSize
, aFloatStateBeforeLine
);
5373 aFlowArea
.mRect
.BStart(wm
) == oldFloatAvailableSpace
.BStart(wm
),
5375 // Restore the BSize to the position of the next band.
5376 aFlowArea
.mRect
.BSize(wm
) = oldFloatAvailableSpace
.BSize(wm
);
5378 // Enforce both IStart() and IEnd() never move outwards to prevent
5379 // infinite grow-shrink loops.
5380 const nscoord iStartDiff
=
5381 aFlowArea
.mRect
.IStart(wm
) - oldFloatAvailableSpace
.IStart(wm
);
5382 const nscoord iEndDiff
=
5383 aFlowArea
.mRect
.IEnd(wm
) - oldFloatAvailableSpace
.IEnd(wm
);
5384 if (iStartDiff
< 0) {
5385 aFlowArea
.mRect
.IStart(wm
) -= iStartDiff
;
5386 aFlowArea
.mRect
.ISize(wm
) += iStartDiff
;
5389 aFlowArea
.mRect
.ISize(wm
) -= iEndDiff
;
5396 if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
5397 static nscoord lastHeight
= 0;
5398 if (ABSURD_SIZE(aLine
->BStart())) {
5399 lastHeight
= aLine
->BStart();
5400 if (abs(aLine
->BStart() - lastHeight
) > ABSURD_COORD
/ 10) {
5401 nsIFrame::ListTag(stdout
);
5402 printf(": line=%p y=%d line.bounds.height=%d\n",
5403 static_cast<void*>(aLine
.get()), aLine
->BStart(),
5412 // Only block frames horizontally align their children because
5413 // inline frames "shrink-wrap" around their children (therefore
5414 // there is no extra horizontal space).
5415 const nsStyleText
* styleText
= StyleText();
5418 * We don't care checking for IsLastLine properly if we don't care (if it
5419 * can't change the used text-align value for the line).
5421 * In other words, isLastLine really means isLastLineAndWeCare.
5423 const bool isLastLine
=
5424 !IsInSVGTextSubtree() &&
5425 styleText
->TextAlignForLastLine() != styleText
->mTextAlign
&&
5426 (aLineLayout
.GetLineEndsInBR() || IsLastLine(aState
, aLine
));
5428 aLineLayout
.TextAlignLine(aLine
, isLastLine
);
5430 // From here on, pfd->mBounds rectangles are incorrect because bidi
5431 // might have moved frames around!
5432 OverflowAreas overflowAreas
;
5433 aLineLayout
.RelativePositionFrames(overflowAreas
);
5434 aLine
->SetOverflowAreas(overflowAreas
);
5436 aLineLayout
.RemoveMarkerFrame(GetOutsideMarker());
5439 // Inline lines do not have margins themselves; however they are
5440 // impacted by prior block margins. If this line ends up having some
5441 // height then we zero out the previous block-end margin value that was
5442 // already applied to the line's starting Y coordinate. Otherwise we
5443 // leave it be so that the previous blocks block-end margin can be
5444 // collapsed with a block that follows.
5447 if (!aLine
->CachedIsEmpty()) {
5448 // This line has some height. Therefore the application of the
5449 // previous-bottom-margin should stick.
5450 aState
.mPrevBEndMargin
.Zero();
5451 newBCoord
= aLine
->BEnd();
5453 // Don't let the previous-bottom-margin value affect the newBCoord
5454 // coordinate (it was applied in ReflowInlineFrames speculatively)
5455 // since the line is empty.
5456 // We already called |ShouldApplyBStartMargin|, and if we applied it
5457 // then mShouldApplyBStartMargin is set.
5458 nscoord dy
= aState
.mFlags
.mShouldApplyBStartMargin
5459 ? -aState
.mPrevBEndMargin
.get()
5461 newBCoord
= aState
.mBCoord
+ dy
;
5464 if (!aState
.mReflowStatus
.IsFullyComplete() &&
5465 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5466 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5467 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5471 // See if the line fit (our first line always does).
5472 if (mLines
.front() != aLine
&&
5473 aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
&&
5474 newBCoord
> aState
.ContentBEnd()) {
5475 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
5476 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5477 // All our content doesn't fit, start on the next page.
5478 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5480 // Push aLine and all of its children and anything else that
5481 // follows to our next-in-flow.
5482 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
5487 // Note that any early return before this update of aState.mBCoord
5488 // must either (a) return false or (b) set aKeepReflowGoing to false.
5489 // Otherwise we'll keep reflowing later lines at an incorrect
5490 // position, and we might not come back and clean up the damage later.
5491 aState
.mBCoord
= newBCoord
;
5493 // Add the already placed current-line floats to the line
5494 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5496 // Any below current line floats to place?
5497 if (!aState
.mBelowCurrentLineFloats
.IsEmpty()) {
5498 // Reflow the below-current-line floats, which places on the line's
5500 aState
.PlaceBelowCurrentLineFloats(aLine
);
5503 // When a line has floats, factor them into the overflow areas computations.
5504 if (aLine
->HasFloats()) {
5505 // Union the float overflow areas (stored in aState) and the value computed
5506 // by the line layout code.
5507 OverflowAreas lineOverflowAreas
= aState
.mFloatOverflowAreas
;
5508 lineOverflowAreas
.UnionWith(aLine
->GetOverflowAreas());
5509 aLine
->SetOverflowAreas(lineOverflowAreas
);
5511 #ifdef NOISY_OVERFLOW_AREAS
5512 printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
5513 ListTag().get(), aLine
.get(),
5514 ToString(aLine
->InkOverflowRect()).c_str(),
5515 ToString(aLine
->ScrollableOverflowRect()).c_str());
5519 // Apply break-after clearing if necessary
5520 // This must stay in sync with |ReflowDirtyLines|.
5521 if (aLine
->HasFloatClearTypeAfter()) {
5522 std::tie(aState
.mBCoord
, std::ignore
) =
5523 aState
.ClearFloats(aState
.mBCoord
, aLine
->FloatClearTypeAfter());
5528 void nsBlockFrame::PushLines(BlockReflowState
& aState
,
5529 nsLineList::iterator aLineBefore
) {
5530 // NOTE: aLineBefore is always a normal line, not an overflow line.
5531 // The following expression will assert otherwise.
5532 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
5534 nsLineList::iterator
overBegin(aLineBefore
.next());
5536 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
5537 bool firstLine
= overBegin
== LinesBegin();
5539 if (overBegin
!= LinesEnd()) {
5540 // Remove floats in the lines from mFloats
5542 CollectFloats(overBegin
->mFirstChild
, floats
, true);
5544 if (floats
.NotEmpty()) {
5546 for (nsIFrame
* f
: floats
) {
5547 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5548 "CollectFloats should've removed that bit");
5551 // Push the floats onto the front of the overflow out-of-flows list
5552 nsAutoOOFFrameList
oofs(this);
5553 oofs
.mList
.InsertFrames(nullptr, nullptr, std::move(floats
));
5556 // overflow lines can already exist in some cases, in particular,
5557 // when shrinkwrapping and we discover that the shrinkwap causes
5558 // the height of some child block to grow which creates additional
5559 // overflowing content. In such cases we must prepend the new
5560 // overflow to the existing overflow.
5561 FrameLines
* overflowLines
= RemoveOverflowLines();
5562 if (!overflowLines
) {
5563 // XXXldb use presshell arena!
5564 overflowLines
= new FrameLines();
5566 if (overflowLines
) {
5567 nsIFrame
* lineBeforeLastFrame
;
5569 lineBeforeLastFrame
= nullptr; // removes all frames
5571 nsIFrame
* f
= overBegin
->mFirstChild
;
5572 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
5573 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
5574 "unexpected line frames");
5576 nsFrameList pushedFrames
= mFrames
.TakeFramesAfter(lineBeforeLastFrame
);
5577 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr,
5578 std::move(pushedFrames
));
5580 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
5581 overBegin
, LinesEnd());
5582 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
5583 // this takes ownership but it won't delete it immediately so we
5584 // can keep using it.
5585 SetOverflowLines(overflowLines
);
5587 // Mark all the overflow lines dirty so that they get reflowed when
5588 // they are pulled up by our next-in-flow.
5590 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
5591 for (LineIterator line
= overflowLines
->mLines
.begin(),
5592 line_end
= overflowLines
->mLines
.end();
5593 line
!= line_end
; ++line
) {
5595 line
->MarkPreviousMarginDirty();
5596 line
->SetMovedFragments();
5597 line
->SetBoundsEmpty();
5598 if (line
->HasFloats()) {
5599 line
->ClearFloats();
5606 VerifyOverflowSituation();
5610 // The overflowLines property is stored as a pointer to a line list,
5611 // which must be deleted. However, the following functions all maintain
5612 // the invariant that the property is never set if the list is empty.
5614 bool nsBlockFrame::DrainOverflowLines() {
5616 VerifyOverflowSituation();
5619 // Steal the prev-in-flow's overflow lines and prepend them.
5620 bool didFindOverflow
= false;
5621 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5623 prevBlock
->ClearLineCursors();
5624 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
5625 if (overflowLines
) {
5626 // Make all the frames on the overflow line list mine.
5627 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
5629 // Collect overflow containers from our OverflowContainers list that are
5630 // continuations from the frames we picked up from our prev-in-flow, then
5631 // prepend those to ExcessOverflowContainers to ensure the continuations
5633 if (GetOverflowContainers()) {
5634 nsFrameList ocContinuations
;
5635 for (auto* f
: overflowLines
->mFrames
) {
5638 while (!done
&& (cont
= cont
->GetNextContinuation()) &&
5639 cont
->GetParent() == this) {
5640 bool onlyChild
= !cont
->GetPrevSibling() && !cont
->GetNextSibling();
5641 if (cont
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
) &&
5642 TryRemoveFrame(OverflowContainersProperty(), cont
)) {
5643 ocContinuations
.AppendFrame(nullptr, cont
);
5653 if (!ocContinuations
.IsEmpty()) {
5654 if (nsFrameList
* eoc
= GetExcessOverflowContainers()) {
5655 eoc
->InsertFrames(nullptr, nullptr, std::move(ocContinuations
));
5657 SetExcessOverflowContainers(std::move(ocContinuations
));
5662 // Make the overflow out-of-flow frames mine too.
5663 nsAutoOOFFrameList
oofs(prevBlock
);
5664 if (oofs
.mList
.NotEmpty()) {
5665 // In case we own any next-in-flows of any of the drained frames, then
5666 // move those to the PushedFloat list.
5667 nsFrameList pushedFloats
;
5668 for (nsIFrame
* f
: oofs
.mList
) {
5669 nsIFrame
* nif
= f
->GetNextInFlow();
5670 for (; nif
&& nif
->GetParent() == this; nif
= nif
->GetNextInFlow()) {
5671 MOZ_ASSERT(nif
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
));
5673 pushedFloats
.AppendFrame(nullptr, nif
);
5676 ReparentFrames(oofs
.mList
, prevBlock
, this);
5677 mFloats
.InsertFrames(nullptr, nullptr, std::move(oofs
.mList
));
5678 if (!pushedFloats
.IsEmpty()) {
5679 nsFrameList
* pf
= EnsurePushedFloats();
5680 pf
->InsertFrames(nullptr, nullptr, std::move(pushedFloats
));
5684 if (!mLines
.empty()) {
5685 // Remember to recompute the margins on the first line. This will
5686 // also recompute the correct deltaBCoord if necessary.
5687 mLines
.front()->MarkPreviousMarginDirty();
5689 // The overflow lines have already been marked dirty and their previous
5690 // margins marked dirty also.
5692 // Prepend the overflow frames/lines to our principal list.
5693 mFrames
.InsertFrames(nullptr, nullptr, std::move(overflowLines
->mFrames
));
5694 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
5695 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
5696 delete overflowLines
;
5697 didFindOverflow
= true;
5701 // Now append our own overflow lines.
5702 return DrainSelfOverflowList() || didFindOverflow
;
5705 bool nsBlockFrame::DrainSelfOverflowList() {
5706 UniquePtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
5707 if (!ourOverflowLines
) {
5711 // No need to reparent frames in our own overflow lines/oofs, because they're
5712 // already ours. But we should put overflow floats back in mFloats.
5713 // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5715 nsAutoOOFFrameList
oofs(this);
5716 if (oofs
.mList
.NotEmpty()) {
5718 for (nsIFrame
* f
: oofs
.mList
) {
5719 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5720 "CollectFloats should've removed that bit");
5723 // The overflow floats go after our regular floats.
5724 mFloats
.AppendFrames(nullptr, std::move(oofs
).mList
);
5727 if (!ourOverflowLines
->mLines
.empty()) {
5728 mFrames
.AppendFrames(nullptr, std::move(ourOverflowLines
->mFrames
));
5729 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
5733 VerifyOverflowSituation();
5739 * Pushed floats are floats whose placeholders are in a previous
5740 * continuation. They might themselves be next-continuations of a float
5741 * that partially fit in an earlier continuation, or they might be the
5742 * first continuation of a float that couldn't be placed at all.
5744 * Pushed floats live permanently at the beginning of a block's float
5745 * list, where they must live *before* any floats whose placeholders are
5748 * Temporarily, during reflow, they also live on the pushed floats list,
5749 * which only holds them between (a) when one continuation pushes them to
5750 * its pushed floats list because they don't fit and (b) when the next
5751 * continuation pulls them onto the beginning of its float list.
5753 * DrainPushedFloats sets up pushed floats the way we need them at the
5754 * start of reflow; they are then reflowed by ReflowPushedFloats (which
5755 * might push some of them on). Floats with placeholders in this block
5756 * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
5757 * also maintains these invariants.
5759 * DrainSelfPushedFloats moves any pushed floats from this block's own
5760 * PushedFloats list back into mFloats. DrainPushedFloats additionally
5761 * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5763 void nsBlockFrame::DrainSelfPushedFloats() {
5764 // If we're getting reflowed multiple times without our
5765 // next-continuation being reflowed, we might need to pull back floats
5766 // that we just put in the list to be pushed to our next-in-flow.
5767 // We don't want to pull back any next-in-flows of floats on our own
5768 // float list, and we only need to pull back first-in-flows whose
5769 // placeholders were in earlier blocks (since first-in-flows whose
5770 // placeholders are in this block will get pulled appropriately by
5771 // AddFloat, and will then be more likely to be in the correct order).
5772 mozilla::PresShell
* presShell
= PresShell();
5773 nsFrameList
* ourPushedFloats
= GetPushedFloats();
5774 if (ourPushedFloats
) {
5775 // When we pull back floats, we want to put them with the pushed
5776 // floats, which must live at the start of our float list, but we
5777 // want them at the end of those pushed floats.
5778 // FIXME: This isn't quite right! What if they're all pushed floats?
5779 nsIFrame
* insertionPrevSibling
= nullptr; /* beginning of list */
5780 for (nsIFrame
* f
= mFloats
.FirstChild();
5781 f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
5782 f
= f
->GetNextSibling()) {
5783 insertionPrevSibling
= f
;
5786 nsIFrame
* f
= ourPushedFloats
->LastChild();
5788 nsIFrame
* prevSibling
= f
->GetPrevSibling();
5790 nsPlaceholderFrame
* placeholder
= f
->GetPlaceholderFrame();
5791 nsIFrame
* floatOriginalParent
=
5792 presShell
->FrameConstructor()->GetFloatContainingBlock(placeholder
);
5793 if (floatOriginalParent
!= this) {
5794 // This is a first continuation that was pushed from one of our
5795 // previous continuations. Take it out of the pushed floats
5796 // list and put it in our floats list, before any of our
5797 // floats, but after other pushed floats.
5798 ourPushedFloats
->RemoveFrame(f
);
5799 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
5805 if (ourPushedFloats
->IsEmpty()) {
5806 RemovePushedFloats()->Delete(presShell
);
5811 void nsBlockFrame::DrainPushedFloats() {
5812 DrainSelfPushedFloats();
5814 // After our prev-in-flow has completed reflow, it may have a pushed
5815 // floats list, containing floats that we need to own. Take these.
5816 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5818 AutoFrameListPtr
list(PresContext(), prevBlock
->RemovePushedFloats());
5819 if (list
&& list
->NotEmpty()) {
5820 mFloats
.InsertFrames(this, nullptr, std::move(*list
));
5825 nsBlockFrame::FrameLines
* nsBlockFrame::GetOverflowLines() const {
5826 if (!HasOverflowLines()) {
5829 FrameLines
* prop
= GetProperty(OverflowLinesProperty());
5831 prop
&& !prop
->mLines
.empty() &&
5832 prop
->mLines
.front()->GetChildCount() == 0
5833 ? prop
->mFrames
.IsEmpty()
5834 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5835 "value should always be stored and non-empty when state set");
5839 nsBlockFrame::FrameLines
* nsBlockFrame::RemoveOverflowLines() {
5840 if (!HasOverflowLines()) {
5843 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5845 prop
&& !prop
->mLines
.empty() &&
5846 prop
->mLines
.front()->GetChildCount() == 0
5847 ? prop
->mFrames
.IsEmpty()
5848 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5849 "value should always be stored and non-empty when state set");
5850 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5854 void nsBlockFrame::DestroyOverflowLines() {
5855 NS_ASSERTION(HasOverflowLines(), "huh?");
5856 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5857 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
5858 "value should always be stored but empty when destroying");
5859 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5863 // This takes ownership of aOverflowLines.
5864 // XXX We should allocate overflowLines from presShell arena!
5865 void nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
) {
5866 NS_ASSERTION(aOverflowLines
, "null lines");
5867 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
5868 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
5869 aOverflowLines
->mFrames
.FirstChild(),
5870 "invalid overflow lines / frames");
5871 NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
),
5872 "Overwriting existing overflow lines");
5874 // Verify that we won't overwrite an existing overflow list
5875 NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5876 SetProperty(OverflowLinesProperty(), aOverflowLines
);
5877 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5880 nsFrameList
* nsBlockFrame::GetOverflowOutOfFlows() const {
5881 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5884 nsFrameList
* result
= GetProperty(OverflowOutOfFlowsProperty());
5885 NS_ASSERTION(result
, "value should always be non-empty when state set");
5889 void nsBlockFrame::SetOverflowOutOfFlows(nsFrameList
&& aList
,
5890 nsFrameList
* aPropValue
) {
5892 HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) == !!aPropValue
,
5893 "state does not match value");
5895 if (aList
.IsEmpty()) {
5896 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5899 nsFrameList
* list
= TakeProperty(OverflowOutOfFlowsProperty());
5900 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
5902 list
->Delete(PresShell());
5903 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5904 } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5905 NS_ASSERTION(aPropValue
== GetProperty(OverflowOutOfFlowsProperty()),
5906 "prop value mismatch");
5907 *aPropValue
= std::move(aList
);
5909 SetProperty(OverflowOutOfFlowsProperty(),
5910 new (PresShell()) nsFrameList(std::move(aList
)));
5911 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5915 nsIFrame
* nsBlockFrame::GetInsideMarker() const {
5916 if (!HasInsideMarker()) {
5919 NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5920 nsIFrame
* frame
= GetProperty(InsideMarkerProperty());
5921 NS_ASSERTION(frame
, "bogus inside ::marker frame");
5925 nsIFrame
* nsBlockFrame::GetOutsideMarker() const {
5926 nsFrameList
* list
= GetOutsideMarkerList();
5927 return list
? list
->FirstChild() : nullptr;
5930 nsFrameList
* nsBlockFrame::GetOutsideMarkerList() const {
5931 if (!HasOutsideMarker()) {
5934 NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5935 nsFrameList
* list
= GetProperty(OutsideMarkerProperty());
5936 NS_ASSERTION(list
&& list
->GetLength() == 1, "bogus outside ::marker list");
5940 nsFrameList
* nsBlockFrame::GetPushedFloats() const {
5941 if (!HasPushedFloats()) {
5944 nsFrameList
* result
= GetProperty(PushedFloatProperty());
5945 NS_ASSERTION(result
, "value should always be non-empty when state set");
5949 nsFrameList
* nsBlockFrame::EnsurePushedFloats() {
5950 nsFrameList
* result
= GetPushedFloats();
5955 result
= new (PresShell()) nsFrameList
;
5956 SetProperty(PushedFloatProperty(), result
);
5957 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5962 nsFrameList
* nsBlockFrame::RemovePushedFloats() {
5963 if (!HasPushedFloats()) {
5966 nsFrameList
* result
= TakeProperty(PushedFloatProperty());
5967 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5968 NS_ASSERTION(result
, "value should always be non-empty when state set");
5972 //////////////////////////////////////////////////////////////////////
5973 // Frame list manipulation routines
5975 void nsBlockFrame::AppendFrames(ChildListID aListID
, nsFrameList
&& aFrameList
) {
5976 if (aFrameList
.IsEmpty()) {
5979 if (aListID
!= FrameChildListID::Principal
) {
5980 if (FrameChildListID::Float
== aListID
) {
5981 DrainSelfPushedFloats(); // ensure the last frame is in mFloats
5982 mFloats
.AppendFrames(nullptr, std::move(aFrameList
));
5985 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
5986 "unexpected child list");
5989 // Find the proper last-child for where the append should go
5990 nsIFrame
* lastKid
= mFrames
.LastChild();
5992 (mLines
.empty() ? nullptr : mLines
.back()->LastChild()) == lastKid
,
5993 "out-of-sync mLines / mFrames");
5995 #ifdef NOISY_REFLOW_REASON
5997 printf(": append ");
5998 for (nsIFrame
* frame
: aFrameList
) {
5999 frame
->ListTag(stdout
);
6003 lastKid
->ListTag(stdout
);
6008 if (IsInSVGTextSubtree()) {
6009 MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
6010 "unexpected block frame in SVG text");
6011 // Workaround for bug 1399425 in case this bit has been removed from the
6012 // SVGTextFrame just before the parser adds more descendant nodes.
6013 GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
);
6016 AddFrames(std::move(aFrameList
), lastKid
, nullptr);
6017 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
6018 PresShell()->FrameNeedsReflow(
6019 this, IntrinsicDirty::FrameAndAncestors
,
6020 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6024 void nsBlockFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
6025 const nsLineList::iterator
* aPrevFrameLine
,
6026 nsFrameList
&& aFrameList
) {
6027 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
6028 "inserting after sibling frame with different parent");
6030 if (aListID
!= FrameChildListID::Principal
) {
6031 if (FrameChildListID::Float
== aListID
) {
6032 DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
6033 mFloats
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
6036 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
6037 "unexpected child list");
6040 #ifdef NOISY_REFLOW_REASON
6042 printf(": insert ");
6043 for (nsIFrame
* frame
: aFrameList
) {
6044 frame
->ListTag(stdout
);
6048 aPrevFrame
->ListTag(stdout
);
6053 AddFrames(std::move(aFrameList
), aPrevFrame
, aPrevFrameLine
);
6054 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
6055 PresShell()->FrameNeedsReflow(
6056 this, IntrinsicDirty::FrameAndAncestors
,
6057 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6061 void nsBlockFrame::RemoveFrame(DestroyContext
& aContext
, ChildListID aListID
,
6062 nsIFrame
* aOldFrame
) {
6063 #ifdef NOISY_REFLOW_REASON
6065 printf(": remove ");
6066 aOldFrame
->ListTag(stdout
);
6070 if (aListID
== FrameChildListID::Principal
) {
6071 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
6072 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6074 MarkSameFloatManagerLinesDirty(this);
6076 } else if (FrameChildListID::Float
== aListID
) {
6077 // Make sure to mark affected lines dirty for the float frame
6078 // we are removing; this way is a bit messy, but so is the rest of the code.
6080 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
6081 "RemoveFrame should not be called on pushed floats.");
6082 for (nsIFrame
* f
= aOldFrame
;
6083 f
&& !f
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
6084 f
= f
->GetNextContinuation()) {
6085 MarkSameFloatManagerLinesDirty(
6086 static_cast<nsBlockFrame
*>(f
->GetParent()));
6088 DoRemoveOutOfFlowFrame(aContext
, aOldFrame
);
6089 } else if (FrameChildListID::NoReflowPrincipal
== aListID
) {
6090 // Skip the call to |FrameNeedsReflow| below by returning now.
6091 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6094 MOZ_CRASH("unexpected child list");
6097 PresShell()->FrameNeedsReflow(
6098 this, IntrinsicDirty::FrameAndAncestors
,
6099 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6102 static bool ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
) {
6103 LayoutFrameType type
= aLastFrame
->Type();
6104 if (type
== LayoutFrameType::Br
) {
6107 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
6108 if (type
== LayoutFrameType::Text
&&
6109 !aLastFrame
->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING
)) {
6110 return aLastFrame
->HasSignificantTerminalNewline();
6115 void nsBlockFrame::AddFrames(nsFrameList
&& aFrameList
, nsIFrame
* aPrevSibling
,
6116 const nsLineList::iterator
* aPrevSiblingLine
) {
6117 // Clear our line cursor, since our lines may change.
6120 if (aFrameList
.IsEmpty()) {
6124 // Attempt to find the line that contains the previous sibling
6125 nsLineList
* lineList
= &mLines
;
6126 nsFrameList
* frames
= &mFrames
;
6127 nsLineList::iterator prevSibLine
;
6128 int32_t prevSiblingIndex
;
6129 if (aPrevSiblingLine
) {
6130 MOZ_ASSERT(aPrevSibling
);
6131 prevSibLine
= *aPrevSiblingLine
;
6132 FrameLines
* overflowLines
= GetOverflowLines();
6133 MOZ_ASSERT(prevSibLine
.IsInSameList(mLines
.begin()) ||
6135 prevSibLine
.IsInSameList(overflowLines
->mLines
.begin())),
6136 "must be one of our line lists");
6137 if (overflowLines
) {
6138 // We need to find out which list it's actually in. Assume that
6139 // *if* we have overflow lines, that our primary lines aren't
6140 // huge, but our overflow lines might be.
6141 nsLineList::iterator line
= mLines
.begin(), lineEnd
= mLines
.end();
6142 while (line
!= lineEnd
) {
6143 if (line
== prevSibLine
) {
6148 if (line
== lineEnd
) {
6149 // By elimination, the line must be in our overflow lines.
6150 lineList
= &overflowLines
->mLines
;
6151 frames
= &overflowLines
->mFrames
;
6155 nsLineList::iterator nextLine
= prevSibLine
.next();
6156 nsIFrame
* lastFrameInLine
= nextLine
== lineList
->end()
6157 ? frames
->LastChild()
6158 : nextLine
->mFirstChild
->GetPrevSibling();
6159 prevSiblingIndex
= prevSibLine
->RIndexOf(aPrevSibling
, lastFrameInLine
);
6160 MOZ_ASSERT(prevSiblingIndex
>= 0,
6161 "aPrevSibling must be in aPrevSiblingLine");
6163 prevSibLine
= lineList
->end();
6164 prevSiblingIndex
= -1;
6166 // XXX_perf This is technically O(N^2) in some cases, but by using
6167 // RFind instead of Find, we make it O(N) in the most common case,
6168 // which is appending content.
6170 // Find the line that contains the previous sibling
6171 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
6172 prevSibLine
, mFrames
.LastChild(),
6173 &prevSiblingIndex
)) {
6174 // Not in mLines - try overflow lines.
6175 FrameLines
* overflowLines
= GetOverflowLines();
6177 if (overflowLines
) {
6178 prevSibLine
= overflowLines
->mLines
.end();
6179 prevSiblingIndex
= -1;
6180 found
= nsLineBox::RFindLineContaining(
6181 aPrevSibling
, overflowLines
->mLines
.begin(), prevSibLine
,
6182 overflowLines
->mFrames
.LastChild(), &prevSiblingIndex
);
6184 if (MOZ_LIKELY(found
)) {
6185 lineList
= &overflowLines
->mLines
;
6186 frames
= &overflowLines
->mFrames
;
6188 // Note: defensive code! RFindLineContaining must not return
6189 // false in this case, so if it does...
6190 MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
6191 aPrevSibling
= nullptr;
6192 prevSibLine
= lineList
->end();
6198 // Find the frame following aPrevSibling so that we can join up the
6199 // two lists of frames.
6201 // Split line containing aPrevSibling in two if the insertion
6202 // point is somewhere in the middle of the line.
6203 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
6205 // Split the line in two where the frame(s) are being inserted.
6207 NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
6208 lineList
->after_insert(prevSibLine
, line
);
6209 // Mark prevSibLine dirty and as needing textrun invalidation, since
6210 // we may be breaking up text in the line. Its previous line may also
6211 // need to be invalidated because it may be able to pull some text up.
6212 MarkLineDirty(prevSibLine
, lineList
);
6213 // The new line will also need its textruns recomputed because of the
6216 line
->SetInvalidateTextRuns(true);
6218 } else if (!lineList
->empty()) {
6219 lineList
->front()->MarkDirty();
6220 lineList
->front()->SetInvalidateTextRuns(true);
6222 const nsFrameList::Slice
& newFrames
=
6223 frames
->InsertFrames(nullptr, aPrevSibling
, std::move(aFrameList
));
6225 // Walk through the new frames being added and update the line data
6226 // structures to fit.
6227 for (nsIFrame
* newFrame
: newFrames
) {
6228 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
6229 "Unexpected aPrevSibling");
6231 !newFrame
->IsPlaceholderFrame() ||
6232 (!newFrame
->IsAbsolutelyPositioned() && !newFrame
->IsFloating()),
6233 "Placeholders should not float or be positioned");
6235 bool isBlock
= newFrame
->IsBlockOutside();
6237 // If the frame is a block frame, or if there is no previous line or if the
6238 // previous line is a block line we need to make a new line. We also make
6239 // a new line, as an optimization, in the two cases we know we'll need it:
6240 // if the previous line ended with a <br>, or if it has significant
6241 // whitespace and ended in a newline.
6242 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
6243 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
6244 // Create a new line for the frame and add its line to the line
6246 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
6247 if (prevSibLine
!= lineList
->end()) {
6248 // Append new line after prevSibLine
6249 lineList
->after_insert(prevSibLine
, line
);
6252 // New line is going before the other lines
6253 lineList
->push_front(line
);
6254 prevSibLine
= lineList
->begin();
6257 prevSibLine
->NoteFrameAdded(newFrame
);
6258 // We're adding inline content to prevSibLine, so we need to mark it
6259 // dirty, ensure its textruns are recomputed, and possibly do the same
6260 // to its previous line since that line may be able to pull content up.
6261 MarkLineDirty(prevSibLine
, lineList
);
6264 aPrevSibling
= newFrame
;
6268 MOZ_ASSERT(aFrameList
.IsEmpty());
6273 nsContainerFrame
* nsBlockFrame::GetRubyContentPseudoFrame() {
6274 auto* firstChild
= PrincipalChildList().FirstChild();
6275 if (firstChild
&& firstChild
->IsRubyFrame() &&
6276 firstChild
->Style()->GetPseudoType() ==
6277 mozilla::PseudoStyleType::blockRubyContent
) {
6278 return static_cast<nsContainerFrame
*>(firstChild
);
6283 nsContainerFrame
* nsBlockFrame::GetContentInsertionFrame() {
6284 // 'display:block ruby' use the inner (Ruby) frame for insertions.
6285 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6286 return rubyContentPseudoFrame
;
6291 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
6292 nsTArray
<OwnedAnonBox
>& aResult
) {
6293 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6294 aResult
.AppendElement(OwnedAnonBox(rubyContentPseudoFrame
));
6298 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
) {
6299 // Find which line contains the float, so we can update
6301 for (auto& line
: Lines()) {
6302 if (line
.IsInline() && line
.RemoveFloat(aFloat
)) {
6308 void nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
6310 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
6311 // frame list properties.
6312 if (!mFloats
.ContainsFrame(aFloat
)) {
6314 (GetOverflowOutOfFlows() &&
6315 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
6316 (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat
)),
6317 "aFloat is not our child or on an unexpected frame list");
6321 if (mFloats
.StartRemoveFrame(aFloat
)) {
6325 nsFrameList
* list
= GetPushedFloats();
6326 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
6328 // XXXmats not yet - need to investigate BlockReflowState::mPushedFloats
6329 // first so we don't leave it pointing to a deleted list.
6330 if (list
->IsEmpty()) {
6331 delete RemovePushedFloats();
6338 nsAutoOOFFrameList
oofs(this);
6339 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
6345 void nsBlockFrame::DoRemoveOutOfFlowFrame(DestroyContext
& aContext
,
6347 // The containing block is always the parent of aFrame.
6348 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
6350 // Remove aFrame from the appropriate list.
6351 if (aFrame
->IsAbsolutelyPositioned()) {
6352 // This also deletes the next-in-flows
6353 block
->GetAbsoluteContainingBlock()->RemoveFrame(
6354 aContext
, FrameChildListID::Absolute
, aFrame
);
6356 // First remove aFrame's next-in-flows.
6357 if (nsIFrame
* nif
= aFrame
->GetNextInFlow()) {
6358 nif
->GetParent()->DeleteNextInFlowChild(aContext
, nif
, false);
6360 // Now remove aFrame from its child list and Destroy it.
6361 block
->RemoveFloatFromFloatCache(aFrame
);
6362 block
->RemoveFloat(aFrame
);
6363 aFrame
->Destroy(aContext
);
6368 * This helps us iterate over the list of all normal + overflow lines
6370 void nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
6371 nsLineList::iterator
* aStartIterator
,
6372 nsLineList::iterator
* aEndIterator
,
6373 bool* aInOverflowLines
,
6374 FrameLines
** aOverflowLines
) {
6375 if (*aIterator
== *aEndIterator
) {
6376 if (!*aInOverflowLines
) {
6377 // Try the overflow lines
6378 *aInOverflowLines
= true;
6379 FrameLines
* lines
= GetOverflowLines();
6381 *aStartIterator
= lines
->mLines
.begin();
6382 *aIterator
= *aStartIterator
;
6383 *aEndIterator
= lines
->mLines
.end();
6384 *aOverflowLines
= lines
;
6390 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6392 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
) {
6393 // This will assert if aLine isn't in mLines of aFrame:
6394 DebugOnly
<bool> check
= aLine
== mFrame
->LinesBegin();
6397 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6402 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
6403 : &aFrame
->mLines
) {}
6405 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6406 bool* aFoundValidLine
)
6407 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6408 mLine
= aFrame
->LinesBegin();
6409 *aFoundValidLine
= FindValidLine();
6412 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState
& aRestyleState
) {
6413 nsIFrame
* letterFrame
= GetFirstLetter();
6418 // Figure out what the right style parent is. This needs to match
6419 // nsCSSFrameConstructor::CreateLetterFrame.
6420 nsIFrame
* inFlowFrame
= letterFrame
;
6421 if (inFlowFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6422 inFlowFrame
= inFlowFrame
->GetPlaceholderFrame();
6424 nsIFrame
* styleParent
= CorrectStyleParentFrame(inFlowFrame
->GetParent(),
6425 PseudoStyleType::firstLetter
);
6426 ComputedStyle
* parentStyle
= styleParent
->Style();
6427 RefPtr
<ComputedStyle
> firstLetterStyle
=
6428 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
6429 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr,
6431 // Note that we don't need to worry about changehints for the continuation
6432 // styles: those will be handled by the styleParent already.
6433 RefPtr
<ComputedStyle
> continuationStyle
=
6434 aRestyleState
.StyleSet().ResolveStyleForFirstLetterContinuation(
6436 UpdateStyleOfOwnedChildFrame(letterFrame
, firstLetterStyle
, aRestyleState
,
6437 Some(continuationStyle
.get()));
6439 // We also want to update the style on the textframe inside the first-letter.
6440 // We don't need to compute a changehint for this, though, since any changes
6441 // to it are handled by the first-letter anyway.
6442 nsIFrame
* textFrame
= letterFrame
->PrincipalChildList().FirstChild();
6443 RefPtr
<ComputedStyle
> firstTextStyle
=
6444 aRestyleState
.StyleSet().ResolveStyleForText(textFrame
->GetContent(),
6446 textFrame
->SetComputedStyle(firstTextStyle
);
6448 // We don't need to update style for textFrame's continuations: it's already
6449 // set up to inherit from parentStyle, which is what we want.
6452 static nsIFrame
* FindChildContaining(nsBlockFrame
* aFrame
,
6453 nsIFrame
* aFindFrame
) {
6454 NS_ASSERTION(aFrame
, "must have frame");
6457 nsIFrame
* block
= aFrame
;
6459 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
6463 block
= block
->GetNextContinuation();
6468 if (!child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6471 aFindFrame
= child
->GetPlaceholderFrame();
6477 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6478 nsIFrame
* aFindFrame
,
6479 bool* aFoundValidLine
)
6480 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6481 *aFoundValidLine
= false;
6483 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
6488 LineIterator line_end
= aFrame
->LinesEnd();
6489 mLine
= aFrame
->LinesBegin();
6490 if (mLine
!= line_end
&& mLine
.next() == line_end
&&
6491 !aFrame
->HasOverflowLines()) {
6492 // The block has a single line - that must be it!
6493 *aFoundValidLine
= true;
6497 // Try to use the cursor if it exists, otherwise fall back to the first line
6498 if (nsLineBox
* const cursor
= aFrame
->GetLineCursorForQuery()) {
6500 // Perform a simultaneous forward and reverse search starting from the
6502 nsBlockFrame::LineIterator line
= aFrame
->LinesBeginFrom(cursor
);
6503 nsBlockFrame::ReverseLineIterator rline
= aFrame
->LinesRBeginFrom(cursor
);
6504 nsBlockFrame::ReverseLineIterator rline_end
= aFrame
->LinesREnd();
6505 // rline is positioned on the line containing 'cursor', so it's not
6506 // rline_end. So we can safely increment it (i.e. move it to one line
6507 // earlier) to start searching there.
6509 while (line
!= line_end
|| rline
!= rline_end
) {
6510 if (line
!= line_end
) {
6511 if (line
->Contains(child
)) {
6517 if (rline
!= rline_end
) {
6518 if (rline
->Contains(child
)) {
6525 if (mLine
!= line_end
) {
6526 *aFoundValidLine
= true;
6527 if (mLine
!= cursor
) {
6528 aFrame
->SetProperty(nsBlockFrame::LineCursorPropertyQuery(), mLine
);
6533 for (mLine
= aFrame
->LinesBegin(); mLine
!= line_end
; ++mLine
) {
6534 if (mLine
->Contains(child
)) {
6535 *aFoundValidLine
= true;
6540 // Didn't find the line
6541 MOZ_ASSERT(mLine
== line_end
, "mLine should be line_end at this point");
6543 // If we reach here, it means that we have not been able to find the
6544 // desired frame in our in-flow lines. So we should start looking at
6545 // our overflow lines. In order to do that, we set mLine to the end
6546 // iterator so that FindValidLine starts to look at overflow lines,
6549 if (!FindValidLine()) {
6554 if (mLine
->Contains(child
)) {
6555 *aFoundValidLine
= true;
6561 nsBlockFrame::LineIterator
nsBlockInFlowLineIterator::End() {
6562 return mLineList
->end();
6565 bool nsBlockInFlowLineIterator::IsLastLineInList() {
6566 LineIterator end
= End();
6567 return mLine
!= end
&& mLine
.next() == end
;
6570 bool nsBlockInFlowLineIterator::Next() {
6572 return FindValidLine();
6575 bool nsBlockInFlowLineIterator::Prev() {
6576 LineIterator begin
= mLineList
->begin();
6577 if (mLine
!= begin
) {
6581 bool currentlyInOverflowLines
= GetInOverflow();
6583 if (currentlyInOverflowLines
) {
6584 mLineList
= &mFrame
->mLines
;
6585 mLine
= mLineList
->end();
6586 if (mLine
!= mLineList
->begin()) {
6591 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
6595 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6596 if (overflowLines
) {
6597 mLineList
= &overflowLines
->mLines
;
6598 mLine
= mLineList
->end();
6599 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
6604 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6608 bool nsBlockInFlowLineIterator::FindValidLine() {
6609 LineIterator end
= mLineList
->end();
6613 bool currentlyInOverflowLines
= GetInOverflow();
6615 if (currentlyInOverflowLines
) {
6616 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
6620 mLineList
= &mFrame
->mLines
;
6621 mLine
= mLineList
->begin();
6622 if (mLine
!= mLineList
->end()) {
6626 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6627 if (overflowLines
) {
6628 mLineList
= &overflowLines
->mLines
;
6629 mLine
= mLineList
->begin();
6630 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
6634 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6638 // This function removes aDeletedFrame and all its continuations. It
6639 // is optimized for deleting a whole series of frames. The easy
6640 // implementation would invoke itself recursively on
6641 // aDeletedFrame->GetNextContinuation, then locate the line containing
6642 // aDeletedFrame and remove aDeletedFrame from that line. But here we
6643 // start by locating aDeletedFrame and then scanning from that point
6644 // on looking for continuations.
6645 void nsBlockFrame::DoRemoveFrame(DestroyContext
& aContext
,
6646 nsIFrame
* aDeletedFrame
, uint32_t aFlags
) {
6647 // Clear our line cursor, since our lines may change.
6650 if (aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6651 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6652 if (!aDeletedFrame
->GetPrevInFlow()) {
6653 NS_ASSERTION(aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6654 "Expected out-of-flow frame");
6655 DoRemoveOutOfFlowFrame(aContext
, aDeletedFrame
);
6657 // FIXME(emilio): aContext is lost here, maybe it's not a big deal?
6658 nsContainerFrame::DeleteNextInFlowChild(aContext
, aDeletedFrame
,
6659 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
6664 // Find the line that contains deletedFrame
6665 nsLineList::iterator line_start
= mLines
.begin(), line_end
= mLines
.end();
6666 nsLineList::iterator line
= line_start
;
6667 FrameLines
* overflowLines
= nullptr;
6668 bool searchingOverflowList
= false;
6669 // Make sure we look in the overflow lines even if the normal line
6671 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6673 while (line
!= line_end
) {
6674 if (line
->Contains(aDeletedFrame
)) {
6678 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6682 if (line
== line_end
) {
6683 NS_ERROR("can't find deleted frame in lines");
6687 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6688 if (line
!= line_start
) {
6689 line
.prev()->MarkDirty();
6690 line
.prev()->SetInvalidateTextRuns(true);
6691 } else if (searchingOverflowList
&& !mLines
.empty()) {
6692 mLines
.back()->MarkDirty();
6693 mLines
.back()->SetInvalidateTextRuns(true);
6697 while (line
!= line_end
&& aDeletedFrame
) {
6698 MOZ_ASSERT(this == aDeletedFrame
->GetParent(), "messed up delete code");
6699 MOZ_ASSERT(line
->Contains(aDeletedFrame
), "frame not in line");
6701 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6703 line
->SetInvalidateTextRuns(true);
6706 // If the frame being deleted is the last one on the line then
6707 // optimize away the line->Contains(next-in-flow) call below.
6708 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
6709 if (!isLastFrameOnLine
) {
6710 LineIterator next
= line
.next();
6711 nsIFrame
* lastFrame
=
6713 ? next
->mFirstChild
->GetPrevSibling()
6714 : (searchingOverflowList
? overflowLines
->mFrames
.LastChild()
6715 : mFrames
.LastChild());
6716 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
6717 "unexpected line frames");
6718 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
6721 // Remove aDeletedFrame from the line
6722 if (line
->mFirstChild
== aDeletedFrame
) {
6723 // We should be setting this to null if aDeletedFrame
6724 // is the only frame on the line. HOWEVER in that case
6725 // we will be removing the line anyway, see below.
6726 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
6729 // Hmm, this won't do anything if we're removing a frame in the first
6730 // overflow line... Hopefully doesn't matter
6732 if (line
!= line_end
&& !line
->IsBlock()) {
6733 // Since we just removed a frame that follows some inline
6734 // frames, we need to reflow the previous line.
6739 // Take aDeletedFrame out of the sibling list. Note that
6740 // prevSibling will only be nullptr when we are deleting the very
6741 // first frame in the main or overflow list.
6742 if (searchingOverflowList
) {
6743 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
6745 mFrames
.RemoveFrame(aDeletedFrame
);
6748 // Update the child count of the line to be accurate
6749 line
->NoteFrameRemoved(aDeletedFrame
);
6751 // Destroy frame; capture its next continuation first in case we need
6752 // to destroy that too.
6753 nsIFrame
* deletedNextContinuation
=
6754 (aFlags
& REMOVE_FIXED_CONTINUATIONS
)
6755 ? aDeletedFrame
->GetNextContinuation()
6756 : aDeletedFrame
->GetNextInFlow();
6757 #ifdef NOISY_REMOVE_FRAME
6758 printf("DoRemoveFrame: %s line=%p frame=",
6759 searchingOverflowList
? "overflow" : "normal", line
.get());
6760 aDeletedFrame
->ListTag(stdout
);
6761 printf(" prevSibling=%p deletedNextContinuation=%p\n",
6762 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
6765 // If next-in-flow is an overflow container, must remove it first.
6766 // FIXME: Can we do this unconditionally?
6767 if (deletedNextContinuation
&& deletedNextContinuation
->HasAnyStateBits(
6768 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6769 deletedNextContinuation
->GetParent()->DeleteNextInFlowChild(
6770 aContext
, deletedNextContinuation
, false);
6771 deletedNextContinuation
= nullptr;
6774 aDeletedFrame
->Destroy(aContext
);
6775 aDeletedFrame
= deletedNextContinuation
;
6777 bool haveAdvancedToNextLine
= false;
6778 // If line is empty, remove it now.
6779 if (0 == line
->GetChildCount()) {
6780 #ifdef NOISY_REMOVE_FRAME
6781 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6782 searchingOverflowList
? "overflow" : "normal", line
.get());
6784 nsLineBox
* cur
= line
;
6785 if (!searchingOverflowList
) {
6786 line
= mLines
.erase(line
);
6787 // Invalidate the space taken up by the line.
6788 // XXX We need to do this if we're removing a frame as a result of
6789 // a call to RemoveFrame(), but we may not need to do this in all
6791 #ifdef NOISY_BLOCK_INVALIDATE
6792 nsRect
inkOverflow(cur
->InkOverflowRect());
6793 printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow
.x
,
6794 inkOverflow
.y
, inkOverflow
.width
, inkOverflow
.height
);
6797 line
= overflowLines
->mLines
.erase(line
);
6798 if (overflowLines
->mLines
.empty()) {
6799 DestroyOverflowLines();
6800 overflowLines
= nullptr;
6801 // We just invalidated our iterators. Since we were in
6802 // the overflow lines list, which is now empty, set them
6803 // so we're at the end of the regular line list.
6804 line_start
= mLines
.begin();
6805 line_end
= mLines
.end();
6811 // If we're removing a line, ReflowDirtyLines isn't going to
6812 // know that it needs to slide lines unless something is marked
6813 // dirty. So mark the previous margin of the next line dirty if
6815 if (line
!= line_end
) {
6816 line
->MarkPreviousMarginDirty();
6818 haveAdvancedToNextLine
= true;
6820 // Make the line that just lost a frame dirty, and advance to
6822 if (!deletedNextContinuation
|| isLastFrameOnLine
||
6823 !line
->Contains(deletedNextContinuation
)) {
6826 haveAdvancedToNextLine
= true;
6830 if (deletedNextContinuation
) {
6831 // See if we should keep looking in the current flow's line list.
6832 if (deletedNextContinuation
->GetParent() != this) {
6833 // The deceased frames continuation is not a child of the
6834 // current block. So break out of the loop so that we advance
6835 // to the next parent.
6837 // If we have a continuation in a different block then all bets are
6838 // off regarding whether we are deleting frames without actual content,
6839 // so don't propagate FRAMES_ARE_EMPTY any further.
6840 aFlags
&= ~FRAMES_ARE_EMPTY
;
6844 // If we advanced to the next line then check if we should switch to the
6845 // overflow line list.
6846 if (haveAdvancedToNextLine
) {
6847 if (line
!= line_end
&& !searchingOverflowList
&&
6848 !line
->Contains(deletedNextContinuation
)) {
6849 // We have advanced to the next *normal* line but the next-in-flow
6850 // is not there - force a switch to the overflow line list.
6854 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6856 #ifdef NOISY_REMOVE_FRAME
6857 printf("DoRemoveFrame: now on %s line=%p\n",
6858 searchingOverflowList
? "overflow" : "normal", line
.get());
6864 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
6865 line
.next()->MarkDirty();
6866 line
.next()->SetInvalidateTextRuns(true);
6871 VerifyOverflowSituation();
6874 // Advance to next flow block if the frame has more continuations.
6875 if (!aDeletedFrame
) {
6878 nsBlockFrame
* nextBlock
= do_QueryFrame(aDeletedFrame
->GetParent());
6879 NS_ASSERTION(nextBlock
, "Our child's continuation's parent is not a block?");
6880 uint32_t flags
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
);
6881 nextBlock
->DoRemoveFrame(aContext
, aDeletedFrame
, flags
);
6884 static bool FindBlockLineFor(nsIFrame
* aChild
, nsLineList::iterator aBegin
,
6885 nsLineList::iterator aEnd
,
6886 nsLineList::iterator
* aResult
) {
6887 MOZ_ASSERT(aChild
->IsBlockOutside());
6888 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6889 MOZ_ASSERT(line
->GetChildCount() > 0);
6890 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
6891 MOZ_ASSERT(line
->GetChildCount() == 1);
6899 static bool FindInlineLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6900 nsLineList::iterator aBegin
,
6901 nsLineList::iterator aEnd
,
6902 nsLineList::iterator
* aResult
) {
6903 MOZ_ASSERT(!aChild
->IsBlockOutside());
6904 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6905 MOZ_ASSERT(line
->GetChildCount() > 0);
6906 if (!line
->IsBlock()) {
6907 // Optimize by comparing the line's last child first.
6908 nsLineList::iterator next
= line
.next();
6909 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
6910 : next
->mFirstChild
->GetPrevSibling()) ||
6911 line
->Contains(aChild
)) {
6920 static bool FindLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6921 nsLineList::iterator aBegin
, nsLineList::iterator aEnd
,
6922 nsLineList::iterator
* aResult
) {
6923 return aChild
->IsBlockOutside()
6924 ? FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
)
6925 : FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
6928 void nsBlockFrame::StealFrame(nsIFrame
* aChild
) {
6929 MOZ_ASSERT(aChild
->GetParent() == this);
6931 if (aChild
->IsFloating()) {
6932 RemoveFloat(aChild
);
6936 if (MaybeStealOverflowContainerFrame(aChild
)) {
6940 MOZ_ASSERT(!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
6942 nsLineList::iterator line
;
6943 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
6944 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
6946 FrameLines
* overflowLines
= GetOverflowLines();
6947 DebugOnly
<bool> found
;
6948 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
6949 overflowLines
->mLines
.begin(),
6950 overflowLines
->mLines
.end(), &line
);
6951 MOZ_ASSERT(found
, "Why can't we find aChild in our overflow lines?");
6952 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
6953 overflowLines
->mLines
);
6954 if (overflowLines
->mLines
.empty()) {
6955 DestroyOverflowLines();
6960 void nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
,
6961 nsLineList::iterator aLine
,
6962 nsFrameList
& aFrameList
,
6963 nsLineList
& aLineList
) {
6964 aFrameList
.RemoveFrame(aChild
);
6965 if (aChild
== aLine
->mFirstChild
) {
6966 aLine
->mFirstChild
= aChild
->GetNextSibling();
6968 aLine
->NoteFrameRemoved(aChild
);
6969 if (aLine
->GetChildCount() > 0) {
6972 // The line became empty - destroy it.
6973 nsLineBox
* lineBox
= aLine
;
6974 aLine
= aLineList
.erase(aLine
);
6975 if (aLine
!= aLineList
.end()) {
6976 aLine
->MarkPreviousMarginDirty();
6978 FreeLineBox(lineBox
);
6982 void nsBlockFrame::DeleteNextInFlowChild(DestroyContext
& aContext
,
6983 nsIFrame
* aNextInFlow
,
6984 bool aDeletingEmptyFrames
) {
6985 MOZ_ASSERT(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
6987 if (aNextInFlow
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6988 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6989 nsContainerFrame::DeleteNextInFlowChild(aContext
, aNextInFlow
,
6990 aDeletingEmptyFrames
);
6993 if (aDeletingEmptyFrames
) {
6994 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
6997 DoRemoveFrame(aContext
, aNextInFlow
,
6998 aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
7002 const nsStyleText
* nsBlockFrame::StyleTextForLineLayout() {
7003 // Return the pointer to an unmodified style text
7007 void nsBlockFrame::ReflowFloat(BlockReflowState
& aState
, ReflowInput
& aFloatRI
,
7009 nsReflowStatus
& aReflowStatus
) {
7010 MOZ_ASSERT(aReflowStatus
.IsEmpty(),
7011 "Caller should pass a fresh reflow status!");
7012 MOZ_ASSERT(aFloat
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
7013 "aFloat must be an out-of-flow frame");
7015 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
7017 // Setup a block reflow context to reflow the float.
7018 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
7020 nsIFrame
* clearanceFrame
= nullptr;
7022 nsCollapsingMargin margin
;
7023 bool mayNeedRetry
= false;
7024 aFloatRI
.mDiscoveredClearance
= nullptr;
7025 // Only first in flow gets a block-start margin.
7026 if (!aFloat
->GetPrevInFlow()) {
7027 brc
.ComputeCollapsedBStartMargin(aFloatRI
, &margin
, clearanceFrame
,
7030 if (mayNeedRetry
&& !clearanceFrame
) {
7031 aFloatRI
.mDiscoveredClearance
= &clearanceFrame
;
7032 // We don't need to push the float manager state because the the block
7033 // has its own float manager that will be destroyed and recreated
7037 // When reflowing a float, aSpace argument doesn't matter because we pass
7038 // nullptr to aLine and we don't call nsBlockReflowContext::PlaceBlock()
7040 brc
.ReflowBlock(LogicalRect(wm
), true, margin
, 0, nullptr, aFloatRI
,
7041 aReflowStatus
, aState
);
7042 } while (clearanceFrame
);
7044 if (aFloat
->IsLetterFrame()) {
7045 // We never split floating first letters; an incomplete status for such
7046 // frames simply means that there is more content to be reflowed on the
7048 if (aReflowStatus
.IsIncomplete()) {
7049 aReflowStatus
.Reset();
7053 NS_ASSERTION(aReflowStatus
.IsFullyComplete() ||
7054 aFloatRI
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
7055 "The status can only be incomplete or overflow-incomplete if "
7056 "the available block-size is constrained!");
7058 if (aReflowStatus
.NextInFlowNeedsReflow()) {
7059 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
7062 const ReflowOutput
& metrics
= brc
.GetMetrics();
7064 // Set the rect, make sure the view is properly sized and positioned,
7065 // and tell the frame we're done reflowing it
7066 // XXXldb This seems like the wrong place to be doing this -- shouldn't
7067 // we be doing this in BlockReflowState::FlowAndPlaceFloat after
7068 // we've positioned the float, and shouldn't we be doing the equivalent
7069 // of |PlaceFrameView| here?
7070 WritingMode metricsWM
= metrics
.GetWritingMode();
7071 aFloat
->SetSize(metricsWM
, metrics
.Size(metricsWM
));
7072 if (aFloat
->HasView()) {
7073 nsContainerFrame::SyncFrameViewAfterReflow(
7074 aState
.mPresContext
, aFloat
, aFloat
->GetView(), metrics
.InkOverflow(),
7075 ReflowChildFlags::NoMoveView
);
7077 aFloat
->DidReflow(aState
.mPresContext
, &aFloatRI
);
7080 StyleClear
nsBlockFrame::FindTrailingClear() {
7081 for (nsBlockFrame
* b
= this; b
;
7082 b
= static_cast<nsBlockFrame
*>(b
->GetPrevInFlow())) {
7083 auto endLine
= b
->LinesRBegin();
7084 if (endLine
!= b
->LinesREnd()) {
7085 return endLine
->FloatClearTypeAfter();
7088 return StyleClear::None
;
7091 void nsBlockFrame::ReflowPushedFloats(BlockReflowState
& aState
,
7092 OverflowAreas
& aOverflowAreas
) {
7093 // Pushed floats live at the start of our float list; see comment
7094 // above nsBlockFrame::DrainPushedFloats.
7095 nsIFrame
* f
= mFloats
.FirstChild();
7096 nsIFrame
* prev
= nullptr;
7097 while (f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7098 MOZ_ASSERT(prev
== f
->GetPrevSibling());
7099 // When we push a first-continuation float in a non-initial reflow,
7100 // it's possible that we end up with two continuations with the same
7101 // parent. This happens if, on the previous reflow of the block or
7102 // a previous reflow of the line containing the block, the float was
7103 // split between continuations A and B of the parent, but on the
7104 // current reflow, none of the float can fit in A.
7106 // When this happens, we might even have the two continuations
7107 // out-of-order due to the management of the pushed floats. In
7108 // particular, if the float's placeholder was in a pushed line that
7109 // we reflowed before it was pushed, and we split the float during
7110 // that reflow, we might have the continuation of the float before
7111 // the float itself. (In the general case, however, it's correct
7112 // for floats in the pushed floats list to come before floats
7113 // anchored in pushed lines; however, in this case it's wrong. We
7114 // should probably find a way to fix it somehow, since it leads to
7115 // incorrect layout in some cases.)
7117 // When we have these out-of-order continuations, we might hit the
7118 // next-continuation before the previous-continuation. When that
7119 // happens, just push it. When we reflow the next continuation,
7120 // we'll either pull all of its content back and destroy it (by
7121 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
7122 // pull it out of its current position and push it again (and
7123 // potentially repeat this cycle for the next continuation, although
7124 // hopefully then they'll be in the right order).
7126 // We should also need this code for the in-order case if the first
7127 // continuation of a float gets moved across more than one
7128 // continuation of the containing block. In this case we'd manage
7129 // to push the second continuation without this check, but not the
7131 nsIFrame
* prevContinuation
= f
->GetPrevContinuation();
7132 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
7133 mFloats
.RemoveFrame(f
);
7134 aState
.AppendPushedFloatChain(f
);
7135 f
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7139 // Always call FlowAndPlaceFloat; we might need to place this float if it
7140 // didn't belong to this block the last time it was reflowed. Note that if
7141 // the float doesn't get placed, we don't consider its overflow areas.
7142 // (Not-getting-placed means it didn't fit and we pushed it instead of
7143 // placing it, and its position could be stale.)
7144 if (aState
.FlowAndPlaceFloat(f
) ==
7145 BlockReflowState::PlaceFloatResult::Placed
) {
7146 ConsiderChildOverflow(aOverflowAreas
, f
);
7149 nsIFrame
* next
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7151 // We didn't push |f| so its next-sibling is next.
7152 next
= f
->GetNextSibling();
7154 } // else: we did push |f| so |prev|'s new next-sibling is next.
7158 // If there are pushed or split floats, then we may need to continue BR
7160 if (auto [bCoord
, result
] = aState
.ClearFloats(0, StyleClear::Both
);
7161 result
!= ClearFloatsResult::BCoordNoChange
) {
7163 if (auto* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow())) {
7164 aState
.mTrailingClearFromPIF
= prevBlock
->FindTrailingClear();
7169 void nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
, WritingMode aWM
,
7170 const nsSize
& aContainerSize
) {
7171 // Recover our own floats
7172 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
7173 // belong to our next-in-flow
7174 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
;
7175 f
= f
->GetNextSibling()) {
7176 LogicalRect region
= nsFloatManager::GetRegionFor(aWM
, f
, aContainerSize
);
7177 aFloatManager
.AddFloat(f
, region
, aWM
, aContainerSize
);
7178 if (!stop
&& f
->GetNextInFlow()) {
7179 stop
= f
->GetNextInFlow();
7183 // Recurse into our overflow container children
7185 GetChildList(FrameChildListID::OverflowContainers
).FirstChild();
7186 oc
; oc
= oc
->GetNextSibling()) {
7187 RecoverFloatsFor(oc
, aFloatManager
, aWM
, aContainerSize
);
7190 // Recurse into our normal children
7191 for (const auto& line
: Lines()) {
7192 if (line
.IsBlock()) {
7193 RecoverFloatsFor(line
.mFirstChild
, aFloatManager
, aWM
, aContainerSize
);
7198 void nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
7199 nsFloatManager
& aFloatManager
,
7201 const nsSize
& aContainerSize
) {
7202 MOZ_ASSERT(aFrame
, "null frame");
7204 // Only blocks have floats
7205 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
7206 // Don't recover any state inside a block that has its own float manager
7207 // (we don't currently have any blocks like this, though, thanks to our
7208 // use of extra frames for 'overflow')
7209 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
7210 // If the element is relatively positioned, then adjust x and y
7211 // accordingly so that we consider relatively positioned frames
7212 // at their original position.
7214 const LogicalRect rect
= block
->GetLogicalNormalRect(aWM
, aContainerSize
);
7215 nscoord lineLeft
= rect
.LineLeft(aWM
, aContainerSize
);
7216 nscoord blockStart
= rect
.BStart(aWM
);
7217 aFloatManager
.Translate(lineLeft
, blockStart
);
7218 block
->RecoverFloats(aFloatManager
, aWM
, aContainerSize
);
7219 aFloatManager
.Translate(-lineLeft
, -blockStart
);
7223 bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
7224 if (!mFloats
.IsEmpty()) {
7225 // If we have pushed floats, then they should be at the beginning of our
7227 if (mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7233 // Double-check the above assertion that pushed floats should be at the
7234 // beginning of our floats list.
7235 for (nsIFrame
* f
: mFloats
) {
7236 NS_ASSERTION(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
7237 "pushed floats must be at the beginning of the float list");
7241 // We may have a pending push of pushed floats too:
7242 if (HasPushedFloats()) {
7243 // XXX we can return 'true' here once we make HasPushedFloats
7244 // not lie. (see nsBlockFrame::RemoveFloat)
7245 auto* pushedFloats
= GetPushedFloats();
7246 return pushedFloats
&& !pushedFloats
->IsEmpty();
7251 //////////////////////////////////////////////////////////////////////
7252 // Painting, event handling
7255 static void ComputeInkOverflowArea(nsLineList
& aLines
, nscoord aWidth
,
7256 nscoord aHeight
, nsRect
& aResult
) {
7257 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
7258 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
7259 line
!= line_end
; ++line
) {
7260 // Compute min and max x/y values for the reflowed frame's
7262 nsRect
inkOverflow(line
->InkOverflowRect());
7263 nscoord x
= inkOverflow
.x
;
7264 nscoord y
= inkOverflow
.y
;
7265 nscoord xmost
= x
+ inkOverflow
.width
;
7266 nscoord ymost
= y
+ inkOverflow
.height
;
7283 aResult
.width
= xb
- xa
;
7284 aResult
.height
= yb
- ya
;
7289 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
7290 if (nsBlockFrame::gNoisyDamageRepair
) {
7291 nsIFrame::IndentBy(stdout
, aDepth
+ 1);
7292 nsRect lineArea
= aLine
->InkOverflowRect();
7293 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7294 aDrawn
? "draw" : "skip", static_cast<void*>(aLine
), aLine
->IStart(),
7295 aLine
->BStart(), aLine
->ISize(), aLine
->BSize(), lineArea
.x
,
7296 lineArea
.y
, lineArea
.width
, lineArea
.height
);
7301 static void DisplayLine(nsDisplayListBuilder
* aBuilder
,
7302 nsBlockFrame::LineIterator
& aLine
,
7303 const bool aLineInLine
, const nsDisplayListSet
& aLists
,
7304 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
,
7305 uint32_t aLineNumberForTextOverflow
, int32_t aDepth
,
7306 int32_t& aDrawnLines
) {
7308 if (nsBlockFrame::gLamePaintMetrics
) {
7311 const bool intersect
=
7312 aLine
->InkOverflowRect().Intersects(aBuilder
->GetDirtyRect());
7313 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
7316 // Collect our line's display items in a temporary nsDisplayListCollection,
7317 // so that we can apply any "text-overflow" clipping to the entire collection
7318 // without affecting previous lines.
7319 nsDisplayListCollection
collection(aBuilder
);
7321 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
7322 // Inline-level child backgrounds go on the regular child content list.
7323 nsDisplayListSet
childLists(
7325 aLineInLine
? collection
.Content() : collection
.BlockBorderBackgrounds());
7329 ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline
)
7330 : nsIFrame::DisplayChildFlags();
7332 nsIFrame
* kid
= aLine
->mFirstChild
;
7333 int32_t n
= aLine
->GetChildCount();
7335 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, childLists
, flags
);
7336 kid
= kid
->GetNextSibling();
7339 if (aTextOverflow
&& aLineInLine
) {
7340 aTextOverflow
->ProcessLine(collection
, aLine
.get(),
7341 aLineNumberForTextOverflow
);
7344 collection
.MoveTo(aLists
);
7347 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
7348 const nsDisplayListSet
& aLists
) {
7349 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
7352 if (gNoisyDamageRepair
) {
7353 nsRect dirty
= aBuilder
->GetDirtyRect();
7356 ::ComputeInkOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
7357 nsIFrame::IndentBy(stdout
, depth
);
7359 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7360 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
, dirty
.x
, dirty
.y
,
7361 dirty
.width
, dirty
.height
, ca
.x
, ca
.y
, ca
.width
, ca
.height
);
7363 PRTime start
= 0; // Initialize these variables to silence the compiler.
7364 if (gLamePaintMetrics
) {
7370 // TODO(heycam): Should we boost the load priority of any shape-outside
7371 // images using CATEGORY_DISPLAY, now that this block is being displayed?
7372 // We don't have a float manager here.
7374 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
7376 if (GetPrevInFlow()) {
7377 DisplayOverflowContainers(aBuilder
, aLists
);
7378 for (nsIFrame
* f
: mFloats
) {
7379 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7380 BuildDisplayListForChild(aBuilder
, f
, aLists
);
7385 aBuilder
->MarkFramesForDisplayList(this, mFloats
);
7387 if (HasOutsideMarker()) {
7388 // Display outside ::marker manually.
7389 BuildDisplayListForChild(aBuilder
, GetOutsideMarker(), aLists
);
7392 // Prepare for text-overflow processing.
7393 Maybe
<TextOverflow
> textOverflow
=
7394 TextOverflow::WillProcessLines(aBuilder
, this);
7396 const bool hasDescendantPlaceHolders
=
7397 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7398 ForceDescendIntoIfVisible() || aBuilder
->GetIncludeAllOutOfFlows();
7400 const auto ShouldDescendIntoLine
= [&](const nsRect
& aLineArea
) -> bool {
7401 // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
7402 // some frame trees, building display list for child lines can change it.
7404 const bool descendAlways
=
7405 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7406 aBuilder
->GetIncludeAllOutOfFlows();
7408 return descendAlways
|| aLineArea
.Intersects(aBuilder
->GetDirtyRect()) ||
7409 (ForceDescendIntoIfVisible() &&
7410 aLineArea
.Intersects(aBuilder
->GetVisibleRect()));
7413 Maybe
<nscolor
> backplateColor
;
7415 // We'll try to draw an accessibility backplate behind text (to ensure it's
7416 // readable over any possible background-images), if all of the following
7418 // (A) the backplate feature is preffed on
7419 // (B) we are not honoring the document colors
7420 // (C) the force color adjust property is set to auto
7421 if (StaticPrefs::browser_display_permit_backplate() &&
7422 PresContext()->ForcingColors() && !IsComboboxControlFrame() &&
7423 StyleText()->mForcedColorAdjust
!= StyleForcedColorAdjust::None
) {
7424 backplateColor
.emplace(GetBackplateColor(this));
7427 // Don't use the line cursor if we might have a descendant placeholder ...
7428 // it might skip lines that contain placeholders but don't themselves
7429 // intersect with the dirty area.
7430 // In particular, we really want to check ShouldDescendIntoFrame()
7431 // on all our child frames, but that might be expensive. So we
7432 // approximate it by checking it on |this|; if it's true for any
7433 // frame in our child list, it's also true for |this|.
7434 // Also skip the cursor if we're creating text overflow markers,
7435 // since we need to know what line number we're up to in order
7436 // to generate unique display item keys.
7437 // Lastly, the cursor should be skipped if we're drawing
7438 // backplates behind text. When backplating we consider consecutive
7439 // runs of text as a whole, which requires we iterate through all lines
7440 // to find our backplate size.
7442 (hasDescendantPlaceHolders
|| textOverflow
.isSome() || backplateColor
)
7444 : GetFirstLineContaining(aBuilder
->GetDirtyRect().y
);
7445 LineIterator line_end
= LinesEnd();
7447 TextOverflow
* textOverflowPtr
= textOverflow
.ptrOr(nullptr);
7450 for (LineIterator line
= mLines
.begin(cursor
); line
!= line_end
; ++line
) {
7451 const nsRect lineArea
= line
->InkOverflowRect();
7452 if (!lineArea
.IsEmpty()) {
7453 // Because we have a cursor, the combinedArea.ys are non-decreasing.
7454 // Once we've passed aDirtyRect.YMost(), we can never see it again.
7455 if (lineArea
.y
>= aBuilder
->GetDirtyRect().YMost()) {
7458 MOZ_ASSERT(textOverflow
.isNothing());
7460 if (ShouldDescendIntoLine(lineArea
)) {
7461 DisplayLine(aBuilder
, line
, line
->IsInline(), aLists
, this, nullptr,
7462 0, depth
, drawnLines
);
7467 bool nonDecreasingYs
= true;
7468 uint32_t lineCount
= 0;
7469 nscoord lastY
= INT32_MIN
;
7470 nscoord lastYMost
= INT32_MIN
;
7472 // A frame's display list cannot contain more than one copy of a
7473 // given display item unless the items are uniquely identifiable.
7474 // Because backplate occasionally requires multiple
7475 // SolidColor items, we use an index (backplateIndex) to maintain
7476 // uniqueness among them. Note this is a mapping of index to
7477 // item, and the mapping is stable even if the dirty rect changes.
7478 uint16_t backplateIndex
= 0;
7479 nsRect curBackplateArea
;
7481 auto AddBackplate
= [&]() {
7482 aLists
.BorderBackground()->AppendNewToTopWithIndex
<nsDisplaySolidColor
>(
7483 aBuilder
, this, backplateIndex
, curBackplateArea
,
7484 backplateColor
.value());
7487 for (LineIterator line
= LinesBegin(); line
!= line_end
; ++line
) {
7488 const nsRect lineArea
= line
->InkOverflowRect();
7489 const bool lineInLine
= line
->IsInline();
7491 if ((lineInLine
&& textOverflowPtr
) || ShouldDescendIntoLine(lineArea
)) {
7492 DisplayLine(aBuilder
, line
, lineInLine
, aLists
, this, textOverflowPtr
,
7493 lineCount
, depth
, drawnLines
);
7496 if (!lineInLine
&& !curBackplateArea
.IsEmpty()) {
7497 // If we have encountered a non-inline line but were previously
7498 // forming a backplate, we should add the backplate to the display
7499 // list as-is and render future backplates disjointly.
7500 MOZ_ASSERT(backplateColor
,
7501 "if this master switch is off, curBackplateArea "
7502 "must be empty and we shouldn't get here");
7505 curBackplateArea
= nsRect();
7508 if (!lineArea
.IsEmpty()) {
7509 if (lineArea
.y
< lastY
|| lineArea
.YMost() < lastYMost
) {
7510 nonDecreasingYs
= false;
7513 lastYMost
= lineArea
.YMost();
7514 if (lineInLine
&& backplateColor
&& LineHasVisibleInlineContent(line
)) {
7515 nsRect lineBackplate
= GetLineTextArea(line
, aBuilder
) +
7516 aBuilder
->ToReferenceFrame(this);
7517 if (curBackplateArea
.IsEmpty()) {
7518 curBackplateArea
= lineBackplate
;
7520 curBackplateArea
.OrWith(lineBackplate
);
7527 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
7528 SetupLineCursorForDisplay();
7531 if (!curBackplateArea
.IsEmpty()) {
7536 if (textOverflow
.isSome()) {
7537 // Put any text-overflow:ellipsis markers on top of the non-positioned
7538 // content of the block's lines. (If we ever start sorting the Content()
7539 // list this will end up in the wrong place.)
7540 aLists
.Content()->AppendToTop(&textOverflow
->GetMarkers());
7544 if (gLamePaintMetrics
) {
7545 PRTime end
= PR_Now();
7547 int32_t numLines
= mLines
.size();
7551 PRTime lines
, deltaPerLine
, delta
;
7552 lines
= int64_t(numLines
);
7553 delta
= end
- start
;
7554 deltaPerLine
= delta
/ lines
;
7559 ": %" PRId64
" elapsed (%" PRId64
7560 " per line) lines=%d drawn=%d skip=%d",
7561 delta
, deltaPerLine
, numLines
, drawnLines
,
7562 numLines
- drawnLines
);
7563 printf("%s\n", buf
);
7568 #ifdef ACCESSIBILITY
7569 a11y::AccType
nsBlockFrame::AccessibleType() {
7570 if (IsTableCaption()) {
7571 return GetRect().IsEmpty() ? a11y::eNoType
: a11y::eHTMLCaptionType
;
7574 // block frame may be for <hr>
7575 if (mContent
->IsHTMLElement(nsGkAtoms::hr
)) {
7576 return a11y::eHTMLHRType
;
7579 if (!HasMarker() || !PresContext()) {
7580 // XXXsmaug What if we're in the shadow dom?
7581 if (!mContent
->GetParent()) {
7582 // Don't create accessible objects for the root content node, they are
7583 // redundant with the nsDocAccessible object created with the document
7585 return a11y::eNoType
;
7588 if (mContent
== mContent
->OwnerDoc()->GetBody()) {
7589 // Don't create accessible objects for the body, they are redundant with
7590 // the nsDocAccessible object created with the document node
7591 return a11y::eNoType
;
7594 // Not a list item with a ::marker, treat as normal HTML container.
7595 return a11y::eHyperTextType
;
7598 // Create special list item accessible since we have a ::marker.
7599 return a11y::eHTMLLiType
;
7603 void nsBlockFrame::SetupLineCursorForDisplay() {
7604 if (mLines
.empty() || HasProperty(LineCursorPropertyDisplay())) {
7608 SetProperty(LineCursorPropertyDisplay(), mLines
.front());
7609 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7612 void nsBlockFrame::SetupLineCursorForQuery() {
7613 if (mLines
.empty() || HasProperty(LineCursorPropertyQuery())) {
7617 SetProperty(LineCursorPropertyQuery(), mLines
.front());
7618 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7621 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
) {
7622 // Although this looks like a "querying" method, it is used by the
7623 // display-list building code, so uses the Display cursor.
7624 nsLineBox
* property
= GetLineCursorForDisplay();
7628 LineIterator cursor
= mLines
.begin(property
);
7629 nsRect cursorArea
= cursor
->InkOverflowRect();
7631 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
) &&
7632 cursor
!= mLines
.front()) {
7633 cursor
= cursor
.prev();
7634 cursorArea
= cursor
->InkOverflowRect();
7636 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
) &&
7637 cursor
!= mLines
.back()) {
7638 cursor
= cursor
.next();
7639 cursorArea
= cursor
->InkOverflowRect();
7642 if (cursor
.get() != property
) {
7643 SetProperty(LineCursorPropertyDisplay(), cursor
.get());
7646 return cursor
.get();
7650 void nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
) {
7651 // See if the child is absolutely positioned
7652 if (aChild
->IsAbsolutelyPositioned()) {
7654 } else if (aChild
== GetOutsideMarker()) {
7655 // The ::marker lives in the first line, unless the first line has
7656 // height 0 and there is a second line, in which case it lives
7657 // in the second line.
7658 LineIterator markerLine
= LinesBegin();
7659 if (markerLine
!= LinesEnd() && markerLine
->BSize() == 0 &&
7660 markerLine
!= mLines
.back()) {
7661 markerLine
= markerLine
.next();
7664 if (markerLine
!= LinesEnd()) {
7665 MarkLineDirty(markerLine
, &mLines
);
7667 // otherwise we have an empty line list, and ReflowDirtyLines
7668 // will handle reflowing the ::marker.
7670 // Note that we should go through our children to mark lines dirty
7671 // before the next reflow. Doing it now could make things O(N^2)
7672 // since finding the right line is O(N).
7673 // We don't need to worry about marking lines on the overflow list
7674 // as dirty; we're guaranteed to reflow them if we take them off the
7676 // However, we might have gotten a float, in which case we need to
7677 // reflow the line containing its placeholder. So find the
7678 // ancestor-or-self of the placeholder that's a child of the block,
7679 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
7680 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7681 // We need to take some care to handle the case where a float is in
7682 // a different continuation than its placeholder, including marking
7683 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7684 if (!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
7685 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7687 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
7688 nsIFrame
* thisFC
= FirstContinuation();
7689 nsIFrame
* placeholderPath
= aChild
->GetPlaceholderFrame();
7690 // SVG code sometimes sends FrameNeedsReflow notifications during
7691 // frame destruction, leading to null placeholders, but we're safe
7693 if (placeholderPath
) {
7695 nsIFrame
* parent
= placeholderPath
->GetParent();
7696 if (parent
->GetContent() == mContent
&&
7697 parent
->FirstContinuation() == thisFC
) {
7698 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7701 placeholderPath
= parent
;
7703 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
7708 nsContainerFrame::ChildIsDirty(aChild
);
7711 void nsBlockFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
7712 nsIFrame
* aPrevInFlow
) {
7713 // These are all the block specific frame bits, they are copied from
7714 // the prev-in-flow to a newly created next-in-flow, except for the
7715 // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
7716 constexpr nsFrameState NS_BLOCK_FLAGS_MASK
=
7717 NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
|
7718 NS_BLOCK_CLIP_PAGINATED_OVERFLOW
| NS_BLOCK_HAS_FIRST_LETTER_STYLE
|
7719 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7720 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7722 // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
7723 // by default. They should only be set on the first-in-flow.
7724 constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK
=
7725 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7726 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7729 // Copy over the inherited block frame bits from the prev-in-flow.
7730 RemoveStateBits(NS_BLOCK_FLAGS_MASK
);
7731 AddStateBits(aPrevInFlow
->GetStateBits() &
7732 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
7735 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
7738 aPrevInFlow
->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
7739 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
7742 // A display:flow-root box establishes a block formatting context.
7744 // If a box has a different writing-mode value than its containing block:
7746 // If the box is a block container, then it establishes a new block
7747 // formatting context.
7748 // (https://drafts.csswg.org/css-writing-modes/#block-flow)
7750 // If the box has contain: paint or contain:layout (or contain:strict),
7751 // then it should also establish a formatting context.
7753 // Per spec, a column-span always establishes a new block formatting context.
7754 if (StyleDisplay()->mDisplay
== mozilla::StyleDisplay::FlowRoot
||
7756 (GetWritingMode().GetBlockDir() !=
7757 GetParent()->GetWritingMode().GetBlockDir() ||
7758 GetWritingMode().IsVerticalSideways() !=
7759 GetParent()->GetWritingMode().IsVerticalSideways())) ||
7760 StyleDisplay()->IsContainPaint() || StyleDisplay()->IsContainLayout() ||
7762 AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7765 if (HasAllStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) {
7766 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
7770 void nsBlockFrame::SetInitialChildList(ChildListID aListID
,
7771 nsFrameList
&& aChildList
) {
7772 if (FrameChildListID::Float
== aListID
) {
7773 mFloats
= std::move(aChildList
);
7774 } else if (FrameChildListID::Principal
== aListID
) {
7776 // The only times a block that is an anonymous box is allowed to have a
7777 // first-letter frame are when it's the block inside a non-anonymous cell,
7778 // the block inside a fieldset, button or column set, or a scrolled content
7779 // block, except for <select>. Note that this means that blocks which are
7780 // the anonymous block in {ib} splits do NOT get first-letter frames.
7781 // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7783 auto pseudo
= Style()->GetPseudoType();
7784 bool haveFirstLetterStyle
=
7785 (pseudo
== PseudoStyleType::NotPseudo
||
7786 (pseudo
== PseudoStyleType::cellContent
&&
7787 !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7788 pseudo
== PseudoStyleType::fieldsetContent
||
7789 pseudo
== PseudoStyleType::buttonContent
||
7790 pseudo
== PseudoStyleType::columnContent
||
7791 (pseudo
== PseudoStyleType::scrolledContent
&&
7792 !GetParent()->IsListControlFrame()) ||
7793 pseudo
== PseudoStyleType::mozSVGText
) &&
7794 !IsComboboxControlFrame() && !IsMathMLFrame() &&
7795 !IsColumnSetWrapperFrame() &&
7796 RefPtr
<ComputedStyle
>(GetFirstLetterStyle(PresContext())) != nullptr;
7797 NS_ASSERTION(haveFirstLetterStyle
==
7798 HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
),
7799 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7802 AddFrames(std::move(aChildList
), nullptr, nullptr);
7804 nsContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
7808 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame
* aMarkerFrame
) {
7809 MOZ_ASSERT(aMarkerFrame
);
7810 MOZ_ASSERT(!HasAnyStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
|
7811 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
),
7812 "How can we have a ::marker frame already?");
7814 if (StyleList()->mListStylePosition
== StyleListStylePosition::Inside
) {
7815 SetProperty(InsideMarkerProperty(), aMarkerFrame
);
7816 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
);
7818 if (nsBlockFrame
* marker
= do_QueryFrame(aMarkerFrame
)) {
7819 // An outside ::marker needs to be an independent formatting context
7820 // to avoid being influenced by the float manager etc.
7821 marker
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7823 SetProperty(OutsideMarkerProperty(),
7824 new (PresShell()) nsFrameList(aMarkerFrame
, aMarkerFrame
));
7825 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
7829 bool nsBlockFrame::MarkerIsEmpty() const {
7830 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
7832 "should only care when we have an outside ::marker");
7833 nsIFrame
* marker
= GetMarker();
7834 const nsStyleList
* list
= marker
->StyleList();
7835 return marker
->StyleContent()->mContent
.IsNone() ||
7836 (list
->mCounterStyle
.IsNone() && list
->mListStyleImage
.IsNone() &&
7837 marker
->StyleContent()->ContentCount() == 0);
7840 void nsBlockFrame::ReflowOutsideMarker(nsIFrame
* aMarkerFrame
,
7841 BlockReflowState
& aState
,
7842 ReflowOutput
& aMetrics
,
7844 const ReflowInput
& ri
= aState
.mReflowInput
;
7846 WritingMode markerWM
= aMarkerFrame
->GetWritingMode();
7847 LogicalSize
availSize(markerWM
);
7848 // Make up an inline-size since it doesn't really matter (XXX).
7849 availSize
.ISize(markerWM
) = aState
.ContentISize();
7850 availSize
.BSize(markerWM
) = NS_UNCONSTRAINEDSIZE
;
7852 ReflowInput
reflowInput(aState
.mPresContext
, ri
, aMarkerFrame
, availSize
,
7853 Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap
});
7854 nsReflowStatus status
;
7855 aMarkerFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowInput
, status
);
7857 // Get the float available space using our saved state from before we
7858 // started reflowing the block, so that we ignore any floats inside
7860 // FIXME: aLineTop isn't actually set correctly by some callers, since
7861 // they reposition the line.
7862 LogicalRect floatAvailSpace
=
7864 .GetFloatAvailableSpaceWithState(aLineTop
, ShapeType::ShapeOutside
,
7865 &aState
.mFloatManagerStateBefore
)
7867 // FIXME (bug 25888): need to check the entire region that the first
7868 // line overlaps, not just the top pixel.
7870 // Place the ::marker now. We want to place the ::marker relative to the
7871 // border-box of the associated block (using the right/left margin of
7872 // the ::marker frame as separation). However, if a line box would be
7873 // displaced by floats that are *outside* the associated block, we
7874 // want to displace it by the same amount. That is, we act as though
7875 // the edge of the floats is the content-edge of the block, and place
7876 // the ::marker at a position offset from there by the block's padding,
7877 // the block's border, and the ::marker frame's margin.
7879 // IStart from floatAvailSpace gives us the content/float start edge
7880 // in the current writing mode. Then we subtract out the start
7881 // border/padding and the ::marker's width and margin to offset the position.
7882 WritingMode wm
= ri
.GetWritingMode();
7883 // Get the ::marker's margin, converted to our writing mode so that we can
7884 // combine it with other logical values here.
7885 LogicalMargin markerMargin
= reflowInput
.ComputedLogicalMargin(wm
);
7886 nscoord iStart
= floatAvailSpace
.IStart(wm
) -
7887 ri
.ComputedLogicalBorderPadding(wm
).IStart(wm
) -
7888 markerMargin
.IEnd(wm
) - aMetrics
.ISize(wm
);
7890 // Approximate the ::marker's position; vertical alignment will provide
7891 // the final vertical location. We pass our writing-mode here, because
7892 // it may be different from the ::marker frame's mode.
7893 nscoord bStart
= floatAvailSpace
.BStart(wm
);
7894 aMarkerFrame
->SetRect(
7896 LogicalRect(wm
, iStart
, bStart
, aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)),
7897 aState
.ContainerSize());
7898 aMarkerFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowInput
);
7901 // This is used to scan frames for any float placeholders, add their
7902 // floats to the list represented by aList, and remove the
7903 // floats from whatever list they might be in. We don't search descendants
7904 // that are float containing blocks. Floats that or not children of 'this'
7905 // are ignored (they are not added to aList).
7906 void nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
7907 bool aCollectSiblings
) {
7909 // Don't descend into float containing blocks.
7910 if (!aFrame
->IsFloatContainingBlock()) {
7911 nsIFrame
* outOfFlowFrame
=
7912 aFrame
->IsPlaceholderFrame()
7913 ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame
)
7915 while (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
7916 RemoveFloat(outOfFlowFrame
);
7917 // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
7918 // the PushedFloats list.
7919 outOfFlowFrame
->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
7920 aList
.AppendFrame(nullptr, outOfFlowFrame
);
7921 outOfFlowFrame
= outOfFlowFrame
->GetNextInFlow();
7922 // FIXME: By not pulling floats whose parent is one of our
7923 // later siblings, are we risking the pushed floats getting
7925 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
7928 DoCollectFloats(aFrame
->PrincipalChildList().FirstChild(), aList
, true);
7930 aFrame
->GetChildList(FrameChildListID::Overflow
).FirstChild(), aList
,
7933 if (!aCollectSiblings
) {
7936 aFrame
= aFrame
->GetNextSibling();
7940 void nsBlockFrame::CheckFloats(BlockReflowState
& aState
) {
7942 // If any line is still dirty, that must mean we're going to reflow this
7943 // block again soon (e.g. because we bailed out after noticing that
7944 // clearance was imposed), so don't worry if the floats are out of sync.
7945 bool anyLineDirty
= false;
7947 // Check that the float list is what we would have built
7948 AutoTArray
<nsIFrame
*, 8> lineFloats
;
7949 for (auto& line
: Lines()) {
7950 if (line
.HasFloats()) {
7951 lineFloats
.AppendElements(line
.Floats());
7953 if (line
.IsDirty()) {
7954 anyLineDirty
= true;
7958 AutoTArray
<nsIFrame
*, 8> storedFloats
;
7960 bool hasHiddenFloats
= false;
7962 for (nsIFrame
* f
: mFloats
) {
7963 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7966 // There are chances that the float children won't be added to lines,
7967 // because in nsBlockFrame::ReflowLine, it skips reflow line if the first
7968 // child of the line is IsHiddenByContentVisibilityOfInFlowParentForLayout.
7969 // There are also chances that the floats in line are out of date, for
7970 // instance, lines could reflow if
7971 // PresShell::IsForcingLayoutForHiddenContent, and after forcingLayout is
7972 // off, the reflow of lines could be skipped, but the floats are still in
7973 // there. Here we can't know whether the floats hidden by c-v are included
7974 // in the lines or not. So we use hasHiddenFloats to skip the float length
7976 if (!hasHiddenFloats
&&
7977 f
->IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
7978 hasHiddenFloats
= true;
7980 storedFloats
.AppendElement(f
);
7981 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
7987 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) &&
7988 !anyLineDirty
&& !hasHiddenFloats
) {
7990 "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
7992 # if defined(DEBUG_roc)
7993 nsIFrame::RootFrameList(PresContext(), stdout
, 0);
7994 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
7995 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
7997 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
7998 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
8004 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
8005 if (oofs
&& oofs
->NotEmpty()) {
8006 // Floats that were pushed should be removed from our float
8007 // manager. Otherwise the float manager's YMost or XMost might
8008 // be larger than necessary, causing this block to get an
8009 // incorrect desired height (or width). Some of these floats
8010 // may not actually have been added to the float manager because
8011 // they weren't reflowed before being pushed; that's OK,
8012 // RemoveRegions will ignore them. It is safe to do this here
8013 // because we know from here on the float manager will only be
8014 // used for its XMost and YMost, not to place new floats and
8016 aState
.FloatManager()->RemoveTrailingRegions(oofs
->FirstChild());
8020 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot
,
8021 bool* aBEndMarginRoot
) {
8022 nsIFrame
* parent
= GetParent();
8023 if (!HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
8024 if (!parent
|| parent
->IsFloatContainingBlock()) {
8025 *aBStartMarginRoot
= false;
8026 *aBEndMarginRoot
= false;
8031 if (parent
&& parent
->IsColumnSetFrame()) {
8032 // The first column is a start margin root and the last column is an end
8033 // margin root. (If the column-set is split by a column-span:all box then
8034 // the first and last column in each column-set fragment are margin roots.)
8035 *aBStartMarginRoot
= GetPrevInFlow() == nullptr;
8036 *aBEndMarginRoot
= GetNextInFlow() == nullptr;
8040 *aBStartMarginRoot
= true;
8041 *aBEndMarginRoot
= true;
8045 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
) {
8046 MOZ_ASSERT(aBlock
, "Must have a frame");
8047 NS_ASSERTION(aBlock
->IsBlockFrameOrSubclass(), "aBlock must be a block");
8049 nsIFrame
* parent
= aBlock
->GetParent();
8050 return aBlock
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
) ||
8051 (parent
&& !parent
->IsFloatContainingBlock());
8055 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
) {
8056 return aFrame
->IsBlockFrameOrSubclass() && !aFrame
->IsReplaced() &&
8057 !aFrame
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
);
8060 // Note that this width can vary based on the vertical position.
8061 // However, the cases where it varies are the cases where the width fits
8062 // in the available space given, which means that variation shouldn't
8065 nsBlockFrame::FloatAvoidingISizeToClear
nsBlockFrame::ISizeToClearPastFloats(
8066 const BlockReflowState
& aState
, const LogicalRect
& aFloatAvailableSpace
,
8067 nsIFrame
* aFloatAvoidingBlock
) {
8068 nscoord inlineStartOffset
, inlineEndOffset
;
8069 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8071 FloatAvoidingISizeToClear result
;
8072 aState
.ComputeFloatAvoidingOffsets(aFloatAvoidingBlock
, aFloatAvailableSpace
,
8073 inlineStartOffset
, inlineEndOffset
);
8074 nscoord availISize
=
8075 aState
.mContentArea
.ISize(wm
) - inlineStartOffset
- inlineEndOffset
;
8077 // We actually don't want the min width here; see bug 427782; we only
8078 // want to displace if the width won't compute to a value small enough
8080 // All we really need here is the result of ComputeSize, and we
8081 // could *almost* get that from an SizeComputationInput, except for the
8083 WritingMode frWM
= aFloatAvoidingBlock
->GetWritingMode();
8084 LogicalSize availSpace
=
8085 LogicalSize(wm
, availISize
, NS_UNCONSTRAINEDSIZE
).ConvertTo(frWM
, wm
);
8086 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
,
8087 aFloatAvoidingBlock
, availSpace
);
8088 result
.borderBoxISize
=
8089 reflowInput
.ComputedSizeWithBorderPadding(wm
).ISize(wm
);
8091 // Use the margins from sizingInput rather than reflowInput so that
8092 // they aren't reduced by ignoring margins in overconstrained cases.
8093 SizeComputationInput
sizingInput(aFloatAvoidingBlock
,
8094 aState
.mReflowInput
.mRenderingContext
, wm
,
8095 aState
.mContentArea
.ISize(wm
));
8096 const LogicalMargin computedMargin
= sizingInput
.ComputedLogicalMargin(wm
);
8098 nscoord marginISize
= computedMargin
.IStartEnd(wm
);
8099 const auto& iSize
= reflowInput
.mStylePosition
->ISize(wm
);
8100 if (marginISize
< 0 && (iSize
.IsAuto() || iSize
.IsMozAvailable())) {
8101 // If we get here, floatAvoidingBlock has a negative amount of inline-axis
8102 // margin and an 'auto' (or ~equivalently, -moz-available) inline
8103 // size. Under these circumstances, we use the margin to establish a
8104 // (positive) minimum size for the border-box, in order to satisfy the
8105 // equation in CSS2 10.3.3. That equation essentially simplifies to the
8108 // iSize of margins + iSize of borderBox = iSize of containingBlock
8110 // ...where "iSize of borderBox" is the sum of floatAvoidingBlock's
8111 // inline-axis components of border, padding, and {width,height}.
8113 // Right now, in the above equation, "iSize of margins" is the only term
8114 // that we know for sure. (And we also know that it's negative, since we
8115 // got here.) The other terms are as-yet unresolved, since the frame has an
8116 // 'auto' iSize, and since we aren't yet sure if we'll clear this frame
8117 // beyond floats or place it alongside them.
8119 // However: we *do* know that the equation's "iSize of containingBlock"
8120 // term *must* be non-negative, since boxes' widths and heights generally
8121 // can't be negative in CSS. To satisfy that requirement, we can then
8122 // infer that the equation's "iSize of borderBox" term *must* be large
8123 // enough to cancel out the (known-to-be-negative) "iSize of margins"
8124 // term. Therefore, marginISize value (negated to make it positive)
8125 // establishes a lower-bound for how much inline-axis space our border-box
8126 // will really require in order to fit alongside any floats.
8128 // XXXdholbert This explanation is admittedly a bit hand-wavy and may not
8129 // precisely match what any particular spec requires. It's the best
8130 // reasoning I could come up with to explain engines' behavior. Also, our
8131 // behavior with -moz-available doesn't seem particularly correct here, per
8132 // bug 1767217, though that's probably due to a bug elsewhere in our float
8134 result
.borderBoxISize
= std::max(result
.borderBoxISize
, -marginISize
);
8137 result
.marginIStart
= computedMargin
.IStart(wm
);
8142 nsBlockFrame
* nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
) {
8143 nsBlockFrame
* block
= nullptr;
8144 while (aCandidate
) {
8145 block
= do_QueryFrame(aCandidate
);
8147 // yay, candidate is a block!
8150 // Not a block. Check its parent next.
8151 aCandidate
= aCandidate
->GetParent();
8153 MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
8157 nscoord
nsBlockFrame::ComputeFinalBSize(BlockReflowState
& aState
,
8158 nscoord aBEndEdgeOfChildren
) {
8159 const WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8161 const nscoord effectiveContentBoxBSize
=
8162 GetEffectiveComputedBSize(aState
.mReflowInput
, aState
.mConsumedBSize
);
8163 const nscoord blockStartBP
= aState
.BorderPadding().BStart(wm
);
8164 const nscoord blockEndBP
= aState
.BorderPadding().BEnd(wm
);
8167 !IsTrueOverflowContainer() || (effectiveContentBoxBSize
== 0 &&
8168 blockStartBP
== 0 && blockEndBP
== 0),
8169 "An overflow container's effective content-box block-size, block-start "
8170 "BP, and block-end BP should all be zero!");
8172 const nscoord effectiveContentBoxBSizeWithBStartBP
=
8173 NSCoordSaturatingAdd(blockStartBP
, effectiveContentBoxBSize
);
8174 const nscoord effectiveBorderBoxBSize
=
8175 NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP
, blockEndBP
);
8177 if (HasColumnSpanSiblings()) {
8178 MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
8179 "Frame constructor should've created column-span siblings!");
8181 // If a block is split by any column-spans, we calculate the final
8182 // block-size by shrinkwrapping our children's block-size for all the
8183 // fragments except for those after the final column-span, but we should
8184 // take no more than our effective border-box block-size. If there's any
8185 // leftover block-size, our next continuations will take up rest.
8187 // We don't need to adjust aBri.mReflowStatus because our children's status
8188 // is the same as ours.
8189 return std::min(effectiveBorderBoxBSize
, aBEndEdgeOfChildren
);
8192 const nscoord availBSize
= aState
.mReflowInput
.AvailableBSize();
8193 if (availBSize
== NS_UNCONSTRAINEDSIZE
) {
8194 return effectiveBorderBoxBSize
;
8197 // Save our children's reflow status.
8198 const bool isChildStatusComplete
= aState
.mReflowStatus
.IsComplete();
8199 if (isChildStatusComplete
&& effectiveContentBoxBSize
> 0 &&
8200 effectiveBorderBoxBSize
> availBSize
&&
8201 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
8202 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
8203 return effectiveBorderBoxBSize
;
8206 const bool isBDBClone
=
8207 aState
.mReflowInput
.mStyleBorder
->mBoxDecorationBreak
==
8208 StyleBoxDecorationBreak::Clone
;
8210 // The maximum value our content-box block-size can take within the given
8211 // available block-size.
8212 const nscoord maxContentBoxBSize
= aState
.ContentBSize();
8214 // The block-end edge of our content-box (relative to this frame's origin) if
8215 // we consumed the maximum block-size available to us (maxContentBoxBSize).
8216 const nscoord maxContentBoxBEnd
= aState
.ContentBEnd();
8218 // These variables are uninitialized intentionally so that the compiler can
8219 // check they are assigned in every if-else branch below.
8220 nscoord finalContentBoxBSizeWithBStartBP
;
8221 bool isOurStatusComplete
;
8223 if (effectiveBorderBoxBSize
<= availBSize
) {
8224 // Our effective border-box block-size can fit in the available block-size,
8225 // so we are complete.
8226 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8227 isOurStatusComplete
= true;
8228 } else if (effectiveContentBoxBSizeWithBStartBP
<= maxContentBoxBEnd
) {
8229 // Note: The following assertion should generally hold because, for
8230 // box-decoration-break:clone, this "else if" branch is mathematically
8231 // equivalent to the initial "if".
8232 NS_ASSERTION(!isBDBClone
,
8233 "This else-if branch is handling a situation that's specific "
8234 "to box-decoration-break:slice, i.e. a case when we can skip "
8235 "our block-end border and padding!");
8237 // Our effective content-box block-size plus the block-start border and
8238 // padding can fit in the available block-size, but it cannot fit after
8239 // adding the block-end border and padding. Thus, we need a continuation
8240 // (unless we already weren't asking for any block-size, in which case we
8241 // stay complete to avoid looping forever).
8242 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8243 isOurStatusComplete
= effectiveContentBoxBSize
== 0;
8245 // We aren't going to be able to fit our content-box in the space available
8246 // to it, which means we'll probably call ourselves incomplete to request a
8247 // continuation. But before making that decision, we check for certain
8248 // conditions which would force us to overflow beyond the available space --
8249 // these might result in us actually being complete if we're forced to
8250 // overflow far enough.
8251 if (MOZ_UNLIKELY(aState
.mReflowInput
.mFlags
.mIsTopOfPage
&& isBDBClone
&&
8252 maxContentBoxBSize
<= 0 &&
8253 aBEndEdgeOfChildren
== blockStartBP
)) {
8254 // In this rare case, we are at the top of page/column, we have
8255 // box-decoration-break:clone and zero available block-size for our
8256 // content-box (e.g. our own block-start border and padding already exceed
8257 // the available block-size), and we didn't lay out any child to consume
8258 // our content-box block-size. To ensure we make progress (avoid looping
8259 // forever), use 1px as our content-box block-size regardless of our
8260 // effective content-box block-size, in the spirit of
8261 // https://drafts.csswg.org/css-break/#breaking-rules.
8262 finalContentBoxBSizeWithBStartBP
= blockStartBP
+ AppUnitsPerCSSPixel();
8263 isOurStatusComplete
= effectiveContentBoxBSize
<= AppUnitsPerCSSPixel();
8264 } else if (aBEndEdgeOfChildren
> maxContentBoxBEnd
) {
8265 // We have a unbreakable child whose block-end edge exceeds the available
8266 // block-size for children.
8267 if (aBEndEdgeOfChildren
>= effectiveContentBoxBSizeWithBStartBP
) {
8268 // The unbreakable child's block-end edge forces us to consume all of
8269 // our effective content-box block-size.
8270 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8272 // Even though we've consumed all of our effective content-box
8273 // block-size, we may still need to report an incomplete status in order
8274 // to get another continuation, which will be responsible for laying out
8275 // & drawing our block-end border & padding. But if we have no such
8276 // border & padding, or if we're forced to apply that border & padding
8277 // on this frame due to box-decoration-break:clone, then we don't need
8278 // to bother with that additional continuation.
8279 isOurStatusComplete
= (isBDBClone
|| blockEndBP
== 0);
8281 // The unbreakable child's block-end edge doesn't force us to consume
8282 // all of our effective content-box block-size.
8283 finalContentBoxBSizeWithBStartBP
= aBEndEdgeOfChildren
;
8284 isOurStatusComplete
= false;
8287 // The children's block-end edge can fit in the content-box space that we
8288 // have available for it. Consume all the space that is available so that
8289 // our inline-start/inline-end borders extend all the way to the block-end
8290 // edge of column/page.
8291 finalContentBoxBSizeWithBStartBP
= maxContentBoxBEnd
;
8292 isOurStatusComplete
= false;
8296 nscoord finalBorderBoxBSize
= finalContentBoxBSizeWithBStartBP
;
8297 if (isOurStatusComplete
) {
8298 finalBorderBoxBSize
= NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8299 if (isChildStatusComplete
) {
8300 // We want to use children's reflow status as ours, which can be overflow
8301 // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
8303 aState
.mReflowStatus
.SetOverflowIncomplete();
8306 NS_ASSERTION(!IsTrueOverflowContainer(),
8307 "An overflow container should always be complete because of "
8308 "its zero border-box block-size!");
8310 finalBorderBoxBSize
=
8311 NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8313 aState
.mReflowStatus
.SetIncomplete();
8314 if (!GetNextInFlow()) {
8315 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
8319 return finalBorderBoxBSize
;
8322 nsresult
nsBlockFrame::ResolveBidi() {
8323 NS_ASSERTION(!GetPrevInFlow(),
8324 "ResolveBidi called on non-first continuation");
8325 MOZ_ASSERT(PresContext()->BidiEnabled());
8326 return nsBidiPresUtils::Resolve(this);
8329 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState
& aRestyleState
) {
8330 // first-letter needs to be updated before first-line, because first-line can
8331 // change the style of the first-letter.
8332 if (HasFirstLetterChild()) {
8333 UpdateFirstLetterStyle(aRestyleState
);
8336 if (nsIFrame
* firstLineFrame
= GetFirstLineFrame()) {
8337 nsIFrame
* styleParent
= CorrectStyleParentFrame(firstLineFrame
->GetParent(),
8338 PseudoStyleType::firstLine
);
8340 ComputedStyle
* parentStyle
= styleParent
->Style();
8341 RefPtr
<ComputedStyle
> firstLineStyle
=
8342 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
8343 *mContent
->AsElement(), PseudoStyleType::firstLine
, nullptr,
8346 // FIXME(bz): Can we make first-line continuations be non-inheriting anon
8348 RefPtr
<ComputedStyle
> continuationStyle
=
8349 aRestyleState
.StyleSet().ResolveInheritingAnonymousBoxStyle(
8350 PseudoStyleType::mozLineFrame
, parentStyle
);
8352 UpdateStyleOfOwnedChildFrame(firstLineFrame
, firstLineStyle
, aRestyleState
,
8353 Some(continuationStyle
.get()));
8355 // We also want to update the styles of the first-line's descendants. We
8356 // don't need to compute a changehint for this, though, since any changes to
8357 // them are handled by the first-line anyway.
8358 RestyleManager
* manager
= PresContext()->RestyleManager();
8359 for (nsIFrame
* kid
: firstLineFrame
->PrincipalChildList()) {
8360 manager
->ReparentComputedStyleForFirstLine(kid
);
8365 nsIFrame
* nsBlockFrame::GetFirstLetter() const {
8366 if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
8367 // Certainly no first-letter frame.
8371 return GetProperty(FirstLetterProperty());
8374 nsIFrame
* nsBlockFrame::GetFirstLineFrame() const {
8375 nsIFrame
* maybeFirstLine
= PrincipalChildList().FirstChild();
8376 if (maybeFirstLine
&& maybeFirstLine
->IsLineFrame()) {
8377 return maybeFirstLine
;
8384 void nsBlockFrame::VerifyLines(bool aFinalCheckOK
) {
8385 if (!gVerifyLines
) {
8388 if (mLines
.empty()) {
8392 nsLineBox
* cursor
= GetLineCursorForQuery();
8394 // Add up the counts on each line. Also validate that IsFirstLine is
8397 for (const auto& line
: Lines()) {
8398 if (&line
== cursor
) {
8401 if (aFinalCheckOK
) {
8402 MOZ_ASSERT(line
.GetChildCount(), "empty line");
8403 if (line
.IsBlock()) {
8404 NS_ASSERTION(1 == line
.GetChildCount(), "bad first line");
8407 count
+= line
.GetChildCount();
8410 // Then count the frames
8411 int32_t frameCount
= 0;
8412 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
8415 frame
= frame
->GetNextSibling();
8417 NS_ASSERTION(count
== frameCount
, "bad line list");
8419 // Next: test that each line has right number of frames on it
8420 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
8421 line
!= line_end
;) {
8422 count
= line
->GetChildCount();
8423 frame
= line
->mFirstChild
;
8424 while (--count
>= 0) {
8425 frame
= frame
->GetNextSibling();
8428 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
8429 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
8434 FrameLines
* overflowLines
= GetOverflowLines();
8435 if (overflowLines
) {
8436 LineIterator line
= overflowLines
->mLines
.begin();
8437 LineIterator line_end
= overflowLines
->mLines
.end();
8438 for (; line
!= line_end
; ++line
) {
8439 if (line
== cursor
) {
8446 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
8449 void nsBlockFrame::VerifyOverflowSituation() {
8450 // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
8451 nsFrameList
* oofs
= GetOverflowOutOfFlows();
8453 for (nsIFrame
* f
: *oofs
) {
8454 nsIFrame
* nif
= f
->GetNextInFlow();
8456 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8460 // Pushed floats must not have a next-in-flow in mFloats or mFrames.
8461 oofs
= GetPushedFloats();
8463 for (nsIFrame
* f
: *oofs
) {
8464 nsIFrame
* nif
= f
->GetNextInFlow();
8466 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8470 // A child float next-in-flow's parent must be |this| or a next-in-flow of
8471 // |this|. Later next-in-flows must have the same or later parents.
8472 ChildListID childLists
[] = {FrameChildListID::Float
,
8473 FrameChildListID::PushedFloats
};
8474 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
8475 const nsFrameList
& children
= GetChildList(childLists
[i
]);
8476 for (nsIFrame
* f
: children
) {
8477 nsIFrame
* parent
= this;
8478 nsIFrame
* nif
= f
->GetNextInFlow();
8479 for (; nif
; nif
= nif
->GetNextInFlow()) {
8481 for (nsIFrame
* p
= parent
; p
; p
= p
->GetNextInFlow()) {
8482 if (nif
->GetParent() == p
) {
8490 "next-in-flow is a child of parent earlier in the frame tree?");
8495 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
8497 FrameLines
* overflowLines
= flow
->GetOverflowLines();
8498 if (overflowLines
) {
8499 NS_ASSERTION(!overflowLines
->mLines
.empty(),
8500 "should not be empty if present");
8501 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
8502 "bad overflow lines");
8503 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
8504 overflowLines
->mFrames
.FirstChild(),
8505 "bad overflow frames / lines");
8507 auto checkCursor
= [&](nsLineBox
* cursor
) -> bool {
8511 LineIterator line
= flow
->LinesBegin();
8512 LineIterator line_end
= flow
->LinesEnd();
8513 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8515 if (line
== line_end
&& overflowLines
) {
8516 line
= overflowLines
->mLines
.begin();
8517 line_end
= overflowLines
->mLines
.end();
8518 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8521 return line
!= line_end
;
8523 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForDisplay()),
8524 "stale LineCursorPropertyDisplay");
8525 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForQuery()),
8526 "stale LineCursorPropertyQuery");
8527 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
8531 int32_t nsBlockFrame::GetDepth() const {
8533 nsIFrame
* parent
= GetParent();
8535 parent
= parent
->GetParent();
8541 already_AddRefed
<ComputedStyle
> nsBlockFrame::GetFirstLetterStyle(
8542 nsPresContext
* aPresContext
) {
8543 return aPresContext
->StyleSet()->ProbePseudoElementStyle(
8544 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr, Style());