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"
66 #include "nsFileControlFrame.h"
67 #include "nsMathMLContainerFrame.h"
68 #include "nsSelectsAreaFrame.h"
70 #include "nsBidiPresUtils.h"
74 static const int MIN_LINES_NEEDING_CURSOR
= 20;
76 using namespace mozilla
;
77 using namespace mozilla::css
;
78 using namespace mozilla::dom
;
79 using namespace mozilla::layout
;
80 using AbsPosReflowFlags
= nsAbsoluteContainingBlock::AbsPosReflowFlags
;
81 using ClearFloatsResult
= BlockReflowState::ClearFloatsResult
;
82 using ShapeType
= nsFloatManager::ShapeType
;
84 static void MarkAllDescendantLinesDirty(nsBlockFrame
* aBlock
) {
85 for (auto& line
: aBlock
->Lines()) {
87 nsBlockFrame
* bf
= do_QueryFrame(line
.mFirstChild
);
89 MarkAllDescendantLinesDirty(bf
);
96 static void MarkSameFloatManagerLinesDirty(nsBlockFrame
* aBlock
) {
97 nsBlockFrame
* blockWithFloatMgr
= aBlock
;
98 while (!blockWithFloatMgr
->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
99 nsBlockFrame
* bf
= do_QueryFrame(blockWithFloatMgr
->GetParent());
103 blockWithFloatMgr
= bf
;
106 // Mark every line at and below the line where the float was
107 // dirty, and mark their lines dirty too. We could probably do
108 // something more efficient --- e.g., just dirty the lines that intersect
109 // the float vertically.
110 MarkAllDescendantLinesDirty(blockWithFloatMgr
);
114 * Returns true if aFrame is a block that has one or more float children.
116 static bool BlockHasAnyFloats(nsIFrame
* aFrame
) {
117 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
121 if (block
->GetChildList(FrameChildListID::Float
).FirstChild()) {
125 for (const auto& line
: block
->Lines()) {
126 if (line
.IsBlock() && BlockHasAnyFloats(line
.mFirstChild
)) {
134 * Determines whether the given frame is visible or has
135 * visible children that participate in the same line. Frames
136 * that are not line participants do not have their
139 static bool FrameHasVisibleInlineContent(nsIFrame
* aFrame
) {
140 MOZ_ASSERT(aFrame
, "Frame argument cannot be null");
142 if (aFrame
->StyleVisibility()->IsVisible()) {
146 if (aFrame
->IsLineParticipant()) {
147 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
148 if (kid
->StyleVisibility()->IsVisible() ||
149 FrameHasVisibleInlineContent(kid
)) {
158 * Determines whether any of the frames descended from the
159 * given line have inline content with 'visibility: visible'.
160 * This function calls FrameHasVisibleInlineContent to process
161 * each frame in the line's child list.
163 static bool LineHasVisibleInlineContent(nsLineBox
* aLine
) {
164 nsIFrame
* kid
= aLine
->mFirstChild
;
165 int32_t n
= aLine
->GetChildCount();
167 if (FrameHasVisibleInlineContent(kid
)) {
171 kid
= kid
->GetNextSibling();
178 * Iterates through the frame's in-flow children and
179 * unions the ink overflow of all text frames which
180 * participate in the line aFrame belongs to.
181 * If a child of aFrame is not a text frame,
182 * we recurse with the child as the aFrame argument.
183 * If aFrame isn't a line participant, we skip it entirely
184 * and return an empty rect.
185 * The resulting nsRect is offset relative to the parent of aFrame.
187 static nsRect
GetFrameTextArea(nsIFrame
* aFrame
,
188 nsDisplayListBuilder
* aBuilder
) {
190 if (const nsTextFrame
* textFrame
= do_QueryFrame(aFrame
)) {
191 if (!textFrame
->IsEntirelyWhitespace()) {
192 textArea
= aFrame
->InkOverflowRect();
194 } else if (aFrame
->IsLineParticipant()) {
195 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
196 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
197 textArea
.OrWith(kidTextArea
);
200 // add aFrame's position to keep textArea relative to aFrame's parent
201 return textArea
+ aFrame
->GetPosition();
205 * Iterates through the line's children and
206 * unions the ink overflow of all text frames.
207 * GetFrameTextArea unions and returns the ink overflow
208 * from all line-participating text frames within the given child.
209 * The nsRect returned from GetLineTextArea is offset
210 * relative to the given line.
212 static nsRect
GetLineTextArea(nsLineBox
* aLine
,
213 nsDisplayListBuilder
* aBuilder
) {
215 nsIFrame
* kid
= aLine
->mFirstChild
;
216 int32_t n
= aLine
->GetChildCount();
218 nsRect kidTextArea
= GetFrameTextArea(kid
, aBuilder
);
219 textArea
.OrWith(kidTextArea
);
220 kid
= kid
->GetNextSibling();
227 * Starting with aFrame, iterates upward through parent frames and checks for
228 * non-transparent background colors. If one is found, we use that as our
229 * backplate color. Otheriwse, we use the default background color from
230 * our high contrast theme.
232 static nscolor
GetBackplateColor(nsIFrame
* aFrame
) {
233 nsPresContext
* pc
= aFrame
->PresContext();
234 nscolor currentBackgroundColor
= NS_TRANSPARENT
;
235 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent()) {
236 // NOTE(emilio): We assume themed frames (frame->IsThemed()) have correct
237 // background-color information so as to compute the right backplate color.
239 // This holds because HTML widgets with author-specified backgrounds or
240 // borders disable theming. So as long as the UA-specified background colors
241 // match the actual theme (which they should because we always use system
242 // colors with the non-native theme, and native system colors should also
243 // match the native theme), then we're alright and we should compute an
244 // appropriate backplate color.
245 const auto* style
= frame
->Style();
246 if (style
->StyleBackground()->IsTransparent(style
)) {
249 bool drawImage
= false, drawColor
= false;
250 nscolor backgroundColor
= nsCSSRendering::DetermineBackgroundColor(
251 pc
, style
, frame
, drawImage
, drawColor
);
252 if (!drawColor
&& !drawImage
) {
255 if (NS_GET_A(backgroundColor
) == 0) {
256 // Even if there's a background image, if there's no background color we
257 // keep going up the frame tree, see bug 1723938.
260 if (NS_GET_A(currentBackgroundColor
) == 0) {
261 // Try to avoid somewhat expensive math in the common case.
262 currentBackgroundColor
= backgroundColor
;
264 currentBackgroundColor
=
265 NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
267 if (NS_GET_A(currentBackgroundColor
) == 0xff) {
268 // If fully opaque, we're done, otherwise keep going up blending with our
270 return currentBackgroundColor
;
273 nscolor backgroundColor
= aFrame
->PresContext()->DefaultBackgroundColor();
274 if (NS_GET_A(currentBackgroundColor
) == 0) {
275 return backgroundColor
;
277 return NS_ComposeColors(backgroundColor
, currentBackgroundColor
);
281 # include "nsBlockDebugFlags.h"
283 bool nsBlockFrame::gLamePaintMetrics
;
284 bool nsBlockFrame::gLameReflowMetrics
;
285 bool nsBlockFrame::gNoisy
;
286 bool nsBlockFrame::gNoisyDamageRepair
;
287 bool nsBlockFrame::gNoisyIntrinsic
;
288 bool nsBlockFrame::gNoisyReflow
;
289 bool nsBlockFrame::gReallyNoisyReflow
;
290 bool nsBlockFrame::gNoisyFloatManager
;
291 bool nsBlockFrame::gVerifyLines
;
292 bool nsBlockFrame::gDisableResizeOpt
;
294 int32_t nsBlockFrame::gNoiseIndent
;
296 struct BlockDebugFlags
{
301 static const BlockDebugFlags gFlags
[] = {
302 {"reflow", &nsBlockFrame::gNoisyReflow
},
303 {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow
},
304 {"intrinsic", &nsBlockFrame::gNoisyIntrinsic
},
305 {"float-manager", &nsBlockFrame::gNoisyFloatManager
},
306 {"verify-lines", &nsBlockFrame::gVerifyLines
},
307 {"damage-repair", &nsBlockFrame::gNoisyDamageRepair
},
308 {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics
},
309 {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics
},
310 {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt
},
312 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
314 static void ShowDebugFlags() {
315 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
316 const BlockDebugFlags
* bdf
= gFlags
;
317 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
318 for (; bdf
< end
; bdf
++) {
319 printf(" %s\n", bdf
->name
);
321 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
322 printf("names (no whitespace)\n");
325 void nsBlockFrame::InitDebugFlags() {
326 static bool firstTime
= true;
329 char* flags
= PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
333 char* cm
= strchr(flags
, ',');
339 const BlockDebugFlags
* bdf
= gFlags
;
340 const BlockDebugFlags
* end
= gFlags
+ NUM_DEBUG_FLAGS
;
341 for (; bdf
< end
; bdf
++) {
342 if (nsCRT::strcasecmp(bdf
->name
, flags
) == 0) {
344 printf("nsBlockFrame: setting %s debug flag on\n", bdf
->name
);
369 //----------------------------------------------------------------------
371 // Debugging support code
374 const char* nsBlockFrame::kReflowCommandType
[] = {
375 "ContentChanged", "StyleChanged", "ReflowDirty", "Timeout", "UserDefined",
378 const char* nsBlockFrame::LineReflowStatusToString(
379 LineReflowStatus aLineReflowStatus
) const {
380 switch (aLineReflowStatus
) {
381 case LineReflowStatus::OK
:
382 return "LINE_REFLOW_OK";
383 case LineReflowStatus::Stop
:
384 return "LINE_REFLOW_STOP";
385 case LineReflowStatus::RedoNoPull
:
386 return "LINE_REFLOW_REDO_NO_PULL";
387 case LineReflowStatus::RedoMoreFloats
:
388 return "LINE_REFLOW_REDO_MORE_FLOATS";
389 case LineReflowStatus::RedoNextBand
:
390 return "LINE_REFLOW_REDO_NEXT_BAND";
391 case LineReflowStatus::Truncated
:
392 return "LINE_REFLOW_TRUNCATED";
399 #ifdef REFLOW_STATUS_COVERAGE
400 static void RecordReflowStatus(bool aChildIsBlock
,
401 const nsReflowStatus
& aFrameReflowStatus
) {
402 static uint32_t record
[2];
405 // 1: child-is-inline
407 if (!aChildIsBlock
) {
411 // Compute new status
412 uint32_t newS
= record
[index
];
413 if (aFrameReflowStatus
.IsInlineBreak()) {
414 if (aFrameReflowStatus
.IsInlineBreakBefore()) {
416 } else if (aFrameReflowStatus
.IsIncomplete()) {
421 } else if (aFrameReflowStatus
.IsIncomplete()) {
427 // Log updates to the status that yield different values
428 if (record
[index
] != newS
) {
429 record
[index
] = newS
;
430 printf("record(%d): %02x %02x\n", index
, record
[0], record
[1]);
435 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty
,
436 nsBlockFrame::FrameLines
)
437 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty
)
438 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty
)
439 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty
)
440 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty
, nsIFrame
)
441 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty
, nscoord
)
443 //----------------------------------------------------------------------
445 nsBlockFrame
* NS_NewBlockFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
446 return new (aPresShell
) nsBlockFrame(aStyle
, aPresShell
->GetPresContext());
449 nsBlockFrame
* NS_NewBlockFormattingContext(PresShell
* aPresShell
,
450 ComputedStyle
* aComputedStyle
) {
451 nsBlockFrame
* blockFrame
= NS_NewBlockFrame(aPresShell
, aComputedStyle
);
452 blockFrame
->AddStateBits(NS_BLOCK_STATIC_BFC
);
456 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame
)
458 nsBlockFrame::~nsBlockFrame() = default;
460 void nsBlockFrame::AddSizeOfExcludingThisForTree(
461 nsWindowSizes
& aWindowSizes
) const {
462 nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes
);
464 // Add the size of any nsLineBox::mFrames hashtables we might have:
465 for (const auto& line
: Lines()) {
466 line
.AddSizeOfExcludingThis(aWindowSizes
);
468 const FrameLines
* overflowLines
= GetOverflowLines();
470 ConstLineIterator line
= overflowLines
->mLines
.begin(),
471 line_end
= overflowLines
->mLines
.end();
472 for (; line
!= line_end
; ++line
) {
473 line
->AddSizeOfExcludingThis(aWindowSizes
);
478 void nsBlockFrame::Destroy(DestroyContext
& aContext
) {
480 DestroyAbsoluteFrames(aContext
);
481 mFloats
.DestroyFrames(aContext
);
482 nsPresContext
* presContext
= PresContext();
483 mozilla::PresShell
* presShell
= presContext
->PresShell();
484 nsLineBox::DeleteLineList(presContext
, mLines
, &mFrames
, aContext
);
486 if (HasPushedFloats()) {
487 SafelyDestroyFrameListProp(aContext
, presShell
, PushedFloatProperty());
488 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
491 // destroy overflow lines now
492 FrameLines
* overflowLines
= RemoveOverflowLines();
494 nsLineBox::DeleteLineList(presContext
, overflowLines
->mLines
,
495 &overflowLines
->mFrames
, aContext
);
496 delete overflowLines
;
499 if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
500 SafelyDestroyFrameListProp(aContext
, presShell
,
501 OverflowOutOfFlowsProperty());
502 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
505 if (HasOutsideMarker()) {
506 SafelyDestroyFrameListProp(aContext
, presShell
, OutsideMarkerProperty());
507 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
510 nsContainerFrame::Destroy(aContext
);
514 nsILineIterator
* nsBlockFrame::GetLineIterator() {
515 nsLineIterator
* iter
= GetProperty(LineIteratorProperty());
517 const nsStyleVisibility
* visibility
= StyleVisibility();
518 iter
= new nsLineIterator(mLines
,
519 visibility
->mDirection
== StyleDirection::Rtl
);
520 SetProperty(LineIteratorProperty(), iter
);
525 NS_QUERYFRAME_HEAD(nsBlockFrame
)
526 NS_QUERYFRAME_ENTRY(nsBlockFrame
)
527 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
529 #ifdef DEBUG_FRAME_DUMP
530 void nsBlockFrame::List(FILE* out
, const char* aPrefix
,
531 ListFlags aFlags
) const {
533 ListGeneric(str
, aPrefix
, aFlags
);
535 fprintf_stderr(out
, "%s <\n", str
.get());
537 nsCString
pfx(aPrefix
);
541 if (!mLines
.empty()) {
542 ConstLineIterator line
= LinesBegin(), line_end
= LinesEnd();
543 for (; line
!= line_end
; ++line
) {
544 line
->List(out
, pfx
.get(), aFlags
);
548 // Output the overflow lines.
549 const FrameLines
* overflowLines
= GetOverflowLines();
550 if (overflowLines
&& !overflowLines
->mLines
.empty()) {
551 fprintf_stderr(out
, "%sOverflow-lines %p/%p <\n", pfx
.get(), overflowLines
,
552 &overflowLines
->mFrames
);
553 nsCString
nestedPfx(pfx
);
555 ConstLineIterator line
= overflowLines
->mLines
.begin(),
556 line_end
= overflowLines
->mLines
.end();
557 for (; line
!= line_end
; ++line
) {
558 line
->List(out
, nestedPfx
.get(), aFlags
);
560 fprintf_stderr(out
, "%s>\n", pfx
.get());
563 // skip the principal list - we printed the lines above
564 // skip the overflow list - we printed the overflow lines above
565 ChildListIDs skip
= {FrameChildListID::Principal
, FrameChildListID::Overflow
};
566 ListChildLists(out
, pfx
.get(), aFlags
, skip
);
568 fprintf_stderr(out
, "%s>\n", aPrefix
);
571 nsresult
nsBlockFrame::GetFrameName(nsAString
& aResult
) const {
572 return MakeFrameName(u
"Block"_ns
, aResult
);
576 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
577 bool aRebuildDisplayItems
) {
578 if (IsInSVGTextSubtree()) {
579 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
580 "unexpected block frame in SVG text");
581 GetParent()->InvalidateFrame();
584 nsContainerFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
587 void nsBlockFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
588 uint32_t aDisplayItemKey
,
589 bool aRebuildDisplayItems
) {
590 if (IsInSVGTextSubtree()) {
591 NS_ASSERTION(GetParent()->IsSVGTextFrame(),
592 "unexpected block frame in SVG text");
593 GetParent()->InvalidateFrame();
596 nsContainerFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
597 aRebuildDisplayItems
);
600 nscoord
nsBlockFrame::SynthesizeFallbackBaseline(
601 WritingMode aWM
, BaselineSharingGroup aBaselineGroup
) const {
602 return Baseline::SynthesizeBOffsetFromMarginBox(this, aWM
, aBaselineGroup
);
605 template <typename LineIteratorType
>
606 Maybe
<nscoord
> nsBlockFrame::GetBaselineBOffset(
607 LineIteratorType aStart
, LineIteratorType aEnd
, WritingMode aWM
,
608 BaselineSharingGroup aBaselineGroup
,
609 BaselineExportContext aExportContext
) const {
610 MOZ_ASSERT((std::is_same_v
<LineIteratorType
, ConstLineIterator
> &&
611 aBaselineGroup
== BaselineSharingGroup::First
) ||
612 (std::is_same_v
<LineIteratorType
, ConstReverseLineIterator
> &&
613 aBaselineGroup
== BaselineSharingGroup::Last
),
614 "Iterator direction must match baseline sharing group.");
615 for (auto line
= aStart
; line
!= aEnd
; ++line
) {
616 if (!line
->IsBlock()) {
617 // XXX Is this the right test? We have some bogus empty lines
618 // floating around, but IsEmpty is perhaps too weak.
619 if (line
->BSize() != 0 || !line
->IsEmpty()) {
620 const auto ascent
= line
->BStart() + line
->GetLogicalAscent();
621 if (aBaselineGroup
== BaselineSharingGroup::Last
) {
622 return Some(BSize(aWM
) - ascent
);
628 nsIFrame
* kid
= line
->mFirstChild
;
629 if (aWM
.IsOrthogonalTo(kid
->GetWritingMode())) {
632 if (aExportContext
== BaselineExportContext::LineLayout
&&
633 kid
->IsTableWrapperFrame()) {
634 // `<table>` in inline-block context does not export any baseline.
637 const auto kidBaselineGroup
=
638 aExportContext
== BaselineExportContext::LineLayout
639 ? kid
->GetDefaultBaselineSharingGroup()
641 const auto kidBaseline
=
642 kid
->GetNaturalBaselineBOffset(aWM
, kidBaselineGroup
, aExportContext
);
646 auto result
= *kidBaseline
;
647 if (kidBaselineGroup
== BaselineSharingGroup::Last
) {
648 result
= kid
->BSize(aWM
) - result
;
650 // Ignore relative positioning for baseline calculations.
651 const nsSize
& sz
= line
->mContainerSize
;
652 result
+= kid
->GetLogicalNormalPosition(aWM
, sz
).B(aWM
);
653 if (aBaselineGroup
== BaselineSharingGroup::Last
) {
654 return Some(BSize(aWM
) - result
);
661 Maybe
<nscoord
> nsBlockFrame::GetNaturalBaselineBOffset(
662 WritingMode aWM
, BaselineSharingGroup aBaselineGroup
,
663 BaselineExportContext aExportContext
) const {
664 if (StyleDisplay()->IsContainLayout()) {
668 if (aBaselineGroup
== BaselineSharingGroup::First
) {
669 return GetBaselineBOffset(LinesBegin(), LinesEnd(), aWM
, aBaselineGroup
,
673 return GetBaselineBOffset(LinesRBegin(), LinesREnd(), aWM
, aBaselineGroup
,
677 nscoord
nsBlockFrame::GetCaretBaseline() const {
678 nsRect contentRect
= GetContentRect();
679 nsMargin bp
= GetUsedBorderAndPadding();
681 if (!mLines
.empty()) {
682 ConstLineIterator line
= LinesBegin();
683 if (!line
->IsEmpty()) {
684 if (line
->IsBlock()) {
685 return bp
.top
+ line
->mFirstChild
->GetCaretBaseline();
687 return line
->BStart() + line
->GetLogicalAscent();
691 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
692 RefPtr
<nsFontMetrics
> fm
=
693 nsLayoutUtils::GetFontMetricsForFrame(this, inflation
);
694 nscoord lineHeight
= ReflowInput::CalcLineHeight(
695 *Style(), PresContext(), GetContent(), contentRect
.height
, inflation
);
696 const WritingMode wm
= GetWritingMode();
697 return nsLayoutUtils::GetCenteredFontBaseline(fm
, lineHeight
,
698 wm
.IsLineInverted()) +
702 /////////////////////////////////////////////////////////////////////////////
703 // Child frame enumeration
705 const nsFrameList
& nsBlockFrame::GetChildList(ChildListID aListID
) const {
707 case FrameChildListID::Principal
:
709 case FrameChildListID::Overflow
: {
710 FrameLines
* overflowLines
= GetOverflowLines();
711 return overflowLines
? overflowLines
->mFrames
: nsFrameList::EmptyList();
713 case FrameChildListID::Float
:
715 case FrameChildListID::OverflowOutOfFlow
: {
716 const nsFrameList
* list
= GetOverflowOutOfFlows();
717 return list
? *list
: nsFrameList::EmptyList();
719 case FrameChildListID::PushedFloats
: {
720 const nsFrameList
* list
= GetPushedFloats();
721 return list
? *list
: nsFrameList::EmptyList();
723 case FrameChildListID::Bullet
: {
724 const nsFrameList
* list
= GetOutsideMarkerList();
725 return list
? *list
: nsFrameList::EmptyList();
728 return nsContainerFrame::GetChildList(aListID
);
732 void nsBlockFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const {
733 nsContainerFrame::GetChildLists(aLists
);
734 FrameLines
* overflowLines
= GetOverflowLines();
736 overflowLines
->mFrames
.AppendIfNonempty(aLists
, FrameChildListID::Overflow
);
738 const nsFrameList
* list
= GetOverflowOutOfFlows();
740 list
->AppendIfNonempty(aLists
, FrameChildListID::OverflowOutOfFlow
);
742 mFloats
.AppendIfNonempty(aLists
, FrameChildListID::Float
);
743 list
= GetOutsideMarkerList();
745 list
->AppendIfNonempty(aLists
, FrameChildListID::Bullet
);
747 list
= GetPushedFloats();
749 list
->AppendIfNonempty(aLists
, FrameChildListID::PushedFloats
);
754 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
757 * Remove the first line from aFromLines and adjust the associated frame list
758 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
759 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
760 * that were extracted from the head of aFromFrames.
761 * aFromLines must contain at least one line, the line may be empty.
762 * @return true if aFromLines becomes empty
764 static bool RemoveFirstLine(nsLineList
& aFromLines
, nsFrameList
& aFromFrames
,
765 nsLineBox
** aOutLine
, nsFrameList
* aOutFrames
) {
766 nsLineList_iterator removedLine
= aFromLines
.begin();
767 *aOutLine
= removedLine
;
768 nsLineList_iterator next
= aFromLines
.erase(removedLine
);
769 bool isLastLine
= next
== aFromLines
.end();
770 nsIFrame
* firstFrameInNextLine
= isLastLine
? nullptr : next
->mFirstChild
;
771 *aOutFrames
= aFromFrames
.TakeFramesBefore(firstFrameInNextLine
);
775 //////////////////////////////////////////////////////////////////////
779 void nsBlockFrame::MarkIntrinsicISizesDirty() {
780 nsBlockFrame
* dirtyBlock
= static_cast<nsBlockFrame
*>(FirstContinuation());
781 dirtyBlock
->mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
782 dirtyBlock
->mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
783 if (!HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
784 for (nsIFrame
* frame
= dirtyBlock
; frame
;
785 frame
= frame
->GetNextContinuation()) {
786 frame
->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
790 nsContainerFrame::MarkIntrinsicISizesDirty();
793 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
794 nsPresContext
* presContext
= PresContext();
795 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext
)) {
798 bool inflationEnabled
= !presContext
->mInflationDisabledForShrinkWrap
;
799 if (inflationEnabled
!= HasAnyStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
)) {
800 mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
801 mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
802 if (inflationEnabled
) {
803 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
805 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED
);
810 // Whether this line is indented by the text-indent amount.
811 bool nsBlockFrame::TextIndentAppliesTo(const LineIterator
& aLine
) const {
812 const auto& textIndent
= StyleText()->mTextIndent
;
814 bool isFirstLineOrAfterHardBreak
= [&] {
815 if (aLine
!= LinesBegin()) {
816 // If not the first line of the block, but 'each-line' is in effect,
817 // check if the previous line was not wrapped.
818 return textIndent
.each_line
&& !aLine
.prev()->IsLineWrapped();
820 if (nsBlockFrame
* prevBlock
= do_QueryFrame(GetPrevInFlow())) {
821 // There's a prev-in-flow, so this only counts as a first-line if
822 // 'each-line' and the prev-in-flow's last line was not wrapped.
823 return textIndent
.each_line
&&
824 (prevBlock
->Lines().empty() ||
825 !prevBlock
->LinesEnd().prev()->IsLineWrapped());
830 // The 'hanging' option inverts which lines are/aren't indented.
831 return isFirstLineOrAfterHardBreak
!= textIndent
.hanging
;
835 nscoord
nsBlockFrame::GetMinISize(gfxContext
* aRenderingContext
) {
836 nsIFrame
* firstInFlow
= FirstContinuation();
837 if (firstInFlow
!= this) {
838 return firstInFlow
->GetMinISize(aRenderingContext
);
841 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
843 CheckIntrinsicCacheAgainstShrinkWrapState();
845 if (mCachedMinISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
846 return mCachedMinISize
;
849 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
850 mCachedMinISize
= *containISize
;
851 return mCachedMinISize
;
855 if (gNoisyIntrinsic
) {
856 IndentBy(stdout
, gNoiseIndent
);
858 printf(": GetMinISize\n");
860 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
863 for (nsBlockFrame
* curFrame
= this; curFrame
;
864 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
865 curFrame
->LazyMarkLinesDirty();
868 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
869 PresContext()->BidiEnabled()) {
873 const bool whiteSpaceCanWrap
= StyleText()->WhiteSpaceCanWrapStyle();
874 InlineMinISizeData data
;
875 for (nsBlockFrame
* curFrame
= this; curFrame
;
876 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
877 for (LineIterator line
= curFrame
->LinesBegin(),
878 line_end
= curFrame
->LinesEnd();
879 line
!= line_end
; ++line
) {
881 if (gNoisyIntrinsic
) {
882 IndentBy(stdout
, gNoiseIndent
);
883 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
884 line
->IsEmpty() ? ", empty" : "");
886 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
888 if (line
->IsBlock()) {
890 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
891 aRenderingContext
, line
->mFirstChild
, IntrinsicISizeType::MinISize
);
894 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
895 data
.mCurrentLine
+= StyleText()->mTextIndent
.length
.Resolve(0);
898 data
.SetLineContainer(curFrame
);
899 nsIFrame
* kid
= line
->mFirstChild
;
900 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
901 ++i
, kid
= kid
->GetNextSibling()) {
902 kid
->AddInlineMinISize(aRenderingContext
, &data
);
903 if (whiteSpaceCanWrap
&& data
.mTrailingWhitespace
) {
904 data
.OptionallyBreak();
909 if (gNoisyIntrinsic
) {
910 IndentBy(stdout
, gNoiseIndent
);
911 printf("min: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
919 mCachedMinISize
= data
.mPrevLines
;
920 return mCachedMinISize
;
924 nscoord
nsBlockFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
925 nsIFrame
* firstInFlow
= FirstContinuation();
926 if (firstInFlow
!= this) {
927 return firstInFlow
->GetPrefISize(aRenderingContext
);
930 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
932 CheckIntrinsicCacheAgainstShrinkWrapState();
934 if (mCachedPrefISize
!= NS_INTRINSIC_ISIZE_UNKNOWN
) {
935 return mCachedPrefISize
;
938 if (Maybe
<nscoord
> containISize
= ContainIntrinsicISize()) {
939 mCachedPrefISize
= *containISize
;
940 return mCachedPrefISize
;
944 if (gNoisyIntrinsic
) {
945 IndentBy(stdout
, gNoiseIndent
);
947 printf(": GetPrefISize\n");
949 AutoNoisyIndenter
indenter(gNoisyIntrinsic
);
952 for (nsBlockFrame
* curFrame
= this; curFrame
;
953 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
954 curFrame
->LazyMarkLinesDirty();
957 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
958 PresContext()->BidiEnabled()) {
961 InlinePrefISizeData data
;
962 for (nsBlockFrame
* curFrame
= this; curFrame
;
963 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
964 for (LineIterator line
= curFrame
->LinesBegin(),
965 line_end
= curFrame
->LinesEnd();
966 line
!= line_end
; ++line
) {
968 if (gNoisyIntrinsic
) {
969 IndentBy(stdout
, gNoiseIndent
);
970 printf("line (%s%s)\n", line
->IsBlock() ? "block" : "inline",
971 line
->IsEmpty() ? ", empty" : "");
973 AutoNoisyIndenter
lineindent(gNoisyIntrinsic
);
975 if (line
->IsBlock()) {
976 StyleClear clearType
;
977 if (!data
.mLineIsEmpty
|| BlockCanIntersectFloats(line
->mFirstChild
)) {
978 clearType
= StyleClear::Both
;
980 clearType
= line
->mFirstChild
->StyleDisplay()->mClear
;
982 data
.ForceBreak(clearType
);
983 data
.mCurrentLine
= nsLayoutUtils::IntrinsicForContainer(
984 aRenderingContext
, line
->mFirstChild
,
985 IntrinsicISizeType::PrefISize
);
988 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
989 nscoord indent
= StyleText()->mTextIndent
.length
.Resolve(0);
990 data
.mCurrentLine
+= indent
;
991 // XXXmats should the test below be indent > 0?
992 if (indent
!= nscoord(0)) {
993 data
.mLineIsEmpty
= false;
997 data
.SetLineContainer(curFrame
);
998 nsIFrame
* kid
= line
->mFirstChild
;
999 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
1000 ++i
, kid
= kid
->GetNextSibling()) {
1001 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1005 if (gNoisyIntrinsic
) {
1006 IndentBy(stdout
, gNoiseIndent
);
1007 printf("pref: [prevLines=%d currentLine=%d]\n", data
.mPrevLines
,
1015 mCachedPrefISize
= data
.mPrevLines
;
1016 return mCachedPrefISize
;
1019 nsRect
nsBlockFrame::ComputeTightBounds(DrawTarget
* aDrawTarget
) const {
1021 if (Style()->HasTextDecorationLines()) {
1022 return InkOverflowRect();
1024 return ComputeSimpleTightBounds(aDrawTarget
);
1028 nsresult
nsBlockFrame::GetPrefWidthTightBounds(gfxContext
* aRenderingContext
,
1029 nscoord
* aX
, nscoord
* aXMost
) {
1030 nsIFrame
* firstInFlow
= FirstContinuation();
1031 if (firstInFlow
!= this) {
1032 return firstInFlow
->GetPrefWidthTightBounds(aRenderingContext
, aX
, aXMost
);
1039 InlinePrefISizeData data
;
1040 for (nsBlockFrame
* curFrame
= this; curFrame
;
1041 curFrame
= static_cast<nsBlockFrame
*>(curFrame
->GetNextContinuation())) {
1042 for (LineIterator line
= curFrame
->LinesBegin(),
1043 line_end
= curFrame
->LinesEnd();
1044 line
!= line_end
; ++line
) {
1045 nscoord childX
, childXMost
;
1046 if (line
->IsBlock()) {
1048 rv
= line
->mFirstChild
->GetPrefWidthTightBounds(aRenderingContext
,
1049 &childX
, &childXMost
);
1050 NS_ENSURE_SUCCESS(rv
, rv
);
1051 *aX
= std::min(*aX
, childX
);
1052 *aXMost
= std::max(*aXMost
, childXMost
);
1054 if (!curFrame
->GetPrevContinuation() && TextIndentAppliesTo(line
)) {
1055 data
.mCurrentLine
+= StyleText()->mTextIndent
.length
.Resolve(0);
1058 data
.SetLineContainer(curFrame
);
1059 nsIFrame
* kid
= line
->mFirstChild
;
1060 for (int32_t i
= 0, i_end
= line
->GetChildCount(); i
!= i_end
;
1061 ++i
, kid
= kid
->GetNextSibling()) {
1062 rv
= kid
->GetPrefWidthTightBounds(aRenderingContext
, &childX
,
1064 NS_ENSURE_SUCCESS(rv
, rv
);
1065 *aX
= std::min(*aX
, data
.mCurrentLine
+ childX
);
1066 *aXMost
= std::max(*aXMost
, data
.mCurrentLine
+ childXMost
);
1067 kid
->AddInlinePrefISize(aRenderingContext
, &data
);
1078 * Return whether aNewAvailableSpace is smaller *on either side*
1079 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
1080 * if we need to redo layout on an line, replaced block, or block
1081 * formatting context, because its height (which we used to compute
1082 * aNewAvailableSpace) caused it to intersect additional floats.
1084 static bool AvailableSpaceShrunk(WritingMode aWM
,
1085 const LogicalRect
& aOldAvailableSpace
,
1086 const LogicalRect
& aNewAvailableSpace
,
1087 bool aCanGrow
/* debug-only */) {
1088 if (aNewAvailableSpace
.ISize(aWM
) == 0) {
1089 // Positions are not significant if the inline size is zero.
1090 return aOldAvailableSpace
.ISize(aWM
) != 0;
1094 aNewAvailableSpace
.IStart(aWM
) <= aOldAvailableSpace
.IStart(aWM
) ||
1095 aNewAvailableSpace
.IEnd(aWM
) <= aOldAvailableSpace
.IEnd(aWM
),
1096 "available space should not shrink on the start side and "
1097 "grow on the end side");
1099 aNewAvailableSpace
.IStart(aWM
) >= aOldAvailableSpace
.IStart(aWM
) ||
1100 aNewAvailableSpace
.IEnd(aWM
) >= aOldAvailableSpace
.IEnd(aWM
),
1101 "available space should not grow on the start side and "
1102 "shrink on the end side");
1105 aOldAvailableSpace
.IStart(aWM
) <= aNewAvailableSpace
.IStart(aWM
) &&
1106 aOldAvailableSpace
.IEnd(aWM
) >= aNewAvailableSpace
.IEnd(aWM
),
1107 "available space should never grow");
1109 // Have we shrunk on either side?
1110 return aNewAvailableSpace
.IStart(aWM
) > aOldAvailableSpace
.IStart(aWM
) ||
1111 aNewAvailableSpace
.IEnd(aWM
) < aOldAvailableSpace
.IEnd(aWM
);
1114 static LogicalSize
CalculateContainingBlockSizeForAbsolutes(
1115 WritingMode aWM
, const ReflowInput
& aReflowInput
, LogicalSize aFrameSize
) {
1116 // The issue here is that for a 'height' of 'auto' the reflow input
1117 // code won't know how to calculate the containing block height
1118 // because it's calculated bottom up. So we use our own computed
1119 // size as the dimensions.
1120 nsIFrame
* frame
= aReflowInput
.mFrame
;
1122 LogicalSize
cbSize(aFrameSize
);
1123 // Containing block is relative to the padding edge
1124 const LogicalMargin border
= aReflowInput
.ComputedLogicalBorder(aWM
);
1125 cbSize
.ISize(aWM
) -= border
.IStartEnd(aWM
);
1126 cbSize
.BSize(aWM
) -= border
.BStartEnd(aWM
);
1128 if (frame
->GetParent()->GetContent() != frame
->GetContent() ||
1129 frame
->GetParent()->IsCanvasFrame()) {
1133 // We are a wrapped frame for the content (and the wrapper is not the
1134 // canvas frame, whose size is not meaningful here).
1135 // Use the container's dimensions, if they have been precomputed.
1136 // XXX This is a hack! We really should be waiting until the outermost
1137 // frame is fully reflowed and using the resulting dimensions, even
1138 // if they're intrinsic.
1139 // In fact we should be attaching absolute children to the outermost
1140 // frame and not always sticking them in block frames.
1142 // First, find the reflow input for the outermost frame for this content.
1143 const ReflowInput
* lastRI
= &aReflowInput
;
1144 DebugOnly
<const ReflowInput
*> lastButOneRI
= &aReflowInput
;
1145 while (lastRI
->mParentReflowInput
&&
1146 lastRI
->mParentReflowInput
->mFrame
->GetContent() ==
1147 frame
->GetContent()) {
1148 lastButOneRI
= lastRI
;
1149 lastRI
= lastRI
->mParentReflowInput
;
1152 if (lastRI
== &aReflowInput
) {
1156 // For scroll containers, we can just use cbSize (which is the padding-box
1157 // size of the scrolled-content frame).
1158 if (nsIScrollableFrame
* scrollFrame
= do_QueryFrame(lastRI
->mFrame
)) {
1159 // Assert that we're not missing any frames between the abspos containing
1160 // block and the scroll container.
1162 Unused
<< scrollFrame
;
1163 MOZ_ASSERT(lastButOneRI
== &aReflowInput
);
1167 // Same for fieldsets, where the inner anonymous frame has the correct padding
1168 // area with the legend taken into account.
1169 if (lastRI
->mFrame
->IsFieldSetFrame()) {
1173 // We found a reflow input for the outermost wrapping frame, so use
1174 // its computed metrics if available, converted to our writing mode
1175 const LogicalSize lastRISize
= lastRI
->ComputedSize(aWM
);
1176 const LogicalMargin lastRIPadding
= lastRI
->ComputedLogicalPadding(aWM
);
1177 if (lastRISize
.ISize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1179 std::max(0, lastRISize
.ISize(aWM
) + lastRIPadding
.IStartEnd(aWM
));
1181 if (lastRISize
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
1183 std::max(0, lastRISize
.BSize(aWM
) + lastRIPadding
.BStartEnd(aWM
));
1190 * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1192 * This is used to determine whether to recurse into aFrame when applying
1193 * -webkit-line-clamp.
1195 static const nsBlockFrame
* GetAsLineClampDescendant(const nsIFrame
* aFrame
) {
1196 if (const nsBlockFrame
* block
= do_QueryFrame(aFrame
)) {
1197 if (!block
->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
1204 static nsBlockFrame
* GetAsLineClampDescendant(nsIFrame
* aFrame
) {
1205 return const_cast<nsBlockFrame
*>(
1206 GetAsLineClampDescendant(const_cast<const nsIFrame
*>(aFrame
)));
1209 static bool IsLineClampRoot(const nsBlockFrame
* aFrame
) {
1210 if (!aFrame
->StyleDisplay()->mWebkitLineClamp
) {
1214 if (!aFrame
->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
1218 if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled()) {
1222 // For now, -webkit-box is the only thing allowed to be a line-clamp root.
1223 // Ideally we'd just make this work everywhere, but for now we're carrying
1224 // this forward as a limitation on the legacy -webkit-line-clamp feature,
1225 // since relaxing this limitation might create webcompat trouble.
1226 auto origDisplay
= [&] {
1227 if (aFrame
->Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
1228 // If we're the anonymous block inside the scroll frame, we need to look
1229 // at the original display of our parent frame.
1230 MOZ_ASSERT(aFrame
->GetParent());
1231 const auto& parentDisp
= *aFrame
->GetParent()->StyleDisplay();
1232 MOZ_ASSERT(parentDisp
.mWebkitLineClamp
==
1233 aFrame
->StyleDisplay()->mWebkitLineClamp
,
1234 ":-moz-scrolled-content should inherit -webkit-line-clamp, "
1235 "via rule in UA stylesheet");
1236 return parentDisp
.mOriginalDisplay
;
1238 return aFrame
->StyleDisplay()->mOriginalDisplay
;
1240 return origDisplay
.Inside() == StyleDisplayInside::WebkitBox
;
1243 bool nsBlockFrame::IsInLineClampContext() const {
1244 if (IsLineClampRoot(this)) {
1247 const nsBlockFrame
* cur
= this;
1248 while (GetAsLineClampDescendant(cur
)) {
1249 cur
= do_QueryFrame(cur
->GetParent());
1253 if (IsLineClampRoot(cur
)) {
1261 * Iterator over all descendant inline line boxes, except for those that are
1262 * under an independent formatting context.
1264 class MOZ_RAII LineClampLineIterator
{
1266 explicit LineClampLineIterator(nsBlockFrame
* aFrame
)
1267 : mCur(aFrame
->LinesBegin()),
1268 mEnd(aFrame
->LinesEnd()),
1269 mCurrentFrame(mCur
== mEnd
? nullptr : aFrame
) {
1270 if (mCur
!= mEnd
&& !mCur
->IsInline()) {
1275 nsLineBox
* GetCurrentLine() { return mCurrentFrame
? mCur
.get() : nullptr; }
1276 nsBlockFrame
* GetCurrentFrame() { return mCurrentFrame
; }
1278 // Advances the iterator to the next line line.
1280 // Next() shouldn't be called once the iterator is at the end, which can be
1281 // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
1283 MOZ_ASSERT(mCur
!= mEnd
&& mCurrentFrame
,
1284 "Don't call Next() when the iterator is at the end");
1293 // Reached the end of the current block. Pop the parent off the
1294 // stack; if there isn't one, then we've reached the end.
1295 if (mStack
.IsEmpty()) {
1296 mCurrentFrame
= nullptr;
1299 auto entry
= mStack
.PopLastElement();
1300 mCurrentFrame
= entry
.first
;
1301 mCur
= entry
.second
;
1302 mEnd
= mCurrentFrame
->LinesEnd();
1303 } else if (mCur
->IsBlock()) {
1304 if (nsBlockFrame
* child
= GetAsLineClampDescendant(mCur
->mFirstChild
)) {
1305 nsBlockFrame::LineIterator next
= mCur
;
1307 mStack
.AppendElement(std::make_pair(mCurrentFrame
, next
));
1308 mCur
= child
->LinesBegin();
1309 mEnd
= child
->LinesEnd();
1310 mCurrentFrame
= child
;
1312 // Some kind of frame we shouldn't descend into.
1316 MOZ_ASSERT(mCur
->IsInline());
1322 // The current line within the current block.
1324 // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1326 nsBlockFrame::LineIterator mCur
;
1328 // The iterator end for the current block.
1329 nsBlockFrame::LineIterator mEnd
;
1331 // The current block.
1332 nsBlockFrame
* mCurrentFrame
;
1334 // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1336 AutoTArray
<std::pair
<nsBlockFrame
*, nsBlockFrame::LineIterator
>, 8> mStack
;
1339 static bool ClearLineClampEllipsis(nsBlockFrame
* aFrame
) {
1340 if (!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
)) {
1341 for (nsIFrame
* f
: aFrame
->PrincipalChildList()) {
1342 if (nsBlockFrame
* child
= GetAsLineClampDescendant(f
)) {
1343 if (ClearLineClampEllipsis(child
)) {
1351 aFrame
->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
1353 for (auto& line
: aFrame
->Lines()) {
1354 if (line
.HasLineClampEllipsis()) {
1355 line
.ClearHasLineClampEllipsis();
1360 // We didn't find a line with the ellipsis; it must have been deleted already.
1364 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1366 void nsBlockFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
1367 const ReflowInput
& aReflowInput
,
1368 nsReflowStatus
& aStatus
) {
1369 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
1370 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1375 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1376 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aMetrics
, aStatus
);
1377 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
1381 IndentBy(stdout
, gNoiseIndent
);
1383 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1384 aReflowInput
.AvailableISize(), aReflowInput
.AvailableBSize(),
1385 aReflowInput
.ComputedISize(), aReflowInput
.ComputedBSize());
1387 AutoNoisyIndenter
indent(gNoisy
);
1388 PRTime start
= 0; // Initialize these variablies to silence the compiler.
1389 int32_t ctc
= 0; // We only use these if they are set (gLameReflowMetrics).
1390 if (gLameReflowMetrics
) {
1392 ctc
= nsLineBox::GetCtorCount();
1396 // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
1397 // max-block-size because both affect the children's available block-size.
1398 if (IsColumnSetWrapperFrame()) {
1399 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
);
1402 Maybe
<nscoord
> restoreReflowInputAvailBSize
;
1403 auto MaybeRestore
= MakeScopeExit([&] {
1404 if (MOZ_UNLIKELY(restoreReflowInputAvailBSize
)) {
1405 const_cast<ReflowInput
&>(aReflowInput
)
1406 .SetAvailableBSize(*restoreReflowInputAvailBSize
);
1410 WritingMode wm
= aReflowInput
.GetWritingMode();
1411 const nscoord consumedBSize
= CalcAndCacheConsumedBSize();
1412 const nscoord effectiveContentBoxBSize
=
1413 GetEffectiveComputedBSize(aReflowInput
, consumedBSize
);
1414 // If we have non-auto block size, we're clipping our kids and we fit,
1415 // make sure our kids fit too.
1416 const PhysicalAxes physicalBlockAxis
=
1417 wm
.IsVertical() ? PhysicalAxes::Horizontal
: PhysicalAxes::Vertical
;
1418 if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
1419 aReflowInput
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
&&
1420 (ShouldApplyOverflowClipping(aReflowInput
.mStyleDisplay
) &
1421 physicalBlockAxis
)) {
1422 LogicalMargin blockDirExtras
=
1423 aReflowInput
.ComputedLogicalBorderPadding(wm
);
1424 if (GetLogicalSkipSides().BStart()) {
1425 blockDirExtras
.BStart(wm
) = 0;
1427 // Block-end margin never causes us to create continuations, so we
1428 // don't need to worry about whether it fits in its entirety.
1429 blockDirExtras
.BStart(wm
) +=
1430 aReflowInput
.ComputedLogicalMargin(wm
).BStart(wm
);
1433 if (effectiveContentBoxBSize
+ blockDirExtras
.BStartEnd(wm
) <=
1434 aReflowInput
.AvailableBSize()) {
1435 restoreReflowInputAvailBSize
.emplace(aReflowInput
.AvailableBSize());
1436 const_cast<ReflowInput
&>(aReflowInput
)
1437 .SetAvailableBSize(NS_UNCONSTRAINEDSIZE
);
1441 if (IsFrameTreeTooDeep(aReflowInput
, aMetrics
, aStatus
)) {
1445 // OK, some lines may be reflowed. Blow away any saved line cursor
1446 // because we may invalidate the nondecreasing
1447 // overflowArea.InkOverflow().y/yMost invariant, and we may even
1448 // delete the line with the line cursor.
1451 // See comment below about oldSize. Use *only* for the
1452 // abs-pos-containing-block-size-change optimization!
1453 nsSize oldSize
= GetSize();
1455 // Should we create a float manager?
1456 nsAutoFloatManager
autoFloatManager(const_cast<ReflowInput
&>(aReflowInput
));
1458 // XXXldb If we start storing the float manager in the frame rather
1459 // than keeping it around only during reflow then we should create it
1460 // only when there are actually floats to manage. Otherwise things
1461 // like tables will gain significant bloat.
1462 bool needFloatManager
= nsBlockFrame::BlockNeedsFloatManager(this);
1463 if (needFloatManager
) {
1464 autoFloatManager
.CreateFloatManager(aPresContext
);
1467 if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
) &&
1468 PresContext()->BidiEnabled()) {
1469 static_cast<nsBlockFrame
*>(FirstContinuation())->ResolveBidi();
1472 // Whether to apply text-wrap: balance behavior.
1474 StyleText()->mTextWrapStyle
== StyleTextWrapStyle::Balance
&&
1475 !GetPrevContinuation();
1477 // Struct used to hold the "target" number of lines or clamp position to
1478 // maintain when doing text-wrap: balance.
1479 struct BalanceTarget
{
1480 // If line-clamp is in effect, mContent and mOffset indicate the starting
1481 // position of the first line after the clamp limit. If line-clamp is not
1482 // in use, mContent is null and mOffset is the total number of lines that
1483 // the block must contain.
1484 nsIContent
* mContent
= nullptr;
1485 int32_t mOffset
= -1;
1487 bool operator==(const BalanceTarget
& aOther
) const {
1488 return mContent
== aOther
.mContent
&& mOffset
== aOther
.mOffset
;
1490 bool operator!=(const BalanceTarget
& aOther
) const {
1491 return !(*this == aOther
);
1495 BalanceTarget balanceTarget
;
1497 // Helpers for text-wrap: balance implementation:
1499 // Count the number of lines in the mLines list, but return -1 (to suppress
1500 // balancing) instead if the count is going to exceed aLimit, or if we
1501 // encounter a block.
1502 auto countLinesUpTo
= [&](int32_t aLimit
) -> int32_t {
1504 for (auto iter
= mLines
.begin(); iter
!= mLines
.end(); ++iter
) {
1505 if (++n
> aLimit
|| iter
->IsBlock()) {
1512 // Return a BalanceTarget record representing the position at which line-clamp
1513 // will take effect for the current line list. Only to be used when there are
1514 // enough lines that the clamp will apply.
1515 auto getClampPosition
= [&](uint32_t aClampCount
) -> BalanceTarget
{
1516 MOZ_ASSERT(aClampCount
< mLines
.size());
1517 auto iter
= mLines
.begin();
1518 for (uint32_t i
= 0; i
< aClampCount
; i
++) {
1521 nsIFrame
* firstChild
= iter
->mFirstChild
;
1523 return BalanceTarget
{};
1525 nsIContent
* content
= firstChild
->GetContent();
1527 return BalanceTarget
{};
1530 if (firstChild
->IsTextFrame()) {
1531 auto* textFrame
= static_cast<nsTextFrame
*>(firstChild
);
1532 offset
= textFrame
->GetContentOffset();
1534 return BalanceTarget
{content
, offset
};
1537 // "balancing" is implemented by shortening the effective inline-size of the
1538 // lines, so that content will tend to be pushed down to fill later lines of
1539 // the block. `balanceInset` is the current amount of "inset" to apply, and
1540 // `balanceStep` is the increment to adjust it by for the next iteration.
1541 nscoord balanceStep
= 0;
1543 // text-wrap: balance loop, executed only once if balancing is not required.
1544 nsReflowStatus reflowStatus
;
1545 TrialReflowState
trialState(consumedBSize
, effectiveContentBoxBSize
,
1548 // Save the initial floatManager state for repeated trial reflows.
1549 // We'll restore (and re-save) the initial state each time we repeat the
1551 nsFloatManager::SavedState floatManagerState
;
1552 aReflowInput
.mFloatManager
->PushState(&floatManagerState
);
1554 aMetrics
= ReflowOutput(aMetrics
.GetWritingMode());
1556 TrialReflow(aPresContext
, aMetrics
, aReflowInput
, trialState
);
1558 // Do we need to start a `text-wrap: balance` iteration?
1561 // Don't try to balance an incomplete block, or if we had to use an
1562 // overflow-wrap break position in the initial reflow.
1563 if (!reflowStatus
.IsFullyComplete() || trialState
.mUsedOverflowWrap
) {
1566 balanceTarget
.mOffset
=
1567 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1568 if (balanceTarget
.mOffset
< 2) {
1569 // If there are less than 2 lines, or the number exceeds the limit,
1570 // no balancing is needed; just break from the balance loop.
1573 // Initialize the amount of inset to try, and the iteration step size.
1574 balanceStep
= aReflowInput
.ComputedISize() / balanceTarget
.mOffset
;
1575 trialState
.ResetForBalance(balanceStep
);
1578 // If -webkit-line-clamp is in effect, then we need to maintain the
1579 // content location at which clamping occurs, rather than the total
1580 // number of lines in the block.
1581 if (StaticPrefs::layout_css_text_wrap_balance_after_clamp_enabled() &&
1582 IsLineClampRoot(this)) {
1583 uint32_t lineClampCount
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
1584 if (uint32_t(balanceTarget
.mOffset
) > lineClampCount
) {
1585 auto t
= getClampPosition(lineClampCount
);
1592 // Restore initial floatManager state for a new trial with updated inset.
1593 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1597 // Helper to determine whether the current trial succeeded (i.e. was able
1598 // to fit the content into the expected number of lines).
1599 auto trialSucceeded
= [&]() -> bool {
1600 if (!reflowStatus
.IsFullyComplete() || trialState
.mUsedOverflowWrap
) {
1603 if (balanceTarget
.mContent
) {
1604 auto t
= getClampPosition(aReflowInput
.mStyleDisplay
->mWebkitLineClamp
);
1605 return t
== balanceTarget
;
1608 countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
1609 return numLines
== balanceTarget
.mOffset
;
1612 // If we're in the process of a balance operation, check whether we've
1613 // inset by too much and either increase or reduce the inset for the next
1615 if (balanceStep
> 0) {
1616 if (trialSucceeded()) {
1617 trialState
.ResetForBalance(balanceStep
);
1619 trialState
.ResetForBalance(-balanceStep
);
1623 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1627 // If we were attempting to balance, check whether the final iteration was
1628 // successful, and if not, back up by one step.
1629 if (balanceTarget
.mOffset
>= 0) {
1630 if (!trialState
.mInset
|| trialSucceeded()) {
1633 trialState
.ResetForBalance(-1);
1635 aReflowInput
.mFloatManager
->PopState(&floatManagerState
);
1639 // If we reach here, no balancing was required, so just exit; we don't
1640 // reset (pop) the floatManager state because this is the reflow we're
1641 // going to keep. So the saved state is just dropped.
1643 } // End of text-wrap: balance retry loop
1645 // If the block direction is right-to-left, we need to update the bounds of
1646 // lines that were placed relative to mContainerSize during reflow, as
1647 // we typically do not know the true container size until we've reflowed all
1648 // its children. So we use a dummy mContainerSize during reflow (see
1649 // BlockReflowState's constructor) and then fix up the positions of the
1650 // lines here, once the final block size is known.
1652 // Note that writing-mode:vertical-rl is the only case where the block
1653 // logical direction progresses in a negative physical direction, and
1654 // therefore block-dir coordinate conversion depends on knowing the width
1655 // of the coordinate space in order to translate between the logical and
1656 // physical origins.
1657 if (aReflowInput
.GetWritingMode().IsVerticalRL()) {
1658 nsSize containerSize
= aMetrics
.PhysicalSize();
1659 nscoord deltaX
= containerSize
.width
- trialState
.mContainerWidth
;
1661 // We compute our lines and markers' overflow areas later in
1662 // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
1664 const nsPoint
physicalDelta(deltaX
, 0);
1665 for (auto& line
: Lines()) {
1666 UpdateLineContainerSize(&line
, containerSize
);
1668 trialState
.mFcBounds
.Clear();
1669 for (nsIFrame
* f
: mFloats
) {
1670 f
->MovePositionBy(physicalDelta
);
1671 ConsiderChildOverflow(trialState
.mFcBounds
, f
);
1673 nsFrameList
* markerList
= GetOutsideMarkerList();
1675 for (nsIFrame
* f
: *markerList
) {
1676 f
->MovePositionBy(physicalDelta
);
1679 if (nsFrameList
* overflowContainers
= GetOverflowContainers()) {
1680 trialState
.mOcBounds
.Clear();
1681 for (nsIFrame
* f
: *overflowContainers
) {
1682 f
->MovePositionBy(physicalDelta
);
1683 ConsiderChildOverflow(trialState
.mOcBounds
, f
);
1689 aMetrics
.SetOverflowAreasToDesiredBounds();
1690 ComputeOverflowAreas(aMetrics
.mOverflowAreas
,
1691 trialState
.mBlockEndEdgeOfChildren
,
1692 aReflowInput
.mStyleDisplay
);
1693 // Factor overflow container child bounds into the overflow area
1694 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mOcBounds
);
1695 // Factor pushed float child bounds into the overflow area
1696 aMetrics
.mOverflowAreas
.UnionWith(trialState
.mFcBounds
);
1698 // Let the absolutely positioned container reflow any absolutely positioned
1699 // child frames that need to be reflowed, e.g., elements with a percentage
1700 // based width/height
1701 // We want to do this under either of two conditions:
1702 // 1. If we didn't do the incremental reflow above.
1703 // 2. If our size changed.
1704 // Even though it's the padding edge that's the containing block, we
1705 // can use our rect (the border edge) since if the border style
1706 // changed, the reflow would have been targeted at us so we'd satisfy
1708 // XXX checking oldSize is bogus, there are various reasons we might have
1709 // reflowed but our size might not have been changed to what we
1710 // asked for (e.g., we ended up being pushed to a new page)
1711 // When WillReflowAgainForClearance is true, we will reflow again without
1712 // resetting the size. Because of this, we must not reflow our abs-pos
1713 // children in that situation --- what we think is our "new size" will not be
1714 // our real new size. This also happens to be more efficient.
1715 WritingMode parentWM
= aMetrics
.GetWritingMode();
1716 if (HasAbsolutelyPositionedChildren()) {
1717 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
1718 bool haveInterrupt
= aPresContext
->HasPendingInterrupt();
1719 if (aReflowInput
.WillReflowAgainForClearance() || haveInterrupt
) {
1720 // Make sure that when we reflow again we'll actually reflow all the abs
1721 // pos frames that might conceivably depend on our size (or all of them,
1722 // if we're dirty right now and interrupted; in that case we also need
1723 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1724 // better than that, because we don't really know what our size will be,
1725 // and it might in fact not change on the followup reflow!
1726 if (haveInterrupt
&& HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
1727 absoluteContainer
->MarkAllFramesDirty();
1729 absoluteContainer
->MarkSizeDependentFramesDirty();
1731 if (haveInterrupt
) {
1732 // We're not going to reflow absolute frames; make sure to account for
1733 // their existing overflow areas, which is usually a side effect of this
1736 // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1737 // interrupt, can we just rely on it and unconditionally take the else
1738 // branch below? That's a bit more subtle / risky, since I don't see
1739 // what would reflow them in that case if they depended on our size.
1740 for (nsIFrame
* kid
= absoluteContainer
->GetChildList().FirstChild();
1741 kid
; kid
= kid
->GetNextSibling()) {
1742 ConsiderChildOverflow(aMetrics
.mOverflowAreas
, kid
);
1746 LogicalSize containingBlockSize
=
1747 CalculateContainingBlockSizeForAbsolutes(parentWM
, aReflowInput
,
1748 aMetrics
.Size(parentWM
));
1750 // Mark frames that depend on changes we just made to this frame as dirty:
1751 // Now we can assume that the padding edge hasn't moved.
1752 // We need to reflow the absolutes if one of them depends on
1753 // its placeholder position, or the containing block size in a
1754 // direction in which the containing block size might have
1757 // XXX "width" and "height" in this block will become ISize and BSize
1758 // when nsAbsoluteContainingBlock is logicalized
1759 bool cbWidthChanged
= aMetrics
.Width() != oldSize
.width
;
1760 bool isRoot
= !GetContent()->GetParent();
1761 // If isRoot and we have auto height, then we are the initial
1762 // containing block and the containing block height is the
1763 // viewport height, which can't change during incremental
1765 bool cbHeightChanged
=
1766 !(isRoot
&& NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedHeight()) &&
1767 aMetrics
.Height() != oldSize
.height
;
1769 nsRect
containingBlock(nsPoint(0, 0),
1770 containingBlockSize
.GetPhysicalSize(parentWM
));
1771 AbsPosReflowFlags flags
= AbsPosReflowFlags::ConstrainHeight
;
1772 if (cbWidthChanged
) {
1773 flags
|= AbsPosReflowFlags::CBWidthChanged
;
1775 if (cbHeightChanged
) {
1776 flags
|= AbsPosReflowFlags::CBHeightChanged
;
1778 // Setup the line cursor here to optimize line searching for
1779 // calculating hypothetical position of absolutely-positioned
1781 SetupLineCursorForQuery();
1782 absoluteContainer
->Reflow(this, aPresContext
, aReflowInput
, reflowStatus
,
1783 containingBlock
, flags
,
1784 &aMetrics
.mOverflowAreas
);
1788 FinishAndStoreOverflow(&aMetrics
, aReflowInput
.mStyleDisplay
);
1790 aStatus
= reflowStatus
;
1793 // Between when we drain pushed floats and when we complete reflow,
1794 // we're allowed to have multiple continuations of the same float on
1795 // our floats list, since a first-in-flow might get pushed to a later
1796 // continuation of its containing block. But it's not permitted
1797 // outside that time.
1798 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1801 IndentBy(stdout
, gNoiseIndent
);
1803 printf(": status=%s metrics=%d,%d carriedMargin=%d",
1804 ToString(aStatus
).c_str(), aMetrics
.ISize(parentWM
),
1805 aMetrics
.BSize(parentWM
), aMetrics
.mCarriedOutBEndMargin
.get());
1806 if (HasOverflowAreas()) {
1807 printf(" overflow-vis={%d,%d,%d,%d}", aMetrics
.InkOverflow().x
,
1808 aMetrics
.InkOverflow().y
, aMetrics
.InkOverflow().width
,
1809 aMetrics
.InkOverflow().height
);
1810 printf(" overflow-scr={%d,%d,%d,%d}", aMetrics
.ScrollableOverflow().x
,
1811 aMetrics
.ScrollableOverflow().y
,
1812 aMetrics
.ScrollableOverflow().width
,
1813 aMetrics
.ScrollableOverflow().height
);
1818 if (gLameReflowMetrics
) {
1819 PRTime end
= PR_Now();
1821 int32_t ectc
= nsLineBox::GetCtorCount();
1822 int32_t numLines
= mLines
.size();
1826 PRTime delta
, perLineDelta
, lines
;
1827 lines
= int64_t(numLines
);
1828 delta
= end
- start
;
1829 perLineDelta
= delta
/ lines
;
1834 ": %" PRId64
" elapsed (%" PRId64
1835 " per line) (%d lines; %d new lines)",
1836 delta
, perLineDelta
, numLines
, ectc
- ctc
);
1837 printf("%s\n", buf
);
1842 nsReflowStatus
nsBlockFrame::TrialReflow(nsPresContext
* aPresContext
,
1843 ReflowOutput
& aMetrics
,
1844 const ReflowInput
& aReflowInput
,
1845 TrialReflowState
& aTrialState
) {
1847 // Between when we drain pushed floats and when we complete reflow,
1848 // we're allowed to have multiple continuations of the same float on
1849 // our floats list, since a first-in-flow might get pushed to a later
1850 // continuation of its containing block. But it's not permitted
1851 // outside that time.
1852 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats
);
1855 // ALWAYS drain overflow. We never want to leave the previnflow's
1856 // overflow lines hanging around; block reflow depends on the
1857 // overflow line lists being cleared out between reflow passes.
1858 DrainOverflowLines();
1860 bool blockStartMarginRoot
, blockEndMarginRoot
;
1861 IsMarginRoot(&blockStartMarginRoot
, &blockEndMarginRoot
);
1863 BlockReflowState
state(aReflowInput
, aPresContext
, this, blockStartMarginRoot
,
1864 blockEndMarginRoot
, aTrialState
.mNeedFloatManager
,
1865 aTrialState
.mConsumedBSize
,
1866 aTrialState
.mEffectiveContentBoxBSize
,
1867 aTrialState
.mInset
);
1869 // Handle paginated overflow (see nsContainerFrame.h)
1870 nsReflowStatus ocStatus
;
1871 if (GetPrevInFlow()) {
1872 ReflowOverflowContainerChildren(
1873 aPresContext
, aReflowInput
, aTrialState
.mOcBounds
,
1874 ReflowChildFlags::Default
, ocStatus
, DefaultChildFrameMerge
,
1875 Some(state
.ContainerSize()));
1878 // Now that we're done cleaning up our overflow container lists, we can
1879 // give |state| its nsOverflowContinuationTracker.
1880 nsOverflowContinuationTracker
tracker(this, false);
1881 state
.mOverflowTracker
= &tracker
;
1883 // Drain & handle pushed floats
1884 DrainPushedFloats();
1885 ReflowPushedFloats(state
, aTrialState
.mFcBounds
);
1887 // If we're not dirty (which means we'll mark everything dirty later)
1888 // and our inline-size has changed, mark the lines dirty that we need to
1889 // mark dirty for a resize reflow.
1890 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.IsIResize()) {
1891 PrepareResizeReflow(state
);
1894 // The same for percentage text-indent, except conditioned on the
1896 if (!HasAnyStateBits(NS_FRAME_IS_DIRTY
) && aReflowInput
.mCBReflowInput
&&
1897 aReflowInput
.mCBReflowInput
->IsIResize() &&
1898 StyleText()->mTextIndent
.length
.HasPercent() && !mLines
.empty()) {
1899 mLines
.front()->MarkDirty();
1902 // For text-wrap:balance trials, we need to reflow all the lines even if
1903 // they're not all "dirty".
1904 if (aTrialState
.mBalancing
) {
1905 MarkAllDescendantLinesDirty(this);
1907 LazyMarkLinesDirty();
1911 aTrialState
.mUsedOverflowWrap
= ReflowDirtyLines(state
);
1913 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1914 // this frame from a previous iteration of reflow, then we should not return
1915 // a status with IsFullyComplete() equals to true, since we actually have
1916 // overflow, it's just already been handled.
1918 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1919 // and reflow them, but just in case it does, this is a safety precaution so
1920 // we don't end up with a placeholder pointing to frames that have already
1921 // been deleted as part of removing our next-in-flow.
1922 if (state
.mReflowStatus
.IsFullyComplete()) {
1923 nsBlockFrame
* nif
= static_cast<nsBlockFrame
*>(GetNextInFlow());
1925 if (nif
->HasPushedFloatsFromPrevContinuation()) {
1926 if (nif
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
1927 state
.mReflowStatus
.SetOverflowIncomplete();
1929 state
.mReflowStatus
.SetIncomplete();
1934 nif
= static_cast<nsBlockFrame
*>(nif
->GetNextInFlow());
1938 state
.mReflowStatus
.MergeCompletionStatusFrom(ocStatus
);
1940 // If we end in a BR with clear and affected floats continue,
1941 // we need to continue, too.
1942 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableBSize() &&
1943 state
.mReflowStatus
.IsComplete() &&
1944 state
.FloatManager()->ClearContinues(FindTrailingClear())) {
1945 state
.mReflowStatus
.SetIncomplete();
1948 if (!state
.mReflowStatus
.IsFullyComplete()) {
1949 if (HasOverflowLines() || HasPushedFloats()) {
1950 state
.mReflowStatus
.SetNextInFlowNeedsReflow();
1955 printf(": block is not fully complete\n");
1959 // Place the ::marker's frame if it is placed next to a block child.
1961 // According to the CSS2 spec, section 12.6.1, the ::marker's box
1962 // participates in the height calculation of the list-item box's
1965 // There are exactly two places a ::marker can be placed: near the
1966 // first or second line. It's only placed on the second line in a
1967 // rare case: an empty first line followed by a second line that
1968 // contains a block (example: <LI>\n<P>... ). This is where
1969 // the second case can happen.
1970 if (HasOutsideMarker() && !mLines
.empty() &&
1971 (mLines
.front()->IsBlock() ||
1972 (0 == mLines
.front()->BSize() && mLines
.front() != mLines
.back() &&
1973 mLines
.begin().next()->IsBlock()))) {
1974 // Reflow the ::marker's frame.
1975 ReflowOutput
reflowOutput(aReflowInput
);
1976 // XXX Use the entire line when we fix bug 25888.
1977 nsLayoutUtils::LinePosition position
;
1978 WritingMode wm
= aReflowInput
.GetWritingMode();
1980 nsLayoutUtils::GetFirstLinePosition(wm
, this, &position
);
1981 nscoord lineBStart
=
1982 havePosition
? position
.mBStart
1983 : aReflowInput
.ComputedLogicalBorderPadding(wm
).BStart(wm
);
1984 nsIFrame
* marker
= GetOutsideMarker();
1985 ReflowOutsideMarker(marker
, state
, reflowOutput
, lineBStart
);
1986 NS_ASSERTION(!MarkerIsEmpty() || reflowOutput
.BSize(wm
) == 0,
1987 "empty ::marker frame took up space");
1989 if (havePosition
&& !MarkerIsEmpty()) {
1990 // We have some lines to align the ::marker with.
1992 // Doing the alignment using the baseline will also cater for
1993 // ::markers that are placed next to a child block (bug 92896)
1995 // Tall ::markers won't look particularly nice here...
1997 marker
->GetLogicalRect(wm
, reflowOutput
.PhysicalSize());
1998 const auto baselineGroup
= BaselineSharingGroup::First
;
1999 Maybe
<nscoord
> result
;
2000 if (MOZ_LIKELY(!wm
.IsOrthogonalTo(marker
->GetWritingMode()))) {
2001 result
= marker
->GetNaturalBaselineBOffset(
2002 wm
, baselineGroup
, BaselineExportContext::LineLayout
);
2004 const auto markerBaseline
= result
.valueOrFrom([bbox
, wm
, marker
]() {
2005 return bbox
.BSize(wm
) + marker
->GetLogicalUsedMargin(wm
).BEnd(wm
);
2007 bbox
.BStart(wm
) = position
.mBaseline
- markerBaseline
;
2008 marker
->SetRect(wm
, bbox
, reflowOutput
.PhysicalSize());
2010 // Otherwise just leave the ::marker where it is, up against our
2011 // block-start padding.
2014 // Clear any existing -webkit-line-clamp ellipsis.
2015 if (aReflowInput
.mStyleDisplay
->mWebkitLineClamp
) {
2016 ClearLineClampEllipsis();
2021 // Compute our final size (for this trial layout)
2022 aTrialState
.mBlockEndEdgeOfChildren
=
2023 ComputeFinalSize(aReflowInput
, state
, aMetrics
);
2024 aTrialState
.mContainerWidth
= state
.ContainerSize().width
;
2026 return state
.mReflowStatus
;
2029 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
2030 for (auto& line
: Reversed(Lines())) {
2031 if (0 != line
.BSize() || !line
.CachedIsEmpty()) {
2034 if (line
.HasClearance()) {
2041 static nsLineBox
* FindLineClampTarget(nsBlockFrame
*& aFrame
,
2042 StyleLineClamp aLineNumber
) {
2043 MOZ_ASSERT(aLineNumber
> 0);
2044 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2045 "Should have been removed earlier in nsBlockReflow::Reflow");
2047 nsLineBox
* target
= nullptr;
2048 nsBlockFrame
* targetFrame
= nullptr;
2049 bool foundFollowingLine
= false;
2051 LineClampLineIterator
iter(aFrame
);
2053 while (nsLineBox
* line
= iter
.GetCurrentLine()) {
2054 MOZ_ASSERT(!line
->HasLineClampEllipsis(),
2055 "Should have been removed earlier in nsBlockFrame::Reflow");
2056 MOZ_ASSERT(!iter
.GetCurrentFrame()->HasAnyStateBits(
2057 NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
),
2058 "Should have been removed earlier in nsBlockReflow::Reflow");
2060 // Don't count a line that only has collapsible white space (as might exist
2061 // after calling e.g. getBoxQuads).
2062 if (line
->IsEmpty()) {
2067 if (aLineNumber
== 0) {
2068 // We already previously found our target line, and now we have
2069 // confirmed that there is another line after it.
2070 foundFollowingLine
= true;
2074 if (--aLineNumber
== 0) {
2075 // This is our target line. Continue looping to confirm that we
2076 // have another line after us.
2078 targetFrame
= iter
.GetCurrentFrame();
2084 if (!foundFollowingLine
) {
2090 MOZ_ASSERT(targetFrame
);
2092 aFrame
= targetFrame
;
2096 static nscoord
ApplyLineClamp(const ReflowInput
& aReflowInput
,
2097 nsBlockFrame
* aFrame
,
2098 nscoord aContentBlockEndEdge
) {
2099 if (!IsLineClampRoot(aFrame
)) {
2100 return aContentBlockEndEdge
;
2102 auto lineClamp
= aReflowInput
.mStyleDisplay
->mWebkitLineClamp
;
2103 nsBlockFrame
* frame
= aFrame
;
2104 nsLineBox
* line
= FindLineClampTarget(frame
, lineClamp
);
2106 // The number of lines did not exceed the -webkit-line-clamp value.
2107 return aContentBlockEndEdge
;
2110 // Mark the line as having an ellipsis so that TextOverflow will render it.
2111 line
->SetHasLineClampEllipsis();
2112 frame
->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS
);
2114 // Translate the b-end edge of the line up to aFrame's space.
2115 nscoord edge
= line
->BEnd();
2116 for (nsIFrame
* f
= frame
; f
!= aFrame
; f
= f
->GetParent()) {
2118 f
->GetLogicalPosition(f
->GetParent()->GetSize()).B(f
->GetWritingMode());
2124 nscoord
nsBlockFrame::ComputeFinalSize(const ReflowInput
& aReflowInput
,
2125 BlockReflowState
& aState
,
2126 ReflowOutput
& aMetrics
) {
2127 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2128 const LogicalMargin
& borderPadding
= aState
.BorderPadding();
2129 #ifdef NOISY_FINAL_SIZE
2131 printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
2132 aState
.mBCoord
, aState
.mFlags
.mIsBEndMarginRoot
? "yes" : "no",
2133 aState
.mPrevBEndMargin
.get(), borderPadding
.BStart(wm
),
2134 borderPadding
.BEnd(wm
));
2137 // Compute final inline size
2138 LogicalSize
finalSize(wm
);
2139 finalSize
.ISize(wm
) =
2140 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding
.IStart(wm
),
2141 aReflowInput
.ComputedISize()),
2142 borderPadding
.IEnd(wm
));
2144 // Return block-end margin information
2145 // rbs says he hit this assertion occasionally (see bug 86947), so
2146 // just set the margin to zero and we'll figure out why later
2147 // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
2148 // "someone else set the margin");
2149 nscoord nonCarriedOutBDirMargin
= 0;
2150 if (!aState
.mFlags
.mIsBEndMarginRoot
) {
2151 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
2152 // line with clearance and a non-zero block-start margin and all
2153 // subsequent lines are empty, then we do not allow our children's
2154 // carried out block-end margin to be carried out of us and collapse
2155 // with our own block-end margin.
2156 if (CheckForCollapsedBEndMarginFromClearanceLine()) {
2157 // Convert the children's carried out margin to something that
2158 // we will include in our height
2159 nonCarriedOutBDirMargin
= aState
.mPrevBEndMargin
.get();
2160 aState
.mPrevBEndMargin
.Zero();
2162 aMetrics
.mCarriedOutBEndMargin
= aState
.mPrevBEndMargin
;
2164 aMetrics
.mCarriedOutBEndMargin
.Zero();
2167 nscoord blockEndEdgeOfChildren
= aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2168 // Shrink wrap our height around our contents.
2169 if (aState
.mFlags
.mIsBEndMarginRoot
||
2170 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2171 // When we are a block-end-margin root make sure that our last
2172 // child's block-end margin is fully applied. We also do this when
2173 // we have a computed height, since in that case the carried out
2174 // margin is not going to be applied anywhere, so we should note it
2175 // here to be included in the overflow area.
2176 // Apply the margin only if there's space for it.
2177 if (blockEndEdgeOfChildren
< aState
.mReflowInput
.AvailableBSize()) {
2178 // Truncate block-end margin if it doesn't fit to our available BSize.
2179 blockEndEdgeOfChildren
=
2180 std::min(blockEndEdgeOfChildren
+ aState
.mPrevBEndMargin
.get(),
2181 aState
.mReflowInput
.AvailableBSize());
2184 if (aState
.mFlags
.mBlockNeedsFloatManager
) {
2185 // Include the float manager's state to properly account for the
2186 // block-end margin of any floated elements; e.g., inside a table cell.
2188 // Note: The block coordinate returned by ClearFloats is always greater than
2189 // or equal to blockEndEdgeOfChildren.
2190 std::tie(blockEndEdgeOfChildren
, std::ignore
) =
2191 aState
.ClearFloats(blockEndEdgeOfChildren
, StyleClear::Both
);
2194 if (NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedBSize()) {
2195 // Note: We don't use blockEndEdgeOfChildren because it includes the
2197 const nscoord contentBSizeWithBStartBP
=
2198 aState
.mBCoord
+ nonCarriedOutBDirMargin
;
2200 // We don't care about ApplyLineClamp's return value (the line-clamped
2201 // content BSize) in this explicit-BSize codepath, but we do still need to
2202 // call ApplyLineClamp for ellipsis markers to be placed as-needed.
2203 ApplyLineClamp(aState
.mReflowInput
, this, contentBSizeWithBStartBP
);
2205 finalSize
.BSize(wm
) = ComputeFinalBSize(aState
, contentBSizeWithBStartBP
);
2207 // If the content block-size is larger than the effective computed
2208 // block-size, we extend the block-size to contain all the content.
2209 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
2210 if (aReflowInput
.ShouldApplyAutomaticMinimumOnBlockAxis()) {
2211 // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
2212 // the content's block-size plus our border and padding..
2213 finalSize
.BSize(wm
) =
2214 std::max(finalSize
.BSize(wm
),
2215 contentBSizeWithBStartBP
+ borderPadding
.BEnd(wm
));
2218 // Don't carry out a block-end margin when our BSize is fixed.
2220 // Note: this also includes the case that aReflowInput.ComputedBSize() is
2221 // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
2222 // is replaced by the block size from aspect-ratio and inline size.
2223 aMetrics
.mCarriedOutBEndMargin
.Zero();
2225 Maybe
<nscoord
> containBSize
= ContainIntrinsicBSize(
2226 IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE
: 0);
2227 if (containBSize
&& *containBSize
!= NS_UNCONSTRAINEDSIZE
) {
2228 // If we're size-containing in block axis and we don't have a specified
2229 // block size, then our final size should actually be computed from only
2230 // our border, padding and contain-intrinsic-block-size, ignoring the
2231 // actual contents. Hence this case is a simplified version of the case
2234 // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
2235 // special case when it has 'contain-intrinsic-block-size: none', because
2236 // comboboxes implicitly honors the size-containment behavior on its
2237 // nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the
2238 // nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even
2239 // need any special content-size-ignoring behavior in its reflow method,
2240 // because that method just resolves "auto" BSize values to one
2241 // line-height rather than by measuring its contents' BSize.)
2242 nscoord contentBSize
= *containBSize
;
2244 aReflowInput
.ApplyMinMaxBSize(contentBSize
, aState
.mConsumedBSize
);
2245 aMetrics
.mCarriedOutBEndMargin
.Zero();
2246 autoBSize
+= borderPadding
.BStartEnd(wm
);
2247 finalSize
.BSize(wm
) = autoBSize
;
2248 } else if (aState
.mReflowStatus
.IsInlineBreakBefore()) {
2249 // Our parent is expected to push this frame to the next page/column so
2250 // what size we set here doesn't really matter.
2251 finalSize
.BSize(wm
) = aReflowInput
.AvailableBSize();
2252 } else if (aState
.mReflowStatus
.IsComplete()) {
2253 const nscoord lineClampedContentBlockEndEdge
=
2254 ApplyLineClamp(aReflowInput
, this, blockEndEdgeOfChildren
);
2256 const nscoord bpBStart
= borderPadding
.BStart(wm
);
2257 const nscoord contentBSize
= blockEndEdgeOfChildren
- bpBStart
;
2258 const nscoord lineClampedContentBSize
=
2259 lineClampedContentBlockEndEdge
- bpBStart
;
2261 const nscoord autoBSize
= aReflowInput
.ApplyMinMaxBSize(
2262 lineClampedContentBSize
, aState
.mConsumedBSize
);
2263 if (autoBSize
!= contentBSize
) {
2264 // Our min-block-size, max-block-size, or -webkit-line-clamp value made
2265 // our bsize change. Don't carry out our kids' block-end margins.
2266 aMetrics
.mCarriedOutBEndMargin
.Zero();
2268 nscoord bSize
= autoBSize
+ borderPadding
.BStartEnd(wm
);
2269 if (MOZ_UNLIKELY(autoBSize
> contentBSize
&&
2270 bSize
> aReflowInput
.AvailableBSize() &&
2271 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
)) {
2272 // Applying `min-size` made us overflow our available size.
2273 // Clamp it and report that we're Incomplete, or BreakBefore if we have
2274 // 'break-inside: avoid' that is applicable.
2275 bSize
= aReflowInput
.AvailableBSize();
2276 if (ShouldAvoidBreakInside(aReflowInput
)) {
2277 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
2279 aState
.mReflowStatus
.SetIncomplete();
2282 finalSize
.BSize(wm
) = bSize
;
2285 aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
2286 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
2287 nscoord bSize
= std::max(aState
.mBCoord
, aReflowInput
.AvailableBSize());
2288 if (aReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
2289 // This should never happen, but it does. See bug 414255
2290 bSize
= aState
.mBCoord
;
2292 const nscoord maxBSize
= aReflowInput
.ComputedMaxBSize();
2293 if (maxBSize
!= NS_UNCONSTRAINEDSIZE
&&
2294 aState
.mConsumedBSize
+ bSize
- borderPadding
.BStart(wm
) > maxBSize
) {
2295 // Compute this fragment's block-size, with the max-block-size
2296 // constraint taken into consideration.
2297 const nscoord clampedBSizeWithoutEndBP
=
2298 std::max(0, maxBSize
- aState
.mConsumedBSize
) +
2299 borderPadding
.BStart(wm
);
2300 const nscoord clampedBSize
=
2301 clampedBSizeWithoutEndBP
+ borderPadding
.BEnd(wm
);
2302 if (clampedBSize
<= aReflowInput
.AvailableBSize()) {
2303 // We actually fit after applying `max-size` so we should be
2304 // Overflow-Incomplete instead.
2305 bSize
= clampedBSize
;
2306 aState
.mReflowStatus
.SetOverflowIncomplete();
2308 // We cannot fit after applying `max-size` with our block-end BP, so
2309 // we should draw it in our next continuation.
2310 bSize
= clampedBSizeWithoutEndBP
;
2313 finalSize
.BSize(wm
) = bSize
;
2317 if (IsTrueOverflowContainer()) {
2318 if (aState
.mReflowStatus
.IsIncomplete()) {
2319 // Overflow containers can only be overflow complete.
2320 // Note that auto height overflow containers have no normal children
2321 NS_ASSERTION(finalSize
.BSize(wm
) == 0,
2322 "overflow containers must be zero-block-size");
2323 aState
.mReflowStatus
.SetOverflowIncomplete();
2325 } else if (aReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2326 !aState
.mReflowStatus
.IsInlineBreakBefore() &&
2327 aState
.mReflowStatus
.IsComplete()) {
2328 // Currently only used for grid items, but could be used in other contexts.
2329 // The FragStretchBSizeProperty is our expected non-fragmented block-size
2330 // we should stretch to (for align-self:stretch etc). In some fragmentation
2331 // cases though, the last fragment (this frame since we're complete), needs
2332 // to have extra size applied because earlier fragments consumed too much of
2333 // our computed size due to overflowing their containing block. (E.g. this
2334 // ensures we fill the last row when a multi-row grid item is fragmented).
2336 nscoord bSize
= GetProperty(FragStretchBSizeProperty(), &found
);
2338 finalSize
.BSize(wm
) = std::max(bSize
, finalSize
.BSize(wm
));
2342 // Clamp the content size to fit within the margin-box clamp size, if any.
2343 if (MOZ_UNLIKELY(aReflowInput
.mComputeSizeFlags
.contains(
2344 ComputeSizeFlag::BClampMarginBoxMinSize
)) &&
2345 aState
.mReflowStatus
.IsComplete()) {
2347 nscoord cbSize
= GetProperty(BClampMarginBoxMinSizeProperty(), &found
);
2349 auto marginBoxBSize
=
2350 finalSize
.BSize(wm
) +
2351 aReflowInput
.ComputedLogicalMargin(wm
).BStartEnd(wm
);
2352 auto overflow
= marginBoxBSize
- cbSize
;
2354 auto contentBSize
= finalSize
.BSize(wm
) - borderPadding
.BStartEnd(wm
);
2355 auto newContentBSize
= std::max(nscoord(0), contentBSize
- overflow
);
2356 // XXXmats deal with percentages better somehow?
2357 finalSize
.BSize(wm
) -= contentBSize
- newContentBSize
;
2362 // Screen out negative block sizes --- can happen due to integer overflows :-(
2363 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
2365 if (blockEndEdgeOfChildren
!= finalSize
.BSize(wm
) - borderPadding
.BEnd(wm
)) {
2366 SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren
);
2368 RemoveProperty(BlockEndEdgeOfChildrenProperty());
2371 aMetrics
.SetSize(wm
, finalSize
);
2374 if ((ABSURD_SIZE(aMetrics
.Width()) || ABSURD_SIZE(aMetrics
.Height())) &&
2375 !GetParent()->IsAbsurdSizeAssertSuppressed()) {
2377 printf(": WARNING: desired:%d,%d\n", aMetrics
.Width(), aMetrics
.Height());
2381 return blockEndEdgeOfChildren
;
2384 void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
2385 OverflowAreas
& aOverflowAreas
, nscoord aBEndEdgeOfChildren
,
2386 const nsStyleDisplay
* aDisplay
) const {
2387 const auto wm
= GetWritingMode();
2389 // Factor in the block-end edge of the children. Child frames will be added
2390 // to the overflow area as we iterate through the lines, but their margins
2391 // won't, so we need to account for block-end margins here.
2392 // REVIEW: For now, we do this for both visual and scrollable area,
2393 // although when we make scrollable overflow area not be a subset of
2394 // visual, we can change this.
2396 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
2397 // If we are a scrolled inner frame, add our block-end padding to our
2398 // children's block-end edge.
2400 // Note: aBEndEdgeOfChildren already includes our own block-start padding
2401 // because it is relative to our block-start edge of our border-box, which
2402 // is the same as our padding-box here.
2403 MOZ_ASSERT(GetLogicalUsedBorderAndPadding(wm
) == GetLogicalUsedPadding(wm
),
2404 "A scrolled inner frame shouldn't have any border!");
2405 aBEndEdgeOfChildren
+= GetLogicalUsedPadding(wm
).BEnd(wm
);
2408 // XXX Currently, overflow areas are stored as physical rects, so we have
2409 // to handle writing modes explicitly here. If we change overflow rects
2410 // to be stored logically, this can be simplified again.
2411 if (wm
.IsVertical()) {
2412 if (wm
.IsVerticalLR()) {
2413 for (const auto otype
: AllOverflowTypes()) {
2414 if (!(aDisplay
->IsContainLayout() &&
2415 otype
== OverflowType::Scrollable
)) {
2416 // Layout containment should force all overflow to be ink (visual)
2417 // overflow, so if we're layout-contained, we only add our children's
2418 // block-end edge to the ink (visual) overflow -- not to the
2419 // scrollable overflow.
2420 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2421 o
.width
= std::max(o
.XMost(), aBEndEdgeOfChildren
) - o
.x
;
2425 for (const auto otype
: AllOverflowTypes()) {
2426 if (!(aDisplay
->IsContainLayout() &&
2427 otype
== OverflowType::Scrollable
)) {
2428 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2429 nscoord xmost
= o
.XMost();
2430 o
.x
= std::min(o
.x
, xmost
- aBEndEdgeOfChildren
);
2431 o
.width
= xmost
- o
.x
;
2436 for (const auto otype
: AllOverflowTypes()) {
2437 if (!(aDisplay
->IsContainLayout() && otype
== OverflowType::Scrollable
)) {
2438 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
2439 o
.height
= std::max(o
.YMost(), aBEndEdgeOfChildren
) - o
.y
;
2445 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas
& aOverflowAreas
,
2446 nscoord aBEndEdgeOfChildren
,
2447 const nsStyleDisplay
* aDisplay
) const {
2448 // XXX_perf: This can be done incrementally. It is currently one of
2449 // the things that makes incremental reflow O(N^2).
2450 auto overflowClipAxes
= ShouldApplyOverflowClipping(aDisplay
);
2451 auto overflowClipMargin
= OverflowClipMargin(overflowClipAxes
);
2452 if (overflowClipAxes
== PhysicalAxes::Both
&&
2453 overflowClipMargin
== nsSize()) {
2457 // We rely here on our caller having called SetOverflowAreasToDesiredBounds().
2458 nsRect frameBounds
= aOverflowAreas
.ScrollableOverflow();
2460 for (const auto& line
: Lines()) {
2461 if (aDisplay
->IsContainLayout()) {
2462 // If we have layout containment, we should only consider our child's
2463 // ink overflow, leaving the scrollable regions of the parent
2465 // Note: scrollable overflow is a subset of ink overflow,
2466 // so this has the same affect as unioning the child's visual and
2467 // scrollable overflow with its parent's ink overflow.
2468 nsRect childVisualRect
= line
.InkOverflowRect();
2469 OverflowAreas childVisualArea
= OverflowAreas(childVisualRect
, nsRect());
2470 aOverflowAreas
.UnionWith(childVisualArea
);
2472 aOverflowAreas
.UnionWith(line
.GetOverflowAreas());
2476 // Factor an outside ::marker in; normally the ::marker will be factored
2477 // into the line-box's overflow areas. However, if the line is a block
2478 // line then it won't; if there are no lines, it won't. So just
2479 // factor it in anyway (it can't hurt if it was already done).
2480 // XXXldb Can we just fix GetOverflowArea instead?
2481 if (nsIFrame
* outsideMarker
= GetOutsideMarker()) {
2482 aOverflowAreas
.UnionAllWith(outsideMarker
->GetRect());
2485 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, aBEndEdgeOfChildren
, aDisplay
);
2487 if (overflowClipAxes
!= PhysicalAxes::None
) {
2488 aOverflowAreas
.ApplyClipping(frameBounds
, overflowClipAxes
,
2489 overflowClipMargin
);
2492 #ifdef NOISY_OVERFLOW_AREAS
2493 printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
2494 ToString(aOverflowAreas
.InkOverflow()).c_str(),
2495 ToString(aOverflowAreas
.ScrollableOverflow()).c_str());
2499 void nsBlockFrame::UnionChildOverflow(OverflowAreas
& aOverflowAreas
) {
2500 // We need to update the overflow areas of lines manually, as they
2501 // get cached and re-used otherwise. Lines aren't exposed as normal
2502 // frame children, so calling UnionChildOverflow alone will end up
2503 // using the old cached values.
2504 for (auto& line
: Lines()) {
2505 nsRect bounds
= line
.GetPhysicalBounds();
2506 OverflowAreas
lineAreas(bounds
, bounds
);
2508 int32_t n
= line
.GetChildCount();
2509 for (nsIFrame
* lineFrame
= line
.mFirstChild
; n
> 0;
2510 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2511 ConsiderChildOverflow(lineAreas
, lineFrame
);
2514 // Consider the overflow areas of the floats attached to the line as well
2515 if (line
.HasFloats()) {
2516 for (nsIFrame
* f
: line
.Floats()) {
2517 ConsiderChildOverflow(lineAreas
, f
);
2521 line
.SetOverflowAreas(lineAreas
);
2522 aOverflowAreas
.UnionWith(lineAreas
);
2525 // Union with child frames, skipping the principal and float lists
2526 // since we already handled those using the line boxes.
2527 nsLayoutUtils::UnionChildOverflow(
2528 this, aOverflowAreas
,
2529 {FrameChildListID::Principal
, FrameChildListID::Float
});
2532 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas
& aOverflowAreas
) {
2534 nscoord blockEndEdgeOfChildren
=
2535 GetProperty(BlockEndEdgeOfChildrenProperty(), &found
);
2537 ConsiderBlockEndEdgeOfChildren(aOverflowAreas
, blockEndEdgeOfChildren
,
2541 // Line cursor invariants depend on the overflow areas of the lines, so
2542 // we must clear the line cursor since those areas may have changed.
2544 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
2547 void nsBlockFrame::LazyMarkLinesDirty() {
2548 if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
)) {
2549 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2550 line
!= line_end
; ++line
) {
2551 int32_t n
= line
->GetChildCount();
2552 for (nsIFrame
* lineFrame
= line
->mFirstChild
; n
> 0;
2553 lineFrame
= lineFrame
->GetNextSibling(), --n
) {
2554 if (lineFrame
->IsSubtreeDirty()) {
2555 // NOTE: MarkLineDirty does more than just marking the line dirty.
2556 MarkLineDirty(line
, &mLines
);
2561 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
2565 void nsBlockFrame::MarkLineDirty(LineIterator aLine
,
2566 const nsLineList
* aLineList
) {
2569 aLine
->SetInvalidateTextRuns(true);
2572 IndentBy(stdout
, gNoiseIndent
);
2574 printf(": mark line %p dirty\n", static_cast<void*>(aLine
.get()));
2578 // Mark previous line dirty if it's an inline line so that it can
2579 // maybe pullup something from the line just affected.
2580 // XXX We don't need to do this if aPrevLine ends in a break-after...
2581 if (aLine
!= aLineList
->front() && aLine
->IsInline() &&
2582 aLine
.prev()->IsInline()) {
2583 aLine
.prev()->MarkDirty();
2584 aLine
.prev()->SetInvalidateTextRuns(true);
2587 IndentBy(stdout
, gNoiseIndent
);
2589 printf(": mark prev-line %p dirty\n",
2590 static_cast<void*>(aLine
.prev().get()));
2597 * Test whether lines are certain to be aligned left so that we can make
2598 * resizing optimizations
2600 static inline bool IsAlignedLeft(StyleTextAlign aAlignment
,
2601 StyleDirection aDirection
,
2602 StyleUnicodeBidi aUnicodeBidi
,
2604 return aFrame
->IsInSVGTextSubtree() || StyleTextAlign::Left
== aAlignment
||
2605 (((StyleTextAlign::Start
== aAlignment
&&
2606 StyleDirection::Ltr
== aDirection
) ||
2607 (StyleTextAlign::End
== aAlignment
&&
2608 StyleDirection::Rtl
== aDirection
)) &&
2609 aUnicodeBidi
!= StyleUnicodeBidi::Plaintext
);
2612 void nsBlockFrame::PrepareResizeReflow(BlockReflowState
& aState
) {
2613 // See if we can try and avoid marking all the lines as dirty
2614 // FIXME(emilio): This should be writing-mode aware, I guess.
2615 bool tryAndSkipLines
=
2616 // The left content-edge must be a constant distance from the left
2618 !StylePadding()->mPadding
.Get(eSideLeft
).HasPercent();
2621 if (gDisableResizeOpt
) {
2622 tryAndSkipLines
= false;
2625 if (!tryAndSkipLines
) {
2626 IndentBy(stdout
, gNoiseIndent
);
2628 printf(": marking all lines dirty: availISize=%d\n",
2629 aState
.mReflowInput
.AvailableISize());
2634 if (tryAndSkipLines
) {
2635 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2636 nscoord newAvailISize
=
2637 aState
.mReflowInput
.ComputedLogicalBorderPadding(wm
).IStart(wm
) +
2638 aState
.mReflowInput
.ComputedISize();
2642 IndentBy(stdout
, gNoiseIndent
);
2644 printf(": trying to avoid marking all lines dirty\n");
2648 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2649 line
!= line_end
; ++line
) {
2650 // We let child blocks make their own decisions the same
2652 bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
2653 if (line
->IsBlock() || line
->HasFloats() ||
2654 (!isLastLine
&& !line
->HasForcedLineBreakAfter()) ||
2655 ((isLastLine
|| !line
->IsLineWrapped())) ||
2656 line
->ResizeReflowOptimizationDisabled() ||
2657 line
->IsImpactedByFloat() || (line
->IEnd() > newAvailISize
)) {
2661 #ifdef REALLY_NOISY_REFLOW
2662 if (!line
->IsBlock()) {
2663 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2664 line
.get(), line
->IsImpactedByFloat() ? "" : "not ");
2668 if (gNoisyReflow
&& !line
->IsDirty()) {
2669 IndentBy(stdout
, gNoiseIndent
+ 1);
2671 "skipped: line=%p next=%p %s %s%s%s clearTypeBefore/After=%s/%s "
2673 static_cast<void*>(line
.get()),
2675 (line
.next() != LinesEnd() ? line
.next().get() : nullptr)),
2676 line
->IsBlock() ? "block" : "inline",
2677 line
->HasForcedLineBreakAfter() ? "has-break-after " : "",
2678 line
->HasFloats() ? "has-floats " : "",
2679 line
->IsImpactedByFloat() ? "impacted " : "",
2680 line
->StyleClearToString(line
->FloatClearTypeBefore()),
2681 line
->StyleClearToString(line
->FloatClearTypeAfter()),
2687 // Mark everything dirty
2688 for (auto& line
: Lines()) {
2694 //----------------------------------------
2697 * Propagate reflow "damage" from from earlier lines to the current
2698 * line. The reflow damage comes from the following sources:
2699 * 1. The regions of float damage remembered during reflow.
2700 * 2. The combination of nonzero |aDeltaBCoord| and any impact by a
2701 * float, either the previous reflow or now.
2703 * When entering this function, |aLine| is still at its old position and
2704 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2705 * doesn't get marked dirty and reflowed entirely).
2707 void nsBlockFrame::PropagateFloatDamage(BlockReflowState
& aState
,
2709 nscoord aDeltaBCoord
) {
2710 nsFloatManager
* floatManager
= aState
.FloatManager();
2712 (aState
.mReflowInput
.mParentReflowInput
&&
2713 aState
.mReflowInput
.mParentReflowInput
->mFloatManager
== floatManager
) ||
2714 aState
.mReflowInput
.mBlockDelta
== 0,
2715 "Bad block delta passed in");
2717 // Check to see if there are any floats; if there aren't, there can't
2718 // be any float damage
2719 if (!floatManager
->HasAnyFloats()) {
2723 // Check the damage region recorded in the float damage.
2724 if (floatManager
->HasFloatDamage()) {
2725 // Need to check mBounds *and* mCombinedArea to find intersections
2726 // with aLine's floats
2727 nscoord lineBCoordBefore
= aLine
->BStart() + aDeltaBCoord
;
2728 nscoord lineBCoordAfter
= lineBCoordBefore
+ aLine
->BSize();
2729 // Scrollable overflow should be sufficient for things that affect
2731 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
2732 nsSize containerSize
= aState
.ContainerSize();
2733 LogicalRect overflow
=
2734 aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
, containerSize
);
2735 nscoord lineBCoordCombinedBefore
= overflow
.BStart(wm
) + aDeltaBCoord
;
2736 nscoord lineBCoordCombinedAfter
=
2737 lineBCoordCombinedBefore
+ overflow
.BSize(wm
);
2740 floatManager
->IntersectsDamage(lineBCoordBefore
, lineBCoordAfter
) ||
2741 floatManager
->IntersectsDamage(lineBCoordCombinedBefore
,
2742 lineBCoordCombinedAfter
);
2749 // Check if the line is moving relative to the float manager
2750 if (aDeltaBCoord
+ aState
.mReflowInput
.mBlockDelta
!= 0) {
2751 if (aLine
->IsBlock()) {
2752 // Unconditionally reflow sliding blocks; we only really need to reflow
2753 // if there's a float impacting this block, but the current float manager
2754 // makes it difficult to check that. Therefore, we let the child block
2755 // decide what it needs to reflow.
2758 bool wasImpactedByFloat
= aLine
->IsImpactedByFloat();
2759 nsFlowAreaRect floatAvailableSpace
=
2760 aState
.GetFloatAvailableSpaceForBSize(aLine
->BStart() + aDeltaBCoord
,
2761 aLine
->BSize(), nullptr);
2763 #ifdef REALLY_NOISY_REFLOW
2764 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2765 wasImpactedByFloat
, floatAvailableSpace
.HasFloats());
2768 // Mark the line dirty if it was or is affected by a float
2769 // We actually only really need to reflow if the amount of impact
2770 // changes, but that's not straightforward to check
2771 if (wasImpactedByFloat
|| floatAvailableSpace
.HasFloats()) {
2778 static bool LineHasClear(nsLineBox
* aLine
) {
2779 return aLine
->IsBlock()
2780 ? (aLine
->HasForcedLineBreakBefore() ||
2781 aLine
->mFirstChild
->HasAnyStateBits(
2782 NS_BLOCK_HAS_CLEAR_CHILDREN
) ||
2783 !nsBlockFrame::BlockCanIntersectFloats(aLine
->mFirstChild
))
2784 : aLine
->HasFloatClearTypeAfter();
2788 * Reparent a whole list of floats from aOldParent to this block. The
2789 * floats might be taken from aOldParent's overflow list. They will be
2790 * removed from the list. They end up appended to our mFloats list.
2792 void nsBlockFrame::ReparentFloats(nsIFrame
* aFirstFrame
,
2793 nsBlockFrame
* aOldParent
,
2794 bool aReparentSiblings
) {
2796 aOldParent
->CollectFloats(aFirstFrame
, list
, aReparentSiblings
);
2797 if (list
.NotEmpty()) {
2798 for (nsIFrame
* f
: list
) {
2799 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
2800 "CollectFloats should've removed that bit");
2801 ReparentFrame(f
, aOldParent
, this);
2803 mFloats
.AppendFrames(nullptr, std::move(list
));
2807 static void DumpLine(const BlockReflowState
& aState
, nsLineBox
* aLine
,
2808 nscoord aDeltaBCoord
, int32_t aDeltaIndent
) {
2810 if (nsBlockFrame::gNoisyReflow
) {
2811 nsRect
ovis(aLine
->InkOverflowRect());
2812 nsRect
oscr(aLine
->ScrollableOverflowRect());
2813 nsBlockFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
+ aDeltaIndent
);
2815 "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2816 "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2817 "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2818 static_cast<void*>(aLine
), aState
.mBCoord
,
2819 aLine
->IsDirty() ? "yes" : "no", aLine
->IStart(), aLine
->BStart(),
2820 aLine
->ISize(), aLine
->BSize(), ovis
.x
, ovis
.y
, ovis
.width
, ovis
.height
,
2821 oscr
.x
, oscr
.y
, oscr
.width
, oscr
.height
, aDeltaBCoord
,
2822 aState
.mPrevBEndMargin
.get(), aLine
->GetChildCount());
2827 static bool LinesAreEmpty(const nsLineList
& aList
) {
2828 for (const auto& line
: aList
) {
2829 if (!line
.IsEmpty()) {
2836 bool nsBlockFrame::ReflowDirtyLines(BlockReflowState
& aState
) {
2837 bool keepGoing
= true;
2838 bool repositionViews
= false; // should we really need this?
2839 bool foundAnyClears
= aState
.mTrailingClearFromPIF
!= StyleClear::None
;
2840 bool willReflowAgain
= false;
2841 bool usedOverflowWrap
= false;
2845 IndentBy(stdout
, gNoiseIndent
);
2847 printf(": reflowing dirty lines");
2848 printf(" computedISize=%d\n", aState
.mReflowInput
.ComputedISize());
2850 AutoNoisyIndenter
indent(gNoisyReflow
);
2853 bool selfDirty
= HasAnyStateBits(NS_FRAME_IS_DIRTY
) ||
2854 (aState
.mReflowInput
.IsBResize() &&
2855 HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE
));
2857 // Reflow our last line if our availableBSize has increased
2858 // so that we (and our last child) pull up content as necessary
2859 if (aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2861 aState
.mReflowInput
.AvailableBSize() >
2862 GetLogicalSize().BSize(aState
.mReflowInput
.GetWritingMode())) {
2863 LineIterator lastLine
= LinesEnd();
2864 if (lastLine
!= LinesBegin()) {
2866 lastLine
->MarkDirty();
2869 // the amount by which we will slide the current line if it is not
2871 nscoord deltaBCoord
= 0;
2873 // whether we did NOT reflow the previous line and thus we need to
2874 // recompute the carried out margin before the line if we want to
2875 // reflow it or if its previous margin is dirty
2876 bool needToRecoverState
= false;
2877 // Float continuations were reflowed in ReflowPushedFloats
2878 bool reflowedFloat
=
2879 mFloats
.NotEmpty() &&
2880 mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
2881 bool lastLineMovedUp
= false;
2882 // We save up information about BR-clearance here
2883 StyleClear inlineFloatClearType
= aState
.mTrailingClearFromPIF
;
2885 LineIterator line
= LinesBegin(), line_end
= LinesEnd();
2887 // Determine if children of this frame could have breaks between them for
2890 // We need to check for paginated layout, the named-page pref, and if the
2891 // available block-size is constrained.
2893 // Note that we need to check for paginated layout as named-pages are only
2894 // used during paginated reflow. We need to additionally check for
2895 // unconstrained block-size to avoid introducing fragmentation breaks during
2896 // "measuring" reflows within an overall paginated reflow, and to avoid
2897 // fragmentation in monolithic containers like 'inline-block'.
2899 // Because we can only break for named pages using Class A breakpoints, we
2900 // also need to check that the block flow direction of the containing frame
2901 // of these items (which is this block) is parallel to that of this page.
2902 // See: https://www.w3.org/TR/css-break-3/#btw-blocks
2903 const nsPresContext
* const presCtx
= aState
.mPresContext
;
2904 const bool canBreakForPageNames
=
2905 aState
.mReflowInput
.mFlags
.mCanHaveClassABreakpoints
&&
2906 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
&&
2907 presCtx
->GetPresShell()->GetRootFrame()->GetWritingMode().IsVertical() ==
2908 GetWritingMode().IsVertical();
2910 // ReflowInput.mFlags.mCanHaveClassABreakpoints should respect the named
2911 // pages pref and presCtx->IsPaginated, so we did not explicitly check these
2912 // above when setting canBreakForPageNames.
2913 if (canBreakForPageNames
) {
2914 MOZ_ASSERT(presCtx
->IsPaginated(),
2915 "canBreakForPageNames should not be set during non-paginated "
2919 // Reflow the lines that are already ours
2920 for (; line
!= line_end
; ++line
, aState
.AdvanceToNextLine()) {
2921 DumpLine(aState
, line
, deltaBCoord
, 0);
2923 AutoNoisyIndenter
indent2(gNoisyReflow
);
2930 // This really sucks, but we have to look inside any blocks that have clear
2931 // elements inside them.
2932 // XXX what can we do smarter here?
2933 if (!line
->IsDirty() && line
->IsBlock() &&
2934 line
->mFirstChild
->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
)) {
2938 nsIFrame
* floatAvoidingBlock
= nullptr;
2939 if (line
->IsBlock() &&
2940 !nsBlockFrame::BlockCanIntersectFloats(line
->mFirstChild
)) {
2941 floatAvoidingBlock
= line
->mFirstChild
;
2944 // We have to reflow the line if it's a block whose clearance
2945 // might have changed, so detect that.
2946 if (!line
->IsDirty() &&
2947 (line
->HasForcedLineBreakBefore() || floatAvoidingBlock
)) {
2948 nscoord curBCoord
= aState
.mBCoord
;
2949 // See where we would be after applying any clearance due to
2951 if (inlineFloatClearType
!= StyleClear::None
) {
2952 std::tie(curBCoord
, std::ignore
) =
2953 aState
.ClearFloats(curBCoord
, inlineFloatClearType
);
2956 auto [newBCoord
, result
] = aState
.ClearFloats(
2957 curBCoord
, line
->FloatClearTypeBefore(), floatAvoidingBlock
);
2959 if (line
->HasClearance()) {
2960 // Reflow the line if it might not have clearance anymore.
2961 if (result
== ClearFloatsResult::BCoordNoChange
2962 // aState.mBCoord is the clearance point which should be the
2963 // block-start border-edge of the block frame. If sliding the
2964 // block by deltaBCoord isn't going to put it in the predicted
2965 // position, then we'd better reflow the line.
2966 || newBCoord
!= line
->BStart() + deltaBCoord
) {
2970 // Reflow the line if the line might have clearance now.
2971 if (result
!= ClearFloatsResult::BCoordNoChange
) {
2977 // We might have to reflow a line that is after a clearing BR.
2978 if (inlineFloatClearType
!= StyleClear::None
) {
2979 std::tie(aState
.mBCoord
, std::ignore
) =
2980 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
2981 if (aState
.mBCoord
!= line
->BStart() + deltaBCoord
) {
2982 // SlideLine is not going to put the line where the clearance
2983 // put it. Reflow the line to be sure.
2986 inlineFloatClearType
= StyleClear::None
;
2989 bool previousMarginWasDirty
= line
->IsPreviousMarginDirty();
2990 if (previousMarginWasDirty
) {
2991 // If the previous margin is dirty, reflow the current line
2993 line
->ClearPreviousMarginDirty();
2994 } else if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
2995 const nscoord scrollableOverflowBEnd
=
2996 LogicalRect(line
->mWritingMode
, line
->ScrollableOverflowRect(),
2997 line
->mContainerSize
)
2998 .BEnd(line
->mWritingMode
);
2999 if (scrollableOverflowBEnd
+ deltaBCoord
> aState
.ContentBEnd()) {
3000 // Lines that aren't dirty but get slid past our available block-size
3001 // constraint must be reflowed.
3006 if (!line
->IsDirty()) {
3007 const bool isPaginated
=
3008 // Last column can be reflowed unconstrained during column balancing.
3009 // Hence the additional NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR bit check
3010 // as a fail-safe fallback.
3011 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
||
3012 HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR
) ||
3013 // Table can also be reflowed unconstrained during printing.
3014 aState
.mPresContext
->IsPaginated();
3016 // We are in a paginated context, i.e. in columns or pages.
3017 const bool mayContainFloats
=
3018 line
->IsBlock() || line
->HasFloats() || line
->HadFloatPushed();
3019 if (mayContainFloats
) {
3020 // The following if-else conditions check whether this line -- which
3021 // might have floats in its subtree, or has floats as direct children,
3022 // or had floats pushed -- needs to be reflowed.
3023 if (deltaBCoord
!= 0 || aState
.mReflowInput
.IsBResize()) {
3024 // The distance to the block-end edge might have changed. Reflow the
3025 // line both because the breakpoints within its floats may have
3026 // changed and because we might have to push/pull the floats in
3029 } else if (HasPushedFloats()) {
3030 // We had pushed floats which haven't been drained by our
3031 // next-in-flow, which means our parent is currently reflowing us
3032 // again due to clearance without creating a next-in-flow for us.
3033 // Reflow the line to redo the floats split logic to correctly set
3034 // our reflow status.
3036 } else if (aState
.mReflowInput
.mFlags
.mMustReflowPlaceholders
) {
3037 // Reflow the line (that may containing a float's placeholder frame)
3038 // if our parent tells us to do so.
3040 } else if (aState
.mReflowInput
.mFlags
.mMovedBlockFragments
) {
3041 // Our parent's line containing us moved to a different fragment.
3042 // Reflow the line because the decision about whether the float fits
3043 // may be different in a different fragment.
3050 if (!line
->IsDirty()) {
3051 // See if there's any reflow damage that requires that we mark the
3053 PropagateFloatDamage(aState
, line
, deltaBCoord
);
3056 // If the container size has changed, reset mContainerSize. If the
3057 // line's writing mode is not ltr, or if the line is not left-aligned, also
3058 // mark the line dirty.
3059 if (aState
.ContainerSize() != line
->mContainerSize
) {
3060 line
->mContainerSize
= aState
.ContainerSize();
3062 const bool isLastLine
= line
== mLines
.back() && !GetNextInFlow();
3063 const auto align
= isLastLine
? StyleText()->TextAlignForLastLine()
3064 : StyleText()->mTextAlign
;
3065 if (line
->mWritingMode
.IsVertical() || line
->mWritingMode
.IsBidiRTL() ||
3066 !IsAlignedLeft(align
, StyleVisibility()->mDirection
,
3067 StyleTextReset()->mUnicodeBidi
, this)) {
3072 // Check for a page break caused by CSS named pages.
3074 // We should break for named pages when two frames meet at a class A
3075 // breakpoint, where the first frame has a different end page value to the
3076 // second frame's start page value. canBreakForPageNames is true iff
3077 // children of this frame can form class A breakpoints, and that we are not
3078 // in a measurement reflow or in a monolithic container such as
3081 // We specifically do not want to cause a page-break for named pages when
3082 // we are at the top of a page. This would otherwise happen when the
3083 // previous sibling is an nsPageBreakFrame, or all previous siblings on the
3084 // current page are zero-height. The latter may not be per-spec, but is
3085 // compatible with Chrome's implementation of named pages.
3086 const nsAtom
* nextPageName
= nullptr;
3087 bool shouldBreakForPageName
= false;
3088 if (canBreakForPageNames
&& (!aState
.mReflowInput
.mFlags
.mIsTopOfPage
||
3089 !aState
.IsAdjacentWithBStart())) {
3090 const nsIFrame
* const frame
= line
->mFirstChild
;
3091 if (!frame
->IsPlaceholderFrame() && !frame
->IsPageBreakFrame()) {
3092 nextPageName
= frame
->GetStartPageValue();
3093 // Walk back to the last frame that isn't a placeholder.
3094 const nsIFrame
* prevFrame
= frame
->GetPrevSibling();
3095 while (prevFrame
&& prevFrame
->IsPlaceholderFrame()) {
3096 prevFrame
= prevFrame
->GetPrevSibling();
3098 if (prevFrame
&& prevFrame
->GetEndPageValue() != nextPageName
) {
3099 shouldBreakForPageName
= true;
3105 if (needToRecoverState
&& line
->IsDirty()) {
3106 // We need to reconstruct the block-end margin only if we didn't
3107 // reflow the previous line and we do need to reflow (or repair
3108 // the block-start position of) the next line.
3109 aState
.ReconstructMarginBefore(line
);
3112 bool reflowedPrevLine
= !needToRecoverState
;
3113 if (needToRecoverState
) {
3114 needToRecoverState
= false;
3116 // Update aState.mPrevChild as if we had reflowed all of the frames in
3118 if (line
->IsDirty()) {
3120 line
->mFirstChild
->GetPrevSibling() == line
.prev()->LastChild(),
3121 "unexpected line frames");
3122 aState
.mPrevChild
= line
->mFirstChild
->GetPrevSibling();
3126 // Now repair the line and update |aState.mBCoord| by calling
3127 // |ReflowLine| or |SlideLine|.
3128 // If we're going to reflow everything again, then no need to reflow
3129 // the dirty line ... unless the line has floats, in which case we'd
3130 // better reflow it now to refresh its float cache, which may contain
3131 // dangling frame pointers! Ugh! This reflow of the line may be
3132 // incorrect because we skipped reflowing previous lines (e.g., floats
3133 // may be placed incorrectly), but that's OK because we'll mark the
3134 // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
3135 if (line
->IsDirty() && (line
->HasFloats() || !willReflowAgain
)) {
3136 lastLineMovedUp
= true;
3138 bool maybeReflowingForFirstTime
=
3139 line
->IStart() == 0 && line
->BStart() == 0 && line
->ISize() == 0 &&
3142 // Compute the dirty lines "before" BEnd, after factoring in
3143 // the running deltaBCoord value - the running value is implicit in
3145 nscoord oldB
= line
->BStart();
3146 nscoord oldBMost
= line
->BEnd();
3148 NS_ASSERTION(!willReflowAgain
|| !line
->IsBlock(),
3149 "Don't reflow blocks while willReflowAgain is true, reflow "
3150 "of block abs-pos children depends on this");
3152 if (shouldBreakForPageName
) {
3153 // Immediately fragment for page-name. It is possible we could break
3154 // out of the loop right here, but this should make it more similar to
3155 // what happens when reflow causes fragmentation.
3156 // Set the page name, so that PushTruncatedLine does not need to
3157 // recalculate the new page name.
3158 PresShell()->FrameConstructor()->SetNextPageContentFramePageName(
3159 nextPageName
? nextPageName
: GetAutoPageValue());
3160 PushTruncatedLine(aState
, line
, &keepGoing
,
3161 ComputeNewPageNameIfNeeded::No
);
3163 // Reflow the dirty line. If it's an incremental reflow, then force
3164 // it to invalidate the dirty area if necessary
3165 usedOverflowWrap
|= ReflowLine(aState
, line
, &keepGoing
);
3168 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3170 willReflowAgain
= true;
3171 // Note that once we've entered this state, every line that gets here
3172 // (e.g. because it has floats) gets marked dirty and reflowed again.
3173 // in the next pass. This is important, see above.
3176 if (line
->HasFloats()) {
3177 reflowedFloat
= true;
3181 DumpLine(aState
, line
, deltaBCoord
, -1);
3182 if (0 == line
->GetChildCount()) {
3183 DeleteLine(aState
, line
, line_end
);
3188 // Test to see whether the margin that should be carried out
3189 // to the next line (NL) might have changed. In ReflowBlockFrame
3190 // we call nextLine->MarkPreviousMarginDirty if the block's
3191 // actual carried-out block-end margin changed. So here we only
3192 // need to worry about the following effects:
3193 // 1) the line was just created, and it might now be blocking
3194 // a carried-out block-end margin from previous lines that
3195 // used to reach NL from reaching NL
3196 // 2) the line used to be empty, and is now not empty,
3197 // thus blocking a carried-out block-end margin from previous lines
3198 // that used to reach NL from reaching NL
3199 // 3) the line wasn't empty, but now is, so a carried-out
3200 // block-end margin from previous lines that didn't used to reach NL
3202 // 4) the line might have changed in a way that affects NL's
3203 // ShouldApplyBStartMargin decision. The three things that matter
3204 // are the line's emptiness, its adjacency to the block-start edge of the
3205 // block, and whether it has clearance (the latter only matters if the
3206 // block was and is adjacent to the block-start and empty).
3208 // If the line is empty now, we can't reliably tell if the line was empty
3209 // before, so we just assume it was and do
3210 // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
3211 // redundant; if the line is empty now we don't need to check 4), but if
3212 // the line is not empty now and we're sure it wasn't empty before, any
3213 // adjacency and clearance changes are irrelevant to the result of
3214 // nextLine->ShouldApplyBStartMargin.
3215 if (line
.next() != LinesEnd()) {
3216 bool maybeWasEmpty
= oldB
== line
.next()->BStart();
3217 bool isEmpty
= line
->CachedIsEmpty();
3218 if (maybeReflowingForFirstTime
/*1*/ ||
3219 (isEmpty
|| maybeWasEmpty
) /*2/3/4*/) {
3220 line
.next()->MarkPreviousMarginDirty();
3221 // since it's marked dirty, nobody will care about |deltaBCoord|
3225 // If the line was just reflowed for the first time, then its
3226 // old mBounds cannot be trusted so this deltaBCoord computation is
3227 // bogus. But that's OK because we just did
3228 // MarkPreviousMarginDirty on the next line which will force it
3229 // to be reflowed, so this computation of deltaBCoord will not be
3231 deltaBCoord
= line
->BEnd() - oldBMost
;
3233 // Now do an interrupt check. We want to do this only in the case when we
3234 // actually reflow the line, so that if we get back in here we'll get
3235 // further on the reflow before interrupting.
3236 aState
.mPresContext
->CheckForInterrupt(this);
3238 aState
.mOverflowTracker
->Skip(line
->mFirstChild
, aState
.mReflowStatus
);
3239 // Nop except for blocks (we don't create overflow container
3240 // continuations for any inlines atm), so only checking mFirstChild
3243 lastLineMovedUp
= deltaBCoord
< 0;
3245 if (deltaBCoord
!= 0) {
3246 SlideLine(aState
, line
, deltaBCoord
);
3248 repositionViews
= true;
3251 NS_ASSERTION(!line
->IsDirty() || !line
->HasFloats(),
3252 "Possibly stale float cache here!");
3253 if (willReflowAgain
&& line
->IsBlock()) {
3254 // If we're going to reflow everything again, and this line is a block,
3255 // then there is no need to recover float state. The line may contain
3256 // other lines with floats, but in that case RecoverStateFrom would only
3257 // add floats to the float manager. We don't need to do that because
3258 // everything's going to get reflowed again "for real". Calling
3259 // RecoverStateFrom in this situation could be lethal because the
3260 // block's descendant lines may have float caches containing dangling
3261 // frame pointers. Ugh!
3262 // If this line is inline, then we need to recover its state now
3263 // to make sure that we don't forget to move its floats by deltaBCoord.
3265 // XXX EVIL O(N^2) EVIL
3266 aState
.RecoverStateFrom(line
, deltaBCoord
);
3269 // Keep mBCoord up to date in case we're propagating reflow damage
3270 // and also because our final height may depend on it. If the
3271 // line is inlines, then only update mBCoord if the line is not
3272 // empty, because that's what PlaceLine does. (Empty blocks may
3273 // want to update mBCoord, e.g. if they have clearance.)
3274 if (line
->IsBlock() || !line
->CachedIsEmpty()) {
3275 aState
.mBCoord
= line
->BEnd();
3278 needToRecoverState
= true;
3280 if (reflowedPrevLine
&& !line
->IsBlock() &&
3281 aState
.mPresContext
->HasPendingInterrupt()) {
3282 // Need to make sure to pull overflows from any prev-in-flows
3283 for (nsIFrame
* inlineKid
= line
->mFirstChild
; inlineKid
;
3284 inlineKid
= inlineKid
->PrincipalChildList().FirstChild()) {
3285 inlineKid
->PullOverflowsFromPrevInFlow();
3290 // Record if we need to clear floats before reflowing the next
3291 // line. Note that inlineFloatClearType will be handled and
3292 // cleared before the next line is processed, so there is no
3293 // need to combine break types here.
3294 if (line
->HasFloatClearTypeAfter()) {
3295 inlineFloatClearType
= line
->FloatClearTypeAfter();
3298 if (LineHasClear(line
.get())) {
3299 foundAnyClears
= true;
3302 DumpLine(aState
, line
, deltaBCoord
, -1);
3304 if (aState
.mPresContext
->HasPendingInterrupt()) {
3305 willReflowAgain
= true;
3306 // Another option here might be to leave |line| clean if
3307 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
3308 // that case the line really did reflow as it should have. Not sure
3309 // whether that would be safe, so doing this for now instead. Also not
3310 // sure whether we really want to mark all lines dirty after an
3311 // interrupt, but until we get better at propagating float damage we
3312 // really do need to do it this way; see comments inside MarkLineDirty.
3313 MarkLineDirtyForInterrupt(line
);
3317 // Handle BR-clearance from the last line of the block
3318 if (inlineFloatClearType
!= StyleClear::None
) {
3319 std::tie(aState
.mBCoord
, std::ignore
) =
3320 aState
.ClearFloats(aState
.mBCoord
, inlineFloatClearType
);
3323 if (needToRecoverState
) {
3324 // Is this expensive?
3325 aState
.ReconstructMarginBefore(line
);
3327 // Update aState.mPrevChild as if we had reflowed all of the frames in
3329 NS_ASSERTION(line
== line_end
|| line
->mFirstChild
->GetPrevSibling() ==
3330 line
.prev()->LastChild(),
3331 "unexpected line frames");
3332 aState
.mPrevChild
= line
== line_end
? mFrames
.LastChild()
3333 : line
->mFirstChild
->GetPrevSibling();
3336 // Should we really have to do this?
3337 if (repositionViews
) {
3338 nsContainerFrame::PlaceFrameView(this);
3341 // We can skip trying to pull up the next line if our height is constrained
3342 // (so we can report being incomplete) and there is no next in flow or we
3343 // were told not to or we know it will be futile, i.e.,
3344 // -- the next in flow is not changing
3345 // -- and we cannot have added more space for its first line to be
3347 // -- it's an incremental reflow of a descendant
3348 // -- and we didn't reflow any floats (so the available space
3350 // -- my chain of next-in-flows either has no first line, or its first
3351 // line isn't dirty.
3352 bool heightConstrained
=
3353 aState
.mReflowInput
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
;
3354 bool skipPull
= willReflowAgain
&& heightConstrained
;
3355 if (!skipPull
&& heightConstrained
&& aState
.mNextInFlow
&&
3356 (aState
.mReflowInput
.mFlags
.mNextInFlowUntouched
&& !lastLineMovedUp
&&
3357 !HasAnyStateBits(NS_FRAME_IS_DIRTY
) && !reflowedFloat
)) {
3358 // We'll place lineIter at the last line of this block, so that
3359 // nsBlockInFlowLineIterator::Next() will take us to the first
3360 // line of my next-in-flow-chain. (But first, check that I
3361 // have any lines -- if I don't, just bail out of this
3363 LineIterator lineIter
= this->LinesEnd();
3364 if (lineIter
!= this->LinesBegin()) {
3365 lineIter
--; // I have lines; step back from dummy iterator to last line.
3366 nsBlockInFlowLineIterator
bifLineIter(this, lineIter
);
3368 // Check for next-in-flow-chain's first line.
3369 // (First, see if there is such a line, and second, see if it's clean)
3370 if (!bifLineIter
.Next() || !bifLineIter
.GetLine()->IsDirty()) {
3376 if (skipPull
&& aState
.mNextInFlow
) {
3377 NS_ASSERTION(heightConstrained
, "Height should be constrained here\n");
3378 if (aState
.mNextInFlow
->IsTrueOverflowContainer()) {
3379 aState
.mReflowStatus
.SetOverflowIncomplete();
3381 aState
.mReflowStatus
.SetIncomplete();
3385 if (!skipPull
&& aState
.mNextInFlow
) {
3386 // Pull data from a next-in-flow if there's still room for more
3388 while (keepGoing
&& aState
.mNextInFlow
) {
3389 // Grab first line from our next-in-flow
3390 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3391 nsLineBox
* pulledLine
;
3392 nsFrameList pulledFrames
;
3393 if (!nextInFlow
->mLines
.empty()) {
3394 RemoveFirstLine(nextInFlow
->mLines
, nextInFlow
->mFrames
, &pulledLine
,
3397 // Grab an overflow line if there are any
3398 FrameLines
* overflowLines
= nextInFlow
->GetOverflowLines();
3399 if (!overflowLines
) {
3400 aState
.mNextInFlow
=
3401 static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3405 RemoveFirstLine(overflowLines
->mLines
, overflowLines
->mFrames
,
3406 &pulledLine
, &pulledFrames
);
3408 nextInFlow
->DestroyOverflowLines();
3412 if (pulledFrames
.IsEmpty()) {
3413 // The line is empty. Try the next one.
3415 pulledLine
->GetChildCount() == 0 && !pulledLine
->mFirstChild
,
3417 nextInFlow
->FreeLineBox(pulledLine
);
3421 if (nextInFlow
->MaybeHasLineCursor()) {
3422 if (pulledLine
== nextInFlow
->GetLineCursorForDisplay()) {
3423 nextInFlow
->ClearLineCursorForDisplay();
3425 if (pulledLine
== nextInFlow
->GetLineCursorForQuery()) {
3426 nextInFlow
->ClearLineCursorForQuery();
3429 ReparentFrames(pulledFrames
, nextInFlow
, this);
3430 pulledLine
->SetMovedFragments();
3432 NS_ASSERTION(pulledFrames
.LastChild() == pulledLine
->LastChild(),
3433 "Unexpected last frame");
3434 NS_ASSERTION(aState
.mPrevChild
|| mLines
.empty(),
3435 "should have a prevchild here");
3436 NS_ASSERTION(aState
.mPrevChild
== mFrames
.LastChild(),
3437 "Incorrect aState.mPrevChild before inserting line at end");
3439 // Shift pulledLine's frames into our mFrames list.
3440 mFrames
.AppendFrames(nullptr, std::move(pulledFrames
));
3442 // Add line to our line list, and set its last child as our new prev-child
3443 line
= mLines
.before_insert(LinesEnd(), pulledLine
);
3444 aState
.mPrevChild
= mFrames
.LastChild();
3446 // Reparent floats whose placeholders are in the line.
3447 ReparentFloats(pulledLine
->mFirstChild
, nextInFlow
, true);
3449 DumpLine(aState
, pulledLine
, deltaBCoord
, 0);
3451 AutoNoisyIndenter
indent2(gNoisyReflow
);
3454 if (aState
.mPresContext
->HasPendingInterrupt()) {
3455 MarkLineDirtyForInterrupt(line
);
3457 // Now reflow it and any lines that it makes during it's reflow
3458 // (we have to loop here because reflowing the line may cause a new
3459 // line to be created; see SplitLine's callers for examples of
3460 // when this happens).
3461 while (line
!= LinesEnd()) {
3462 usedOverflowWrap
|= ReflowLine(aState
, line
, &keepGoing
);
3464 if (aState
.mReflowInput
.WillReflowAgainForClearance()) {
3467 aState
.mReflowStatus
.SetIncomplete();
3471 DumpLine(aState
, line
, deltaBCoord
, -1);
3473 if (0 == line
->GetChildCount()) {
3474 DeleteLine(aState
, line
, line_end
);
3479 if (LineHasClear(line
.get())) {
3480 foundAnyClears
= true;
3483 if (aState
.mPresContext
->CheckForInterrupt(this)) {
3484 MarkLineDirtyForInterrupt(line
);
3488 // If this is an inline frame then its time to stop
3490 aState
.AdvanceToNextLine();
3495 if (aState
.mReflowStatus
.IsIncomplete()) {
3496 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
3497 } // XXXfr shouldn't set this flag when nextinflow has no lines
3500 // Handle an odd-ball case: a list-item with no lines
3501 if (mLines
.empty() && HasOutsideMarker()) {
3502 ReflowOutput
metrics(aState
.mReflowInput
);
3503 nsIFrame
* marker
= GetOutsideMarker();
3504 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3505 ReflowOutsideMarker(
3506 marker
, aState
, metrics
,
3507 aState
.mReflowInput
.ComputedPhysicalBorderPadding().top
);
3508 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
3509 "empty ::marker frame took up space");
3511 if (!MarkerIsEmpty()) {
3512 // There are no lines so we have to fake up some y motion so that
3513 // we end up with *some* height.
3514 // (Note: if we're layout-contained, we have to be sure to leave our
3515 // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
3516 // because layout-contained frames have no baseline.)
3517 if (!aState
.mReflowInput
.mStyleDisplay
->IsContainLayout() &&
3518 metrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
3520 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
3521 if (nsLayoutUtils::GetFirstLineBaseline(wm
, marker
, &ascent
)) {
3522 metrics
.SetBlockStartAscent(ascent
);
3524 metrics
.SetBlockStartAscent(metrics
.BSize(wm
));
3528 RefPtr
<nsFontMetrics
> fm
=
3529 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
3531 nscoord minAscent
= nsLayoutUtils::GetCenteredFontBaseline(
3532 fm
, aState
.mMinLineHeight
, wm
.IsLineInverted());
3533 nscoord minDescent
= aState
.mMinLineHeight
- minAscent
;
3536 std::max(minAscent
, metrics
.BlockStartAscent()) +
3537 std::max(minDescent
, metrics
.BSize(wm
) - metrics
.BlockStartAscent());
3539 nscoord offset
= minAscent
- metrics
.BlockStartAscent();
3541 marker
->SetRect(marker
->GetRect() + nsPoint(0, offset
));
3546 if (LinesAreEmpty(mLines
) && ShouldHaveLineIfEmpty()) {
3547 aState
.mBCoord
+= aState
.mMinLineHeight
;
3550 if (foundAnyClears
) {
3551 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3553 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN
);
3558 VerifyOverflowSituation();
3560 IndentBy(stdout
, gNoiseIndent
- 1);
3562 printf(": done reflowing dirty lines (status=%s)\n",
3563 ToString(aState
.mReflowStatus
).c_str());
3567 return usedOverflowWrap
;
3570 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox
* aLine
) {
3573 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
3574 // marked the lines that need to be marked dirty based on our
3575 // vertical resize stuff. So we'll definitely reflow all those kids;
3576 // the only question is how they should behave.
3577 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
3578 // Mark all our child frames dirty so we make sure to reflow them
3580 int32_t n
= aLine
->GetChildCount();
3581 for (nsIFrame
* f
= aLine
->mFirstChild
; n
> 0;
3582 f
= f
->GetNextSibling(), --n
) {
3583 f
->MarkSubtreeDirty();
3585 // And mark all the floats whose reflows we might be skipping dirty too.
3586 if (aLine
->HasFloats()) {
3587 for (nsIFrame
* f
: aLine
->Floats()) {
3588 f
->MarkSubtreeDirty();
3592 // Dirty all the descendant lines of block kids to handle float damage,
3593 // since our nsFloatManager will go away by the next time we're reflowing.
3594 // XXXbz Can we do something more like what PropagateFloatDamage does?
3595 // Would need to sort out the exact business with mBlockDelta for that....
3596 // This marks way too much dirty. If we ever make this better, revisit
3597 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
3598 nsBlockFrame
* bf
= do_QueryFrame(aLine
->mFirstChild
);
3600 MarkAllDescendantLinesDirty(bf
);
3605 void nsBlockFrame::DeleteLine(BlockReflowState
& aState
,
3606 nsLineList::iterator aLine
,
3607 nsLineList::iterator aLineEnd
) {
3608 MOZ_ASSERT(0 == aLine
->GetChildCount(), "can't delete !empty line");
3609 if (0 == aLine
->GetChildCount()) {
3610 NS_ASSERTION(aState
.mCurrentLine
== aLine
,
3611 "using function more generally than designed, "
3612 "but perhaps OK now");
3613 nsLineBox
* line
= aLine
;
3614 aLine
= mLines
.erase(aLine
);
3616 // Mark the previous margin of the next line dirty since we need to
3617 // recompute its top position.
3618 if (aLine
!= aLineEnd
) {
3619 aLine
->MarkPreviousMarginDirty();
3625 * Reflow a line. The line will either contain a single block frame
3626 * or contain 1 or more inline frames. aKeepReflowGoing indicates
3627 * whether or not the caller should continue to reflow more lines.
3628 * Returns true if the reflow used an overflow-wrap breakpoint.
3630 bool nsBlockFrame::ReflowLine(BlockReflowState
& aState
, LineIterator aLine
,
3631 bool* aKeepReflowGoing
) {
3632 MOZ_ASSERT(aLine
->GetChildCount(), "reflowing empty line");
3634 // Setup the line-layout for the new line
3635 aState
.mCurrentLine
= aLine
;
3636 aLine
->ClearDirty();
3637 aLine
->InvalidateCachedIsEmpty();
3638 aLine
->ClearHadFloatPushed();
3640 // If this line contains a single block that is hidden by `content-visibility`
3641 // don't reflow the line. If this line contains inlines and the first one is
3642 // hidden by `content-visibility`, all of them are, so avoid reflow in that
3644 // For frames that own anonymous children, even the first child is hidden by
3645 // `content-visibility`, there could be some anonymous children need reflow,
3646 // so we don't skip reflow this line.
3647 nsIFrame
* firstChild
= aLine
->mFirstChild
;
3648 if (firstChild
->IsHiddenByContentVisibilityOfInFlowParentForLayout() &&
3649 !HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES
)) {
3653 // Now that we know what kind of line we have, reflow it
3654 bool usedOverflowWrap
= false;
3655 if (aLine
->IsBlock()) {
3656 ReflowBlockFrame(aState
, aLine
, aKeepReflowGoing
);
3658 aLine
->SetLineWrapped(false);
3659 usedOverflowWrap
= ReflowInlineFrames(aState
, aLine
, aKeepReflowGoing
);
3661 // Store the line's float edges for overflow marker analysis if needed.
3662 aLine
->ClearFloatEdges();
3663 if (aState
.mFlags
.mCanHaveOverflowMarkers
) {
3664 WritingMode wm
= aLine
->mWritingMode
;
3665 nsFlowAreaRect r
= aState
.GetFloatAvailableSpaceForBSize(
3666 aLine
->BStart(), aLine
->BSize(), nullptr);
3667 if (r
.HasFloats()) {
3668 LogicalRect so
= aLine
->GetOverflowArea(OverflowType::Scrollable
, wm
,
3669 aLine
->mContainerSize
);
3670 nscoord s
= r
.mRect
.IStart(wm
);
3671 nscoord e
= r
.mRect
.IEnd(wm
);
3672 if (so
.IEnd(wm
) > e
|| so
.IStart(wm
) < s
) {
3673 // This line is overlapping a float - store the edges marking the area
3674 // between the floats for text-overflow analysis.
3675 aLine
->SetFloatEdges(s
, e
);
3681 aLine
->ClearMovedFragments();
3683 return usedOverflowWrap
;
3686 nsIFrame
* nsBlockFrame::PullFrame(BlockReflowState
& aState
,
3687 LineIterator aLine
) {
3688 // First check our remaining lines.
3689 if (LinesEnd() != aLine
.next()) {
3690 return PullFrameFrom(aLine
, this, aLine
.next());
3694 !GetOverflowLines(),
3695 "Our overflow lines should have been removed at the start of reflow");
3697 // Try each next-in-flow.
3698 nsBlockFrame
* nextInFlow
= aState
.mNextInFlow
;
3699 while (nextInFlow
) {
3700 if (nextInFlow
->mLines
.empty()) {
3701 nextInFlow
->DrainSelfOverflowList();
3703 if (!nextInFlow
->mLines
.empty()) {
3704 return PullFrameFrom(aLine
, nextInFlow
, nextInFlow
->mLines
.begin());
3706 nextInFlow
= static_cast<nsBlockFrame
*>(nextInFlow
->GetNextInFlow());
3707 aState
.mNextInFlow
= nextInFlow
;
3713 nsIFrame
* nsBlockFrame::PullFrameFrom(nsLineBox
* aLine
,
3714 nsBlockFrame
* aFromContainer
,
3715 nsLineList::iterator aFromLine
) {
3716 nsLineBox
* fromLine
= aFromLine
;
3717 MOZ_ASSERT(fromLine
, "bad line to pull from");
3718 MOZ_ASSERT(fromLine
->GetChildCount(), "empty line");
3719 MOZ_ASSERT(aLine
->GetChildCount(), "empty line");
3720 MOZ_ASSERT(!HasProperty(LineIteratorProperty()),
3721 "Shouldn't have line iterators mid-reflow");
3723 NS_ASSERTION(fromLine
->IsBlock() == fromLine
->mFirstChild
->IsBlockOutside(),
3724 "Disagreement about whether it's a block or not");
3726 if (fromLine
->IsBlock()) {
3727 // If our line is not empty and the child in aFromLine is a block
3728 // then we cannot pull up the frame into this line. In this case
3732 // Take frame from fromLine
3733 nsIFrame
* frame
= fromLine
->mFirstChild
;
3734 nsIFrame
* newFirstChild
= frame
->GetNextSibling();
3736 if (aFromContainer
!= this) {
3737 // The frame is being pulled from a next-in-flow; therefore we need to add
3738 // it to our sibling list.
3739 MOZ_ASSERT(aLine
== mLines
.back());
3740 MOZ_ASSERT(aFromLine
== aFromContainer
->mLines
.begin(),
3741 "should only pull from first line");
3742 aFromContainer
->mFrames
.RemoveFrame(frame
);
3744 // When pushing and pulling frames we need to check for whether any
3745 // views need to be reparented.
3746 ReparentFrame(frame
, aFromContainer
, this);
3747 mFrames
.AppendFrame(nullptr, frame
);
3749 // The frame might have (or contain) floats that need to be brought
3750 // over too. (pass 'false' since there are no siblings to check)
3751 ReparentFloats(frame
, aFromContainer
, false);
3753 MOZ_ASSERT(aLine
== aFromLine
.prev());
3756 aLine
->NoteFrameAdded(frame
);
3757 fromLine
->NoteFrameRemoved(frame
);
3759 if (fromLine
->GetChildCount() > 0) {
3760 // Mark line dirty now that we pulled a child
3761 fromLine
->MarkDirty();
3762 fromLine
->mFirstChild
= newFirstChild
;
3764 // Free up the fromLine now that it's empty.
3765 // Its bounds might need to be redrawn, though.
3766 if (aFromLine
.next() != aFromContainer
->mLines
.end()) {
3767 aFromLine
.next()->MarkPreviousMarginDirty();
3769 aFromContainer
->mLines
.erase(aFromLine
);
3770 // aFromLine is now invalid
3771 aFromContainer
->FreeLineBox(fromLine
);
3776 VerifyOverflowSituation();
3782 void nsBlockFrame::SlideLine(BlockReflowState
& aState
, nsLineBox
* aLine
,
3783 nscoord aDeltaBCoord
) {
3784 MOZ_ASSERT(aDeltaBCoord
!= 0, "why slide a line nowhere?");
3786 // Adjust line state
3787 aLine
->SlideBy(aDeltaBCoord
, aState
.ContainerSize());
3789 // Adjust the frames in the line
3790 MoveChildFramesOfLine(aLine
, aDeltaBCoord
);
3793 void nsBlockFrame::UpdateLineContainerSize(nsLineBox
* aLine
,
3794 const nsSize
& aNewContainerSize
) {
3795 if (aNewContainerSize
== aLine
->mContainerSize
) {
3799 // Adjust line state
3800 nsSize sizeDelta
= aLine
->UpdateContainerSize(aNewContainerSize
);
3802 // Changing container width only matters if writing mode is vertical-rl
3803 if (GetWritingMode().IsVerticalRL()) {
3804 MoveChildFramesOfLine(aLine
, sizeDelta
.width
);
3808 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox
* aLine
,
3809 nscoord aDeltaBCoord
) {
3810 // Adjust the frames in the line
3811 nsIFrame
* kid
= aLine
->mFirstChild
;
3816 WritingMode wm
= GetWritingMode();
3817 LogicalPoint
translation(wm
, 0, aDeltaBCoord
);
3819 if (aLine
->IsBlock()) {
3821 kid
->MovePositionBy(wm
, translation
);
3824 // Make sure the frame's view and any child views are updated
3825 nsContainerFrame::PlaceFrameView(kid
);
3827 // Adjust the block-dir coordinate of the frames in the line.
3828 // Note: we need to re-position views even if aDeltaBCoord is 0, because
3829 // one of our parent frames may have moved and so the view's position
3830 // relative to its parent may have changed.
3831 int32_t n
= aLine
->GetChildCount();
3834 kid
->MovePositionBy(wm
, translation
);
3836 // Make sure the frame's view and any child views are updated
3837 nsContainerFrame::PlaceFrameView(kid
);
3838 kid
= kid
->GetNextSibling();
3843 static inline bool IsNonAutoNonZeroBSize(const StyleSize
& aCoord
) {
3844 // The "extremum length" values (see ExtremumLength) were originally aimed at
3845 // inline-size (or width, as it was before logicalization). For now, let them
3846 // return false here, so we treat them like 'auto' pending a real
3847 // implementation. (See bug 1126420.)
3849 // FIXME (bug 567039, bug 527285) This isn't correct for the 'fill' value,
3850 // which should more likely (but not necessarily, depending on the available
3851 // space) be returning true.
3852 if (aCoord
.BehavesLikeInitialValueOnBlockAxis()) {
3855 MOZ_ASSERT(aCoord
.IsLengthPercentage());
3856 // If we evaluate the length/percent/calc at a percentage basis of
3857 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3858 // length, percent, or combination thereof. Test > 0 so we clamp
3859 // negative calc() results to 0.
3860 return aCoord
.AsLengthPercentage().Resolve(nscoord_MAX
) > 0 ||
3861 aCoord
.AsLengthPercentage().Resolve(0) > 0;
3865 bool nsBlockFrame::IsSelfEmpty() {
3866 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
3870 // Blocks which are margin-roots (including inline-blocks) cannot be treated
3871 // as empty for margin-collapsing and other purposes. They're more like
3872 // replaced elements.
3873 if (HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
3877 WritingMode wm
= GetWritingMode();
3878 const nsStylePosition
* position
= StylePosition();
3880 if (IsNonAutoNonZeroBSize(position
->MinBSize(wm
)) ||
3881 IsNonAutoNonZeroBSize(position
->BSize(wm
))) {
3885 // FIXME: Bug 1646100 - Take intrinsic size into account.
3886 // FIXME: Handle the case that both inline and block sizes are auto.
3887 // https://github.com/w3c/csswg-drafts/issues/5060.
3888 // Note: block-size could be zero or auto/intrinsic keywords here.
3889 if (position
->BSize(wm
).BehavesLikeInitialValueOnBlockAxis() &&
3890 position
->mAspectRatio
.HasFiniteRatio()) {
3894 const nsStyleBorder
* border
= StyleBorder();
3895 const nsStylePadding
* padding
= StylePadding();
3897 if (border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBStart
)) !=
3899 border
->GetComputedBorderWidth(wm
.PhysicalSide(eLogicalSideBEnd
)) != 0 ||
3900 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBStart(wm
)) ||
3901 !nsLayoutUtils::IsPaddingZero(padding
->mPadding
.GetBEnd(wm
))) {
3905 if (HasOutsideMarker() && !MarkerIsEmpty()) {
3912 bool nsBlockFrame::CachedIsEmpty() {
3913 if (!IsSelfEmpty()) {
3916 for (auto& line
: mLines
) {
3917 if (!line
.CachedIsEmpty()) {
3924 bool nsBlockFrame::IsEmpty() {
3925 if (!IsSelfEmpty()) {
3929 return LinesAreEmpty(mLines
);
3932 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState
& aState
,
3934 if (aLine
->mFirstChild
->IsPageBreakFrame()) {
3935 // A page break frame consumes margins adjacent to it.
3936 // https://drafts.csswg.org/css-break/#break-margins
3940 if (aState
.mFlags
.mShouldApplyBStartMargin
) {
3941 // Apply short-circuit check to avoid searching the line list
3945 if (!aState
.IsAdjacentWithBStart()) {
3946 // If we aren't at the start block-coordinate then something of non-zero
3947 // height must have been placed. Therefore the childs block-start margin
3949 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3953 // Determine if this line is "essentially" the first line
3954 LineIterator line
= LinesBegin();
3955 if (aState
.mFlags
.mHasLineAdjacentToTop
) {
3956 line
= aState
.mLineAdjacentToTop
;
3958 while (line
!= aLine
) {
3959 if (!line
->CachedIsEmpty() || line
->HasClearance()) {
3960 // A line which precedes aLine is non-empty, or has clearance,
3961 // so therefore the block-start margin applies.
3962 aState
.mFlags
.mShouldApplyBStartMargin
= true;
3965 // No need to apply the block-start margin if the line has floats. We
3966 // should collapse anyway (bug 44419)
3968 aState
.mFlags
.mHasLineAdjacentToTop
= true;
3969 aState
.mLineAdjacentToTop
= line
;
3972 // The line being reflowed is "essentially" the first line in the
3973 // block. Therefore its block-start margin will be collapsed by the
3974 // generational collapsing logic with its parent (us).
3978 void nsBlockFrame::ReflowBlockFrame(BlockReflowState
& aState
,
3980 bool* aKeepReflowGoing
) {
3981 MOZ_ASSERT(*aKeepReflowGoing
, "bad caller");
3983 nsIFrame
* frame
= aLine
->mFirstChild
;
3985 NS_ASSERTION(false, "program error - unexpected empty line");
3989 // If the previous frame was a page-break-frame, then preemptively push this
3990 // frame to the next page.
3991 // This is primarily important for the placeholders for abspos frames, which
3992 // measure as zero height and then would be placed on this page.
3993 if (aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
) {
3994 const nsIFrame
* const prev
= frame
->GetPrevSibling();
3995 if (prev
&& prev
->IsPageBreakFrame()) {
3996 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4001 // Prepare the block reflow engine
4002 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
4004 StyleClear clearType
= frame
->StyleDisplay()->mClear
;
4005 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
4006 clearType
= nsLayoutUtils::CombineClearType(clearType
,
4007 aState
.mTrailingClearFromPIF
);
4008 aState
.mTrailingClearFromPIF
= StyleClear::None
;
4011 // Clear past floats before the block if the clear style is not none
4012 aLine
->ClearForcedLineBreak();
4013 if (clearType
!= StyleClear::None
) {
4014 aLine
->SetForcedLineBreakBefore(clearType
);
4017 // See if we should apply the block-start margin. If the block frame being
4018 // reflowed is a continuation, then we don't apply its block-start margin
4019 // because it's not significant. Otherwise, dig deeper.
4020 bool applyBStartMargin
=
4021 !frame
->GetPrevContinuation() && ShouldApplyBStartMargin(aState
, aLine
);
4022 if (applyBStartMargin
) {
4023 // The HasClearance setting is only valid if ShouldApplyBStartMargin
4024 // returned false (in which case the block-start margin-root set our
4025 // clearance flag). Otherwise clear it now. We'll set it later on
4026 // ourselves if necessary.
4027 aLine
->ClearHasClearance();
4029 bool treatWithClearance
= aLine
->HasClearance();
4031 bool mightClearFloats
= clearType
!= StyleClear::None
;
4032 nsIFrame
* floatAvoidingBlock
= nullptr;
4033 if (!nsBlockFrame::BlockCanIntersectFloats(frame
)) {
4034 mightClearFloats
= true;
4035 floatAvoidingBlock
= frame
;
4038 // If our block-start margin was counted as part of some parent's block-start
4039 // margin collapse, and we are being speculatively reflowed assuming this
4040 // frame DID NOT need clearance, then we need to check that
4042 if (!treatWithClearance
&& !applyBStartMargin
&& mightClearFloats
&&
4043 aState
.mReflowInput
.mDiscoveredClearance
) {
4044 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4045 if (auto [clearBCoord
, result
] =
4046 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4047 result
!= ClearFloatsResult::BCoordNoChange
) {
4048 Unused
<< clearBCoord
;
4050 // Only record the first frame that requires clearance
4051 if (!*aState
.mReflowInput
.mDiscoveredClearance
) {
4052 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4054 aState
.mPrevChild
= frame
;
4055 // Exactly what we do now is flexible since we'll definitely be
4060 if (treatWithClearance
) {
4061 applyBStartMargin
= true;
4064 nsIFrame
* clearanceFrame
= nullptr;
4065 const nscoord startingBCoord
= aState
.mBCoord
;
4066 const nsCollapsingMargin incomingMargin
= aState
.mPrevBEndMargin
;
4068 // Save the original position of the frame so that we can reposition
4069 // its view as needed.
4070 nsPoint originalPosition
= frame
->GetPosition();
4073 nscoord bStartMargin
= 0;
4074 bool mayNeedRetry
= false;
4075 bool clearedFloats
= false;
4076 bool clearedPushedOrSplitFloat
= false;
4077 if (applyBStartMargin
) {
4078 // Precompute the blocks block-start margin value so that we can get the
4079 // correct available space (there might be a float that's
4080 // already been placed below the aState.mPrevBEndMargin
4082 // Setup a reflowInput to get the style computed block-start margin
4083 // value. We'll use a reason of `resize' so that we don't fudge
4084 // any incremental reflow input.
4086 // The availSpace here is irrelevant to our needs - all we want
4087 // out if this setup is the block-start margin value which doesn't depend
4088 // on the childs available space.
4089 // XXX building a complete ReflowInput just to get the block-start
4090 // margin seems like a waste. And we do this for almost every block!
4091 WritingMode wm
= frame
->GetWritingMode();
4092 LogicalSize availSpace
= aState
.ContentSize(wm
);
4093 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4096 if (treatWithClearance
) {
4097 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4098 aState
.mPrevBEndMargin
.Zero();
4101 // Now compute the collapsed margin-block-start value into
4102 // aState.mPrevBEndMargin, assuming that all child margins
4103 // collapse down to clearanceFrame.
4104 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4105 clearanceFrame
, &mayNeedRetry
);
4107 // XXX optimization; we could check the collapsing children to see if they
4108 // are sure to require clearance, and so avoid retrying them
4110 if (clearanceFrame
) {
4111 // Don't allow retries on the second pass. The clearance decisions for
4112 // the blocks whose block-start margins collapse with ours are now
4114 mayNeedRetry
= false;
4117 if (!treatWithClearance
&& !clearanceFrame
&& mightClearFloats
) {
4118 // We don't know if we need clearance and this is the first,
4119 // optimistic pass. So determine whether *this block* needs
4120 // clearance. Note that we do not allow the decision for whether
4121 // this block has clearance to change on the second pass; that
4122 // decision is only allowed to be made under the optimistic
4124 nscoord curBCoord
= aState
.mBCoord
+ aState
.mPrevBEndMargin
.get();
4125 if (auto [clearBCoord
, result
] =
4126 aState
.ClearFloats(curBCoord
, clearType
, floatAvoidingBlock
);
4127 result
!= ClearFloatsResult::BCoordNoChange
) {
4128 Unused
<< clearBCoord
;
4130 // Looks like we need clearance and we didn't know about it already.
4131 // So recompute collapsed margin
4132 treatWithClearance
= true;
4133 // Remember this decision, needed for incremental reflow
4134 aLine
->SetHasClearance();
4136 // Apply incoming margins
4137 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4138 aState
.mPrevBEndMargin
.Zero();
4140 // Compute the collapsed margin again, ignoring the incoming margin
4142 mayNeedRetry
= false;
4143 brc
.ComputeCollapsedBStartMargin(reflowInput
, &aState
.mPrevBEndMargin
,
4144 clearanceFrame
, &mayNeedRetry
);
4148 // Temporarily advance the running block-direction value so that the
4149 // GetFloatAvailableSpace method will return the right available space.
4150 // This undone as soon as the horizontal margins are computed.
4151 bStartMargin
= aState
.mPrevBEndMargin
.get();
4153 if (treatWithClearance
) {
4154 nscoord currentBCoord
= aState
.mBCoord
;
4155 // advance mBCoord to the clear position.
4156 auto [clearBCoord
, result
] =
4157 aState
.ClearFloats(aState
.mBCoord
, clearType
, floatAvoidingBlock
);
4158 aState
.mBCoord
= clearBCoord
;
4160 clearedFloats
= result
!= ClearFloatsResult::BCoordNoChange
;
4161 clearedPushedOrSplitFloat
=
4162 result
== ClearFloatsResult::FloatsPushedOrSplit
;
4164 // Compute clearance. It's the amount we need to add to the block-start
4165 // border-edge of the frame, after applying collapsed margins
4166 // from the frame and its children, to get it to line up with
4167 // the block-end of the floats. The former is
4168 // currentBCoord + bStartMargin, the latter is the current
4170 // Note that negative clearance is possible
4171 clearance
= aState
.mBCoord
- (currentBCoord
+ bStartMargin
);
4173 // Add clearance to our block-start margin while we compute available
4174 // space for the frame
4175 bStartMargin
+= clearance
;
4177 // Note that aState.mBCoord should stay where it is: at the block-start
4178 // border-edge of the frame
4180 // Advance aState.mBCoord to the block-start border-edge of the frame.
4181 aState
.mBCoord
+= bStartMargin
;
4185 aLine
->SetLineIsImpactedByFloat(false);
4187 // Here aState.mBCoord is the block-start border-edge of the block.
4188 // Compute the available space for the block
4189 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4190 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
4191 LogicalRect availSpace
= aState
.ComputeBlockAvailSpace(
4192 frame
, floatAvailableSpace
, (floatAvoidingBlock
));
4195 // (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
4196 // is to some degree out of paranoia: if we reliably eat up block-start
4197 // margins at the top of the page as we ought to, it wouldn't be
4199 if ((!aState
.mReflowInput
.mFlags
.mIsTopOfPage
|| clearedFloats
) &&
4200 (availSpace
.BSize(wm
) < 0 || clearedPushedOrSplitFloat
)) {
4201 // We know already that this child block won't fit on this
4202 // page/column due to the block-start margin or the clearance. So we
4203 // need to get out of here now. (If we don't, most blocks will handle
4204 // things fine, and report break-before, but zero-height blocks
4205 // won't, and will thus make their parent overly-large and force
4206 // *it* to be pushed in its entirety.)
4207 aState
.mBCoord
= startingBCoord
;
4208 aState
.mPrevBEndMargin
= incomingMargin
;
4209 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4210 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4212 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4217 // Now put the block-dir coordinate back to the start of the
4218 // block-start-margin + clearance.
4219 aState
.mBCoord
-= bStartMargin
;
4220 availSpace
.BStart(wm
) -= bStartMargin
;
4221 if (NS_UNCONSTRAINEDSIZE
!= availSpace
.BSize(wm
)) {
4222 availSpace
.BSize(wm
) += bStartMargin
;
4225 // Construct the reflow input for the block.
4226 Maybe
<ReflowInput
> childReflowInput
;
4227 Maybe
<LogicalSize
> cbSize
;
4228 LogicalSize availSize
= availSpace
.Size(wm
);
4229 bool columnSetWrapperHasNoBSizeLeft
= false;
4230 if (Style()->GetPseudoType() == PseudoStyleType::columnContent
) {
4231 // Calculate the multicol containing block's block size so that the
4232 // children with percentage block size get correct percentage basis.
4233 const ReflowInput
* cbReflowInput
=
4234 aState
.mReflowInput
.mParentReflowInput
->mCBReflowInput
;
4235 MOZ_ASSERT(cbReflowInput
->mFrame
->StyleColumn()->IsColumnContainerStyle(),
4236 "Get unexpected reflow input of multicol containing block!");
4238 // Use column-width as the containing block's inline-size, i.e. the column
4239 // content's computed inline-size.
4240 cbSize
.emplace(LogicalSize(wm
, aState
.mReflowInput
.ComputedISize(),
4241 cbReflowInput
->ComputedBSize())
4242 .ConvertTo(frame
->GetWritingMode(), wm
));
4244 // If a ColumnSetWrapper is in a balancing column content, it may be
4245 // pushed or pulled back and forth between column contents. Always add
4246 // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
4247 // can have a chance to reflow under current block size constraint.
4248 if (aState
.mReflowInput
.mFlags
.mIsColumnBalancing
&&
4249 frame
->IsColumnSetWrapperFrame()) {
4250 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4252 } else if (IsColumnSetWrapperFrame()) {
4253 // If we are reflowing our ColumnSet children, we want to apply our block
4254 // size constraint to the available block size when constructing reflow
4255 // input for ColumnSet so that ColumnSet can use it to compute its max
4256 // column block size.
4257 if (frame
->IsColumnSetFrame()) {
4258 nscoord contentBSize
= aState
.mReflowInput
.ComputedBSize();
4259 if (aState
.mReflowInput
.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE
) {
4261 std::min(contentBSize
, aState
.mReflowInput
.ComputedMaxBSize());
4263 if (contentBSize
!= NS_UNCONSTRAINEDSIZE
) {
4264 // To get the remaining content block-size, subtract the content
4265 // block-size consumed by our previous continuations.
4266 contentBSize
-= aState
.mConsumedBSize
;
4268 // ColumnSet is not the outermost frame in the column container, so it
4269 // cannot have any margin. We don't need to consider any margin that
4270 // can be generated by "box-decoration-break: clone" as we do in
4271 // BlockReflowState::ComputeBlockAvailSpace().
4272 const nscoord availContentBSize
= std::max(
4273 0, contentBSize
- (aState
.mBCoord
- aState
.ContentBStart()));
4274 if (availSize
.BSize(wm
) >= availContentBSize
) {
4275 availSize
.BSize(wm
) = availContentBSize
;
4276 columnSetWrapperHasNoBSizeLeft
= true;
4282 childReflowInput
.emplace(aState
.mPresContext
, aState
.mReflowInput
, frame
,
4283 availSize
.ConvertTo(frame
->GetWritingMode(), wm
),
4286 childReflowInput
->mFlags
.mColumnSetWrapperHasNoBSizeLeft
=
4287 columnSetWrapperHasNoBSizeLeft
;
4289 if (aLine
->MovedFragments()) {
4290 // We only need to set this the first reflow, since if we reflow
4291 // again (and replace childReflowInput) we'll be reflowing it
4292 // again in the same fragment as the previous time.
4293 childReflowInput
->mFlags
.mMovedBlockFragments
= true;
4296 nsFloatManager::SavedState floatManagerState
;
4297 nsReflowStatus frameReflowStatus
;
4299 if (floatAvailableSpace
.HasFloats()) {
4300 // Set if floatAvailableSpace.HasFloats() is true for any
4301 // iteration of the loop.
4302 aLine
->SetLineIsImpactedByFloat(true);
4305 // We might need to store into mDiscoveredClearance later if it's
4306 // currently null; we want to overwrite any writes that
4307 // brc.ReflowBlock() below does, so we need to remember now
4308 // whether it's empty.
4309 const bool shouldStoreClearance
=
4310 aState
.mReflowInput
.mDiscoveredClearance
&&
4311 !*aState
.mReflowInput
.mDiscoveredClearance
;
4313 // Reflow the block into the available space
4314 if (mayNeedRetry
|| floatAvoidingBlock
) {
4315 aState
.FloatManager()->PushState(&floatManagerState
);
4319 childReflowInput
->mDiscoveredClearance
= &clearanceFrame
;
4320 } else if (!applyBStartMargin
) {
4321 childReflowInput
->mDiscoveredClearance
=
4322 aState
.mReflowInput
.mDiscoveredClearance
;
4325 frameReflowStatus
.Reset();
4326 brc
.ReflowBlock(availSpace
, applyBStartMargin
, aState
.mPrevBEndMargin
,
4327 clearance
, aLine
.get(), *childReflowInput
,
4328 frameReflowStatus
, aState
);
4330 if (frameReflowStatus
.IsInlineBreakBefore()) {
4331 // No need to retry this loop if there is a break opportunity before the
4336 // Now the block has a height. Using that height, get the
4337 // available space again and call ComputeBlockAvailSpace again.
4338 // If ComputeBlockAvailSpace gives a different result, we need to
4340 if (!floatAvoidingBlock
) {
4344 LogicalRect
oldFloatAvailableSpaceRect(floatAvailableSpace
.mRect
);
4345 floatAvailableSpace
= aState
.GetFloatAvailableSpaceForBSize(
4346 aState
.mBCoord
+ bStartMargin
, brc
.GetMetrics().BSize(wm
),
4347 &floatManagerState
);
4348 NS_ASSERTION(floatAvailableSpace
.mRect
.BStart(wm
) ==
4349 oldFloatAvailableSpaceRect
.BStart(wm
),
4351 // Restore the height to the position of the next band.
4352 floatAvailableSpace
.mRect
.BSize(wm
) =
4353 oldFloatAvailableSpaceRect
.BSize(wm
);
4354 // Determine whether the available space shrunk on either side,
4355 // because (the first time round) we now know the block's height,
4356 // and it may intersect additional floats, or (on later
4357 // iterations) because narrowing the width relative to the
4358 // previous time may cause the block to become taller. Note that
4359 // since we're reflowing the block, narrowing the width might also
4360 // make it shorter, so we must pass aCanGrow as true.
4361 if (!AvailableSpaceShrunk(wm
, oldFloatAvailableSpaceRect
,
4362 floatAvailableSpace
.mRect
, true)) {
4363 // The size and position we chose before are fine (i.e., they
4364 // don't cause intersecting with floats that requires a change
4365 // in size or position), so we're done.
4369 bool advanced
= false;
4370 if (!aState
.FloatAvoidingBlockFitsInAvailSpace(floatAvoidingBlock
,
4371 floatAvailableSpace
)) {
4372 // Advance to the next band.
4373 nscoord newBCoord
= aState
.mBCoord
;
4374 if (aState
.AdvanceToNextBand(floatAvailableSpace
.mRect
, &newBCoord
)) {
4377 // ClearFloats might be able to advance us further once we're there.
4378 std::tie(aState
.mBCoord
, std::ignore
) =
4379 aState
.ClearFloats(newBCoord
, StyleClear::None
, floatAvoidingBlock
);
4381 // Start over with a new available space rect at the new height.
4382 floatAvailableSpace
= aState
.GetFloatAvailableSpaceWithState(
4383 aState
.mBCoord
, ShapeType::ShapeOutside
, &floatManagerState
);
4386 const LogicalRect oldAvailSpace
= availSpace
;
4387 availSpace
= aState
.ComputeBlockAvailSpace(frame
, floatAvailableSpace
,
4388 (floatAvoidingBlock
));
4390 if (!advanced
&& availSpace
.IsEqualEdges(oldAvailSpace
)) {
4394 // We need another reflow.
4395 aState
.FloatManager()->PopState(&floatManagerState
);
4397 if (!treatWithClearance
&& !applyBStartMargin
&&
4398 aState
.mReflowInput
.mDiscoveredClearance
) {
4399 // We set shouldStoreClearance above to record only the first
4400 // frame that requires clearance.
4401 if (shouldStoreClearance
) {
4402 *aState
.mReflowInput
.mDiscoveredClearance
= frame
;
4404 aState
.mPrevChild
= frame
;
4405 // Exactly what we do now is flexible since we'll definitely be
4411 // We're pushing down the border-box, so we don't apply margin anymore.
4412 // This should never cause us to move up since the call to
4413 // GetFloatAvailableSpaceForBSize above included the margin.
4414 applyBStartMargin
= false;
4416 treatWithClearance
= true; // avoid hitting test above
4420 childReflowInput
.reset();
4421 childReflowInput
.emplace(
4422 aState
.mPresContext
, aState
.mReflowInput
, frame
,
4423 availSpace
.Size(wm
).ConvertTo(frame
->GetWritingMode(), wm
));
4426 if (mayNeedRetry
&& clearanceFrame
) {
4427 // Found a clearance frame, so we need to reflow |frame| a second time.
4428 // Restore the states and start over again.
4429 aState
.FloatManager()->PopState(&floatManagerState
);
4430 aState
.mBCoord
= startingBCoord
;
4431 aState
.mPrevBEndMargin
= incomingMargin
;
4435 aState
.mPrevChild
= frame
;
4437 if (childReflowInput
->WillReflowAgainForClearance()) {
4438 // If an ancestor of ours is going to reflow for clearance, we
4439 // need to avoid calling PlaceBlock, because it unsets dirty bits
4440 // on the child block (both itself, and through its call to
4441 // nsIFrame::DidReflow), and those dirty bits imply dirtiness for
4442 // all of the child block, including the lines it didn't reflow.
4443 NS_ASSERTION(originalPosition
== frame
->GetPosition(),
4444 "we need to call PositionChildViews");
4448 #if defined(REFLOW_STATUS_COVERAGE)
4449 RecordReflowStatus(true, frameReflowStatus
);
4452 if (frameReflowStatus
.IsInlineBreakBefore()) {
4453 // None of the child block fits.
4454 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4455 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4457 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4460 // Note: line-break-after a block is a nop
4462 // Try to place the child block.
4463 // Don't force the block to fit if we have positive clearance, because
4464 // pushing it to the next page would give it more room.
4465 // Don't force the block to fit if it's impacted by a float. If it is,
4466 // then pushing it to the next page would give it more room. Note that
4467 // isImpacted doesn't include impact from the block's own floats.
4468 bool forceFit
= aState
.IsAdjacentWithBStart() && clearance
<= 0 &&
4469 !floatAvailableSpace
.HasFloats();
4470 nsCollapsingMargin collapsedBEndMargin
;
4471 OverflowAreas overflowAreas
;
4473 brc
.PlaceBlock(*childReflowInput
, forceFit
, aLine
.get(),
4474 collapsedBEndMargin
, overflowAreas
, frameReflowStatus
);
4475 if (!frameReflowStatus
.IsFullyComplete() &&
4476 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4477 *aKeepReflowGoing
= false;
4481 if (aLine
->SetCarriedOutBEndMargin(collapsedBEndMargin
)) {
4482 LineIterator nextLine
= aLine
;
4484 if (nextLine
!= LinesEnd()) {
4485 nextLine
->MarkPreviousMarginDirty();
4489 aLine
->SetOverflowAreas(overflowAreas
);
4490 if (*aKeepReflowGoing
) {
4491 // Some of the child block fit
4493 // Advance to new Y position
4494 nscoord newBCoord
= aLine
->BEnd();
4495 aState
.mBCoord
= newBCoord
;
4497 // Continue the block frame now if it didn't completely fit in
4498 // the available space.
4499 if (!frameReflowStatus
.IsFullyComplete()) {
4500 bool madeContinuation
= CreateContinuationFor(aState
, nullptr, frame
);
4502 nsIFrame
* nextFrame
= frame
->GetNextInFlow();
4503 NS_ASSERTION(nextFrame
,
4504 "We're supposed to have a next-in-flow by now");
4506 if (frameReflowStatus
.IsIncomplete()) {
4507 // If nextFrame used to be an overflow container, make it a normal
4509 if (!madeContinuation
&&
4510 nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4511 nsOverflowContinuationTracker::AutoFinish
fini(
4512 aState
.mOverflowTracker
, frame
);
4513 nsContainerFrame
* parent
= nextFrame
->GetParent();
4514 parent
->StealFrame(nextFrame
);
4515 if (parent
!= this) {
4516 ReparentFrame(nextFrame
, parent
, this);
4518 mFrames
.InsertFrame(nullptr, frame
, nextFrame
);
4519 madeContinuation
= true; // needs to be added to mLines
4520 nextFrame
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
4521 frameReflowStatus
.SetNextInFlowNeedsReflow();
4524 // Push continuation to a new line, but only if we actually made
4526 if (madeContinuation
) {
4527 nsLineBox
* line
= NewLineBox(nextFrame
, true);
4528 mLines
.after_insert(aLine
, line
);
4531 PushTruncatedLine(aState
, aLine
.next(), aKeepReflowGoing
);
4533 // If we need to reflow the continuation of the block child,
4534 // then we'd better reflow our continuation
4535 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
4536 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4537 // We also need to make that continuation's line dirty so
4538 // it gets reflowed when we reflow our next in flow. The
4539 // nif's line must always be either a line of the nif's
4540 // parent block (only if we didn't make a continuation) or
4541 // else one of our own overflow lines. In the latter case
4542 // the line is already marked dirty, so just handle the
4544 if (!madeContinuation
) {
4545 nsBlockFrame
* nifBlock
= do_QueryFrame(nextFrame
->GetParent());
4548 "A block's child's next in flow's parent must be a block!");
4549 for (auto& line
: nifBlock
->Lines()) {
4550 if (line
.Contains(nextFrame
)) {
4558 // The block-end margin for a block is only applied on the last
4559 // flow block. Since we just continued the child block frame,
4560 // we know that line->mFirstChild is not the last flow block
4561 // therefore zero out the running margin value.
4562 #ifdef NOISY_BLOCK_DIR_MARGINS
4564 printf(": reflow incomplete, frame=");
4565 frame
->ListTag(stdout
);
4566 printf(" prevBEndMargin=%d, setting to zero\n",
4567 aState
.mPrevBEndMargin
.get());
4569 aState
.mPrevBEndMargin
.Zero();
4570 } else { // frame is complete but its overflow is not complete
4571 // Disconnect the next-in-flow and put it in our overflow tracker
4572 if (!madeContinuation
&&
4573 !nextFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
4574 // It already exists, but as a normal next-in-flow, so we need
4575 // to dig it out of the child lists.
4576 nextFrame
->GetParent()->StealFrame(nextFrame
);
4577 } else if (madeContinuation
) {
4578 mFrames
.RemoveFrame(nextFrame
);
4581 // Put it in our overflow list
4582 aState
.mOverflowTracker
->Insert(nextFrame
, frameReflowStatus
);
4583 aState
.mReflowStatus
.MergeCompletionStatusFrom(frameReflowStatus
);
4585 #ifdef NOISY_BLOCK_DIR_MARGINS
4587 printf(": reflow complete but overflow incomplete for ");
4588 frame
->ListTag(stdout
);
4589 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4590 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4592 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4594 } else { // frame is fully complete
4595 #ifdef NOISY_BLOCK_DIR_MARGINS
4597 printf(": reflow complete for ");
4598 frame
->ListTag(stdout
);
4599 printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4600 aState
.mPrevBEndMargin
.get(), collapsedBEndMargin
.get());
4602 aState
.mPrevBEndMargin
= collapsedBEndMargin
;
4604 #ifdef NOISY_BLOCK_DIR_MARGINS
4607 frame
->ListTag(stdout
);
4608 printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
4609 brc
.GetCarriedOutBEndMargin().get(), collapsedBEndMargin
.get(),
4610 aState
.mPrevBEndMargin
.get());
4613 if (!frameReflowStatus
.IsFullyComplete()) {
4614 // The frame reported an incomplete status, but then it also didn't
4615 // fit. This means we need to reflow it again so that it can
4616 // (again) report the incomplete status.
4617 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
4620 if ((aLine
== mLines
.front() && !GetPrevInFlow()) ||
4621 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
4622 // If it's our very first line *or* we're not at the top of the page
4623 // and we have page-break-inside:avoid, then we need to be pushed to
4624 // our parent's next-in-flow.
4625 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
4627 // Push the line that didn't fit and any lines that follow it
4628 // to our next-in-flow.
4629 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4633 break; // out of the reflow retry loop
4636 // Now that we've got its final position all figured out, position any child
4637 // views it may have. Note that the case when frame has a view got handled
4638 // by FinishReflowChild, but that function didn't have the coordinates needed
4639 // to correctly decide whether to reposition child views.
4640 if (originalPosition
!= frame
->GetPosition() && !frame
->HasView()) {
4641 nsContainerFrame::PositionChildViews(frame
);
4649 // Returns true if an overflow-wrap break was used.
4650 bool nsBlockFrame::ReflowInlineFrames(BlockReflowState
& aState
,
4652 bool* aKeepReflowGoing
) {
4653 *aKeepReflowGoing
= true;
4654 bool usedOverflowWrap
= false;
4656 aLine
->SetLineIsImpactedByFloat(false);
4658 // Setup initial coordinate system for reflowing the inline frames
4659 // into. Apply a previous block frame's block-end margin first.
4660 if (ShouldApplyBStartMargin(aState
, aLine
)) {
4661 aState
.mBCoord
+= aState
.mPrevBEndMargin
.get();
4663 nsFlowAreaRect floatAvailableSpace
= aState
.GetFloatAvailableSpace();
4665 LineReflowStatus lineReflowStatus
;
4667 nscoord availableSpaceBSize
= 0;
4668 aState
.mLineBSize
.reset();
4670 bool allowPullUp
= true;
4671 nsIFrame
* forceBreakInFrame
= nullptr;
4672 int32_t forceBreakOffset
= -1;
4673 gfxBreakPriority forceBreakPriority
= gfxBreakPriority::eNoBreak
;
4675 nsFloatManager::SavedState floatManagerState
;
4676 aState
.FloatManager()->PushState(&floatManagerState
);
4678 // Once upon a time we allocated the first 30 nsLineLayout objects
4679 // on the stack, and then we switched to the heap. At that time
4680 // these objects were large (1100 bytes on a 32 bit system).
4681 // Then the nsLineLayout object was shrunk to 156 bytes by
4682 // removing some internal buffers. Given that it is so much
4683 // smaller, the complexity of 2 different ways of allocating
4684 // no longer makes sense. Now we always allocate on the stack.
4685 nsLineLayout
lineLayout(aState
.mPresContext
, aState
.FloatManager(),
4686 aState
.mReflowInput
, &aLine
, nullptr);
4687 lineLayout
.Init(&aState
, aState
.mMinLineHeight
, aState
.mLineNumber
);
4688 if (forceBreakInFrame
) {
4689 lineLayout
.ForceBreakAtPosition(forceBreakInFrame
, forceBreakOffset
);
4691 DoReflowInlineFrames(aState
, lineLayout
, aLine
, floatAvailableSpace
,
4692 availableSpaceBSize
, &floatManagerState
,
4693 aKeepReflowGoing
, &lineReflowStatus
, allowPullUp
);
4694 usedOverflowWrap
= lineLayout
.EndLineReflow();
4696 if (LineReflowStatus::RedoNoPull
== lineReflowStatus
||
4697 LineReflowStatus::RedoMoreFloats
== lineReflowStatus
||
4698 LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4699 if (lineLayout
.NeedsBackup()) {
4700 NS_ASSERTION(!forceBreakInFrame
,
4701 "Backing up twice; this should never be necessary");
4702 // If there is no saved break position, then this will set
4703 // set forceBreakInFrame to null and we won't back up, which is
4705 forceBreakInFrame
= lineLayout
.GetLastOptionalBreakPosition(
4706 &forceBreakOffset
, &forceBreakPriority
);
4708 forceBreakInFrame
= nullptr;
4710 // restore the float manager state
4711 aState
.FloatManager()->PopState(&floatManagerState
);
4712 // Clear out float lists
4713 aState
.mCurrentLineFloats
.Clear();
4714 aState
.mBelowCurrentLineFloats
.Clear();
4715 aState
.mNoWrapFloats
.Clear();
4718 // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4719 allowPullUp
= false;
4720 } while (LineReflowStatus::RedoNoPull
== lineReflowStatus
);
4721 } while (LineReflowStatus::RedoMoreFloats
== lineReflowStatus
);
4722 } while (LineReflowStatus::RedoNextBand
== lineReflowStatus
);
4724 return usedOverflowWrap
;
4727 void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState
& aState
,
4729 bool* aKeepReflowGoing
) {
4730 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
4731 // Reflow the line again when we reflow at our new position.
4733 *aKeepReflowGoing
= false;
4736 void nsBlockFrame::PushTruncatedLine(
4737 BlockReflowState
& aState
, LineIterator aLine
, bool* aKeepReflowGoing
,
4738 ComputeNewPageNameIfNeeded aComputeNewPageName
) {
4739 PushLines(aState
, aLine
.prev());
4740 *aKeepReflowGoing
= false;
4742 if (aComputeNewPageName
== ComputeNewPageNameIfNeeded::Yes
) {
4743 // mCanHaveClassABreakpoints can only be true during paginated reflow, and
4744 // we expect this function to only be called when the available bsize is
4746 const WritingMode wm
= GetWritingMode();
4747 const bool canBreakForPageNames
=
4748 aState
.mReflowInput
.mFlags
.mCanHaveClassABreakpoints
&&
4749 !PresShell()->GetRootFrame()->GetWritingMode().IsOrthogonalTo(wm
);
4750 if (canBreakForPageNames
) {
4751 PresShell()->FrameConstructor()->MaybeSetNextPageContentFramePageName(
4752 aLine
->mFirstChild
);
4755 aState
.mReflowStatus
.SetIncomplete();
4758 void nsBlockFrame::DoReflowInlineFrames(
4759 BlockReflowState
& aState
, nsLineLayout
& aLineLayout
, LineIterator aLine
,
4760 nsFlowAreaRect
& aFloatAvailableSpace
, nscoord
& aAvailableSpaceBSize
,
4761 nsFloatManager::SavedState
* aFloatStateBeforeLine
, bool* aKeepReflowGoing
,
4762 LineReflowStatus
* aLineReflowStatus
, bool aAllowPullUp
) {
4763 // Forget all of the floats on the line
4764 aLine
->ClearFloats();
4765 aState
.mFloatOverflowAreas
.Clear();
4767 // We need to set this flag on the line if any of our reflow passes
4768 // are impacted by floats.
4769 if (aFloatAvailableSpace
.HasFloats()) {
4770 aLine
->SetLineIsImpactedByFloat(true);
4772 #ifdef REALLY_NOISY_REFLOW
4773 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4774 aFloatAvailableSpace
.HasFloats());
4777 WritingMode outerWM
= aState
.mReflowInput
.GetWritingMode();
4778 WritingMode lineWM
= WritingModeForLine(outerWM
, aLine
->mFirstChild
);
4779 LogicalRect lineRect
= aFloatAvailableSpace
.mRect
.ConvertTo(
4780 lineWM
, outerWM
, aState
.ContainerSize());
4782 nscoord iStart
= lineRect
.IStart(lineWM
);
4783 nscoord availISize
= lineRect
.ISize(lineWM
);
4785 if (aState
.mReflowInput
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
4786 availBSize
= NS_UNCONSTRAINEDSIZE
;
4788 /* XXX get the height right! */
4789 availBSize
= lineRect
.BSize(lineWM
);
4792 // Make sure to enable resize optimization before we call BeginLineReflow
4793 // because it might get disabled there
4794 aLine
->EnableResizeReflowOptimization();
4796 aLineLayout
.BeginLineReflow(
4797 iStart
, aState
.mBCoord
, availISize
, availBSize
,
4798 aFloatAvailableSpace
.HasFloats(), false, /*XXX isTopOfPage*/
4799 lineWM
, aState
.mContainerSize
, aState
.mInsetForBalance
);
4801 aState
.mFlags
.mIsLineLayoutEmpty
= false;
4803 // XXX Unfortunately we need to know this before reflowing the first
4804 // inline frame in the line. FIX ME.
4805 if (0 == aLineLayout
.GetLineNumber() &&
4806 HasAllStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
4807 NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
4808 aLineLayout
.SetFirstLetterStyleOK(true);
4810 NS_ASSERTION(!(HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD
) &&
4811 GetPrevContinuation()),
4812 "first letter child bit should only be on first continuation");
4814 // Reflow the frames that are already on the line first
4815 LineReflowStatus lineReflowStatus
= LineReflowStatus::OK
;
4817 nsIFrame
* frame
= aLine
->mFirstChild
;
4819 if (aFloatAvailableSpace
.HasFloats()) {
4820 // There is a soft break opportunity at the start of the line, because
4821 // we can always move this line down below float(s).
4822 if (aLineLayout
.NotifyOptionalBreakPosition(
4823 frame
, 0, true, gfxBreakPriority::eNormalBreak
)) {
4824 lineReflowStatus
= LineReflowStatus::RedoNextBand
;
4828 // need to repeatedly call GetChildCount here, because the child
4829 // count can change during the loop!
4831 LineReflowStatus::OK
== lineReflowStatus
&& i
< aLine
->GetChildCount();
4832 i
++, frame
= frame
->GetNextSibling()) {
4833 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4834 if (LineReflowStatus::OK
!= lineReflowStatus
) {
4835 // It is possible that one or more of next lines are empty
4836 // (because of DeleteNextInFlowChild). If so, delete them now
4837 // in case we are finished.
4839 while ((aLine
!= LinesEnd()) && (0 == aLine
->GetChildCount())) {
4840 // XXX Is this still necessary now that DeleteNextInFlowChild
4841 // uses DoRemoveFrame?
4842 nsLineBox
* toremove
= aLine
;
4843 aLine
= mLines
.erase(aLine
);
4844 NS_ASSERTION(nullptr == toremove
->mFirstChild
, "bad empty line");
4845 FreeLineBox(toremove
);
4849 NS_ASSERTION(lineReflowStatus
!= LineReflowStatus::Truncated
,
4850 "ReflowInlineFrame should never determine that a line "
4851 "needs to go to the next page/column");
4855 // Don't pull up new frames into lines with continuation placeholders
4857 // Pull frames and reflow them until we can't
4858 while (LineReflowStatus::OK
== lineReflowStatus
) {
4859 frame
= PullFrame(aState
, aLine
);
4864 while (LineReflowStatus::OK
== lineReflowStatus
) {
4865 int32_t oldCount
= aLine
->GetChildCount();
4866 ReflowInlineFrame(aState
, aLineLayout
, aLine
, frame
, &lineReflowStatus
);
4867 if (aLine
->GetChildCount() != oldCount
) {
4868 // We just created a continuation for aFrame AND its going
4869 // to end up on this line (e.g. :first-letter
4870 // situation). Therefore we have to loop here before trying
4871 // to pull another frame.
4872 frame
= frame
->GetNextSibling();
4880 aState
.mFlags
.mIsLineLayoutEmpty
= aLineLayout
.LineIsEmpty();
4882 // We only need to backup if the line isn't going to be reflowed again anyway
4883 bool needsBackup
= aLineLayout
.NeedsBackup() &&
4884 (lineReflowStatus
== LineReflowStatus::Stop
||
4885 lineReflowStatus
== LineReflowStatus::OK
);
4886 if (needsBackup
&& aLineLayout
.HaveForcedBreakPosition()) {
4888 "We shouldn't be backing up more than once! "
4889 "Someone must have set a break opportunity beyond the available width, "
4890 "even though there were better break opportunities before it");
4891 needsBackup
= false;
4894 // We need to try backing up to before a text run
4895 // XXX It's possible, in fact not unusual, for the break opportunity to
4896 // already be the end of the line. We should detect that and optimize to not
4898 if (aLineLayout
.HasOptionalBreakPosition()) {
4900 lineReflowStatus
= LineReflowStatus::RedoNoPull
;
4903 // In case we reflow this line again, remember that we don't
4904 // need to force any breaking
4905 aLineLayout
.ClearOptionalBreakPosition();
4908 if (LineReflowStatus::RedoNextBand
== lineReflowStatus
) {
4909 // This happens only when we have a line that is impacted by
4910 // floats and the first element in the line doesn't fit with
4913 // If there's block space available, we either try to reflow the line
4914 // past the current band (if it's non-zero and the band definitely won't
4915 // widen around a shape-outside), otherwise we try one pixel down. If
4916 // there's no block space available, we push the line to the next
4919 NS_UNCONSTRAINEDSIZE
!= aFloatAvailableSpace
.mRect
.BSize(outerWM
),
4920 "unconstrained block size on totally empty line");
4922 // See the analogous code for blocks in BlockReflowState::ClearFloats.
4923 nscoord bandBSize
= aFloatAvailableSpace
.mRect
.BSize(outerWM
);
4924 if (bandBSize
> 0 ||
4925 NS_UNCONSTRAINEDSIZE
== aState
.mReflowInput
.AvailableBSize()) {
4926 NS_ASSERTION(bandBSize
== 0 || aFloatAvailableSpace
.HasFloats(),
4927 "redo line on totally empty line with non-empty band...");
4928 // We should never hit this case if we've placed floats on the
4929 // line; if we have, then the GetFloatAvailableSpace call is wrong
4930 // and needs to happen after the caller pops the float manager
4932 aState
.FloatManager()->AssertStateMatches(aFloatStateBeforeLine
);
4934 if (!aFloatAvailableSpace
.MayWiden() && bandBSize
> 0) {
4935 // Move it down far enough to clear the current band.
4936 aState
.mBCoord
+= bandBSize
;
4938 // Move it down by one dev pixel.
4939 aState
.mBCoord
+= aState
.mPresContext
->DevPixelsToAppUnits(1);
4942 aFloatAvailableSpace
= aState
.GetFloatAvailableSpace();
4944 // There's nowhere to retry placing the line, so we want to push
4945 // it to the next page/column where its contents can fit not
4947 lineReflowStatus
= LineReflowStatus::Truncated
;
4948 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
4951 // XXX: a small optimization can be done here when paginating:
4952 // if the new Y coordinate is past the end of the block then
4953 // push the line and return now instead of later on after we are
4955 } else if (LineReflowStatus::Truncated
!= lineReflowStatus
&&
4956 LineReflowStatus::RedoNoPull
!= lineReflowStatus
) {
4957 // If we are propagating out a break-before status then there is
4958 // no point in placing the line.
4959 if (!aState
.mReflowStatus
.IsInlineBreakBefore()) {
4960 if (!PlaceLine(aState
, aLineLayout
, aLine
, aFloatStateBeforeLine
,
4961 aFloatAvailableSpace
, aAvailableSpaceBSize
,
4962 aKeepReflowGoing
)) {
4963 lineReflowStatus
= LineReflowStatus::RedoMoreFloats
;
4964 // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4971 printf("Line reflow status = %s\n",
4972 LineReflowStatusToString(lineReflowStatus
));
4976 if (aLineLayout
.GetDirtyNextLine()) {
4977 // aLine may have been pushed to the overflow lines.
4978 FrameLines
* overflowLines
= GetOverflowLines();
4979 // We can't just compare iterators front() to aLine here, since they may be
4980 // in different lists.
4981 bool pushedToOverflowLines
=
4982 overflowLines
&& overflowLines
->mLines
.front() == aLine
.get();
4983 if (pushedToOverflowLines
) {
4984 // aLine is stale, it's associated with the main line list but it should
4985 // be associated with the overflow line list now
4986 aLine
= overflowLines
->mLines
.begin();
4988 nsBlockInFlowLineIterator
iter(this, aLine
, pushedToOverflowLines
);
4989 if (iter
.Next() && iter
.GetLine()->IsInline()) {
4990 iter
.GetLine()->MarkDirty();
4991 if (iter
.GetContainer() != this) {
4992 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
4997 *aLineReflowStatus
= lineReflowStatus
;
5001 * Reflow an inline frame. The reflow status is mapped from the frames
5002 * reflow status to the lines reflow status (not to our reflow status).
5003 * The line reflow status is simple: true means keep placing frames
5004 * on the line; false means don't (the line is done). If the line
5005 * has some sort of breaking affect then aLine's break-type will be set
5006 * to something other than StyleClear::None.
5008 void nsBlockFrame::ReflowInlineFrame(BlockReflowState
& aState
,
5009 nsLineLayout
& aLineLayout
,
5010 LineIterator aLine
, nsIFrame
* aFrame
,
5011 LineReflowStatus
* aLineReflowStatus
) {
5013 *aLineReflowStatus
= LineReflowStatus::OK
;
5015 #ifdef NOISY_FIRST_LETTER
5017 printf(": reflowing ");
5018 aFrame
->ListTag(stdout
);
5019 printf(" reflowingFirstLetter=%s\n",
5020 aLineLayout
.GetFirstLetterStyleOK() ? "on" : "off");
5023 if (aFrame
->IsPlaceholderFrame()) {
5024 auto ph
= static_cast<nsPlaceholderFrame
*>(aFrame
);
5025 ph
->ForgetLineIsEmptySoFar();
5028 // Reflow the inline frame
5029 nsReflowStatus frameReflowStatus
;
5031 aLineLayout
.ReflowFrame(aFrame
, frameReflowStatus
, nullptr, pushedFrame
);
5033 if (frameReflowStatus
.NextInFlowNeedsReflow()) {
5034 aLineLayout
.SetDirtyNextLine();
5037 #ifdef REALLY_NOISY_REFLOW
5038 aFrame
->ListTag(stdout
);
5039 printf(": status=%s\n", ToString(frameReflowStatus
).c_str());
5042 #if defined(REFLOW_STATUS_COVERAGE)
5043 RecordReflowStatus(false, frameReflowStatus
);
5046 // Send post-reflow notification
5047 aState
.mPrevChild
= aFrame
;
5050 This is where we need to add logic to handle some odd behavior.
5051 For one thing, we should usually place at least one thing next
5052 to a left float, even when that float takes up all the width on a line.
5056 // Process the child frames reflow status. There are 5 cases:
5057 // complete, not-complete, break-before, break-after-complete,
5058 // break-after-not-complete. There are two situations: we are a
5059 // block or we are an inline. This makes a total of 10 cases
5060 // (fortunately, there is some overlap).
5061 aLine
->ClearForcedLineBreak();
5062 if (frameReflowStatus
.IsInlineBreak() ||
5063 aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5064 // Always abort the line reflow (because a line break is the
5065 // minimal amount of break we do).
5066 *aLineReflowStatus
= LineReflowStatus::Stop
;
5068 // XXX what should aLine's break-type be set to in all these cases?
5069 if (frameReflowStatus
.IsInlineBreakBefore()) {
5070 // Break-before cases.
5071 if (aFrame
== aLine
->mFirstChild
) {
5072 // If we break before the first frame on the line then we must
5073 // be trying to place content where there's no room (e.g. on a
5074 // line with wide floats). Inform the caller to reflow the
5075 // line after skipping past a float.
5076 *aLineReflowStatus
= LineReflowStatus::RedoNextBand
;
5078 // It's not the first child on this line so go ahead and split
5079 // the line. We will see the frame again on the next-line.
5080 SplitLine(aState
, aLineLayout
, aLine
, aFrame
, aLineReflowStatus
);
5082 // If we're splitting the line because the frame didn't fit and it
5083 // was pushed, then mark the line as having word wrapped. We need to
5084 // know that if we're shrink wrapping our width
5086 aLine
->SetLineWrapped(true);
5090 MOZ_ASSERT(frameReflowStatus
.IsInlineBreakAfter() ||
5091 aState
.mTrailingClearFromPIF
!= StyleClear::None
,
5092 "We should've handled inline break-before in the if-branch!");
5094 // If a float split and its prev-in-flow was followed by a <BR>, then
5095 // combine the <BR>'s float clear type with the inline's float clear type
5096 // (the inline will be the very next frame after the split float).
5097 StyleClear clearType
= frameReflowStatus
.FloatClearType();
5098 if (aState
.mTrailingClearFromPIF
!= StyleClear::None
) {
5099 clearType
= nsLayoutUtils::CombineClearType(
5100 clearType
, aState
.mTrailingClearFromPIF
);
5101 aState
.mTrailingClearFromPIF
= StyleClear::None
;
5103 // Break-after cases
5104 if (clearType
!= StyleClear::None
|| aLineLayout
.GetLineEndsInBR()) {
5105 aLine
->SetForcedLineBreakAfter(clearType
);
5107 if (frameReflowStatus
.IsComplete()) {
5108 // Split line, but after the frame just reflowed
5109 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5112 if (frameReflowStatus
.IsInlineBreakAfter() &&
5113 !aLineLayout
.GetLineEndsInBR()) {
5114 aLineLayout
.SetDirtyNextLine();
5120 if (!frameReflowStatus
.IsFullyComplete()) {
5121 // Create a continuation for the incomplete frame. Note that the
5122 // frame may already have a continuation.
5123 CreateContinuationFor(aState
, aLine
, aFrame
);
5125 // Remember that the line has wrapped
5126 if (!aLineLayout
.GetLineEndsInBR()) {
5127 aLine
->SetLineWrapped(true);
5130 // If we just ended a first-letter frame or reflowed a placeholder then
5131 // don't split the line and don't stop the line reflow...
5132 // But if we are going to stop anyways we'd better split the line.
5133 if ((!frameReflowStatus
.FirstLetterComplete() &&
5134 !aFrame
->IsPlaceholderFrame()) ||
5135 *aLineReflowStatus
== LineReflowStatus::Stop
) {
5136 // Split line after the current frame
5137 *aLineReflowStatus
= LineReflowStatus::Stop
;
5138 SplitLine(aState
, aLineLayout
, aLine
, aFrame
->GetNextSibling(),
5144 bool nsBlockFrame::CreateContinuationFor(BlockReflowState
& aState
,
5145 nsLineBox
* aLine
, nsIFrame
* aFrame
) {
5146 nsIFrame
* newFrame
= nullptr;
5148 if (!aFrame
->GetNextInFlow()) {
5150 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame
, this);
5152 mFrames
.InsertFrame(nullptr, aFrame
, newFrame
);
5155 aLine
->NoteFrameAdded(newFrame
);
5164 void nsBlockFrame::SplitFloat(BlockReflowState
& aState
, nsIFrame
* aFloat
,
5165 const nsReflowStatus
& aFloatStatus
) {
5166 MOZ_ASSERT(!aFloatStatus
.IsFullyComplete(),
5167 "why split the frame if it's fully complete?");
5168 MOZ_ASSERT(aState
.mBlock
== this);
5170 nsIFrame
* nextInFlow
= aFloat
->GetNextInFlow();
5172 nsContainerFrame
* oldParent
= nextInFlow
->GetParent();
5173 oldParent
->StealFrame(nextInFlow
);
5174 if (oldParent
!= this) {
5175 ReparentFrame(nextInFlow
, oldParent
, this);
5177 if (!aFloatStatus
.IsOverflowIncomplete()) {
5178 nextInFlow
->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5182 PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat
, this);
5184 if (aFloatStatus
.IsOverflowIncomplete()) {
5185 nextInFlow
->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
5188 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
5189 if (floatStyle
== StyleFloat::Left
) {
5190 aState
.FloatManager()->SetSplitLeftFloatAcrossBreak();
5192 MOZ_ASSERT(floatStyle
== StyleFloat::Right
, "Unexpected float side!");
5193 aState
.FloatManager()->SetSplitRightFloatAcrossBreak();
5196 aState
.AppendPushedFloatChain(nextInFlow
);
5197 if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) ||
5198 MOZ_UNLIKELY(IsTrueOverflowContainer())) {
5199 aState
.mReflowStatus
.SetOverflowIncomplete();
5201 aState
.mReflowStatus
.SetIncomplete();
5205 static bool CheckPlaceholderInLine(nsIFrame
* aBlock
, nsLineBox
* aLine
,
5210 NS_ASSERTION(!aFloat
->GetPrevContinuation(),
5211 "float in a line should never be a continuation");
5212 NS_ASSERTION(!aFloat
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5213 "float in a line should never be a pushed float");
5214 nsIFrame
* ph
= aFloat
->FirstInFlow()->GetPlaceholderFrame();
5215 for (nsIFrame
* f
= ph
; f
; f
= f
->GetParent()) {
5216 if (f
->GetParent() == aBlock
) {
5217 return aLine
->Contains(f
);
5220 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
5224 void nsBlockFrame::SplitLine(BlockReflowState
& aState
,
5225 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5227 LineReflowStatus
* aLineReflowStatus
) {
5228 MOZ_ASSERT(aLine
->IsInline(), "illegal SplitLine on block line");
5231 aLine
->GetChildCount() - aLineLayout
.GetCurrentSpanCount();
5232 MOZ_ASSERT(pushCount
>= 0, "bad push count");
5236 nsIFrame::IndentBy(stdout
, gNoiseIndent
);
5237 printf("split line: from line=%p pushCount=%d aFrame=",
5238 static_cast<void*>(aLine
.get()), pushCount
);
5240 aFrame
->ListTag(stdout
);
5245 if (gReallyNoisyReflow
) {
5246 aLine
->List(stdout
, gNoiseIndent
+ 1);
5251 if (0 != pushCount
) {
5252 MOZ_ASSERT(aLine
->GetChildCount() > pushCount
, "bad push");
5253 MOZ_ASSERT(nullptr != aFrame
, "whoops");
5256 nsIFrame
* f
= aFrame
;
5257 int32_t count
= pushCount
;
5258 while (f
&& count
> 0) {
5259 f
= f
->GetNextSibling();
5262 NS_ASSERTION(count
== 0, "Not enough frames to push");
5266 // Put frames being split out into their own line
5267 nsLineBox
* newLine
= NewLineBox(aLine
, aFrame
, pushCount
);
5268 mLines
.after_insert(aLine
, newLine
);
5270 if (gReallyNoisyReflow
) {
5271 newLine
->List(stdout
, gNoiseIndent
+ 1);
5275 // Let line layout know that some frames are no longer part of its
5277 aLineLayout
.SplitLineTo(aLine
->GetChildCount());
5279 // If floats have been placed whose placeholders have been pushed to the new
5280 // line, we need to reflow the old line again. We don't want to look at the
5281 // frames in the new line, because as a large paragraph is laid out the
5282 // we'd get O(N^2) performance. So instead we just check that the last
5283 // float and the last below-current-line float are still in aLine.
5284 if (!CheckPlaceholderInLine(
5286 aLine
->HasFloats() ? aLine
->Floats().LastElement() : nullptr) ||
5287 !CheckPlaceholderInLine(
5289 aState
.mBelowCurrentLineFloats
.SafeLastElement(nullptr))) {
5290 *aLineReflowStatus
= LineReflowStatus::RedoNoPull
;
5299 bool nsBlockFrame::IsLastLine(BlockReflowState
& aState
, LineIterator aLine
) {
5300 while (++aLine
!= LinesEnd()) {
5301 // There is another line
5302 if (0 != aLine
->GetChildCount()) {
5303 // If the next line is a block line then this line is the last in a
5304 // group of inline lines.
5305 return aLine
->IsBlock();
5307 // The next line is empty, try the next one
5310 // Try our next-in-flows lines to answer the question
5311 nsBlockFrame
* nextInFlow
= (nsBlockFrame
*)GetNextInFlow();
5312 while (nullptr != nextInFlow
) {
5313 for (const auto& line
: nextInFlow
->Lines()) {
5314 if (0 != line
.GetChildCount()) {
5315 return line
.IsBlock();
5318 nextInFlow
= (nsBlockFrame
*)nextInFlow
->GetNextInFlow();
5321 // This is the last line - so don't allow justification
5325 bool nsBlockFrame::PlaceLine(BlockReflowState
& aState
,
5326 nsLineLayout
& aLineLayout
, LineIterator aLine
,
5327 nsFloatManager::SavedState
* aFloatStateBeforeLine
,
5328 nsFlowAreaRect
& aFlowArea
,
5329 nscoord
& aAvailableSpaceBSize
,
5330 bool* aKeepReflowGoing
) {
5331 // Try to position the floats in a nowrap context.
5332 aLineLayout
.FlushNoWrapFloats();
5334 // Trim extra white-space from the line before placing the frames
5335 aLineLayout
.TrimTrailingWhiteSpace();
5337 // Vertically align the frames on this line.
5339 // According to the CSS2 spec, section 12.6.1, the "marker" box
5340 // participates in the height calculation of the list-item box's
5343 // There are exactly two places a ::marker can be placed: near the
5344 // first or second line. It's only placed on the second line in a
5345 // rare case: when the first line is empty.
5346 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
5347 bool addedMarker
= false;
5348 if (HasOutsideMarker() &&
5349 ((aLine
== mLines
.front() &&
5350 (!aLineLayout
.IsZeroBSize() || (aLine
== mLines
.back()))) ||
5351 (mLines
.front() != mLines
.back() && 0 == mLines
.front()->BSize() &&
5352 aLine
== mLines
.begin().next()))) {
5353 ReflowOutput
metrics(aState
.mReflowInput
);
5354 nsIFrame
* marker
= GetOutsideMarker();
5355 ReflowOutsideMarker(marker
, aState
, metrics
, aState
.mBCoord
);
5356 NS_ASSERTION(!MarkerIsEmpty() || metrics
.BSize(wm
) == 0,
5357 "empty ::marker frame took up space");
5358 aLineLayout
.AddMarkerFrame(marker
, metrics
);
5361 aLineLayout
.VerticalAlignLine();
5363 // We want to consider the floats in the current line when determining
5364 // whether the float available space is shrunk. If mLineBSize doesn't
5365 // exist, we are in the first pass trying to place the line. Calling
5366 // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
5367 // for UpdateBand().
5369 // floatAvailableSpaceWithOldLineBSize is the float available space with
5370 // the old BSize, but including the floats that were added in this line.
5371 LogicalRect floatAvailableSpaceWithOldLineBSize
=
5372 aState
.mLineBSize
.isNothing()
5373 ? aState
.GetFloatAvailableSpace(aLine
->BStart()).mRect
5375 .GetFloatAvailableSpaceForBSize(
5376 aLine
->BStart(), aState
.mLineBSize
.value(), nullptr)
5379 // As we redo for floats, we can't reduce the amount of BSize we're
5381 aAvailableSpaceBSize
= std::max(aAvailableSpaceBSize
, aLine
->BSize());
5382 LogicalRect floatAvailableSpaceWithLineBSize
=
5384 .GetFloatAvailableSpaceForBSize(aLine
->BStart(), aAvailableSpaceBSize
,
5388 // If the available space between the floats is smaller now that we
5389 // know the BSize, return false (and cause another pass with
5390 // LineReflowStatus::RedoMoreFloats). We ensure aAvailableSpaceBSize
5391 // never decreases, which means that we can't reduce the set of floats
5392 // we intersect, which means that the available space cannot grow.
5393 if (AvailableSpaceShrunk(wm
, floatAvailableSpaceWithOldLineBSize
,
5394 floatAvailableSpaceWithLineBSize
, false)) {
5395 // Prepare data for redoing the line.
5396 aState
.mLineBSize
= Some(aLine
->BSize());
5398 // Since we want to redo the line, we update aFlowArea by using the
5399 // aFloatStateBeforeLine, which is the float manager's state before the
5401 LogicalRect
oldFloatAvailableSpace(aFlowArea
.mRect
);
5402 aFlowArea
= aState
.GetFloatAvailableSpaceForBSize(
5403 aLine
->BStart(), aAvailableSpaceBSize
, aFloatStateBeforeLine
);
5406 aFlowArea
.mRect
.BStart(wm
) == oldFloatAvailableSpace
.BStart(wm
),
5408 // Restore the BSize to the position of the next band.
5409 aFlowArea
.mRect
.BSize(wm
) = oldFloatAvailableSpace
.BSize(wm
);
5411 // Enforce both IStart() and IEnd() never move outwards to prevent
5412 // infinite grow-shrink loops.
5413 const nscoord iStartDiff
=
5414 aFlowArea
.mRect
.IStart(wm
) - oldFloatAvailableSpace
.IStart(wm
);
5415 const nscoord iEndDiff
=
5416 aFlowArea
.mRect
.IEnd(wm
) - oldFloatAvailableSpace
.IEnd(wm
);
5417 if (iStartDiff
< 0) {
5418 aFlowArea
.mRect
.IStart(wm
) -= iStartDiff
;
5419 aFlowArea
.mRect
.ISize(wm
) += iStartDiff
;
5422 aFlowArea
.mRect
.ISize(wm
) -= iEndDiff
;
5429 if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
5430 static nscoord lastHeight
= 0;
5431 if (ABSURD_SIZE(aLine
->BStart())) {
5432 lastHeight
= aLine
->BStart();
5433 if (abs(aLine
->BStart() - lastHeight
) > ABSURD_COORD
/ 10) {
5434 nsIFrame::ListTag(stdout
);
5435 printf(": line=%p y=%d line.bounds.height=%d\n",
5436 static_cast<void*>(aLine
.get()), aLine
->BStart(),
5445 // Only block frames horizontally align their children because
5446 // inline frames "shrink-wrap" around their children (therefore
5447 // there is no extra horizontal space).
5448 const nsStyleText
* styleText
= StyleText();
5451 * We don't care checking for IsLastLine properly if we don't care (if it
5452 * can't change the used text-align value for the line).
5454 * In other words, isLastLine really means isLastLineAndWeCare.
5456 const bool isLastLine
=
5457 !IsInSVGTextSubtree() &&
5458 styleText
->TextAlignForLastLine() != styleText
->mTextAlign
&&
5459 (aLineLayout
.GetLineEndsInBR() || IsLastLine(aState
, aLine
));
5461 aLineLayout
.TextAlignLine(aLine
, isLastLine
);
5463 // From here on, pfd->mBounds rectangles are incorrect because bidi
5464 // might have moved frames around!
5465 OverflowAreas overflowAreas
;
5466 aLineLayout
.RelativePositionFrames(overflowAreas
);
5467 aLine
->SetOverflowAreas(overflowAreas
);
5469 aLineLayout
.RemoveMarkerFrame(GetOutsideMarker());
5472 // Inline lines do not have margins themselves; however they are
5473 // impacted by prior block margins. If this line ends up having some
5474 // height then we zero out the previous block-end margin value that was
5475 // already applied to the line's starting Y coordinate. Otherwise we
5476 // leave it be so that the previous blocks block-end margin can be
5477 // collapsed with a block that follows.
5480 if (!aLine
->CachedIsEmpty()) {
5481 // This line has some height. Therefore the application of the
5482 // previous-bottom-margin should stick.
5483 aState
.mPrevBEndMargin
.Zero();
5484 newBCoord
= aLine
->BEnd();
5486 // Don't let the previous-bottom-margin value affect the newBCoord
5487 // coordinate (it was applied in ReflowInlineFrames speculatively)
5488 // since the line is empty.
5489 // We already called |ShouldApplyBStartMargin|, and if we applied it
5490 // then mShouldApplyBStartMargin is set.
5491 nscoord dy
= aState
.mFlags
.mShouldApplyBStartMargin
5492 ? -aState
.mPrevBEndMargin
.get()
5494 newBCoord
= aState
.mBCoord
+ dy
;
5497 if (!aState
.mReflowStatus
.IsFullyComplete() &&
5498 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5499 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5500 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5504 // See if the line fit (our first line always does).
5505 if (mLines
.front() != aLine
&&
5506 aState
.ContentBSize() != NS_UNCONSTRAINEDSIZE
&&
5507 newBCoord
> aState
.ContentBEnd()) {
5508 NS_ASSERTION(aState
.mCurrentLine
== aLine
, "oops");
5509 if (ShouldAvoidBreakInside(aState
.mReflowInput
)) {
5510 // All our content doesn't fit, start on the next page.
5511 SetBreakBeforeStatusBeforeLine(aState
, aLine
, aKeepReflowGoing
);
5513 // Push aLine and all of its children and anything else that
5514 // follows to our next-in-flow.
5515 PushTruncatedLine(aState
, aLine
, aKeepReflowGoing
);
5520 // Note that any early return before this update of aState.mBCoord
5521 // must either (a) return false or (b) set aKeepReflowGoing to false.
5522 // Otherwise we'll keep reflowing later lines at an incorrect
5523 // position, and we might not come back and clean up the damage later.
5524 aState
.mBCoord
= newBCoord
;
5526 // Add the already placed current-line floats to the line
5527 aLine
->AppendFloats(std::move(aState
.mCurrentLineFloats
));
5529 // Any below current line floats to place?
5530 if (!aState
.mBelowCurrentLineFloats
.IsEmpty()) {
5531 // Reflow the below-current-line floats, which places on the line's
5533 aState
.PlaceBelowCurrentLineFloats(aLine
);
5536 // When a line has floats, factor them into the overflow areas computations.
5537 if (aLine
->HasFloats()) {
5538 // Union the float overflow areas (stored in aState) and the value computed
5539 // by the line layout code.
5540 OverflowAreas lineOverflowAreas
= aState
.mFloatOverflowAreas
;
5541 lineOverflowAreas
.UnionWith(aLine
->GetOverflowAreas());
5542 aLine
->SetOverflowAreas(lineOverflowAreas
);
5544 #ifdef NOISY_OVERFLOW_AREAS
5545 printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
5546 ListTag().get(), aLine
.get(),
5547 ToString(aLine
->InkOverflowRect()).c_str(),
5548 ToString(aLine
->ScrollableOverflowRect()).c_str());
5552 // Apply break-after clearing if necessary
5553 // This must stay in sync with |ReflowDirtyLines|.
5554 if (aLine
->HasFloatClearTypeAfter()) {
5555 std::tie(aState
.mBCoord
, std::ignore
) =
5556 aState
.ClearFloats(aState
.mBCoord
, aLine
->FloatClearTypeAfter());
5561 void nsBlockFrame::PushLines(BlockReflowState
& aState
,
5562 nsLineList::iterator aLineBefore
) {
5563 // NOTE: aLineBefore is always a normal line, not an overflow line.
5564 // The following expression will assert otherwise.
5565 DebugOnly
<bool> check
= aLineBefore
== mLines
.begin();
5567 nsLineList::iterator
overBegin(aLineBefore
.next());
5569 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
5570 bool firstLine
= overBegin
== LinesBegin();
5572 if (overBegin
!= LinesEnd()) {
5573 // Remove floats in the lines from mFloats
5575 CollectFloats(overBegin
->mFirstChild
, floats
, true);
5577 if (floats
.NotEmpty()) {
5579 for (nsIFrame
* f
: floats
) {
5580 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5581 "CollectFloats should've removed that bit");
5584 // Push the floats onto the front of the overflow out-of-flows list
5585 nsAutoOOFFrameList
oofs(this);
5586 oofs
.mList
.InsertFrames(nullptr, nullptr, std::move(floats
));
5589 // overflow lines can already exist in some cases, in particular,
5590 // when shrinkwrapping and we discover that the shrinkwap causes
5591 // the height of some child block to grow which creates additional
5592 // overflowing content. In such cases we must prepend the new
5593 // overflow to the existing overflow.
5594 FrameLines
* overflowLines
= RemoveOverflowLines();
5595 if (!overflowLines
) {
5596 // XXXldb use presshell arena!
5597 overflowLines
= new FrameLines();
5599 if (overflowLines
) {
5600 nsIFrame
* lineBeforeLastFrame
;
5602 lineBeforeLastFrame
= nullptr; // removes all frames
5604 nsIFrame
* f
= overBegin
->mFirstChild
;
5605 lineBeforeLastFrame
= f
? f
->GetPrevSibling() : mFrames
.LastChild();
5606 NS_ASSERTION(!f
|| lineBeforeLastFrame
== aLineBefore
->LastChild(),
5607 "unexpected line frames");
5609 nsFrameList pushedFrames
= mFrames
.TakeFramesAfter(lineBeforeLastFrame
);
5610 overflowLines
->mFrames
.InsertFrames(nullptr, nullptr,
5611 std::move(pushedFrames
));
5613 overflowLines
->mLines
.splice(overflowLines
->mLines
.begin(), mLines
,
5614 overBegin
, LinesEnd());
5615 NS_ASSERTION(!overflowLines
->mLines
.empty(), "should not be empty");
5616 // this takes ownership but it won't delete it immediately so we
5617 // can keep using it.
5618 SetOverflowLines(overflowLines
);
5620 // Mark all the overflow lines dirty so that they get reflowed when
5621 // they are pulled up by our next-in-flow.
5623 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
5624 for (LineIterator line
= overflowLines
->mLines
.begin(),
5625 line_end
= overflowLines
->mLines
.end();
5626 line
!= line_end
; ++line
) {
5628 line
->MarkPreviousMarginDirty();
5629 line
->SetMovedFragments();
5630 line
->SetBoundsEmpty();
5631 if (line
->HasFloats()) {
5632 line
->ClearFloats();
5639 VerifyOverflowSituation();
5643 // The overflowLines property is stored as a pointer to a line list,
5644 // which must be deleted. However, the following functions all maintain
5645 // the invariant that the property is never set if the list is empty.
5647 bool nsBlockFrame::DrainOverflowLines() {
5649 VerifyOverflowSituation();
5652 // Steal the prev-in-flow's overflow lines and prepend them.
5653 bool didFindOverflow
= false;
5654 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5656 prevBlock
->ClearLineCursors();
5657 FrameLines
* overflowLines
= prevBlock
->RemoveOverflowLines();
5658 if (overflowLines
) {
5659 // Make all the frames on the overflow line list mine.
5660 ReparentFrames(overflowLines
->mFrames
, prevBlock
, this);
5662 // Collect overflow containers from our OverflowContainers list that are
5663 // continuations from the frames we picked up from our prev-in-flow, then
5664 // prepend those to ExcessOverflowContainers to ensure the continuations
5666 if (GetOverflowContainers()) {
5667 nsFrameList ocContinuations
;
5668 for (auto* f
: overflowLines
->mFrames
) {
5671 while (!done
&& (cont
= cont
->GetNextContinuation()) &&
5672 cont
->GetParent() == this) {
5673 bool onlyChild
= !cont
->GetPrevSibling() && !cont
->GetNextSibling();
5674 if (cont
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
) &&
5675 TryRemoveFrame(OverflowContainersProperty(), cont
)) {
5676 ocContinuations
.AppendFrame(nullptr, cont
);
5686 if (!ocContinuations
.IsEmpty()) {
5687 if (nsFrameList
* eoc
= GetExcessOverflowContainers()) {
5688 eoc
->InsertFrames(nullptr, nullptr, std::move(ocContinuations
));
5690 SetExcessOverflowContainers(std::move(ocContinuations
));
5695 // Make the overflow out-of-flow frames mine too.
5696 nsAutoOOFFrameList
oofs(prevBlock
);
5697 if (oofs
.mList
.NotEmpty()) {
5698 // In case we own any next-in-flows of any of the drained frames, then
5699 // move those to the PushedFloat list.
5700 nsFrameList pushedFloats
;
5701 for (nsIFrame
* f
: oofs
.mList
) {
5702 nsIFrame
* nif
= f
->GetNextInFlow();
5703 for (; nif
&& nif
->GetParent() == this; nif
= nif
->GetNextInFlow()) {
5704 MOZ_ASSERT(nif
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
));
5706 pushedFloats
.AppendFrame(nullptr, nif
);
5709 ReparentFrames(oofs
.mList
, prevBlock
, this);
5710 mFloats
.InsertFrames(nullptr, nullptr, std::move(oofs
.mList
));
5711 if (!pushedFloats
.IsEmpty()) {
5712 nsFrameList
* pf
= EnsurePushedFloats();
5713 pf
->InsertFrames(nullptr, nullptr, std::move(pushedFloats
));
5717 if (!mLines
.empty()) {
5718 // Remember to recompute the margins on the first line. This will
5719 // also recompute the correct deltaBCoord if necessary.
5720 mLines
.front()->MarkPreviousMarginDirty();
5722 // The overflow lines have already been marked dirty and their previous
5723 // margins marked dirty also.
5725 // Prepend the overflow frames/lines to our principal list.
5726 mFrames
.InsertFrames(nullptr, nullptr, std::move(overflowLines
->mFrames
));
5727 mLines
.splice(mLines
.begin(), overflowLines
->mLines
);
5728 NS_ASSERTION(overflowLines
->mLines
.empty(), "splice should empty list");
5729 delete overflowLines
;
5730 didFindOverflow
= true;
5734 // Now append our own overflow lines.
5735 return DrainSelfOverflowList() || didFindOverflow
;
5738 bool nsBlockFrame::DrainSelfOverflowList() {
5739 UniquePtr
<FrameLines
> ourOverflowLines(RemoveOverflowLines());
5740 if (!ourOverflowLines
) {
5744 // No need to reparent frames in our own overflow lines/oofs, because they're
5745 // already ours. But we should put overflow floats back in mFloats.
5746 // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5748 nsAutoOOFFrameList
oofs(this);
5749 if (oofs
.mList
.NotEmpty()) {
5751 for (nsIFrame
* f
: oofs
.mList
) {
5752 MOZ_ASSERT(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
5753 "CollectFloats should've removed that bit");
5756 // The overflow floats go after our regular floats.
5757 mFloats
.AppendFrames(nullptr, std::move(oofs
).mList
);
5760 if (!ourOverflowLines
->mLines
.empty()) {
5761 mFrames
.AppendFrames(nullptr, std::move(ourOverflowLines
->mFrames
));
5762 mLines
.splice(mLines
.end(), ourOverflowLines
->mLines
);
5766 VerifyOverflowSituation();
5772 * Pushed floats are floats whose placeholders are in a previous
5773 * continuation. They might themselves be next-continuations of a float
5774 * that partially fit in an earlier continuation, or they might be the
5775 * first continuation of a float that couldn't be placed at all.
5777 * Pushed floats live permanently at the beginning of a block's float
5778 * list, where they must live *before* any floats whose placeholders are
5781 * Temporarily, during reflow, they also live on the pushed floats list,
5782 * which only holds them between (a) when one continuation pushes them to
5783 * its pushed floats list because they don't fit and (b) when the next
5784 * continuation pulls them onto the beginning of its float list.
5786 * DrainPushedFloats sets up pushed floats the way we need them at the
5787 * start of reflow; they are then reflowed by ReflowPushedFloats (which
5788 * might push some of them on). Floats with placeholders in this block
5789 * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
5790 * also maintains these invariants.
5792 * DrainSelfPushedFloats moves any pushed floats from this block's own
5793 * PushedFloats list back into mFloats. DrainPushedFloats additionally
5794 * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5796 void nsBlockFrame::DrainSelfPushedFloats() {
5797 // If we're getting reflowed multiple times without our
5798 // next-continuation being reflowed, we might need to pull back floats
5799 // that we just put in the list to be pushed to our next-in-flow.
5800 // We don't want to pull back any next-in-flows of floats on our own
5801 // float list, and we only need to pull back first-in-flows whose
5802 // placeholders were in earlier blocks (since first-in-flows whose
5803 // placeholders are in this block will get pulled appropriately by
5804 // AddFloat, and will then be more likely to be in the correct order).
5805 mozilla::PresShell
* presShell
= PresShell();
5806 nsFrameList
* ourPushedFloats
= GetPushedFloats();
5807 if (ourPushedFloats
) {
5808 // When we pull back floats, we want to put them with the pushed
5809 // floats, which must live at the start of our float list, but we
5810 // want them at the end of those pushed floats.
5811 // FIXME: This isn't quite right! What if they're all pushed floats?
5812 nsIFrame
* insertionPrevSibling
= nullptr; /* beginning of list */
5813 for (nsIFrame
* f
= mFloats
.FirstChild();
5814 f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
5815 f
= f
->GetNextSibling()) {
5816 insertionPrevSibling
= f
;
5819 nsIFrame
* f
= ourPushedFloats
->LastChild();
5821 nsIFrame
* prevSibling
= f
->GetPrevSibling();
5823 nsPlaceholderFrame
* placeholder
= f
->GetPlaceholderFrame();
5824 nsIFrame
* floatOriginalParent
=
5825 presShell
->FrameConstructor()->GetFloatContainingBlock(placeholder
);
5826 if (floatOriginalParent
!= this) {
5827 // This is a first continuation that was pushed from one of our
5828 // previous continuations. Take it out of the pushed floats
5829 // list and put it in our floats list, before any of our
5830 // floats, but after other pushed floats.
5831 ourPushedFloats
->RemoveFrame(f
);
5832 mFloats
.InsertFrame(nullptr, insertionPrevSibling
, f
);
5838 if (ourPushedFloats
->IsEmpty()) {
5839 RemovePushedFloats()->Delete(presShell
);
5844 void nsBlockFrame::DrainPushedFloats() {
5845 DrainSelfPushedFloats();
5847 // After our prev-in-flow has completed reflow, it may have a pushed
5848 // floats list, containing floats that we need to own. Take these.
5849 nsBlockFrame
* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow());
5851 AutoFrameListPtr
list(PresContext(), prevBlock
->RemovePushedFloats());
5852 if (list
&& list
->NotEmpty()) {
5853 mFloats
.InsertFrames(this, nullptr, std::move(*list
));
5858 nsBlockFrame::FrameLines
* nsBlockFrame::GetOverflowLines() const {
5859 if (!HasOverflowLines()) {
5862 FrameLines
* prop
= GetProperty(OverflowLinesProperty());
5864 prop
&& !prop
->mLines
.empty() &&
5865 prop
->mLines
.front()->GetChildCount() == 0
5866 ? prop
->mFrames
.IsEmpty()
5867 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5868 "value should always be stored and non-empty when state set");
5872 nsBlockFrame::FrameLines
* nsBlockFrame::RemoveOverflowLines() {
5873 if (!HasOverflowLines()) {
5876 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5878 prop
&& !prop
->mLines
.empty() &&
5879 prop
->mLines
.front()->GetChildCount() == 0
5880 ? prop
->mFrames
.IsEmpty()
5881 : prop
->mLines
.front()->mFirstChild
== prop
->mFrames
.FirstChild(),
5882 "value should always be stored and non-empty when state set");
5883 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5887 void nsBlockFrame::DestroyOverflowLines() {
5888 NS_ASSERTION(HasOverflowLines(), "huh?");
5889 FrameLines
* prop
= TakeProperty(OverflowLinesProperty());
5890 NS_ASSERTION(prop
&& prop
->mLines
.empty(),
5891 "value should always be stored but empty when destroying");
5892 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5896 // This takes ownership of aOverflowLines.
5897 // XXX We should allocate overflowLines from presShell arena!
5898 void nsBlockFrame::SetOverflowLines(FrameLines
* aOverflowLines
) {
5899 NS_ASSERTION(aOverflowLines
, "null lines");
5900 NS_ASSERTION(!aOverflowLines
->mLines
.empty(), "empty lines");
5901 NS_ASSERTION(aOverflowLines
->mLines
.front()->mFirstChild
==
5902 aOverflowLines
->mFrames
.FirstChild(),
5903 "invalid overflow lines / frames");
5904 NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
),
5905 "Overwriting existing overflow lines");
5907 // Verify that we won't overwrite an existing overflow list
5908 NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5909 SetProperty(OverflowLinesProperty(), aOverflowLines
);
5910 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES
);
5913 nsFrameList
* nsBlockFrame::GetOverflowOutOfFlows() const {
5914 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5917 nsFrameList
* result
= GetProperty(OverflowOutOfFlowsProperty());
5918 NS_ASSERTION(result
, "value should always be non-empty when state set");
5922 void nsBlockFrame::SetOverflowOutOfFlows(nsFrameList
&& aList
,
5923 nsFrameList
* aPropValue
) {
5925 HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
) == !!aPropValue
,
5926 "state does not match value");
5928 if (aList
.IsEmpty()) {
5929 if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5932 nsFrameList
* list
= TakeProperty(OverflowOutOfFlowsProperty());
5933 NS_ASSERTION(aPropValue
== list
, "prop value mismatch");
5935 list
->Delete(PresShell());
5936 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5937 } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
)) {
5938 NS_ASSERTION(aPropValue
== GetProperty(OverflowOutOfFlowsProperty()),
5939 "prop value mismatch");
5940 *aPropValue
= std::move(aList
);
5942 SetProperty(OverflowOutOfFlowsProperty(),
5943 new (PresShell()) nsFrameList(std::move(aList
)));
5944 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS
);
5948 nsIFrame
* nsBlockFrame::GetInsideMarker() const {
5949 if (!HasInsideMarker()) {
5952 NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5953 nsIFrame
* frame
= GetProperty(InsideMarkerProperty());
5954 NS_ASSERTION(frame
, "bogus inside ::marker frame");
5958 nsIFrame
* nsBlockFrame::GetOutsideMarker() const {
5959 nsFrameList
* list
= GetOutsideMarkerList();
5960 return list
? list
->FirstChild() : nullptr;
5963 nsFrameList
* nsBlockFrame::GetOutsideMarkerList() const {
5964 if (!HasOutsideMarker()) {
5967 NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5968 nsFrameList
* list
= GetProperty(OutsideMarkerProperty());
5969 NS_ASSERTION(list
&& list
->GetLength() == 1, "bogus outside ::marker list");
5973 nsFrameList
* nsBlockFrame::GetPushedFloats() const {
5974 if (!HasPushedFloats()) {
5977 nsFrameList
* result
= GetProperty(PushedFloatProperty());
5978 NS_ASSERTION(result
, "value should always be non-empty when state set");
5982 nsFrameList
* nsBlockFrame::EnsurePushedFloats() {
5983 nsFrameList
* result
= GetPushedFloats();
5988 result
= new (PresShell()) nsFrameList
;
5989 SetProperty(PushedFloatProperty(), result
);
5990 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
5995 nsFrameList
* nsBlockFrame::RemovePushedFloats() {
5996 if (!HasPushedFloats()) {
5999 nsFrameList
* result
= TakeProperty(PushedFloatProperty());
6000 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS
);
6001 NS_ASSERTION(result
, "value should always be non-empty when state set");
6005 //////////////////////////////////////////////////////////////////////
6006 // Frame list manipulation routines
6008 void nsBlockFrame::AppendFrames(ChildListID aListID
, nsFrameList
&& aFrameList
) {
6009 if (aFrameList
.IsEmpty()) {
6012 if (aListID
!= FrameChildListID::Principal
) {
6013 if (FrameChildListID::Float
== aListID
) {
6014 DrainSelfPushedFloats(); // ensure the last frame is in mFloats
6015 mFloats
.AppendFrames(nullptr, std::move(aFrameList
));
6018 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
6019 "unexpected child list");
6022 // Find the proper last-child for where the append should go
6023 nsIFrame
* lastKid
= mFrames
.LastChild();
6025 (mLines
.empty() ? nullptr : mLines
.back()->LastChild()) == lastKid
,
6026 "out-of-sync mLines / mFrames");
6028 #ifdef NOISY_REFLOW_REASON
6030 printf(": append ");
6031 for (nsIFrame
* frame
: aFrameList
) {
6032 frame
->ListTag(stdout
);
6036 lastKid
->ListTag(stdout
);
6041 if (IsInSVGTextSubtree()) {
6042 MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
6043 "unexpected block frame in SVG text");
6044 // Workaround for bug 1399425 in case this bit has been removed from the
6045 // SVGTextFrame just before the parser adds more descendant nodes.
6046 GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
);
6049 AddFrames(std::move(aFrameList
), lastKid
, nullptr);
6050 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
6051 PresShell()->FrameNeedsReflow(
6052 this, IntrinsicDirty::FrameAndAncestors
,
6053 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6057 void nsBlockFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
6058 const nsLineList::iterator
* aPrevFrameLine
,
6059 nsFrameList
&& aFrameList
) {
6060 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
6061 "inserting after sibling frame with different parent");
6063 if (aListID
!= FrameChildListID::Principal
) {
6064 if (FrameChildListID::Float
== aListID
) {
6065 DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats
6066 mFloats
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
6069 MOZ_ASSERT(FrameChildListID::NoReflowPrincipal
== aListID
,
6070 "unexpected child list");
6073 #ifdef NOISY_REFLOW_REASON
6075 printf(": insert ");
6076 for (nsIFrame
* frame
: aFrameList
) {
6077 frame
->ListTag(stdout
);
6081 aPrevFrame
->ListTag(stdout
);
6086 AddFrames(std::move(aFrameList
), aPrevFrame
, aPrevFrameLine
);
6087 if (aListID
!= FrameChildListID::NoReflowPrincipal
) {
6088 PresShell()->FrameNeedsReflow(
6089 this, IntrinsicDirty::FrameAndAncestors
,
6090 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6094 void nsBlockFrame::RemoveFrame(DestroyContext
& aContext
, ChildListID aListID
,
6095 nsIFrame
* aOldFrame
) {
6096 #ifdef NOISY_REFLOW_REASON
6098 printf(": remove ");
6099 aOldFrame
->ListTag(stdout
);
6103 if (aListID
== FrameChildListID::Principal
) {
6104 bool hasFloats
= BlockHasAnyFloats(aOldFrame
);
6105 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6107 MarkSameFloatManagerLinesDirty(this);
6109 } else if (FrameChildListID::Float
== aListID
) {
6110 // Make sure to mark affected lines dirty for the float frame
6111 // we are removing; this way is a bit messy, but so is the rest of the code.
6113 NS_ASSERTION(!aOldFrame
->GetPrevContinuation(),
6114 "RemoveFrame should not be called on pushed floats.");
6115 for (nsIFrame
* f
= aOldFrame
;
6116 f
&& !f
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
);
6117 f
= f
->GetNextContinuation()) {
6118 MarkSameFloatManagerLinesDirty(
6119 static_cast<nsBlockFrame
*>(f
->GetParent()));
6121 DoRemoveOutOfFlowFrame(aContext
, aOldFrame
);
6122 } else if (FrameChildListID::NoReflowPrincipal
== aListID
) {
6123 // Skip the call to |FrameNeedsReflow| below by returning now.
6124 DoRemoveFrame(aContext
, aOldFrame
, REMOVE_FIXED_CONTINUATIONS
);
6127 MOZ_CRASH("unexpected child list");
6130 PresShell()->FrameNeedsReflow(
6131 this, IntrinsicDirty::FrameAndAncestors
,
6132 NS_FRAME_HAS_DIRTY_CHILDREN
); // XXX sufficient?
6135 static bool ShouldPutNextSiblingOnNewLine(nsIFrame
* aLastFrame
) {
6136 LayoutFrameType type
= aLastFrame
->Type();
6137 if (type
== LayoutFrameType::Br
) {
6140 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
6141 if (type
== LayoutFrameType::Text
&&
6142 !aLastFrame
->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING
)) {
6143 return aLastFrame
->HasSignificantTerminalNewline();
6148 void nsBlockFrame::AddFrames(nsFrameList
&& aFrameList
, nsIFrame
* aPrevSibling
,
6149 const nsLineList::iterator
* aPrevSiblingLine
) {
6150 // Clear our line cursor, since our lines may change.
6153 if (aFrameList
.IsEmpty()) {
6157 // Attempt to find the line that contains the previous sibling
6158 nsLineList
* lineList
= &mLines
;
6159 nsFrameList
* frames
= &mFrames
;
6160 nsLineList::iterator prevSibLine
;
6161 int32_t prevSiblingIndex
;
6162 if (aPrevSiblingLine
) {
6163 MOZ_ASSERT(aPrevSibling
);
6164 prevSibLine
= *aPrevSiblingLine
;
6165 FrameLines
* overflowLines
= GetOverflowLines();
6166 MOZ_ASSERT(prevSibLine
.IsInSameList(mLines
.begin()) ||
6168 prevSibLine
.IsInSameList(overflowLines
->mLines
.begin())),
6169 "must be one of our line lists");
6170 if (overflowLines
) {
6171 // We need to find out which list it's actually in. Assume that
6172 // *if* we have overflow lines, that our primary lines aren't
6173 // huge, but our overflow lines might be.
6174 nsLineList::iterator line
= mLines
.begin(), lineEnd
= mLines
.end();
6175 while (line
!= lineEnd
) {
6176 if (line
== prevSibLine
) {
6181 if (line
== lineEnd
) {
6182 // By elimination, the line must be in our overflow lines.
6183 lineList
= &overflowLines
->mLines
;
6184 frames
= &overflowLines
->mFrames
;
6188 nsLineList::iterator nextLine
= prevSibLine
.next();
6189 nsIFrame
* lastFrameInLine
= nextLine
== lineList
->end()
6190 ? frames
->LastChild()
6191 : nextLine
->mFirstChild
->GetPrevSibling();
6192 prevSiblingIndex
= prevSibLine
->RIndexOf(aPrevSibling
, lastFrameInLine
);
6193 MOZ_ASSERT(prevSiblingIndex
>= 0,
6194 "aPrevSibling must be in aPrevSiblingLine");
6196 prevSibLine
= lineList
->end();
6197 prevSiblingIndex
= -1;
6199 // XXX_perf This is technically O(N^2) in some cases, but by using
6200 // RFind instead of Find, we make it O(N) in the most common case,
6201 // which is appending content.
6203 // Find the line that contains the previous sibling
6204 if (!nsLineBox::RFindLineContaining(aPrevSibling
, lineList
->begin(),
6205 prevSibLine
, mFrames
.LastChild(),
6206 &prevSiblingIndex
)) {
6207 // Not in mLines - try overflow lines.
6208 FrameLines
* overflowLines
= GetOverflowLines();
6210 if (overflowLines
) {
6211 prevSibLine
= overflowLines
->mLines
.end();
6212 prevSiblingIndex
= -1;
6213 found
= nsLineBox::RFindLineContaining(
6214 aPrevSibling
, overflowLines
->mLines
.begin(), prevSibLine
,
6215 overflowLines
->mFrames
.LastChild(), &prevSiblingIndex
);
6217 if (MOZ_LIKELY(found
)) {
6218 lineList
= &overflowLines
->mLines
;
6219 frames
= &overflowLines
->mFrames
;
6221 // Note: defensive code! RFindLineContaining must not return
6222 // false in this case, so if it does...
6223 MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
6224 aPrevSibling
= nullptr;
6225 prevSibLine
= lineList
->end();
6231 // Find the frame following aPrevSibling so that we can join up the
6232 // two lists of frames.
6234 // Split line containing aPrevSibling in two if the insertion
6235 // point is somewhere in the middle of the line.
6236 int32_t rem
= prevSibLine
->GetChildCount() - prevSiblingIndex
- 1;
6238 // Split the line in two where the frame(s) are being inserted.
6240 NewLineBox(prevSibLine
, aPrevSibling
->GetNextSibling(), rem
);
6241 lineList
->after_insert(prevSibLine
, line
);
6242 // Mark prevSibLine dirty and as needing textrun invalidation, since
6243 // we may be breaking up text in the line. Its previous line may also
6244 // need to be invalidated because it may be able to pull some text up.
6245 MarkLineDirty(prevSibLine
, lineList
);
6246 // The new line will also need its textruns recomputed because of the
6249 line
->SetInvalidateTextRuns(true);
6251 } else if (!lineList
->empty()) {
6252 lineList
->front()->MarkDirty();
6253 lineList
->front()->SetInvalidateTextRuns(true);
6255 const nsFrameList::Slice
& newFrames
=
6256 frames
->InsertFrames(nullptr, aPrevSibling
, std::move(aFrameList
));
6258 // Walk through the new frames being added and update the line data
6259 // structures to fit.
6260 for (nsIFrame
* newFrame
: newFrames
) {
6261 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetNextSibling() == newFrame
,
6262 "Unexpected aPrevSibling");
6264 !newFrame
->IsPlaceholderFrame() ||
6265 (!newFrame
->IsAbsolutelyPositioned() && !newFrame
->IsFloating()),
6266 "Placeholders should not float or be positioned");
6268 bool isBlock
= newFrame
->IsBlockOutside();
6270 // If the frame is a block frame, or if there is no previous line or if the
6271 // previous line is a block line we need to make a new line. We also make
6272 // a new line, as an optimization, in the two cases we know we'll need it:
6273 // if the previous line ended with a <br>, or if it has significant
6274 // whitespace and ended in a newline.
6275 if (isBlock
|| prevSibLine
== lineList
->end() || prevSibLine
->IsBlock() ||
6276 (aPrevSibling
&& ShouldPutNextSiblingOnNewLine(aPrevSibling
))) {
6277 // Create a new line for the frame and add its line to the line
6279 nsLineBox
* line
= NewLineBox(newFrame
, isBlock
);
6280 if (prevSibLine
!= lineList
->end()) {
6281 // Append new line after prevSibLine
6282 lineList
->after_insert(prevSibLine
, line
);
6285 // New line is going before the other lines
6286 lineList
->push_front(line
);
6287 prevSibLine
= lineList
->begin();
6290 prevSibLine
->NoteFrameAdded(newFrame
);
6291 // We're adding inline content to prevSibLine, so we need to mark it
6292 // dirty, ensure its textruns are recomputed, and possibly do the same
6293 // to its previous line since that line may be able to pull content up.
6294 MarkLineDirty(prevSibLine
, lineList
);
6297 aPrevSibling
= newFrame
;
6301 MOZ_ASSERT(aFrameList
.IsEmpty());
6306 nsContainerFrame
* nsBlockFrame::GetRubyContentPseudoFrame() {
6307 auto* firstChild
= PrincipalChildList().FirstChild();
6308 if (firstChild
&& firstChild
->IsRubyFrame() &&
6309 firstChild
->Style()->GetPseudoType() ==
6310 mozilla::PseudoStyleType::blockRubyContent
) {
6311 return static_cast<nsContainerFrame
*>(firstChild
);
6316 nsContainerFrame
* nsBlockFrame::GetContentInsertionFrame() {
6317 // 'display:block ruby' use the inner (Ruby) frame for insertions.
6318 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6319 return rubyContentPseudoFrame
;
6324 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
6325 nsTArray
<OwnedAnonBox
>& aResult
) {
6326 if (auto* rubyContentPseudoFrame
= GetRubyContentPseudoFrame()) {
6327 aResult
.AppendElement(OwnedAnonBox(rubyContentPseudoFrame
));
6331 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame
* aFloat
) {
6332 // Find which line contains the float, so we can update
6334 for (auto& line
: Lines()) {
6335 if (line
.IsInline() && line
.RemoveFloat(aFloat
)) {
6341 void nsBlockFrame::RemoveFloat(nsIFrame
* aFloat
) {
6343 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
6344 // frame list properties.
6345 if (!mFloats
.ContainsFrame(aFloat
)) {
6347 (GetOverflowOutOfFlows() &&
6348 GetOverflowOutOfFlows()->ContainsFrame(aFloat
)) ||
6349 (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat
)),
6350 "aFloat is not our child or on an unexpected frame list");
6354 if (mFloats
.StartRemoveFrame(aFloat
)) {
6358 nsFrameList
* list
= GetPushedFloats();
6359 if (list
&& list
->ContinueRemoveFrame(aFloat
)) {
6361 // XXXmats not yet - need to investigate BlockReflowState::mPushedFloats
6362 // first so we don't leave it pointing to a deleted list.
6363 if (list
->IsEmpty()) {
6364 delete RemovePushedFloats();
6371 nsAutoOOFFrameList
oofs(this);
6372 if (oofs
.mList
.ContinueRemoveFrame(aFloat
)) {
6378 void nsBlockFrame::DoRemoveOutOfFlowFrame(DestroyContext
& aContext
,
6380 // The containing block is always the parent of aFrame.
6381 nsBlockFrame
* block
= (nsBlockFrame
*)aFrame
->GetParent();
6383 // Remove aFrame from the appropriate list.
6384 if (aFrame
->IsAbsolutelyPositioned()) {
6385 // This also deletes the next-in-flows
6386 block
->GetAbsoluteContainingBlock()->RemoveFrame(
6387 aContext
, FrameChildListID::Absolute
, aFrame
);
6389 // First remove aFrame's next-in-flows.
6390 if (nsIFrame
* nif
= aFrame
->GetNextInFlow()) {
6391 nif
->GetParent()->DeleteNextInFlowChild(aContext
, nif
, false);
6393 // Now remove aFrame from its child list and Destroy it.
6394 block
->RemoveFloatFromFloatCache(aFrame
);
6395 block
->RemoveFloat(aFrame
);
6396 aFrame
->Destroy(aContext
);
6401 * This helps us iterate over the list of all normal + overflow lines
6403 void nsBlockFrame::TryAllLines(nsLineList::iterator
* aIterator
,
6404 nsLineList::iterator
* aStartIterator
,
6405 nsLineList::iterator
* aEndIterator
,
6406 bool* aInOverflowLines
,
6407 FrameLines
** aOverflowLines
) {
6408 if (*aIterator
== *aEndIterator
) {
6409 if (!*aInOverflowLines
) {
6410 // Try the overflow lines
6411 *aInOverflowLines
= true;
6412 FrameLines
* lines
= GetOverflowLines();
6414 *aStartIterator
= lines
->mLines
.begin();
6415 *aIterator
= *aStartIterator
;
6416 *aEndIterator
= lines
->mLines
.end();
6417 *aOverflowLines
= lines
;
6423 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6425 : mFrame(aFrame
), mLine(aLine
), mLineList(&aFrame
->mLines
) {
6426 // This will assert if aLine isn't in mLines of aFrame:
6427 DebugOnly
<bool> check
= aLine
== mFrame
->LinesBegin();
6430 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6435 mLineList(aInOverflow
? &aFrame
->GetOverflowLines()->mLines
6436 : &aFrame
->mLines
) {}
6438 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6439 bool* aFoundValidLine
)
6440 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6441 mLine
= aFrame
->LinesBegin();
6442 *aFoundValidLine
= FindValidLine();
6445 static bool StyleEstablishesBFC(const ComputedStyle
* aStyle
) {
6446 // paint/layout containment boxes and multi-column containers establish an
6447 // independent formatting context.
6448 // https://drafts.csswg.org/css-contain/#containment-paint
6449 // https://drafts.csswg.org/css-contain/#containment-layout
6450 // https://drafts.csswg.org/css-multicol/#columns
6451 return aStyle
->StyleDisplay()->IsContainPaint() ||
6452 aStyle
->StyleDisplay()->IsContainLayout() ||
6453 aStyle
->GetPseudoType() == PseudoStyleType::columnContent
;
6456 void nsBlockFrame::DidSetComputedStyle(ComputedStyle
* aOldStyle
) {
6457 nsContainerFrame::DidSetComputedStyle(aOldStyle
);
6462 // If NS_BLOCK_STATIC_BFC flag was set when the frame was initialized, it
6463 // remains set during the lifetime of the frame and always forces it to be
6464 // treated as a BFC, independently of the value of NS_BLOCK_DYNAMIC_BFC.
6465 // Consequently, we don't bother invalidating or updating that latter flag.
6466 if (HasAnyStateBits(NS_BLOCK_STATIC_BFC
)) {
6470 bool isBFC
= StyleEstablishesBFC(Style());
6471 if (StyleEstablishesBFC(aOldStyle
) != isBFC
) {
6472 if (MaybeHasFloats()) {
6473 // If the frame contains floats, this update may change their float
6474 // manager. Be safe by dirtying all descendant lines of the nearest
6475 // ancestor's float manager.
6476 RemoveStateBits(NS_BLOCK_DYNAMIC_BFC
);
6477 MarkSameFloatManagerLinesDirty(this);
6479 AddOrRemoveStateBits(NS_BLOCK_DYNAMIC_BFC
, isBFC
);
6483 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState
& aRestyleState
) {
6484 nsIFrame
* letterFrame
= GetFirstLetter();
6489 // Figure out what the right style parent is. This needs to match
6490 // nsCSSFrameConstructor::CreateLetterFrame.
6491 nsIFrame
* inFlowFrame
= letterFrame
;
6492 if (inFlowFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6493 inFlowFrame
= inFlowFrame
->GetPlaceholderFrame();
6495 nsIFrame
* styleParent
= CorrectStyleParentFrame(inFlowFrame
->GetParent(),
6496 PseudoStyleType::firstLetter
);
6497 ComputedStyle
* parentStyle
= styleParent
->Style();
6498 RefPtr
<ComputedStyle
> firstLetterStyle
=
6499 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
6500 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr,
6502 // Note that we don't need to worry about changehints for the continuation
6503 // styles: those will be handled by the styleParent already.
6504 RefPtr
<ComputedStyle
> continuationStyle
=
6505 aRestyleState
.StyleSet().ResolveStyleForFirstLetterContinuation(
6507 UpdateStyleOfOwnedChildFrame(letterFrame
, firstLetterStyle
, aRestyleState
,
6508 Some(continuationStyle
.get()));
6510 // We also want to update the style on the textframe inside the first-letter.
6511 // We don't need to compute a changehint for this, though, since any changes
6512 // to it are handled by the first-letter anyway.
6513 nsIFrame
* textFrame
= letterFrame
->PrincipalChildList().FirstChild();
6514 RefPtr
<ComputedStyle
> firstTextStyle
=
6515 aRestyleState
.StyleSet().ResolveStyleForText(textFrame
->GetContent(),
6517 textFrame
->SetComputedStyle(firstTextStyle
);
6519 // We don't need to update style for textFrame's continuations: it's already
6520 // set up to inherit from parentStyle, which is what we want.
6523 static nsIFrame
* FindChildContaining(nsBlockFrame
* aFrame
,
6524 nsIFrame
* aFindFrame
) {
6525 NS_ASSERTION(aFrame
, "must have frame");
6528 nsIFrame
* block
= aFrame
;
6530 child
= nsLayoutUtils::FindChildContainingDescendant(block
, aFindFrame
);
6534 block
= block
->GetNextContinuation();
6539 if (!child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
6542 aFindFrame
= child
->GetPlaceholderFrame();
6548 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame
* aFrame
,
6549 nsIFrame
* aFindFrame
,
6550 bool* aFoundValidLine
)
6551 : mFrame(aFrame
), mLineList(&aFrame
->mLines
) {
6552 *aFoundValidLine
= false;
6554 nsIFrame
* child
= FindChildContaining(aFrame
, aFindFrame
);
6559 LineIterator line_end
= aFrame
->LinesEnd();
6560 mLine
= aFrame
->LinesBegin();
6561 if (mLine
!= line_end
&& mLine
.next() == line_end
&&
6562 !aFrame
->HasOverflowLines()) {
6563 // The block has a single line - that must be it!
6564 *aFoundValidLine
= true;
6568 // Try to use the cursor if it exists, otherwise fall back to the first line
6569 if (nsLineBox
* const cursor
= aFrame
->GetLineCursorForQuery()) {
6571 // Perform a simultaneous forward and reverse search starting from the
6573 nsBlockFrame::LineIterator line
= aFrame
->LinesBeginFrom(cursor
);
6574 nsBlockFrame::ReverseLineIterator rline
= aFrame
->LinesRBeginFrom(cursor
);
6575 nsBlockFrame::ReverseLineIterator rline_end
= aFrame
->LinesREnd();
6576 // rline is positioned on the line containing 'cursor', so it's not
6577 // rline_end. So we can safely increment it (i.e. move it to one line
6578 // earlier) to start searching there.
6580 while (line
!= line_end
|| rline
!= rline_end
) {
6581 if (line
!= line_end
) {
6582 if (line
->Contains(child
)) {
6588 if (rline
!= rline_end
) {
6589 if (rline
->Contains(child
)) {
6596 if (mLine
!= line_end
) {
6597 *aFoundValidLine
= true;
6598 if (mLine
!= cursor
) {
6599 aFrame
->SetProperty(nsBlockFrame::LineCursorPropertyQuery(), mLine
);
6604 for (mLine
= aFrame
->LinesBegin(); mLine
!= line_end
; ++mLine
) {
6605 if (mLine
->Contains(child
)) {
6606 *aFoundValidLine
= true;
6611 // Didn't find the line
6612 MOZ_ASSERT(mLine
== line_end
, "mLine should be line_end at this point");
6614 // If we reach here, it means that we have not been able to find the
6615 // desired frame in our in-flow lines. So we should start looking at
6616 // our overflow lines. In order to do that, we set mLine to the end
6617 // iterator so that FindValidLine starts to look at overflow lines,
6620 if (!FindValidLine()) {
6625 if (mLine
->Contains(child
)) {
6626 *aFoundValidLine
= true;
6632 nsBlockFrame::LineIterator
nsBlockInFlowLineIterator::End() {
6633 return mLineList
->end();
6636 bool nsBlockInFlowLineIterator::IsLastLineInList() {
6637 LineIterator end
= End();
6638 return mLine
!= end
&& mLine
.next() == end
;
6641 bool nsBlockInFlowLineIterator::Next() {
6643 return FindValidLine();
6646 bool nsBlockInFlowLineIterator::Prev() {
6647 LineIterator begin
= mLineList
->begin();
6648 if (mLine
!= begin
) {
6652 bool currentlyInOverflowLines
= GetInOverflow();
6654 if (currentlyInOverflowLines
) {
6655 mLineList
= &mFrame
->mLines
;
6656 mLine
= mLineList
->end();
6657 if (mLine
!= mLineList
->begin()) {
6662 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetPrevInFlow());
6666 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6667 if (overflowLines
) {
6668 mLineList
= &overflowLines
->mLines
;
6669 mLine
= mLineList
->end();
6670 NS_ASSERTION(mLine
!= mLineList
->begin(), "empty overflow line list?");
6675 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6679 bool nsBlockInFlowLineIterator::FindValidLine() {
6680 LineIterator end
= mLineList
->end();
6684 bool currentlyInOverflowLines
= GetInOverflow();
6686 if (currentlyInOverflowLines
) {
6687 mFrame
= static_cast<nsBlockFrame
*>(mFrame
->GetNextInFlow());
6691 mLineList
= &mFrame
->mLines
;
6692 mLine
= mLineList
->begin();
6693 if (mLine
!= mLineList
->end()) {
6697 nsBlockFrame::FrameLines
* overflowLines
= mFrame
->GetOverflowLines();
6698 if (overflowLines
) {
6699 mLineList
= &overflowLines
->mLines
;
6700 mLine
= mLineList
->begin();
6701 NS_ASSERTION(mLine
!= mLineList
->end(), "empty overflow line list?");
6705 currentlyInOverflowLines
= !currentlyInOverflowLines
;
6709 // This function removes aDeletedFrame and all its continuations. It
6710 // is optimized for deleting a whole series of frames. The easy
6711 // implementation would invoke itself recursively on
6712 // aDeletedFrame->GetNextContinuation, then locate the line containing
6713 // aDeletedFrame and remove aDeletedFrame from that line. But here we
6714 // start by locating aDeletedFrame and then scanning from that point
6715 // on looking for continuations.
6716 void nsBlockFrame::DoRemoveFrame(DestroyContext
& aContext
,
6717 nsIFrame
* aDeletedFrame
, uint32_t aFlags
) {
6718 // Clear our line cursor, since our lines may change.
6721 if (aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
6722 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6723 if (!aDeletedFrame
->GetPrevInFlow()) {
6724 NS_ASSERTION(aDeletedFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
6725 "Expected out-of-flow frame");
6726 DoRemoveOutOfFlowFrame(aContext
, aDeletedFrame
);
6728 // FIXME(emilio): aContext is lost here, maybe it's not a big deal?
6729 nsContainerFrame::DeleteNextInFlowChild(aContext
, aDeletedFrame
,
6730 (aFlags
& FRAMES_ARE_EMPTY
) != 0);
6735 // Find the line that contains deletedFrame
6736 nsLineList::iterator line_start
= mLines
.begin(), line_end
= mLines
.end();
6737 nsLineList::iterator line
= line_start
;
6738 FrameLines
* overflowLines
= nullptr;
6739 bool searchingOverflowList
= false;
6740 // Make sure we look in the overflow lines even if the normal line
6742 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6744 while (line
!= line_end
) {
6745 if (line
->Contains(aDeletedFrame
)) {
6749 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6753 if (line
== line_end
) {
6754 NS_ERROR("can't find deleted frame in lines");
6758 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6759 if (line
!= line_start
) {
6760 line
.prev()->MarkDirty();
6761 line
.prev()->SetInvalidateTextRuns(true);
6762 } else if (searchingOverflowList
&& !mLines
.empty()) {
6763 mLines
.back()->MarkDirty();
6764 mLines
.back()->SetInvalidateTextRuns(true);
6768 while (line
!= line_end
&& aDeletedFrame
) {
6769 MOZ_ASSERT(this == aDeletedFrame
->GetParent(), "messed up delete code");
6770 MOZ_ASSERT(line
->Contains(aDeletedFrame
), "frame not in line");
6772 if (!(aFlags
& FRAMES_ARE_EMPTY
)) {
6774 line
->SetInvalidateTextRuns(true);
6777 // If the frame being deleted is the last one on the line then
6778 // optimize away the line->Contains(next-in-flow) call below.
6779 bool isLastFrameOnLine
= 1 == line
->GetChildCount();
6780 if (!isLastFrameOnLine
) {
6781 LineIterator next
= line
.next();
6782 nsIFrame
* lastFrame
=
6784 ? next
->mFirstChild
->GetPrevSibling()
6785 : (searchingOverflowList
? overflowLines
->mFrames
.LastChild()
6786 : mFrames
.LastChild());
6787 NS_ASSERTION(next
== line_end
|| lastFrame
== line
->LastChild(),
6788 "unexpected line frames");
6789 isLastFrameOnLine
= lastFrame
== aDeletedFrame
;
6792 // Remove aDeletedFrame from the line
6793 if (line
->mFirstChild
== aDeletedFrame
) {
6794 // We should be setting this to null if aDeletedFrame
6795 // is the only frame on the line. HOWEVER in that case
6796 // we will be removing the line anyway, see below.
6797 line
->mFirstChild
= aDeletedFrame
->GetNextSibling();
6800 // Hmm, this won't do anything if we're removing a frame in the first
6801 // overflow line... Hopefully doesn't matter
6803 if (line
!= line_end
&& !line
->IsBlock()) {
6804 // Since we just removed a frame that follows some inline
6805 // frames, we need to reflow the previous line.
6810 // Take aDeletedFrame out of the sibling list. Note that
6811 // prevSibling will only be nullptr when we are deleting the very
6812 // first frame in the main or overflow list.
6813 if (searchingOverflowList
) {
6814 overflowLines
->mFrames
.RemoveFrame(aDeletedFrame
);
6816 mFrames
.RemoveFrame(aDeletedFrame
);
6819 // Update the child count of the line to be accurate
6820 line
->NoteFrameRemoved(aDeletedFrame
);
6822 // Destroy frame; capture its next continuation first in case we need
6823 // to destroy that too.
6824 nsIFrame
* deletedNextContinuation
=
6825 (aFlags
& REMOVE_FIXED_CONTINUATIONS
)
6826 ? aDeletedFrame
->GetNextContinuation()
6827 : aDeletedFrame
->GetNextInFlow();
6828 #ifdef NOISY_REMOVE_FRAME
6829 printf("DoRemoveFrame: %s line=%p frame=",
6830 searchingOverflowList
? "overflow" : "normal", line
.get());
6831 aDeletedFrame
->ListTag(stdout
);
6832 printf(" prevSibling=%p deletedNextContinuation=%p\n",
6833 aDeletedFrame
->GetPrevSibling(), deletedNextContinuation
);
6836 // If next-in-flow is an overflow container, must remove it first.
6837 // FIXME: Can we do this unconditionally?
6838 if (deletedNextContinuation
&& deletedNextContinuation
->HasAnyStateBits(
6839 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
6840 deletedNextContinuation
->GetParent()->DeleteNextInFlowChild(
6841 aContext
, deletedNextContinuation
, false);
6842 deletedNextContinuation
= nullptr;
6845 aDeletedFrame
->Destroy(aContext
);
6846 aDeletedFrame
= deletedNextContinuation
;
6848 bool haveAdvancedToNextLine
= false;
6849 // If line is empty, remove it now.
6850 if (0 == line
->GetChildCount()) {
6851 #ifdef NOISY_REMOVE_FRAME
6852 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6853 searchingOverflowList
? "overflow" : "normal", line
.get());
6855 nsLineBox
* cur
= line
;
6856 if (!searchingOverflowList
) {
6857 line
= mLines
.erase(line
);
6858 // Invalidate the space taken up by the line.
6859 // XXX We need to do this if we're removing a frame as a result of
6860 // a call to RemoveFrame(), but we may not need to do this in all
6862 #ifdef NOISY_BLOCK_INVALIDATE
6863 nsRect
inkOverflow(cur
->InkOverflowRect());
6864 printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow
.x
,
6865 inkOverflow
.y
, inkOverflow
.width
, inkOverflow
.height
);
6868 line
= overflowLines
->mLines
.erase(line
);
6869 if (overflowLines
->mLines
.empty()) {
6870 DestroyOverflowLines();
6871 overflowLines
= nullptr;
6872 // We just invalidated our iterators. Since we were in
6873 // the overflow lines list, which is now empty, set them
6874 // so we're at the end of the regular line list.
6875 line_start
= mLines
.begin();
6876 line_end
= mLines
.end();
6882 // If we're removing a line, ReflowDirtyLines isn't going to
6883 // know that it needs to slide lines unless something is marked
6884 // dirty. So mark the previous margin of the next line dirty if
6886 if (line
!= line_end
) {
6887 line
->MarkPreviousMarginDirty();
6889 haveAdvancedToNextLine
= true;
6891 // Make the line that just lost a frame dirty, and advance to
6893 if (!deletedNextContinuation
|| isLastFrameOnLine
||
6894 !line
->Contains(deletedNextContinuation
)) {
6897 haveAdvancedToNextLine
= true;
6901 if (deletedNextContinuation
) {
6902 // See if we should keep looking in the current flow's line list.
6903 if (deletedNextContinuation
->GetParent() != this) {
6904 // The deceased frames continuation is not a child of the
6905 // current block. So break out of the loop so that we advance
6906 // to the next parent.
6908 // If we have a continuation in a different block then all bets are
6909 // off regarding whether we are deleting frames without actual content,
6910 // so don't propagate FRAMES_ARE_EMPTY any further.
6911 aFlags
&= ~FRAMES_ARE_EMPTY
;
6915 // If we advanced to the next line then check if we should switch to the
6916 // overflow line list.
6917 if (haveAdvancedToNextLine
) {
6918 if (line
!= line_end
&& !searchingOverflowList
&&
6919 !line
->Contains(deletedNextContinuation
)) {
6920 // We have advanced to the next *normal* line but the next-in-flow
6921 // is not there - force a switch to the overflow line list.
6925 TryAllLines(&line
, &line_start
, &line_end
, &searchingOverflowList
,
6927 #ifdef NOISY_REMOVE_FRAME
6928 printf("DoRemoveFrame: now on %s line=%p\n",
6929 searchingOverflowList
? "overflow" : "normal", line
.get());
6935 if (!(aFlags
& FRAMES_ARE_EMPTY
) && line
.next() != line_end
) {
6936 line
.next()->MarkDirty();
6937 line
.next()->SetInvalidateTextRuns(true);
6942 VerifyOverflowSituation();
6945 // Advance to next flow block if the frame has more continuations.
6946 if (!aDeletedFrame
) {
6949 nsBlockFrame
* nextBlock
= do_QueryFrame(aDeletedFrame
->GetParent());
6950 NS_ASSERTION(nextBlock
, "Our child's continuation's parent is not a block?");
6951 uint32_t flags
= (aFlags
& REMOVE_FIXED_CONTINUATIONS
);
6952 nextBlock
->DoRemoveFrame(aContext
, aDeletedFrame
, flags
);
6955 static bool FindBlockLineFor(nsIFrame
* aChild
, nsLineList::iterator aBegin
,
6956 nsLineList::iterator aEnd
,
6957 nsLineList::iterator
* aResult
) {
6958 MOZ_ASSERT(aChild
->IsBlockOutside());
6959 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6960 MOZ_ASSERT(line
->GetChildCount() > 0);
6961 if (line
->IsBlock() && line
->mFirstChild
== aChild
) {
6962 MOZ_ASSERT(line
->GetChildCount() == 1);
6970 static bool FindInlineLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6971 nsLineList::iterator aBegin
,
6972 nsLineList::iterator aEnd
,
6973 nsLineList::iterator
* aResult
) {
6974 MOZ_ASSERT(!aChild
->IsBlockOutside());
6975 for (nsLineList::iterator line
= aBegin
; line
!= aEnd
; ++line
) {
6976 MOZ_ASSERT(line
->GetChildCount() > 0);
6977 if (!line
->IsBlock()) {
6978 // Optimize by comparing the line's last child first.
6979 nsLineList::iterator next
= line
.next();
6980 if (aChild
== (next
== aEnd
? aFrameList
.LastChild()
6981 : next
->mFirstChild
->GetPrevSibling()) ||
6982 line
->Contains(aChild
)) {
6991 static bool FindLineFor(nsIFrame
* aChild
, const nsFrameList
& aFrameList
,
6992 nsLineList::iterator aBegin
, nsLineList::iterator aEnd
,
6993 nsLineList::iterator
* aResult
) {
6994 return aChild
->IsBlockOutside()
6995 ? FindBlockLineFor(aChild
, aBegin
, aEnd
, aResult
)
6996 : FindInlineLineFor(aChild
, aFrameList
, aBegin
, aEnd
, aResult
);
6999 void nsBlockFrame::StealFrame(nsIFrame
* aChild
) {
7000 MOZ_ASSERT(aChild
->GetParent() == this);
7002 if (aChild
->IsFloating()) {
7003 RemoveFloat(aChild
);
7007 if (MaybeStealOverflowContainerFrame(aChild
)) {
7011 MOZ_ASSERT(!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
7013 nsLineList::iterator line
;
7014 if (FindLineFor(aChild
, mFrames
, mLines
.begin(), mLines
.end(), &line
)) {
7015 RemoveFrameFromLine(aChild
, line
, mFrames
, mLines
);
7017 FrameLines
* overflowLines
= GetOverflowLines();
7018 DebugOnly
<bool> found
;
7019 found
= FindLineFor(aChild
, overflowLines
->mFrames
,
7020 overflowLines
->mLines
.begin(),
7021 overflowLines
->mLines
.end(), &line
);
7022 MOZ_ASSERT(found
, "Why can't we find aChild in our overflow lines?");
7023 RemoveFrameFromLine(aChild
, line
, overflowLines
->mFrames
,
7024 overflowLines
->mLines
);
7025 if (overflowLines
->mLines
.empty()) {
7026 DestroyOverflowLines();
7031 void nsBlockFrame::RemoveFrameFromLine(nsIFrame
* aChild
,
7032 nsLineList::iterator aLine
,
7033 nsFrameList
& aFrameList
,
7034 nsLineList
& aLineList
) {
7035 aFrameList
.RemoveFrame(aChild
);
7036 if (aChild
== aLine
->mFirstChild
) {
7037 aLine
->mFirstChild
= aChild
->GetNextSibling();
7039 aLine
->NoteFrameRemoved(aChild
);
7040 if (aLine
->GetChildCount() > 0) {
7043 // The line became empty - destroy it.
7044 nsLineBox
* lineBox
= aLine
;
7045 aLine
= aLineList
.erase(aLine
);
7046 if (aLine
!= aLineList
.end()) {
7047 aLine
->MarkPreviousMarginDirty();
7049 FreeLineBox(lineBox
);
7053 void nsBlockFrame::DeleteNextInFlowChild(DestroyContext
& aContext
,
7054 nsIFrame
* aNextInFlow
,
7055 bool aDeletingEmptyFrames
) {
7056 MOZ_ASSERT(aNextInFlow
->GetPrevInFlow(), "bad next-in-flow");
7058 if (aNextInFlow
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
|
7059 NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
7060 nsContainerFrame::DeleteNextInFlowChild(aContext
, aNextInFlow
,
7061 aDeletingEmptyFrames
);
7064 if (aDeletingEmptyFrames
) {
7065 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow
);
7068 DoRemoveFrame(aContext
, aNextInFlow
,
7069 aDeletingEmptyFrames
? FRAMES_ARE_EMPTY
: 0);
7073 const nsStyleText
* nsBlockFrame::StyleTextForLineLayout() {
7074 // Return the pointer to an unmodified style text
7078 void nsBlockFrame::ReflowFloat(BlockReflowState
& aState
, ReflowInput
& aFloatRI
,
7080 nsReflowStatus
& aReflowStatus
) {
7081 MOZ_ASSERT(aReflowStatus
.IsEmpty(),
7082 "Caller should pass a fresh reflow status!");
7083 MOZ_ASSERT(aFloat
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
7084 "aFloat must be an out-of-flow frame");
7086 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
7088 // Setup a block reflow context to reflow the float.
7089 nsBlockReflowContext
brc(aState
.mPresContext
, aState
.mReflowInput
);
7091 nsIFrame
* clearanceFrame
= nullptr;
7093 nsCollapsingMargin margin
;
7094 bool mayNeedRetry
= false;
7095 aFloatRI
.mDiscoveredClearance
= nullptr;
7096 // Only first in flow gets a block-start margin.
7097 if (!aFloat
->GetPrevInFlow()) {
7098 brc
.ComputeCollapsedBStartMargin(aFloatRI
, &margin
, clearanceFrame
,
7101 if (mayNeedRetry
&& !clearanceFrame
) {
7102 aFloatRI
.mDiscoveredClearance
= &clearanceFrame
;
7103 // We don't need to push the float manager state because the the block
7104 // has its own float manager that will be destroyed and recreated
7108 // When reflowing a float, aSpace argument doesn't matter because we pass
7109 // nullptr to aLine and we don't call nsBlockReflowContext::PlaceBlock()
7111 brc
.ReflowBlock(LogicalRect(wm
), true, margin
, 0, nullptr, aFloatRI
,
7112 aReflowStatus
, aState
);
7113 } while (clearanceFrame
);
7115 if (aFloat
->IsLetterFrame()) {
7116 // We never split floating first letters; an incomplete status for such
7117 // frames simply means that there is more content to be reflowed on the
7119 if (aReflowStatus
.IsIncomplete()) {
7120 aReflowStatus
.Reset();
7124 NS_ASSERTION(aReflowStatus
.IsFullyComplete() ||
7125 aFloatRI
.AvailableBSize() != NS_UNCONSTRAINEDSIZE
,
7126 "The status can only be incomplete or overflow-incomplete if "
7127 "the available block-size is constrained!");
7129 if (aReflowStatus
.NextInFlowNeedsReflow()) {
7130 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
7133 const ReflowOutput
& metrics
= brc
.GetMetrics();
7135 // Set the rect, make sure the view is properly sized and positioned,
7136 // and tell the frame we're done reflowing it
7137 // XXXldb This seems like the wrong place to be doing this -- shouldn't
7138 // we be doing this in BlockReflowState::FlowAndPlaceFloat after
7139 // we've positioned the float, and shouldn't we be doing the equivalent
7140 // of |PlaceFrameView| here?
7141 WritingMode metricsWM
= metrics
.GetWritingMode();
7142 aFloat
->SetSize(metricsWM
, metrics
.Size(metricsWM
));
7143 if (aFloat
->HasView()) {
7144 nsContainerFrame::SyncFrameViewAfterReflow(
7145 aState
.mPresContext
, aFloat
, aFloat
->GetView(), metrics
.InkOverflow(),
7146 ReflowChildFlags::NoMoveView
);
7148 aFloat
->DidReflow(aState
.mPresContext
, &aFloatRI
);
7151 StyleClear
nsBlockFrame::FindTrailingClear() {
7152 for (nsBlockFrame
* b
= this; b
;
7153 b
= static_cast<nsBlockFrame
*>(b
->GetPrevInFlow())) {
7154 auto endLine
= b
->LinesRBegin();
7155 if (endLine
!= b
->LinesREnd()) {
7156 return endLine
->FloatClearTypeAfter();
7159 return StyleClear::None
;
7162 void nsBlockFrame::ReflowPushedFloats(BlockReflowState
& aState
,
7163 OverflowAreas
& aOverflowAreas
) {
7164 // Pushed floats live at the start of our float list; see comment
7165 // above nsBlockFrame::DrainPushedFloats.
7166 nsIFrame
* f
= mFloats
.FirstChild();
7167 nsIFrame
* prev
= nullptr;
7168 while (f
&& f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7169 MOZ_ASSERT(prev
== f
->GetPrevSibling());
7170 // When we push a first-continuation float in a non-initial reflow,
7171 // it's possible that we end up with two continuations with the same
7172 // parent. This happens if, on the previous reflow of the block or
7173 // a previous reflow of the line containing the block, the float was
7174 // split between continuations A and B of the parent, but on the
7175 // current reflow, none of the float can fit in A.
7177 // When this happens, we might even have the two continuations
7178 // out-of-order due to the management of the pushed floats. In
7179 // particular, if the float's placeholder was in a pushed line that
7180 // we reflowed before it was pushed, and we split the float during
7181 // that reflow, we might have the continuation of the float before
7182 // the float itself. (In the general case, however, it's correct
7183 // for floats in the pushed floats list to come before floats
7184 // anchored in pushed lines; however, in this case it's wrong. We
7185 // should probably find a way to fix it somehow, since it leads to
7186 // incorrect layout in some cases.)
7188 // When we have these out-of-order continuations, we might hit the
7189 // next-continuation before the previous-continuation. When that
7190 // happens, just push it. When we reflow the next continuation,
7191 // we'll either pull all of its content back and destroy it (by
7192 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
7193 // pull it out of its current position and push it again (and
7194 // potentially repeat this cycle for the next continuation, although
7195 // hopefully then they'll be in the right order).
7197 // We should also need this code for the in-order case if the first
7198 // continuation of a float gets moved across more than one
7199 // continuation of the containing block. In this case we'd manage
7200 // to push the second continuation without this check, but not the
7202 nsIFrame
* prevContinuation
= f
->GetPrevContinuation();
7203 if (prevContinuation
&& prevContinuation
->GetParent() == f
->GetParent()) {
7204 mFloats
.RemoveFrame(f
);
7205 aState
.AppendPushedFloatChain(f
);
7206 f
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7210 // Always call FlowAndPlaceFloat; we might need to place this float if it
7211 // didn't belong to this block the last time it was reflowed. Note that if
7212 // the float doesn't get placed, we don't consider its overflow areas.
7213 // (Not-getting-placed means it didn't fit and we pushed it instead of
7214 // placing it, and its position could be stale.)
7215 if (aState
.FlowAndPlaceFloat(f
) ==
7216 BlockReflowState::PlaceFloatResult::Placed
) {
7217 ConsiderChildOverflow(aOverflowAreas
, f
);
7220 nsIFrame
* next
= !prev
? mFloats
.FirstChild() : prev
->GetNextSibling();
7222 // We didn't push |f| so its next-sibling is next.
7223 next
= f
->GetNextSibling();
7225 } // else: we did push |f| so |prev|'s new next-sibling is next.
7229 // If there are pushed or split floats, then we may need to continue BR
7231 if (auto [bCoord
, result
] = aState
.ClearFloats(0, StyleClear::Both
);
7232 result
!= ClearFloatsResult::BCoordNoChange
) {
7234 if (auto* prevBlock
= static_cast<nsBlockFrame
*>(GetPrevInFlow())) {
7235 aState
.mTrailingClearFromPIF
= prevBlock
->FindTrailingClear();
7240 void nsBlockFrame::RecoverFloats(nsFloatManager
& aFloatManager
, WritingMode aWM
,
7241 const nsSize
& aContainerSize
) {
7242 // Recover our own floats
7243 nsIFrame
* stop
= nullptr; // Stop before we reach pushed floats that
7244 // belong to our next-in-flow
7245 for (nsIFrame
* f
= mFloats
.FirstChild(); f
&& f
!= stop
;
7246 f
= f
->GetNextSibling()) {
7247 LogicalRect region
= nsFloatManager::GetRegionFor(aWM
, f
, aContainerSize
);
7248 aFloatManager
.AddFloat(f
, region
, aWM
, aContainerSize
);
7249 if (!stop
&& f
->GetNextInFlow()) {
7250 stop
= f
->GetNextInFlow();
7254 // Recurse into our overflow container children
7256 GetChildList(FrameChildListID::OverflowContainers
).FirstChild();
7257 oc
; oc
= oc
->GetNextSibling()) {
7258 RecoverFloatsFor(oc
, aFloatManager
, aWM
, aContainerSize
);
7261 // Recurse into our normal children
7262 for (const auto& line
: Lines()) {
7263 if (line
.IsBlock()) {
7264 RecoverFloatsFor(line
.mFirstChild
, aFloatManager
, aWM
, aContainerSize
);
7269 void nsBlockFrame::RecoverFloatsFor(nsIFrame
* aFrame
,
7270 nsFloatManager
& aFloatManager
,
7272 const nsSize
& aContainerSize
) {
7273 MOZ_ASSERT(aFrame
, "null frame");
7275 // Only blocks have floats
7276 nsBlockFrame
* block
= do_QueryFrame(aFrame
);
7277 // Don't recover any state inside a block that has its own float manager
7278 // (we don't currently have any blocks like this, though, thanks to our
7279 // use of extra frames for 'overflow')
7280 if (block
&& !nsBlockFrame::BlockNeedsFloatManager(block
)) {
7281 // If the element is relatively positioned, then adjust x and y
7282 // accordingly so that we consider relatively positioned frames
7283 // at their original position.
7285 const LogicalRect rect
= block
->GetLogicalNormalRect(aWM
, aContainerSize
);
7286 nscoord lineLeft
= rect
.LineLeft(aWM
, aContainerSize
);
7287 nscoord blockStart
= rect
.BStart(aWM
);
7288 aFloatManager
.Translate(lineLeft
, blockStart
);
7289 block
->RecoverFloats(aFloatManager
, aWM
, aContainerSize
);
7290 aFloatManager
.Translate(-lineLeft
, -blockStart
);
7294 bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
7295 if (!mFloats
.IsEmpty()) {
7296 // If we have pushed floats, then they should be at the beginning of our
7298 if (mFloats
.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7304 // Double-check the above assertion that pushed floats should be at the
7305 // beginning of our floats list.
7306 for (nsIFrame
* f
: mFloats
) {
7307 NS_ASSERTION(!f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
),
7308 "pushed floats must be at the beginning of the float list");
7312 // We may have a pending push of pushed floats too:
7313 if (HasPushedFloats()) {
7314 // XXX we can return 'true' here once we make HasPushedFloats
7315 // not lie. (see nsBlockFrame::RemoveFloat)
7316 auto* pushedFloats
= GetPushedFloats();
7317 return pushedFloats
&& !pushedFloats
->IsEmpty();
7322 //////////////////////////////////////////////////////////////////////
7323 // Painting, event handling
7326 static void ComputeInkOverflowArea(nsLineList
& aLines
, nscoord aWidth
,
7327 nscoord aHeight
, nsRect
& aResult
) {
7328 nscoord xa
= 0, ya
= 0, xb
= aWidth
, yb
= aHeight
;
7329 for (nsLineList::iterator line
= aLines
.begin(), line_end
= aLines
.end();
7330 line
!= line_end
; ++line
) {
7331 // Compute min and max x/y values for the reflowed frame's
7333 nsRect
inkOverflow(line
->InkOverflowRect());
7334 nscoord x
= inkOverflow
.x
;
7335 nscoord y
= inkOverflow
.y
;
7336 nscoord xmost
= x
+ inkOverflow
.width
;
7337 nscoord ymost
= y
+ inkOverflow
.height
;
7354 aResult
.width
= xb
- xa
;
7355 aResult
.height
= yb
- ya
;
7360 static void DebugOutputDrawLine(int32_t aDepth
, nsLineBox
* aLine
, bool aDrawn
) {
7361 if (nsBlockFrame::gNoisyDamageRepair
) {
7362 nsIFrame::IndentBy(stdout
, aDepth
+ 1);
7363 nsRect lineArea
= aLine
->InkOverflowRect();
7364 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7365 aDrawn
? "draw" : "skip", static_cast<void*>(aLine
), aLine
->IStart(),
7366 aLine
->BStart(), aLine
->ISize(), aLine
->BSize(), lineArea
.x
,
7367 lineArea
.y
, lineArea
.width
, lineArea
.height
);
7372 static void DisplayLine(nsDisplayListBuilder
* aBuilder
,
7373 nsBlockFrame::LineIterator
& aLine
,
7374 const bool aLineInLine
, const nsDisplayListSet
& aLists
,
7375 nsBlockFrame
* aFrame
, TextOverflow
* aTextOverflow
,
7376 uint32_t aLineNumberForTextOverflow
, int32_t aDepth
,
7377 int32_t& aDrawnLines
) {
7379 if (nsBlockFrame::gLamePaintMetrics
) {
7382 const bool intersect
=
7383 aLine
->InkOverflowRect().Intersects(aBuilder
->GetDirtyRect());
7384 DebugOutputDrawLine(aDepth
, aLine
.get(), intersect
);
7387 // Collect our line's display items in a temporary nsDisplayListCollection,
7388 // so that we can apply any "text-overflow" clipping to the entire collection
7389 // without affecting previous lines.
7390 nsDisplayListCollection
collection(aBuilder
);
7392 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
7393 // Inline-level child backgrounds go on the regular child content list.
7394 nsDisplayListSet
childLists(
7396 aLineInLine
? collection
.Content() : collection
.BlockBorderBackgrounds());
7400 ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline
)
7401 : nsIFrame::DisplayChildFlags();
7403 nsIFrame
* kid
= aLine
->mFirstChild
;
7404 int32_t n
= aLine
->GetChildCount();
7406 aFrame
->BuildDisplayListForChild(aBuilder
, kid
, childLists
, flags
);
7407 kid
= kid
->GetNextSibling();
7410 if (aTextOverflow
&& aLineInLine
) {
7411 aTextOverflow
->ProcessLine(collection
, aLine
.get(),
7412 aLineNumberForTextOverflow
);
7415 collection
.MoveTo(aLists
);
7418 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
7419 const nsDisplayListSet
& aLists
) {
7420 int32_t drawnLines
; // Will only be used if set (gLamePaintMetrics).
7423 if (gNoisyDamageRepair
) {
7424 nsRect dirty
= aBuilder
->GetDirtyRect();
7427 ::ComputeInkOverflowArea(mLines
, mRect
.width
, mRect
.height
, ca
);
7428 nsIFrame::IndentBy(stdout
, depth
);
7430 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
7431 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
, dirty
.x
, dirty
.y
,
7432 dirty
.width
, dirty
.height
, ca
.x
, ca
.y
, ca
.width
, ca
.height
);
7434 PRTime start
= 0; // Initialize these variables to silence the compiler.
7435 if (gLamePaintMetrics
) {
7441 // TODO(heycam): Should we boost the load priority of any shape-outside
7442 // images using CATEGORY_DISPLAY, now that this block is being displayed?
7443 // We don't have a float manager here.
7445 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
7447 if (GetPrevInFlow()) {
7448 DisplayOverflowContainers(aBuilder
, aLists
);
7449 for (nsIFrame
* f
: mFloats
) {
7450 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
7451 BuildDisplayListForChild(aBuilder
, f
, aLists
);
7456 aBuilder
->MarkFramesForDisplayList(this, mFloats
);
7458 if (HasOutsideMarker()) {
7459 // Display outside ::marker manually.
7460 BuildDisplayListForChild(aBuilder
, GetOutsideMarker(), aLists
);
7463 // Prepare for text-overflow processing.
7464 Maybe
<TextOverflow
> textOverflow
=
7465 TextOverflow::WillProcessLines(aBuilder
, this);
7467 const bool hasDescendantPlaceHolders
=
7468 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7469 ForceDescendIntoIfVisible() || aBuilder
->GetIncludeAllOutOfFlows();
7471 const auto ShouldDescendIntoLine
= [&](const nsRect
& aLineArea
) -> bool {
7472 // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
7473 // some frame trees, building display list for child lines can change it.
7475 const bool descendAlways
=
7476 HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
) ||
7477 aBuilder
->GetIncludeAllOutOfFlows();
7479 return descendAlways
|| aLineArea
.Intersects(aBuilder
->GetDirtyRect()) ||
7480 (ForceDescendIntoIfVisible() &&
7481 aLineArea
.Intersects(aBuilder
->GetVisibleRect()));
7484 Maybe
<nscolor
> backplateColor
;
7486 // We'll try to draw an accessibility backplate behind text (to ensure it's
7487 // readable over any possible background-images), if all of the following
7489 // (A) the backplate feature is preffed on
7490 // (B) we are not honoring the document colors
7491 // (C) the force color adjust property is set to auto
7492 if (StaticPrefs::browser_display_permit_backplate() &&
7493 PresContext()->ForcingColors() && !IsComboboxControlFrame() &&
7494 StyleText()->mForcedColorAdjust
!= StyleForcedColorAdjust::None
) {
7495 backplateColor
.emplace(GetBackplateColor(this));
7498 // Don't use the line cursor if we might have a descendant placeholder ...
7499 // it might skip lines that contain placeholders but don't themselves
7500 // intersect with the dirty area.
7501 // In particular, we really want to check ShouldDescendIntoFrame()
7502 // on all our child frames, but that might be expensive. So we
7503 // approximate it by checking it on |this|; if it's true for any
7504 // frame in our child list, it's also true for |this|.
7505 // Also skip the cursor if we're creating text overflow markers,
7506 // since we need to know what line number we're up to in order
7507 // to generate unique display item keys.
7508 // Lastly, the cursor should be skipped if we're drawing
7509 // backplates behind text. When backplating we consider consecutive
7510 // runs of text as a whole, which requires we iterate through all lines
7511 // to find our backplate size.
7513 (hasDescendantPlaceHolders
|| textOverflow
.isSome() || backplateColor
)
7515 : GetFirstLineContaining(aBuilder
->GetDirtyRect().y
);
7516 LineIterator line_end
= LinesEnd();
7518 TextOverflow
* textOverflowPtr
= textOverflow
.ptrOr(nullptr);
7521 for (LineIterator line
= mLines
.begin(cursor
); line
!= line_end
; ++line
) {
7522 const nsRect lineArea
= line
->InkOverflowRect();
7523 if (!lineArea
.IsEmpty()) {
7524 // Because we have a cursor, the combinedArea.ys are non-decreasing.
7525 // Once we've passed aDirtyRect.YMost(), we can never see it again.
7526 if (lineArea
.y
>= aBuilder
->GetDirtyRect().YMost()) {
7529 MOZ_ASSERT(textOverflow
.isNothing());
7531 if (ShouldDescendIntoLine(lineArea
)) {
7532 DisplayLine(aBuilder
, line
, line
->IsInline(), aLists
, this, nullptr,
7533 0, depth
, drawnLines
);
7538 bool nonDecreasingYs
= true;
7539 uint32_t lineCount
= 0;
7540 nscoord lastY
= INT32_MIN
;
7541 nscoord lastYMost
= INT32_MIN
;
7543 // A frame's display list cannot contain more than one copy of a
7544 // given display item unless the items are uniquely identifiable.
7545 // Because backplate occasionally requires multiple
7546 // SolidColor items, we use an index (backplateIndex) to maintain
7547 // uniqueness among them. Note this is a mapping of index to
7548 // item, and the mapping is stable even if the dirty rect changes.
7549 uint16_t backplateIndex
= 0;
7550 nsRect curBackplateArea
;
7552 auto AddBackplate
= [&]() {
7553 aLists
.BorderBackground()->AppendNewToTopWithIndex
<nsDisplaySolidColor
>(
7554 aBuilder
, this, backplateIndex
, curBackplateArea
,
7555 backplateColor
.value());
7558 for (LineIterator line
= LinesBegin(); line
!= line_end
; ++line
) {
7559 const nsRect lineArea
= line
->InkOverflowRect();
7560 const bool lineInLine
= line
->IsInline();
7562 if ((lineInLine
&& textOverflowPtr
) || ShouldDescendIntoLine(lineArea
)) {
7563 DisplayLine(aBuilder
, line
, lineInLine
, aLists
, this, textOverflowPtr
,
7564 lineCount
, depth
, drawnLines
);
7567 if (!lineInLine
&& !curBackplateArea
.IsEmpty()) {
7568 // If we have encountered a non-inline line but were previously
7569 // forming a backplate, we should add the backplate to the display
7570 // list as-is and render future backplates disjointly.
7571 MOZ_ASSERT(backplateColor
,
7572 "if this master switch is off, curBackplateArea "
7573 "must be empty and we shouldn't get here");
7576 curBackplateArea
= nsRect();
7579 if (!lineArea
.IsEmpty()) {
7580 if (lineArea
.y
< lastY
|| lineArea
.YMost() < lastYMost
) {
7581 nonDecreasingYs
= false;
7584 lastYMost
= lineArea
.YMost();
7585 if (lineInLine
&& backplateColor
&& LineHasVisibleInlineContent(line
)) {
7586 nsRect lineBackplate
= GetLineTextArea(line
, aBuilder
) +
7587 aBuilder
->ToReferenceFrame(this);
7588 if (curBackplateArea
.IsEmpty()) {
7589 curBackplateArea
= lineBackplate
;
7591 curBackplateArea
.OrWith(lineBackplate
);
7598 if (nonDecreasingYs
&& lineCount
>= MIN_LINES_NEEDING_CURSOR
) {
7599 SetupLineCursorForDisplay();
7602 if (!curBackplateArea
.IsEmpty()) {
7607 if (textOverflow
.isSome()) {
7608 // Put any text-overflow:ellipsis markers on top of the non-positioned
7609 // content of the block's lines. (If we ever start sorting the Content()
7610 // list this will end up in the wrong place.)
7611 aLists
.Content()->AppendToTop(&textOverflow
->GetMarkers());
7615 if (gLamePaintMetrics
) {
7616 PRTime end
= PR_Now();
7618 int32_t numLines
= mLines
.size();
7622 PRTime lines
, deltaPerLine
, delta
;
7623 lines
= int64_t(numLines
);
7624 delta
= end
- start
;
7625 deltaPerLine
= delta
/ lines
;
7630 ": %" PRId64
" elapsed (%" PRId64
7631 " per line) lines=%d drawn=%d skip=%d",
7632 delta
, deltaPerLine
, numLines
, drawnLines
,
7633 numLines
- drawnLines
);
7634 printf("%s\n", buf
);
7639 #ifdef ACCESSIBILITY
7640 a11y::AccType
nsBlockFrame::AccessibleType() {
7641 if (IsTableCaption()) {
7642 return GetRect().IsEmpty() ? a11y::eNoType
: a11y::eHTMLCaptionType
;
7645 // block frame may be for <hr>
7646 if (mContent
->IsHTMLElement(nsGkAtoms::hr
)) {
7647 return a11y::eHTMLHRType
;
7650 if (!HasMarker() || !PresContext()) {
7651 // XXXsmaug What if we're in the shadow dom?
7652 if (!mContent
->GetParent()) {
7653 // Don't create accessible objects for the root content node, they are
7654 // redundant with the nsDocAccessible object created with the document
7656 return a11y::eNoType
;
7659 if (mContent
== mContent
->OwnerDoc()->GetBody()) {
7660 // Don't create accessible objects for the body, they are redundant with
7661 // the nsDocAccessible object created with the document node
7662 return a11y::eNoType
;
7665 // Not a list item with a ::marker, treat as normal HTML container.
7666 return a11y::eHyperTextType
;
7669 // Create special list item accessible since we have a ::marker.
7670 return a11y::eHTMLLiType
;
7674 void nsBlockFrame::SetupLineCursorForDisplay() {
7675 if (mLines
.empty() || HasProperty(LineCursorPropertyDisplay())) {
7679 SetProperty(LineCursorPropertyDisplay(), mLines
.front());
7680 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7683 void nsBlockFrame::SetupLineCursorForQuery() {
7684 if (mLines
.empty() || HasProperty(LineCursorPropertyQuery())) {
7688 SetProperty(LineCursorPropertyQuery(), mLines
.front());
7689 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR
);
7692 nsLineBox
* nsBlockFrame::GetFirstLineContaining(nscoord y
) {
7693 // Although this looks like a "querying" method, it is used by the
7694 // display-list building code, so uses the Display cursor.
7695 nsLineBox
* property
= GetLineCursorForDisplay();
7699 LineIterator cursor
= mLines
.begin(property
);
7700 nsRect cursorArea
= cursor
->InkOverflowRect();
7702 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() > y
) &&
7703 cursor
!= mLines
.front()) {
7704 cursor
= cursor
.prev();
7705 cursorArea
= cursor
->InkOverflowRect();
7707 while ((cursorArea
.IsEmpty() || cursorArea
.YMost() <= y
) &&
7708 cursor
!= mLines
.back()) {
7709 cursor
= cursor
.next();
7710 cursorArea
= cursor
->InkOverflowRect();
7713 if (cursor
.get() != property
) {
7714 SetProperty(LineCursorPropertyDisplay(), cursor
.get());
7717 return cursor
.get();
7721 void nsBlockFrame::ChildIsDirty(nsIFrame
* aChild
) {
7722 // See if the child is absolutely positioned
7723 if (aChild
->IsAbsolutelyPositioned()) {
7725 } else if (aChild
== GetOutsideMarker()) {
7726 // The ::marker lives in the first line, unless the first line has
7727 // height 0 and there is a second line, in which case it lives
7728 // in the second line.
7729 LineIterator markerLine
= LinesBegin();
7730 if (markerLine
!= LinesEnd() && markerLine
->BSize() == 0 &&
7731 markerLine
!= mLines
.back()) {
7732 markerLine
= markerLine
.next();
7735 if (markerLine
!= LinesEnd()) {
7736 MarkLineDirty(markerLine
, &mLines
);
7738 // otherwise we have an empty line list, and ReflowDirtyLines
7739 // will handle reflowing the ::marker.
7741 // Note that we should go through our children to mark lines dirty
7742 // before the next reflow. Doing it now could make things O(N^2)
7743 // since finding the right line is O(N).
7744 // We don't need to worry about marking lines on the overflow list
7745 // as dirty; we're guaranteed to reflow them if we take them off the
7747 // However, we might have gotten a float, in which case we need to
7748 // reflow the line containing its placeholder. So find the
7749 // ancestor-or-self of the placeholder that's a child of the block,
7750 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
7751 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7752 // We need to take some care to handle the case where a float is in
7753 // a different continuation than its placeholder, including marking
7754 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7755 if (!aChild
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
7756 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7758 NS_ASSERTION(aChild
->IsFloating(), "should be a float");
7759 nsIFrame
* thisFC
= FirstContinuation();
7760 nsIFrame
* placeholderPath
= aChild
->GetPlaceholderFrame();
7761 // SVG code sometimes sends FrameNeedsReflow notifications during
7762 // frame destruction, leading to null placeholders, but we're safe
7764 if (placeholderPath
) {
7766 nsIFrame
* parent
= placeholderPath
->GetParent();
7767 if (parent
->GetContent() == mContent
&&
7768 parent
->FirstContinuation() == thisFC
) {
7769 parent
->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES
);
7772 placeholderPath
= parent
;
7774 placeholderPath
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
7779 nsContainerFrame::ChildIsDirty(aChild
);
7782 static bool AlwaysEstablishesBFC(const nsBlockFrame
* aFrame
) {
7783 switch (aFrame
->Type()) {
7784 case LayoutFrameType::ColumnSetWrapper
:
7785 // CSS Multi-column level 1 section 2: A multi-column container
7786 // establishes a new block formatting context, as per CSS 2.1 section
7788 case LayoutFrameType::ComboboxControl
:
7790 case LayoutFrameType::Block
:
7791 return static_cast<const nsFileControlFrame
*>(do_QueryFrame(aFrame
)) ||
7792 // Ensure that the options inside the select aren't expanded by
7793 // right floats outside the select.
7794 static_cast<const nsSelectsAreaFrame
*>(do_QueryFrame(aFrame
)) ||
7795 // See bug 1373767 and bug 353894.
7796 static_cast<const nsMathMLmathBlockFrame
*>(do_QueryFrame(aFrame
));
7802 void nsBlockFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
7803 nsIFrame
* aPrevInFlow
) {
7804 // These are all the block specific frame bits, they are copied from
7805 // the prev-in-flow to a newly created next-in-flow, except for the
7806 // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
7807 constexpr nsFrameState NS_BLOCK_FLAGS_MASK
=
7808 NS_BLOCK_BFC_STATE_BITS
| NS_BLOCK_CLIP_PAGINATED_OVERFLOW
|
7809 NS_BLOCK_HAS_FIRST_LETTER_STYLE
| NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
|
7810 NS_BLOCK_HAS_FIRST_LETTER_CHILD
| NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7812 // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
7813 // by default. They should only be set on the first-in-flow.
7814 constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK
=
7815 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
| NS_BLOCK_HAS_FIRST_LETTER_CHILD
|
7816 NS_BLOCK_FRAME_HAS_INSIDE_MARKER
;
7819 // Copy over the inherited block frame bits from the prev-in-flow.
7820 RemoveStateBits(NS_BLOCK_FLAGS_MASK
);
7821 AddStateBits(aPrevInFlow
->GetStateBits() &
7822 (NS_BLOCK_FLAGS_MASK
& ~NS_BLOCK_FLAGS_NON_INHERITED_MASK
));
7825 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
7828 aPrevInFlow
->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
)) {
7829 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
7832 // A display:flow-root box establishes a block formatting context.
7834 // If a box has a different writing-mode value than its containing block:
7836 // If the box is a block container, then it establishes a new block
7837 // formatting context.
7838 // (https://drafts.csswg.org/css-writing-modes/#block-flow)
7840 // If the box has contain: paint or contain:layout (or contain:strict),
7841 // then it should also establish a formatting context.
7843 // Per spec, a column-span always establishes a new block formatting context.
7845 // Other more specific frame types also always establish a BFC.
7847 if (StyleDisplay()->mDisplay
== mozilla::StyleDisplay::FlowRoot
||
7849 (GetWritingMode().GetBlockDir() !=
7850 GetParent()->GetWritingMode().GetBlockDir() ||
7851 GetWritingMode().IsVerticalSideways() !=
7852 GetParent()->GetWritingMode().IsVerticalSideways())) ||
7853 IsColumnSpan() || AlwaysEstablishesBFC(this)) {
7854 AddStateBits(NS_BLOCK_STATIC_BFC
);
7857 if (StyleEstablishesBFC(Style())) {
7858 AddStateBits(NS_BLOCK_DYNAMIC_BFC
);
7861 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
) &&
7862 HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
7863 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
7867 void nsBlockFrame::SetInitialChildList(ChildListID aListID
,
7868 nsFrameList
&& aChildList
) {
7869 if (FrameChildListID::Float
== aListID
) {
7870 mFloats
= std::move(aChildList
);
7871 } else if (FrameChildListID::Principal
== aListID
) {
7873 // The only times a block that is an anonymous box is allowed to have a
7874 // first-letter frame are when it's the block inside a non-anonymous cell,
7875 // the block inside a fieldset, button or column set, or a scrolled content
7876 // block, except for <select>. Note that this means that blocks which are
7877 // the anonymous block in {ib} splits do NOT get first-letter frames.
7878 // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7880 auto pseudo
= Style()->GetPseudoType();
7881 bool haveFirstLetterStyle
=
7882 (pseudo
== PseudoStyleType::NotPseudo
||
7883 (pseudo
== PseudoStyleType::cellContent
&&
7884 !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7885 pseudo
== PseudoStyleType::fieldsetContent
||
7886 pseudo
== PseudoStyleType::buttonContent
||
7887 pseudo
== PseudoStyleType::columnContent
||
7888 (pseudo
== PseudoStyleType::scrolledContent
&&
7889 !GetParent()->IsListControlFrame()) ||
7890 pseudo
== PseudoStyleType::mozSVGText
) &&
7891 !IsComboboxControlFrame() && !IsMathMLFrame() &&
7892 !IsColumnSetWrapperFrame() &&
7893 RefPtr
<ComputedStyle
>(GetFirstLetterStyle(PresContext())) != nullptr;
7894 NS_ASSERTION(haveFirstLetterStyle
==
7895 HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
),
7896 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7899 AddFrames(std::move(aChildList
), nullptr, nullptr);
7901 nsContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
7905 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame
* aMarkerFrame
) {
7906 MOZ_ASSERT(aMarkerFrame
);
7907 MOZ_ASSERT(!HasAnyStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
|
7908 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
),
7909 "How can we have a ::marker frame already?");
7911 if (StyleList()->mListStylePosition
== StyleListStylePosition::Inside
) {
7912 SetProperty(InsideMarkerProperty(), aMarkerFrame
);
7913 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER
);
7915 if (nsBlockFrame
* marker
= do_QueryFrame(aMarkerFrame
)) {
7916 // An outside ::marker needs to be an independent formatting context
7917 // to avoid being influenced by the float manager etc.
7918 marker
->AddStateBits(NS_BLOCK_STATIC_BFC
);
7920 SetProperty(OutsideMarkerProperty(),
7921 new (PresShell()) nsFrameList(aMarkerFrame
, aMarkerFrame
));
7922 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER
);
7926 bool nsBlockFrame::MarkerIsEmpty() const {
7927 NS_ASSERTION(mContent
->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
7929 "should only care when we have an outside ::marker");
7930 nsIFrame
* marker
= GetMarker();
7931 const nsStyleList
* list
= marker
->StyleList();
7932 return marker
->StyleContent()->mContent
.IsNone() ||
7933 (list
->mCounterStyle
.IsNone() && list
->mListStyleImage
.IsNone() &&
7934 marker
->StyleContent()->ContentCount() == 0);
7937 void nsBlockFrame::ReflowOutsideMarker(nsIFrame
* aMarkerFrame
,
7938 BlockReflowState
& aState
,
7939 ReflowOutput
& aMetrics
,
7941 const ReflowInput
& ri
= aState
.mReflowInput
;
7943 WritingMode markerWM
= aMarkerFrame
->GetWritingMode();
7944 LogicalSize
availSize(markerWM
);
7945 // Make up an inline-size since it doesn't really matter (XXX).
7946 availSize
.ISize(markerWM
) = aState
.ContentISize();
7947 availSize
.BSize(markerWM
) = NS_UNCONSTRAINEDSIZE
;
7949 ReflowInput
reflowInput(aState
.mPresContext
, ri
, aMarkerFrame
, availSize
,
7950 Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap
});
7951 nsReflowStatus status
;
7952 aMarkerFrame
->Reflow(aState
.mPresContext
, aMetrics
, reflowInput
, status
);
7954 // Get the float available space using our saved state from before we
7955 // started reflowing the block, so that we ignore any floats inside
7957 // FIXME: aLineTop isn't actually set correctly by some callers, since
7958 // they reposition the line.
7959 LogicalRect floatAvailSpace
=
7961 .GetFloatAvailableSpaceWithState(aLineTop
, ShapeType::ShapeOutside
,
7962 &aState
.mFloatManagerStateBefore
)
7964 // FIXME (bug 25888): need to check the entire region that the first
7965 // line overlaps, not just the top pixel.
7967 // Place the ::marker now. We want to place the ::marker relative to the
7968 // border-box of the associated block (using the right/left margin of
7969 // the ::marker frame as separation). However, if a line box would be
7970 // displaced by floats that are *outside* the associated block, we
7971 // want to displace it by the same amount. That is, we act as though
7972 // the edge of the floats is the content-edge of the block, and place
7973 // the ::marker at a position offset from there by the block's padding,
7974 // the block's border, and the ::marker frame's margin.
7976 // IStart from floatAvailSpace gives us the content/float start edge
7977 // in the current writing mode. Then we subtract out the start
7978 // border/padding and the ::marker's width and margin to offset the position.
7979 WritingMode wm
= ri
.GetWritingMode();
7980 // Get the ::marker's margin, converted to our writing mode so that we can
7981 // combine it with other logical values here.
7982 LogicalMargin markerMargin
= reflowInput
.ComputedLogicalMargin(wm
);
7983 nscoord iStart
= floatAvailSpace
.IStart(wm
) -
7984 ri
.ComputedLogicalBorderPadding(wm
).IStart(wm
) -
7985 markerMargin
.IEnd(wm
) - aMetrics
.ISize(wm
);
7987 // Approximate the ::marker's position; vertical alignment will provide
7988 // the final vertical location. We pass our writing-mode here, because
7989 // it may be different from the ::marker frame's mode.
7990 nscoord bStart
= floatAvailSpace
.BStart(wm
);
7991 aMarkerFrame
->SetRect(
7993 LogicalRect(wm
, iStart
, bStart
, aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)),
7994 aState
.ContainerSize());
7995 aMarkerFrame
->DidReflow(aState
.mPresContext
, &aState
.mReflowInput
);
7998 // This is used to scan frames for any float placeholders, add their
7999 // floats to the list represented by aList, and remove the
8000 // floats from whatever list they might be in. We don't search descendants
8001 // that are float containing blocks. Floats that or not children of 'this'
8002 // are ignored (they are not added to aList).
8003 void nsBlockFrame::DoCollectFloats(nsIFrame
* aFrame
, nsFrameList
& aList
,
8004 bool aCollectSiblings
) {
8006 // Don't descend into float containing blocks.
8007 if (!aFrame
->IsFloatContainingBlock()) {
8008 nsIFrame
* outOfFlowFrame
=
8009 aFrame
->IsPlaceholderFrame()
8010 ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame
)
8012 while (outOfFlowFrame
&& outOfFlowFrame
->GetParent() == this) {
8013 RemoveFloat(outOfFlowFrame
);
8014 // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
8015 // the PushedFloats list.
8016 outOfFlowFrame
->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
8017 aList
.AppendFrame(nullptr, outOfFlowFrame
);
8018 outOfFlowFrame
= outOfFlowFrame
->GetNextInFlow();
8019 // FIXME: By not pulling floats whose parent is one of our
8020 // later siblings, are we risking the pushed floats getting
8022 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
8025 DoCollectFloats(aFrame
->PrincipalChildList().FirstChild(), aList
, true);
8027 aFrame
->GetChildList(FrameChildListID::Overflow
).FirstChild(), aList
,
8030 if (!aCollectSiblings
) {
8033 aFrame
= aFrame
->GetNextSibling();
8037 void nsBlockFrame::CheckFloats(BlockReflowState
& aState
) {
8039 // If any line is still dirty, that must mean we're going to reflow this
8040 // block again soon (e.g. because we bailed out after noticing that
8041 // clearance was imposed), so don't worry if the floats are out of sync.
8042 bool anyLineDirty
= false;
8044 // Check that the float list is what we would have built
8045 AutoTArray
<nsIFrame
*, 8> lineFloats
;
8046 for (auto& line
: Lines()) {
8047 if (line
.HasFloats()) {
8048 lineFloats
.AppendElements(line
.Floats());
8050 if (line
.IsDirty()) {
8051 anyLineDirty
= true;
8055 AutoTArray
<nsIFrame
*, 8> storedFloats
;
8057 bool hasHiddenFloats
= false;
8059 for (nsIFrame
* f
: mFloats
) {
8060 if (f
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
8063 // There are chances that the float children won't be added to lines,
8064 // because in nsBlockFrame::ReflowLine, it skips reflow line if the first
8065 // child of the line is IsHiddenByContentVisibilityOfInFlowParentForLayout.
8066 // There are also chances that the floats in line are out of date, for
8067 // instance, lines could reflow if
8068 // PresShell::IsForcingLayoutForHiddenContent, and after forcingLayout is
8069 // off, the reflow of lines could be skipped, but the floats are still in
8070 // there. Here we can't know whether the floats hidden by c-v are included
8071 // in the lines or not. So we use hasHiddenFloats to skip the float length
8073 if (!hasHiddenFloats
&&
8074 f
->IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
8075 hasHiddenFloats
= true;
8077 storedFloats
.AppendElement(f
);
8078 if (i
< lineFloats
.Length() && lineFloats
.ElementAt(i
) != f
) {
8084 if ((!equal
|| lineFloats
.Length() != storedFloats
.Length()) &&
8085 !anyLineDirty
&& !hasHiddenFloats
) {
8087 "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
8089 # if defined(DEBUG_roc)
8090 nsIFrame::RootFrameList(PresContext(), stdout
, 0);
8091 for (i
= 0; i
< lineFloats
.Length(); ++i
) {
8092 printf("Line float: %p\n", lineFloats
.ElementAt(i
));
8094 for (i
= 0; i
< storedFloats
.Length(); ++i
) {
8095 printf("Stored float: %p\n", storedFloats
.ElementAt(i
));
8101 const nsFrameList
* oofs
= GetOverflowOutOfFlows();
8102 if (oofs
&& oofs
->NotEmpty()) {
8103 // Floats that were pushed should be removed from our float
8104 // manager. Otherwise the float manager's YMost or XMost might
8105 // be larger than necessary, causing this block to get an
8106 // incorrect desired height (or width). Some of these floats
8107 // may not actually have been added to the float manager because
8108 // they weren't reflowed before being pushed; that's OK,
8109 // RemoveRegions will ignore them. It is safe to do this here
8110 // because we know from here on the float manager will only be
8111 // used for its XMost and YMost, not to place new floats and
8113 aState
.FloatManager()->RemoveTrailingRegions(oofs
->FirstChild());
8117 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot
,
8118 bool* aBEndMarginRoot
) {
8119 nsIFrame
* parent
= GetParent();
8120 if (!HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
)) {
8121 if (!parent
|| parent
->IsFloatContainingBlock()) {
8122 *aBStartMarginRoot
= false;
8123 *aBEndMarginRoot
= false;
8128 if (parent
&& parent
->IsColumnSetFrame()) {
8129 // The first column is a start margin root and the last column is an end
8130 // margin root. (If the column-set is split by a column-span:all box then
8131 // the first and last column in each column-set fragment are margin roots.)
8132 *aBStartMarginRoot
= GetPrevInFlow() == nullptr;
8133 *aBEndMarginRoot
= GetNextInFlow() == nullptr;
8137 *aBStartMarginRoot
= true;
8138 *aBEndMarginRoot
= true;
8142 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame
* aBlock
) {
8143 MOZ_ASSERT(aBlock
, "Must have a frame");
8144 NS_ASSERTION(aBlock
->IsBlockFrameOrSubclass(), "aBlock must be a block");
8146 nsIFrame
* parent
= aBlock
->GetParent();
8147 return aBlock
->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
) ||
8148 (parent
&& !parent
->IsFloatContainingBlock());
8152 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame
* aFrame
) {
8153 return aFrame
->IsBlockFrameOrSubclass() && !aFrame
->IsReplaced() &&
8154 !aFrame
->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS
);
8157 // Note that this width can vary based on the vertical position.
8158 // However, the cases where it varies are the cases where the width fits
8159 // in the available space given, which means that variation shouldn't
8162 nsBlockFrame::FloatAvoidingISizeToClear
nsBlockFrame::ISizeToClearPastFloats(
8163 const BlockReflowState
& aState
, const LogicalRect
& aFloatAvailableSpace
,
8164 nsIFrame
* aFloatAvoidingBlock
) {
8165 nscoord inlineStartOffset
, inlineEndOffset
;
8166 WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8168 FloatAvoidingISizeToClear result
;
8169 aState
.ComputeFloatAvoidingOffsets(aFloatAvoidingBlock
, aFloatAvailableSpace
,
8170 inlineStartOffset
, inlineEndOffset
);
8171 nscoord availISize
=
8172 aState
.mContentArea
.ISize(wm
) - inlineStartOffset
- inlineEndOffset
;
8174 // We actually don't want the min width here; see bug 427782; we only
8175 // want to displace if the width won't compute to a value small enough
8177 // All we really need here is the result of ComputeSize, and we
8178 // could *almost* get that from an SizeComputationInput, except for the
8180 WritingMode frWM
= aFloatAvoidingBlock
->GetWritingMode();
8181 LogicalSize availSpace
=
8182 LogicalSize(wm
, availISize
, NS_UNCONSTRAINEDSIZE
).ConvertTo(frWM
, wm
);
8183 ReflowInput
reflowInput(aState
.mPresContext
, aState
.mReflowInput
,
8184 aFloatAvoidingBlock
, availSpace
);
8185 result
.borderBoxISize
=
8186 reflowInput
.ComputedSizeWithBorderPadding(wm
).ISize(wm
);
8188 // Use the margins from sizingInput rather than reflowInput so that
8189 // they aren't reduced by ignoring margins in overconstrained cases.
8190 SizeComputationInput
sizingInput(aFloatAvoidingBlock
,
8191 aState
.mReflowInput
.mRenderingContext
, wm
,
8192 aState
.mContentArea
.ISize(wm
));
8193 const LogicalMargin computedMargin
= sizingInput
.ComputedLogicalMargin(wm
);
8195 nscoord marginISize
= computedMargin
.IStartEnd(wm
);
8196 const auto& iSize
= reflowInput
.mStylePosition
->ISize(wm
);
8197 if (marginISize
< 0 && (iSize
.IsAuto() || iSize
.IsMozAvailable())) {
8198 // If we get here, floatAvoidingBlock has a negative amount of inline-axis
8199 // margin and an 'auto' (or ~equivalently, -moz-available) inline
8200 // size. Under these circumstances, we use the margin to establish a
8201 // (positive) minimum size for the border-box, in order to satisfy the
8202 // equation in CSS2 10.3.3. That equation essentially simplifies to the
8205 // iSize of margins + iSize of borderBox = iSize of containingBlock
8207 // ...where "iSize of borderBox" is the sum of floatAvoidingBlock's
8208 // inline-axis components of border, padding, and {width,height}.
8210 // Right now, in the above equation, "iSize of margins" is the only term
8211 // that we know for sure. (And we also know that it's negative, since we
8212 // got here.) The other terms are as-yet unresolved, since the frame has an
8213 // 'auto' iSize, and since we aren't yet sure if we'll clear this frame
8214 // beyond floats or place it alongside them.
8216 // However: we *do* know that the equation's "iSize of containingBlock"
8217 // term *must* be non-negative, since boxes' widths and heights generally
8218 // can't be negative in CSS. To satisfy that requirement, we can then
8219 // infer that the equation's "iSize of borderBox" term *must* be large
8220 // enough to cancel out the (known-to-be-negative) "iSize of margins"
8221 // term. Therefore, marginISize value (negated to make it positive)
8222 // establishes a lower-bound for how much inline-axis space our border-box
8223 // will really require in order to fit alongside any floats.
8225 // XXXdholbert This explanation is admittedly a bit hand-wavy and may not
8226 // precisely match what any particular spec requires. It's the best
8227 // reasoning I could come up with to explain engines' behavior. Also, our
8228 // behavior with -moz-available doesn't seem particularly correct here, per
8229 // bug 1767217, though that's probably due to a bug elsewhere in our float
8231 result
.borderBoxISize
= std::max(result
.borderBoxISize
, -marginISize
);
8234 result
.marginIStart
= computedMargin
.IStart(wm
);
8239 nsBlockFrame
* nsBlockFrame::GetNearestAncestorBlock(nsIFrame
* aCandidate
) {
8240 nsBlockFrame
* block
= nullptr;
8241 while (aCandidate
) {
8242 block
= do_QueryFrame(aCandidate
);
8244 // yay, candidate is a block!
8247 // Not a block. Check its parent next.
8248 aCandidate
= aCandidate
->GetParent();
8250 MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
8254 nscoord
nsBlockFrame::ComputeFinalBSize(BlockReflowState
& aState
,
8255 nscoord aBEndEdgeOfChildren
) {
8256 const WritingMode wm
= aState
.mReflowInput
.GetWritingMode();
8258 const nscoord effectiveContentBoxBSize
=
8259 GetEffectiveComputedBSize(aState
.mReflowInput
, aState
.mConsumedBSize
);
8260 const nscoord blockStartBP
= aState
.BorderPadding().BStart(wm
);
8261 const nscoord blockEndBP
= aState
.BorderPadding().BEnd(wm
);
8264 !IsTrueOverflowContainer() || (effectiveContentBoxBSize
== 0 &&
8265 blockStartBP
== 0 && blockEndBP
== 0),
8266 "An overflow container's effective content-box block-size, block-start "
8267 "BP, and block-end BP should all be zero!");
8269 const nscoord effectiveContentBoxBSizeWithBStartBP
=
8270 NSCoordSaturatingAdd(blockStartBP
, effectiveContentBoxBSize
);
8271 const nscoord effectiveBorderBoxBSize
=
8272 NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP
, blockEndBP
);
8274 if (HasColumnSpanSiblings()) {
8275 MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
8276 "Frame constructor should've created column-span siblings!");
8278 // If a block is split by any column-spans, we calculate the final
8279 // block-size by shrinkwrapping our children's block-size for all the
8280 // fragments except for those after the final column-span, but we should
8281 // take no more than our effective border-box block-size. If there's any
8282 // leftover block-size, our next continuations will take up rest.
8284 // We don't need to adjust aBri.mReflowStatus because our children's status
8285 // is the same as ours.
8286 return std::min(effectiveBorderBoxBSize
, aBEndEdgeOfChildren
);
8289 const nscoord availBSize
= aState
.mReflowInput
.AvailableBSize();
8290 if (availBSize
== NS_UNCONSTRAINEDSIZE
) {
8291 return effectiveBorderBoxBSize
;
8294 // Save our children's reflow status.
8295 const bool isChildStatusComplete
= aState
.mReflowStatus
.IsComplete();
8296 if (isChildStatusComplete
&& effectiveContentBoxBSize
> 0 &&
8297 effectiveBorderBoxBSize
> availBSize
&&
8298 ShouldAvoidBreakInside(aState
.mReflowInput
)) {
8299 aState
.mReflowStatus
.SetInlineLineBreakBeforeAndReset();
8300 return effectiveBorderBoxBSize
;
8303 const bool isBDBClone
=
8304 aState
.mReflowInput
.mStyleBorder
->mBoxDecorationBreak
==
8305 StyleBoxDecorationBreak::Clone
;
8307 // The maximum value our content-box block-size can take within the given
8308 // available block-size.
8309 const nscoord maxContentBoxBSize
= aState
.ContentBSize();
8311 // The block-end edge of our content-box (relative to this frame's origin) if
8312 // we consumed the maximum block-size available to us (maxContentBoxBSize).
8313 const nscoord maxContentBoxBEnd
= aState
.ContentBEnd();
8315 // These variables are uninitialized intentionally so that the compiler can
8316 // check they are assigned in every if-else branch below.
8317 nscoord finalContentBoxBSizeWithBStartBP
;
8318 bool isOurStatusComplete
;
8320 if (effectiveBorderBoxBSize
<= availBSize
) {
8321 // Our effective border-box block-size can fit in the available block-size,
8322 // so we are complete.
8323 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8324 isOurStatusComplete
= true;
8325 } else if (effectiveContentBoxBSizeWithBStartBP
<= maxContentBoxBEnd
) {
8326 // Note: The following assertion should generally hold because, for
8327 // box-decoration-break:clone, this "else if" branch is mathematically
8328 // equivalent to the initial "if".
8329 NS_ASSERTION(!isBDBClone
,
8330 "This else-if branch is handling a situation that's specific "
8331 "to box-decoration-break:slice, i.e. a case when we can skip "
8332 "our block-end border and padding!");
8334 // Our effective content-box block-size plus the block-start border and
8335 // padding can fit in the available block-size, but it cannot fit after
8336 // adding the block-end border and padding. Thus, we need a continuation
8337 // (unless we already weren't asking for any block-size, in which case we
8338 // stay complete to avoid looping forever).
8339 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8340 isOurStatusComplete
= effectiveContentBoxBSize
== 0;
8342 // We aren't going to be able to fit our content-box in the space available
8343 // to it, which means we'll probably call ourselves incomplete to request a
8344 // continuation. But before making that decision, we check for certain
8345 // conditions which would force us to overflow beyond the available space --
8346 // these might result in us actually being complete if we're forced to
8347 // overflow far enough.
8348 if (MOZ_UNLIKELY(aState
.mReflowInput
.mFlags
.mIsTopOfPage
&& isBDBClone
&&
8349 maxContentBoxBSize
<= 0 &&
8350 aBEndEdgeOfChildren
== blockStartBP
)) {
8351 // In this rare case, we are at the top of page/column, we have
8352 // box-decoration-break:clone and zero available block-size for our
8353 // content-box (e.g. our own block-start border and padding already exceed
8354 // the available block-size), and we didn't lay out any child to consume
8355 // our content-box block-size. To ensure we make progress (avoid looping
8356 // forever), use 1px as our content-box block-size regardless of our
8357 // effective content-box block-size, in the spirit of
8358 // https://drafts.csswg.org/css-break/#breaking-rules.
8359 finalContentBoxBSizeWithBStartBP
= blockStartBP
+ AppUnitsPerCSSPixel();
8360 isOurStatusComplete
= effectiveContentBoxBSize
<= AppUnitsPerCSSPixel();
8361 } else if (aBEndEdgeOfChildren
> maxContentBoxBEnd
) {
8362 // We have a unbreakable child whose block-end edge exceeds the available
8363 // block-size for children.
8364 if (aBEndEdgeOfChildren
>= effectiveContentBoxBSizeWithBStartBP
) {
8365 // The unbreakable child's block-end edge forces us to consume all of
8366 // our effective content-box block-size.
8367 finalContentBoxBSizeWithBStartBP
= effectiveContentBoxBSizeWithBStartBP
;
8369 // Even though we've consumed all of our effective content-box
8370 // block-size, we may still need to report an incomplete status in order
8371 // to get another continuation, which will be responsible for laying out
8372 // & drawing our block-end border & padding. But if we have no such
8373 // border & padding, or if we're forced to apply that border & padding
8374 // on this frame due to box-decoration-break:clone, then we don't need
8375 // to bother with that additional continuation.
8376 isOurStatusComplete
= (isBDBClone
|| blockEndBP
== 0);
8378 // The unbreakable child's block-end edge doesn't force us to consume
8379 // all of our effective content-box block-size.
8380 finalContentBoxBSizeWithBStartBP
= aBEndEdgeOfChildren
;
8381 isOurStatusComplete
= false;
8384 // The children's block-end edge can fit in the content-box space that we
8385 // have available for it. Consume all the space that is available so that
8386 // our inline-start/inline-end borders extend all the way to the block-end
8387 // edge of column/page.
8388 finalContentBoxBSizeWithBStartBP
= maxContentBoxBEnd
;
8389 isOurStatusComplete
= false;
8393 nscoord finalBorderBoxBSize
= finalContentBoxBSizeWithBStartBP
;
8394 if (isOurStatusComplete
) {
8395 finalBorderBoxBSize
= NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8396 if (isChildStatusComplete
) {
8397 // We want to use children's reflow status as ours, which can be overflow
8398 // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
8400 aState
.mReflowStatus
.SetOverflowIncomplete();
8403 NS_ASSERTION(!IsTrueOverflowContainer(),
8404 "An overflow container should always be complete because of "
8405 "its zero border-box block-size!");
8407 finalBorderBoxBSize
=
8408 NSCoordSaturatingAdd(finalBorderBoxBSize
, blockEndBP
);
8410 aState
.mReflowStatus
.SetIncomplete();
8411 if (!GetNextInFlow()) {
8412 aState
.mReflowStatus
.SetNextInFlowNeedsReflow();
8416 return finalBorderBoxBSize
;
8419 nsresult
nsBlockFrame::ResolveBidi() {
8420 NS_ASSERTION(!GetPrevInFlow(),
8421 "ResolveBidi called on non-first continuation");
8422 MOZ_ASSERT(PresContext()->BidiEnabled());
8423 return nsBidiPresUtils::Resolve(this);
8426 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState
& aRestyleState
) {
8427 // first-letter needs to be updated before first-line, because first-line can
8428 // change the style of the first-letter.
8429 if (HasFirstLetterChild()) {
8430 UpdateFirstLetterStyle(aRestyleState
);
8433 if (nsIFrame
* firstLineFrame
= GetFirstLineFrame()) {
8434 nsIFrame
* styleParent
= CorrectStyleParentFrame(firstLineFrame
->GetParent(),
8435 PseudoStyleType::firstLine
);
8437 ComputedStyle
* parentStyle
= styleParent
->Style();
8438 RefPtr
<ComputedStyle
> firstLineStyle
=
8439 aRestyleState
.StyleSet().ResolvePseudoElementStyle(
8440 *mContent
->AsElement(), PseudoStyleType::firstLine
, nullptr,
8443 // FIXME(bz): Can we make first-line continuations be non-inheriting anon
8445 RefPtr
<ComputedStyle
> continuationStyle
=
8446 aRestyleState
.StyleSet().ResolveInheritingAnonymousBoxStyle(
8447 PseudoStyleType::mozLineFrame
, parentStyle
);
8449 UpdateStyleOfOwnedChildFrame(firstLineFrame
, firstLineStyle
, aRestyleState
,
8450 Some(continuationStyle
.get()));
8452 // We also want to update the styles of the first-line's descendants. We
8453 // don't need to compute a changehint for this, though, since any changes to
8454 // them are handled by the first-line anyway.
8455 RestyleManager
* manager
= PresContext()->RestyleManager();
8456 for (nsIFrame
* kid
: firstLineFrame
->PrincipalChildList()) {
8457 manager
->ReparentComputedStyleForFirstLine(kid
);
8462 nsIFrame
* nsBlockFrame::GetFirstLetter() const {
8463 if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE
)) {
8464 // Certainly no first-letter frame.
8468 return GetProperty(FirstLetterProperty());
8471 nsIFrame
* nsBlockFrame::GetFirstLineFrame() const {
8472 nsIFrame
* maybeFirstLine
= PrincipalChildList().FirstChild();
8473 if (maybeFirstLine
&& maybeFirstLine
->IsLineFrame()) {
8474 return maybeFirstLine
;
8481 void nsBlockFrame::VerifyLines(bool aFinalCheckOK
) {
8482 if (!gVerifyLines
) {
8485 if (mLines
.empty()) {
8489 nsLineBox
* cursor
= GetLineCursorForQuery();
8491 // Add up the counts on each line. Also validate that IsFirstLine is
8494 for (const auto& line
: Lines()) {
8495 if (&line
== cursor
) {
8498 if (aFinalCheckOK
) {
8499 MOZ_ASSERT(line
.GetChildCount(), "empty line");
8500 if (line
.IsBlock()) {
8501 NS_ASSERTION(1 == line
.GetChildCount(), "bad first line");
8504 count
+= line
.GetChildCount();
8507 // Then count the frames
8508 int32_t frameCount
= 0;
8509 nsIFrame
* frame
= mLines
.front()->mFirstChild
;
8512 frame
= frame
->GetNextSibling();
8514 NS_ASSERTION(count
== frameCount
, "bad line list");
8516 // Next: test that each line has right number of frames on it
8517 for (LineIterator line
= LinesBegin(), line_end
= LinesEnd();
8518 line
!= line_end
;) {
8519 count
= line
->GetChildCount();
8520 frame
= line
->mFirstChild
;
8521 while (--count
>= 0) {
8522 frame
= frame
->GetNextSibling();
8525 if ((line
!= line_end
) && (0 != line
->GetChildCount())) {
8526 NS_ASSERTION(frame
== line
->mFirstChild
, "bad line list");
8531 FrameLines
* overflowLines
= GetOverflowLines();
8532 if (overflowLines
) {
8533 LineIterator line
= overflowLines
->mLines
.begin();
8534 LineIterator line_end
= overflowLines
->mLines
.end();
8535 for (; line
!= line_end
; ++line
) {
8536 if (line
== cursor
) {
8543 NS_ASSERTION(!cursor
, "stale LineCursorProperty");
8546 void nsBlockFrame::VerifyOverflowSituation() {
8547 // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
8548 nsFrameList
* oofs
= GetOverflowOutOfFlows();
8550 for (nsIFrame
* f
: *oofs
) {
8551 nsIFrame
* nif
= f
->GetNextInFlow();
8553 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8557 // Pushed floats must not have a next-in-flow in mFloats or mFrames.
8558 oofs
= GetPushedFloats();
8560 for (nsIFrame
* f
: *oofs
) {
8561 nsIFrame
* nif
= f
->GetNextInFlow();
8563 (!mFloats
.ContainsFrame(nif
) && !mFrames
.ContainsFrame(nif
)));
8567 // A child float next-in-flow's parent must be |this| or a next-in-flow of
8568 // |this|. Later next-in-flows must have the same or later parents.
8569 ChildListID childLists
[] = {FrameChildListID::Float
,
8570 FrameChildListID::PushedFloats
};
8571 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
8572 const nsFrameList
& children
= GetChildList(childLists
[i
]);
8573 for (nsIFrame
* f
: children
) {
8574 nsIFrame
* parent
= this;
8575 nsIFrame
* nif
= f
->GetNextInFlow();
8576 for (; nif
; nif
= nif
->GetNextInFlow()) {
8578 for (nsIFrame
* p
= parent
; p
; p
= p
->GetNextInFlow()) {
8579 if (nif
->GetParent() == p
) {
8587 "next-in-flow is a child of parent earlier in the frame tree?");
8592 nsBlockFrame
* flow
= static_cast<nsBlockFrame
*>(FirstInFlow());
8594 FrameLines
* overflowLines
= flow
->GetOverflowLines();
8595 if (overflowLines
) {
8596 NS_ASSERTION(!overflowLines
->mLines
.empty(),
8597 "should not be empty if present");
8598 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
,
8599 "bad overflow lines");
8600 NS_ASSERTION(overflowLines
->mLines
.front()->mFirstChild
==
8601 overflowLines
->mFrames
.FirstChild(),
8602 "bad overflow frames / lines");
8604 auto checkCursor
= [&](nsLineBox
* cursor
) -> bool {
8608 LineIterator line
= flow
->LinesBegin();
8609 LineIterator line_end
= flow
->LinesEnd();
8610 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8612 if (line
== line_end
&& overflowLines
) {
8613 line
= overflowLines
->mLines
.begin();
8614 line_end
= overflowLines
->mLines
.end();
8615 for (; line
!= line_end
&& line
!= cursor
; ++line
)
8618 return line
!= line_end
;
8620 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForDisplay()),
8621 "stale LineCursorPropertyDisplay");
8622 MOZ_ASSERT(checkCursor(flow
->GetLineCursorForQuery()),
8623 "stale LineCursorPropertyQuery");
8624 flow
= static_cast<nsBlockFrame
*>(flow
->GetNextInFlow());
8628 int32_t nsBlockFrame::GetDepth() const {
8630 nsIFrame
* parent
= GetParent();
8632 parent
= parent
->GetParent();
8638 already_AddRefed
<ComputedStyle
> nsBlockFrame::GetFirstLetterStyle(
8639 nsPresContext
* aPresContext
) {
8640 return aPresContext
->StyleSet()->ProbePseudoElementStyle(
8641 *mContent
->AsElement(), PseudoStyleType::firstLetter
, nullptr, Style());