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