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
->IsFrameOfType(nsIFrame::eLineParticipant
)) {
144 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
145 if (kid
->StyleVisibility()->IsVisible() ||
146 FrameHasVisibleInlineContent(kid
)) {
155 * Determines whether any of the frames descended from the
156 * given line have inline content with 'visibility: visible'.
157 * This function calls FrameHasVisibleInlineContent to process
158 * each frame in the line's child list.
160 static bool LineHasVisibleInlineContent(nsLineBox
* aLine
) {
161 nsIFrame
* kid
= aLine
->mFirstChild
;
162 int32_t n
= aLine
->GetChildCount();
164 if (FrameHasVisibleInlineContent(kid
)) {
168 kid
= kid
->GetNextSibling();
175 * Iterates through the frame's in-flow children and
176 * unions the ink overflow of all text frames which
177 * participate in the line aFrame belongs to.
178 * If a child of aFrame is not a text frame,
179 * we recurse with the child as the aFrame argument.
180 * If aFrame isn't a line participant, we skip it entirely
181 * and return an empty rect.
182 * The resulting nsRect is offset relative to the parent of aFrame.
184 static nsRect
GetFrameTextArea(nsIFrame
* aFrame
,
185 nsDisplayListBuilder
* aBuilder
) {
187 if (const nsTextFrame
* textFrame
= do_QueryFrame(aFrame
)) {
188 if (!textFrame
->IsEntirelyWhitespace()) {
189 textArea
= aFrame
->InkOverflowRect();
191 } else if (aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
)) {
192 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
193 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
194 textArea
.OrWith(kidTextArea
);
197 // add aFrame's position to keep textArea relative to aFrame's parent
198 return textArea
+ aFrame
->GetPosition();
202 * Iterates through the line's children and
203 * unions the ink overflow of all text frames.
204 * GetFrameTextArea unions and returns the ink overflow
205 * from all line-participating text frames within the given child.
206 * The nsRect returned from GetLineTextArea is offset
207 * relative to the given line.
209 static nsRect
GetLineTextArea(nsLineBox
* aLine
,
210 nsDisplayListBuilder
* aBuilder
) {
212 nsIFrame
* kid
= aLine
->mFirstChild
;
213 int32_t n
= aLine
->GetChildCount();
215 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
216 textArea
.OrWith(kidTextArea
);
217 kid
= kid
->GetNextSibling();
224 * Starting with aFrame, iterates upward through parent frames and checks for
225 * non-transparent background colors. If one is found, we use that as our
226 * backplate color. Otheriwse, we use the default background color from
227 * our high contrast theme.
229 static nscolor
GetBackplateColor(nsIFrame
* aFrame
) {
230 nsPresContext
* pc
= aFrame
->PresContext();
231 nscolor currentBackgroundColor
= NS_TRANSPARENT
;
232 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent()) {
233 // NOTE(emilio): We assume themed frames (frame->IsThemed()) have correct
234 // background-color information so as to compute the right backplate color.
236 // This holds because HTML widgets with author-specified backgrounds or
237 // borders disable theming. So as long as the UA-specified background colors
238 // match the actual theme (which they should because we always use system
239 // colors with the non-native theme, and native system colors should also
240 // match the native theme), then we're alright and we should compute an
241 // appropriate backplate color.
242 const auto* style
= frame
->Style();
243 if (style
->StyleBackground()->IsTransparent(style
)) {
246 bool drawImage
= false, drawColor
= false;
247 nscolor backgroundColor
= nsCSSRendering::DetermineBackgroundColor(
248 pc
, style
, frame
, drawImage
, drawColor
);
249 if (!drawColor
&& !drawImage
) {
252 if (NS_GET_A(backgroundColor
) == 0) {
253 // Even if there's a background image, if there's no background color we
254 // keep going up the frame tree, see bug 1723938.
257 if (NS_GET_A(currentBackgroundColor
) == 0) {
258 // Try to avoid somewhat expensive math in the common case.
259 currentBackgroundColor
= backgroundColor
;
261 currentBackgroundColor
=
262 NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
264 if (NS_GET_A(currentBackgroundColor
) == 0xff) {
265 // If fully opaque, we're done, otherwise keep going up blending with our
267 return currentBackgroundColor
;
270 nscolor backgroundColor
= aFrame
->PresContext()->DefaultBackgroundColor();
271 if (NS_GET_A(currentBackgroundColor
) == 0) {
272 return backgroundColor
;
274 return NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
278 # include "nsBlockDebugFlags.h"
280 bool nsBlockFrame::gLamePaintMetrics
;
281 bool nsBlockFrame::gLameReflowMetrics
;
282 bool nsBlockFrame::gNoisy
;
283 bool nsBlockFrame::gNoisyDamageRepair
;
284 bool nsBlockFrame::gNoisyIntrinsic
;
285 bool nsBlockFrame::gNoisyReflow
;
286 bool nsBlockFrame::gReallyNoisyReflow
;
287 bool nsBlockFrame::gNoisyFloatManager
;
288 bool nsBlockFrame::gVerifyLines
;
289 bool nsBlockFrame::gDisableResizeOpt
;
291 int32_t nsBlockFrame::gNoiseIndent
;
293 struct BlockDebugFlags
{
298 static const BlockDebugFlags gFlags
[] = {
299 {"reflow", &nsBlockFrame::gNoisyReflow
},
300 {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow
},
301 {"intrinsic", &nsBlockFrame::gNoisyIntrinsic
},
302 {"float-manager", &nsBlockFrame::gNoisyFloatManager
},
303 {"verify-lines", &nsBlockFrame::gVerifyLines
},
304 {"damage-repair", &nsBlockFrame::gNoisyDamageRepair
},
305 {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics
},
306 {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics
},
307 {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt
},
309 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
311 static void ShowDebugFlags() {
312 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
313 const BlockDebugFlags
* bdf
= gFlags
;
314 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
315 for (; bdf
< end
; bdf
++) {
316 printf(" %s\n", bdf
->name
);
318 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
319 printf("names (no whitespace)\n");
322 void nsBlockFrame::InitDebugFlags() {
323 static bool firstTime
= true;
326 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
330 char* cm
= strchr(flags
, ',');
336 const BlockDebugFlags
* bdf
= gFlags
;
337 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
338 for (; bdf
< end
; bdf
++) {
339 if (nsCRT::strcasecmp(bdf
->name
, flags
) == 0) {
341 printf("nsBlockFrame: setting %s debug flag on\n", bdf
->name
);
366 //----------------------------------------------------------------------
368 // Debugging support code
371 const char* nsBlockFrame::kReflowCommandType
[] = {
372 "ContentChanged", "StyleChanged", "ReflowDirty", "Timeout", "UserDefined",
375 const char* nsBlockFrame::LineReflowStatusToString(
376 LineReflowStatus aLineReflowStatus
) const {
377 switch (aLineReflowStatus
) {
378 case LineReflowStatus::OK
:
379 return "LINE_REFLOW_OK";
380 case LineReflowStatus::Stop
:
381 return "LINE_REFLOW_STOP";
382 case LineReflowStatus::RedoNoPull
:
383 return "LINE_REFLOW_REDO_NO_PULL";
384 case LineReflowStatus::RedoMoreFloats
:
385 return "LINE_REFLOW_REDO_MORE_FLOATS";
386 case LineReflowStatus::RedoNextBand
:
387 return "LINE_REFLOW_REDO_NEXT_BAND";
388 case LineReflowStatus::Truncated
:
389 return "LINE_REFLOW_TRUNCATED";
396 #ifdef REFLOW_STATUS_COVERAGE
397 static void RecordReflowStatus(bool aChildIsBlock
,
398 const nsReflowStatus
& aFrameReflowStatus
) {
399 static uint32_t record
[2];
402 // 1: child-is-inline
404 if (!aChildIsBlock
) {
408 // Compute new status
409 uint32_t newS
= record
[index
];
410 if (aFrameReflowStatus
.IsInlineBreak()) {
411 if (aFrameReflowStatus
.IsInlineBreakBefore()) {
413 } else if (aFrameReflowStatus
.IsIncomplete()) {
418 } else if (aFrameReflowStatus
.IsIncomplete()) {
424 // Log updates to the status that yield different values
425 if (record
[index
] != newS
) {
426 record
[index
] = newS
;
427 printf("record(%d): %02x %02x\n", index
, record
[0], record
[1]);
432 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty
,
433 nsBlockFrame::FrameLines
)
434 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty
)
435 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty
)
436 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty
)
437 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty
, nsIFrame
)
438 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty
, nscoord
)
440 //----------------------------------------------------------------------
442 nsBlockFrame
* NS_NewBlockFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
443 return new (aPresShell
) nsBlockFrame(aStyle
, aPresShell
->GetPresContext());
446 nsBlockFrame
* NS_NewBlockFormattingContext(PresShell
* aPresShell
,
447 ComputedStyle
* aComputedStyle
) {
448 nsBlockFrame
* blockFrame
= NS_NewBlockFrame(aPresShell
, aComputedStyle
);
449 blockFrame
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
453 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame
)
455 nsBlockFrame::~nsBlockFrame() = default;
457 void nsBlockFrame::AddSizeOfExcludingThisForTree(
458 nsWindowSizes
& aWindowSizes
) const {
459 nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes
);
461 // Add the size of any nsLineBox::mFrames hashtables we might have:
462 for (const auto& line
: Lines()) {
463 line
.AddSizeOfExcludingThis(aWindowSizes
);
465 const FrameLines
* overflowLines
= GetOverflowLines();
467 ConstLineIterator line
= overflowLines
->mLines
.begin(),
468 line_end
= overflowLines
->mLines
.end();
469 for (; line
!= line_end
; ++line
) {
470 line
->AddSizeOfExcludingThis(aWindowSizes
);
475 void nsBlockFrame::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
);
808 nscoord
nsBlockFrame::GetMinISize(gfxContext
* aRenderingContext
) {
809 nsIFrame
* firstInFlow
= FirstContinuation();
810 if (firstInFlow
!= this) {
811 return firstInFlow
->GetMinISize(aRenderingContext
);
814 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
816 CheckIntrinsicCacheAgainstShrinkWrapState();
818 if (mCachedMinISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
819 return mCachedMinISize
;
822 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
823 mCachedMinISize
= *containISize
;
824 return mCachedMinISize
;
828 if (gNoisyIntrinsic
) {
829 IndentBy(stdout
, gNoiseIndent
);
831 printf(": GetMinISize\n");
833 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
836 for (nsBlockFrame
* curFrame
= this; curFrame
;
837 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
838 curFrame
->LazyMarkLinesDirty();
841 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
842 PresContext()->BidiEnabled()) {
846 const bool whiteSpaceCanWrap
= StyleText()->WhiteSpaceCanWrapStyle();
847 InlineMinISizeData data
;
848 for (nsBlockFrame
* curFrame
= this; curFrame
;
849 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
850 for (LineIterator line
= curFrame
->LinesBegin(),
851 line_end
= curFrame
->LinesEnd();
852 line
!= line_end
; ++line
) {
854 if (gNoisyIntrinsic
) {
855 IndentBy(stdout
, gNoiseIndent
);
856 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
857 line
->IsEmpty() ? ", empty" : "");
859 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
861 if (line
->IsBlock()) {
863 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
864 aRenderingContext
, line
->mFirstChild
, IntrinsicISizeType::MinISize
);
867 if (!curFrame
->GetPrevContinuation() &&
868 line
== curFrame
->LinesBegin()) {
869 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
872 data
.SetLineContainer(curFrame
);
873 nsIFrame
* kid
= line
->mFirstChild
;
874 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
875 ++i
, kid
= kid
->GetNextSibling()) {
876 kid
->AddInlineMinISize(aRenderingContext
, &data
);
877 if (whiteSpaceCanWrap
&& data
.mTrailingWhitespace
) {
878 data
.OptionallyBreak();
883 if (gNoisyIntrinsic
) {
884 IndentBy(stdout
, gNoiseIndent
);
885 printf("min: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
893 mCachedMinISize
= data
.mPrevLines
;
894 return mCachedMinISize
;
898 nscoord
nsBlockFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
899 nsIFrame
* firstInFlow
= FirstContinuation();
900 if (firstInFlow
!= this) {
901 return firstInFlow
->GetPrefISize(aRenderingContext
);
904 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
906 CheckIntrinsicCacheAgainstShrinkWrapState();
908 if (mCachedPrefISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
909 return mCachedPrefISize
;
912 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
913 mCachedPrefISize
= *containISize
;
914 return mCachedPrefISize
;
918 if (gNoisyIntrinsic
) {
919 IndentBy(stdout
, gNoiseIndent
);
921 printf(": GetPrefISize\n");
923 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
926 for (nsBlockFrame
* curFrame
= this; curFrame
;
927 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
928 curFrame
->LazyMarkLinesDirty();
931 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
932 PresContext()->BidiEnabled()) {
935 InlinePrefISizeData data
;
936 for (nsBlockFrame
* curFrame
= this; curFrame
;
937 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
938 for (LineIterator line
= curFrame
->LinesBegin(),
939 line_end
= curFrame
->LinesEnd();
940 line
!= line_end
; ++line
) {
942 if (gNoisyIntrinsic
) {
943 IndentBy(stdout
, gNoiseIndent
);
944 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
945 line
->IsEmpty() ? ", empty" : "");
947 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
949 if (line
->IsBlock()) {
950 StyleClear clearType
;
951 if (!data
.mLineIsEmpty
|| BlockCanIntersectFloats(line
->mFirstChild
)) {
952 clearType
= StyleClear::Both
;
954 clearType
= line
->mFirstChild
->StyleDisplay()->mClear
;
956 data
.ForceBreak(clearType
);
957 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
958 aRenderingContext
, line
->mFirstChild
,
959 IntrinsicISizeType::PrefISize
);
962 if (!curFrame
->GetPrevContinuation() &&
963 line
== curFrame
->LinesBegin()) {
964 nscoord indent
= StyleText()->mTextIndent
.Resolve(0);
965 data
.mCurrentLine
+= indent
;
966 // XXXmats should the test below be indent > 0?
967 if (indent
!= nscoord(0)) {
968 data
.mLineIsEmpty
= false;
972 data
.SetLineContainer(curFrame
);
973 nsIFrame
* kid
= line
->mFirstChild
;
974 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
975 ++i
, kid
= kid
->GetNextSibling()) {
976 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
980 if (gNoisyIntrinsic
) {
981 IndentBy(stdout
, gNoiseIndent
);
982 printf("pref: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
990 mCachedPrefISize
= data
.mPrevLines
;
991 return mCachedPrefISize
;
994 nsRect
nsBlockFrame::ComputeTightBounds(DrawTarget
* aDrawTarget
) const {
996 if (Style()->HasTextDecorationLines()) {
997 return InkOverflowRect();
999 return ComputeSimpleTightBounds(aDrawTarget
);
1003 nsresult
nsBlockFrame::GetPrefWidthTightBounds(gfxContext
* aRenderingContext
,
1004 nscoord
* aX
, nscoord
* aXMost
) {
1005 nsIFrame
* firstInFlow
= FirstContinuation();
1006 if (firstInFlow
!= this) {
1007 return firstInFlow
->GetPrefWidthTightBounds(aRenderingContext
, aX
, aXMost
);
1014 InlinePrefISizeData data
;
1015 for (nsBlockFrame
* curFrame
= this; curFrame
;
1016 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
1017 for (LineIterator line
= curFrame
->LinesBegin(),
1018 line_end
= curFrame
->LinesEnd();
1019 line
!= line_end
; ++line
) {
1020 nscoord childX
, childXMost
;
1021 if (line
->IsBlock()) {
1023 rv
= line
->mFirstChild
->GetPrefWidthTightBounds(aRenderingContext
,
1024 &childX
, &childXMost
);
1025 NS_ENSURE_SUCCESS(rv
, rv
);
1026 *aX
= std::min(*aX
, childX
);
1027 *aXMost
= std::max(*aXMost
, childXMost
);
1029 if (!curFrame
->GetPrevContinuation() &&
1030 line
== curFrame
->LinesBegin()) {
1031 data
.mCurrentLine
+= StyleText()->mTextIndent
.Resolve(0);
1034 data
.SetLineContainer(curFrame
);
1035 nsIFrame
* kid
= line
->mFirstChild
;
1036 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
1037 ++i
, kid
= kid
->GetNextSibling()) {
1038 rv
= kid
->GetPrefWidthTightBounds(aRenderingContext
, &childX
,
1040 NS_ENSURE_SUCCESS(rv
, rv
);
1041 *aX
= std::min(*aX
, data
.mCurrentLine
+ childX
);
1042 *aXMost
= std::max(*aXMost
, data
.mCurrentLine
+ childXMost
);
1043 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1054 * Return whether aNewAvailableSpace is smaller *on either side*
1055 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
1056 * if we need to redo layout on an line, replaced block, or block
1057 * formatting context, because its height (which we used to compute
1058 * aNewAvailableSpace) caused it to intersect additional floats.
1060 static bool AvailableSpaceShrunk(WritingMode aWM
,
1061 const LogicalRect
& aOldAvailableSpace
,
1062 const LogicalRect
& aNewAvailableSpace
,
1063 bool aCanGrow
/* debug-only */) {
1064 if (aNewAvailableSpace
.ISize(aWM
) == 0) {
1065 // Positions are not significant if the inline size is zero.
1066 return aOldAvailableSpace
.ISize(aWM
) != 0;
1070 aNewAvailableSpace
.IStart(aWM
) <= aOldAvailableSpace
.IStart(aWM
) ||
1071 aNewAvailableSpace
.IEnd(aWM
) <= aOldAvailableSpace
.IEnd(aWM
),
1072 "available space should not shrink on the start side and "
1073 "grow on the end side");
1075 aNewAvailableSpace
.IStart(aWM
) >= aOldAvailableSpace
.IStart(aWM
) ||
1076 aNewAvailableSpace
.IEnd(aWM
) >= aOldAvailableSpace
.IEnd(aWM
),
1077 "available space should not grow on the start side and "
1078 "shrink on the end side");
1081 aOldAvailableSpace
.IStart(aWM
) <= aNewAvailableSpace
.IStart(aWM
) &&
1082 aOldAvailableSpace
.IEnd(aWM
) >= aNewAvailableSpace
.IEnd(aWM
),
1083 "available space should never grow");
1085 // Have we shrunk on either side?
1086 return aNewAvailableSpace
.IStart(aWM
) > aOldAvailableSpace
.IStart(aWM
) ||
1087 aNewAvailableSpace
.IEnd(aWM
) < aOldAvailableSpace
.IEnd(aWM
);
1090 static LogicalSize
CalculateContainingBlockSizeForAbsolutes(
1091 WritingMode aWM
, const ReflowInput
& aReflowInput
, LogicalSize aFrameSize
) {
1092 // The issue here is that for a 'height' of 'auto' the reflow input
1093 // code won't know how to calculate the containing block height
1094 // because it's calculated bottom up. So we use our own computed
1095 // size as the dimensions.
1096 nsIFrame
* frame
= aReflowInput
.mFrame
;
1098 LogicalSize
cbSize(aFrameSize
);
1099 // Containing block is relative to the padding edge
1100 const LogicalMargin border
= aReflowInput
.ComputedLogicalBorder(aWM
);
1101 cbSize
.ISize(aWM
) -= border
.IStartEnd(aWM
);
1102 cbSize
.BSize(aWM
) -= border
.BStartEnd(aWM
);
1104 if (frame
->GetParent()->GetContent() != frame
->GetContent() ||
1105 frame
->GetParent()->IsCanvasFrame()) {
1109 // We are a wrapped frame for the content (and the wrapper is not the
1110 // canvas frame, whose size is not meaningful here).
1111 // Use the container's dimensions, if they have been precomputed.
1112 // XXX This is a hack! We really should be waiting until the outermost
1113 // frame is fully reflowed and using the resulting dimensions, even
1114 // if they're intrinsic.
1115 // In fact we should be attaching absolute children to the outermost
1116 // frame and not always sticking them in block frames.
1118 // First, find the reflow input for the outermost frame for this content.
1119 const ReflowInput
* lastRI
= &aReflowInput
;
1120 DebugOnly
<const ReflowInput
*> lastButOneRI
= &aReflowInput
;
1121 while (lastRI
->mParentReflowInput
&&
1122 lastRI
->mParentReflowInput
->mFrame
->GetContent() ==
1123 frame
->GetContent()) {
1124 lastButOneRI
= lastRI
;
1125 lastRI
= lastRI
->mParentReflowInput
;
1128 if (lastRI
== &aReflowInput
) {
1132 // For scroll containers, we can just use cbSize (which is the padding-box
1133 // size of the scrolled-content frame).
1134 if (nsIScrollableFrame
* scrollFrame
= do_QueryFrame(lastRI
->mFrame
)) {
1135 // Assert that we're not missing any frames between the abspos containing
1136 // block and the scroll container.
1138 Unused
<< scrollFrame
;
1139 MOZ_ASSERT(lastButOneRI
== &aReflowInput
);
1143 // Same for fieldsets, where the inner anonymous frame has the correct padding
1144 // area with the legend taken into account.
1145 if (lastRI
->mFrame
->IsFieldSetFrame()) {
1149 // We found a reflow input for the outermost wrapping frame, so use
1150 // its computed metrics if available, converted to our writing mode
1151 const LogicalSize lastRISize
= lastRI
->ComputedSize(aWM
);
1152 const LogicalMargin lastRIPadding
= lastRI
->ComputedLogicalPadding(aWM
);
1153 if (lastRISize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1155 std::max(0, lastRISize
.ISize(aWM
) + lastRIPadding
.IStartEnd(aWM
));
1157 if (lastRISize
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1159 std::max(0, lastRISize
.BSize(aWM
) + lastRIPadding
.BStartEnd(aWM
));
1166 * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1168 * This is used to determine whether to recurse into aFrame when applying
1169 * -webkit-line-clamp.
1171 static const nsBlockFrame
* GetAsLineClampDescendant(const nsIFrame
* aFrame
) {
1172 if (const nsBlockFrame
* block
= do_QueryFrame(aFrame
)) {
1173 if (!block
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1180 static nsBlockFrame
* GetAsLineClampDescendant(nsIFrame
* aFrame
) {
1181 return const_cast<nsBlockFrame
*>(
1182 GetAsLineClampDescendant(const_cast<const nsIFrame
*>(aFrame
)));
1185 static bool IsLineClampRoot(const nsBlockFrame
* aFrame
) {
1186 if (!aFrame
->StyleDisplay()->mWebkitLineClamp
) {
1190 if (!aFrame
->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
)) {
1194 if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled()) {
1198 // For now, -webkit-box is the only thing allowed to be a line-clamp root.
1199 // Ideally we'd just make this work everywhere, but for now we're carrying
1200 // this forward as a limitation on the legacy -webkit-line-clamp feature,
1201 // since relaxing this limitation might create webcompat trouble.
1202 auto origDisplay
= [&] {
1203 if (aFrame
->Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
1204 // If we're the anonymous block inside the scroll frame, we need to look
1205 // at the original display of our parent frame.
1206 MOZ_ASSERT(aFrame
->GetParent());
1207 const auto& parentDisp
= *aFrame
->GetParent()->StyleDisplay();
1208 MOZ_ASSERT(parentDisp
.mWebkitLineClamp
==
1209 aFrame
->StyleDisplay()->mWebkitLineClamp
,
1210 ":-moz-scrolled-content should inherit -webkit-line-clamp, "
1211 "via rule in UA stylesheet");
1212 return parentDisp
.mOriginalDisplay
;
1214 return aFrame
->StyleDisplay()->mOriginalDisplay
;
1216 return origDisplay
.Inside() == StyleDisplayInside::WebkitBox
;
1219 bool nsBlockFrame::IsInLineClampContext() const {
1220 if (IsLineClampRoot(this)) {
1223 const nsBlockFrame
* cur
= this;
1224 while (GetAsLineClampDescendant(cur
)) {
1225 cur
= do_QueryFrame(cur
->GetParent());
1229 if (IsLineClampRoot(cur
)) {
1237 * Iterator over all descendant inline line boxes, except for those that are
1238 * under an independent formatting context.
1240 class MOZ_RAII LineClampLineIterator
{
1242 explicit LineClampLineIterator(nsBlockFrame
* aFrame
)
1243 : mCur(aFrame
->LinesBegin()),
1244 mEnd(aFrame
->LinesEnd()),
1245 mCurrentFrame(mCur
== mEnd
? nullptr : aFrame
) {
1246 if (mCur
!= mEnd
&& !mCur
->IsInline()) {
1251 nsLineBox
* GetCurrentLine() { return mCurrentFrame
? mCur
.get() : nullptr; }
1252 nsBlockFrame
* GetCurrentFrame() { return mCurrentFrame
; }
1254 // Advances the iterator to the next line line.
1256 // Next() shouldn't be called once the iterator is at the end, which can be
1257 // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
1259 MOZ_ASSERT(mCur
!= mEnd
&& mCurrentFrame
,
1260 "Don't call Next() when the iterator is at the end");
1269 // Reached the end of the current block. Pop the parent off the
1270 // stack; if there isn't one, then we've reached the end.
1271 if (mStack
.IsEmpty()) {
1272 mCurrentFrame
= nullptr;
1275 auto entry
= mStack
.PopLastElement();
1276 mCurrentFrame
= entry
.first
;
1277 mCur
= entry
.second
;
1278 mEnd
= mCurrentFrame
->LinesEnd();
1279 } else if (mCur
->IsBlock()) {
1280 if (nsBlockFrame
* child
= GetAsLineClampDescendant(mCur
->mFirstChild
)) {
1281 nsBlockFrame::LineIterator next
= mCur
;
1283 mStack
.AppendElement(std::make_pair(mCurrentFrame
, next
));
1284 mCur
= child
->LinesBegin();
1285 mEnd
= child
->LinesEnd();
1286 mCurrentFrame
= child
;
1288 // Some kind of frame we shouldn't descend into.
1292 MOZ_ASSERT(mCur
->IsInline());
1298 // The current line within the current block.
1300 // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1302 nsBlockFrame::LineIterator mCur
;
1304 // The iterator end for the current block.
1305 nsBlockFrame::LineIterator mEnd
;
1307 // The current block.
1308 nsBlockFrame
* mCurrentFrame
;
1310 // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1312 AutoTArray
<std::pair
<nsBlockFrame
*, nsBlockFrame::LineIterator
>, 8> mStack
;
1315 static bool ClearLineClampEllipsis(nsBlockFrame
* aFrame
) {
1316 if (!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
)) {
1317 for (nsIFrame
* f
: aFrame
->PrincipalChildList()) {
1318 if (nsBlockFrame
* child
= GetAsLineClampDescendant(f
)) {
1319 if (ClearLineClampEllipsis(child
)) {
1327 aFrame
->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1329 for (auto& line
: aFrame
->Lines()) {
1330 if (line
.HasLineClampEllipsis()) {
1331 line
.ClearHasLineClampEllipsis();
1336 // We didn't find a line with the ellipsis; it must have been deleted already.
1340 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1342 void nsBlockFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
1343 const ReflowInput
& aReflowInput
,
1344 nsReflowStatus
& aStatus
) {
1345 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
1346 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1351 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1352 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
1353 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1357 IndentBy(stdout
, gNoiseIndent
);
1359 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1360 aReflowInput
.AvailableISize(), aReflowInput
.AvailableBSize(),
1361 aReflowInput
.ComputedISize(), aReflowInput
.ComputedBSize());
1363 AutoNoisyIndenter
indent(gNoisy
);
1364 PRTime start
= 0; // Initialize these variablies to silence the compiler.
1365 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
1366 if (gLameReflowMetrics
) {
1368 ctc
= nsLineBox::GetCtorCount();
1372 // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
1373 // max-block-size because both affect the children's available block-size.
1374 if (IsColumnSetWrapperFrame()) {
1375 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
);
1378 Maybe
<nscoord
> restoreReflowInputAvailBSize
;
1379 auto MaybeRestore
= MakeScopeExit([&] {
1380 if (MOZ_UNLIKELY(restoreReflowInputAvailBSize
)) {
1381 const_cast<ReflowInput
&>(aReflowInput
)
1382 .SetAvailableBSize(*restoreReflowInputAvailBSize
);
1386 WritingMode wm
= aReflowInput
.GetWritingMode();
1387 const nscoord consumedBSize
= CalcAndCacheConsumedBSize();
1388 const nscoord effectiveContentBoxBSize
=
1389 GetEffectiveComputedBSize(aReflowInput
, consumedBSize
);
1390 // If we have non-auto block size, we're clipping our kids and we fit,
1391 // make sure our kids fit too.
1392 const PhysicalAxes physicalBlockAxis
=
1393 wm
.IsVertical() ? PhysicalAxes::Horizontal
: PhysicalAxes::Vertical
;
1394 if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1395 aReflowInput
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
&&
1396 (ShouldApplyOverflowClipping(aReflowInput
.mStyleDisplay
) &
1397 physicalBlockAxis
)) {
1398 LogicalMargin blockDirExtras
=
1399 aReflowInput
.ComputedLogicalBorderPadding(wm
);
1400 if (GetLogicalSkipSides().BStart()) {
1401 blockDirExtras
.BStart(wm
) = 0;
1403 // Block-end margin never causes us to create continuations, so we
1404 // don't need to worry about whether it fits in its entirety.
1405 blockDirExtras
.BStart(wm
) +=
1406 aReflowInput
.ComputedLogicalMargin(wm
).BStart(wm
);
1409 if (effectiveContentBoxBSize
+ blockDirExtras
.BStartEnd(wm
) <=
1410 aReflowInput
.AvailableBSize()) {
1411 restoreReflowInputAvailBSize
.emplace(aReflowInput
.AvailableBSize());
1412 const_cast<ReflowInput
&>(aReflowInput
)
1413 .SetAvailableBSize(NS_UNCONSTRAINEDSIZE
);
1417 if (IsFrameTreeTooDeep(aReflowInput
, aMetrics
, aStatus
)) {
1421 // OK, some lines may be reflowed. Blow away any saved line cursor
1422 // because we may invalidate the nondecreasing
1423 // overflowArea.InkOverflow().y/yMost invariant, and we may even
1424 // delete the line with the line cursor.
1427 // See comment below about oldSize. Use *only* for the
1428 // abs-pos-containing-block-size-change optimization!
1429 nsSize oldSize
= GetSize();
1431 // Should we create a float manager?
1432 nsAutoFloatManager
autoFloatManager(const_cast<ReflowInput
&>(aReflowInput
));
1434 // XXXldb If we start storing the float manager in the frame rather
1435 // than keeping it around only during reflow then we should create it
1436 // only when there are actually floats to manage. Otherwise things
1437 // like tables will gain significant bloat.
1438 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
1439 if (needFloatManager
) {
1440 autoFloatManager
.CreateFloatManager(aPresContext
);
1443 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
1444 PresContext()->BidiEnabled()) {
1445 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
1448 // Whether to apply text-wrap: balance behavior.
1449 bool tryBalance
= StyleText()->mTextWrap
== StyleTextWrap::Balance
&&
1450 !GetPrevContinuation();
1452 // Struct used to hold the "target" number of lines or clamp position to
1453 // maintain when doing text-wrap: balance.
1454 struct BalanceTarget
{
1455 // If line-clamp is in effect, mContent and mOffset indicate the starting
1456 // position of the first line after the clamp limit. If line-clamp is not
1457 // in use, mContent is null and mOffset is the total number of lines that
1458 // the block must contain.
1459 nsIContent
* mContent
= nullptr;
1460 int32_t mOffset
= -1;
1462 bool operator==(const BalanceTarget
& aOther
) const {
1463 return mContent
== aOther
.mContent
&& mOffset
== aOther
.mOffset
;
1465 bool operator!=(const BalanceTarget
& aOther
) const {
1466 return !(*this == aOther
);
1470 BalanceTarget balanceTarget
;
1472 // Helpers for text-wrap: balance implementation:
1474 // Count the number of lines in the mLines list, but return -1 instead if the
1475 // count is going to exceed aLimit.
1476 auto countLinesUpTo
= [&](int32_t aLimit
) -> int32_t {
1478 for (auto iter
= mLines
.begin(); iter
!= mLines
.end(); ++iter
) {
1486 // Return a BalanceTarget record representing the position at which line-clamp
1487 // will take effect for the current line list. Only to be used when there are
1488 // enough lines that the clamp will apply.
1489 auto getClampPosition
= [&](uint32_t aClampCount
) -> BalanceTarget
{
1490 MOZ_ASSERT(aClampCount
< mLines
.size());
1491 auto iter
= mLines
.begin();
1492 for (uint32_t i
= 0; i
< aClampCount
; i
++) {
1495 nsIFrame
* firstChild
= iter
->mFirstChild
;
1497 return BalanceTarget
{};
1499 nsIContent
* content
= firstChild
->GetContent();
1501 return BalanceTarget
{};
1504 if (firstChild
->IsTextFrame()) {
1505 auto* textFrame
= static_cast<nsTextFrame
*>(firstChild
);
1506 offset
= textFrame
->GetContentOffset();
1508 return BalanceTarget
{content
, offset
};
1511 // "balancing" is implemented by shortening the effective inline-size of the
1512 // lines, so that content will tend to be pushed down to fill later lines of
1513 // the block. `balanceInset` is the current amount of "inset" to apply, and
1514 // `balanceStep` is the increment to adjust it by for the next iteration.
1515 nscoord balanceStep
= 0;
1517 // text-wrap: balance loop, executed only once if balancing is not required.
1518 nsReflowStatus reflowStatus
;
1519 TrialReflowState
trialState(consumedBSize
, effectiveContentBoxBSize
,
1522 // Save the initial floatManager state for repeated trial reflows.
1523 // We'll restore (and re-save) the initial state each time we repeat the
1525 nsFloatManager::SavedState floatManagerState
;
1526 aReflowInput
.mFloatManager
->PushState(&floatManagerState
);
1528 aMetrics
= ReflowOutput(aMetrics
.GetWritingMode());
1530 TrialReflow(aPresContext
, aMetrics
, aReflowInput
, trialState
);
1532 // Do we need to start a `text-wrap: balance` iteration?
1535 // Don't try to balance an incomplete block.
1536 if (!reflowStatus
.IsFullyComplete()) {
1539 balanceTarget
.mOffset
=
1540 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1541 if (balanceTarget
.mOffset
< 2) {
1542 // If there are less than 2 lines, or the number exceeds the limit,
1543 // no balancing is needed; just break from the balance loop.
1546 // Initialize the amount of inset to try, and the iteration step size.
1547 balanceStep
= aReflowInput
.ComputedISize() / balanceTarget
.mOffset
;
1548 trialState
.ResetForBalance(balanceStep
);
1551 // If -webkit-line-clamp is in effect, then we need to maintain the
1552 // content location at which clamping occurs, rather than the total
1553 // number of lines in the block.
1554 if (StaticPrefs::layout_css_text_wrap_balance_after_clamp_enabled() &&
1555 IsLineClampRoot(this)) {
1556 uint32_t lineClampCount
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
1557 if (uint32_t(balanceTarget
.mOffset
) > lineClampCount
) {
1558 auto t
= getClampPosition(lineClampCount
);
1565 // Restore initial floatManager state for a new trial with updated inset.
1566 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1570 // Helper to determine whether the current trial succeeded (i.e. was able
1571 // to fit the content into the expected number of lines).
1572 auto trialSucceeded
= [&]() -> bool {
1573 if (!reflowStatus
.IsFullyComplete()) {
1576 if (balanceTarget
.mContent
) {
1577 auto t
= getClampPosition(aReflowInput
.mStyleDisplay
->mWebkitLineClamp
);
1578 return t
== balanceTarget
;
1581 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1582 return numLines
== balanceTarget
.mOffset
;
1585 // If we're in the process of a balance operation, check whether we've
1586 // inset by too much and either increase or reduce the inset for the next
1588 if (balanceStep
> 0) {
1589 if (trialSucceeded()) {
1590 trialState
.ResetForBalance(balanceStep
);
1592 trialState
.ResetForBalance(-balanceStep
);
1596 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1600 // If we were attempting to balance, check whether the final iteration was
1601 // successful, and if not, back up by one step.
1602 if (balanceTarget
.mOffset
>= 0) {
1603 if (trialSucceeded()) {
1606 trialState
.ResetForBalance(-1);
1608 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1612 // If we reach here, no balancing was required, so just exit; we don't
1613 // reset (pop) the floatManager state because this is the reflow we're
1614 // going to keep. So the saved state is just dropped.
1616 } // End of text-wrap: balance retry loop
1618 // If the block direction is right-to-left, we need to update the bounds of
1619 // lines that were placed relative to mContainerSize during reflow, as
1620 // we typically do not know the true container size until we've reflowed all
1621 // its children. So we use a dummy mContainerSize during reflow (see
1622 // BlockReflowState's constructor) and then fix up the positions of the
1623 // lines here, once the final block size is known.
1625 // Note that writing-mode:vertical-rl is the only case where the block
1626 // logical direction progresses in a negative physical direction, and
1627 // therefore block-dir coordinate conversion depends on knowing the width
1628 // of the coordinate space in order to translate between the logical and
1629 // physical origins.
1630 if (aReflowInput
.GetWritingMode().IsVerticalRL()) {
1631 nsSize containerSize
= aMetrics
.PhysicalSize();
1632 nscoord deltaX
= containerSize
.width
- trialState
.mContainerWidth
;
1634 // We compute our lines and markers' overflow areas later in
1635 // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
1637 const nsPoint
physicalDelta(deltaX
, 0);
1638 for (auto& line
: Lines()) {
1639 UpdateLineContainerSize(&line
, containerSize
);
1641 trialState
.mFcBounds
.Clear();
1642 for (nsIFrame
* f
: mFloats
) {
1643 f
->MovePositionBy(physicalDelta
);
1644 ConsiderChildOverflow(trialState
.mFcBounds
, f
);
1646 nsFrameList
* markerList
= GetOutsideMarkerList();
1648 for (nsIFrame
* f
: *markerList
) {
1649 f
->MovePositionBy(physicalDelta
);
1652 if (nsFrameList
* overflowContainers
= GetOverflowContainers()) {
1653 trialState
.mOcBounds
.Clear();
1654 for (nsIFrame
* f
: *overflowContainers
) {
1655 f
->MovePositionBy(physicalDelta
);
1656 ConsiderChildOverflow(trialState
.mOcBounds
, f
);
1662 aMetrics
.SetOverflowAreasToDesiredBounds();
1663 ComputeOverflowAreas(aMetrics
.mOverflowAreas
,
1664 trialState
.mBlockEndEdgeOfChildren
,
1665 aReflowInput
.mStyleDisplay
);
1666 // Factor overflow container child bounds into the overflow area
1667 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mOcBounds
);
1668 // Factor pushed float child bounds into the overflow area
1669 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mFcBounds
);
1671 // Let the absolutely positioned container reflow any absolutely positioned
1672 // child frames that need to be reflowed, e.g., elements with a percentage
1673 // based width/height
1674 // We want to do this under either of two conditions:
1675 // 1. If we didn't do the incremental reflow above.
1676 // 2. If our size changed.
1677 // Even though it's the padding edge that's the containing block, we
1678 // can use our rect (the border edge) since if the border style
1679 // changed, the reflow would have been targeted at us so we'd satisfy
1681 // XXX checking oldSize is bogus, there are various reasons we might have
1682 // reflowed but our size might not have been changed to what we
1683 // asked for (e.g., we ended up being pushed to a new page)
1684 // When WillReflowAgainForClearance is true, we will reflow again without
1685 // resetting the size. Because of this, we must not reflow our abs-pos
1686 // children in that situation --- what we think is our "new size" will not be
1687 // our real new size. This also happens to be more efficient.
1688 WritingMode parentWM
= aMetrics
.GetWritingMode();
1689 if (HasAbsolutelyPositionedChildren()) {
1690 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1691 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1692 if (aReflowInput
.WillReflowAgainForClearance() || haveInterrupt
) {
1693 // Make sure that when we reflow again we'll actually reflow all the abs
1694 // pos frames that might conceivably depend on our size (or all of them,
1695 // if we're dirty right now and interrupted; in that case we also need
1696 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1697 // better than that, because we don't really know what our size will be,
1698 // and it might in fact not change on the followup reflow!
1699 if (haveInterrupt
&& HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
1700 absoluteContainer
->MarkAllFramesDirty();
1702 absoluteContainer
->MarkSizeDependentFramesDirty();
1704 if (haveInterrupt
) {
1705 // We're not going to reflow absolute frames; make sure to account for
1706 // their existing overflow areas, which is usually a side effect of this
1709 // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1710 // interrupt, can we just rely on it and unconditionally take the else
1711 // branch below? That's a bit more subtle / risky, since I don't see
1712 // what would reflow them in that case if they depended on our size.
1713 for (nsIFrame
* kid
= absoluteContainer
->GetChildList().FirstChild();
1714 kid
; kid
= kid
->GetNextSibling()) {
1715 ConsiderChildOverflow(aMetrics
.mOverflowAreas
, kid
);
1719 LogicalSize containingBlockSize
=
1720 CalculateContainingBlockSizeForAbsolutes(parentWM
, aReflowInput
,
1721 aMetrics
.Size(parentWM
));
1723 // Mark frames that depend on changes we just made to this frame as dirty:
1724 // Now we can assume that the padding edge hasn't moved.
1725 // We need to reflow the absolutes if one of them depends on
1726 // its placeholder position, or the containing block size in a
1727 // direction in which the containing block size might have
1730 // XXX "width" and "height" in this block will become ISize and BSize
1731 // when nsAbsoluteContainingBlock is logicalized
1732 bool cbWidthChanged
= aMetrics
.Width() != oldSize
.width
;
1733 bool isRoot
= !GetContent()->GetParent();
1734 // If isRoot and we have auto height, then we are the initial
1735 // containing block and the containing block height is the
1736 // viewport height, which can't change during incremental
1738 bool cbHeightChanged
=
1739 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedHeight()) &&
1740 aMetrics
.Height() != oldSize
.height
;
1742 nsRect
containingBlock(nsPoint(0, 0),
1743 containingBlockSize
.GetPhysicalSize(parentWM
));
1744 AbsPosReflowFlags flags
= AbsPosReflowFlags::ConstrainHeight
;
1745 if (cbWidthChanged
) {
1746 flags
|= AbsPosReflowFlags::CBWidthChanged
;
1748 if (cbHeightChanged
) {
1749 flags
|= AbsPosReflowFlags::CBHeightChanged
;
1751 // Setup the line cursor here to optimize line searching for
1752 // calculating hypothetical position of absolutely-positioned
1754 SetupLineCursorForQuery();
1755 absoluteContainer
->Reflow(this, aPresContext
, aReflowInput
, reflowStatus
,
1756 containingBlock
, flags
,
1757 &aMetrics
.mOverflowAreas
);
1761 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1763 aStatus
= reflowStatus
;
1766 // Between when we drain pushed floats and when we complete reflow,
1767 // we're allowed to have multiple continuations of the same float on
1768 // our floats list, since a first-in-flow might get pushed to a later
1769 // continuation of its containing block. But it's not permitted
1770 // outside that time.
1771 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1774 IndentBy(stdout
, gNoiseIndent
);
1776 printf(": status=%s metrics=%d,%d carriedMargin=%d",
1777 ToString(aStatus
).c_str(), aMetrics
.ISize(parentWM
),
1778 aMetrics
.BSize(parentWM
), aMetrics
.mCarriedOutBEndMargin
.get());
1779 if (HasOverflowAreas()) {
1780 printf(" overflow-vis={%d,%d,%d,%d}", aMetrics
.InkOverflow().x
,
1781 aMetrics
.InkOverflow().y
, aMetrics
.InkOverflow().width
,
1782 aMetrics
.InkOverflow().height
);
1783 printf(" overflow-scr={%d,%d,%d,%d}", aMetrics
.ScrollableOverflow().x
,
1784 aMetrics
.ScrollableOverflow().y
,
1785 aMetrics
.ScrollableOverflow().width
,
1786 aMetrics
.ScrollableOverflow().height
);
1791 if (gLameReflowMetrics
) {
1792 PRTime end
= PR_Now();
1794 int32_t ectc
= nsLineBox::GetCtorCount();
1795 int32_t numLines
= mLines
.size();
1799 PRTime delta
, perLineDelta
, lines
;
1800 lines
= int64_t(numLines
);
1801 delta
= end
- start
;
1802 perLineDelta
= delta
/ lines
;
1807 ": %" PRId64
" elapsed (%" PRId64
1808 " per line) (%d lines; %d new lines)",
1809 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1810 printf("%s\n", buf
);
1815 nsReflowStatus
nsBlockFrame::TrialReflow(nsPresContext
* aPresContext
,
1816 ReflowOutput
& aMetrics
,
1817 const ReflowInput
& aReflowInput
,
1818 TrialReflowState
& aTrialState
) {
1820 // Between when we drain pushed floats and when we complete reflow,
1821 // we're allowed to have multiple continuations of the same float on
1822 // our floats list, since a first-in-flow might get pushed to a later
1823 // continuation of its containing block. But it's not permitted
1824 // outside that time.
1825 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1828 // ALWAYS drain overflow. We never want to leave the previnflow's
1829 // overflow lines hanging around; block reflow depends on the
1830 // overflow line lists being cleared out between reflow passes.
1831 DrainOverflowLines();
1833 bool blockStartMarginRoot
, blockEndMarginRoot
;
1834 IsMarginRoot(&blockStartMarginRoot
, &blockEndMarginRoot
);
1836 BlockReflowState
state(aReflowInput
, aPresContext
, this, blockStartMarginRoot
,
1837 blockEndMarginRoot
, aTrialState
.mNeedFloatManager
,
1838 aTrialState
.mConsumedBSize
,
1839 aTrialState
.mEffectiveContentBoxBSize
,
1840 aTrialState
.mInset
);
1842 // Handle paginated overflow (see nsContainerFrame.h)
1843 nsReflowStatus ocStatus
;
1844 if (GetPrevInFlow()) {
1845 ReflowOverflowContainerChildren(
1846 aPresContext
, aReflowInput
, aTrialState
.mOcBounds
,
1847 ReflowChildFlags::Default
, ocStatus
, DefaultChildFrameMerge
,
1848 Some(state
.ContainerSize()));
1851 // Now that we're done cleaning up our overflow container lists, we can
1852 // give |state| its nsOverflowContinuationTracker.
1853 nsOverflowContinuationTracker
tracker(this, false);
1854 state
.mOverflowTracker
= &tracker
;
1856 // Drain & handle pushed floats
1857 DrainPushedFloats();
1858 ReflowPushedFloats(state
, aTrialState
.mFcBounds
);
1860 // If we're not dirty (which means we'll mark everything dirty later)
1861 // and our inline-size has changed, mark the lines dirty that we need to
1862 // mark dirty for a resize reflow.
1863 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.IsIResize()) {
1864 PrepareResizeReflow(state
);
1867 // The same for percentage text-indent, except conditioned on the
1869 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.mCBReflowInput
&&
1870 aReflowInput
.mCBReflowInput
->IsIResize() &&
1871 StyleText()->mTextIndent
.HasPercent() && !mLines
.empty()) {
1872 mLines
.front()->MarkDirty();
1875 // For text-wrap:balance trials, we need to reflow all the lines even if
1876 // they're not all "dirty".
1877 if (aTrialState
.mBalancing
) {
1878 MarkAllDescendantLinesDirty(this);
1880 LazyMarkLinesDirty();
1884 ReflowDirtyLines(state
);
1886 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1887 // this frame from a previous iteration of reflow, then we should not return
1888 // a status with IsFullyComplete() equals to true, since we actually have
1889 // overflow, it's just already been handled.
1891 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1892 // and reflow them, but just in case it does, this is a safety precaution so
1893 // we don't end up with a placeholder pointing to frames that have already
1894 // been deleted as part of removing our next-in-flow.
1895 if (state
.mReflowStatus
.IsFullyComplete()) {
1896 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1898 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1899 if (nif
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
1900 state
.mReflowStatus
.SetOverflowIncomplete();
1902 state
.mReflowStatus
.SetIncomplete();
1907 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1911 state
.mReflowStatus
.MergeCompletionStatusFrom(ocStatus
);
1913 // If we end in a BR with clear and affected floats continue,
1914 // we need to continue, too.
1915 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize() &&
1916 state
.mReflowStatus
.IsComplete() &&
1917 state
.FloatManager()->ClearContinues(FindTrailingClear())) {
1918 state
.mReflowStatus
.SetIncomplete();
1921 if (!state
.mReflowStatus
.IsFullyComplete()) {
1922 if (HasOverflowLines() || HasPushedFloats()) {
1923 state
.mReflowStatus
.SetNextInFlowNeedsReflow();
1928 printf(": block is not fully complete\n");
1932 // Place the ::marker's frame if it is placed next to a block child.
1934 // According to the CSS2 spec, section 12.6.1, the ::marker's box
1935 // participates in the height calculation of the list-item box's
1938 // There are exactly two places a ::marker can be placed: near the
1939 // first or second line. It's only placed on the second line in a
1940 // rare case: an empty first line followed by a second line that
1941 // contains a block (example: <LI>\n<P>... ). This is where
1942 // the second case can happen.
1943 if (HasOutsideMarker() && !mLines
.empty() &&
1944 (mLines
.front()->IsBlock() ||
1945 (0 == mLines
.front()->BSize() && mLines
.front() != mLines
.back() &&
1946 mLines
.begin().next()->IsBlock()))) {
1947 // Reflow the ::marker's frame.
1948 ReflowOutput
reflowOutput(aReflowInput
);
1949 // XXX Use the entire line when we fix bug 25888.
1950 nsLayoutUtils::LinePosition position
;
1951 WritingMode wm
= aReflowInput
.GetWritingMode();
1953 nsLayoutUtils::GetFirstLinePosition(wm
, this, &position
);
1954 nscoord lineBStart
=
1955 havePosition
? position
.mBStart
1956 : aReflowInput
.ComputedLogicalBorderPadding(wm
).BStart(wm
);
1957 nsIFrame
* marker
= GetOutsideMarker();
1958 ReflowOutsideMarker(marker
, state
, reflowOutput
, lineBStart
);
1959 NS_ASSERTION(!MarkerIsEmpty() || reflowOutput
.BSize(wm
) == 0,
1960 "empty ::marker frame took up space");
1962 if (havePosition
&& !MarkerIsEmpty()) {
1963 // We have some lines to align the ::marker with.
1965 // Doing the alignment using the baseline will also cater for
1966 // ::markers that are placed next to a child block (bug 92896)
1968 // Tall ::markers won't look particularly nice here...
1970 marker
->GetLogicalRect(wm
, reflowOutput
.PhysicalSize());
1971 const auto baselineGroup
= BaselineSharingGroup::First
;
1972 Maybe
<nscoord
> result
;
1973 if (MOZ_LIKELY(!wm
.IsOrthogonalTo(marker
->GetWritingMode()))) {
1974 result
= marker
->GetNaturalBaselineBOffset(
1975 wm
, baselineGroup
, BaselineExportContext::LineLayout
);
1977 const auto markerBaseline
= result
.valueOrFrom([bbox
, wm
, marker
]() {
1978 return bbox
.BSize(wm
) + marker
->GetLogicalUsedMargin(wm
).BEnd(wm
);
1980 bbox
.BStart(wm
) = position
.mBaseline
- markerBaseline
;
1981 marker
->SetRect(wm
, bbox
, reflowOutput
.PhysicalSize());
1983 // Otherwise just leave the ::marker where it is, up against our
1984 // block-start padding.
1987 // Clear any existing -webkit-line-clamp ellipsis.
1988 if (aReflowInput
.mStyleDisplay
->mWebkitLineClamp
) {
1989 ClearLineClampEllipsis();
1994 // Compute our final size (for this trial layout)
1995 aTrialState
.mBlockEndEdgeOfChildren
=
1996 ComputeFinalSize(aReflowInput
, state
, aMetrics
);
1997 aTrialState
.mContainerWidth
= state
.ContainerSize().width
;
1999 return state
.mReflowStatus
;
2002 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
2003 for (auto& line
: Reversed(Lines())) {
2004 if (0 != line
.BSize() || !line
.CachedIsEmpty()) {
2007 if (line
.HasClearance()) {
2014 static nsLineBox
* FindLineClampTarget(nsBlockFrame
*& aFrame
,
2015 StyleLineClamp aLineNumber
) {
2016 MOZ_ASSERT(aLineNumber
> 0);
2017 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2018 "Should have been removed earlier in nsBlockReflow::Reflow");
2020 nsLineBox
* target
= nullptr;
2021 nsBlockFrame
* targetFrame
= nullptr;
2022 bool foundFollowingLine
= false;
2024 LineClampLineIterator
iter(aFrame
);
2026 while (nsLineBox
* line
= iter
.GetCurrentLine()) {
2027 MOZ_ASSERT(!line
->HasLineClampEllipsis(),
2028 "Should have been removed earlier in nsBlockFrame::Reflow");
2029 MOZ_ASSERT(!iter
.GetCurrentFrame()->HasAnyStateBits(
2030 NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2031 "Should have been removed earlier in nsBlockReflow::Reflow");
2033 // Don't count a line that only has collapsible white space (as might exist
2034 // after calling e.g. getBoxQuads).
2035 if (line
->IsEmpty()) {
2040 if (aLineNumber
== 0) {
2041 // We already previously found our target line, and now we have
2042 // confirmed that there is another line after it.
2043 foundFollowingLine
= true;
2047 if (--aLineNumber
== 0) {
2048 // This is our target line. Continue looping to confirm that we
2049 // have another line after us.
2051 targetFrame
= iter
.GetCurrentFrame();
2057 if (!foundFollowingLine
) {
2063 MOZ_ASSERT(targetFrame
);
2065 aFrame
= targetFrame
;
2069 static nscoord
ApplyLineClamp(const ReflowInput
& aReflowInput
,
2070 nsBlockFrame
* aFrame
,
2071 nscoord aContentBlockEndEdge
) {
2072 if (!IsLineClampRoot(aFrame
)) {
2073 return aContentBlockEndEdge
;
2075 auto lineClamp
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
2076 nsBlockFrame
* frame
= aFrame
;
2077 nsLineBox
* line
= FindLineClampTarget(frame
, lineClamp
);
2079 // The number of lines did not exceed the -webkit-line-clamp value.
2080 return aContentBlockEndEdge
;
2083 // Mark the line as having an ellipsis so that TextOverflow will render it.
2084 line
->SetHasLineClampEllipsis();
2085 frame
->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
2087 // Translate the b-end edge of the line up to aFrame's space.
2088 nscoord edge
= line
->BEnd();
2089 for (nsIFrame
* f
= frame
; f
!= aFrame
; f
= f
->GetParent()) {
2091 f
->GetLogicalPosition(f
->GetParent()->GetSize()).B(f
->GetWritingMode());
2097 nscoord
nsBlockFrame::ComputeFinalSize(const ReflowInput
& aReflowInput
,
2098 BlockReflowState
& aState
,
2099 ReflowOutput
& aMetrics
) {
2100 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2101 const LogicalMargin
& borderPadding
= aState
.BorderPadding();
2102 #ifdef NOISY_FINAL_SIZE
2104 printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
2105 aState
.mBCoord
, aState
.mFlags
.mIsBEndMarginRoot
? "yes" : "no",
2106 aState
.mPrevBEndMargin
.get(), borderPadding
.BStart(wm
),
2107 borderPadding
.BEnd(wm
));
2110 // Compute final inline size
2111 LogicalSize
finalSize(wm
);
2112 finalSize
.ISize(wm
) =
2113 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.IStart(wm
),
2114 aReflowInput
.ComputedISize()),
2115 borderPadding
.IEnd(wm
));
2117 // Return block-end margin information
2118 // rbs says he hit this assertion occasionally (see bug 86947), so
2119 // just set the margin to zero and we'll figure out why later
2120 // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
2121 // "someone else set the margin");
2122 nscoord nonCarriedOutBDirMargin
= 0;
2123 if (!aState
.mFlags
.mIsBEndMarginRoot
) {
2124 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
2125 // line with clearance and a non-zero block-start margin and all
2126 // subsequent lines are empty, then we do not allow our children's
2127 // carried out block-end margin to be carried out of us and collapse
2128 // with our own block-end margin.
2129 if (CheckForCollapsedBEndMarginFromClearanceLine()) {
2130 // Convert the children's carried out margin to something that
2131 // we will include in our height
2132 nonCarriedOutBDirMargin
= aState
.mPrevBEndMargin
.get();
2133 aState
.mPrevBEndMargin
.Zero();
2135 aMetrics
.mCarriedOutBEndMargin
= aState
.mPrevBEndMargin
;
2137 aMetrics
.mCarriedOutBEndMargin
.Zero();
2140 nscoord blockEndEdgeOfChildren
= aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2141 // Shrink wrap our height around our contents.
2142 if (aState
.mFlags
.mIsBEndMarginRoot
||
2143 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2144 // When we are a block-end-margin root make sure that our last
2145 // child's block-end margin is fully applied. We also do this when
2146 // we have a computed height, since in that case the carried out
2147 // margin is not going to be applied anywhere, so we should note it
2148 // here to be included in the overflow area.
2149 // Apply the margin only if there's space for it.
2150 if (blockEndEdgeOfChildren
< aState
.mReflowInput
.AvailableBSize()) {
2151 // Truncate block-end margin if it doesn't fit to our available BSize.
2152 blockEndEdgeOfChildren
=
2153 std::min(blockEndEdgeOfChildren
+ aState
.mPrevBEndMargin
.get(),
2154 aState
.mReflowInput
.AvailableBSize());
2157 if (aState
.mFlags
.mBlockNeedsFloatManager
) {
2158 // Include the float manager's state to properly account for the
2159 // block-end margin of any floated elements; e.g., inside a table cell.
2161 // Note: The block coordinate returned by ClearFloats is always greater than
2162 // or equal to blockEndEdgeOfChildren.
2163 std::tie(blockEndEdgeOfChildren
, std::ignore
) =
2164 aState
.ClearFloats(blockEndEdgeOfChildren
, StyleClear::Both
);
2167 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2168 // Note: We don't use blockEndEdgeOfChildren because it includes the
2170 const nscoord contentBSizeWithBStartBP
=
2171 aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2173 // We don't care about ApplyLineClamp's return value (the line-clamped
2174 // content BSize) in this explicit-BSize codepath, but we do still need to
2175 // call ApplyLineClamp for ellipsis markers to be placed as-needed.
2176 ApplyLineClamp(aState
.mReflowInput
, this, contentBSizeWithBStartBP
);
2178 finalSize
.BSize(wm
) = ComputeFinalBSize(aState
, contentBSizeWithBStartBP
);
2180 // If the content block-size is larger than the effective computed
2181 // block-size, we extend the block-size to contain all the content.
2182 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
2183 if (aReflowInput
.ShouldApplyAutomaticMinimumOnBlockAxis()) {
2184 // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
2185 // the content's block-size plus our border and padding..
2186 finalSize
.BSize(wm
) =
2187 std::max(finalSize
.BSize(wm
),
2188 contentBSizeWithBStartBP
+ borderPadding
.BEnd(wm
));
2191 // Don't carry out a block-end margin when our BSize is fixed.
2193 // Note: this also includes the case that aReflowInput.ComputedBSize() is
2194 // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
2195 // is replaced by the block size from aspect-ratio and inline size.
2196 aMetrics
.mCarriedOutBEndMargin
.Zero();
2198 Maybe
<nscoord
> containBSize
= ContainIntrinsicBSize(
2199 IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE
: 0);
2200 if (containBSize
&& *containBSize
!= NS_UNCONSTRAINEDSIZE
) {
2201 // If we're size-containing in block axis and we don't have a specified
2202 // block size, then our final size should actually be computed from only
2203 // our border, padding and contain-intrinsic-block-size, ignoring the
2204 // actual contents. Hence this case is a simplified version of the case
2207 // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
2208 // special case when it has 'contain-intrinsic-block-size: none', because
2209 // comboboxes implicitly honors the size-containment behavior on its
2210 // nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the
2211 // nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even
2212 // need any special content-size-ignoring behavior in its reflow method,
2213 // because that method just resolves "auto" BSize values to one
2214 // line-height rather than by measuring its contents' BSize.)
2215 nscoord contentBSize
= *containBSize
;
2217 aReflowInput
.ApplyMinMaxBSize(contentBSize
, aState
.mConsumedBSize
);
2218 aMetrics
.mCarriedOutBEndMargin
.Zero();
2219 autoBSize
+= borderPadding
.BStartEnd(wm
);
2220 finalSize
.BSize(wm
) = autoBSize
;
2221 } else if (aState
.mReflowStatus
.IsInlineBreakBefore()) {
2222 // Our parent is expected to push this frame to the next page/column so
2223 // what size we set here doesn't really matter.
2224 finalSize
.BSize(wm
) = aReflowInput
.AvailableBSize();
2225 } else if (aState
.mReflowStatus
.IsComplete()) {
2226 const nscoord lineClampedContentBlockEndEdge
=
2227 ApplyLineClamp(aReflowInput
, this, blockEndEdgeOfChildren
);
2229 const nscoord bpBStart
= borderPadding
.BStart(wm
);
2230 const nscoord contentBSize
= blockEndEdgeOfChildren
- bpBStart
;
2231 const nscoord lineClampedContentBSize
=
2232 lineClampedContentBlockEndEdge
- bpBStart
;
2234 const nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(
2235 lineClampedContentBSize
, aState
.mConsumedBSize
);
2236 if (autoBSize
!= contentBSize
) {
2237 // Our min-block-size, max-block-size, or -webkit-line-clamp value made
2238 // our bsize change. Don't carry out our kids' block-end margins.
2239 aMetrics
.mCarriedOutBEndMargin
.Zero();
2241 nscoord bSize
= autoBSize
+ borderPadding
.BStartEnd(wm
);
2242 if (MOZ_UNLIKELY(autoBSize
> contentBSize
&&
2243 bSize
> aReflowInput
.AvailableBSize() &&
2244 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
)) {
2245 // Applying `min-size` made us overflow our available size.
2246 // Clamp it and report that we're Incomplete, or BreakBefore if we have
2247 // 'break-inside: avoid' that is applicable.
2248 bSize
= aReflowInput
.AvailableBSize();
2249 if (ShouldAvoidBreakInside(aReflowInput
)) {
2250 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
2252 aState
.mReflowStatus
.SetIncomplete();
2255 finalSize
.BSize(wm
) = bSize
;
2258 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
2259 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
2260 nscoord bSize
= std::max(aState
.mBCoord
, aReflowInput
.AvailableBSize());
2261 if (aReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
2262 // This should never happen, but it does. See bug 414255
2263 bSize
= aState
.mBCoord
;
2265 const nscoord maxBSize
= aReflowInput
.ComputedMaxBSize();
2266 if (maxBSize
!= NS_UNCONSTRAINEDSIZE
&&
2267 aState
.mConsumedBSize
+ bSize
- borderPadding
.BStart(wm
) > maxBSize
) {
2268 // Compute this fragment's block-size, with the max-block-size
2269 // constraint taken into consideration.
2270 const nscoord clampedBSizeWithoutEndBP
=
2271 std::max(0, maxBSize
- aState
.mConsumedBSize
) +
2272 borderPadding
.BStart(wm
);
2273 const nscoord clampedBSize
=
2274 clampedBSizeWithoutEndBP
+ borderPadding
.BEnd(wm
);
2275 if (clampedBSize
<= aReflowInput
.AvailableBSize()) {
2276 // We actually fit after applying `max-size` so we should be
2277 // Overflow-Incomplete instead.
2278 bSize
= clampedBSize
;
2279 aState
.mReflowStatus
.SetOverflowIncomplete();
2281 // We cannot fit after applying `max-size` with our block-end BP, so
2282 // we should draw it in our next continuation.
2283 bSize
= clampedBSizeWithoutEndBP
;
2286 finalSize
.BSize(wm
) = bSize
;
2290 if (IsTrueOverflowContainer()) {
2291 if (aState
.mReflowStatus
.IsIncomplete()) {
2292 // Overflow containers can only be overflow complete.
2293 // Note that auto height overflow containers have no normal children
2294 NS_ASSERTION(finalSize
.BSize(wm
) == 0,
2295 "overflow containers must be zero-block-size");
2296 aState
.mReflowStatus
.SetOverflowIncomplete();
2298 } else if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2299 !aState
.mReflowStatus
.IsInlineBreakBefore() &&
2300 aState
.mReflowStatus
.IsComplete()) {
2301 // Currently only used for grid items, but could be used in other contexts.
2302 // The FragStretchBSizeProperty is our expected non-fragmented block-size
2303 // we should stretch to (for align-self:stretch etc). In some fragmentation
2304 // cases though, the last fragment (this frame since we're complete), needs
2305 // to have extra size applied because earlier fragments consumed too much of
2306 // our computed size due to overflowing their containing block. (E.g. this
2307 // ensures we fill the last row when a multi-row grid item is fragmented).
2309 nscoord bSize
= GetProperty(FragStretchBSizeProperty(), &found
);
2311 finalSize
.BSize(wm
) = std::max(bSize
, finalSize
.BSize(wm
));
2315 // Clamp the content size to fit within the margin-box clamp size, if any.
2316 if (MOZ_UNLIKELY(aReflowInput
.mComputeSizeFlags
.contains(
2317 ComputeSizeFlag::BClampMarginBoxMinSize
)) &&
2318 aState
.mReflowStatus
.IsComplete()) {
2320 nscoord cbSize
= GetProperty(BClampMarginBoxMinSizeProperty(), &found
);
2322 auto marginBoxBSize
=
2323 finalSize
.BSize(wm
) +
2324 aReflowInput
.ComputedLogicalMargin(wm
).BStartEnd(wm
);
2325 auto overflow
= marginBoxBSize
- cbSize
;
2327 auto contentBSize
= finalSize
.BSize(wm
) - borderPadding
.BStartEnd(wm
);
2328 auto newContentBSize
= std::max(nscoord(0), contentBSize
- overflow
);
2329 // XXXmats deal with percentages better somehow?
2330 finalSize
.BSize(wm
) -= contentBSize
- newContentBSize
;
2335 // Screen out negative block sizes --- can happen due to integer overflows :-(
2336 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
2338 if (blockEndEdgeOfChildren
!= finalSize
.BSize(wm
) - borderPadding
.BEnd(wm
)) {
2339 SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren
);
2341 RemoveProperty(BlockEndEdgeOfChildrenProperty());
2344 aMetrics
.SetSize(wm
, finalSize
);
2347 if ((ABSURD_SIZE(aMetrics
.Width()) || ABSURD_SIZE(aMetrics
.Height())) &&
2348 !GetParent()->IsAbsurdSizeAssertSuppressed()) {
2350 printf(": WARNING: desired:%d,%d\n", aMetrics
.Width(), aMetrics
.Height());
2354 return blockEndEdgeOfChildren
;
2357 void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
2358 OverflowAreas
& aOverflowAreas
, nscoord aBEndEdgeOfChildren
,
2359 const nsStyleDisplay
* aDisplay
) const {
2360 const auto wm
= GetWritingMode();
2362 // Factor in the block-end edge of the children. Child frames will be added
2363 // to the overflow area as we iterate through the lines, but their margins
2364 // won't, so we need to account for block-end margins here.
2365 // REVIEW: For now, we do this for both visual and scrollable area,
2366 // although when we make scrollable overflow area not be a subset of
2367 // visual, we can change this.
2369 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
2370 // If we are a scrolled inner frame, add our block-end padding to our
2371 // children's block-end edge.
2373 // Note: aBEndEdgeOfChildren already includes our own block-start padding
2374 // because it is relative to our block-start edge of our border-box, which
2375 // is the same as our padding-box here.
2376 MOZ_ASSERT(GetLogicalUsedBorderAndPadding(wm
) == GetLogicalUsedPadding(wm
),
2377 "A scrolled inner frame shouldn't have any border!");
2378 aBEndEdgeOfChildren
+= GetLogicalUsedPadding(wm
).BEnd(wm
);
2381 // XXX Currently, overflow areas are stored as physical rects, so we have
2382 // to handle writing modes explicitly here. If we change overflow rects
2383 // to be stored logically, this can be simplified again.
2384 if (wm
.IsVertical()) {
2385 if (wm
.IsVerticalLR()) {
2386 for (const auto otype
: AllOverflowTypes()) {
2387 if (!(aDisplay
->IsContainLayout() &&
2388 otype
== OverflowType::Scrollable
)) {
2389 // Layout containment should force all overflow to be ink (visual)
2390 // overflow, so if we're layout-contained, we only add our children's
2391 // block-end edge to the ink (visual) overflow -- not to the
2392 // scrollable overflow.
2393 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2394 o
.width
= std::max(o
.XMost(), aBEndEdgeOfChildren
) - o
.x
;
2398 for (const auto otype
: AllOverflowTypes()) {
2399 if (!(aDisplay
->IsContainLayout() &&
2400 otype
== OverflowType::Scrollable
)) {
2401 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2402 nscoord xmost
= o
.XMost();
2403 o
.x
= std::min(o
.x
, xmost
- aBEndEdgeOfChildren
);
2404 o
.width
= xmost
- o
.x
;
2409 for (const auto otype
: AllOverflowTypes()) {
2410 if (!(aDisplay
->IsContainLayout() && otype
== OverflowType::Scrollable
)) {
2411 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2412 o
.height
= std::max(o
.YMost(), aBEndEdgeOfChildren
) - o
.y
;
2418 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas
& aOverflowAreas
,
2419 nscoord aBEndEdgeOfChildren
,
2420 const nsStyleDisplay
* aDisplay
) const {
2421 // XXX_perf: This can be done incrementally. It is currently one of
2422 // the things that makes incremental reflow O(N^2).
2423 auto overflowClipAxes
= ShouldApplyOverflowClipping(aDisplay
);
2424 auto overflowClipMargin
= OverflowClipMargin(overflowClipAxes
);
2425 if (overflowClipAxes
== PhysicalAxes::Both
&&
2426 overflowClipMargin
== nsSize()) {
2430 // We rely here on our caller having called SetOverflowAreasToDesiredBounds().
2431 nsRect frameBounds
= aOverflowAreas
.ScrollableOverflow();
2433 for (const auto& line
: Lines()) {
2434 if (aDisplay
->IsContainLayout()) {
2435 // If we have layout containment, we should only consider our child's
2436 // ink overflow, leaving the scrollable regions of the parent
2438 // Note: scrollable overflow is a subset of ink overflow,
2439 // so this has the same affect as unioning the child's visual and
2440 // scrollable overflow with its parent's ink overflow.
2441 nsRect childVisualRect
= line
.InkOverflowRect();
2442 OverflowAreas childVisualArea
= OverflowAreas(childVisualRect
, nsRect());
2443 aOverflowAreas
.UnionWith(childVisualArea
);
2445 aOverflowAreas
.UnionWith(line
.GetOverflowAreas());
2449 // Factor an outside ::marker in; normally the ::marker will be factored
2450 // into the line-box's overflow areas. However, if the line is a block
2451 // line then it won't; if there are no lines, it won't. So just
2452 // factor it in anyway (it can't hurt if it was already done).
2453 // XXXldb Can we just fix GetOverflowArea instead?
2454 if (nsIFrame
* outsideMarker
= GetOutsideMarker()) {
2455 aOverflowAreas
.UnionAllWith(outsideMarker
->GetRect());
2458 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, aBEndEdgeOfChildren
, aDisplay
);
2460 if (overflowClipAxes
!= PhysicalAxes::None
) {
2461 aOverflowAreas
.ApplyClipping(frameBounds
, overflowClipAxes
,
2462 overflowClipMargin
);
2465 #ifdef NOISY_OVERFLOW_AREAS
2466 printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
2467 ToString(aOverflowAreas
.InkOverflow()).c_str(),
2468 ToString(aOverflowAreas
.ScrollableOverflow()).c_str());
2472 void nsBlockFrame::UnionChildOverflow(OverflowAreas
& aOverflowAreas
) {
2473 // We need to update the overflow areas of lines manually, as they
2474 // get cached and re-used otherwise. Lines aren't exposed as normal
2475 // frame children, so calling UnionChildOverflow alone will end up
2476 // using the old cached values.
2477 for (auto& line
: Lines()) {
2478 nsRect bounds
= line
.GetPhysicalBounds();
2479 OverflowAreas
lineAreas(bounds
, bounds
);
2481 int32_t n
= line
.GetChildCount();
2482 for (nsIFrame
* lineFrame
= line
.mFirstChild
; n
> 0;
2483 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2484 ConsiderChildOverflow(lineAreas
, lineFrame
);
2487 // Consider the overflow areas of the floats attached to the line as well
2488 if (line
.HasFloats()) {
2489 for (nsIFrame
* f
: line
.Floats()) {
2490 ConsiderChildOverflow(lineAreas
, f
);
2494 line
.SetOverflowAreas(lineAreas
);
2495 aOverflowAreas
.UnionWith(lineAreas
);
2498 // Union with child frames, skipping the principal and float lists
2499 // since we already handled those using the line boxes.
2500 nsLayoutUtils::UnionChildOverflow(
2501 this, aOverflowAreas
,
2502 {FrameChildListID::Principal
, FrameChildListID::Float
});
2505 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas
& aOverflowAreas
) {
2507 nscoord blockEndEdgeOfChildren
=
2508 GetProperty(BlockEndEdgeOfChildrenProperty(), &found
);
2510 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, blockEndEdgeOfChildren
,
2514 // Line cursor invariants depend on the overflow areas of the lines, so
2515 // we must clear the line cursor since those areas may have changed.
2517 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
2520 void nsBlockFrame::LazyMarkLinesDirty() {
2521 if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
)) {
2522 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2523 line
!= line_end
; ++line
) {
2524 int32_t n
= line
->GetChildCount();
2525 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2526 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2527 if (lineFrame
->IsSubtreeDirty()) {
2528 // NOTE: MarkLineDirty does more than just marking the line dirty.
2529 MarkLineDirty(line
, &mLines
);
2534 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
2538 void nsBlockFrame::MarkLineDirty(LineIterator aLine
,
2539 const nsLineList
* aLineList
) {
2542 aLine
->SetInvalidateTextRuns(true);
2545 IndentBy(stdout
, gNoiseIndent
);
2547 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
2551 // Mark previous line dirty if it's an inline line so that it can
2552 // maybe pullup something from the line just affected.
2553 // XXX We don't need to do this if aPrevLine ends in a break-after...
2554 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
2555 aLine
.prev()->IsInline()) {
2556 aLine
.prev()->MarkDirty();
2557 aLine
.prev()->SetInvalidateTextRuns(true);
2560 IndentBy(stdout
, gNoiseIndent
);
2562 printf(": mark prev-line %p dirty\n",
2563 static_cast<void*>(aLine
.prev().get()));
2570 * Test whether lines are certain to be aligned left so that we can make
2571 * resizing optimizations
2573 static inline bool IsAlignedLeft(StyleTextAlign aAlignment
,
2574 StyleDirection aDirection
,
2575 StyleUnicodeBidi aUnicodeBidi
,
2577 return aFrame
->IsInSVGTextSubtree() || StyleTextAlign::Left
== aAlignment
||
2578 (((StyleTextAlign::Start
== aAlignment
&&
2579 StyleDirection::Ltr
== aDirection
) ||
2580 (StyleTextAlign::End
== aAlignment
&&
2581 StyleDirection::Rtl
== aDirection
)) &&
2582 aUnicodeBidi
!= StyleUnicodeBidi::Plaintext
);
2585 void nsBlockFrame::PrepareResizeReflow(BlockReflowState
& aState
) {
2586 // See if we can try and avoid marking all the lines as dirty
2587 // FIXME(emilio): This should be writing-mode aware, I guess.
2588 bool tryAndSkipLines
=
2589 // The left content-edge must be a constant distance from the left
2591 !StylePadding()->mPadding
.Get(eSideLeft
).HasPercent();
2594 if (gDisableResizeOpt
) {
2595 tryAndSkipLines
= false;
2598 if (!tryAndSkipLines
) {
2599 IndentBy(stdout
, gNoiseIndent
);
2601 printf(": marking all lines dirty: availISize=%d\n",
2602 aState
.mReflowInput
.AvailableISize());
2607 if (tryAndSkipLines
) {
2608 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2609 nscoord newAvailISize
=
2610 aState
.mReflowInput
.ComputedLogicalBorderPadding(wm
).IStart(wm
) +
2611 aState
.mReflowInput
.ComputedISize();
2615 IndentBy(stdout
, gNoiseIndent
);
2617 printf(": trying to avoid marking all lines dirty\n");
2621 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2622 line
!= line_end
; ++line
) {
2623 // We let child blocks make their own decisions the same
2625 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2626 if (line
->IsBlock() || line
->HasFloats() ||
2627 (!isLastLine
&& !line
->HasForcedLineBreakAfter()) ||
2628 ((isLastLine
|| !line
->IsLineWrapped())) ||
2629 line
->ResizeReflowOptimizationDisabled() ||
2630 line
->IsImpactedByFloat() || (line
->IEnd() > newAvailISize
)) {
2634 #ifdef REALLY_NOISY_REFLOW
2635 if (!line
->IsBlock()) {
2636 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2637 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
2641 if (gNoisyReflow
&& !line
->IsDirty()) {
2642 IndentBy(stdout
, gNoiseIndent
+ 1);
2644 "skipped: line=%p next=%p %s %s%s%s clearTypeBefore/After=%s/%s "
2646 static_cast<void*>(line
.get()),
2648 (line
.next() != LinesEnd() ? line
.next().get() : nullptr)),
2649 line
->IsBlock() ? "block" : "inline",
2650 line
->HasForcedLineBreakAfter() ? "has-break-after " : "",
2651 line
->HasFloats() ? "has-floats " : "",
2652 line
->IsImpactedByFloat() ? "impacted " : "",
2653 line
->StyleClearToString(line
->FloatClearTypeBefore()),
2654 line
->StyleClearToString(line
->FloatClearTypeAfter()),
2660 // Mark everything dirty
2661 for (auto& line
: Lines()) {
2667 //----------------------------------------
2670 * Propagate reflow "damage" from from earlier lines to the current
2671 * line. The reflow damage comes from the following sources:
2672 * 1. The regions of float damage remembered during reflow.
2673 * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
2674 * float, either the previous reflow or now.
2676 * When entering this function, |aLine| is still at its old position and
2677 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2678 * doesn't get marked dirty and reflowed entirely).
2680 void nsBlockFrame::PropagateFloatDamage(BlockReflowState
& aState
,
2682 nscoord aDeltaBCoord
) {
2683 nsFloatManager
* floatManager
= aState
.FloatManager();
2685 (aState
.mReflowInput
.mParentReflowInput
&&
2686 aState
.mReflowInput
.mParentReflowInput
->mFloatManager
== floatManager
) ||
2687 aState
.mReflowInput
.mBlockDelta
== 0,
2688 "Bad block delta passed in");
2690 // Check to see if there are any floats; if there aren't, there can't
2691 // be any float damage
2692 if (!floatManager
->HasAnyFloats()) {
2696 // Check the damage region recorded in the float damage.
2697 if (floatManager
->HasFloatDamage()) {
2698 // Need to check mBounds *and* mCombinedArea to find intersections
2699 // with aLine's floats
2700 nscoord lineBCoordBefore
= aLine
->BStart() + aDeltaBCoord
;
2701 nscoord lineBCoordAfter
= lineBCoordBefore
+ aLine
->BSize();
2702 // Scrollable overflow should be sufficient for things that affect
2704 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2705 nsSize containerSize
= aState
.ContainerSize();
2706 LogicalRect overflow
=
2707 aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
, containerSize
);
2708 nscoord lineBCoordCombinedBefore
= overflow
.BStart(wm
) + aDeltaBCoord
;
2709 nscoord lineBCoordCombinedAfter
=
2710 lineBCoordCombinedBefore
+ overflow
.BSize(wm
);
2713 floatManager
->IntersectsDamage(lineBCoordBefore
, lineBCoordAfter
) ||
2714 floatManager
->IntersectsDamage(lineBCoordCombinedBefore
,
2715 lineBCoordCombinedAfter
);
2722 // Check if the line is moving relative to the float manager
2723 if (aDeltaBCoord
+ aState
.mReflowInput
.mBlockDelta
!= 0) {
2724 if (aLine
->IsBlock()) {
2725 // Unconditionally reflow sliding blocks; we only really need to reflow
2726 // if there's a float impacting this block, but the current float manager
2727 // makes it difficult to check that. Therefore, we let the child block
2728 // decide what it needs to reflow.
2731 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
2732 nsFlowAreaRect floatAvailableSpace
=
2733 aState
.GetFloatAvailableSpaceForBSize(aLine
->BStart() + aDeltaBCoord
,
2734 aLine
->BSize(), nullptr);
2736 #ifdef REALLY_NOISY_REFLOW
2737 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2738 wasImpactedByFloat
, floatAvailableSpace
.HasFloats());
2741 // Mark the line dirty if it was or is affected by a float
2742 // We actually only really need to reflow if the amount of impact
2743 // changes, but that's not straightforward to check
2744 if (wasImpactedByFloat
|| floatAvailableSpace
.HasFloats()) {
2751 static bool LineHasClear(nsLineBox
* aLine
) {
2752 return aLine
->IsBlock()
2753 ? (aLine
->HasForcedLineBreakBefore() ||
2754 aLine
->mFirstChild
->HasAnyStateBits(
2755 NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
2756 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
2757 : aLine
->HasFloatClearTypeAfter();
2761 * Reparent a whole list of floats from aOldParent to this block. The
2762 * floats might be taken from aOldParent's overflow list. They will be
2763 * removed from the list. They end up appended to our mFloats list.
2765 void nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
2766 nsBlockFrame
* aOldParent
,
2767 bool aReparentSiblings
) {
2769 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
2770 if (list
.NotEmpty()) {
2771 for (nsIFrame
* f
: list
) {
2772 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
2773 "CollectFloats should've removed that bit");
2774 ReparentFrame(f
, aOldParent
, this);
2776 mFloats
.AppendFrames(nullptr, std::move(list
));
2780 static void DumpLine(const BlockReflowState
& aState
, nsLineBox
* aLine
,
2781 nscoord aDeltaBCoord
, int32_t aDeltaIndent
) {
2783 if (nsBlockFrame::gNoisyReflow
) {
2784 nsRect
ovis(aLine
->InkOverflowRect());
2785 nsRect
oscr(aLine
->ScrollableOverflowRect());
2786 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
2788 "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2789 "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2790 "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2791 static_cast<void*>(aLine
), aState
.mBCoord
,
2792 aLine
->IsDirty() ? "yes" : "no", aLine
->IStart(), aLine
->BStart(),
2793 aLine
->ISize(), aLine
->BSize(), ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
2794 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
, aDeltaBCoord
,
2795 aState
.mPrevBEndMargin
.get(), aLine
->GetChildCount());
2800 static bool LinesAreEmpty(const nsLineList
& aList
) {
2801 for (const auto& line
: aList
) {
2802 if (!line
.IsEmpty()) {
2809 void nsBlockFrame::ReflowDirtyLines(BlockReflowState
& aState
) {
2810 bool keepGoing
= true;
2811 bool repositionViews
= false; // should we really need this?
2812 bool foundAnyClears
= aState
.mTrailingClearFromPIF
!= StyleClear::None
;
2813 bool willReflowAgain
= false;
2817 IndentBy(stdout
, gNoiseIndent
);
2819 printf(": reflowing dirty lines");
2820 printf(" computedISize=%d\n", aState
.mReflowInput
.ComputedISize());
2822 AutoNoisyIndenter
indent(gNoisyReflow
);
2825 bool selfDirty
= HasAnyStateBits(NS_FRAME_IS_DIRTY
) ||
2826 (aState
.mReflowInput
.IsBResize() &&
2827 HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
));
2829 // Reflow our last line if our availableBSize has increased
2830 // so that we (and our last child) pull up content as necessary
2831 if (aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2833 aState
.mReflowInput
.AvailableBSize() >
2834 GetLogicalSize().BSize(aState
.mReflowInput
.GetWritingMode())) {
2835 LineIterator lastLine
= LinesEnd();
2836 if (lastLine
!= LinesBegin()) {
2838 lastLine
->MarkDirty();
2841 // the amount by which we will slide the current line if it is not
2843 nscoord deltaBCoord
= 0;
2845 // whether we did NOT reflow the previous line and thus we need to
2846 // recompute the carried out margin before the line if we want to
2847 // reflow it or if its previous margin is dirty
2848 bool needToRecoverState
= false;
2849 // Float continuations were reflowed in ReflowPushedFloats
2850 bool reflowedFloat
=
2851 mFloats
.NotEmpty() &&
2852 mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
2853 bool lastLineMovedUp
= false;
2854 // We save up information about BR-clearance here
2855 StyleClear inlineFloatClearType
= aState
.mTrailingClearFromPIF
;
2857 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2859 // Determine if children of this frame could have breaks between them for
2862 // We need to check for paginated layout, the named-page pref, and if the
2863 // available block-size is constrained.
2865 // Note that we need to check for paginated layout as named-pages are only
2866 // used during paginated reflow. We need to additionally check for
2867 // unconstrained block-size to avoid introducing fragmentation breaks during
2868 // "measuring" reflows within an overall paginated reflow, and to avoid
2869 // fragmentation in monolithic containers like 'inline-block'.
2871 // Because we can only break for named pages using Class A breakpoints, we
2872 // also need to check that the block flow direction of the containing frame
2873 // of these items (which is this block) is parallel to that of this page.
2874 // See: https://www.w3.org/TR/css-break-3/#btw-blocks
2875 const nsPresContext
* const presCtx
= aState
.mPresContext
;
2876 const bool canBreakForPageNames
=
2877 aState
.mReflowInput
.mFlags
.mCanHaveClassABreakpoints
&&
2878 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2879 presCtx
->GetPresShell()->GetRootFrame()->GetWritingMode().IsVertical() ==
2880 GetWritingMode().IsVertical();
2882 // ReflowInput.mFlags.mCanHaveClassABreakpoints should respect the named
2883 // pages pref and presCtx->IsPaginated, so we did not explicitly check these
2884 // above when setting canBreakForPageNames.
2885 if (canBreakForPageNames
) {
2886 MOZ_ASSERT(presCtx
->IsPaginated(),
2887 "canBreakForPageNames should not be set during non-paginated "
2891 // Reflow the lines that are already ours
2892 for (; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
2893 DumpLine(aState
, line
, deltaBCoord
, 0);
2895 AutoNoisyIndenter
indent2(gNoisyReflow
);
2902 // This really sucks, but we have to look inside any blocks that have clear
2903 // elements inside them.
2904 // XXX what can we do smarter here?
2905 if (!line
->IsDirty() && line
->IsBlock() &&
2906 line
->mFirstChild
->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
2910 nsIFrame
* floatAvoidingBlock
= nullptr;
2911 if (line
->IsBlock() &&
2912 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
2913 floatAvoidingBlock
= line
->mFirstChild
;
2916 // We have to reflow the line if it's a block whose clearance
2917 // might have changed, so detect that.
2918 if (!line
->IsDirty() &&
2919 (line
->HasForcedLineBreakBefore() || floatAvoidingBlock
)) {
2920 nscoord curBCoord
= aState
.mBCoord
;
2921 // See where we would be after applying any clearance due to
2923 if (inlineFloatClearType
!= StyleClear::None
) {
2924 std::tie(curBCoord
, std::ignore
) =
2925 aState
.ClearFloats(curBCoord
, inlineFloatClearType
);
2928 auto [newBCoord
, result
] = aState
.ClearFloats(
2929 curBCoord
, line
->FloatClearTypeBefore(), floatAvoidingBlock
);
2931 if (line
->HasClearance()) {
2932 // Reflow the line if it might not have clearance anymore.
2933 if (result
== ClearFloatsResult::BCoordNoChange
2934 // aState.mBCoord is the clearance point which should be the
2935 // block-start border-edge of the block frame. If sliding the
2936 // block by deltaBCoord isn't going to put it in the predicted
2937 // position, then we'd better reflow the line.
2938 || newBCoord
!= line
->BStart() + deltaBCoord
) {
2942 // Reflow the line if the line might have clearance now.
2943 if (result
!= ClearFloatsResult::BCoordNoChange
) {
2949 // We might have to reflow a line that is after a clearing BR.
2950 if (inlineFloatClearType
!= StyleClear::None
) {
2951 std::tie(aState
.mBCoord
, std::ignore
) =
2952 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
2953 if (aState
.mBCoord
!= line
->BStart() + deltaBCoord
) {
2954 // SlideLine is not going to put the line where the clearance
2955 // put it. Reflow the line to be sure.
2958 inlineFloatClearType
= StyleClear::None
;
2961 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
2962 if (previousMarginWasDirty
) {
2963 // If the previous margin is dirty, reflow the current line
2965 line
->ClearPreviousMarginDirty();
2966 } else if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
2967 const nscoord scrollableOverflowBEnd
=
2968 LogicalRect(line
->mWritingMode
, line
->ScrollableOverflowRect(),
2969 line
->mContainerSize
)
2970 .BEnd(line
->mWritingMode
);
2971 if (scrollableOverflowBEnd
+ deltaBCoord
> aState
.ContentBEnd()) {
2972 // Lines that aren't dirty but get slid past our available block-size
2973 // constraint must be reflowed.
2978 if (!line
->IsDirty()) {
2979 const bool isPaginated
=
2980 // Last column can be reflowed unconstrained during column balancing.
2981 // Hence the additional NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR bit check
2982 // as a fail-safe fallback.
2983 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
||
2984 HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR
) ||
2985 // Table can also be reflowed unconstrained during printing.
2986 aState
.mPresContext
->IsPaginated();
2988 // We are in a paginated context, i.e. in columns or pages.
2989 const bool mayContainFloats
=
2990 line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed();
2991 if (mayContainFloats
) {
2992 // The following if-else conditions check whether this line -- which
2993 // might have floats in its subtree, or has floats as direct children,
2994 // or had floats pushed -- needs to be reflowed.
2995 if (deltaBCoord
!= 0 || aState
.mReflowInput
.IsBResize()) {
2996 // The distance to the block-end edge might have changed. Reflow the
2997 // line both because the breakpoints within its floats may have
2998 // changed and because we might have to push/pull the floats in
3001 } else if (HasPushedFloats()) {
3002 // We had pushed floats which haven't been drained by our
3003 // next-in-flow, which means our parent is currently reflowing us
3004 // again due to clearance without creating a next-in-flow for us.
3005 // Reflow the line to redo the floats split logic to correctly set
3006 // our reflow status.
3008 } else if (aState
.mReflowInput
.mFlags
.mMustReflowPlaceholders
) {
3009 // Reflow the line (that may containing a float's placeholder frame)
3010 // if our parent tells us to do so.
3012 } else if (aState
.mReflowInput
.mFlags
.mMovedBlockFragments
) {
3013 // Our parent's line containing us moved to a different fragment.
3014 // Reflow the line because the decision about whether the float fits
3015 // may be different in a different fragment.
3022 if (!line
->IsDirty()) {
3023 // See if there's any reflow damage that requires that we mark the
3025 PropagateFloatDamage(aState
, line
, deltaBCoord
);
3028 // If the container size has changed, reset mContainerSize. If the
3029 // line's writing mode is not ltr, or if the line is not left-aligned, also
3030 // mark the line dirty.
3031 if (aState
.ContainerSize() != line
->mContainerSize
) {
3032 line
->mContainerSize
= aState
.ContainerSize();
3034 const bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
3035 const auto align
= isLastLine
? StyleText()->TextAlignForLastLine()
3036 : StyleText()->mTextAlign
;
3037 if (line
->mWritingMode
.IsVertical() || line
->mWritingMode
.IsBidiRTL() ||
3038 !IsAlignedLeft(align
, StyleVisibility()->mDirection
,
3039 StyleTextReset()->mUnicodeBidi
, this)) {
3044 // Check for a page break caused by CSS named pages.
3046 // We should break for named pages when two frames meet at a class A
3047 // breakpoint, where the first frame has a different end page value to the
3048 // second frame's start page value. canBreakForPageNames is true iff
3049 // children of this frame can form class A breakpoints, and that we are not
3050 // in a measurement reflow or in a monolithic container such as
3053 // We specifically do not want to cause a page-break for named pages when
3054 // we are at the top of a page. This would otherwise happen when the
3055 // previous sibling is an nsPageBreakFrame, or all previous siblings on the
3056 // current page are zero-height. The latter may not be per-spec, but is
3057 // compatible with Chrome's implementation of named pages.
3058 const nsAtom
* nextPageName
= nullptr;
3059 bool shouldBreakForPageName
= false;
3060 if (canBreakForPageNames
&& (!aState
.mReflowInput
.mFlags
.mIsTopOfPage
||
3061 !aState
.IsAdjacentWithBStart())) {
3062 const nsIFrame
* const frame
= line
->mFirstChild
;
3063 if (!frame
->IsPlaceholderFrame()) {
3064 nextPageName
= frame
->GetStartPageValue();
3065 // Walk back to the last frame that isn't a placeholder.
3066 const nsIFrame
* prevFrame
= frame
->GetPrevSibling();
3067 while (prevFrame
&& prevFrame
->IsPlaceholderFrame()) {
3068 prevFrame
= prevFrame
->GetPrevSibling();
3070 if (prevFrame
&& prevFrame
->GetEndPageValue() != nextPageName
) {
3071 shouldBreakForPageName
= true;
3077 if (needToRecoverState
&& line
->IsDirty()) {
3078 // We need to reconstruct the block-end margin only if we didn't
3079 // reflow the previous line and we do need to reflow (or repair
3080 // the block-start position of) the next line.
3081 aState
.ReconstructMarginBefore(line
);
3084 bool reflowedPrevLine
= !needToRecoverState
;
3085 if (needToRecoverState
) {
3086 needToRecoverState
= false;
3088 // Update aState.mPrevChild as if we had reflowed all of the frames in
3090 if (line
->IsDirty()) {
3092 line
->mFirstChild
->GetPrevSibling() == line
.prev()->LastChild(),
3093 "unexpected line frames");
3094 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
3098 // Now repair the line and update |aState.mBCoord| by calling
3099 // |ReflowLine| or |SlideLine|.
3100 // If we're going to reflow everything again, then no need to reflow
3101 // the dirty line ... unless the line has floats, in which case we'd
3102 // better reflow it now to refresh its float cache, which may contain
3103 // dangling frame pointers! Ugh! This reflow of the line may be
3104 // incorrect because we skipped reflowing previous lines (e.g., floats
3105 // may be placed incorrectly), but that's OK because we'll mark the
3106 // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
3107 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
3108 lastLineMovedUp
= true;
3110 bool maybeReflowingForFirstTime
=
3111 line
->IStart() == 0 && line
->BStart() == 0 && line
->ISize() == 0 &&
3114 // Compute the dirty lines "before" BEnd, after factoring in
3115 // the running deltaBCoord value - the running value is implicit in
3117 nscoord oldB
= line
->BStart();
3118 nscoord oldBMost
= line
->BEnd();
3120 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
3121 "Don't reflow blocks while willReflowAgain is true, reflow "
3122 "of block abs-pos children depends on this");
3124 if (shouldBreakForPageName
) {
3125 // Immediately fragment for page-name. It is possible we could break
3126 // out of the loop right here, but this should make it more similar to
3127 // what happens when reflow causes fragmentation.
3128 PushTruncatedLine(aState
, line
, &keepGoing
);
3129 PresShell()->FrameConstructor()->SetNextPageContentFramePageName(
3130 nextPageName
? nextPageName
: GetAutoPageValue());
3132 // Reflow the dirty line. If it's an incremental reflow, then force
3133 // it to invalidate the dirty area if necessary
3134 ReflowLine(aState
, line
, &keepGoing
);
3137 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3139 willReflowAgain
= true;
3140 // Note that once we've entered this state, every line that gets here
3141 // (e.g. because it has floats) gets marked dirty and reflowed again.
3142 // in the next pass. This is important, see above.
3145 if (line
->HasFloats()) {
3146 reflowedFloat
= true;
3150 DumpLine(aState
, line
, deltaBCoord
, -1);
3151 if (0 == line
->GetChildCount()) {
3152 DeleteLine(aState
, line
, line_end
);
3157 // Test to see whether the margin that should be carried out
3158 // to the next line (NL) might have changed. In ReflowBlockFrame
3159 // we call nextLine->MarkPreviousMarginDirty if the block's
3160 // actual carried-out block-end margin changed. So here we only
3161 // need to worry about the following effects:
3162 // 1) the line was just created, and it might now be blocking
3163 // a carried-out block-end margin from previous lines that
3164 // used to reach NL from reaching NL
3165 // 2) the line used to be empty, and is now not empty,
3166 // thus blocking a carried-out block-end margin from previous lines
3167 // that used to reach NL from reaching NL
3168 // 3) the line wasn't empty, but now is, so a carried-out
3169 // block-end margin from previous lines that didn't used to reach NL
3171 // 4) the line might have changed in a way that affects NL's
3172 // ShouldApplyBStartMargin decision. The three things that matter
3173 // are the line's emptiness, its adjacency to the block-start edge of the
3174 // block, and whether it has clearance (the latter only matters if the
3175 // block was and is adjacent to the block-start and empty).
3177 // If the line is empty now, we can't reliably tell if the line was empty
3178 // before, so we just assume it was and do
3179 // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
3180 // redundant; if the line is empty now we don't need to check 4), but if
3181 // the line is not empty now and we're sure it wasn't empty before, any
3182 // adjacency and clearance changes are irrelevant to the result of
3183 // nextLine->ShouldApplyBStartMargin.
3184 if (line
.next() != LinesEnd()) {
3185 bool maybeWasEmpty
= oldB
== line
.next()->BStart();
3186 bool isEmpty
= line
->CachedIsEmpty();
3187 if (maybeReflowingForFirstTime
/*1*/ ||
3188 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
3189 line
.next()->MarkPreviousMarginDirty();
3190 // since it's marked dirty, nobody will care about |deltaBCoord|
3194 // If the line was just reflowed for the first time, then its
3195 // old mBounds cannot be trusted so this deltaBCoord computation is
3196 // bogus. But that's OK because we just did
3197 // MarkPreviousMarginDirty on the next line which will force it
3198 // to be reflowed, so this computation of deltaBCoord will not be
3200 deltaBCoord
= line
->BEnd() - oldBMost
;
3202 // Now do an interrupt check. We want to do this only in the case when we
3203 // actually reflow the line, so that if we get back in here we'll get
3204 // further on the reflow before interrupting.
3205 aState
.mPresContext
->CheckForInterrupt(this);
3207 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
3208 // Nop except for blocks (we don't create overflow container
3209 // continuations for any inlines atm), so only checking mFirstChild
3212 lastLineMovedUp
= deltaBCoord
< 0;
3214 if (deltaBCoord
!= 0) {
3215 SlideLine(aState
, line
, deltaBCoord
);
3217 repositionViews
= true;
3220 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
3221 "Possibly stale float cache here!");
3222 if (willReflowAgain
&& line
->IsBlock()) {
3223 // If we're going to reflow everything again, and this line is a block,
3224 // then there is no need to recover float state. The line may contain
3225 // other lines with floats, but in that case RecoverStateFrom would only
3226 // add floats to the float manager. We don't need to do that because
3227 // everything's going to get reflowed again "for real". Calling
3228 // RecoverStateFrom in this situation could be lethal because the
3229 // block's descendant lines may have float caches containing dangling
3230 // frame pointers. Ugh!
3231 // If this line is inline, then we need to recover its state now
3232 // to make sure that we don't forget to move its floats by deltaBCoord.
3234 // XXX EVIL O(N^2) EVIL
3235 aState
.RecoverStateFrom(line
, deltaBCoord
);
3238 // Keep mBCoord up to date in case we're propagating reflow damage
3239 // and also because our final height may depend on it. If the
3240 // line is inlines, then only update mBCoord if the line is not
3241 // empty, because that's what PlaceLine does. (Empty blocks may
3242 // want to update mBCoord, e.g. if they have clearance.)
3243 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
3244 aState
.mBCoord
= line
->BEnd();
3247 needToRecoverState
= true;
3249 if (reflowedPrevLine
&& !line
->IsBlock() &&
3250 aState
.mPresContext
->HasPendingInterrupt()) {
3251 // Need to make sure to pull overflows from any prev-in-flows
3252 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
3253 inlineKid
= inlineKid
->PrincipalChildList().FirstChild()) {
3254 inlineKid
->PullOverflowsFromPrevInFlow();
3259 // Record if we need to clear floats before reflowing the next
3260 // line. Note that inlineFloatClearType will be handled and
3261 // cleared before the next line is processed, so there is no
3262 // need to combine break types here.
3263 if (line
->HasFloatClearTypeAfter()) {
3264 inlineFloatClearType
= line
->FloatClearTypeAfter();
3267 if (LineHasClear(line
.get())) {
3268 foundAnyClears
= true;
3271 DumpLine(aState
, line
, deltaBCoord
, -1);
3273 if (aState
.mPresContext
->HasPendingInterrupt()) {
3274 willReflowAgain
= true;
3275 // Another option here might be to leave |line| clean if
3276 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
3277 // that case the line really did reflow as it should have. Not sure
3278 // whether that would be safe, so doing this for now instead. Also not
3279 // sure whether we really want to mark all lines dirty after an
3280 // interrupt, but until we get better at propagating float damage we
3281 // really do need to do it this way; see comments inside MarkLineDirty.
3282 MarkLineDirtyForInterrupt(line
);
3286 // Handle BR-clearance from the last line of the block
3287 if (inlineFloatClearType
!= StyleClear::None
) {
3288 std::tie(aState
.mBCoord
, std::ignore
) =
3289 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
3292 if (needToRecoverState
) {
3293 // Is this expensive?
3294 aState
.ReconstructMarginBefore(line
);
3296 // Update aState.mPrevChild as if we had reflowed all of the frames in
3298 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
3299 line
.prev()->LastChild(),
3300 "unexpected line frames");
3301 aState
.mPrevChild
= line
== line_end
? mFrames
.LastChild()
3302 : line
->mFirstChild
->GetPrevSibling();
3305 // Should we really have to do this?
3306 if (repositionViews
) {
3307 nsContainerFrame::PlaceFrameView(this);
3310 // We can skip trying to pull up the next line if our height is constrained
3311 // (so we can report being incomplete) and there is no next in flow or we
3312 // were told not to or we know it will be futile, i.e.,
3313 // -- the next in flow is not changing
3314 // -- and we cannot have added more space for its first line to be
3316 // -- it's an incremental reflow of a descendant
3317 // -- and we didn't reflow any floats (so the available space
3319 // -- my chain of next-in-flows either has no first line, or its first
3320 // line isn't dirty.
3321 bool heightConstrained
=
3322 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
;
3323 bool skipPull
= willReflowAgain
&& heightConstrained
;
3324 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
3325 (aState
.mReflowInput
.mFlags
.mNextInFlowUntouched
&& !lastLineMovedUp
&&
3326 !HasAnyStateBits(NS_FRAME_IS_DIRTY
) && !reflowedFloat
)) {
3327 // We'll place lineIter at the last line of this block, so that
3328 // nsBlockInFlowLineIterator::Next() will take us to the first
3329 // line of my next-in-flow-chain. (But first, check that I
3330 // have any lines -- if I don't, just bail out of this
3332 LineIterator lineIter
= this->LinesEnd();
3333 if (lineIter
!= this->LinesBegin()) {
3334 lineIter
--; // I have lines; step back from dummy iterator to last line.
3335 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
3337 // Check for next-in-flow-chain's first line.
3338 // (First, see if there is such a line, and second, see if it's clean)
3339 if (!bifLineIter
.Next() || !bifLineIter
.GetLine()->IsDirty()) {
3345 if (skipPull
&& aState
.mNextInFlow
) {
3346 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
3347 if (aState
.mNextInFlow
->IsTrueOverflowContainer()) {
3348 aState
.mReflowStatus
.SetOverflowIncomplete();
3350 aState
.mReflowStatus
.SetIncomplete();
3354 if (!skipPull
&& aState
.mNextInFlow
) {
3355 // Pull data from a next-in-flow if there's still room for more
3357 while (keepGoing
&& aState
.mNextInFlow
) {
3358 // Grab first line from our next-in-flow
3359 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3360 nsLineBox
* pulledLine
;
3361 nsFrameList pulledFrames
;
3362 if (!nextInFlow
->mLines
.empty()) {
3363 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
, &pulledLine
,
3366 // Grab an overflow line if there are any
3367 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
3368 if (!overflowLines
) {
3369 aState
.mNextInFlow
=
3370 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3374 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
3375 &pulledLine
, &pulledFrames
);
3377 nextInFlow
->DestroyOverflowLines();
3381 if (pulledFrames
.IsEmpty()) {
3382 // The line is empty. Try the next one.
3384 pulledLine
->GetChildCount() == 0 && !pulledLine
->mFirstChild
,
3386 nextInFlow
->FreeLineBox(pulledLine
);
3390 if (nextInFlow
->MaybeHasLineCursor()) {
3391 if (pulledLine
== nextInFlow
->GetLineCursorForDisplay()) {
3392 nextInFlow
->ClearLineCursorForDisplay();
3394 if (pulledLine
== nextInFlow
->GetLineCursorForQuery()) {
3395 nextInFlow
->ClearLineCursorForQuery();
3398 ReparentFrames(pulledFrames
, nextInFlow
, this);
3399 pulledLine
->SetMovedFragments();
3401 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
3402 "Unexpected last frame");
3403 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(),
3404 "should have a prevchild here");
3405 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
3406 "Incorrect aState.mPrevChild before inserting line at end");
3408 // Shift pulledLine's frames into our mFrames list.
3409 mFrames
.AppendFrames(nullptr, std::move(pulledFrames
));
3411 // Add line to our line list, and set its last child as our new prev-child
3412 line
= mLines
.before_insert(LinesEnd(), pulledLine
);
3413 aState
.mPrevChild
= mFrames
.LastChild();
3415 // Reparent floats whose placeholders are in the line.
3416 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
3418 DumpLine(aState
, pulledLine
, deltaBCoord
, 0);
3420 AutoNoisyIndenter
indent2(gNoisyReflow
);
3423 if (aState
.mPresContext
->HasPendingInterrupt()) {
3424 MarkLineDirtyForInterrupt(line
);
3426 // Now reflow it and any lines that it makes during it's reflow
3427 // (we have to loop here because reflowing the line may cause a new
3428 // line to be created; see SplitLine's callers for examples of
3429 // when this happens).
3430 while (line
!= LinesEnd()) {
3431 ReflowLine(aState
, line
, &keepGoing
);
3433 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3436 aState
.mReflowStatus
.SetIncomplete();
3440 DumpLine(aState
, line
, deltaBCoord
, -1);
3442 if (0 == line
->GetChildCount()) {
3443 DeleteLine(aState
, line
, line_end
);
3448 if (LineHasClear(line
.get())) {
3449 foundAnyClears
= true;
3452 if (aState
.mPresContext
->CheckForInterrupt(this)) {
3453 MarkLineDirtyForInterrupt(line
);
3457 // If this is an inline frame then its time to stop
3459 aState
.AdvanceToNextLine();
3464 if (aState
.mReflowStatus
.IsIncomplete()) {
3465 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
3466 } // XXXfr shouldn't set this flag when nextinflow has no lines
3469 // Handle an odd-ball case: a list-item with no lines
3470 if (mLines
.empty() && HasOutsideMarker()) {
3471 ReflowOutput
metrics(aState
.mReflowInput
);
3472 nsIFrame
* marker
= GetOutsideMarker();
3473 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3474 ReflowOutsideMarker(
3475 marker
, aState
, metrics
,
3476 aState
.mReflowInput
.ComputedPhysicalBorderPadding().top
);
3477 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
3478 "empty ::marker frame took up space");
3480 if (!MarkerIsEmpty()) {
3481 // There are no lines so we have to fake up some y motion so that
3482 // we end up with *some* height.
3483 // (Note: if we're layout-contained, we have to be sure to leave our
3484 // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
3485 // because layout-contained frames have no baseline.)
3486 if (!aState
.mReflowInput
.mStyleDisplay
->IsContainLayout() &&
3487 metrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
3489 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3490 if (nsLayoutUtils::GetFirstLineBaseline(wm
, marker
, &ascent
)) {
3491 metrics
.SetBlockStartAscent(ascent
);
3493 metrics
.SetBlockStartAscent(metrics
.BSize(wm
));
3497 RefPtr
<nsFontMetrics
> fm
=
3498 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
3500 nscoord minAscent
= nsLayoutUtils::GetCenteredFontBaseline(
3501 fm
, aState
.mMinLineHeight
, wm
.IsLineInverted());
3502 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
3505 std::max(minAscent
, metrics
.BlockStartAscent()) +
3506 std::max(minDescent
, metrics
.BSize(wm
) - metrics
.BlockStartAscent());
3508 nscoord offset
= minAscent
- metrics
.BlockStartAscent();
3510 marker
->SetRect(marker
->GetRect() + nsPoint(0, offset
));
3515 if (LinesAreEmpty(mLines
) && ShouldHaveLineIfEmpty()) {
3516 aState
.mBCoord
+= aState
.mMinLineHeight
;
3519 if (foundAnyClears
) {
3520 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3522 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3527 VerifyOverflowSituation();
3529 IndentBy(stdout
, gNoiseIndent
- 1);
3531 printf(": done reflowing dirty lines (status=%s)\n",
3532 ToString(aState
.mReflowStatus
).c_str());
3537 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
) {
3540 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
3541 // marked the lines that need to be marked dirty based on our
3542 // vertical resize stuff. So we'll definitely reflow all those kids;
3543 // the only question is how they should behave.
3544 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
3545 // Mark all our child frames dirty so we make sure to reflow them
3547 int32_t n
= aLine
->GetChildCount();
3548 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
3549 f
= f
->GetNextSibling(), --n
) {
3550 f
->MarkSubtreeDirty();
3552 // And mark all the floats whose reflows we might be skipping dirty too.
3553 if (aLine
->HasFloats()) {
3554 for (nsIFrame
* f
: aLine
->Floats()) {
3555 f
->MarkSubtreeDirty();
3559 // Dirty all the descendant lines of block kids to handle float damage,
3560 // since our nsFloatManager will go away by the next time we're reflowing.
3561 // XXXbz Can we do something more like what PropagateFloatDamage does?
3562 // Would need to sort out the exact business with mBlockDelta for that....
3563 // This marks way too much dirty. If we ever make this better, revisit
3564 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
3565 nsBlockFrame
* bf
= do_QueryFrame(aLine
->mFirstChild
);
3567 MarkAllDescendantLinesDirty(bf
);
3572 void nsBlockFrame::DeleteLine(BlockReflowState
& aState
,
3573 nsLineList::iterator aLine
,
3574 nsLineList::iterator aLineEnd
) {
3575 MOZ_ASSERT(0 == aLine
->GetChildCount(), "can't delete !empty line");
3576 if (0 == aLine
->GetChildCount()) {
3577 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
3578 "using function more generally than designed, "
3579 "but perhaps OK now");
3580 nsLineBox
* line
= aLine
;
3581 aLine
= mLines
.erase(aLine
);
3583 // Mark the previous margin of the next line dirty since we need to
3584 // recompute its top position.
3585 if (aLine
!= aLineEnd
) {
3586 aLine
->MarkPreviousMarginDirty();
3592 * Reflow a line. The line will either contain a single block frame
3593 * or contain 1 or more inline frames. aKeepReflowGoing indicates
3594 * whether or not the caller should continue to reflow more lines.
3596 void nsBlockFrame::ReflowLine(BlockReflowState
& aState
, LineIterator aLine
,
3597 bool* aKeepReflowGoing
) {
3598 MOZ_ASSERT(aLine
->GetChildCount(), "reflowing empty line");
3600 // Setup the line-layout for the new line
3601 aState
.mCurrentLine
= aLine
;
3602 aLine
->ClearDirty();
3603 aLine
->InvalidateCachedIsEmpty();
3604 aLine
->ClearHadFloatPushed();
3606 // If this line contains a single block that is hidden by `content-visibility`
3607 // don't reflow the line. If this line contains inlines and the first one is
3608 // hidden by `content-visibility`, all of them are, so avoid reflow in that
3610 // For frames that own anonymous children, even the first child is hidden by
3611 // `content-visibility`, there could be some anonymous children need reflow,
3612 // so we don't skip reflow this line.
3613 nsIFrame
* firstChild
= aLine
->mFirstChild
;
3614 if (firstChild
->IsHiddenByContentVisibilityOfInFlowParentForLayout() &&
3615 !HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES
)) {
3619 // Now that we know what kind of line we have, reflow it
3620 if (aLine
->IsBlock()) {
3621 ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
3623 aLine
->SetLineWrapped(false);
3624 ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
3626 // Store the line's float edges for overflow marker analysis if needed.
3627 aLine
->ClearFloatEdges();
3628 if (aState
.mFlags
.mCanHaveOverflowMarkers
) {
3629 WritingMode wm
= aLine
->mWritingMode
;
3630 nsFlowAreaRect r
= aState
.GetFloatAvailableSpaceForBSize(
3631 aLine
->BStart(), aLine
->BSize(), nullptr);
3632 if (r
.HasFloats()) {
3633 LogicalRect so
= aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
,
3634 aLine
->mContainerSize
);
3635 nscoord s
= r
.mRect
.IStart(wm
);
3636 nscoord e
= r
.mRect
.IEnd(wm
);
3637 if (so
.IEnd(wm
) > e
|| so
.IStart(wm
) < s
) {
3638 // This line is overlapping a float - store the edges marking the area
3639 // between the floats for text-overflow analysis.
3640 aLine
->SetFloatEdges(s
, e
);
3646 aLine
->ClearMovedFragments();
3649 nsIFrame
* nsBlockFrame::PullFrame(BlockReflowState
& aState
,
3650 LineIterator aLine
) {
3651 // First check our remaining lines.
3652 if (LinesEnd() != aLine
.next()) {
3653 return PullFrameFrom(aLine
, this, aLine
.next());
3657 !GetOverflowLines(),
3658 "Our overflow lines should have been removed at the start of reflow");
3660 // Try each next-in-flow.
3661 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3662 while (nextInFlow
) {
3663 if (nextInFlow
->mLines
.empty()) {
3664 nextInFlow
->DrainSelfOverflowList();
3666 if (!nextInFlow
->mLines
.empty()) {
3667 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
3669 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3670 aState
.mNextInFlow
= nextInFlow
;
3676 nsIFrame
* nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
3677 nsBlockFrame
* aFromContainer
,
3678 nsLineList::iterator aFromLine
) {
3679 nsLineBox
* fromLine
= aFromLine
;
3680 MOZ_ASSERT(fromLine
, "bad line to pull from");
3681 MOZ_ASSERT(fromLine
->GetChildCount(), "empty line");
3682 MOZ_ASSERT(aLine
->GetChildCount(), "empty line");
3683 MOZ_ASSERT(!HasProperty(LineIteratorProperty()),
3684 "Shouldn't have line iterators mid-reflow");
3686 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
3687 "Disagreement about whether it's a block or not");
3689 if (fromLine
->IsBlock()) {
3690 // If our line is not empty and the child in aFromLine is a block
3691 // then we cannot pull up the frame into this line. In this case
3695 // Take frame from fromLine
3696 nsIFrame
* frame
= fromLine
->mFirstChild
;
3697 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
3699 if (aFromContainer
!= this) {
3700 // The frame is being pulled from a next-in-flow; therefore we need to add
3701 // it to our sibling list.
3702 MOZ_ASSERT(aLine
== mLines
.back());
3703 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
3704 "should only pull from first line");
3705 aFromContainer
->mFrames
.RemoveFrame(frame
);
3707 // When pushing and pulling frames we need to check for whether any
3708 // views need to be reparented.
3709 ReparentFrame(frame
, aFromContainer
, this);
3710 mFrames
.AppendFrame(nullptr, frame
);
3712 // The frame might have (or contain) floats that need to be brought
3713 // over too. (pass 'false' since there are no siblings to check)
3714 ReparentFloats(frame
, aFromContainer
, false);
3716 MOZ_ASSERT(aLine
== aFromLine
.prev());
3719 aLine
->NoteFrameAdded(frame
);
3720 fromLine
->NoteFrameRemoved(frame
);
3722 if (fromLine
->GetChildCount() > 0) {
3723 // Mark line dirty now that we pulled a child
3724 fromLine
->MarkDirty();
3725 fromLine
->mFirstChild
= newFirstChild
;
3727 // Free up the fromLine now that it's empty.
3728 // Its bounds might need to be redrawn, though.
3729 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
3730 aFromLine
.next()->MarkPreviousMarginDirty();
3732 aFromContainer
->mLines
.erase(aFromLine
);
3733 // aFromLine is now invalid
3734 aFromContainer
->FreeLineBox(fromLine
);
3739 VerifyOverflowSituation();
3745 void nsBlockFrame::SlideLine(BlockReflowState
& aState
, nsLineBox
* aLine
,
3746 nscoord aDeltaBCoord
) {
3747 MOZ_ASSERT(aDeltaBCoord
!= 0, "why slide a line nowhere?");
3749 // Adjust line state
3750 aLine
->SlideBy(aDeltaBCoord
, aState
.ContainerSize());
3752 // Adjust the frames in the line
3753 MoveChildFramesOfLine(aLine
, aDeltaBCoord
);
3756 void nsBlockFrame::UpdateLineContainerSize(nsLineBox
* aLine
,
3757 const nsSize
& aNewContainerSize
) {
3758 if (aNewContainerSize
== aLine
->mContainerSize
) {
3762 // Adjust line state
3763 nsSize sizeDelta
= aLine
->UpdateContainerSize(aNewContainerSize
);
3765 // Changing container width only matters if writing mode is vertical-rl
3766 if (GetWritingMode().IsVerticalRL()) {
3767 MoveChildFramesOfLine(aLine
, sizeDelta
.width
);
3771 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox
* aLine
,
3772 nscoord aDeltaBCoord
) {
3773 // Adjust the frames in the line
3774 nsIFrame
* kid
= aLine
->mFirstChild
;
3779 WritingMode wm
= GetWritingMode();
3780 LogicalPoint
translation(wm
, 0, aDeltaBCoord
);
3782 if (aLine
->IsBlock()) {
3784 kid
->MovePositionBy(wm
, translation
);
3787 // Make sure the frame's view and any child views are updated
3788 nsContainerFrame::PlaceFrameView(kid
);
3790 // Adjust the block-dir coordinate of the frames in the line.
3791 // Note: we need to re-position views even if aDeltaBCoord is 0, because
3792 // one of our parent frames may have moved and so the view's position
3793 // relative to its parent may have changed.
3794 int32_t n
= aLine
->GetChildCount();
3797 kid
->MovePositionBy(wm
, translation
);
3799 // Make sure the frame's view and any child views are updated
3800 nsContainerFrame::PlaceFrameView(kid
);
3801 kid
= kid
->GetNextSibling();
3806 static inline bool IsNonAutoNonZeroBSize(const StyleSize
& aCoord
) {
3807 // The "extremum length" values (see ExtremumLength) were originally aimed at
3808 // inline-size (or width, as it was before logicalization). For now, let them
3809 // return false here, so we treat them like 'auto' pending a real
3810 // implementation. (See bug 1126420.)
3812 // FIXME (bug 567039, bug 527285) This isn't correct for the 'fill' value,
3813 // which should more likely (but not necessarily, depending on the available
3814 // space) be returning true.
3815 if (aCoord
.BehavesLikeInitialValueOnBlockAxis()) {
3818 MOZ_ASSERT(aCoord
.IsLengthPercentage());
3819 // If we evaluate the length/percent/calc at a percentage basis of
3820 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3821 // length, percent, or combination thereof. Test > 0 so we clamp
3822 // negative calc() results to 0.
3823 return aCoord
.AsLengthPercentage().Resolve(nscoord_MAX
) > 0 ||
3824 aCoord
.AsLengthPercentage().Resolve(0) > 0;
3828 bool nsBlockFrame::IsSelfEmpty() {
3829 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
3833 // Blocks which are margin-roots (including inline-blocks) cannot be treated
3834 // as empty for margin-collapsing and other purposes. They're more like
3835 // replaced elements.
3836 if (HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
3840 WritingMode wm
= GetWritingMode();
3841 const nsStylePosition
* position
= StylePosition();
3843 if (IsNonAutoNonZeroBSize(position
->MinBSize(wm
)) ||
3844 IsNonAutoNonZeroBSize(position
->BSize(wm
))) {
3848 // FIXME: Bug 1646100 - Take intrinsic size into account.
3849 // FIXME: Handle the case that both inline and block sizes are auto.
3850 // https://github.com/w3c/csswg-drafts/issues/5060.
3851 // Note: block-size could be zero or auto/intrinsic keywords here.
3852 if (position
->BSize(wm
).BehavesLikeInitialValueOnBlockAxis() &&
3853 position
->mAspectRatio
.HasFiniteRatio()) {
3857 const nsStyleBorder
* border
= StyleBorder();
3858 const nsStylePadding
* padding
= StylePadding();
3860 if (border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBStart
)) !=
3862 border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBEnd
)) != 0 ||
3863 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBStart(wm
)) ||
3864 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBEnd(wm
))) {
3868 if (HasOutsideMarker() && !MarkerIsEmpty()) {
3875 bool nsBlockFrame::CachedIsEmpty() {
3876 if (!IsSelfEmpty()) {
3879 for (auto& line
: mLines
) {
3880 if (!line
.CachedIsEmpty()) {
3887 bool nsBlockFrame::IsEmpty() {
3888 if (!IsSelfEmpty()) {
3892 return LinesAreEmpty(mLines
);
3895 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState
& aState
,
3897 if (aLine
->mFirstChild
->IsPageBreakFrame()) {
3898 // A page break frame consumes margins adjacent to it.
3899 // https://drafts.csswg.org/css-break/#break-margins
3903 if (aState
.mFlags
.mShouldApplyBStartMargin
) {
3904 // Apply short-circuit check to avoid searching the line list
3908 if (!aState
.IsAdjacentWithBStart()) {
3909 // If we aren't at the start block-coordinate then something of non-zero
3910 // height must have been placed. Therefore the childs block-start margin
3912 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3916 // Determine if this line is "essentially" the first line
3917 LineIterator line
= LinesBegin();
3918 if (aState
.mFlags
.mHasLineAdjacentToTop
) {
3919 line
= aState
.mLineAdjacentToTop
;
3921 while (line
!= aLine
) {
3922 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
3923 // A line which precedes aLine is non-empty, or has clearance,
3924 // so therefore the block-start margin applies.
3925 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3928 // No need to apply the block-start margin if the line has floats. We
3929 // should collapse anyway (bug 44419)
3931 aState
.mFlags
.mHasLineAdjacentToTop
= true;
3932 aState
.mLineAdjacentToTop
= line
;
3935 // The line being reflowed is "essentially" the first line in the
3936 // block. Therefore its block-start margin will be collapsed by the
3937 // generational collapsing logic with its parent (us).
3941 void nsBlockFrame::ReflowBlockFrame(BlockReflowState
& aState
,
3943 bool* aKeepReflowGoing
) {
3944 MOZ_ASSERT(*aKeepReflowGoing
, "bad caller");
3946 nsIFrame
* frame
= aLine
->mFirstChild
;
3948 NS_ASSERTION(false, "program error - unexpected empty line");
3952 // If the previous frame was a page-break-frame, then preemptively push this
3953 // frame to the next page.
3954 // This is primarily important for the placeholders for abspos frames, which
3955 // measure as zero height and then would be placed on this page.
3956 if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
3957 const nsIFrame
* const prev
= frame
->GetPrevSibling();
3958 if (prev
&& prev
->IsPageBreakFrame()) {
3959 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
3964 // Prepare the block reflow engine
3965 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
3967 StyleClear clearType
= frame
->StyleDisplay()->mClear
;
3968 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
3969 clearType
= nsLayoutUtils::CombineClearType(clearType
,
3970 aState
.mTrailingClearFromPIF
);
3971 aState
.mTrailingClearFromPIF
= StyleClear::None
;
3974 // Clear past floats before the block if the clear style is not none
3975 aLine
->ClearForcedLineBreak();
3976 if (clearType
!= StyleClear::None
) {
3977 aLine
->SetForcedLineBreakBefore(clearType
);
3980 // See if we should apply the block-start margin. If the block frame being
3981 // reflowed is a continuation, then we don't apply its block-start margin
3982 // because it's not significant. Otherwise, dig deeper.
3983 bool applyBStartMargin
=
3984 !frame
->GetPrevContinuation() && ShouldApplyBStartMargin(aState
, aLine
);
3985 if (applyBStartMargin
) {
3986 // The HasClearance setting is only valid if ShouldApplyBStartMargin
3987 // returned false (in which case the block-start margin-root set our
3988 // clearance flag). Otherwise clear it now. We'll set it later on
3989 // ourselves if necessary.
3990 aLine
->ClearHasClearance();
3992 bool treatWithClearance
= aLine
->HasClearance();
3994 bool mightClearFloats
= clearType
!= StyleClear::None
;
3995 nsIFrame
* floatAvoidingBlock
= nullptr;
3996 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
3997 mightClearFloats
= true;
3998 floatAvoidingBlock
= frame
;
4001 // If our block-start margin was counted as part of some parent's block-start
4002 // margin collapse, and we are being speculatively reflowed assuming this
4003 // frame DID NOT need clearance, then we need to check that
4005 if (!treatWithClearance
&& !applyBStartMargin
&& mightClearFloats
&&
4006 aState
.mReflowInput
.mDiscoveredClearance
) {
4007 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4008 if (auto [clearBCoord
, result
] =
4009 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4010 result
!= ClearFloatsResult::BCoordNoChange
) {
4011 Unused
<< clearBCoord
;
4013 // Only record the first frame that requires clearance
4014 if (!*aState
.mReflowInput
.mDiscoveredClearance
) {
4015 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4017 aState
.mPrevChild
= frame
;
4018 // Exactly what we do now is flexible since we'll definitely be
4023 if (treatWithClearance
) {
4024 applyBStartMargin
= true;
4027 nsIFrame
* clearanceFrame
= nullptr;
4028 const nscoord startingBCoord
= aState
.mBCoord
;
4029 const nsCollapsingMargin incomingMargin
= aState
.mPrevBEndMargin
;
4031 // Save the original position of the frame so that we can reposition
4032 // its view as needed.
4033 nsPoint originalPosition
= frame
->GetPosition();
4036 nscoord bStartMargin
= 0;
4037 bool mayNeedRetry
= false;
4038 bool clearedFloats
= false;
4039 bool clearedPushedOrSplitFloat
= false;
4040 if (applyBStartMargin
) {
4041 // Precompute the blocks block-start margin value so that we can get the
4042 // correct available space (there might be a float that's
4043 // already been placed below the aState.mPrevBEndMargin
4045 // Setup a reflowInput to get the style computed block-start margin
4046 // value. We'll use a reason of `resize' so that we don't fudge
4047 // any incremental reflow input.
4049 // The availSpace here is irrelevant to our needs - all we want
4050 // out if this setup is the block-start margin value which doesn't depend
4051 // on the childs available space.
4052 // XXX building a complete ReflowInput just to get the block-start
4053 // margin seems like a waste. And we do this for almost every block!
4054 WritingMode wm
= frame
->GetWritingMode();
4055 LogicalSize availSpace
= aState
.ContentSize(wm
);
4056 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4059 if (treatWithClearance
) {
4060 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4061 aState
.mPrevBEndMargin
.Zero();
4064 // Now compute the collapsed margin-block-start value into
4065 // aState.mPrevBEndMargin, assuming that all child margins
4066 // collapse down to clearanceFrame.
4067 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4068 clearanceFrame
, &mayNeedRetry
);
4070 // XXX optimization; we could check the collapsing children to see if they
4071 // are sure to require clearance, and so avoid retrying them
4073 if (clearanceFrame
) {
4074 // Don't allow retries on the second pass. The clearance decisions for
4075 // the blocks whose block-start margins collapse with ours are now
4077 mayNeedRetry
= false;
4080 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
4081 // We don't know if we need clearance and this is the first,
4082 // optimistic pass. So determine whether *this block* needs
4083 // clearance. Note that we do not allow the decision for whether
4084 // this block has clearance to change on the second pass; that
4085 // decision is only allowed to be made under the optimistic
4087 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4088 if (auto [clearBCoord
, result
] =
4089 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4090 result
!= ClearFloatsResult::BCoordNoChange
) {
4091 Unused
<< clearBCoord
;
4093 // Looks like we need clearance and we didn't know about it already.
4094 // So recompute collapsed margin
4095 treatWithClearance
= true;
4096 // Remember this decision, needed for incremental reflow
4097 aLine
->SetHasClearance();
4099 // Apply incoming margins
4100 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4101 aState
.mPrevBEndMargin
.Zero();
4103 // Compute the collapsed margin again, ignoring the incoming margin
4105 mayNeedRetry
= false;
4106 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4107 clearanceFrame
, &mayNeedRetry
);
4111 // Temporarily advance the running block-direction value so that the
4112 // GetFloatAvailableSpace method will return the right available space.
4113 // This undone as soon as the horizontal margins are computed.
4114 bStartMargin
= aState
.mPrevBEndMargin
.get();
4116 if (treatWithClearance
) {
4117 nscoord currentBCoord
= aState
.mBCoord
;
4118 // advance mBCoord to the clear position.
4119 auto [clearBCoord
, result
] =
4120 aState
.ClearFloats(aState
.mBCoord
, clearType
, floatAvoidingBlock
);
4121 aState
.mBCoord
= clearBCoord
;
4123 clearedFloats
= result
!= ClearFloatsResult::BCoordNoChange
;
4124 clearedPushedOrSplitFloat
=
4125 result
== ClearFloatsResult::FloatsPushedOrSplit
;
4127 // Compute clearance. It's the amount we need to add to the block-start
4128 // border-edge of the frame, after applying collapsed margins
4129 // from the frame and its children, to get it to line up with
4130 // the block-end of the floats. The former is
4131 // currentBCoord + bStartMargin, the latter is the current
4133 // Note that negative clearance is possible
4134 clearance
= aState
.mBCoord
- (currentBCoord
+ bStartMargin
);
4136 // Add clearance to our block-start margin while we compute available
4137 // space for the frame
4138 bStartMargin
+= clearance
;
4140 // Note that aState.mBCoord should stay where it is: at the block-start
4141 // border-edge of the frame
4143 // Advance aState.mBCoord to the block-start border-edge of the frame.
4144 aState
.mBCoord
+= bStartMargin
;
4148 aLine
->SetLineIsImpactedByFloat(false);
4150 // Here aState.mBCoord is the block-start border-edge of the block.
4151 // Compute the available space for the block
4152 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4153 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
4154 LogicalRect availSpace
= aState
.ComputeBlockAvailSpace(
4155 frame
, floatAvailableSpace
, (floatAvoidingBlock
));
4158 // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
4159 // is to some degree out of paranoia: if we reliably eat up block-start
4160 // margins at the top of the page as we ought to, it wouldn't be
4162 if ((!aState
.mReflowInput
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
4163 (availSpace
.BSize(wm
) < 0 || clearedPushedOrSplitFloat
)) {
4164 // We know already that this child block won't fit on this
4165 // page/column due to the block-start margin or the clearance. So we
4166 // need to get out of here now. (If we don't, most blocks will handle
4167 // things fine, and report break-before, but zero-height blocks
4168 // won't, and will thus make their parent overly-large and force
4169 // *it* to be pushed in its entirety.)
4170 aState
.mBCoord
= startingBCoord
;
4171 aState
.mPrevBEndMargin
= incomingMargin
;
4172 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4173 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4175 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4180 // Now put the block-dir coordinate back to the start of the
4181 // block-start-margin + clearance.
4182 aState
.mBCoord
-= bStartMargin
;
4183 availSpace
.BStart(wm
) -= bStartMargin
;
4184 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.BSize(wm
)) {
4185 availSpace
.BSize(wm
) += bStartMargin
;
4188 // Construct the reflow input for the block.
4189 Maybe
<ReflowInput
> childReflowInput
;
4190 Maybe
<LogicalSize
> cbSize
;
4191 LogicalSize availSize
= availSpace
.Size(wm
);
4192 bool columnSetWrapperHasNoBSizeLeft
= false;
4193 if (Style()->GetPseudoType() == PseudoStyleType::columnContent
) {
4194 // Calculate the multicol containing block's block size so that the
4195 // children with percentage block size get correct percentage basis.
4196 const ReflowInput
* cbReflowInput
=
4197 aState
.mReflowInput
.mParentReflowInput
->mCBReflowInput
;
4198 MOZ_ASSERT(cbReflowInput
->mFrame
->StyleColumn()->IsColumnContainerStyle(),
4199 "Get unexpected reflow input of multicol containing block!");
4201 // Use column-width as the containing block's inline-size, i.e. the column
4202 // content's computed inline-size.
4203 cbSize
.emplace(LogicalSize(wm
, aState
.mReflowInput
.ComputedISize(),
4204 cbReflowInput
->ComputedBSize())
4205 .ConvertTo(frame
->GetWritingMode(), wm
));
4207 // If a ColumnSetWrapper is in a balancing column content, it may be
4208 // pushed or pulled back and forth between column contents. Always add
4209 // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
4210 // can have a chance to reflow under current block size constraint.
4211 if (aState
.mReflowInput
.mFlags
.mIsColumnBalancing
&&
4212 frame
->IsColumnSetWrapperFrame()) {
4213 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4215 } else if (IsColumnSetWrapperFrame()) {
4216 // If we are reflowing our ColumnSet children, we want to apply our block
4217 // size constraint to the available block size when constructing reflow
4218 // input for ColumnSet so that ColumnSet can use it to compute its max
4219 // column block size.
4220 if (frame
->IsColumnSetFrame()) {
4221 nscoord contentBSize
= aState
.mReflowInput
.ComputedBSize();
4222 if (aState
.mReflowInput
.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE
) {
4224 std::min(contentBSize
, aState
.mReflowInput
.ComputedMaxBSize());
4226 if (contentBSize
!= NS_UNCONSTRAINEDSIZE
) {
4227 // To get the remaining content block-size, subtract the content
4228 // block-size consumed by our previous continuations.
4229 contentBSize
-= aState
.mConsumedBSize
;
4231 // ColumnSet is not the outermost frame in the column container, so it
4232 // cannot have any margin. We don't need to consider any margin that
4233 // can be generated by "box-decoration-break: clone" as we do in
4234 // BlockReflowState::ComputeBlockAvailSpace().
4235 const nscoord availContentBSize
= std::max(
4236 0, contentBSize
- (aState
.mBCoord
- aState
.ContentBStart()));
4237 if (availSize
.BSize(wm
) >= availContentBSize
) {
4238 availSize
.BSize(wm
) = availContentBSize
;
4239 columnSetWrapperHasNoBSizeLeft
= true;
4245 childReflowInput
.emplace(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4246 availSize
.ConvertTo(frame
->GetWritingMode(), wm
),
4249 childReflowInput
->mFlags
.mColumnSetWrapperHasNoBSizeLeft
=
4250 columnSetWrapperHasNoBSizeLeft
;
4252 if (aLine
->MovedFragments()) {
4253 // We only need to set this the first reflow, since if we reflow
4254 // again (and replace childReflowInput) we'll be reflowing it
4255 // again in the same fragment as the previous time.
4256 childReflowInput
->mFlags
.mMovedBlockFragments
= true;
4259 nsFloatManager::SavedState floatManagerState
;
4260 nsReflowStatus frameReflowStatus
;
4262 if (floatAvailableSpace
.HasFloats()) {
4263 // Set if floatAvailableSpace.HasFloats() is true for any
4264 // iteration of the loop.
4265 aLine
->SetLineIsImpactedByFloat(true);
4268 // We might need to store into mDiscoveredClearance later if it's
4269 // currently null; we want to overwrite any writes that
4270 // brc.ReflowBlock() below does, so we need to remember now
4271 // whether it's empty.
4272 const bool shouldStoreClearance
=
4273 aState
.mReflowInput
.mDiscoveredClearance
&&
4274 !*aState
.mReflowInput
.mDiscoveredClearance
;
4276 // Reflow the block into the available space
4277 if (mayNeedRetry
|| floatAvoidingBlock
) {
4278 aState
.FloatManager()->PushState(&floatManagerState
);
4282 childReflowInput
->mDiscoveredClearance
= &clearanceFrame
;
4283 } else if (!applyBStartMargin
) {
4284 childReflowInput
->mDiscoveredClearance
=
4285 aState
.mReflowInput
.mDiscoveredClearance
;
4288 frameReflowStatus
.Reset();
4289 brc
.ReflowBlock(availSpace
, applyBStartMargin
, aState
.mPrevBEndMargin
,
4290 clearance
, aLine
.get(), *childReflowInput
,
4291 frameReflowStatus
, aState
);
4293 if (frameReflowStatus
.IsInlineBreakBefore()) {
4294 // No need to retry this loop if there is a break opportunity before the
4299 // Now the block has a height. Using that height, get the
4300 // available space again and call ComputeBlockAvailSpace again.
4301 // If ComputeBlockAvailSpace gives a different result, we need to
4303 if (!floatAvoidingBlock
) {
4307 LogicalRect
oldFloatAvailableSpaceRect(floatAvailableSpace
.mRect
);
4308 floatAvailableSpace
= aState
.GetFloatAvailableSpaceForBSize(
4309 aState
.mBCoord
+ bStartMargin
, brc
.GetMetrics().BSize(wm
),
4310 &floatManagerState
);
4311 NS_ASSERTION(floatAvailableSpace
.mRect
.BStart(wm
) ==
4312 oldFloatAvailableSpaceRect
.BStart(wm
),
4314 // Restore the height to the position of the next band.
4315 floatAvailableSpace
.mRect
.BSize(wm
) =
4316 oldFloatAvailableSpaceRect
.BSize(wm
);
4317 // Determine whether the available space shrunk on either side,
4318 // because (the first time round) we now know the block's height,
4319 // and it may intersect additional floats, or (on later
4320 // iterations) because narrowing the width relative to the
4321 // previous time may cause the block to become taller. Note that
4322 // since we're reflowing the block, narrowing the width might also
4323 // make it shorter, so we must pass aCanGrow as true.
4324 if (!AvailableSpaceShrunk(wm
, oldFloatAvailableSpaceRect
,
4325 floatAvailableSpace
.mRect
, true)) {
4326 // The size and position we chose before are fine (i.e., they
4327 // don't cause intersecting with floats that requires a change
4328 // in size or position), so we're done.
4332 bool advanced
= false;
4333 if (!aState
.FloatAvoidingBlockFitsInAvailSpace(floatAvoidingBlock
,
4334 floatAvailableSpace
)) {
4335 // Advance to the next band.
4336 nscoord newBCoord
= aState
.mBCoord
;
4337 if (aState
.AdvanceToNextBand(floatAvailableSpace
.mRect
, &newBCoord
)) {
4340 // ClearFloats might be able to advance us further once we're there.
4341 std::tie(aState
.mBCoord
, std::ignore
) =
4342 aState
.ClearFloats(newBCoord
, StyleClear::None
, floatAvoidingBlock
);
4344 // Start over with a new available space rect at the new height.
4345 floatAvailableSpace
= aState
.GetFloatAvailableSpaceWithState(
4346 aState
.mBCoord
, ShapeType::ShapeOutside
, &floatManagerState
);
4349 const LogicalRect oldAvailSpace
= availSpace
;
4350 availSpace
= aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
4351 (floatAvoidingBlock
));
4353 if (!advanced
&& availSpace
.IsEqualEdges(oldAvailSpace
)) {
4357 // We need another reflow.
4358 aState
.FloatManager()->PopState(&floatManagerState
);
4360 if (!treatWithClearance
&& !applyBStartMargin
&&
4361 aState
.mReflowInput
.mDiscoveredClearance
) {
4362 // We set shouldStoreClearance above to record only the first
4363 // frame that requires clearance.
4364 if (shouldStoreClearance
) {
4365 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4367 aState
.mPrevChild
= frame
;
4368 // Exactly what we do now is flexible since we'll definitely be
4374 // We're pushing down the border-box, so we don't apply margin anymore.
4375 // This should never cause us to move up since the call to
4376 // GetFloatAvailableSpaceForBSize above included the margin.
4377 applyBStartMargin
= false;
4379 treatWithClearance
= true; // avoid hitting test above
4383 childReflowInput
.reset();
4384 childReflowInput
.emplace(
4385 aState
.mPresContext
, aState
.mReflowInput
, frame
,
4386 availSpace
.Size(wm
).ConvertTo(frame
->GetWritingMode(), wm
));
4389 if (mayNeedRetry
&& clearanceFrame
) {
4390 // Found a clearance frame, so we need to reflow |frame| a second time.
4391 // Restore the states and start over again.
4392 aState
.FloatManager()->PopState(&floatManagerState
);
4393 aState
.mBCoord
= startingBCoord
;
4394 aState
.mPrevBEndMargin
= incomingMargin
;
4398 aState
.mPrevChild
= frame
;
4400 if (childReflowInput
->WillReflowAgainForClearance()) {
4401 // If an ancestor of ours is going to reflow for clearance, we
4402 // need to avoid calling PlaceBlock, because it unsets dirty bits
4403 // on the child block (both itself, and through its call to
4404 // nsIFrame::DidReflow), and those dirty bits imply dirtiness for
4405 // all of the child block, including the lines it didn't reflow.
4406 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
4407 "we need to call PositionChildViews");
4411 #if defined(REFLOW_STATUS_COVERAGE)
4412 RecordReflowStatus(true, frameReflowStatus
);
4415 if (frameReflowStatus
.IsInlineBreakBefore()) {
4416 // None of the child block fits.
4417 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4418 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4420 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4423 // Note: line-break-after a block is a nop
4425 // Try to place the child block.
4426 // Don't force the block to fit if we have positive clearance, because
4427 // pushing it to the next page would give it more room.
4428 // Don't force the block to fit if it's impacted by a float. If it is,
4429 // then pushing it to the next page would give it more room. Note that
4430 // isImpacted doesn't include impact from the block's own floats.
4431 bool forceFit
= aState
.IsAdjacentWithBStart() && clearance
<= 0 &&
4432 !floatAvailableSpace
.HasFloats();
4433 nsCollapsingMargin collapsedBEndMargin
;
4434 OverflowAreas overflowAreas
;
4436 brc
.PlaceBlock(*childReflowInput
, forceFit
, aLine
.get(),
4437 collapsedBEndMargin
, overflowAreas
, frameReflowStatus
);
4438 if (!frameReflowStatus
.IsFullyComplete() &&
4439 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4440 *aKeepReflowGoing
= false;
4444 if (aLine
->SetCarriedOutBEndMargin(collapsedBEndMargin
)) {
4445 LineIterator nextLine
= aLine
;
4447 if (nextLine
!= LinesEnd()) {
4448 nextLine
->MarkPreviousMarginDirty();
4452 aLine
->SetOverflowAreas(overflowAreas
);
4453 if (*aKeepReflowGoing
) {
4454 // Some of the child block fit
4456 // Advance to new Y position
4457 nscoord newBCoord
= aLine
->BEnd();
4458 aState
.mBCoord
= newBCoord
;
4460 // Continue the block frame now if it didn't completely fit in
4461 // the available space.
4462 if (!frameReflowStatus
.IsFullyComplete()) {
4463 bool madeContinuation
= CreateContinuationFor(aState
, nullptr, frame
);
4465 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
4466 NS_ASSERTION(nextFrame
,
4467 "We're supposed to have a next-in-flow by now");
4469 if (frameReflowStatus
.IsIncomplete()) {
4470 // If nextFrame used to be an overflow container, make it a normal
4472 if (!madeContinuation
&&
4473 nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4474 nsOverflowContinuationTracker::AutoFinish
fini(
4475 aState
.mOverflowTracker
, frame
);
4476 nsContainerFrame
* parent
= nextFrame
->GetParent();
4477 parent
->StealFrame(nextFrame
);
4478 if (parent
!= this) {
4479 ReparentFrame(nextFrame
, parent
, this);
4481 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
4482 madeContinuation
= true; // needs to be added to mLines
4483 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4484 frameReflowStatus
.SetNextInFlowNeedsReflow();
4487 // Push continuation to a new line, but only if we actually made
4489 if (madeContinuation
) {
4490 nsLineBox
* line
= NewLineBox(nextFrame
, true);
4491 mLines
.after_insert(aLine
, line
);
4494 PushTruncatedLine(aState
, aLine
.next(), aKeepReflowGoing
);
4496 // If we need to reflow the continuation of the block child,
4497 // then we'd better reflow our continuation
4498 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4499 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4500 // We also need to make that continuation's line dirty so
4501 // it gets reflowed when we reflow our next in flow. The
4502 // nif's line must always be either a line of the nif's
4503 // parent block (only if we didn't make a continuation) or
4504 // else one of our own overflow lines. In the latter case
4505 // the line is already marked dirty, so just handle the
4507 if (!madeContinuation
) {
4508 nsBlockFrame
* nifBlock
= do_QueryFrame(nextFrame
->GetParent());
4511 "A block's child's next in flow's parent must be a block!");
4512 for (auto& line
: nifBlock
->Lines()) {
4513 if (line
.Contains(nextFrame
)) {
4521 // The block-end margin for a block is only applied on the last
4522 // flow block. Since we just continued the child block frame,
4523 // we know that line->mFirstChild is not the last flow block
4524 // therefore zero out the running margin value.
4525 #ifdef NOISY_BLOCK_DIR_MARGINS
4527 printf(": reflow incomplete, frame=");
4528 frame
->ListTag(stdout
);
4529 printf(" prevBEndMargin=%d, setting to zero\n",
4530 aState
.mPrevBEndMargin
.get());
4532 aState
.mPrevBEndMargin
.Zero();
4533 } else { // frame is complete but its overflow is not complete
4534 // Disconnect the next-in-flow and put it in our overflow tracker
4535 if (!madeContinuation
&&
4536 !nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4537 // It already exists, but as a normal next-in-flow, so we need
4538 // to dig it out of the child lists.
4539 nextFrame
->GetParent()->StealFrame(nextFrame
);
4540 } else if (madeContinuation
) {
4541 mFrames
.RemoveFrame(nextFrame
);
4544 // Put it in our overflow list
4545 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
4546 aState
.mReflowStatus
.MergeCompletionStatusFrom(frameReflowStatus
);
4548 #ifdef NOISY_BLOCK_DIR_MARGINS
4550 printf(": reflow complete but overflow incomplete for ");
4551 frame
->ListTag(stdout
);
4552 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4553 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4555 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4557 } else { // frame is fully complete
4558 #ifdef NOISY_BLOCK_DIR_MARGINS
4560 printf(": reflow complete for ");
4561 frame
->ListTag(stdout
);
4562 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4563 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4565 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4567 #ifdef NOISY_BLOCK_DIR_MARGINS
4570 frame
->ListTag(stdout
);
4571 printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
4572 brc
.GetCarriedOutBEndMargin().get(), collapsedBEndMargin
.get(),
4573 aState
.mPrevBEndMargin
.get());
4576 if (!frameReflowStatus
.IsFullyComplete()) {
4577 // The frame reported an incomplete status, but then it also didn't
4578 // fit. This means we need to reflow it again so that it can
4579 // (again) report the incomplete status.
4580 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4583 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
4584 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4585 // If it's our very first line *or* we're not at the top of the page
4586 // and we have page-break-inside:avoid, then we need to be pushed to
4587 // our parent's next-in-flow.
4588 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4590 // Push the line that didn't fit and any lines that follow it
4591 // to our next-in-flow.
4592 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4596 break; // out of the reflow retry loop
4599 // Now that we've got its final position all figured out, position any child
4600 // views it may have. Note that the case when frame has a view got handled
4601 // by FinishReflowChild, but that function didn't have the coordinates needed
4602 // to correctly decide whether to reposition child views.
4603 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
4604 nsContainerFrame::PositionChildViews(frame
);
4612 void nsBlockFrame::ReflowInlineFrames(BlockReflowState
& aState
,
4614 bool* aKeepReflowGoing
) {
4615 *aKeepReflowGoing
= true;
4617 aLine
->SetLineIsImpactedByFloat(false);
4619 // Setup initial coordinate system for reflowing the inline frames
4620 // into. Apply a previous block frame's block-end margin first.
4621 if (ShouldApplyBStartMargin(aState
, aLine
)) {
4622 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4624 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4626 LineReflowStatus lineReflowStatus
;
4628 nscoord availableSpaceBSize
= 0;
4629 aState
.mLineBSize
.reset();
4631 bool allowPullUp
= true;
4632 nsIFrame
* forceBreakInFrame
= nullptr;
4633 int32_t forceBreakOffset
= -1;
4634 gfxBreakPriority forceBreakPriority
= gfxBreakPriority::eNoBreak
;
4636 nsFloatManager::SavedState floatManagerState
;
4637 aState
.FloatManager()->PushState(&floatManagerState
);
4639 // Once upon a time we allocated the first 30 nsLineLayout objects
4640 // on the stack, and then we switched to the heap. At that time
4641 // these objects were large (1100 bytes on a 32 bit system).
4642 // Then the nsLineLayout object was shrunk to 156 bytes by
4643 // removing some internal buffers. Given that it is so much
4644 // smaller, the complexity of 2 different ways of allocating
4645 // no longer makes sense. Now we always allocate on the stack.
4646 nsLineLayout
lineLayout(aState
.mPresContext
, aState
.FloatManager(),
4647 aState
.mReflowInput
, &aLine
, nullptr);
4648 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
4649 if (forceBreakInFrame
) {
4650 lineLayout
.ForceBreakAtPosition(forceBreakInFrame
, forceBreakOffset
);
4652 DoReflowInlineFrames(aState
, lineLayout
, aLine
, floatAvailableSpace
,
4653 availableSpaceBSize
, &floatManagerState
,
4654 aKeepReflowGoing
, &lineReflowStatus
, allowPullUp
);
4655 lineLayout
.EndLineReflow();
4657 if (LineReflowStatus::RedoNoPull
== lineReflowStatus
||
4658 LineReflowStatus::RedoMoreFloats
== lineReflowStatus
||
4659 LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4660 if (lineLayout
.NeedsBackup()) {
4661 NS_ASSERTION(!forceBreakInFrame
,
4662 "Backing up twice; this should never be necessary");
4663 // If there is no saved break position, then this will set
4664 // set forceBreakInFrame to null and we won't back up, which is
4666 forceBreakInFrame
= lineLayout
.GetLastOptionalBreakPosition(
4667 &forceBreakOffset
, &forceBreakPriority
);
4669 forceBreakInFrame
= nullptr;
4671 // restore the float manager state
4672 aState
.FloatManager()->PopState(&floatManagerState
);
4673 // Clear out float lists
4674 aState
.mCurrentLineFloats
.Clear();
4675 aState
.mBelowCurrentLineFloats
.Clear();
4676 aState
.mNoWrapFloats
.Clear();
4679 // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4680 allowPullUp
= false;
4681 } while (LineReflowStatus::RedoNoPull
== lineReflowStatus
);
4682 } while (LineReflowStatus::RedoMoreFloats
== lineReflowStatus
);
4683 } while (LineReflowStatus::RedoNextBand
== lineReflowStatus
);
4686 void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState
& aState
,
4688 bool* aKeepReflowGoing
) {
4689 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4690 // Reflow the line again when we reflow at our new position.
4692 *aKeepReflowGoing
= false;
4695 void nsBlockFrame::PushTruncatedLine(BlockReflowState
& aState
,
4697 bool* aKeepReflowGoing
) {
4698 PushLines(aState
, aLine
.prev());
4699 *aKeepReflowGoing
= false;
4700 aState
.mReflowStatus
.SetIncomplete();
4703 void nsBlockFrame::DoReflowInlineFrames(
4704 BlockReflowState
& aState
, nsLineLayout
& aLineLayout
, LineIterator aLine
,
4705 nsFlowAreaRect
& aFloatAvailableSpace
, nscoord
& aAvailableSpaceBSize
,
4706 nsFloatManager::SavedState
* aFloatStateBeforeLine
, bool* aKeepReflowGoing
,
4707 LineReflowStatus
* aLineReflowStatus
, bool aAllowPullUp
) {
4708 // Forget all of the floats on the line
4709 aLine
->ClearFloats();
4710 aState
.mFloatOverflowAreas
.Clear();
4712 // We need to set this flag on the line if any of our reflow passes
4713 // are impacted by floats.
4714 if (aFloatAvailableSpace
.HasFloats()) {
4715 aLine
->SetLineIsImpactedByFloat(true);
4717 #ifdef REALLY_NOISY_REFLOW
4718 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4719 aFloatAvailableSpace
.HasFloats());
4722 WritingMode outerWM
= aState
.mReflowInput
.GetWritingMode();
4723 WritingMode lineWM
= WritingModeForLine(outerWM
, aLine
->mFirstChild
);
4724 LogicalRect lineRect
= aFloatAvailableSpace
.mRect
.ConvertTo(
4725 lineWM
, outerWM
, aState
.ContainerSize());
4727 nscoord iStart
= lineRect
.IStart(lineWM
);
4728 nscoord availISize
= lineRect
.ISize(lineWM
);
4730 if (aState
.mReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
4731 availBSize
= NS_UNCONSTRAINEDSIZE
;
4733 /* XXX get the height right! */
4734 availBSize
= lineRect
.BSize(lineWM
);
4737 // Make sure to enable resize optimization before we call BeginLineReflow
4738 // because it might get disabled there
4739 aLine
->EnableResizeReflowOptimization();
4741 aLineLayout
.BeginLineReflow(
4742 iStart
, aState
.mBCoord
, availISize
, availBSize
,
4743 aFloatAvailableSpace
.HasFloats(), false, /*XXX isTopOfPage*/
4744 lineWM
, aState
.mContainerSize
, aState
.mInsetForBalance
);
4746 aState
.mFlags
.mIsLineLayoutEmpty
= false;
4748 // XXX Unfortunately we need to know this before reflowing the first
4749 // inline frame in the line. FIX ME.
4750 if (0 == aLineLayout
.GetLineNumber() &&
4751 HasAllStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
4752 NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
4753 aLineLayout
.SetFirstLetterStyleOK(true);
4755 NS_ASSERTION(!(HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
) &&
4756 GetPrevContinuation()),
4757 "first letter child bit should only be on first continuation");
4759 // Reflow the frames that are already on the line first
4760 LineReflowStatus lineReflowStatus
= LineReflowStatus::OK
;
4762 nsIFrame
* frame
= aLine
->mFirstChild
;
4764 if (aFloatAvailableSpace
.HasFloats()) {
4765 // There is a soft break opportunity at the start of the line, because
4766 // we can always move this line down below float(s).
4767 if (aLineLayout
.NotifyOptionalBreakPosition(
4768 frame
, 0, true, gfxBreakPriority::eNormalBreak
)) {
4769 lineReflowStatus
= LineReflowStatus::RedoNextBand
;
4773 // need to repeatedly call GetChildCount here, because the child
4774 // count can change during the loop!
4776 LineReflowStatus::OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
4777 i
++, frame
= frame
->GetNextSibling()) {
4778 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4779 if (LineReflowStatus::OK
!= lineReflowStatus
) {
4780 // It is possible that one or more of next lines are empty
4781 // (because of DeleteNextInFlowChild). If so, delete them now
4782 // in case we are finished.
4784 while ((aLine
!= LinesEnd()) && (0 == aLine
->GetChildCount())) {
4785 // XXX Is this still necessary now that DeleteNextInFlowChild
4786 // uses DoRemoveFrame?
4787 nsLineBox
* toremove
= aLine
;
4788 aLine
= mLines
.erase(aLine
);
4789 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
4790 FreeLineBox(toremove
);
4794 NS_ASSERTION(lineReflowStatus
!= LineReflowStatus::Truncated
,
4795 "ReflowInlineFrame should never determine that a line "
4796 "needs to go to the next page/column");
4800 // Don't pull up new frames into lines with continuation placeholders
4802 // Pull frames and reflow them until we can't
4803 while (LineReflowStatus::OK
== lineReflowStatus
) {
4804 frame
= PullFrame(aState
, aLine
);
4809 while (LineReflowStatus::OK
== lineReflowStatus
) {
4810 int32_t oldCount
= aLine
->GetChildCount();
4811 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4812 if (aLine
->GetChildCount() != oldCount
) {
4813 // We just created a continuation for aFrame AND its going
4814 // to end up on this line (e.g. :first-letter
4815 // situation). Therefore we have to loop here before trying
4816 // to pull another frame.
4817 frame
= frame
->GetNextSibling();
4825 aState
.mFlags
.mIsLineLayoutEmpty
= aLineLayout
.LineIsEmpty();
4827 // We only need to backup if the line isn't going to be reflowed again anyway
4828 bool needsBackup
= aLineLayout
.NeedsBackup() &&
4829 (lineReflowStatus
== LineReflowStatus::Stop
||
4830 lineReflowStatus
== LineReflowStatus::OK
);
4831 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
4833 "We shouldn't be backing up more than once! "
4834 "Someone must have set a break opportunity beyond the available width, "
4835 "even though there were better break opportunities before it");
4836 needsBackup
= false;
4839 // We need to try backing up to before a text run
4840 // XXX It's possible, in fact not unusual, for the break opportunity to
4841 // already be the end of the line. We should detect that and optimize to not
4843 if (aLineLayout
.HasOptionalBreakPosition()) {
4845 lineReflowStatus
= LineReflowStatus::RedoNoPull
;
4848 // In case we reflow this line again, remember that we don't
4849 // need to force any breaking
4850 aLineLayout
.ClearOptionalBreakPosition();
4853 if (LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4854 // This happens only when we have a line that is impacted by
4855 // floats and the first element in the line doesn't fit with
4858 // If there's block space available, we either try to reflow the line
4859 // past the current band (if it's non-zero and the band definitely won't
4860 // widen around a shape-outside), otherwise we try one pixel down. If
4861 // there's no block space available, we push the line to the next
4864 NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.BSize(outerWM
),
4865 "unconstrained block size on totally empty line");
4867 // See the analogous code for blocks in BlockReflowState::ClearFloats.
4868 nscoord bandBSize
= aFloatAvailableSpace
.mRect
.BSize(outerWM
);
4869 if (bandBSize
> 0 ||
4870 NS_UNCONSTRAINEDSIZE
== aState
.mReflowInput
.AvailableBSize()) {
4871 NS_ASSERTION(bandBSize
== 0 || aFloatAvailableSpace
.HasFloats(),
4872 "redo line on totally empty line with non-empty band...");
4873 // We should never hit this case if we've placed floats on the
4874 // line; if we have, then the GetFloatAvailableSpace call is wrong
4875 // and needs to happen after the caller pops the float manager
4877 aState
.FloatManager()->AssertStateMatches(aFloatStateBeforeLine
);
4879 if (!aFloatAvailableSpace
.MayWiden() && bandBSize
> 0) {
4880 // Move it down far enough to clear the current band.
4881 aState
.mBCoord
+= bandBSize
;
4883 // Move it down by one dev pixel.
4884 aState
.mBCoord
+= aState
.mPresContext
->DevPixelsToAppUnits(1);
4887 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
4889 // There's nowhere to retry placing the line, so we want to push
4890 // it to the next page/column where its contents can fit not
4892 lineReflowStatus
= LineReflowStatus::Truncated
;
4893 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4896 // XXX: a small optimization can be done here when paginating:
4897 // if the new Y coordinate is past the end of the block then
4898 // push the line and return now instead of later on after we are
4900 } else if (LineReflowStatus::Truncated
!= lineReflowStatus
&&
4901 LineReflowStatus::RedoNoPull
!= lineReflowStatus
) {
4902 // If we are propagating out a break-before status then there is
4903 // no point in placing the line.
4904 if (!aState
.mReflowStatus
.IsInlineBreakBefore()) {
4905 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
4906 aFloatAvailableSpace
, aAvailableSpaceBSize
,
4907 aKeepReflowGoing
)) {
4908 lineReflowStatus
= LineReflowStatus::RedoMoreFloats
;
4909 // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4916 printf("Line reflow status = %s\n",
4917 LineReflowStatusToString(lineReflowStatus
));
4921 if (aLineLayout
.GetDirtyNextLine()) {
4922 // aLine may have been pushed to the overflow lines.
4923 FrameLines
* overflowLines
= GetOverflowLines();
4924 // We can't just compare iterators front() to aLine here, since they may be
4925 // in different lists.
4926 bool pushedToOverflowLines
=
4927 overflowLines
&& overflowLines
->mLines
.front() == aLine
.get();
4928 if (pushedToOverflowLines
) {
4929 // aLine is stale, it's associated with the main line list but it should
4930 // be associated with the overflow line list now
4931 aLine
= overflowLines
->mLines
.begin();
4933 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
4934 if (iter
.Next() && iter
.GetLine()->IsInline()) {
4935 iter
.GetLine()->MarkDirty();
4936 if (iter
.GetContainer() != this) {
4937 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4942 *aLineReflowStatus
= lineReflowStatus
;
4946 * Reflow an inline frame. The reflow status is mapped from the frames
4947 * reflow status to the lines reflow status (not to our reflow status).
4948 * The line reflow status is simple: true means keep placing frames
4949 * on the line; false means don't (the line is done). If the line
4950 * has some sort of breaking affect then aLine's break-type will be set
4951 * to something other than StyleClear::None.
4953 void nsBlockFrame::ReflowInlineFrame(BlockReflowState
& aState
,
4954 nsLineLayout
& aLineLayout
,
4955 LineIterator aLine
, nsIFrame
* aFrame
,
4956 LineReflowStatus
* aLineReflowStatus
) {
4958 *aLineReflowStatus
= LineReflowStatus::OK
;
4960 #ifdef NOISY_FIRST_LETTER
4962 printf(": reflowing ");
4963 aFrame
->ListTag(stdout
);
4964 printf(" reflowingFirstLetter=%s\n",
4965 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
4968 if (aFrame
->IsPlaceholderFrame()) {
4969 auto ph
= static_cast<nsPlaceholderFrame
*>(aFrame
);
4970 ph
->ForgetLineIsEmptySoFar();
4973 // Reflow the inline frame
4974 nsReflowStatus frameReflowStatus
;
4976 aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
, nullptr, pushedFrame
);
4978 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4979 aLineLayout
.SetDirtyNextLine();
4982 #ifdef REALLY_NOISY_REFLOW
4983 aFrame
->ListTag(stdout
);
4984 printf(": status=%s\n", ToString(frameReflowStatus
).c_str());
4987 #if defined(REFLOW_STATUS_COVERAGE)
4988 RecordReflowStatus(false, frameReflowStatus
);
4991 // Send post-reflow notification
4992 aState
.mPrevChild
= aFrame
;
4995 This is where we need to add logic to handle some odd behavior.
4996 For one thing, we should usually place at least one thing next
4997 to a left float, even when that float takes up all the width on a line.
5001 // Process the child frames reflow status. There are 5 cases:
5002 // complete, not-complete, break-before, break-after-complete,
5003 // break-after-not-complete. There are two situations: we are a
5004 // block or we are an inline. This makes a total of 10 cases
5005 // (fortunately, there is some overlap).
5006 aLine
->ClearForcedLineBreak();
5007 if (frameReflowStatus
.IsInlineBreak() ||
5008 aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5009 // Always abort the line reflow (because a line break is the
5010 // minimal amount of break we do).
5011 *aLineReflowStatus
= LineReflowStatus::Stop
;
5013 // XXX what should aLine's break-type be set to in all these cases?
5014 if (frameReflowStatus
.IsInlineBreakBefore()) {
5015 // Break-before cases.
5016 if (aFrame
== aLine
->mFirstChild
) {
5017 // If we break before the first frame on the line then we must
5018 // be trying to place content where there's no room (e.g. on a
5019 // line with wide floats). Inform the caller to reflow the
5020 // line after skipping past a float.
5021 *aLineReflowStatus
= LineReflowStatus::RedoNextBand
;
5023 // It's not the first child on this line so go ahead and split
5024 // the line. We will see the frame again on the next-line.
5025 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
5027 // If we're splitting the line because the frame didn't fit and it
5028 // was pushed, then mark the line as having word wrapped. We need to
5029 // know that if we're shrink wrapping our width
5031 aLine
->SetLineWrapped(true);
5035 MOZ_ASSERT(frameReflowStatus
.IsInlineBreakAfter() ||
5036 aState
.mTrailingClearFromPIF
!= StyleClear::None
,
5037 "We should've handled inline break-before in the if-branch!");
5039 // If a float split and its prev-in-flow was followed by a <BR>, then
5040 // combine the <BR>'s float clear type with the inline's float clear type
5041 // (the inline will be the very next frame after the split float).
5042 StyleClear clearType
= frameReflowStatus
.FloatClearType();
5043 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5044 clearType
= nsLayoutUtils::CombineClearType(
5045 clearType
, aState
.mTrailingClearFromPIF
);
5046 aState
.mTrailingClearFromPIF
= StyleClear::None
;
5048 // Break-after cases
5049 if (clearType
!= StyleClear::None
|| aLineLayout
.GetLineEndsInBR()) {
5050 aLine
->SetForcedLineBreakAfter(clearType
);
5052 if (frameReflowStatus
.IsComplete()) {
5053 // Split line, but after the frame just reflowed
5054 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5057 if (frameReflowStatus
.IsInlineBreakAfter() &&
5058 !aLineLayout
.GetLineEndsInBR()) {
5059 aLineLayout
.SetDirtyNextLine();
5065 if (!frameReflowStatus
.IsFullyComplete()) {
5066 // Create a continuation for the incomplete frame. Note that the
5067 // frame may already have a continuation.
5068 CreateContinuationFor(aState
, aLine
, aFrame
);
5070 // Remember that the line has wrapped
5071 if (!aLineLayout
.GetLineEndsInBR()) {
5072 aLine
->SetLineWrapped(true);
5075 // If we just ended a first-letter frame or reflowed a placeholder then
5076 // don't split the line and don't stop the line reflow...
5077 // But if we are going to stop anyways we'd better split the line.
5078 if ((!frameReflowStatus
.FirstLetterComplete() &&
5079 !aFrame
->IsPlaceholderFrame()) ||
5080 *aLineReflowStatus
== LineReflowStatus::Stop
) {
5081 // Split line after the current frame
5082 *aLineReflowStatus
= LineReflowStatus::Stop
;
5083 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5089 bool nsBlockFrame::CreateContinuationFor(BlockReflowState
& aState
,
5090 nsLineBox
* aLine
, nsIFrame
* aFrame
) {
5091 nsIFrame
* newFrame
= nullptr;
5093 if (!aFrame
->GetNextInFlow()) {
5095 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame
, this);
5097 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
5100 aLine
->NoteFrameAdded(newFrame
);
5109 void nsBlockFrame::SplitFloat(BlockReflowState
& aState
, nsIFrame
* aFloat
,
5110 const nsReflowStatus
& aFloatStatus
) {
5111 MOZ_ASSERT(!aFloatStatus
.IsFullyComplete(),
5112 "why split the frame if it's fully complete?");
5113 MOZ_ASSERT(aState
.mBlock
== this);
5115 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
5117 nsContainerFrame
* oldParent
= nextInFlow
->GetParent();
5118 oldParent
->StealFrame(nextInFlow
);
5119 if (oldParent
!= this) {
5120 ReparentFrame(nextInFlow
, oldParent
, this);
5122 if (!aFloatStatus
.IsOverflowIncomplete()) {
5123 nextInFlow
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5127 PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat
, this);
5129 if (aFloatStatus
.IsOverflowIncomplete()) {
5130 nextInFlow
->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5133 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
5134 if (floatStyle
== StyleFloat::Left
) {
5135 aState
.FloatManager()->SetSplitLeftFloatAcrossBreak();
5137 MOZ_ASSERT(floatStyle
== StyleFloat::Right
, "Unexpected float side!");
5138 aState
.FloatManager()->SetSplitRightFloatAcrossBreak();
5141 aState
.AppendPushedFloatChain(nextInFlow
);
5142 if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_FLOAT_MGR
)) ||
5143 MOZ_UNLIKELY(IsTrueOverflowContainer())) {
5144 aState
.mReflowStatus
.SetOverflowIncomplete();
5146 aState
.mReflowStatus
.SetIncomplete();
5150 static bool CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
,
5155 NS_ASSERTION(!aFloat
->GetPrevContinuation(),
5156 "float in a line should never be a continuation");
5157 NS_ASSERTION(!aFloat
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5158 "float in a line should never be a pushed float");
5159 nsIFrame
* ph
= aFloat
->FirstInFlow()->GetPlaceholderFrame();
5160 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
5161 if (f
->GetParent() == aBlock
) {
5162 return aLine
->Contains(f
);
5165 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
5169 void nsBlockFrame::SplitLine(BlockReflowState
& aState
,
5170 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5172 LineReflowStatus
* aLineReflowStatus
) {
5173 MOZ_ASSERT(aLine
->IsInline(), "illegal SplitLine on block line");
5176 aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
5177 MOZ_ASSERT(pushCount
>= 0, "bad push count");
5181 nsIFrame::IndentBy(stdout
, gNoiseIndent
);
5182 printf("split line: from line=%p pushCount=%d aFrame=",
5183 static_cast<void*>(aLine
.get()), pushCount
);
5185 aFrame
->ListTag(stdout
);
5190 if (gReallyNoisyReflow
) {
5191 aLine
->List(stdout
, gNoiseIndent
+ 1);
5196 if (0 != pushCount
) {
5197 MOZ_ASSERT(aLine
->GetChildCount() > pushCount
, "bad push");
5198 MOZ_ASSERT(nullptr != aFrame
, "whoops");
5201 nsIFrame
* f
= aFrame
;
5202 int32_t count
= pushCount
;
5203 while (f
&& count
> 0) {
5204 f
= f
->GetNextSibling();
5207 NS_ASSERTION(count
== 0, "Not enough frames to push");
5211 // Put frames being split out into their own line
5212 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
5213 mLines
.after_insert(aLine
, newLine
);
5215 if (gReallyNoisyReflow
) {
5216 newLine
->List(stdout
, gNoiseIndent
+ 1);
5220 // Let line layout know that some frames are no longer part of its
5222 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
5224 // If floats have been placed whose placeholders have been pushed to the new
5225 // line, we need to reflow the old line again. We don't want to look at the
5226 // frames in the new line, because as a large paragraph is laid out the
5227 // we'd get O(N^2) performance. So instead we just check that the last
5228 // float and the last below-current-line float are still in aLine.
5229 if (!CheckPlaceholderInLine(
5231 aLine
->HasFloats() ? aLine
->Floats().LastElement() : nullptr) ||
5232 !CheckPlaceholderInLine(
5234 aState
.mBelowCurrentLineFloats
.SafeLastElement(nullptr))) {
5235 *aLineReflowStatus
= LineReflowStatus::RedoNoPull
;
5244 bool nsBlockFrame::IsLastLine(BlockReflowState
& aState
, LineIterator aLine
) {
5245 while (++aLine
!= LinesEnd()) {
5246 // There is another line
5247 if (0 != aLine
->GetChildCount()) {
5248 // If the next line is a block line then this line is the last in a
5249 // group of inline lines.
5250 return aLine
->IsBlock();
5252 // The next line is empty, try the next one
5255 // Try our next-in-flows lines to answer the question
5256 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*)GetNextInFlow();
5257 while (nullptr != nextInFlow
) {
5258 for (const auto& line
: nextInFlow
->Lines()) {
5259 if (0 != line
.GetChildCount()) {
5260 return line
.IsBlock();
5263 nextInFlow
= (nsBlockFrame
*)nextInFlow
->GetNextInFlow();
5266 // This is the last line - so don't allow justification
5270 bool nsBlockFrame::PlaceLine(BlockReflowState
& aState
,
5271 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5272 nsFloatManager::SavedState
* aFloatStateBeforeLine
,
5273 nsFlowAreaRect
& aFlowArea
,
5274 nscoord
& aAvailableSpaceBSize
,
5275 bool* aKeepReflowGoing
) {
5276 // Try to position the floats in a nowrap context.
5277 aLineLayout
.FlushNoWrapFloats();
5279 // Trim extra white-space from the line before placing the frames
5280 aLineLayout
.TrimTrailingWhiteSpace();
5282 // Vertically align the frames on this line.
5284 // According to the CSS2 spec, section 12.6.1, the "marker" box
5285 // participates in the height calculation of the list-item box's
5288 // There are exactly two places a ::marker can be placed: near the
5289 // first or second line. It's only placed on the second line in a
5290 // rare case: when the first line is empty.
5291 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
5292 bool addedMarker
= false;
5293 if (HasOutsideMarker() &&
5294 ((aLine
== mLines
.front() &&
5295 (!aLineLayout
.IsZeroBSize() || (aLine
== mLines
.back()))) ||
5296 (mLines
.front() != mLines
.back() && 0 == mLines
.front()->BSize() &&
5297 aLine
== mLines
.begin().next()))) {
5298 ReflowOutput
metrics(aState
.mReflowInput
);
5299 nsIFrame
* marker
= GetOutsideMarker();
5300 ReflowOutsideMarker(marker
, aState
, metrics
, aState
.mBCoord
);
5301 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
5302 "empty ::marker frame took up space");
5303 aLineLayout
.AddMarkerFrame(marker
, metrics
);
5306 aLineLayout
.VerticalAlignLine();
5308 // We want to consider the floats in the current line when determining
5309 // whether the float available space is shrunk. If mLineBSize doesn't
5310 // exist, we are in the first pass trying to place the line. Calling
5311 // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
5312 // for UpdateBand().
5314 // floatAvailableSpaceWithOldLineBSize is the float available space with
5315 // the old BSize, but including the floats that were added in this line.
5316 LogicalRect floatAvailableSpaceWithOldLineBSize
=
5317 aState
.mLineBSize
.isNothing()
5318 ? aState
.GetFloatAvailableSpace(aLine
->BStart()).mRect
5320 .GetFloatAvailableSpaceForBSize(
5321 aLine
->BStart(), aState
.mLineBSize
.value(), nullptr)
5324 // As we redo for floats, we can't reduce the amount of BSize we're
5326 aAvailableSpaceBSize
= std::max(aAvailableSpaceBSize
, aLine
->BSize());
5327 LogicalRect floatAvailableSpaceWithLineBSize
=
5329 .GetFloatAvailableSpaceForBSize(aLine
->BStart(), aAvailableSpaceBSize
,
5333 // If the available space between the floats is smaller now that we
5334 // know the BSize, return false (and cause another pass with
5335 // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
5336 // never decreases, which means that we can't reduce the set of floats
5337 // we intersect, which means that the available space cannot grow.
5338 if (AvailableSpaceShrunk(wm
, floatAvailableSpaceWithOldLineBSize
,
5339 floatAvailableSpaceWithLineBSize
, false)) {
5340 // Prepare data for redoing the line.
5341 aState
.mLineBSize
= Some(aLine
->BSize());
5343 // Since we want to redo the line, we update aFlowArea by using the
5344 // aFloatStateBeforeLine, which is the float manager's state before the
5346 LogicalRect
oldFloatAvailableSpace(aFlowArea
.mRect
);
5347 aFlowArea
= aState
.GetFloatAvailableSpaceForBSize(
5348 aLine
->BStart(), aAvailableSpaceBSize
, aFloatStateBeforeLine
);
5351 aFlowArea
.mRect
.BStart(wm
) == oldFloatAvailableSpace
.BStart(wm
),
5353 // Restore the BSize to the position of the next band.
5354 aFlowArea
.mRect
.BSize(wm
) = oldFloatAvailableSpace
.BSize(wm
);
5356 // Enforce both IStart() and IEnd() never move outwards to prevent
5357 // infinite grow-shrink loops.
5358 const nscoord iStartDiff
=
5359 aFlowArea
.mRect
.IStart(wm
) - oldFloatAvailableSpace
.IStart(wm
);
5360 const nscoord iEndDiff
=
5361 aFlowArea
.mRect
.IEnd(wm
) - oldFloatAvailableSpace
.IEnd(wm
);
5362 if (iStartDiff
< 0) {
5363 aFlowArea
.mRect
.IStart(wm
) -= iStartDiff
;
5364 aFlowArea
.mRect
.ISize(wm
) += iStartDiff
;
5367 aFlowArea
.mRect
.ISize(wm
) -= iEndDiff
;
5374 if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
5375 static nscoord lastHeight
= 0;
5376 if (ABSURD_SIZE(aLine
->BStart())) {
5377 lastHeight
= aLine
->BStart();
5378 if (abs(aLine
->BStart() - lastHeight
) > ABSURD_COORD
/ 10) {
5379 nsIFrame::ListTag(stdout
);
5380 printf(": line=%p y=%d line.bounds.height=%d\n",
5381 static_cast<void*>(aLine
.get()), aLine
->BStart(),
5390 // Only block frames horizontally align their children because
5391 // inline frames "shrink-wrap" around their children (therefore
5392 // there is no extra horizontal space).
5393 const nsStyleText
* styleText
= StyleText();
5396 * We don't care checking for IsLastLine properly if we don't care (if it
5397 * can't change the used text-align value for the line).
5399 * In other words, isLastLine really means isLastLineAndWeCare.
5401 const bool isLastLine
=
5402 !IsInSVGTextSubtree() &&
5403 styleText
->TextAlignForLastLine() != styleText
->mTextAlign
&&
5404 (aLineLayout
.GetLineEndsInBR() || IsLastLine(aState
, aLine
));
5406 aLineLayout
.TextAlignLine(aLine
, isLastLine
);
5408 // From here on, pfd->mBounds rectangles are incorrect because bidi
5409 // might have moved frames around!
5410 OverflowAreas overflowAreas
;
5411 aLineLayout
.RelativePositionFrames(overflowAreas
);
5412 aLine
->SetOverflowAreas(overflowAreas
);
5414 aLineLayout
.RemoveMarkerFrame(GetOutsideMarker());
5417 // Inline lines do not have margins themselves; however they are
5418 // impacted by prior block margins. If this line ends up having some
5419 // height then we zero out the previous block-end margin value that was
5420 // already applied to the line's starting Y coordinate. Otherwise we
5421 // leave it be so that the previous blocks block-end margin can be
5422 // collapsed with a block that follows.
5425 if (!aLine
->CachedIsEmpty()) {
5426 // This line has some height. Therefore the application of the
5427 // previous-bottom-margin should stick.
5428 aState
.mPrevBEndMargin
.Zero();
5429 newBCoord
= aLine
->BEnd();
5431 // Don't let the previous-bottom-margin value affect the newBCoord
5432 // coordinate (it was applied in ReflowInlineFrames speculatively)
5433 // since the line is empty.
5434 // We already called |ShouldApplyBStartMargin|, and if we applied it
5435 // then mShouldApplyBStartMargin is set.
5436 nscoord dy
= aState
.mFlags
.mShouldApplyBStartMargin
5437 ? -aState
.mPrevBEndMargin
.get()
5439 newBCoord
= aState
.mBCoord
+ dy
;
5442 if (!aState
.mReflowStatus
.IsFullyComplete() &&
5443 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5444 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5445 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5449 // See if the line fit (our first line always does).
5450 if (mLines
.front() != aLine
&&
5451 aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
&&
5452 newBCoord
> aState
.ContentBEnd()) {
5453 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
5454 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5455 // All our content doesn't fit, start on the next page.
5456 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5458 // Push aLine and all of its children and anything else that
5459 // follows to our next-in-flow.
5460 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
5465 // Note that any early return before this update of aState.mBCoord
5466 // must either (a) return false or (b) set aKeepReflowGoing to false.
5467 // Otherwise we'll keep reflowing later lines at an incorrect
5468 // position, and we might not come back and clean up the damage later.
5469 aState
.mBCoord
= newBCoord
;
5471 // Add the already placed current-line floats to the line
5472 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5474 // Any below current line floats to place?
5475 if (!aState
.mBelowCurrentLineFloats
.IsEmpty()) {
5476 // Reflow the below-current-line floats, which places on the line's
5478 aState
.PlaceBelowCurrentLineFloats(aLine
);
5481 // When a line has floats, factor them into the overflow areas computations.
5482 if (aLine
->HasFloats()) {
5483 // Union the float overflow areas (stored in aState) and the value computed
5484 // by the line layout code.
5485 OverflowAreas lineOverflowAreas
= aState
.mFloatOverflowAreas
;
5486 lineOverflowAreas
.UnionWith(aLine
->GetOverflowAreas());
5487 aLine
->SetOverflowAreas(lineOverflowAreas
);
5489 #ifdef NOISY_OVERFLOW_AREAS
5490 printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
5491 ListTag().get(), aLine
.get(),
5492 ToString(aLine
->InkOverflowRect()).c_str(),
5493 ToString(aLine
->ScrollableOverflowRect()).c_str());
5497 // Apply break-after clearing if necessary
5498 // This must stay in sync with |ReflowDirtyLines|.
5499 if (aLine
->HasFloatClearTypeAfter()) {
5500 std::tie(aState
.mBCoord
, std::ignore
) =
5501 aState
.ClearFloats(aState
.mBCoord
, aLine
->FloatClearTypeAfter());
5506 void nsBlockFrame::PushLines(BlockReflowState
& aState
,
5507 nsLineList::iterator aLineBefore
) {
5508 // NOTE: aLineBefore is always a normal line, not an overflow line.
5509 // The following expression will assert otherwise.
5510 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
5512 nsLineList::iterator
overBegin(aLineBefore
.next());
5514 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
5515 bool firstLine
= overBegin
== LinesBegin();
5517 if (overBegin
!= LinesEnd()) {
5518 // Remove floats in the lines from mFloats
5520 CollectFloats(overBegin
->mFirstChild
, floats
, true);
5522 if (floats
.NotEmpty()) {
5524 for (nsIFrame
* f
: floats
) {
5525 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5526 "CollectFloats should've removed that bit");
5529 // Push the floats onto the front of the overflow out-of-flows list
5530 nsAutoOOFFrameList
oofs(this);
5531 oofs
.mList
.InsertFrames(nullptr, nullptr, std::move(floats
));
5534 // overflow lines can already exist in some cases, in particular,
5535 // when shrinkwrapping and we discover that the shrinkwap causes
5536 // the height of some child block to grow which creates additional
5537 // overflowing content. In such cases we must prepend the new
5538 // overflow to the existing overflow.
5539 FrameLines
* overflowLines
= RemoveOverflowLines();
5540 if (!overflowLines
) {
5541 // XXXldb use presshell arena!
5542 overflowLines
= new FrameLines();
5544 if (overflowLines
) {
5545 nsIFrame
* lineBeforeLastFrame
;
5547 lineBeforeLastFrame
= nullptr; // removes all frames
5549 nsIFrame
* f
= overBegin
->mFirstChild
;
5550 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
5551 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
5552 "unexpected line frames");
5554 nsFrameList pushedFrames
= mFrames
.TakeFramesAfter(lineBeforeLastFrame
);
5555 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr,
5556 std::move(pushedFrames
));
5558 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
5559 overBegin
, LinesEnd());
5560 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
5561 // this takes ownership but it won't delete it immediately so we
5562 // can keep using it.
5563 SetOverflowLines(overflowLines
);
5565 // Mark all the overflow lines dirty so that they get reflowed when
5566 // they are pulled up by our next-in-flow.
5568 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
5569 for (LineIterator line
= overflowLines
->mLines
.begin(),
5570 line_end
= overflowLines
->mLines
.end();
5571 line
!= line_end
; ++line
) {
5573 line
->MarkPreviousMarginDirty();
5574 line
->SetMovedFragments();
5575 line
->SetBoundsEmpty();
5576 if (line
->HasFloats()) {
5577 line
->ClearFloats();
5584 VerifyOverflowSituation();
5588 // The overflowLines property is stored as a pointer to a line list,
5589 // which must be deleted. However, the following functions all maintain
5590 // the invariant that the property is never set if the list is empty.
5592 bool nsBlockFrame::DrainOverflowLines() {
5594 VerifyOverflowSituation();
5597 // Steal the prev-in-flow's overflow lines and prepend them.
5598 bool didFindOverflow
= false;
5599 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5601 prevBlock
->ClearLineCursors();
5602 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
5603 if (overflowLines
) {
5604 // Make all the frames on the overflow line list mine.
5605 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
5607 // Collect overflow containers from our OverflowContainers list that are
5608 // continuations from the frames we picked up from our prev-in-flow, then
5609 // prepend those to ExcessOverflowContainers to ensure the continuations
5611 if (GetOverflowContainers()) {
5612 nsFrameList ocContinuations
;
5613 for (auto* f
: overflowLines
->mFrames
) {
5616 while (!done
&& (cont
= cont
->GetNextContinuation()) &&
5617 cont
->GetParent() == this) {
5618 bool onlyChild
= !cont
->GetPrevSibling() && !cont
->GetNextSibling();
5619 if (cont
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
) &&
5620 TryRemoveFrame(OverflowContainersProperty(), cont
)) {
5621 ocContinuations
.AppendFrame(nullptr, cont
);
5631 if (!ocContinuations
.IsEmpty()) {
5632 if (nsFrameList
* eoc
= GetExcessOverflowContainers()) {
5633 eoc
->InsertFrames(nullptr, nullptr, std::move(ocContinuations
));
5635 SetExcessOverflowContainers(std::move(ocContinuations
));
5640 // Make the overflow out-of-flow frames mine too.
5641 nsAutoOOFFrameList
oofs(prevBlock
);
5642 if (oofs
.mList
.NotEmpty()) {
5643 // In case we own any next-in-flows of any of the drained frames, then
5644 // move those to the PushedFloat list.
5645 nsFrameList pushedFloats
;
5646 for (nsIFrame
* f
: oofs
.mList
) {
5647 nsIFrame
* nif
= f
->GetNextInFlow();
5648 for (; nif
&& nif
->GetParent() == this; nif
= nif
->GetNextInFlow()) {
5649 MOZ_ASSERT(nif
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
));
5651 pushedFloats
.AppendFrame(nullptr, nif
);
5654 ReparentFrames(oofs
.mList
, prevBlock
, this);
5655 mFloats
.InsertFrames(nullptr, nullptr, std::move(oofs
.mList
));
5656 if (!pushedFloats
.IsEmpty()) {
5657 nsFrameList
* pf
= EnsurePushedFloats();
5658 pf
->InsertFrames(nullptr, nullptr, std::move(pushedFloats
));
5662 if (!mLines
.empty()) {
5663 // Remember to recompute the margins on the first line. This will
5664 // also recompute the correct deltaBCoord if necessary.
5665 mLines
.front()->MarkPreviousMarginDirty();
5667 // The overflow lines have already been marked dirty and their previous
5668 // margins marked dirty also.
5670 // Prepend the overflow frames/lines to our principal list.
5671 mFrames
.InsertFrames(nullptr, nullptr, std::move(overflowLines
->mFrames
));
5672 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
5673 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
5674 delete overflowLines
;
5675 didFindOverflow
= true;
5679 // Now append our own overflow lines.
5680 return DrainSelfOverflowList() || didFindOverflow
;
5683 bool nsBlockFrame::DrainSelfOverflowList() {
5684 UniquePtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
5685 if (!ourOverflowLines
) {
5689 // No need to reparent frames in our own overflow lines/oofs, because they're
5690 // already ours. But we should put overflow floats back in mFloats.
5691 // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5693 nsAutoOOFFrameList
oofs(this);
5694 if (oofs
.mList
.NotEmpty()) {
5696 for (nsIFrame
* f
: oofs
.mList
) {
5697 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5698 "CollectFloats should've removed that bit");
5701 // The overflow floats go after our regular floats.
5702 mFloats
.AppendFrames(nullptr, std::move(oofs
).mList
);
5705 if (!ourOverflowLines
->mLines
.empty()) {
5706 mFrames
.AppendFrames(nullptr, std::move(ourOverflowLines
->mFrames
));
5707 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
5711 VerifyOverflowSituation();
5717 * Pushed floats are floats whose placeholders are in a previous
5718 * continuation. They might themselves be next-continuations of a float
5719 * that partially fit in an earlier continuation, or they might be the
5720 * first continuation of a float that couldn't be placed at all.
5722 * Pushed floats live permanently at the beginning of a block's float
5723 * list, where they must live *before* any floats whose placeholders are
5726 * Temporarily, during reflow, they also live on the pushed floats list,
5727 * which only holds them between (a) when one continuation pushes them to
5728 * its pushed floats list because they don't fit and (b) when the next
5729 * continuation pulls them onto the beginning of its float list.
5731 * DrainPushedFloats sets up pushed floats the way we need them at the
5732 * start of reflow; they are then reflowed by ReflowPushedFloats (which
5733 * might push some of them on). Floats with placeholders in this block
5734 * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
5735 * also maintains these invariants.
5737 * DrainSelfPushedFloats moves any pushed floats from this block's own
5738 * PushedFloats list back into mFloats. DrainPushedFloats additionally
5739 * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5741 void nsBlockFrame::DrainSelfPushedFloats() {
5742 // If we're getting reflowed multiple times without our
5743 // next-continuation being reflowed, we might need to pull back floats
5744 // that we just put in the list to be pushed to our next-in-flow.
5745 // We don't want to pull back any next-in-flows of floats on our own
5746 // float list, and we only need to pull back first-in-flows whose
5747 // placeholders were in earlier blocks (since first-in-flows whose
5748 // placeholders are in this block will get pulled appropriately by
5749 // AddFloat, and will then be more likely to be in the correct order).
5750 mozilla::PresShell
* presShell
= PresShell();
5751 nsFrameList
* ourPushedFloats
= GetPushedFloats();
5752 if (ourPushedFloats
) {
5753 // When we pull back floats, we want to put them with the pushed
5754 // floats, which must live at the start of our float list, but we
5755 // want them at the end of those pushed floats.
5756 // FIXME: This isn't quite right! What if they're all pushed floats?
5757 nsIFrame
* insertionPrevSibling
= nullptr; /* beginning of list */
5758 for (nsIFrame
* f
= mFloats
.FirstChild();
5759 f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
5760 f
= f
->GetNextSibling()) {
5761 insertionPrevSibling
= f
;
5764 nsIFrame
* f
= ourPushedFloats
->LastChild();
5766 nsIFrame
* prevSibling
= f
->GetPrevSibling();
5768 nsPlaceholderFrame
* placeholder
= f
->GetPlaceholderFrame();
5769 nsIFrame
* floatOriginalParent
=
5770 presShell
->FrameConstructor()->GetFloatContainingBlock(placeholder
);
5771 if (floatOriginalParent
!= this) {
5772 // This is a first continuation that was pushed from one of our
5773 // previous continuations. Take it out of the pushed floats
5774 // list and put it in our floats list, before any of our
5775 // floats, but after other pushed floats.
5776 ourPushedFloats
->RemoveFrame(f
);
5777 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
5783 if (ourPushedFloats
->IsEmpty()) {
5784 RemovePushedFloats()->Delete(presShell
);
5789 void nsBlockFrame::DrainPushedFloats() {
5790 DrainSelfPushedFloats();
5792 // After our prev-in-flow has completed reflow, it may have a pushed
5793 // floats list, containing floats that we need to own. Take these.
5794 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5796 AutoFrameListPtr
list(PresContext(), prevBlock
->RemovePushedFloats());
5797 if (list
&& list
->NotEmpty()) {
5798 mFloats
.InsertFrames(this, nullptr, std::move(*list
));
5803 nsBlockFrame::FrameLines
* nsBlockFrame::GetOverflowLines() const {
5804 if (!HasOverflowLines()) {
5807 FrameLines
* prop
= GetProperty(OverflowLinesProperty());
5809 prop
&& !prop
->mLines
.empty() &&
5810 prop
->mLines
.front()->GetChildCount() == 0
5811 ? prop
->mFrames
.IsEmpty()
5812 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5813 "value should always be stored and non-empty when state set");
5817 nsBlockFrame::FrameLines
* nsBlockFrame::RemoveOverflowLines() {
5818 if (!HasOverflowLines()) {
5821 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5823 prop
&& !prop
->mLines
.empty() &&
5824 prop
->mLines
.front()->GetChildCount() == 0
5825 ? prop
->mFrames
.IsEmpty()
5826 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5827 "value should always be stored and non-empty when state set");
5828 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5832 void nsBlockFrame::DestroyOverflowLines() {
5833 NS_ASSERTION(HasOverflowLines(), "huh?");
5834 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5835 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
5836 "value should always be stored but empty when destroying");
5837 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5841 // This takes ownership of aOverflowLines.
5842 // XXX We should allocate overflowLines from presShell arena!
5843 void nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
) {
5844 NS_ASSERTION(aOverflowLines
, "null lines");
5845 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
5846 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
5847 aOverflowLines
->mFrames
.FirstChild(),
5848 "invalid overflow lines / frames");
5849 NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
),
5850 "Overwriting existing overflow lines");
5852 // Verify that we won't overwrite an existing overflow list
5853 NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5854 SetProperty(OverflowLinesProperty(), aOverflowLines
);
5855 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5858 nsFrameList
* nsBlockFrame::GetOverflowOutOfFlows() const {
5859 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5862 nsFrameList
* result
= GetProperty(OverflowOutOfFlowsProperty());
5863 NS_ASSERTION(result
, "value should always be non-empty when state set");
5867 void nsBlockFrame::SetOverflowOutOfFlows(nsFrameList
&& aList
,
5868 nsFrameList
* aPropValue
) {
5870 HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) == !!aPropValue
,
5871 "state does not match value");
5873 if (aList
.IsEmpty()) {
5874 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5877 nsFrameList
* list
= TakeProperty(OverflowOutOfFlowsProperty());
5878 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
5880 list
->Delete(PresShell());
5881 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5882 } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5883 NS_ASSERTION(aPropValue
== GetProperty(OverflowOutOfFlowsProperty()),
5884 "prop value mismatch");
5885 *aPropValue
= std::move(aList
);
5887 SetProperty(OverflowOutOfFlowsProperty(),
5888 new (PresShell()) nsFrameList(std::move(aList
)));
5889 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5893 nsIFrame
* nsBlockFrame::GetInsideMarker() const {
5894 if (!HasInsideMarker()) {
5897 NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5898 nsIFrame
* frame
= GetProperty(InsideMarkerProperty());
5899 NS_ASSERTION(frame
, "bogus inside ::marker frame");
5903 nsIFrame
* nsBlockFrame::GetOutsideMarker() const {
5904 nsFrameList
* list
= GetOutsideMarkerList();
5905 return list
? list
->FirstChild() : nullptr;
5908 nsFrameList
* nsBlockFrame::GetOutsideMarkerList() const {
5909 if (!HasOutsideMarker()) {
5912 NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5913 nsFrameList
* list
= GetProperty(OutsideMarkerProperty());
5914 NS_ASSERTION(list
&& list
->GetLength() == 1, "bogus outside ::marker list");
5918 nsFrameList
* nsBlockFrame::GetPushedFloats() const {
5919 if (!HasPushedFloats()) {
5922 nsFrameList
* result
= GetProperty(PushedFloatProperty());
5923 NS_ASSERTION(result
, "value should always be non-empty when state set");
5927 nsFrameList
* nsBlockFrame::EnsurePushedFloats() {
5928 nsFrameList
* result
= GetPushedFloats();
5933 result
= new (PresShell()) nsFrameList
;
5934 SetProperty(PushedFloatProperty(), result
);
5935 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5940 nsFrameList
* nsBlockFrame::RemovePushedFloats() {
5941 if (!HasPushedFloats()) {
5944 nsFrameList
* result
= TakeProperty(PushedFloatProperty());
5945 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5946 NS_ASSERTION(result
, "value should always be non-empty when state set");
5950 //////////////////////////////////////////////////////////////////////
5951 // Frame list manipulation routines
5953 void nsBlockFrame::AppendFrames(ChildListID aListID
, nsFrameList
&& aFrameList
) {
5954 if (aFrameList
.IsEmpty()) {
5957 if (aListID
!= FrameChildListID::Principal
) {
5958 if (FrameChildListID::Float
== aListID
) {
5959 DrainSelfPushedFloats(); // ensure the last frame is in mFloats
5960 mFloats
.AppendFrames(nullptr, std::move(aFrameList
));
5963 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
5964 "unexpected child list");
5967 // Find the proper last-child for where the append should go
5968 nsIFrame
* lastKid
= mFrames
.LastChild();
5970 (mLines
.empty() ? nullptr : mLines
.back()->LastChild()) == lastKid
,
5971 "out-of-sync mLines / mFrames");
5973 #ifdef NOISY_REFLOW_REASON
5975 printf(": append ");
5976 for (nsIFrame
* frame
: aFrameList
) {
5977 frame
->ListTag(stdout
);
5981 lastKid
->ListTag(stdout
);
5986 if (IsInSVGTextSubtree()) {
5987 MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
5988 "unexpected block frame in SVG text");
5989 // Workaround for bug 1399425 in case this bit has been removed from the
5990 // SVGTextFrame just before the parser adds more descendant nodes.
5991 GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
);
5994 AddFrames(std::move(aFrameList
), lastKid
, nullptr);
5995 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
5996 PresShell()->FrameNeedsReflow(
5997 this, IntrinsicDirty::FrameAndAncestors
,
5998 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6002 void nsBlockFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
6003 const nsLineList::iterator
* aPrevFrameLine
,
6004 nsFrameList
&& aFrameList
) {
6005 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
6006 "inserting after sibling frame with different parent");
6008 if (aListID
!= FrameChildListID::Principal
) {
6009 if (FrameChildListID::Float
== aListID
) {
6010 DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
6011 mFloats
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
6014 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
6015 "unexpected child list");
6018 #ifdef NOISY_REFLOW_REASON
6020 printf(": insert ");
6021 for (nsIFrame
* frame
: aFrameList
) {
6022 frame
->ListTag(stdout
);
6026 aPrevFrame
->ListTag(stdout
);
6031 AddFrames(std::move(aFrameList
), aPrevFrame
, aPrevFrameLine
);
6032 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
6033 PresShell()->FrameNeedsReflow(
6034 this, IntrinsicDirty::FrameAndAncestors
,
6035 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6039 void nsBlockFrame::RemoveFrame(DestroyContext
& aContext
, ChildListID aListID
,
6040 nsIFrame
* aOldFrame
) {
6041 #ifdef NOISY_REFLOW_REASON
6043 printf(": remove ");
6044 aOldFrame
->ListTag(stdout
);
6048 if (aListID
== FrameChildListID::Principal
) {
6049 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
6050 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6052 MarkSameFloatManagerLinesDirty(this);
6054 } else if (FrameChildListID::Float
== aListID
) {
6055 // Make sure to mark affected lines dirty for the float frame
6056 // we are removing; this way is a bit messy, but so is the rest of the code.
6058 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
6059 "RemoveFrame should not be called on pushed floats.");
6060 for (nsIFrame
* f
= aOldFrame
;
6061 f
&& !f
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
6062 f
= f
->GetNextContinuation()) {
6063 MarkSameFloatManagerLinesDirty(
6064 static_cast<nsBlockFrame
*>(f
->GetParent()));
6066 DoRemoveOutOfFlowFrame(aContext
, aOldFrame
);
6067 } else if (FrameChildListID::NoReflowPrincipal
== aListID
) {
6068 // Skip the call to |FrameNeedsReflow| below by returning now.
6069 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6072 MOZ_CRASH("unexpected child list");
6075 PresShell()->FrameNeedsReflow(
6076 this, IntrinsicDirty::FrameAndAncestors
,
6077 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6080 static bool ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
) {
6081 LayoutFrameType type
= aLastFrame
->Type();
6082 if (type
== LayoutFrameType::Br
) {
6085 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
6086 if (type
== LayoutFrameType::Text
&&
6087 !aLastFrame
->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING
)) {
6088 return aLastFrame
->HasSignificantTerminalNewline();
6093 void nsBlockFrame::AddFrames(nsFrameList
&& aFrameList
, nsIFrame
* aPrevSibling
,
6094 const nsLineList::iterator
* aPrevSiblingLine
) {
6095 // Clear our line cursor, since our lines may change.
6098 if (aFrameList
.IsEmpty()) {
6102 // Attempt to find the line that contains the previous sibling
6103 nsLineList
* lineList
= &mLines
;
6104 nsFrameList
* frames
= &mFrames
;
6105 nsLineList::iterator prevSibLine
;
6106 int32_t prevSiblingIndex
;
6107 if (aPrevSiblingLine
) {
6108 MOZ_ASSERT(aPrevSibling
);
6109 prevSibLine
= *aPrevSiblingLine
;
6110 FrameLines
* overflowLines
= GetOverflowLines();
6111 MOZ_ASSERT(prevSibLine
.IsInSameList(mLines
.begin()) ||
6113 prevSibLine
.IsInSameList(overflowLines
->mLines
.begin())),
6114 "must be one of our line lists");
6115 if (overflowLines
) {
6116 // We need to find out which list it's actually in. Assume that
6117 // *if* we have overflow lines, that our primary lines aren't
6118 // huge, but our overflow lines might be.
6119 nsLineList::iterator line
= mLines
.begin(), lineEnd
= mLines
.end();
6120 while (line
!= lineEnd
) {
6121 if (line
== prevSibLine
) {
6126 if (line
== lineEnd
) {
6127 // By elimination, the line must be in our overflow lines.
6128 lineList
= &overflowLines
->mLines
;
6129 frames
= &overflowLines
->mFrames
;
6133 nsLineList::iterator nextLine
= prevSibLine
.next();
6134 nsIFrame
* lastFrameInLine
= nextLine
== lineList
->end()
6135 ? frames
->LastChild()
6136 : nextLine
->mFirstChild
->GetPrevSibling();
6137 prevSiblingIndex
= prevSibLine
->RIndexOf(aPrevSibling
, lastFrameInLine
);
6138 MOZ_ASSERT(prevSiblingIndex
>= 0,
6139 "aPrevSibling must be in aPrevSiblingLine");
6141 prevSibLine
= lineList
->end();
6142 prevSiblingIndex
= -1;
6144 // XXX_perf This is technically O(N^2) in some cases, but by using
6145 // RFind instead of Find, we make it O(N) in the most common case,
6146 // which is appending content.
6148 // Find the line that contains the previous sibling
6149 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
6150 prevSibLine
, mFrames
.LastChild(),
6151 &prevSiblingIndex
)) {
6152 // Not in mLines - try overflow lines.
6153 FrameLines
* overflowLines
= GetOverflowLines();
6155 if (overflowLines
) {
6156 prevSibLine
= overflowLines
->mLines
.end();
6157 prevSiblingIndex
= -1;
6158 found
= nsLineBox::RFindLineContaining(
6159 aPrevSibling
, overflowLines
->mLines
.begin(), prevSibLine
,
6160 overflowLines
->mFrames
.LastChild(), &prevSiblingIndex
);
6162 if (MOZ_LIKELY(found
)) {
6163 lineList
= &overflowLines
->mLines
;
6164 frames
= &overflowLines
->mFrames
;
6166 // Note: defensive code! RFindLineContaining must not return
6167 // false in this case, so if it does...
6168 MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
6169 aPrevSibling
= nullptr;
6170 prevSibLine
= lineList
->end();
6176 // Find the frame following aPrevSibling so that we can join up the
6177 // two lists of frames.
6179 // Split line containing aPrevSibling in two if the insertion
6180 // point is somewhere in the middle of the line.
6181 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
6183 // Split the line in two where the frame(s) are being inserted.
6185 NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
6186 lineList
->after_insert(prevSibLine
, line
);
6187 // Mark prevSibLine dirty and as needing textrun invalidation, since
6188 // we may be breaking up text in the line. Its previous line may also
6189 // need to be invalidated because it may be able to pull some text up.
6190 MarkLineDirty(prevSibLine
, lineList
);
6191 // The new line will also need its textruns recomputed because of the
6194 line
->SetInvalidateTextRuns(true);
6196 } else if (!lineList
->empty()) {
6197 lineList
->front()->MarkDirty();
6198 lineList
->front()->SetInvalidateTextRuns(true);
6200 const nsFrameList::Slice
& newFrames
=
6201 frames
->InsertFrames(nullptr, aPrevSibling
, std::move(aFrameList
));
6203 // Walk through the new frames being added and update the line data
6204 // structures to fit.
6205 for (nsIFrame
* newFrame
: newFrames
) {
6206 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
6207 "Unexpected aPrevSibling");
6209 !newFrame
->IsPlaceholderFrame() ||
6210 (!newFrame
->IsAbsolutelyPositioned() && !newFrame
->IsFloating()),
6211 "Placeholders should not float or be positioned");
6213 bool isBlock
= newFrame
->IsBlockOutside();
6215 // If the frame is a block frame, or if there is no previous line or if the
6216 // previous line is a block line we need to make a new line. We also make
6217 // a new line, as an optimization, in the two cases we know we'll need it:
6218 // if the previous line ended with a <br>, or if it has significant
6219 // whitespace and ended in a newline.
6220 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
6221 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
6222 // Create a new line for the frame and add its line to the line
6224 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
6225 if (prevSibLine
!= lineList
->end()) {
6226 // Append new line after prevSibLine
6227 lineList
->after_insert(prevSibLine
, line
);
6230 // New line is going before the other lines
6231 lineList
->push_front(line
);
6232 prevSibLine
= lineList
->begin();
6235 prevSibLine
->NoteFrameAdded(newFrame
);
6236 // We're adding inline content to prevSibLine, so we need to mark it
6237 // dirty, ensure its textruns are recomputed, and possibly do the same
6238 // to its previous line since that line may be able to pull content up.
6239 MarkLineDirty(prevSibLine
, lineList
);
6242 aPrevSibling
= newFrame
;
6246 MOZ_ASSERT(aFrameList
.IsEmpty());
6251 nsContainerFrame
* nsBlockFrame::GetRubyContentPseudoFrame() {
6252 auto* firstChild
= PrincipalChildList().FirstChild();
6253 if (firstChild
&& firstChild
->IsRubyFrame() &&
6254 firstChild
->Style()->GetPseudoType() ==
6255 mozilla::PseudoStyleType::blockRubyContent
) {
6256 return static_cast<nsContainerFrame
*>(firstChild
);
6261 nsContainerFrame
* nsBlockFrame::GetContentInsertionFrame() {
6262 // 'display:block ruby' use the inner (Ruby) frame for insertions.
6263 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6264 return rubyContentPseudoFrame
;
6269 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
6270 nsTArray
<OwnedAnonBox
>& aResult
) {
6271 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6272 aResult
.AppendElement(OwnedAnonBox(rubyContentPseudoFrame
));
6276 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
) {
6277 // Find which line contains the float, so we can update
6279 for (auto& line
: Lines()) {
6280 if (line
.IsInline() && line
.RemoveFloat(aFloat
)) {
6286 void nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
6288 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
6289 // frame list properties.
6290 if (!mFloats
.ContainsFrame(aFloat
)) {
6292 (GetOverflowOutOfFlows() &&
6293 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
6294 (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat
)),
6295 "aFloat is not our child or on an unexpected frame list");
6299 if (mFloats
.StartRemoveFrame(aFloat
)) {
6303 nsFrameList
* list
= GetPushedFloats();
6304 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
6306 // XXXmats not yet - need to investigate BlockReflowState::mPushedFloats
6307 // first so we don't leave it pointing to a deleted list.
6308 if (list
->IsEmpty()) {
6309 delete RemovePushedFloats();
6316 nsAutoOOFFrameList
oofs(this);
6317 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
6323 void nsBlockFrame::DoRemoveOutOfFlowFrame(DestroyContext
& aContext
,
6325 // The containing block is always the parent of aFrame.
6326 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
6328 // Remove aFrame from the appropriate list.
6329 if (aFrame
->IsAbsolutelyPositioned()) {
6330 // This also deletes the next-in-flows
6331 block
->GetAbsoluteContainingBlock()->RemoveFrame(
6332 aContext
, FrameChildListID::Absolute
, aFrame
);
6334 // First remove aFrame's next-in-flows.
6335 if (nsIFrame
* nif
= aFrame
->GetNextInFlow()) {
6336 nif
->GetParent()->DeleteNextInFlowChild(aContext
, nif
, false);
6338 // Now remove aFrame from its child list and Destroy it.
6339 block
->RemoveFloatFromFloatCache(aFrame
);
6340 block
->RemoveFloat(aFrame
);
6341 aFrame
->Destroy(aContext
);
6346 * This helps us iterate over the list of all normal + overflow lines
6348 void nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
6349 nsLineList::iterator
* aStartIterator
,
6350 nsLineList::iterator
* aEndIterator
,
6351 bool* aInOverflowLines
,
6352 FrameLines
** aOverflowLines
) {
6353 if (*aIterator
== *aEndIterator
) {
6354 if (!*aInOverflowLines
) {
6355 // Try the overflow lines
6356 *aInOverflowLines
= true;
6357 FrameLines
* lines
= GetOverflowLines();
6359 *aStartIterator
= lines
->mLines
.begin();
6360 *aIterator
= *aStartIterator
;
6361 *aEndIterator
= lines
->mLines
.end();
6362 *aOverflowLines
= lines
;
6368 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6370 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
) {
6371 // This will assert if aLine isn't in mLines of aFrame:
6372 DebugOnly
<bool> check
= aLine
== mFrame
->LinesBegin();
6375 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6380 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
6381 : &aFrame
->mLines
) {}
6383 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6384 bool* aFoundValidLine
)
6385 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6386 mLine
= aFrame
->LinesBegin();
6387 *aFoundValidLine
= FindValidLine();
6390 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState
& aRestyleState
) {
6391 nsIFrame
* letterFrame
= GetFirstLetter();
6396 // Figure out what the right style parent is. This needs to match
6397 // nsCSSFrameConstructor::CreateLetterFrame.
6398 nsIFrame
* inFlowFrame
= letterFrame
;
6399 if (inFlowFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6400 inFlowFrame
= inFlowFrame
->GetPlaceholderFrame();
6402 nsIFrame
* styleParent
= CorrectStyleParentFrame(inFlowFrame
->GetParent(),
6403 PseudoStyleType::firstLetter
);
6404 ComputedStyle
* parentStyle
= styleParent
->Style();
6405 RefPtr
<ComputedStyle
> firstLetterStyle
=
6406 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
6407 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr,
6409 // Note that we don't need to worry about changehints for the continuation
6410 // styles: those will be handled by the styleParent already.
6411 RefPtr
<ComputedStyle
> continuationStyle
=
6412 aRestyleState
.StyleSet().ResolveStyleForFirstLetterContinuation(
6414 UpdateStyleOfOwnedChildFrame(letterFrame
, firstLetterStyle
, aRestyleState
,
6415 Some(continuationStyle
.get()));
6417 // We also want to update the style on the textframe inside the first-letter.
6418 // We don't need to compute a changehint for this, though, since any changes
6419 // to it are handled by the first-letter anyway.
6420 nsIFrame
* textFrame
= letterFrame
->PrincipalChildList().FirstChild();
6421 RefPtr
<ComputedStyle
> firstTextStyle
=
6422 aRestyleState
.StyleSet().ResolveStyleForText(textFrame
->GetContent(),
6424 textFrame
->SetComputedStyle(firstTextStyle
);
6426 // We don't need to update style for textFrame's continuations: it's already
6427 // set up to inherit from parentStyle, which is what we want.
6430 static nsIFrame
* FindChildContaining(nsBlockFrame
* aFrame
,
6431 nsIFrame
* aFindFrame
) {
6432 NS_ASSERTION(aFrame
, "must have frame");
6435 nsIFrame
* block
= aFrame
;
6437 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
6441 block
= block
->GetNextContinuation();
6446 if (!child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6449 aFindFrame
= child
->GetPlaceholderFrame();
6455 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6456 nsIFrame
* aFindFrame
,
6457 bool* aFoundValidLine
)
6458 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6459 *aFoundValidLine
= false;
6461 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
6466 LineIterator line_end
= aFrame
->LinesEnd();
6467 mLine
= aFrame
->LinesBegin();
6468 if (mLine
!= line_end
&& mLine
.next() == line_end
&&
6469 !aFrame
->HasOverflowLines()) {
6470 // The block has a single line - that must be it!
6471 *aFoundValidLine
= true;
6475 // Try to use the cursor if it exists, otherwise fall back to the first line
6476 if (nsLineBox
* const cursor
= aFrame
->GetLineCursorForQuery()) {
6478 // Perform a simultaneous forward and reverse search starting from the
6480 nsBlockFrame::LineIterator line
= aFrame
->LinesBeginFrom(cursor
);
6481 nsBlockFrame::ReverseLineIterator rline
= aFrame
->LinesRBeginFrom(cursor
);
6482 nsBlockFrame::ReverseLineIterator rline_end
= aFrame
->LinesREnd();
6483 // rline is positioned on the line containing 'cursor', so it's not
6484 // rline_end. So we can safely increment it (i.e. move it to one line
6485 // earlier) to start searching there.
6487 while (line
!= line_end
|| rline
!= rline_end
) {
6488 if (line
!= line_end
) {
6489 if (line
->Contains(child
)) {
6495 if (rline
!= rline_end
) {
6496 if (rline
->Contains(child
)) {
6503 if (mLine
!= line_end
) {
6504 *aFoundValidLine
= true;
6505 if (mLine
!= cursor
) {
6506 aFrame
->SetProperty(nsBlockFrame::LineCursorPropertyQuery(), mLine
);
6511 for (mLine
= aFrame
->LinesBegin(); mLine
!= line_end
; ++mLine
) {
6512 if (mLine
->Contains(child
)) {
6513 *aFoundValidLine
= true;
6518 // Didn't find the line
6519 MOZ_ASSERT(mLine
== line_end
, "mLine should be line_end at this point");
6521 // If we reach here, it means that we have not been able to find the
6522 // desired frame in our in-flow lines. So we should start looking at
6523 // our overflow lines. In order to do that, we set mLine to the end
6524 // iterator so that FindValidLine starts to look at overflow lines,
6527 if (!FindValidLine()) {
6532 if (mLine
->Contains(child
)) {
6533 *aFoundValidLine
= true;
6539 nsBlockFrame::LineIterator
nsBlockInFlowLineIterator::End() {
6540 return mLineList
->end();
6543 bool nsBlockInFlowLineIterator::IsLastLineInList() {
6544 LineIterator end
= End();
6545 return mLine
!= end
&& mLine
.next() == end
;
6548 bool nsBlockInFlowLineIterator::Next() {
6550 return FindValidLine();
6553 bool nsBlockInFlowLineIterator::Prev() {
6554 LineIterator begin
= mLineList
->begin();
6555 if (mLine
!= begin
) {
6559 bool currentlyInOverflowLines
= GetInOverflow();
6561 if (currentlyInOverflowLines
) {
6562 mLineList
= &mFrame
->mLines
;
6563 mLine
= mLineList
->end();
6564 if (mLine
!= mLineList
->begin()) {
6569 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
6573 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6574 if (overflowLines
) {
6575 mLineList
= &overflowLines
->mLines
;
6576 mLine
= mLineList
->end();
6577 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
6582 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6586 bool nsBlockInFlowLineIterator::FindValidLine() {
6587 LineIterator end
= mLineList
->end();
6591 bool currentlyInOverflowLines
= GetInOverflow();
6593 if (currentlyInOverflowLines
) {
6594 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
6598 mLineList
= &mFrame
->mLines
;
6599 mLine
= mLineList
->begin();
6600 if (mLine
!= mLineList
->end()) {
6604 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6605 if (overflowLines
) {
6606 mLineList
= &overflowLines
->mLines
;
6607 mLine
= mLineList
->begin();
6608 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
6612 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6616 // This function removes aDeletedFrame and all its continuations. It
6617 // is optimized for deleting a whole series of frames. The easy
6618 // implementation would invoke itself recursively on
6619 // aDeletedFrame->GetNextContinuation, then locate the line containing
6620 // aDeletedFrame and remove aDeletedFrame from that line. But here we
6621 // start by locating aDeletedFrame and then scanning from that point
6622 // on looking for continuations.
6623 void nsBlockFrame::DoRemoveFrame(DestroyContext
& aContext
,
6624 nsIFrame
* aDeletedFrame
, uint32_t aFlags
) {
6625 // Clear our line cursor, since our lines may change.
6628 if (aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6629 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6630 if (!aDeletedFrame
->GetPrevInFlow()) {
6631 NS_ASSERTION(aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6632 "Expected out-of-flow frame");
6633 DoRemoveOutOfFlowFrame(aContext
, aDeletedFrame
);
6635 // FIXME(emilio): aContext is lost here, maybe it's not a big deal?
6636 nsContainerFrame::DeleteNextInFlowChild(aContext
, aDeletedFrame
,
6637 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
6642 // Find the line that contains deletedFrame
6643 nsLineList::iterator line_start
= mLines
.begin(), line_end
= mLines
.end();
6644 nsLineList::iterator line
= line_start
;
6645 FrameLines
* overflowLines
= nullptr;
6646 bool searchingOverflowList
= false;
6647 // Make sure we look in the overflow lines even if the normal line
6649 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6651 while (line
!= line_end
) {
6652 if (line
->Contains(aDeletedFrame
)) {
6656 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6660 if (line
== line_end
) {
6661 NS_ERROR("can't find deleted frame in lines");
6665 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6666 if (line
!= line_start
) {
6667 line
.prev()->MarkDirty();
6668 line
.prev()->SetInvalidateTextRuns(true);
6669 } else if (searchingOverflowList
&& !mLines
.empty()) {
6670 mLines
.back()->MarkDirty();
6671 mLines
.back()->SetInvalidateTextRuns(true);
6675 while (line
!= line_end
&& aDeletedFrame
) {
6676 MOZ_ASSERT(this == aDeletedFrame
->GetParent(), "messed up delete code");
6677 MOZ_ASSERT(line
->Contains(aDeletedFrame
), "frame not in line");
6679 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6681 line
->SetInvalidateTextRuns(true);
6684 // If the frame being deleted is the last one on the line then
6685 // optimize away the line->Contains(next-in-flow) call below.
6686 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
6687 if (!isLastFrameOnLine
) {
6688 LineIterator next
= line
.next();
6689 nsIFrame
* lastFrame
=
6691 ? next
->mFirstChild
->GetPrevSibling()
6692 : (searchingOverflowList
? overflowLines
->mFrames
.LastChild()
6693 : mFrames
.LastChild());
6694 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
6695 "unexpected line frames");
6696 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
6699 // Remove aDeletedFrame from the line
6700 if (line
->mFirstChild
== aDeletedFrame
) {
6701 // We should be setting this to null if aDeletedFrame
6702 // is the only frame on the line. HOWEVER in that case
6703 // we will be removing the line anyway, see below.
6704 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
6707 // Hmm, this won't do anything if we're removing a frame in the first
6708 // overflow line... Hopefully doesn't matter
6710 if (line
!= line_end
&& !line
->IsBlock()) {
6711 // Since we just removed a frame that follows some inline
6712 // frames, we need to reflow the previous line.
6717 // Take aDeletedFrame out of the sibling list. Note that
6718 // prevSibling will only be nullptr when we are deleting the very
6719 // first frame in the main or overflow list.
6720 if (searchingOverflowList
) {
6721 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
6723 mFrames
.RemoveFrame(aDeletedFrame
);
6726 // Update the child count of the line to be accurate
6727 line
->NoteFrameRemoved(aDeletedFrame
);
6729 // Destroy frame; capture its next continuation first in case we need
6730 // to destroy that too.
6731 nsIFrame
* deletedNextContinuation
=
6732 (aFlags
& REMOVE_FIXED_CONTINUATIONS
)
6733 ? aDeletedFrame
->GetNextContinuation()
6734 : aDeletedFrame
->GetNextInFlow();
6735 #ifdef NOISY_REMOVE_FRAME
6736 printf("DoRemoveFrame: %s line=%p frame=",
6737 searchingOverflowList
? "overflow" : "normal", line
.get());
6738 aDeletedFrame
->ListTag(stdout
);
6739 printf(" prevSibling=%p deletedNextContinuation=%p\n",
6740 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
6743 // If next-in-flow is an overflow container, must remove it first.
6744 // FIXME: Can we do this unconditionally?
6745 if (deletedNextContinuation
&& deletedNextContinuation
->HasAnyStateBits(
6746 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6747 deletedNextContinuation
->GetParent()->DeleteNextInFlowChild(
6748 aContext
, deletedNextContinuation
, false);
6749 deletedNextContinuation
= nullptr;
6752 aDeletedFrame
->Destroy(aContext
);
6753 aDeletedFrame
= deletedNextContinuation
;
6755 bool haveAdvancedToNextLine
= false;
6756 // If line is empty, remove it now.
6757 if (0 == line
->GetChildCount()) {
6758 #ifdef NOISY_REMOVE_FRAME
6759 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6760 searchingOverflowList
? "overflow" : "normal", line
.get());
6762 nsLineBox
* cur
= line
;
6763 if (!searchingOverflowList
) {
6764 line
= mLines
.erase(line
);
6765 // Invalidate the space taken up by the line.
6766 // XXX We need to do this if we're removing a frame as a result of
6767 // a call to RemoveFrame(), but we may not need to do this in all
6769 #ifdef NOISY_BLOCK_INVALIDATE
6770 nsRect
inkOverflow(cur
->InkOverflowRect());
6771 printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow
.x
,
6772 inkOverflow
.y
, inkOverflow
.width
, inkOverflow
.height
);
6775 line
= overflowLines
->mLines
.erase(line
);
6776 if (overflowLines
->mLines
.empty()) {
6777 DestroyOverflowLines();
6778 overflowLines
= nullptr;
6779 // We just invalidated our iterators. Since we were in
6780 // the overflow lines list, which is now empty, set them
6781 // so we're at the end of the regular line list.
6782 line_start
= mLines
.begin();
6783 line_end
= mLines
.end();
6789 // If we're removing a line, ReflowDirtyLines isn't going to
6790 // know that it needs to slide lines unless something is marked
6791 // dirty. So mark the previous margin of the next line dirty if
6793 if (line
!= line_end
) {
6794 line
->MarkPreviousMarginDirty();
6796 haveAdvancedToNextLine
= true;
6798 // Make the line that just lost a frame dirty, and advance to
6800 if (!deletedNextContinuation
|| isLastFrameOnLine
||
6801 !line
->Contains(deletedNextContinuation
)) {
6804 haveAdvancedToNextLine
= true;
6808 if (deletedNextContinuation
) {
6809 // See if we should keep looking in the current flow's line list.
6810 if (deletedNextContinuation
->GetParent() != this) {
6811 // The deceased frames continuation is not a child of the
6812 // current block. So break out of the loop so that we advance
6813 // to the next parent.
6815 // If we have a continuation in a different block then all bets are
6816 // off regarding whether we are deleting frames without actual content,
6817 // so don't propagate FRAMES_ARE_EMPTY any further.
6818 aFlags
&= ~FRAMES_ARE_EMPTY
;
6822 // If we advanced to the next line then check if we should switch to the
6823 // overflow line list.
6824 if (haveAdvancedToNextLine
) {
6825 if (line
!= line_end
&& !searchingOverflowList
&&
6826 !line
->Contains(deletedNextContinuation
)) {
6827 // We have advanced to the next *normal* line but the next-in-flow
6828 // is not there - force a switch to the overflow line list.
6832 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6834 #ifdef NOISY_REMOVE_FRAME
6835 printf("DoRemoveFrame: now on %s line=%p\n",
6836 searchingOverflowList
? "overflow" : "normal", line
.get());
6842 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
6843 line
.next()->MarkDirty();
6844 line
.next()->SetInvalidateTextRuns(true);
6849 VerifyOverflowSituation();
6852 // Advance to next flow block if the frame has more continuations.
6853 if (!aDeletedFrame
) {
6856 nsBlockFrame
* nextBlock
= do_QueryFrame(aDeletedFrame
->GetParent());
6857 NS_ASSERTION(nextBlock
, "Our child's continuation's parent is not a block?");
6858 uint32_t flags
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
);
6859 nextBlock
->DoRemoveFrame(aContext
, aDeletedFrame
, flags
);
6862 static bool FindBlockLineFor(nsIFrame
* aChild
, nsLineList::iterator aBegin
,
6863 nsLineList::iterator aEnd
,
6864 nsLineList::iterator
* aResult
) {
6865 MOZ_ASSERT(aChild
->IsBlockOutside());
6866 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6867 MOZ_ASSERT(line
->GetChildCount() > 0);
6868 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
6869 MOZ_ASSERT(line
->GetChildCount() == 1);
6877 static bool FindInlineLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6878 nsLineList::iterator aBegin
,
6879 nsLineList::iterator aEnd
,
6880 nsLineList::iterator
* aResult
) {
6881 MOZ_ASSERT(!aChild
->IsBlockOutside());
6882 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6883 MOZ_ASSERT(line
->GetChildCount() > 0);
6884 if (!line
->IsBlock()) {
6885 // Optimize by comparing the line's last child first.
6886 nsLineList::iterator next
= line
.next();
6887 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
6888 : next
->mFirstChild
->GetPrevSibling()) ||
6889 line
->Contains(aChild
)) {
6898 static bool FindLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6899 nsLineList::iterator aBegin
, nsLineList::iterator aEnd
,
6900 nsLineList::iterator
* aResult
) {
6901 return aChild
->IsBlockOutside()
6902 ? FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
)
6903 : FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
6906 void nsBlockFrame::StealFrame(nsIFrame
* aChild
) {
6907 MOZ_ASSERT(aChild
->GetParent() == this);
6909 if (aChild
->IsFloating()) {
6910 RemoveFloat(aChild
);
6914 if (MaybeStealOverflowContainerFrame(aChild
)) {
6918 MOZ_ASSERT(!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
6920 nsLineList::iterator line
;
6921 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
6922 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
6924 FrameLines
* overflowLines
= GetOverflowLines();
6925 DebugOnly
<bool> found
;
6926 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
6927 overflowLines
->mLines
.begin(),
6928 overflowLines
->mLines
.end(), &line
);
6929 MOZ_ASSERT(found
, "Why can't we find aChild in our overflow lines?");
6930 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
6931 overflowLines
->mLines
);
6932 if (overflowLines
->mLines
.empty()) {
6933 DestroyOverflowLines();
6938 void nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
,
6939 nsLineList::iterator aLine
,
6940 nsFrameList
& aFrameList
,
6941 nsLineList
& aLineList
) {
6942 aFrameList
.RemoveFrame(aChild
);
6943 if (aChild
== aLine
->mFirstChild
) {
6944 aLine
->mFirstChild
= aChild
->GetNextSibling();
6946 aLine
->NoteFrameRemoved(aChild
);
6947 if (aLine
->GetChildCount() > 0) {
6950 // The line became empty - destroy it.
6951 nsLineBox
* lineBox
= aLine
;
6952 aLine
= aLineList
.erase(aLine
);
6953 if (aLine
!= aLineList
.end()) {
6954 aLine
->MarkPreviousMarginDirty();
6956 FreeLineBox(lineBox
);
6960 void nsBlockFrame::DeleteNextInFlowChild(DestroyContext
& aContext
,
6961 nsIFrame
* aNextInFlow
,
6962 bool aDeletingEmptyFrames
) {
6963 MOZ_ASSERT(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
6965 if (aNextInFlow
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6966 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6967 nsContainerFrame::DeleteNextInFlowChild(aContext
, aNextInFlow
,
6968 aDeletingEmptyFrames
);
6971 if (aDeletingEmptyFrames
) {
6972 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
6975 DoRemoveFrame(aContext
, aNextInFlow
,
6976 aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
6980 const nsStyleText
* nsBlockFrame::StyleTextForLineLayout() {
6981 // Return the pointer to an unmodified style text
6985 void nsBlockFrame::ReflowFloat(BlockReflowState
& aState
, ReflowInput
& aFloatRI
,
6987 nsReflowStatus
& aReflowStatus
) {
6988 MOZ_ASSERT(aReflowStatus
.IsEmpty(),
6989 "Caller should pass a fresh reflow status!");
6990 MOZ_ASSERT(aFloat
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6991 "aFloat must be an out-of-flow frame");
6993 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
6995 // Setup a block reflow context to reflow the float.
6996 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
6998 nsIFrame
* clearanceFrame
= nullptr;
7000 nsCollapsingMargin margin
;
7001 bool mayNeedRetry
= false;
7002 aFloatRI
.mDiscoveredClearance
= nullptr;
7003 // Only first in flow gets a block-start margin.
7004 if (!aFloat
->GetPrevInFlow()) {
7005 brc
.ComputeCollapsedBStartMargin(aFloatRI
, &margin
, clearanceFrame
,
7008 if (mayNeedRetry
&& !clearanceFrame
) {
7009 aFloatRI
.mDiscoveredClearance
= &clearanceFrame
;
7010 // We don't need to push the float manager state because the the block
7011 // has its own float manager that will be destroyed and recreated
7015 // When reflowing a float, aSpace argument doesn't matter because we pass
7016 // nullptr to aLine and we don't call nsBlockReflowContext::PlaceBlock()
7018 brc
.ReflowBlock(LogicalRect(wm
), true, margin
, 0, nullptr, aFloatRI
,
7019 aReflowStatus
, aState
);
7020 } while (clearanceFrame
);
7022 if (aFloat
->IsLetterFrame()) {
7023 // We never split floating first letters; an incomplete status for such
7024 // frames simply means that there is more content to be reflowed on the
7026 if (aReflowStatus
.IsIncomplete()) {
7027 aReflowStatus
.Reset();
7031 NS_ASSERTION(aReflowStatus
.IsFullyComplete() ||
7032 aFloatRI
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
7033 "The status can only be incomplete or overflow-incomplete if "
7034 "the available block-size is constrained!");
7036 if (aReflowStatus
.NextInFlowNeedsReflow()) {
7037 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
7040 const ReflowOutput
& metrics
= brc
.GetMetrics();
7042 // Set the rect, make sure the view is properly sized and positioned,
7043 // and tell the frame we're done reflowing it
7044 // XXXldb This seems like the wrong place to be doing this -- shouldn't
7045 // we be doing this in BlockReflowState::FlowAndPlaceFloat after
7046 // we've positioned the float, and shouldn't we be doing the equivalent
7047 // of |PlaceFrameView| here?
7048 WritingMode metricsWM
= metrics
.GetWritingMode();
7049 aFloat
->SetSize(metricsWM
, metrics
.Size(metricsWM
));
7050 if (aFloat
->HasView()) {
7051 nsContainerFrame::SyncFrameViewAfterReflow(
7052 aState
.mPresContext
, aFloat
, aFloat
->GetView(), metrics
.InkOverflow(),
7053 ReflowChildFlags::NoMoveView
);
7055 aFloat
->DidReflow(aState
.mPresContext
, &aFloatRI
);
7058 StyleClear
nsBlockFrame::FindTrailingClear() {
7059 for (nsBlockFrame
* b
= this; b
;
7060 b
= static_cast<nsBlockFrame
*>(b
->GetPrevInFlow())) {
7061 auto endLine
= b
->LinesRBegin();
7062 if (endLine
!= b
->LinesREnd()) {
7063 return endLine
->FloatClearTypeAfter();
7066 return StyleClear::None
;
7069 void nsBlockFrame::ReflowPushedFloats(BlockReflowState
& aState
,
7070 OverflowAreas
& aOverflowAreas
) {
7071 // Pushed floats live at the start of our float list; see comment
7072 // above nsBlockFrame::DrainPushedFloats.
7073 nsIFrame
* f
= mFloats
.FirstChild();
7074 nsIFrame
* prev
= nullptr;
7075 while (f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7076 MOZ_ASSERT(prev
== f
->GetPrevSibling());
7077 // When we push a first-continuation float in a non-initial reflow,
7078 // it's possible that we end up with two continuations with the same
7079 // parent. This happens if, on the previous reflow of the block or
7080 // a previous reflow of the line containing the block, the float was
7081 // split between continuations A and B of the parent, but on the
7082 // current reflow, none of the float can fit in A.
7084 // When this happens, we might even have the two continuations
7085 // out-of-order due to the management of the pushed floats. In
7086 // particular, if the float's placeholder was in a pushed line that
7087 // we reflowed before it was pushed, and we split the float during
7088 // that reflow, we might have the continuation of the float before
7089 // the float itself. (In the general case, however, it's correct
7090 // for floats in the pushed floats list to come before floats
7091 // anchored in pushed lines; however, in this case it's wrong. We
7092 // should probably find a way to fix it somehow, since it leads to
7093 // incorrect layout in some cases.)
7095 // When we have these out-of-order continuations, we might hit the
7096 // next-continuation before the previous-continuation. When that
7097 // happens, just push it. When we reflow the next continuation,
7098 // we'll either pull all of its content back and destroy it (by
7099 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
7100 // pull it out of its current position and push it again (and
7101 // potentially repeat this cycle for the next continuation, although
7102 // hopefully then they'll be in the right order).
7104 // We should also need this code for the in-order case if the first
7105 // continuation of a float gets moved across more than one
7106 // continuation of the containing block. In this case we'd manage
7107 // to push the second continuation without this check, but not the
7109 nsIFrame
* prevContinuation
= f
->GetPrevContinuation();
7110 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
7111 mFloats
.RemoveFrame(f
);
7112 aState
.AppendPushedFloatChain(f
);
7113 f
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7117 // Always call FlowAndPlaceFloat; we might need to place this float if it
7118 // didn't belong to this block the last time it was reflowed. Note that if
7119 // the float doesn't get placed, we don't consider its overflow areas.
7120 // (Not-getting-placed means it didn't fit and we pushed it instead of
7121 // placing it, and its position could be stale.)
7122 if (aState
.FlowAndPlaceFloat(f
) ==
7123 BlockReflowState::PlaceFloatResult::Placed
) {
7124 ConsiderChildOverflow(aOverflowAreas
, f
);
7127 nsIFrame
* next
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7129 // We didn't push |f| so its next-sibling is next.
7130 next
= f
->GetNextSibling();
7132 } // else: we did push |f| so |prev|'s new next-sibling is next.
7136 // If there are pushed or split floats, then we may need to continue BR
7138 if (auto [bCoord
, result
] = aState
.ClearFloats(0, StyleClear::Both
);
7139 result
!= ClearFloatsResult::BCoordNoChange
) {
7141 if (auto* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow())) {
7142 aState
.mTrailingClearFromPIF
= prevBlock
->FindTrailingClear();
7147 void nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
, WritingMode aWM
,
7148 const nsSize
& aContainerSize
) {
7149 // Recover our own floats
7150 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
7151 // belong to our next-in-flow
7152 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
;
7153 f
= f
->GetNextSibling()) {
7154 LogicalRect region
= nsFloatManager::GetRegionFor(aWM
, f
, aContainerSize
);
7155 aFloatManager
.AddFloat(f
, region
, aWM
, aContainerSize
);
7156 if (!stop
&& f
->GetNextInFlow()) {
7157 stop
= f
->GetNextInFlow();
7161 // Recurse into our overflow container children
7163 GetChildList(FrameChildListID::OverflowContainers
).FirstChild();
7164 oc
; oc
= oc
->GetNextSibling()) {
7165 RecoverFloatsFor(oc
, aFloatManager
, aWM
, aContainerSize
);
7168 // Recurse into our normal children
7169 for (const auto& line
: Lines()) {
7170 if (line
.IsBlock()) {
7171 RecoverFloatsFor(line
.mFirstChild
, aFloatManager
, aWM
, aContainerSize
);
7176 void nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
7177 nsFloatManager
& aFloatManager
,
7179 const nsSize
& aContainerSize
) {
7180 MOZ_ASSERT(aFrame
, "null frame");
7182 // Only blocks have floats
7183 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
7184 // Don't recover any state inside a block that has its own float manager
7185 // (we don't currently have any blocks like this, though, thanks to our
7186 // use of extra frames for 'overflow')
7187 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
7188 // If the element is relatively positioned, then adjust x and y
7189 // accordingly so that we consider relatively positioned frames
7190 // at their original position.
7192 const LogicalRect rect
= block
->GetLogicalNormalRect(aWM
, aContainerSize
);
7193 nscoord lineLeft
= rect
.LineLeft(aWM
, aContainerSize
);
7194 nscoord blockStart
= rect
.BStart(aWM
);
7195 aFloatManager
.Translate(lineLeft
, blockStart
);
7196 block
->RecoverFloats(aFloatManager
, aWM
, aContainerSize
);
7197 aFloatManager
.Translate(-lineLeft
, -blockStart
);
7201 bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
7202 if (!mFloats
.IsEmpty()) {
7203 // If we have pushed floats, then they should be at the beginning of our
7205 if (mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7211 // Double-check the above assertion that pushed floats should be at the
7212 // beginning of our floats list.
7213 for (nsIFrame
* f
: mFloats
) {
7214 NS_ASSERTION(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
7215 "pushed floats must be at the beginning of the float list");
7219 // We may have a pending push of pushed floats too:
7220 if (HasPushedFloats()) {
7221 // XXX we can return 'true' here once we make HasPushedFloats
7222 // not lie. (see nsBlockFrame::RemoveFloat)
7223 auto* pushedFloats
= GetPushedFloats();
7224 return pushedFloats
&& !pushedFloats
->IsEmpty();
7229 //////////////////////////////////////////////////////////////////////
7230 // Painting, event handling
7233 static void ComputeInkOverflowArea(nsLineList
& aLines
, nscoord aWidth
,
7234 nscoord aHeight
, nsRect
& aResult
) {
7235 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
7236 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
7237 line
!= line_end
; ++line
) {
7238 // Compute min and max x/y values for the reflowed frame's
7240 nsRect
inkOverflow(line
->InkOverflowRect());
7241 nscoord x
= inkOverflow
.x
;
7242 nscoord y
= inkOverflow
.y
;
7243 nscoord xmost
= x
+ inkOverflow
.width
;
7244 nscoord ymost
= y
+ inkOverflow
.height
;
7261 aResult
.width
= xb
- xa
;
7262 aResult
.height
= yb
- ya
;
7267 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
7268 if (nsBlockFrame::gNoisyDamageRepair
) {
7269 nsIFrame::IndentBy(stdout
, aDepth
+ 1);
7270 nsRect lineArea
= aLine
->InkOverflowRect();
7271 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7272 aDrawn
? "draw" : "skip", static_cast<void*>(aLine
), aLine
->IStart(),
7273 aLine
->BStart(), aLine
->ISize(), aLine
->BSize(), lineArea
.x
,
7274 lineArea
.y
, lineArea
.width
, lineArea
.height
);
7279 static void DisplayLine(nsDisplayListBuilder
* aBuilder
,
7280 nsBlockFrame::LineIterator
& aLine
,
7281 const bool aLineInLine
, const nsDisplayListSet
& aLists
,
7282 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
,
7283 uint32_t aLineNumberForTextOverflow
, int32_t aDepth
,
7284 int32_t& aDrawnLines
) {
7286 if (nsBlockFrame::gLamePaintMetrics
) {
7289 const bool intersect
=
7290 aLine
->InkOverflowRect().Intersects(aBuilder
->GetDirtyRect());
7291 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
7294 // Collect our line's display items in a temporary nsDisplayListCollection,
7295 // so that we can apply any "text-overflow" clipping to the entire collection
7296 // without affecting previous lines.
7297 nsDisplayListCollection
collection(aBuilder
);
7299 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
7300 // Inline-level child backgrounds go on the regular child content list.
7301 nsDisplayListSet
childLists(
7303 aLineInLine
? collection
.Content() : collection
.BlockBorderBackgrounds());
7307 ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline
)
7308 : nsIFrame::DisplayChildFlags();
7310 nsIFrame
* kid
= aLine
->mFirstChild
;
7311 int32_t n
= aLine
->GetChildCount();
7313 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, childLists
, flags
);
7314 kid
= kid
->GetNextSibling();
7317 if (aTextOverflow
&& aLineInLine
) {
7318 aTextOverflow
->ProcessLine(collection
, aLine
.get(),
7319 aLineNumberForTextOverflow
);
7322 collection
.MoveTo(aLists
);
7325 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
7326 const nsDisplayListSet
& aLists
) {
7327 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
7330 if (gNoisyDamageRepair
) {
7331 nsRect dirty
= aBuilder
->GetDirtyRect();
7334 ::ComputeInkOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
7335 nsIFrame::IndentBy(stdout
, depth
);
7337 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7338 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
, dirty
.x
, dirty
.y
,
7339 dirty
.width
, dirty
.height
, ca
.x
, ca
.y
, ca
.width
, ca
.height
);
7341 PRTime start
= 0; // Initialize these variables to silence the compiler.
7342 if (gLamePaintMetrics
) {
7348 // TODO(heycam): Should we boost the load priority of any shape-outside
7349 // images using CATEGORY_DISPLAY, now that this block is being displayed?
7350 // We don't have a float manager here.
7352 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
7354 if (GetPrevInFlow()) {
7355 DisplayOverflowContainers(aBuilder
, aLists
);
7356 for (nsIFrame
* f
: mFloats
) {
7357 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7358 BuildDisplayListForChild(aBuilder
, f
, aLists
);
7363 aBuilder
->MarkFramesForDisplayList(this, mFloats
);
7365 if (HasOutsideMarker()) {
7366 // Display outside ::marker manually.
7367 BuildDisplayListForChild(aBuilder
, GetOutsideMarker(), aLists
);
7370 // Prepare for text-overflow processing.
7371 Maybe
<TextOverflow
> textOverflow
=
7372 TextOverflow::WillProcessLines(aBuilder
, this);
7374 const bool hasDescendantPlaceHolders
=
7375 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7376 ForceDescendIntoIfVisible() || aBuilder
->GetIncludeAllOutOfFlows();
7378 const auto ShouldDescendIntoLine
= [&](const nsRect
& aLineArea
) -> bool {
7379 // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
7380 // some frame trees, building display list for child lines can change it.
7382 const bool descendAlways
=
7383 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7384 aBuilder
->GetIncludeAllOutOfFlows();
7386 return descendAlways
|| aLineArea
.Intersects(aBuilder
->GetDirtyRect()) ||
7387 (ForceDescendIntoIfVisible() &&
7388 aLineArea
.Intersects(aBuilder
->GetVisibleRect()));
7391 Maybe
<nscolor
> backplateColor
;
7393 // We'll try to draw an accessibility backplate behind text (to ensure it's
7394 // readable over any possible background-images), if all of the following
7396 // (A) the backplate feature is preffed on
7397 // (B) we are not honoring the document colors
7398 // (C) the force color adjust property is set to auto
7399 if (StaticPrefs::browser_display_permit_backplate() &&
7400 PresContext()->ForcingColors() && !IsComboboxControlFrame() &&
7401 StyleText()->mForcedColorAdjust
!= StyleForcedColorAdjust::None
) {
7402 backplateColor
.emplace(GetBackplateColor(this));
7405 // Don't use the line cursor if we might have a descendant placeholder ...
7406 // it might skip lines that contain placeholders but don't themselves
7407 // intersect with the dirty area.
7408 // In particular, we really want to check ShouldDescendIntoFrame()
7409 // on all our child frames, but that might be expensive. So we
7410 // approximate it by checking it on |this|; if it's true for any
7411 // frame in our child list, it's also true for |this|.
7412 // Also skip the cursor if we're creating text overflow markers,
7413 // since we need to know what line number we're up to in order
7414 // to generate unique display item keys.
7415 // Lastly, the cursor should be skipped if we're drawing
7416 // backplates behind text. When backplating we consider consecutive
7417 // runs of text as a whole, which requires we iterate through all lines
7418 // to find our backplate size.
7420 (hasDescendantPlaceHolders
|| textOverflow
.isSome() || backplateColor
)
7422 : GetFirstLineContaining(aBuilder
->GetDirtyRect().y
);
7423 LineIterator line_end
= LinesEnd();
7425 TextOverflow
* textOverflowPtr
= textOverflow
.ptrOr(nullptr);
7428 for (LineIterator line
= mLines
.begin(cursor
); line
!= line_end
; ++line
) {
7429 const nsRect lineArea
= line
->InkOverflowRect();
7430 if (!lineArea
.IsEmpty()) {
7431 // Because we have a cursor, the combinedArea.ys are non-decreasing.
7432 // Once we've passed aDirtyRect.YMost(), we can never see it again.
7433 if (lineArea
.y
>= aBuilder
->GetDirtyRect().YMost()) {
7436 MOZ_ASSERT(textOverflow
.isNothing());
7438 if (ShouldDescendIntoLine(lineArea
)) {
7439 DisplayLine(aBuilder
, line
, line
->IsInline(), aLists
, this, nullptr,
7440 0, depth
, drawnLines
);
7445 bool nonDecreasingYs
= true;
7446 uint32_t lineCount
= 0;
7447 nscoord lastY
= INT32_MIN
;
7448 nscoord lastYMost
= INT32_MIN
;
7450 // A frame's display list cannot contain more than one copy of a
7451 // given display item unless the items are uniquely identifiable.
7452 // Because backplate occasionally requires multiple
7453 // SolidColor items, we use an index (backplateIndex) to maintain
7454 // uniqueness among them. Note this is a mapping of index to
7455 // item, and the mapping is stable even if the dirty rect changes.
7456 uint16_t backplateIndex
= 0;
7457 nsRect curBackplateArea
;
7459 auto AddBackplate
= [&]() {
7460 aLists
.BorderBackground()->AppendNewToTopWithIndex
<nsDisplaySolidColor
>(
7461 aBuilder
, this, backplateIndex
, curBackplateArea
,
7462 backplateColor
.value());
7465 for (LineIterator line
= LinesBegin(); line
!= line_end
; ++line
) {
7466 const nsRect lineArea
= line
->InkOverflowRect();
7467 const bool lineInLine
= line
->IsInline();
7469 if ((lineInLine
&& textOverflowPtr
) || ShouldDescendIntoLine(lineArea
)) {
7470 DisplayLine(aBuilder
, line
, lineInLine
, aLists
, this, textOverflowPtr
,
7471 lineCount
, depth
, drawnLines
);
7474 if (!lineInLine
&& !curBackplateArea
.IsEmpty()) {
7475 // If we have encountered a non-inline line but were previously
7476 // forming a backplate, we should add the backplate to the display
7477 // list as-is and render future backplates disjointly.
7478 MOZ_ASSERT(backplateColor
,
7479 "if this master switch is off, curBackplateArea "
7480 "must be empty and we shouldn't get here");
7483 curBackplateArea
= nsRect();
7486 if (!lineArea
.IsEmpty()) {
7487 if (lineArea
.y
< lastY
|| lineArea
.YMost() < lastYMost
) {
7488 nonDecreasingYs
= false;
7491 lastYMost
= lineArea
.YMost();
7492 if (lineInLine
&& backplateColor
&& LineHasVisibleInlineContent(line
)) {
7493 nsRect lineBackplate
= GetLineTextArea(line
, aBuilder
) +
7494 aBuilder
->ToReferenceFrame(this);
7495 if (curBackplateArea
.IsEmpty()) {
7496 curBackplateArea
= lineBackplate
;
7498 curBackplateArea
.OrWith(lineBackplate
);
7505 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
7506 SetupLineCursorForDisplay();
7509 if (!curBackplateArea
.IsEmpty()) {
7514 if (textOverflow
.isSome()) {
7515 // Put any text-overflow:ellipsis markers on top of the non-positioned
7516 // content of the block's lines. (If we ever start sorting the Content()
7517 // list this will end up in the wrong place.)
7518 aLists
.Content()->AppendToTop(&textOverflow
->GetMarkers());
7522 if (gLamePaintMetrics
) {
7523 PRTime end
= PR_Now();
7525 int32_t numLines
= mLines
.size();
7529 PRTime lines
, deltaPerLine
, delta
;
7530 lines
= int64_t(numLines
);
7531 delta
= end
- start
;
7532 deltaPerLine
= delta
/ lines
;
7537 ": %" PRId64
" elapsed (%" PRId64
7538 " per line) lines=%d drawn=%d skip=%d",
7539 delta
, deltaPerLine
, numLines
, drawnLines
,
7540 numLines
- drawnLines
);
7541 printf("%s\n", buf
);
7546 #ifdef ACCESSIBILITY
7547 a11y::AccType
nsBlockFrame::AccessibleType() {
7548 if (IsTableCaption()) {
7549 return GetRect().IsEmpty() ? a11y::eNoType
: a11y::eHTMLCaptionType
;
7552 // block frame may be for <hr>
7553 if (mContent
->IsHTMLElement(nsGkAtoms::hr
)) {
7554 return a11y::eHTMLHRType
;
7557 if (!HasMarker() || !PresContext()) {
7558 // XXXsmaug What if we're in the shadow dom?
7559 if (!mContent
->GetParent()) {
7560 // Don't create accessible objects for the root content node, they are
7561 // redundant with the nsDocAccessible object created with the document
7563 return a11y::eNoType
;
7566 if (mContent
== mContent
->OwnerDoc()->GetBody()) {
7567 // Don't create accessible objects for the body, they are redundant with
7568 // the nsDocAccessible object created with the document node
7569 return a11y::eNoType
;
7572 // Not a list item with a ::marker, treat as normal HTML container.
7573 return a11y::eHyperTextType
;
7576 // Create special list item accessible since we have a ::marker.
7577 return a11y::eHTMLLiType
;
7581 void nsBlockFrame::SetupLineCursorForDisplay() {
7582 if (mLines
.empty() || HasProperty(LineCursorPropertyDisplay())) {
7586 SetProperty(LineCursorPropertyDisplay(), mLines
.front());
7587 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7590 void nsBlockFrame::SetupLineCursorForQuery() {
7591 if (mLines
.empty() || HasProperty(LineCursorPropertyQuery())) {
7595 SetProperty(LineCursorPropertyQuery(), mLines
.front());
7596 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7599 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
) {
7600 // Although this looks like a "querying" method, it is used by the
7601 // display-list building code, so uses the Display cursor.
7602 nsLineBox
* property
= GetLineCursorForDisplay();
7606 LineIterator cursor
= mLines
.begin(property
);
7607 nsRect cursorArea
= cursor
->InkOverflowRect();
7609 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
) &&
7610 cursor
!= mLines
.front()) {
7611 cursor
= cursor
.prev();
7612 cursorArea
= cursor
->InkOverflowRect();
7614 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
) &&
7615 cursor
!= mLines
.back()) {
7616 cursor
= cursor
.next();
7617 cursorArea
= cursor
->InkOverflowRect();
7620 if (cursor
.get() != property
) {
7621 SetProperty(LineCursorPropertyDisplay(), cursor
.get());
7624 return cursor
.get();
7628 void nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
) {
7629 // See if the child is absolutely positioned
7630 if (aChild
->IsAbsolutelyPositioned()) {
7632 } else if (aChild
== GetOutsideMarker()) {
7633 // The ::marker lives in the first line, unless the first line has
7634 // height 0 and there is a second line, in which case it lives
7635 // in the second line.
7636 LineIterator markerLine
= LinesBegin();
7637 if (markerLine
!= LinesEnd() && markerLine
->BSize() == 0 &&
7638 markerLine
!= mLines
.back()) {
7639 markerLine
= markerLine
.next();
7642 if (markerLine
!= LinesEnd()) {
7643 MarkLineDirty(markerLine
, &mLines
);
7645 // otherwise we have an empty line list, and ReflowDirtyLines
7646 // will handle reflowing the ::marker.
7648 // Note that we should go through our children to mark lines dirty
7649 // before the next reflow. Doing it now could make things O(N^2)
7650 // since finding the right line is O(N).
7651 // We don't need to worry about marking lines on the overflow list
7652 // as dirty; we're guaranteed to reflow them if we take them off the
7654 // However, we might have gotten a float, in which case we need to
7655 // reflow the line containing its placeholder. So find the
7656 // ancestor-or-self of the placeholder that's a child of the block,
7657 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
7658 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7659 // We need to take some care to handle the case where a float is in
7660 // a different continuation than its placeholder, including marking
7661 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7662 if (!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
7663 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7665 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
7666 nsIFrame
* thisFC
= FirstContinuation();
7667 nsIFrame
* placeholderPath
= aChild
->GetPlaceholderFrame();
7668 // SVG code sometimes sends FrameNeedsReflow notifications during
7669 // frame destruction, leading to null placeholders, but we're safe
7671 if (placeholderPath
) {
7673 nsIFrame
* parent
= placeholderPath
->GetParent();
7674 if (parent
->GetContent() == mContent
&&
7675 parent
->FirstContinuation() == thisFC
) {
7676 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7679 placeholderPath
= parent
;
7681 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
7686 nsContainerFrame::ChildIsDirty(aChild
);
7689 void nsBlockFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
7690 nsIFrame
* aPrevInFlow
) {
7691 // These are all the block specific frame bits, they are copied from
7692 // the prev-in-flow to a newly created next-in-flow, except for the
7693 // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
7694 constexpr nsFrameState NS_BLOCK_FLAGS_MASK
=
7695 NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
|
7696 NS_BLOCK_CLIP_PAGINATED_OVERFLOW
| NS_BLOCK_HAS_FIRST_LETTER_STYLE
|
7697 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7698 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7700 // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
7701 // by default. They should only be set on the first-in-flow.
7702 constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK
=
7703 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7704 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7707 // Copy over the inherited block frame bits from the prev-in-flow.
7708 RemoveStateBits(NS_BLOCK_FLAGS_MASK
);
7709 AddStateBits(aPrevInFlow
->GetStateBits() &
7710 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
7713 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
7716 aPrevInFlow
->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
7717 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
7720 // A display:flow-root box establishes a block formatting context.
7722 // If a box has a different writing-mode value than its containing block:
7724 // If the box is a block container, then it establishes a new block
7725 // formatting context.
7726 // (https://drafts.csswg.org/css-writing-modes/#block-flow)
7728 // If the box has contain: paint or contain:layout (or contain:strict),
7729 // then it should also establish a formatting context.
7731 // Per spec, a column-span always establishes a new block formatting context.
7732 if (StyleDisplay()->mDisplay
== mozilla::StyleDisplay::FlowRoot
||
7734 (GetWritingMode().GetBlockDir() !=
7735 GetParent()->GetWritingMode().GetBlockDir() ||
7736 GetWritingMode().IsVerticalSideways() !=
7737 GetParent()->GetWritingMode().IsVerticalSideways())) ||
7738 StyleDisplay()->IsContainPaint() || StyleDisplay()->IsContainLayout() ||
7740 AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7743 if (HasAllStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
| NS_BLOCK_FLOAT_MGR
)) {
7744 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
7748 void nsBlockFrame::SetInitialChildList(ChildListID aListID
,
7749 nsFrameList
&& aChildList
) {
7750 if (FrameChildListID::Float
== aListID
) {
7751 mFloats
= std::move(aChildList
);
7752 } else if (FrameChildListID::Principal
== aListID
) {
7754 // The only times a block that is an anonymous box is allowed to have a
7755 // first-letter frame are when it's the block inside a non-anonymous cell,
7756 // the block inside a fieldset, button or column set, or a scrolled content
7757 // block, except for <select>. Note that this means that blocks which are
7758 // the anonymous block in {ib} splits do NOT get first-letter frames.
7759 // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7761 auto pseudo
= Style()->GetPseudoType();
7762 bool haveFirstLetterStyle
=
7763 (pseudo
== PseudoStyleType::NotPseudo
||
7764 (pseudo
== PseudoStyleType::cellContent
&&
7765 !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7766 pseudo
== PseudoStyleType::fieldsetContent
||
7767 pseudo
== PseudoStyleType::buttonContent
||
7768 pseudo
== PseudoStyleType::columnContent
||
7769 (pseudo
== PseudoStyleType::scrolledContent
&&
7770 !GetParent()->IsListControlFrame()) ||
7771 pseudo
== PseudoStyleType::mozSVGText
) &&
7772 !IsComboboxControlFrame() && !IsFrameOfType(eMathML
) &&
7773 !IsColumnSetWrapperFrame() &&
7774 RefPtr
<ComputedStyle
>(GetFirstLetterStyle(PresContext())) != nullptr;
7775 NS_ASSERTION(haveFirstLetterStyle
==
7776 HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
),
7777 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7780 AddFrames(std::move(aChildList
), nullptr, nullptr);
7782 nsContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
7786 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame
* aMarkerFrame
) {
7787 MOZ_ASSERT(aMarkerFrame
);
7788 MOZ_ASSERT(!HasAnyStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
|
7789 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
),
7790 "How can we have a ::marker frame already?");
7792 if (StyleList()->mListStylePosition
== StyleListStylePosition::Inside
) {
7793 SetProperty(InsideMarkerProperty(), aMarkerFrame
);
7794 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
);
7796 if (nsBlockFrame
* marker
= do_QueryFrame(aMarkerFrame
)) {
7797 // An outside ::marker needs to be an independent formatting context
7798 // to avoid being influenced by the float manager etc.
7799 marker
->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
7801 SetProperty(OutsideMarkerProperty(),
7802 new (PresShell()) nsFrameList(aMarkerFrame
, aMarkerFrame
));
7803 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
7807 bool nsBlockFrame::MarkerIsEmpty() const {
7808 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
7810 "should only care when we have an outside ::marker");
7811 nsIFrame
* marker
= GetMarker();
7812 const nsStyleList
* list
= marker
->StyleList();
7813 return marker
->StyleContent()->mContent
.IsNone() ||
7814 (list
->mCounterStyle
.IsNone() && list
->mListStyleImage
.IsNone() &&
7815 marker
->StyleContent()->ContentCount() == 0);
7818 void nsBlockFrame::ReflowOutsideMarker(nsIFrame
* aMarkerFrame
,
7819 BlockReflowState
& aState
,
7820 ReflowOutput
& aMetrics
,
7822 const ReflowInput
& ri
= aState
.mReflowInput
;
7824 WritingMode markerWM
= aMarkerFrame
->GetWritingMode();
7825 LogicalSize
availSize(markerWM
);
7826 // Make up an inline-size since it doesn't really matter (XXX).
7827 availSize
.ISize(markerWM
) = aState
.ContentISize();
7828 availSize
.BSize(markerWM
) = NS_UNCONSTRAINEDSIZE
;
7830 ReflowInput
reflowInput(aState
.mPresContext
, ri
, aMarkerFrame
, availSize
,
7831 Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap
});
7832 nsReflowStatus status
;
7833 aMarkerFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowInput
, status
);
7835 // Get the float available space using our saved state from before we
7836 // started reflowing the block, so that we ignore any floats inside
7838 // FIXME: aLineTop isn't actually set correctly by some callers, since
7839 // they reposition the line.
7840 LogicalRect floatAvailSpace
=
7842 .GetFloatAvailableSpaceWithState(aLineTop
, ShapeType::ShapeOutside
,
7843 &aState
.mFloatManagerStateBefore
)
7845 // FIXME (bug 25888): need to check the entire region that the first
7846 // line overlaps, not just the top pixel.
7848 // Place the ::marker now. We want to place the ::marker relative to the
7849 // border-box of the associated block (using the right/left margin of
7850 // the ::marker frame as separation). However, if a line box would be
7851 // displaced by floats that are *outside* the associated block, we
7852 // want to displace it by the same amount. That is, we act as though
7853 // the edge of the floats is the content-edge of the block, and place
7854 // the ::marker at a position offset from there by the block's padding,
7855 // the block's border, and the ::marker frame's margin.
7857 // IStart from floatAvailSpace gives us the content/float start edge
7858 // in the current writing mode. Then we subtract out the start
7859 // border/padding and the ::marker's width and margin to offset the position.
7860 WritingMode wm
= ri
.GetWritingMode();
7861 // Get the ::marker's margin, converted to our writing mode so that we can
7862 // combine it with other logical values here.
7863 LogicalMargin markerMargin
= reflowInput
.ComputedLogicalMargin(wm
);
7864 nscoord iStart
= floatAvailSpace
.IStart(wm
) -
7865 ri
.ComputedLogicalBorderPadding(wm
).IStart(wm
) -
7866 markerMargin
.IEnd(wm
) - aMetrics
.ISize(wm
);
7868 // Approximate the ::marker's position; vertical alignment will provide
7869 // the final vertical location. We pass our writing-mode here, because
7870 // it may be different from the ::marker frame's mode.
7871 nscoord bStart
= floatAvailSpace
.BStart(wm
);
7872 aMarkerFrame
->SetRect(
7874 LogicalRect(wm
, iStart
, bStart
, aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)),
7875 aState
.ContainerSize());
7876 aMarkerFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowInput
);
7879 // This is used to scan frames for any float placeholders, add their
7880 // floats to the list represented by aList, and remove the
7881 // floats from whatever list they might be in. We don't search descendants
7882 // that are float containing blocks. Floats that or not children of 'this'
7883 // are ignored (they are not added to aList).
7884 void nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
7885 bool aCollectSiblings
) {
7887 // Don't descend into float containing blocks.
7888 if (!aFrame
->IsFloatContainingBlock()) {
7889 nsIFrame
* outOfFlowFrame
=
7890 aFrame
->IsPlaceholderFrame()
7891 ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame
)
7893 while (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
7894 RemoveFloat(outOfFlowFrame
);
7895 // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
7896 // the PushedFloats list.
7897 outOfFlowFrame
->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
7898 aList
.AppendFrame(nullptr, outOfFlowFrame
);
7899 outOfFlowFrame
= outOfFlowFrame
->GetNextInFlow();
7900 // FIXME: By not pulling floats whose parent is one of our
7901 // later siblings, are we risking the pushed floats getting
7903 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
7906 DoCollectFloats(aFrame
->PrincipalChildList().FirstChild(), aList
, true);
7908 aFrame
->GetChildList(FrameChildListID::Overflow
).FirstChild(), aList
,
7911 if (!aCollectSiblings
) {
7914 aFrame
= aFrame
->GetNextSibling();
7918 void nsBlockFrame::CheckFloats(BlockReflowState
& aState
) {
7920 // If any line is still dirty, that must mean we're going to reflow this
7921 // block again soon (e.g. because we bailed out after noticing that
7922 // clearance was imposed), so don't worry if the floats are out of sync.
7923 bool anyLineDirty
= false;
7925 // Check that the float list is what we would have built
7926 AutoTArray
<nsIFrame
*, 8> lineFloats
;
7927 for (auto& line
: Lines()) {
7928 if (line
.HasFloats()) {
7929 lineFloats
.AppendElements(line
.Floats());
7931 if (line
.IsDirty()) {
7932 anyLineDirty
= true;
7936 AutoTArray
<nsIFrame
*, 8> storedFloats
;
7938 bool hasHiddenFloats
= false;
7940 for (nsIFrame
* f
: mFloats
) {
7941 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7944 // There are chances that the float children won't be added to lines,
7945 // because in nsBlockFrame::ReflowLine, it skips reflow line if the first
7946 // child of the line is IsHiddenByContentVisibilityOfInFlowParentForLayout.
7947 // There are also chances that the floats in line are out of date, for
7948 // instance, lines could reflow if
7949 // PresShell::IsForcingLayoutForHiddenContent, and after forcingLayout is
7950 // off, the reflow of lines could be skipped, but the floats are still in
7951 // there. Here we can't know whether the floats hidden by c-v are included
7952 // in the lines or not. So we use hasHiddenFloats to skip the float length
7954 if (!hasHiddenFloats
&&
7955 f
->IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
7956 hasHiddenFloats
= true;
7958 storedFloats
.AppendElement(f
);
7959 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
7965 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) &&
7966 !anyLineDirty
&& !hasHiddenFloats
) {
7968 "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
7970 # if defined(DEBUG_roc)
7971 nsIFrame::RootFrameList(PresContext(), stdout
, 0);
7972 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
7973 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
7975 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
7976 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
7982 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
7983 if (oofs
&& oofs
->NotEmpty()) {
7984 // Floats that were pushed should be removed from our float
7985 // manager. Otherwise the float manager's YMost or XMost might
7986 // be larger than necessary, causing this block to get an
7987 // incorrect desired height (or width). Some of these floats
7988 // may not actually have been added to the float manager because
7989 // they weren't reflowed before being pushed; that's OK,
7990 // RemoveRegions will ignore them. It is safe to do this here
7991 // because we know from here on the float manager will only be
7992 // used for its XMost and YMost, not to place new floats and
7994 aState
.FloatManager()->RemoveTrailingRegions(oofs
->FirstChild());
7998 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot
,
7999 bool* aBEndMarginRoot
) {
8000 nsIFrame
* parent
= GetParent();
8001 if (!HasAnyStateBits(NS_BLOCK_MARGIN_ROOT
)) {
8002 if (!parent
|| parent
->IsFloatContainingBlock()) {
8003 *aBStartMarginRoot
= false;
8004 *aBEndMarginRoot
= false;
8009 if (parent
&& parent
->IsColumnSetFrame()) {
8010 // The first column is a start margin root and the last column is an end
8011 // margin root. (If the column-set is split by a column-span:all box then
8012 // the first and last column in each column-set fragment are margin roots.)
8013 *aBStartMarginRoot
= GetPrevInFlow() == nullptr;
8014 *aBEndMarginRoot
= GetNextInFlow() == nullptr;
8018 *aBStartMarginRoot
= true;
8019 *aBEndMarginRoot
= true;
8023 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
) {
8024 MOZ_ASSERT(aBlock
, "Must have a frame");
8025 NS_ASSERTION(aBlock
->IsBlockFrameOrSubclass(), "aBlock must be a block");
8027 nsIFrame
* parent
= aBlock
->GetParent();
8028 return aBlock
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
) ||
8029 (parent
&& !parent
->IsFloatContainingBlock());
8033 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
) {
8034 return aFrame
->IsBlockFrameOrSubclass() &&
8035 !aFrame
->IsFrameOfType(nsIFrame::eReplaced
) &&
8036 !aFrame
->HasAnyStateBits(NS_BLOCK_FLOAT_MGR
);
8039 // Note that this width can vary based on the vertical position.
8040 // However, the cases where it varies are the cases where the width fits
8041 // in the available space given, which means that variation shouldn't
8044 nsBlockFrame::FloatAvoidingISizeToClear
nsBlockFrame::ISizeToClearPastFloats(
8045 const BlockReflowState
& aState
, const LogicalRect
& aFloatAvailableSpace
,
8046 nsIFrame
* aFloatAvoidingBlock
) {
8047 nscoord inlineStartOffset
, inlineEndOffset
;
8048 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8050 FloatAvoidingISizeToClear result
;
8051 aState
.ComputeFloatAvoidingOffsets(aFloatAvoidingBlock
, aFloatAvailableSpace
,
8052 inlineStartOffset
, inlineEndOffset
);
8053 nscoord availISize
=
8054 aState
.mContentArea
.ISize(wm
) - inlineStartOffset
- inlineEndOffset
;
8056 // We actually don't want the min width here; see bug 427782; we only
8057 // want to displace if the width won't compute to a value small enough
8059 // All we really need here is the result of ComputeSize, and we
8060 // could *almost* get that from an SizeComputationInput, except for the
8062 WritingMode frWM
= aFloatAvoidingBlock
->GetWritingMode();
8063 LogicalSize availSpace
=
8064 LogicalSize(wm
, availISize
, NS_UNCONSTRAINEDSIZE
).ConvertTo(frWM
, wm
);
8065 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
,
8066 aFloatAvoidingBlock
, availSpace
);
8067 result
.borderBoxISize
=
8068 reflowInput
.ComputedSizeWithBorderPadding(wm
).ISize(wm
);
8070 // Use the margins from sizingInput rather than reflowInput so that
8071 // they aren't reduced by ignoring margins in overconstrained cases.
8072 SizeComputationInput
sizingInput(aFloatAvoidingBlock
,
8073 aState
.mReflowInput
.mRenderingContext
, wm
,
8074 aState
.mContentArea
.ISize(wm
));
8075 const LogicalMargin computedMargin
= sizingInput
.ComputedLogicalMargin(wm
);
8077 nscoord marginISize
= computedMargin
.IStartEnd(wm
);
8078 const auto& iSize
= reflowInput
.mStylePosition
->ISize(wm
);
8079 if (marginISize
< 0 && (iSize
.IsAuto() || iSize
.IsMozAvailable())) {
8080 // If we get here, floatAvoidingBlock has a negative amount of inline-axis
8081 // margin and an 'auto' (or ~equivalently, -moz-available) inline
8082 // size. Under these circumstances, we use the margin to establish a
8083 // (positive) minimum size for the border-box, in order to satisfy the
8084 // equation in CSS2 10.3.3. That equation essentially simplifies to the
8087 // iSize of margins + iSize of borderBox = iSize of containingBlock
8089 // ...where "iSize of borderBox" is the sum of floatAvoidingBlock's
8090 // inline-axis components of border, padding, and {width,height}.
8092 // Right now, in the above equation, "iSize of margins" is the only term
8093 // that we know for sure. (And we also know that it's negative, since we
8094 // got here.) The other terms are as-yet unresolved, since the frame has an
8095 // 'auto' iSize, and since we aren't yet sure if we'll clear this frame
8096 // beyond floats or place it alongside them.
8098 // However: we *do* know that the equation's "iSize of containingBlock"
8099 // term *must* be non-negative, since boxes' widths and heights generally
8100 // can't be negative in CSS. To satisfy that requirement, we can then
8101 // infer that the equation's "iSize of borderBox" term *must* be large
8102 // enough to cancel out the (known-to-be-negative) "iSize of margins"
8103 // term. Therefore, marginISize value (negated to make it positive)
8104 // establishes a lower-bound for how much inline-axis space our border-box
8105 // will really require in order to fit alongside any floats.
8107 // XXXdholbert This explanation is admittedly a bit hand-wavy and may not
8108 // precisely match what any particular spec requires. It's the best
8109 // reasoning I could come up with to explain engines' behavior. Also, our
8110 // behavior with -moz-available doesn't seem particularly correct here, per
8111 // bug 1767217, though that's probably due to a bug elsewhere in our float
8113 result
.borderBoxISize
= std::max(result
.borderBoxISize
, -marginISize
);
8116 result
.marginIStart
= computedMargin
.IStart(wm
);
8121 nsBlockFrame
* nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
) {
8122 nsBlockFrame
* block
= nullptr;
8123 while (aCandidate
) {
8124 block
= do_QueryFrame(aCandidate
);
8126 // yay, candidate is a block!
8129 // Not a block. Check its parent next.
8130 aCandidate
= aCandidate
->GetParent();
8132 MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
8136 nscoord
nsBlockFrame::ComputeFinalBSize(BlockReflowState
& aState
,
8137 nscoord aBEndEdgeOfChildren
) {
8138 const WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8140 const nscoord effectiveContentBoxBSize
=
8141 GetEffectiveComputedBSize(aState
.mReflowInput
, aState
.mConsumedBSize
);
8142 const nscoord blockStartBP
= aState
.BorderPadding().BStart(wm
);
8143 const nscoord blockEndBP
= aState
.BorderPadding().BEnd(wm
);
8146 !IsTrueOverflowContainer() || (effectiveContentBoxBSize
== 0 &&
8147 blockStartBP
== 0 && blockEndBP
== 0),
8148 "An overflow container's effective content-box block-size, block-start "
8149 "BP, and block-end BP should all be zero!");
8151 const nscoord effectiveContentBoxBSizeWithBStartBP
=
8152 NSCoordSaturatingAdd(blockStartBP
, effectiveContentBoxBSize
);
8153 const nscoord effectiveBorderBoxBSize
=
8154 NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP
, blockEndBP
);
8156 if (HasColumnSpanSiblings()) {
8157 MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
8158 "Frame constructor should've created column-span siblings!");
8160 // If a block is split by any column-spans, we calculate the final
8161 // block-size by shrinkwrapping our children's block-size for all the
8162 // fragments except for those after the final column-span, but we should
8163 // take no more than our effective border-box block-size. If there's any
8164 // leftover block-size, our next continuations will take up rest.
8166 // We don't need to adjust aBri.mReflowStatus because our children's status
8167 // is the same as ours.
8168 return std::min(effectiveBorderBoxBSize
, aBEndEdgeOfChildren
);
8171 const nscoord availBSize
= aState
.mReflowInput
.AvailableBSize();
8172 if (availBSize
== NS_UNCONSTRAINEDSIZE
) {
8173 return effectiveBorderBoxBSize
;
8176 // Save our children's reflow status.
8177 const bool isChildStatusComplete
= aState
.mReflowStatus
.IsComplete();
8178 if (isChildStatusComplete
&& effectiveContentBoxBSize
> 0 &&
8179 effectiveBorderBoxBSize
> availBSize
&&
8180 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
8181 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
8182 return effectiveBorderBoxBSize
;
8185 const bool isBDBClone
=
8186 aState
.mReflowInput
.mStyleBorder
->mBoxDecorationBreak
==
8187 StyleBoxDecorationBreak::Clone
;
8189 // The maximum value our content-box block-size can take within the given
8190 // available block-size.
8191 const nscoord maxContentBoxBSize
= aState
.ContentBSize();
8193 // The block-end edge of our content-box (relative to this frame's origin) if
8194 // we consumed the maximum block-size available to us (maxContentBoxBSize).
8195 const nscoord maxContentBoxBEnd
= aState
.ContentBEnd();
8197 // These variables are uninitialized intentionally so that the compiler can
8198 // check they are assigned in every if-else branch below.
8199 nscoord finalContentBoxBSizeWithBStartBP
;
8200 bool isOurStatusComplete
;
8202 if (effectiveBorderBoxBSize
<= availBSize
) {
8203 // Our effective border-box block-size can fit in the available block-size,
8204 // so we are complete.
8205 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8206 isOurStatusComplete
= true;
8207 } else if (effectiveContentBoxBSizeWithBStartBP
<= maxContentBoxBEnd
) {
8208 // Note: The following assertion should generally hold because, for
8209 // box-decoration-break:clone, this "else if" branch is mathematically
8210 // equivalent to the initial "if".
8211 NS_ASSERTION(!isBDBClone
,
8212 "This else-if branch is handling a situation that's specific "
8213 "to box-decoration-break:slice, i.e. a case when we can skip "
8214 "our block-end border and padding!");
8216 // Our effective content-box block-size plus the block-start border and
8217 // padding can fit in the available block-size, but it cannot fit after
8218 // adding the block-end border and padding. Thus, we need a continuation
8219 // (unless we already weren't asking for any block-size, in which case we
8220 // stay complete to avoid looping forever).
8221 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8222 isOurStatusComplete
= effectiveContentBoxBSize
== 0;
8224 // We aren't going to be able to fit our content-box in the space available
8225 // to it, which means we'll probably call ourselves incomplete to request a
8226 // continuation. But before making that decision, we check for certain
8227 // conditions which would force us to overflow beyond the available space --
8228 // these might result in us actually being complete if we're forced to
8229 // overflow far enough.
8230 if (MOZ_UNLIKELY(aState
.mReflowInput
.mFlags
.mIsTopOfPage
&& isBDBClone
&&
8231 maxContentBoxBSize
<= 0 &&
8232 aBEndEdgeOfChildren
== blockStartBP
)) {
8233 // In this rare case, we are at the top of page/column, we have
8234 // box-decoration-break:clone and zero available block-size for our
8235 // content-box (e.g. our own block-start border and padding already exceed
8236 // the available block-size), and we didn't lay out any child to consume
8237 // our content-box block-size. To ensure we make progress (avoid looping
8238 // forever), use 1px as our content-box block-size regardless of our
8239 // effective content-box block-size, in the spirit of
8240 // https://drafts.csswg.org/css-break/#breaking-rules.
8241 finalContentBoxBSizeWithBStartBP
= blockStartBP
+ AppUnitsPerCSSPixel();
8242 isOurStatusComplete
= effectiveContentBoxBSize
<= AppUnitsPerCSSPixel();
8243 } else if (aBEndEdgeOfChildren
> maxContentBoxBEnd
) {
8244 // We have a unbreakable child whose block-end edge exceeds the available
8245 // block-size for children.
8246 if (aBEndEdgeOfChildren
>= effectiveContentBoxBSizeWithBStartBP
) {
8247 // The unbreakable child's block-end edge forces us to consume all of
8248 // our effective content-box block-size.
8249 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8251 // Even though we've consumed all of our effective content-box
8252 // block-size, we may still need to report an incomplete status in order
8253 // to get another continuation, which will be responsible for laying out
8254 // & drawing our block-end border & padding. But if we have no such
8255 // border & padding, or if we're forced to apply that border & padding
8256 // on this frame due to box-decoration-break:clone, then we don't need
8257 // to bother with that additional continuation.
8258 isOurStatusComplete
= (isBDBClone
|| blockEndBP
== 0);
8260 // The unbreakable child's block-end edge doesn't force us to consume
8261 // all of our effective content-box block-size.
8262 finalContentBoxBSizeWithBStartBP
= aBEndEdgeOfChildren
;
8263 isOurStatusComplete
= false;
8266 // The children's block-end edge can fit in the content-box space that we
8267 // have available for it. Consume all the space that is available so that
8268 // our inline-start/inline-end borders extend all the way to the block-end
8269 // edge of column/page.
8270 finalContentBoxBSizeWithBStartBP
= maxContentBoxBEnd
;
8271 isOurStatusComplete
= false;
8275 nscoord finalBorderBoxBSize
= finalContentBoxBSizeWithBStartBP
;
8276 if (isOurStatusComplete
) {
8277 finalBorderBoxBSize
= NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8278 if (isChildStatusComplete
) {
8279 // We want to use children's reflow status as ours, which can be overflow
8280 // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
8282 aState
.mReflowStatus
.SetOverflowIncomplete();
8285 NS_ASSERTION(!IsTrueOverflowContainer(),
8286 "An overflow container should always be complete because of "
8287 "its zero border-box block-size!");
8289 finalBorderBoxBSize
=
8290 NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8292 aState
.mReflowStatus
.SetIncomplete();
8293 if (!GetNextInFlow()) {
8294 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
8298 return finalBorderBoxBSize
;
8301 nsresult
nsBlockFrame::ResolveBidi() {
8302 NS_ASSERTION(!GetPrevInFlow(),
8303 "ResolveBidi called on non-first continuation");
8304 MOZ_ASSERT(PresContext()->BidiEnabled());
8305 return nsBidiPresUtils::Resolve(this);
8308 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState
& aRestyleState
) {
8309 // first-letter needs to be updated before first-line, because first-line can
8310 // change the style of the first-letter.
8311 if (HasFirstLetterChild()) {
8312 UpdateFirstLetterStyle(aRestyleState
);
8315 if (nsIFrame
* firstLineFrame
= GetFirstLineFrame()) {
8316 nsIFrame
* styleParent
= CorrectStyleParentFrame(firstLineFrame
->GetParent(),
8317 PseudoStyleType::firstLine
);
8319 ComputedStyle
* parentStyle
= styleParent
->Style();
8320 RefPtr
<ComputedStyle
> firstLineStyle
=
8321 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
8322 *mContent
->AsElement(), PseudoStyleType::firstLine
, nullptr,
8325 // FIXME(bz): Can we make first-line continuations be non-inheriting anon
8327 RefPtr
<ComputedStyle
> continuationStyle
=
8328 aRestyleState
.StyleSet().ResolveInheritingAnonymousBoxStyle(
8329 PseudoStyleType::mozLineFrame
, parentStyle
);
8331 UpdateStyleOfOwnedChildFrame(firstLineFrame
, firstLineStyle
, aRestyleState
,
8332 Some(continuationStyle
.get()));
8334 // We also want to update the styles of the first-line's descendants. We
8335 // don't need to compute a changehint for this, though, since any changes to
8336 // them are handled by the first-line anyway.
8337 RestyleManager
* manager
= PresContext()->RestyleManager();
8338 for (nsIFrame
* kid
: firstLineFrame
->PrincipalChildList()) {
8339 manager
->ReparentComputedStyleForFirstLine(kid
);
8344 nsIFrame
* nsBlockFrame::GetFirstLetter() const {
8345 if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
8346 // Certainly no first-letter frame.
8350 return GetProperty(FirstLetterProperty());
8353 nsIFrame
* nsBlockFrame::GetFirstLineFrame() const {
8354 nsIFrame
* maybeFirstLine
= PrincipalChildList().FirstChild();
8355 if (maybeFirstLine
&& maybeFirstLine
->IsLineFrame()) {
8356 return maybeFirstLine
;
8363 void nsBlockFrame::VerifyLines(bool aFinalCheckOK
) {
8364 if (!gVerifyLines
) {
8367 if (mLines
.empty()) {
8371 nsLineBox
* cursor
= GetLineCursorForQuery();
8373 // Add up the counts on each line. Also validate that IsFirstLine is
8376 for (const auto& line
: Lines()) {
8377 if (&line
== cursor
) {
8380 if (aFinalCheckOK
) {
8381 MOZ_ASSERT(line
.GetChildCount(), "empty line");
8382 if (line
.IsBlock()) {
8383 NS_ASSERTION(1 == line
.GetChildCount(), "bad first line");
8386 count
+= line
.GetChildCount();
8389 // Then count the frames
8390 int32_t frameCount
= 0;
8391 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
8394 frame
= frame
->GetNextSibling();
8396 NS_ASSERTION(count
== frameCount
, "bad line list");
8398 // Next: test that each line has right number of frames on it
8399 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
8400 line
!= line_end
;) {
8401 count
= line
->GetChildCount();
8402 frame
= line
->mFirstChild
;
8403 while (--count
>= 0) {
8404 frame
= frame
->GetNextSibling();
8407 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
8408 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
8413 FrameLines
* overflowLines
= GetOverflowLines();
8414 if (overflowLines
) {
8415 LineIterator line
= overflowLines
->mLines
.begin();
8416 LineIterator line_end
= overflowLines
->mLines
.end();
8417 for (; line
!= line_end
; ++line
) {
8418 if (line
== cursor
) {
8425 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
8428 void nsBlockFrame::VerifyOverflowSituation() {
8429 // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
8430 nsFrameList
* oofs
= GetOverflowOutOfFlows();
8432 for (nsIFrame
* f
: *oofs
) {
8433 nsIFrame
* nif
= f
->GetNextInFlow();
8435 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8439 // Pushed floats must not have a next-in-flow in mFloats or mFrames.
8440 oofs
= GetPushedFloats();
8442 for (nsIFrame
* f
: *oofs
) {
8443 nsIFrame
* nif
= f
->GetNextInFlow();
8445 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8449 // A child float next-in-flow's parent must be |this| or a next-in-flow of
8450 // |this|. Later next-in-flows must have the same or later parents.
8451 ChildListID childLists
[] = {FrameChildListID::Float
,
8452 FrameChildListID::PushedFloats
};
8453 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
8454 const nsFrameList
& children
= GetChildList(childLists
[i
]);
8455 for (nsIFrame
* f
: children
) {
8456 nsIFrame
* parent
= this;
8457 nsIFrame
* nif
= f
->GetNextInFlow();
8458 for (; nif
; nif
= nif
->GetNextInFlow()) {
8460 for (nsIFrame
* p
= parent
; p
; p
= p
->GetNextInFlow()) {
8461 if (nif
->GetParent() == p
) {
8469 "next-in-flow is a child of parent earlier in the frame tree?");
8474 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
8476 FrameLines
* overflowLines
= flow
->GetOverflowLines();
8477 if (overflowLines
) {
8478 NS_ASSERTION(!overflowLines
->mLines
.empty(),
8479 "should not be empty if present");
8480 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
8481 "bad overflow lines");
8482 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
8483 overflowLines
->mFrames
.FirstChild(),
8484 "bad overflow frames / lines");
8486 auto checkCursor
= [&](nsLineBox
* cursor
) -> bool {
8490 LineIterator line
= flow
->LinesBegin();
8491 LineIterator line_end
= flow
->LinesEnd();
8492 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8494 if (line
== line_end
&& overflowLines
) {
8495 line
= overflowLines
->mLines
.begin();
8496 line_end
= overflowLines
->mLines
.end();
8497 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8500 return line
!= line_end
;
8502 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForDisplay()),
8503 "stale LineCursorPropertyDisplay");
8504 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForQuery()),
8505 "stale LineCursorPropertyQuery");
8506 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
8510 int32_t nsBlockFrame::GetDepth() const {
8512 nsIFrame
* parent
= GetParent();
8514 parent
= parent
->GetParent();
8520 already_AddRefed
<ComputedStyle
> nsBlockFrame::GetFirstLetterStyle(
8521 nsPresContext
* aPresContext
) {
8522 return aPresContext
->StyleSet()->ProbePseudoElementStyle(
8523 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr, Style());