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/. */
6 
7 /*
8  * rendering object for CSS display:block, inline-block, and list-item
9  * boxes, also used for various anonymous boxes
10  */
11 
12 #include "nsBlockFrame.h"
13 
14 #include "gfxContext.h"
15 
16 #include "mozilla/AppUnits.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/Maybe.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/StaticPrefs_browser.h"
22 #include "mozilla/StaticPrefs_layout.h"
23 #include "mozilla/SVGUtils.h"
24 #include "mozilla/ToString.h"
25 #include "mozilla/UniquePtr.h"
26 
27 #include "nsCRT.h"
28 #include "nsCOMPtr.h"
29 #include "nsCSSRendering.h"
30 #include "nsAbsoluteContainingBlock.h"
31 #include "nsBlockReflowContext.h"
32 #include "BlockReflowState.h"
33 #include "nsFontMetrics.h"
34 #include "nsGenericHTMLElement.h"
35 #include "nsLineBox.h"
36 #include "nsLineLayout.h"
37 #include "nsPlaceholderFrame.h"
38 #include "nsStyleConsts.h"
39 #include "nsFrameManager.h"
40 #include "nsPresContext.h"
41 #include "nsPresContextInlines.h"
42 #include "nsHTMLParts.h"
43 #include "nsGkAtoms.h"
44 #include "nsAttrValueInlines.h"
45 #include "mozilla/Sprintf.h"
46 #include "nsFloatManager.h"
47 #include "prenv.h"
48 #include "nsError.h"
49 #include "nsIScrollableFrame.h"
50 #include <algorithm>
51 #include "nsLayoutUtils.h"
52 #include "nsDisplayList.h"
53 #include "nsCSSAnonBoxes.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "TextOverflow.h"
56 #include "nsIFrameInlines.h"
57 #include "CounterStyleManager.h"
58 #include "mozilla/dom/HTMLDetailsElement.h"
59 #include "mozilla/dom/HTMLSummaryElement.h"
60 #include "mozilla/dom/Selection.h"
61 #include "mozilla/PresShell.h"
62 #include "mozilla/RestyleManager.h"
63 #include "mozilla/ServoStyleSet.h"
64 #include "mozilla/Telemetry.h"
65 #include "nsFlexContainerFrame.h"
66 
67 #include "nsBidiPresUtils.h"
68 
69 #include <inttypes.h>
70 
71 static const int MIN_LINES_NEEDING_CURSOR = 20;
72 
73 using namespace mozilla;
74 using namespace mozilla::css;
75 using namespace mozilla::dom;
76 using namespace mozilla::layout;
77 using AbsPosReflowFlags = nsAbsoluteContainingBlock::AbsPosReflowFlags;
78 using ClearFloatsResult = BlockReflowState::ClearFloatsResult;
79 using ShapeType = nsFloatManager::ShapeType;
80 
MarkAllDescendantLinesDirty(nsBlockFrame * aBlock)81 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) {
82   for (auto& line : aBlock->Lines()) {
83     if (line.IsBlock()) {
84       nsBlockFrame* bf = do_QueryFrame(line.mFirstChild);
85       if (bf) {
86         MarkAllDescendantLinesDirty(bf);
87       }
88     }
89     line.MarkDirty();
90   }
91 }
92 
MarkSameFloatManagerLinesDirty(nsBlockFrame * aBlock)93 static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock) {
94   nsBlockFrame* blockWithFloatMgr = aBlock;
95   while (!blockWithFloatMgr->HasAnyStateBits(NS_BLOCK_FLOAT_MGR)) {
96     nsBlockFrame* bf = do_QueryFrame(blockWithFloatMgr->GetParent());
97     if (!bf) {
98       break;
99     }
100     blockWithFloatMgr = bf;
101   }
102 
103   // Mark every line at and below the line where the float was
104   // dirty, and mark their lines dirty too. We could probably do
105   // something more efficient --- e.g., just dirty the lines that intersect
106   // the float vertically.
107   MarkAllDescendantLinesDirty(blockWithFloatMgr);
108 }
109 
110 /**
111  * Returns true if aFrame is a block that has one or more float children.
112  */
BlockHasAnyFloats(nsIFrame * aFrame)113 static bool BlockHasAnyFloats(nsIFrame* aFrame) {
114   nsBlockFrame* block = do_QueryFrame(aFrame);
115   if (!block) return false;
116   if (block->GetChildList(nsIFrame::kFloatList).FirstChild()) return true;
117 
118   for (const auto& line : block->Lines()) {
119     if (line.IsBlock() && BlockHasAnyFloats(line.mFirstChild)) {
120       return true;
121     }
122   }
123   return false;
124 }
125 
126 /**
127  * Determines whether the given frame is visible or has
128  * visible children that participate in the same line. Frames
129  * that are not line participants do not have their
130  * children checked.
131  */
FrameHasVisibleInlineContent(nsIFrame * aFrame)132 static bool FrameHasVisibleInlineContent(nsIFrame* aFrame) {
133   MOZ_ASSERT(aFrame, "Frame argument cannot be null");
134 
135   if (aFrame->StyleVisibility()->IsVisible()) {
136     return true;
137   }
138 
139   if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
140     for (nsIFrame* kid : aFrame->PrincipalChildList()) {
141       if (kid->StyleVisibility()->IsVisible() ||
142           FrameHasVisibleInlineContent(kid)) {
143         return true;
144       }
145     }
146   }
147   return false;
148 }
149 
150 /**
151  * Determines whether any of the frames descended from the
152  * given line have inline content with 'visibility: visible'.
153  * This function calls FrameHasVisibleInlineContent to process
154  * each frame in the line's child list.
155  */
LineHasVisibleInlineContent(nsLineBox * aLine)156 static bool LineHasVisibleInlineContent(nsLineBox* aLine) {
157   nsIFrame* kid = aLine->mFirstChild;
158   int32_t n = aLine->GetChildCount();
159   while (n-- > 0) {
160     if (FrameHasVisibleInlineContent(kid)) {
161       return true;
162     }
163 
164     kid = kid->GetNextSibling();
165   }
166 
167   return false;
168 }
169 
170 /**
171  * Iterates through the frame's in-flow children and
172  * unions the ink overflow of all text frames which
173  * participate in the line aFrame belongs to.
174  * If a child of aFrame is not a text frame,
175  * we recurse with the child as the aFrame argument.
176  * If aFrame isn't a line participant, we skip it entirely
177  * and return an empty rect.
178  * The resulting nsRect is offset relative to the parent of aFrame.
179  */
GetFrameTextArea(nsIFrame * aFrame,nsDisplayListBuilder * aBuilder)180 static nsRect GetFrameTextArea(nsIFrame* aFrame,
181                                nsDisplayListBuilder* aBuilder) {
182   nsRect textArea;
183   if (aFrame->IsTextFrame()) {
184     textArea = aFrame->InkOverflowRect();
185   } else if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
186     for (nsIFrame* kid : aFrame->PrincipalChildList()) {
187       nsRect kidTextArea = GetFrameTextArea(kid, aBuilder);
188       textArea.OrWith(kidTextArea);
189     }
190   }
191   // add aFrame's position to keep textArea relative to aFrame's parent
192   return textArea + aFrame->GetPosition();
193 }
194 
195 /**
196  * Iterates through the line's children and
197  * unions the ink overflow of all text frames.
198  * GetFrameTextArea unions and returns the ink overflow
199  * from all line-participating text frames within the given child.
200  * The nsRect returned from GetLineTextArea is offset
201  * relative to the given line.
202  */
GetLineTextArea(nsLineBox * aLine,nsDisplayListBuilder * aBuilder)203 static nsRect GetLineTextArea(nsLineBox* aLine,
204                               nsDisplayListBuilder* aBuilder) {
205   nsRect textArea;
206   nsIFrame* kid = aLine->mFirstChild;
207   int32_t n = aLine->GetChildCount();
208   while (n-- > 0) {
209     nsRect kidTextArea = GetFrameTextArea(kid, aBuilder);
210     textArea.OrWith(kidTextArea);
211     kid = kid->GetNextSibling();
212   }
213 
214   return textArea;
215 }
216 
217 /**
218  * Starting with aFrame, iterates upward through parent frames and checks for
219  * non-transparent background colors. If one is found, we use that as our
220  * backplate color. Otheriwse, we use the default background color from
221  * our high contrast theme.
222  */
GetBackplateColor(nsIFrame * aFrame)223 static nscolor GetBackplateColor(nsIFrame* aFrame) {
224   nsPresContext* pc = aFrame->PresContext();
225   nscolor currentBackgroundColor = NS_TRANSPARENT;
226   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
227     // NOTE(emilio): We assume themed frames (frame->IsThemed()) have correct
228     // background-color information so as to compute the right backplate color.
229     //
230     // This holds because HTML widgets with author-specified backgrounds or
231     // borders disable theming. So as long as the UA-specified background colors
232     // match the actual theme (which they should because we always use system
233     // colors with the non-native theme, and native system colors should also
234     // match the native theme), then we're alright and we should compute an
235     // appropriate backplate color.
236     auto* style = frame->Style();
237     if (style->StyleBackground()->IsTransparent(style)) {
238       continue;
239     }
240     bool drawImage = false, drawColor = false;
241     nscolor backgroundColor = nsCSSRendering::DetermineBackgroundColor(
242         pc, style, frame, drawImage, drawColor);
243     if (!drawColor && !drawImage) {
244       continue;
245     }
246     if (NS_GET_A(backgroundColor) == 0) {
247       // Even if there's a background image, if there's no background color we
248       // keep going up the frame tree, see bug 1723938.
249       continue;
250     }
251     if (NS_GET_A(currentBackgroundColor) == 0) {
252       // Try to avoid somewhat expensive math in the common case.
253       currentBackgroundColor = backgroundColor;
254     } else {
255       currentBackgroundColor =
256           NS_ComposeColors(backgroundColor, currentBackgroundColor);
257     }
258     if (NS_GET_A(currentBackgroundColor) == 0xff) {
259       // If fully opaque, we're done, otherwise keep going up blending with our
260       // background.
261       return currentBackgroundColor;
262     }
263   }
264   nscolor backgroundColor = aFrame->PresContext()->DefaultBackgroundColor();
265   if (NS_GET_A(currentBackgroundColor) == 0) {
266     return backgroundColor;
267   }
268   return NS_ComposeColors(backgroundColor, currentBackgroundColor);
269 }
270 
271 #ifdef DEBUG
272 #  include "nsBlockDebugFlags.h"
273 
274 bool nsBlockFrame::gLamePaintMetrics;
275 bool nsBlockFrame::gLameReflowMetrics;
276 bool nsBlockFrame::gNoisy;
277 bool nsBlockFrame::gNoisyDamageRepair;
278 bool nsBlockFrame::gNoisyIntrinsic;
279 bool nsBlockFrame::gNoisyReflow;
280 bool nsBlockFrame::gReallyNoisyReflow;
281 bool nsBlockFrame::gNoisyFloatManager;
282 bool nsBlockFrame::gVerifyLines;
283 bool nsBlockFrame::gDisableResizeOpt;
284 
285 int32_t nsBlockFrame::gNoiseIndent;
286 
287 struct BlockDebugFlags {
288   const char* name;
289   bool* on;
290 };
291 
292 static const BlockDebugFlags gFlags[] = {
293     {"reflow", &nsBlockFrame::gNoisyReflow},
294     {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow},
295     {"intrinsic", &nsBlockFrame::gNoisyIntrinsic},
296     {"float-manager", &nsBlockFrame::gNoisyFloatManager},
297     {"verify-lines", &nsBlockFrame::gVerifyLines},
298     {"damage-repair", &nsBlockFrame::gNoisyDamageRepair},
299     {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics},
300     {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics},
301     {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt},
302 };
303 #  define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
304 
ShowDebugFlags()305 static void ShowDebugFlags() {
306   printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
307   const BlockDebugFlags* bdf = gFlags;
308   const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
309   for (; bdf < end; bdf++) {
310     printf("  %s\n", bdf->name);
311   }
312   printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
313   printf("names (no whitespace)\n");
314 }
315 
InitDebugFlags()316 void nsBlockFrame::InitDebugFlags() {
317   static bool firstTime = true;
318   if (firstTime) {
319     firstTime = false;
320     char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
321     if (flags) {
322       bool error = false;
323       for (;;) {
324         char* cm = strchr(flags, ',');
325         if (cm) *cm = '\0';
326 
327         bool found = false;
328         const BlockDebugFlags* bdf = gFlags;
329         const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
330         for (; bdf < end; bdf++) {
331           if (nsCRT::strcasecmp(bdf->name, flags) == 0) {
332             *(bdf->on) = true;
333             printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
334             gNoisy = true;
335             found = true;
336             break;
337           }
338         }
339         if (!found) {
340           error = true;
341         }
342 
343         if (!cm) break;
344         *cm = ',';
345         flags = cm + 1;
346       }
347       if (error) {
348         ShowDebugFlags();
349       }
350     }
351   }
352 }
353 
354 #endif
355 
356 //----------------------------------------------------------------------
357 
358 // Debugging support code
359 
360 #ifdef DEBUG
361 const char* nsBlockFrame::kReflowCommandType[] = {
362     "ContentChanged", "StyleChanged", "ReflowDirty", "Timeout", "UserDefined",
363 };
364 
LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const365 const char* nsBlockFrame::LineReflowStatusToString(
366     LineReflowStatus aLineReflowStatus) const {
367   switch (aLineReflowStatus) {
368     case LineReflowStatus::OK:
369       return "LINE_REFLOW_OK";
370     case LineReflowStatus::Stop:
371       return "LINE_REFLOW_STOP";
372     case LineReflowStatus::RedoNoPull:
373       return "LINE_REFLOW_REDO_NO_PULL";
374     case LineReflowStatus::RedoMoreFloats:
375       return "LINE_REFLOW_REDO_MORE_FLOATS";
376     case LineReflowStatus::RedoNextBand:
377       return "LINE_REFLOW_REDO_NEXT_BAND";
378     case LineReflowStatus::Truncated:
379       return "LINE_REFLOW_TRUNCATED";
380   }
381   return "unknown";
382 }
383 
384 #endif
385 
386 #ifdef REFLOW_STATUS_COVERAGE
RecordReflowStatus(bool aChildIsBlock,const nsReflowStatus & aFrameReflowStatus)387 static void RecordReflowStatus(bool aChildIsBlock,
388                                const nsReflowStatus& aFrameReflowStatus) {
389   static uint32_t record[2];
390 
391   // 0: child-is-block
392   // 1: child-is-inline
393   int index = 0;
394   if (!aChildIsBlock) index |= 1;
395 
396   // Compute new status
397   uint32_t newS = record[index];
398   if (aFrameReflowStatus.IsInlineBreak()) {
399     if (aFrameReflowStatus.IsInlineBreakBefore()) {
400       newS |= 1;
401     } else if (aFrameReflowStatus.IsIncomplete()) {
402       newS |= 2;
403     } else {
404       newS |= 4;
405     }
406   } else if (aFrameReflowStatus.IsIncomplete()) {
407     newS |= 8;
408   } else {
409     newS |= 16;
410   }
411 
412   // Log updates to the status that yield different values
413   if (record[index] != newS) {
414     record[index] = newS;
415     printf("record(%d): %02x %02x\n", index, record[0], record[1]);
416   }
417 }
418 #endif
419 
NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty,nsBlockFrame::FrameLines)420 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty,
421                                                  nsBlockFrame::FrameLines)
422 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
423 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
424 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty)
425 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty, nsIFrame)
426 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BlockEndEdgeOfChildrenProperty, nscoord)
427 
428 //----------------------------------------------------------------------
429 
430 nsBlockFrame* NS_NewBlockFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
431   return new (aPresShell) nsBlockFrame(aStyle, aPresShell->GetPresContext());
432 }
433 
NS_NewBlockFormattingContext(PresShell * aPresShell,ComputedStyle * aComputedStyle)434 nsBlockFrame* NS_NewBlockFormattingContext(PresShell* aPresShell,
435                                            ComputedStyle* aComputedStyle) {
436   nsBlockFrame* blockFrame = NS_NewBlockFrame(aPresShell, aComputedStyle);
437   blockFrame->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
438   return blockFrame;
439 }
440 
441 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
442 
443 nsBlockFrame::~nsBlockFrame() = default;
444 
AddSizeOfExcludingThisForTree(nsWindowSizes & aWindowSizes) const445 void nsBlockFrame::AddSizeOfExcludingThisForTree(
446     nsWindowSizes& aWindowSizes) const {
447   nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes);
448 
449   // Add the size of any nsLineBox::mFrames hashtables we might have:
450   for (const auto& line : Lines()) {
451     line.AddSizeOfExcludingThis(aWindowSizes);
452   }
453   const FrameLines* overflowLines = GetOverflowLines();
454   if (overflowLines) {
455     ConstLineIterator line = overflowLines->mLines.begin(),
456                       line_end = overflowLines->mLines.end();
457     for (; line != line_end; ++line) {
458       line->AddSizeOfExcludingThis(aWindowSizes);
459     }
460   }
461 }
462 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)463 void nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot,
464                                PostDestroyData& aPostDestroyData) {
465   ClearLineCursor();
466   DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
467   mFloats.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
468   nsPresContext* presContext = PresContext();
469   mozilla::PresShell* presShell = presContext->PresShell();
470   nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot, &mFrames,
471                             aPostDestroyData);
472 
473   if (HasPushedFloats()) {
474     SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
475                                PushedFloatProperty());
476     RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
477   }
478 
479   // destroy overflow lines now
480   FrameLines* overflowLines = RemoveOverflowLines();
481   if (overflowLines) {
482     nsLineBox::DeleteLineList(presContext, overflowLines->mLines, aDestructRoot,
483                               &overflowLines->mFrames, aPostDestroyData);
484     delete overflowLines;
485   }
486 
487   if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
488     SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
489                                OverflowOutOfFlowsProperty());
490     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
491   }
492 
493   if (HasOutsideMarker()) {
494     SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
495                                OutsideMarkerProperty());
496     RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER);
497   }
498 
499   nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
500 }
501 
502 /* virtual */
GetLineIterator()503 nsILineIterator* nsBlockFrame::GetLineIterator() {
504   const nsStyleVisibility* visibility = StyleVisibility();
505   return new nsLineIterator(mLines,
506                             visibility->mDirection == StyleDirection::Rtl);
507 }
508 
509 NS_QUERYFRAME_HEAD(nsBlockFrame)
NS_QUERYFRAME_ENTRY(nsBlockFrame)510   NS_QUERYFRAME_ENTRY(nsBlockFrame)
511 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
512 
513 #ifdef DEBUG_FRAME_DUMP
514 void nsBlockFrame::List(FILE* out, const char* aPrefix,
515                         ListFlags aFlags) const {
516   nsCString str;
517   ListGeneric(str, aPrefix, aFlags);
518 
519   fprintf_stderr(out, "%s <\n", str.get());
520 
521   nsCString pfx(aPrefix);
522   pfx += "  ";
523 
524   // Output the lines
525   if (!mLines.empty()) {
526     ConstLineIterator line = LinesBegin(), line_end = LinesEnd();
527     for (; line != line_end; ++line) {
528       line->List(out, pfx.get(), aFlags);
529     }
530   }
531 
532   // Output the overflow lines.
533   const FrameLines* overflowLines = GetOverflowLines();
534   if (overflowLines && !overflowLines->mLines.empty()) {
535     fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines,
536                    &overflowLines->mFrames);
537     nsCString nestedPfx(pfx);
538     nestedPfx += "  ";
539     ConstLineIterator line = overflowLines->mLines.begin(),
540                       line_end = overflowLines->mLines.end();
541     for (; line != line_end; ++line) {
542       line->List(out, nestedPfx.get(), aFlags);
543     }
544     fprintf_stderr(out, "%s>\n", pfx.get());
545   }
546 
547   // skip the principal list - we printed the lines above
548   // skip the overflow list - we printed the overflow lines above
549   ChildListIDs skip = {kPrincipalList, kOverflowList};
550   ListChildLists(out, pfx.get(), aFlags, skip);
551 
552   fprintf_stderr(out, "%s>\n", aPrefix);
553 }
554 
GetFrameName(nsAString & aResult) const555 nsresult nsBlockFrame::GetFrameName(nsAString& aResult) const {
556   return MakeFrameName(u"Block"_ns, aResult);
557 }
558 #endif
559 
InvalidateFrame(uint32_t aDisplayItemKey,bool aRebuildDisplayItems)560 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey,
561                                    bool aRebuildDisplayItems) {
562   if (SVGUtils::IsInSVGTextSubtree(this)) {
563     NS_ASSERTION(GetParent()->IsSVGTextFrame(),
564                  "unexpected block frame in SVG text");
565     GetParent()->InvalidateFrame();
566     return;
567   }
568   nsContainerFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
569 }
570 
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)571 void nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect,
572                                            uint32_t aDisplayItemKey,
573                                            bool aRebuildDisplayItems) {
574   if (SVGUtils::IsInSVGTextSubtree(this)) {
575     NS_ASSERTION(GetParent()->IsSVGTextFrame(),
576                  "unexpected block frame in SVG text");
577     GetParent()->InvalidateFrame();
578     return;
579   }
580   nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
581                                             aRebuildDisplayItems);
582 }
583 
GetLogicalBaseline(WritingMode aWM) const584 nscoord nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const {
585   auto lastBaseline = BaselineBOffset(aWM, BaselineSharingGroup::Last,
586                                       AlignmentContext::Inline);
587   return BSize(aWM) - lastBaseline;
588 }
589 
GetNaturalBaselineBOffset(mozilla::WritingMode aWM,BaselineSharingGroup aBaselineGroup,nscoord * aBaseline) const590 bool nsBlockFrame::GetNaturalBaselineBOffset(
591     mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup,
592     nscoord* aBaseline) const {
593   if (StyleDisplay()->IsContainLayout()) {
594     return false;
595   }
596 
597   if (aBaselineGroup == BaselineSharingGroup::First) {
598     return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline);
599   }
600 
601   for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd();
602        line != line_end; ++line) {
603     if (line->IsBlock()) {
604       nscoord offset;
605       nsIFrame* kid = line->mFirstChild;
606       if (!aWM.IsOrthogonalTo(kid->GetWritingMode()) &&
607           kid->GetVerticalAlignBaseline(aWM, &offset)) {
608         // Ignore relative positioning for baseline calculations.
609         const nsSize& sz = line->mContainerSize;
610         offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
611         *aBaseline = BSize(aWM) - offset;
612         return true;
613       }
614     } else {
615       // XXX Is this the right test?  We have some bogus empty lines
616       // floating around, but IsEmpty is perhaps too weak.
617       if (line->BSize() != 0 || !line->IsEmpty()) {
618         *aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent());
619         return true;
620       }
621     }
622   }
623   return false;
624 }
625 
GetCaretBaseline() const626 nscoord nsBlockFrame::GetCaretBaseline() const {
627   nsRect contentRect = GetContentRect();
628   nsMargin bp = GetUsedBorderAndPadding();
629 
630   if (!mLines.empty()) {
631     ConstLineIterator line = LinesBegin();
632     if (!line->IsEmpty()) {
633       if (line->IsBlock()) {
634         return bp.top + line->mFirstChild->GetCaretBaseline();
635       }
636       return line->BStart() + line->GetLogicalAscent();
637     }
638   }
639 
640   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
641   RefPtr<nsFontMetrics> fm =
642       nsLayoutUtils::GetFontMetricsForFrame(this, inflation);
643   nscoord lineHeight = ReflowInput::CalcLineHeight(
644       GetContent(), Style(), PresContext(), contentRect.height, inflation);
645   const WritingMode wm = GetWritingMode();
646   return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
647                                                 wm.IsLineInverted()) +
648          bp.top;
649 }
650 
651 /////////////////////////////////////////////////////////////////////////////
652 // Child frame enumeration
653 
GetChildList(ChildListID aListID) const654 const nsFrameList& nsBlockFrame::GetChildList(ChildListID aListID) const {
655   switch (aListID) {
656     case kPrincipalList:
657       return mFrames;
658     case kOverflowList: {
659       FrameLines* overflowLines = GetOverflowLines();
660       return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
661     }
662     case kFloatList:
663       return mFloats;
664     case kOverflowOutOfFlowList: {
665       const nsFrameList* list = GetOverflowOutOfFlows();
666       return list ? *list : nsFrameList::EmptyList();
667     }
668     case kPushedFloatsList: {
669       const nsFrameList* list = GetPushedFloats();
670       return list ? *list : nsFrameList::EmptyList();
671     }
672     case kBulletList: {
673       const nsFrameList* list = GetOutsideMarkerList();
674       return list ? *list : nsFrameList::EmptyList();
675     }
676     default:
677       return nsContainerFrame::GetChildList(aListID);
678   }
679 }
680 
GetChildLists(nsTArray<ChildList> * aLists) const681 void nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
682   nsContainerFrame::GetChildLists(aLists);
683   FrameLines* overflowLines = GetOverflowLines();
684   if (overflowLines) {
685     overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
686   }
687   const nsFrameList* list = GetOverflowOutOfFlows();
688   if (list) {
689     list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
690   }
691   mFloats.AppendIfNonempty(aLists, kFloatList);
692   list = GetOutsideMarkerList();
693   if (list) {
694     list->AppendIfNonempty(aLists, kBulletList);
695   }
696   list = GetPushedFloats();
697   if (list) {
698     list->AppendIfNonempty(aLists, kPushedFloatsList);
699   }
700 }
701 
702 /* virtual */
IsFloatContainingBlock() const703 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
704 
705 /**
706  * Remove the first line from aFromLines and adjust the associated frame list
707  * aFromFrames accordingly.  The removed line is assigned to *aOutLine and
708  * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
709  * that were extracted from the head of aFromFrames.
710  * aFromLines must contain at least one line, the line may be empty.
711  * @return true if aFromLines becomes empty
712  */
RemoveFirstLine(nsLineList & aFromLines,nsFrameList & aFromFrames,nsLineBox ** aOutLine,nsFrameList * aOutFrames)713 static bool RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
714                             nsLineBox** aOutLine, nsFrameList* aOutFrames) {
715   nsLineList_iterator removedLine = aFromLines.begin();
716   *aOutLine = removedLine;
717   nsLineList_iterator next = aFromLines.erase(removedLine);
718   bool isLastLine = next == aFromLines.end();
719   nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
720                                    : next->mFirstChild->GetPrevSibling();
721   nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
722   *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
723   return isLastLine;
724 }
725 
726 //////////////////////////////////////////////////////////////////////
727 // Reflow methods
728 
729 /* virtual */
MarkIntrinsicISizesDirty()730 void nsBlockFrame::MarkIntrinsicISizesDirty() {
731   nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
732   dirtyBlock->mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
733   dirtyBlock->mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
734   if (!HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
735     for (nsIFrame* frame = dirtyBlock; frame;
736          frame = frame->GetNextContinuation()) {
737       frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
738     }
739   }
740 
741   nsContainerFrame::MarkIntrinsicISizesDirty();
742 }
743 
CheckIntrinsicCacheAgainstShrinkWrapState()744 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
745   nsPresContext* presContext = PresContext();
746   if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
747     return;
748   }
749   bool inflationEnabled = !presContext->mInflationDisabledForShrinkWrap;
750   if (inflationEnabled != HasAnyStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED)) {
751     mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
752     mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
753     if (inflationEnabled) {
754       AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
755     } else {
756       RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
757     }
758   }
759 }
760 
761 /* virtual */
GetMinISize(gfxContext * aRenderingContext)762 nscoord nsBlockFrame::GetMinISize(gfxContext* aRenderingContext) {
763   nsIFrame* firstInFlow = FirstContinuation();
764   if (firstInFlow != this) return firstInFlow->GetMinISize(aRenderingContext);
765 
766   DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
767 
768   CheckIntrinsicCacheAgainstShrinkWrapState();
769 
770   if (mCachedMinISize != NS_INTRINSIC_ISIZE_UNKNOWN) {
771     return mCachedMinISize;
772   }
773 
774   if (StyleDisplay()->IsContainSize()) {
775     mCachedMinISize = 0;
776     return mCachedMinISize;
777   }
778 
779 #ifdef DEBUG
780   if (gNoisyIntrinsic) {
781     IndentBy(stdout, gNoiseIndent);
782     ListTag(stdout);
783     printf(": GetMinISize\n");
784   }
785   AutoNoisyIndenter indenter(gNoisyIntrinsic);
786 #endif
787 
788   for (nsBlockFrame* curFrame = this; curFrame;
789        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
790     curFrame->LazyMarkLinesDirty();
791   }
792 
793   if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
794       PresContext()->BidiEnabled()) {
795     ResolveBidi();
796   }
797 
798   const bool whiteSpaceCanWrap = StyleText()->WhiteSpaceCanWrapStyle();
799   InlineMinISizeData data;
800   for (nsBlockFrame* curFrame = this; curFrame;
801        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
802     for (LineIterator line = curFrame->LinesBegin(),
803                       line_end = curFrame->LinesEnd();
804          line != line_end; ++line) {
805 #ifdef DEBUG
806       if (gNoisyIntrinsic) {
807         IndentBy(stdout, gNoiseIndent);
808         printf("line (%s%s)\n", line->IsBlock() ? "block" : "inline",
809                line->IsEmpty() ? ", empty" : "");
810       }
811       AutoNoisyIndenter lineindent(gNoisyIntrinsic);
812 #endif
813       if (line->IsBlock()) {
814         data.ForceBreak();
815         data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(
816             aRenderingContext, line->mFirstChild, IntrinsicISizeType::MinISize);
817         data.ForceBreak();
818       } else {
819         if (!curFrame->GetPrevContinuation() &&
820             line == curFrame->LinesBegin()) {
821           data.mCurrentLine += StyleText()->mTextIndent.Resolve(0);
822         }
823         data.mLine = &line;
824         data.SetLineContainer(curFrame);
825         nsIFrame* kid = line->mFirstChild;
826         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
827              ++i, kid = kid->GetNextSibling()) {
828           kid->AddInlineMinISize(aRenderingContext, &data);
829           if (whiteSpaceCanWrap && data.mTrailingWhitespace) {
830             data.OptionallyBreak();
831           }
832         }
833       }
834 #ifdef DEBUG
835       if (gNoisyIntrinsic) {
836         IndentBy(stdout, gNoiseIndent);
837         printf("min: [prevLines=%d currentLine=%d]\n", data.mPrevLines,
838                data.mCurrentLine);
839       }
840 #endif
841     }
842   }
843   data.ForceBreak();
844 
845   mCachedMinISize = data.mPrevLines;
846   return mCachedMinISize;
847 }
848 
849 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)850 nscoord nsBlockFrame::GetPrefISize(gfxContext* aRenderingContext) {
851   nsIFrame* firstInFlow = FirstContinuation();
852   if (firstInFlow != this) return firstInFlow->GetPrefISize(aRenderingContext);
853 
854   DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize);
855 
856   CheckIntrinsicCacheAgainstShrinkWrapState();
857 
858   if (mCachedPrefISize != NS_INTRINSIC_ISIZE_UNKNOWN) {
859     return mCachedPrefISize;
860   }
861 
862   if (StyleDisplay()->IsContainSize()) {
863     mCachedPrefISize = 0;
864     return mCachedPrefISize;
865   }
866 
867 #ifdef DEBUG
868   if (gNoisyIntrinsic) {
869     IndentBy(stdout, gNoiseIndent);
870     ListTag(stdout);
871     printf(": GetPrefISize\n");
872   }
873   AutoNoisyIndenter indenter(gNoisyIntrinsic);
874 #endif
875 
876   for (nsBlockFrame* curFrame = this; curFrame;
877        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
878     curFrame->LazyMarkLinesDirty();
879   }
880 
881   if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
882       PresContext()->BidiEnabled()) {
883     ResolveBidi();
884   }
885   InlinePrefISizeData data;
886   for (nsBlockFrame* curFrame = this; curFrame;
887        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
888     for (LineIterator line = curFrame->LinesBegin(),
889                       line_end = curFrame->LinesEnd();
890          line != line_end; ++line) {
891 #ifdef DEBUG
892       if (gNoisyIntrinsic) {
893         IndentBy(stdout, gNoiseIndent);
894         printf("line (%s%s)\n", line->IsBlock() ? "block" : "inline",
895                line->IsEmpty() ? ", empty" : "");
896       }
897       AutoNoisyIndenter lineindent(gNoisyIntrinsic);
898 #endif
899       if (line->IsBlock()) {
900         StyleClear breakType;
901         if (!data.mLineIsEmpty || BlockCanIntersectFloats(line->mFirstChild)) {
902           breakType = StyleClear::Both;
903         } else {
904           breakType = line->mFirstChild->StyleDisplay()->mBreakType;
905         }
906         data.ForceBreak(breakType);
907         data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(
908             aRenderingContext, line->mFirstChild,
909             IntrinsicISizeType::PrefISize);
910         data.ForceBreak();
911       } else {
912         if (!curFrame->GetPrevContinuation() &&
913             line == curFrame->LinesBegin()) {
914           nscoord indent = StyleText()->mTextIndent.Resolve(0);
915           data.mCurrentLine += indent;
916           // XXXmats should the test below be indent > 0?
917           if (indent != nscoord(0)) {
918             data.mLineIsEmpty = false;
919           }
920         }
921         data.mLine = &line;
922         data.SetLineContainer(curFrame);
923         nsIFrame* kid = line->mFirstChild;
924         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
925              ++i, kid = kid->GetNextSibling()) {
926           kid->AddInlinePrefISize(aRenderingContext, &data);
927         }
928       }
929 #ifdef DEBUG
930       if (gNoisyIntrinsic) {
931         IndentBy(stdout, gNoiseIndent);
932         printf("pref: [prevLines=%d currentLine=%d]\n", data.mPrevLines,
933                data.mCurrentLine);
934       }
935 #endif
936     }
937   }
938   data.ForceBreak();
939 
940   mCachedPrefISize = data.mPrevLines;
941   return mCachedPrefISize;
942 }
943 
ComputeTightBounds(DrawTarget * aDrawTarget) const944 nsRect nsBlockFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
945   // be conservative
946   if (Style()->HasTextDecorationLines()) {
947     return InkOverflowRect();
948   }
949   return ComputeSimpleTightBounds(aDrawTarget);
950 }
951 
952 /* virtual */
GetPrefWidthTightBounds(gfxContext * aRenderingContext,nscoord * aX,nscoord * aXMost)953 nsresult nsBlockFrame::GetPrefWidthTightBounds(gfxContext* aRenderingContext,
954                                                nscoord* aX, nscoord* aXMost) {
955   nsIFrame* firstInFlow = FirstContinuation();
956   if (firstInFlow != this) {
957     return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
958   }
959 
960   *aX = 0;
961   *aXMost = 0;
962 
963   nsresult rv;
964   InlinePrefISizeData data;
965   for (nsBlockFrame* curFrame = this; curFrame;
966        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
967     for (LineIterator line = curFrame->LinesBegin(),
968                       line_end = curFrame->LinesEnd();
969          line != line_end; ++line) {
970       nscoord childX, childXMost;
971       if (line->IsBlock()) {
972         data.ForceBreak();
973         rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
974                                                         &childX, &childXMost);
975         NS_ENSURE_SUCCESS(rv, rv);
976         *aX = std::min(*aX, childX);
977         *aXMost = std::max(*aXMost, childXMost);
978       } else {
979         if (!curFrame->GetPrevContinuation() &&
980             line == curFrame->LinesBegin()) {
981           data.mCurrentLine += StyleText()->mTextIndent.Resolve(0);
982         }
983         data.mLine = &line;
984         data.SetLineContainer(curFrame);
985         nsIFrame* kid = line->mFirstChild;
986         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
987              ++i, kid = kid->GetNextSibling()) {
988           rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
989                                             &childXMost);
990           NS_ENSURE_SUCCESS(rv, rv);
991           *aX = std::min(*aX, data.mCurrentLine + childX);
992           *aXMost = std::max(*aXMost, data.mCurrentLine + childXMost);
993           kid->AddInlinePrefISize(aRenderingContext, &data);
994         }
995       }
996     }
997   }
998   data.ForceBreak();
999 
1000   return NS_OK;
1001 }
1002 
1003 /**
1004  * Return whether aNewAvailableSpace is smaller *on either side*
1005  * (inline-start or inline-end) than aOldAvailableSpace, so that we know
1006  * if we need to redo layout on an line, replaced block, or block
1007  * formatting context, because its height (which we used to compute
1008  * aNewAvailableSpace) caused it to intersect additional floats.
1009  */
AvailableSpaceShrunk(WritingMode aWM,const LogicalRect & aOldAvailableSpace,const LogicalRect & aNewAvailableSpace,bool aCanGrow)1010 static bool AvailableSpaceShrunk(WritingMode aWM,
1011                                  const LogicalRect& aOldAvailableSpace,
1012                                  const LogicalRect& aNewAvailableSpace,
1013                                  bool aCanGrow /* debug-only */) {
1014   if (aNewAvailableSpace.ISize(aWM) == 0) {
1015     // Positions are not significant if the inline size is zero.
1016     return aOldAvailableSpace.ISize(aWM) != 0;
1017   }
1018   if (aCanGrow) {
1019     NS_ASSERTION(
1020         aNewAvailableSpace.IStart(aWM) <= aOldAvailableSpace.IStart(aWM) ||
1021             aNewAvailableSpace.IEnd(aWM) <= aOldAvailableSpace.IEnd(aWM),
1022         "available space should not shrink on the start side and "
1023         "grow on the end side");
1024     NS_ASSERTION(
1025         aNewAvailableSpace.IStart(aWM) >= aOldAvailableSpace.IStart(aWM) ||
1026             aNewAvailableSpace.IEnd(aWM) >= aOldAvailableSpace.IEnd(aWM),
1027         "available space should not grow on the start side and "
1028         "shrink on the end side");
1029   } else {
1030     NS_ASSERTION(
1031         aOldAvailableSpace.IStart(aWM) <= aNewAvailableSpace.IStart(aWM) &&
1032             aOldAvailableSpace.IEnd(aWM) >= aNewAvailableSpace.IEnd(aWM),
1033         "available space should never grow");
1034   }
1035   // Have we shrunk on either side?
1036   return aNewAvailableSpace.IStart(aWM) > aOldAvailableSpace.IStart(aWM) ||
1037          aNewAvailableSpace.IEnd(aWM) < aOldAvailableSpace.IEnd(aWM);
1038 }
1039 
CalculateContainingBlockSizeForAbsolutes(WritingMode aWM,const ReflowInput & aReflowInput,LogicalSize aFrameSize)1040 static LogicalSize CalculateContainingBlockSizeForAbsolutes(
1041     WritingMode aWM, const ReflowInput& aReflowInput, LogicalSize aFrameSize) {
1042   // The issue here is that for a 'height' of 'auto' the reflow input
1043   // code won't know how to calculate the containing block height
1044   // because it's calculated bottom up. So we use our own computed
1045   // size as the dimensions.
1046   nsIFrame* frame = aReflowInput.mFrame;
1047 
1048   LogicalSize cbSize(aFrameSize);
1049   // Containing block is relative to the padding edge
1050   const LogicalMargin border = aReflowInput.ComputedLogicalBorder(aWM);
1051   cbSize.ISize(aWM) -= border.IStartEnd(aWM);
1052   cbSize.BSize(aWM) -= border.BStartEnd(aWM);
1053 
1054   if (frame->GetParent()->GetContent() != frame->GetContent() ||
1055       frame->GetParent()->IsCanvasFrame()) {
1056     return cbSize;
1057   }
1058 
1059   // We are a wrapped frame for the content (and the wrapper is not the
1060   // canvas frame, whose size is not meaningful here).
1061   // Use the container's dimensions, if they have been precomputed.
1062   // XXX This is a hack! We really should be waiting until the outermost
1063   // frame is fully reflowed and using the resulting dimensions, even
1064   // if they're intrinsic.
1065   // In fact we should be attaching absolute children to the outermost
1066   // frame and not always sticking them in block frames.
1067 
1068   // First, find the reflow input for the outermost frame for this content.
1069   const ReflowInput* lastRI = &aReflowInput;
1070   DebugOnly<const ReflowInput*> lastButOneRI = &aReflowInput;
1071   while (lastRI->mParentReflowInput &&
1072          lastRI->mParentReflowInput->mFrame->GetContent() ==
1073              frame->GetContent()) {
1074     lastButOneRI = lastRI;
1075     lastRI = lastRI->mParentReflowInput;
1076   }
1077 
1078   if (lastRI == &aReflowInput) {
1079     return cbSize;
1080   }
1081 
1082   // For scroll containers, we can just use cbSize (which is the padding-box
1083   // size of the scrolled-content frame).
1084   if (nsIScrollableFrame* scrollFrame = do_QueryFrame(lastRI->mFrame)) {
1085     // Assert that we're not missing any frames between the abspos containing
1086     // block and the scroll container.
1087     // the parent.
1088     Unused << scrollFrame;
1089     MOZ_ASSERT(lastButOneRI == &aReflowInput);
1090     return cbSize;
1091   }
1092 
1093   // Same for fieldsets, where the inner anonymous frame has the correct padding
1094   // area with the legend taken into account.
1095   if (lastRI->mFrame->IsFieldSetFrame()) {
1096     return cbSize;
1097   }
1098 
1099   // We found a reflow input for the outermost wrapping frame, so use
1100   // its computed metrics if available, converted to our writing mode
1101   const LogicalSize lastRISize = lastRI->ComputedSize(aWM);
1102   const LogicalMargin lastRIPadding = lastRI->ComputedLogicalPadding(aWM);
1103   if (lastRISize.ISize(aWM) != NS_UNCONSTRAINEDSIZE) {
1104     cbSize.ISize(aWM) =
1105         std::max(0, lastRISize.ISize(aWM) + lastRIPadding.IStartEnd(aWM));
1106   }
1107   if (lastRISize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
1108     cbSize.BSize(aWM) =
1109         std::max(0, lastRISize.BSize(aWM) + lastRIPadding.BStartEnd(aWM));
1110   }
1111 
1112   return cbSize;
1113 }
1114 
1115 /**
1116  * Returns aFrame if it is a non-BFC block frame, and null otherwise.
1117  *
1118  * This is used to determine whether to recurse into aFrame when applying
1119  * -webkit-line-clamp.
1120  */
GetAsLineClampDescendant(nsIFrame * aFrame)1121 static nsBlockFrame* GetAsLineClampDescendant(nsIFrame* aFrame) {
1122   if (nsBlockFrame* block = do_QueryFrame(aFrame)) {
1123     if (!block->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS)) {
1124       return block;
1125     }
1126   }
1127   return nullptr;
1128 }
1129 
1130 /**
1131  * Iterator over all descendant inline line boxes, except for those that are
1132  * under an independent formatting context.
1133  */
1134 class MOZ_RAII LineClampLineIterator {
1135  public:
LineClampLineIterator(nsBlockFrame * aFrame)1136   explicit LineClampLineIterator(nsBlockFrame* aFrame)
1137       : mCur(aFrame->LinesBegin()),
1138         mEnd(aFrame->LinesEnd()),
1139         mCurrentFrame(mCur == mEnd ? nullptr : aFrame) {
1140     if (mCur != mEnd && !mCur->IsInline()) {
1141       Advance();
1142     }
1143   }
1144 
GetCurrentLine()1145   nsLineBox* GetCurrentLine() { return mCurrentFrame ? mCur.get() : nullptr; }
GetCurrentFrame()1146   nsBlockFrame* GetCurrentFrame() { return mCurrentFrame; }
1147 
1148   // Advances the iterator to the next line line.
1149   //
1150   // Next() shouldn't be called once the iterator is at the end, which can be
1151   // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
Next()1152   void Next() {
1153     MOZ_ASSERT(mCur != mEnd && mCurrentFrame,
1154                "Don't call Next() when the iterator is at the end");
1155     ++mCur;
1156     Advance();
1157   }
1158 
1159  private:
Advance()1160   void Advance() {
1161     for (;;) {
1162       if (mCur == mEnd) {
1163         // Reached the end of the current block.  Pop the parent off the
1164         // stack; if there isn't one, then we've reached the end.
1165         if (mStack.IsEmpty()) {
1166           mCurrentFrame = nullptr;
1167           break;
1168         }
1169         auto entry = mStack.PopLastElement();
1170         mCurrentFrame = entry.first;
1171         mCur = entry.second;
1172         mEnd = mCurrentFrame->LinesEnd();
1173       } else if (mCur->IsBlock()) {
1174         if (nsBlockFrame* child = GetAsLineClampDescendant(mCur->mFirstChild)) {
1175           nsBlockFrame::LineIterator next = mCur;
1176           ++next;
1177           mStack.AppendElement(std::make_pair(mCurrentFrame, next));
1178           mCur = child->LinesBegin();
1179           mEnd = child->LinesEnd();
1180           mCurrentFrame = child;
1181         } else {
1182           // Some kind of frame we shouldn't descend into.
1183           ++mCur;
1184         }
1185       } else {
1186         MOZ_ASSERT(mCur->IsInline());
1187         break;
1188       }
1189     }
1190   }
1191 
1192   // The current line within the current block.
1193   //
1194   // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
1195   // is set to null.
1196   nsBlockFrame::LineIterator mCur;
1197 
1198   // The iterator end for the current block.
1199   nsBlockFrame::LineIterator mEnd;
1200 
1201   // The current block.
1202   nsBlockFrame* mCurrentFrame;
1203 
1204   // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
1205   // exist blocks.
1206   AutoTArray<std::pair<nsBlockFrame*, nsBlockFrame::LineIterator>, 8> mStack;
1207 };
1208 
ClearLineClampEllipsis(nsBlockFrame * aFrame)1209 static bool ClearLineClampEllipsis(nsBlockFrame* aFrame) {
1210   if (!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)) {
1211     for (nsIFrame* f : aFrame->PrincipalChildList()) {
1212       if (nsBlockFrame* child = GetAsLineClampDescendant(f)) {
1213         if (ClearLineClampEllipsis(child)) {
1214           return true;
1215         }
1216       }
1217     }
1218     return false;
1219   }
1220 
1221   aFrame->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
1222 
1223   for (auto& line : aFrame->Lines()) {
1224     if (line.HasLineClampEllipsis()) {
1225       line.ClearHasLineClampEllipsis();
1226       return true;
1227     }
1228   }
1229 
1230   // We didn't find a line with the ellipsis; it must have been deleted already.
1231   return true;
1232 }
1233 
ClearLineClampEllipsis()1234 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
1235 
IsLineClampItem(const ReflowInput & aReflowInput)1236 static bool IsLineClampItem(const ReflowInput& aReflowInput) {
1237   return aReflowInput.mFlags.mApplyLineClamp ||
1238          (aReflowInput.mParentReflowInput &&
1239           aReflowInput.mParentReflowInput->mFrame->IsScrollFrame() &&
1240           aReflowInput.mParentReflowInput->mFlags.mApplyLineClamp);
1241 }
1242 
Reflow(nsPresContext * aPresContext,ReflowOutput & aMetrics,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)1243 void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
1244                           const ReflowInput& aReflowInput,
1245                           nsReflowStatus& aStatus) {
1246   MarkInReflow();
1247   DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
1248   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
1249   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
1250 
1251 #ifdef DEBUG
1252   if (gNoisyReflow) {
1253     IndentBy(stdout, gNoiseIndent);
1254     ListTag(stdout);
1255     printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
1256            aReflowInput.AvailableISize(), aReflowInput.AvailableBSize(),
1257            aReflowInput.ComputedISize(), aReflowInput.ComputedBSize());
1258   }
1259   AutoNoisyIndenter indent(gNoisy);
1260   PRTime start = 0;  // Initialize these variablies to silence the compiler.
1261   int32_t ctc = 0;   // We only use these if they are set (gLameReflowMetrics).
1262   if (gLameReflowMetrics) {
1263     start = PR_Now();
1264     ctc = nsLineBox::GetCtorCount();
1265   }
1266 #endif
1267 
1268   // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
1269   // max-block-size because both affect the children's available block-size.
1270   if (IsColumnSetWrapperFrame()) {
1271     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
1272   }
1273 
1274   const ReflowInput* reflowInput = &aReflowInput;
1275   WritingMode wm = aReflowInput.GetWritingMode();
1276   const nscoord consumedBSize = CalcAndCacheConsumedBSize();
1277   const nscoord effectiveContentBoxBSize =
1278       GetEffectiveComputedBSize(aReflowInput, consumedBSize);
1279   Maybe<ReflowInput> mutableReflowInput;
1280   // If we have non-auto block size, we're clipping our kids and we fit,
1281   // make sure our kids fit too.
1282   const PhysicalAxes physicalBlockAxis =
1283       wm.IsVertical() ? PhysicalAxes::Horizontal : PhysicalAxes::Vertical;
1284   if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
1285       aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE &&
1286       (ShouldApplyOverflowClipping(aReflowInput.mStyleDisplay) &
1287        physicalBlockAxis)) {
1288     LogicalMargin blockDirExtras =
1289         aReflowInput.ComputedLogicalBorderPadding(wm);
1290     if (GetLogicalSkipSides().BStart()) {
1291       blockDirExtras.BStart(wm) = 0;
1292     } else {
1293       // Block-end margin never causes us to create continuations, so we
1294       // don't need to worry about whether it fits in its entirety.
1295       blockDirExtras.BStart(wm) +=
1296           aReflowInput.ComputedLogicalMargin(wm).BStart(wm);
1297     }
1298 
1299     if (effectiveContentBoxBSize + blockDirExtras.BStartEnd(wm) <=
1300         aReflowInput.AvailableBSize()) {
1301       mutableReflowInput.emplace(aReflowInput);
1302       mutableReflowInput->AvailableBSize() = NS_UNCONSTRAINEDSIZE;
1303       reflowInput = mutableReflowInput.ptr();
1304     }
1305   }
1306 
1307   // See comment below about oldSize. Use *only* for the
1308   // abs-pos-containing-block-size-change optimization!
1309   nsSize oldSize = GetSize();
1310 
1311   // Should we create a float manager?
1312   nsAutoFloatManager autoFloatManager(const_cast<ReflowInput&>(*reflowInput));
1313 
1314   // XXXldb If we start storing the float manager in the frame rather
1315   // than keeping it around only during reflow then we should create it
1316   // only when there are actually floats to manage.  Otherwise things
1317   // like tables will gain significant bloat.
1318   bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
1319   if (needFloatManager) autoFloatManager.CreateFloatManager(aPresContext);
1320 
1321   // OK, some lines may be reflowed. Blow away any saved line cursor
1322   // because we may invalidate the nondecreasing
1323   // overflowArea.InkOverflow().y/yMost invariant, and we may even
1324   // delete the line with the line cursor.
1325   ClearLineCursor();
1326 
1327   if (IsFrameTreeTooDeep(*reflowInput, aMetrics, aStatus)) {
1328     return;
1329   }
1330 
1331 #ifdef DEBUG
1332   // Between when we drain pushed floats and when we complete reflow,
1333   // we're allowed to have multiple continuations of the same float on
1334   // our floats list, since a first-in-flow might get pushed to a later
1335   // continuation of its containing block.  But it's not permitted
1336   // outside that time.
1337   nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
1338 #endif
1339 
1340   // ALWAYS drain overflow. We never want to leave the previnflow's
1341   // overflow lines hanging around; block reflow depends on the
1342   // overflow line lists being cleared out between reflow passes.
1343   DrainOverflowLines();
1344 
1345   bool blockStartMarginRoot, blockEndMarginRoot;
1346   IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
1347 
1348   BlockReflowState state(*reflowInput, aPresContext, this, blockStartMarginRoot,
1349                          blockEndMarginRoot, needFloatManager, consumedBSize,
1350                          effectiveContentBoxBSize);
1351 
1352   if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
1353       PresContext()->BidiEnabled()) {
1354     static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
1355   }
1356 
1357   // Handle paginated overflow (see nsContainerFrame.h)
1358   OverflowAreas ocBounds;
1359   nsReflowStatus ocStatus;
1360   if (GetPrevInFlow()) {
1361     ReflowOverflowContainerChildren(
1362         aPresContext, *reflowInput, ocBounds, ReflowChildFlags::Default,
1363         ocStatus, DefaultChildFrameMerge, Some(state.ContainerSize()));
1364   }
1365 
1366   // Now that we're done cleaning up our overflow container lists, we can
1367   // give |state| its nsOverflowContinuationTracker.
1368   nsOverflowContinuationTracker tracker(this, false);
1369   state.mOverflowTracker = &tracker;
1370 
1371   // Drain & handle pushed floats
1372   DrainPushedFloats();
1373   OverflowAreas fcBounds;
1374   ReflowPushedFloats(state, fcBounds);
1375 
1376   // If we're not dirty (which means we'll mark everything dirty later)
1377   // and our inline-size has changed, mark the lines dirty that we need to
1378   // mark dirty for a resize reflow.
1379   if (!HasAnyStateBits(NS_FRAME_IS_DIRTY) && reflowInput->IsIResize()) {
1380     PrepareResizeReflow(state);
1381   }
1382 
1383   // The same for percentage text-indent, except conditioned on the
1384   // parent resizing.
1385   if (!HasAnyStateBits(NS_FRAME_IS_DIRTY) && reflowInput->mCBReflowInput &&
1386       reflowInput->mCBReflowInput->IsIResize() &&
1387       reflowInput->mStyleText->mTextIndent.HasPercent() && !mLines.empty()) {
1388     mLines.front()->MarkDirty();
1389   }
1390 
1391   LazyMarkLinesDirty();
1392 
1393   // Now reflow...
1394   ReflowDirtyLines(state);
1395 
1396   // If we have a next-in-flow, and that next-in-flow has pushed floats from
1397   // this frame from a previous iteration of reflow, then we should not return
1398   // a status with IsFullyComplete() equals to true, since we actually have
1399   // overflow, it's just already been handled.
1400 
1401   // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1402   // and reflow them, but just in case it does, this is a safety precaution so
1403   // we don't end up with a placeholder pointing to frames that have already
1404   // been deleted as part of removing our next-in-flow.
1405   if (state.mReflowStatus.IsFullyComplete()) {
1406     nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
1407     while (nif) {
1408       if (nif->HasPushedFloatsFromPrevContinuation()) {
1409         if (nif->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1410           state.mReflowStatus.SetOverflowIncomplete();
1411         } else {
1412           state.mReflowStatus.SetIncomplete();
1413         }
1414         break;
1415       }
1416 
1417       nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
1418     }
1419   }
1420 
1421   state.mReflowStatus.MergeCompletionStatusFrom(ocStatus);
1422 
1423   // If we end in a BR with clear and affected floats continue,
1424   // we need to continue, too.
1425   if (NS_UNCONSTRAINEDSIZE != reflowInput->AvailableBSize() &&
1426       state.mReflowStatus.IsComplete() &&
1427       state.FloatManager()->ClearContinues(FindTrailingClear())) {
1428     state.mReflowStatus.SetIncomplete();
1429   }
1430 
1431   if (!state.mReflowStatus.IsFullyComplete()) {
1432     if (HasOverflowLines() || HasPushedFloats()) {
1433       state.mReflowStatus.SetNextInFlowNeedsReflow();
1434     }
1435 
1436 #ifdef DEBUG_kipp
1437     ListTag(stdout);
1438     printf(": block is not fully complete\n");
1439 #endif
1440   }
1441 
1442   // Place the ::marker's frame if it is placed next to a block child.
1443   //
1444   // According to the CSS2 spec, section 12.6.1, the ::marker's box
1445   // participates in the height calculation of the list-item box's
1446   // first line box.
1447   //
1448   // There are exactly two places a ::marker can be placed: near the
1449   // first or second line. It's only placed on the second line in a
1450   // rare case: an empty first line followed by a second line that
1451   // contains a block (example: <LI>\n<P>... ). This is where
1452   // the second case can happen.
1453   if (HasOutsideMarker() && !mLines.empty() &&
1454       (mLines.front()->IsBlock() ||
1455        (0 == mLines.front()->BSize() && mLines.front() != mLines.back() &&
1456         mLines.begin().next()->IsBlock()))) {
1457     // Reflow the ::marker's frame.
1458     ReflowOutput reflowOutput(aReflowInput);
1459     // XXX Use the entire line when we fix bug 25888.
1460     nsLayoutUtils::LinePosition position;
1461     WritingMode wm = aReflowInput.GetWritingMode();
1462     bool havePosition =
1463         nsLayoutUtils::GetFirstLinePosition(wm, this, &position);
1464     nscoord lineBStart =
1465         havePosition ? position.mBStart
1466                      : reflowInput->ComputedLogicalBorderPadding(wm).BStart(wm);
1467     nsIFrame* marker = GetOutsideMarker();
1468     ReflowOutsideMarker(marker, state, reflowOutput, lineBStart);
1469     NS_ASSERTION(!MarkerIsEmpty() || reflowOutput.BSize(wm) == 0,
1470                  "empty ::marker frame took up space");
1471 
1472     if (havePosition && !MarkerIsEmpty()) {
1473       // We have some lines to align the ::marker with.
1474 
1475       // Doing the alignment using the baseline will also cater for
1476       // ::markers that are placed next to a child block (bug 92896)
1477 
1478       // Tall ::markers won't look particularly nice here...
1479       LogicalRect bbox =
1480           marker->GetLogicalRect(wm, reflowOutput.PhysicalSize());
1481       const auto baselineGroup = BaselineSharingGroup::First;
1482       nscoord markerBaseline;
1483       if (MOZ_UNLIKELY(wm.IsOrthogonalTo(marker->GetWritingMode()) ||
1484                        !marker->GetNaturalBaselineBOffset(wm, baselineGroup,
1485                                                           &markerBaseline))) {
1486         // ::marker has no baseline in this axis: align with its margin-box end.
1487         markerBaseline =
1488             bbox.BSize(wm) + marker->GetLogicalUsedMargin(wm).BEnd(wm);
1489       }
1490       bbox.BStart(wm) = position.mBaseline - markerBaseline;
1491       marker->SetRect(wm, bbox, reflowOutput.PhysicalSize());
1492     }
1493     // Otherwise just leave the ::marker where it is, up against our
1494     // block-start padding.
1495   }
1496 
1497   // Clear any existing -webkit-line-clamp ellipsis.
1498   if (IsLineClampItem(aReflowInput)) {
1499     ClearLineClampEllipsis();
1500   }
1501 
1502   CheckFloats(state);
1503 
1504   // Compute our final size
1505   nscoord blockEndEdgeOfChildren;
1506   ComputeFinalSize(*reflowInput, state, aMetrics, &blockEndEdgeOfChildren);
1507 
1508   // If the block direction is right-to-left, we need to update the bounds of
1509   // lines that were placed relative to mContainerSize during reflow, as
1510   // we typically do not know the true container size until we've reflowed all
1511   // its children. So we use a dummy mContainerSize during reflow (see
1512   // BlockReflowState's constructor) and then fix up the positions of the
1513   // lines here, once the final block size is known.
1514   //
1515   // Note that writing-mode:vertical-rl is the only case where the block
1516   // logical direction progresses in a negative physical direction, and
1517   // therefore block-dir coordinate conversion depends on knowing the width
1518   // of the coordinate space in order to translate between the logical and
1519   // physical origins.
1520   if (wm.IsVerticalRL()) {
1521     nsSize containerSize = aMetrics.PhysicalSize();
1522     nscoord deltaX = containerSize.width - state.ContainerSize().width;
1523     if (deltaX != 0) {
1524       // We compute our lines and markers' overflow areas later in
1525       // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
1526       // here.
1527       const nsPoint physicalDelta(deltaX, 0);
1528       for (auto& line : Lines()) {
1529         UpdateLineContainerSize(&line, containerSize);
1530       }
1531       fcBounds.Clear();
1532       for (nsIFrame* f : mFloats) {
1533         f->MovePositionBy(physicalDelta);
1534         ConsiderChildOverflow(fcBounds, f);
1535       }
1536       nsFrameList* markerList = GetOutsideMarkerList();
1537       if (markerList) {
1538         for (nsIFrame* f : *markerList) {
1539           f->MovePositionBy(physicalDelta);
1540         }
1541       }
1542       if (nsFrameList* overflowContainers = GetOverflowContainers()) {
1543         ocBounds.Clear();
1544         for (nsIFrame* f : *overflowContainers) {
1545           f->MovePositionBy(physicalDelta);
1546           ConsiderChildOverflow(ocBounds, f);
1547         }
1548       }
1549     }
1550   }
1551 
1552   aMetrics.SetOverflowAreasToDesiredBounds();
1553   ComputeOverflowAreas(aMetrics.mOverflowAreas, blockEndEdgeOfChildren,
1554                        reflowInput->mStyleDisplay);
1555   // Factor overflow container child bounds into the overflow area
1556   aMetrics.mOverflowAreas.UnionWith(ocBounds);
1557   // Factor pushed float child bounds into the overflow area
1558   aMetrics.mOverflowAreas.UnionWith(fcBounds);
1559 
1560   // Let the absolutely positioned container reflow any absolutely positioned
1561   // child frames that need to be reflowed, e.g., elements with a percentage
1562   // based width/height
1563   // We want to do this under either of two conditions:
1564   //  1. If we didn't do the incremental reflow above.
1565   //  2. If our size changed.
1566   // Even though it's the padding edge that's the containing block, we
1567   // can use our rect (the border edge) since if the border style
1568   // changed, the reflow would have been targeted at us so we'd satisfy
1569   // condition 1.
1570   // XXX checking oldSize is bogus, there are various reasons we might have
1571   // reflowed but our size might not have been changed to what we
1572   // asked for (e.g., we ended up being pushed to a new page)
1573   // When WillReflowAgainForClearance is true, we will reflow again without
1574   // resetting the size. Because of this, we must not reflow our abs-pos
1575   // children in that situation --- what we think is our "new size" will not be
1576   // our real new size. This also happens to be more efficient.
1577   WritingMode parentWM = aMetrics.GetWritingMode();
1578   if (HasAbsolutelyPositionedChildren()) {
1579     nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
1580     bool haveInterrupt = aPresContext->HasPendingInterrupt();
1581     if (reflowInput->WillReflowAgainForClearance() || haveInterrupt) {
1582       // Make sure that when we reflow again we'll actually reflow all the abs
1583       // pos frames that might conceivably depend on our size (or all of them,
1584       // if we're dirty right now and interrupted; in that case we also need
1585       // to mark them all with NS_FRAME_IS_DIRTY).  Sadly, we can't do much
1586       // better than that, because we don't really know what our size will be,
1587       // and it might in fact not change on the followup reflow!
1588       if (haveInterrupt && HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
1589         absoluteContainer->MarkAllFramesDirty();
1590       } else {
1591         absoluteContainer->MarkSizeDependentFramesDirty();
1592       }
1593       if (haveInterrupt) {
1594         // We're not going to reflow absolute frames; make sure to account for
1595         // their existing overflow areas, which is usually a side effect of this
1596         // reflow.
1597         //
1598         // TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
1599         // interrupt, can we just rely on it and unconditionally take the else
1600         // branch below? That's a bit more subtle / risky, since I don't see
1601         // what would reflow them in that case if they depended on our size.
1602         for (nsIFrame* kid = absoluteContainer->GetChildList().FirstChild();
1603              kid; kid = kid->GetNextSibling()) {
1604           ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
1605         }
1606       }
1607     } else {
1608       LogicalSize containingBlockSize =
1609           CalculateContainingBlockSizeForAbsolutes(parentWM, *reflowInput,
1610                                                    aMetrics.Size(parentWM));
1611 
1612       // Mark frames that depend on changes we just made to this frame as dirty:
1613       // Now we can assume that the padding edge hasn't moved.
1614       // We need to reflow the absolutes if one of them depends on
1615       // its placeholder position, or the containing block size in a
1616       // direction in which the containing block size might have
1617       // changed.
1618 
1619       // XXX "width" and "height" in this block will become ISize and BSize
1620       // when nsAbsoluteContainingBlock is logicalized
1621       bool cbWidthChanged = aMetrics.Width() != oldSize.width;
1622       bool isRoot = !GetContent()->GetParent();
1623       // If isRoot and we have auto height, then we are the initial
1624       // containing block and the containing block height is the
1625       // viewport height, which can't change during incremental
1626       // reflow.
1627       bool cbHeightChanged =
1628           !(isRoot && NS_UNCONSTRAINEDSIZE == reflowInput->ComputedHeight()) &&
1629           aMetrics.Height() != oldSize.height;
1630 
1631       nsRect containingBlock(nsPoint(0, 0),
1632                              containingBlockSize.GetPhysicalSize(parentWM));
1633       AbsPosReflowFlags flags = AbsPosReflowFlags::ConstrainHeight;
1634       if (cbWidthChanged) {
1635         flags |= AbsPosReflowFlags::CBWidthChanged;
1636       }
1637       if (cbHeightChanged) {
1638         flags |= AbsPosReflowFlags::CBHeightChanged;
1639       }
1640       // Setup the line cursor here to optimize line searching for
1641       // calculating hypothetical position of absolutely-positioned
1642       // frames. The line cursor is immediately cleared afterward to
1643       // avoid affecting the display list generation.
1644       AutoLineCursorSetup autoLineCursor(this);
1645       absoluteContainer->Reflow(this, aPresContext, *reflowInput,
1646                                 state.mReflowStatus, containingBlock, flags,
1647                                 &aMetrics.mOverflowAreas);
1648     }
1649   }
1650 
1651   FinishAndStoreOverflow(&aMetrics, reflowInput->mStyleDisplay);
1652 
1653   aStatus = state.mReflowStatus;
1654 
1655 #ifdef DEBUG
1656   // Between when we drain pushed floats and when we complete reflow,
1657   // we're allowed to have multiple continuations of the same float on
1658   // our floats list, since a first-in-flow might get pushed to a later
1659   // continuation of its containing block.  But it's not permitted
1660   // outside that time.
1661   nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
1662 
1663   if (gNoisyReflow) {
1664     IndentBy(stdout, gNoiseIndent);
1665     ListTag(stdout);
1666     printf(": status=%s metrics=%d,%d carriedMargin=%d",
1667            ToString(aStatus).c_str(), aMetrics.ISize(parentWM),
1668            aMetrics.BSize(parentWM), aMetrics.mCarriedOutBEndMargin.get());
1669     if (HasOverflowAreas()) {
1670       printf(" overflow-vis={%d,%d,%d,%d}", aMetrics.InkOverflow().x,
1671              aMetrics.InkOverflow().y, aMetrics.InkOverflow().width,
1672              aMetrics.InkOverflow().height);
1673       printf(" overflow-scr={%d,%d,%d,%d}", aMetrics.ScrollableOverflow().x,
1674              aMetrics.ScrollableOverflow().y,
1675              aMetrics.ScrollableOverflow().width,
1676              aMetrics.ScrollableOverflow().height);
1677     }
1678     printf("\n");
1679   }
1680 
1681   if (gLameReflowMetrics) {
1682     PRTime end = PR_Now();
1683 
1684     int32_t ectc = nsLineBox::GetCtorCount();
1685     int32_t numLines = mLines.size();
1686     if (!numLines) numLines = 1;
1687     PRTime delta, perLineDelta, lines;
1688     lines = int64_t(numLines);
1689     delta = end - start;
1690     perLineDelta = delta / lines;
1691 
1692     ListTag(stdout);
1693     char buf[400];
1694     SprintfLiteral(buf,
1695                    ": %" PRId64 " elapsed (%" PRId64
1696                    " per line) (%d lines; %d new lines)",
1697                    delta, perLineDelta, numLines, ectc - ctc);
1698     printf("%s\n", buf);
1699   }
1700 #endif
1701 
1702   NS_FRAME_SET_TRUNCATION(aStatus, (*reflowInput), aMetrics);
1703 }
1704 
CheckForCollapsedBEndMarginFromClearanceLine()1705 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
1706   for (auto& line : Reversed(Lines())) {
1707     if (0 != line.BSize() || !line.CachedIsEmpty()) {
1708       return false;
1709     }
1710     if (line.HasClearance()) {
1711       return true;
1712     }
1713   }
1714   return false;
1715 }
1716 
FindLineClampTarget(nsBlockFrame * & aFrame,uint32_t aLineNumber)1717 static nsLineBox* FindLineClampTarget(nsBlockFrame*& aFrame,
1718                                       uint32_t aLineNumber) {
1719   MOZ_ASSERT(aLineNumber > 0);
1720   MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS),
1721              "Should have been removed earlier in nsBlockReflow::Reflow");
1722 
1723   nsLineBox* target = nullptr;
1724   nsBlockFrame* targetFrame = nullptr;
1725   bool foundFollowingLine = false;
1726 
1727   LineClampLineIterator iter(aFrame);
1728 
1729   while (nsLineBox* line = iter.GetCurrentLine()) {
1730     MOZ_ASSERT(!line->HasLineClampEllipsis(),
1731                "Should have been removed earlier in nsBlockFrame::Reflow");
1732     MOZ_ASSERT(!iter.GetCurrentFrame()->HasAnyStateBits(
1733                    NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS),
1734                "Should have been removed earlier in nsBlockReflow::Reflow");
1735 
1736     // Don't count a line that only has collapsible white space (as might exist
1737     // after calling e.g. getBoxQuads).
1738     if (line->IsEmpty()) {
1739       iter.Next();
1740       continue;
1741     }
1742 
1743     if (aLineNumber == 0) {
1744       // We already previously found our target line, and now we have
1745       // confirmed that there is another line after it.
1746       foundFollowingLine = true;
1747       break;
1748     }
1749 
1750     if (--aLineNumber == 0) {
1751       // This is our target line.  Continue looping to confirm that we
1752       // have another line after us.
1753       target = line;
1754       targetFrame = iter.GetCurrentFrame();
1755     }
1756 
1757     iter.Next();
1758   }
1759 
1760   if (!foundFollowingLine) {
1761     aFrame = nullptr;
1762     return nullptr;
1763   }
1764 
1765   MOZ_ASSERT(target);
1766   MOZ_ASSERT(targetFrame);
1767 
1768   aFrame = targetFrame;
1769   return target;
1770 }
1771 
ApplyLineClamp(const ReflowInput & aReflowInput,nsBlockFrame * aFrame,nscoord aContentBSize)1772 static nscoord ApplyLineClamp(const ReflowInput& aReflowInput,
1773                               nsBlockFrame* aFrame, nscoord aContentBSize) {
1774   // We only do the work of applying the -webkit-line-clamp value during the
1775   // measuring bsize reflow.  Boxes affected by -webkit-line-clamp are always
1776   // inflexible, so we will never need to select a different line to place the
1777   // ellipsis on in the subsequent real reflow.
1778   if (!IsLineClampItem(aReflowInput)) {
1779     return aContentBSize;
1780   }
1781 
1782   auto container =
1783       static_cast<nsFlexContainerFrame*>(nsLayoutUtils::GetClosestFrameOfType(
1784           aFrame, LayoutFrameType::FlexContainer));
1785   MOZ_ASSERT(container,
1786              "A flex item affected by -webkit-line-clamp must have an ancestor "
1787              "flex container");
1788 
1789   uint32_t lineClamp = container->GetLineClampValue();
1790   if (lineClamp == 0) {
1791     // -webkit-line-clamp is none or doesn't apply.
1792     return aContentBSize;
1793   }
1794 
1795   MOZ_ASSERT(container->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX),
1796              "Should only have an effective -webkit-line-clamp value if we "
1797              "are in a legacy flex container");
1798 
1799   nsBlockFrame* frame = aFrame;
1800   nsLineBox* line = FindLineClampTarget(frame, lineClamp);
1801   if (!line) {
1802     // The number of lines did not exceed the -webkit-line-clamp value.
1803     return aContentBSize;
1804   }
1805 
1806   // Mark the line as having an ellipsis so that TextOverflow will render it.
1807   line->SetHasLineClampEllipsis();
1808   frame->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
1809   container->AddStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS);
1810 
1811   // Translate the b-end edge of the line up to aFrame's space.
1812   nscoord edge = line->BEnd();
1813   for (nsIFrame* f = frame; f != aFrame; f = f->GetParent()) {
1814     edge +=
1815         f->GetLogicalPosition(f->GetParent()->GetSize()).B(f->GetWritingMode());
1816   }
1817 
1818   return edge;
1819 }
1820 
ShouldApplyAutomaticMinimumOnBlockAxis(WritingMode aWM,const nsStyleDisplay * aDisplay,const nsStylePosition * aPosition)1821 static bool ShouldApplyAutomaticMinimumOnBlockAxis(
1822     WritingMode aWM, const nsStyleDisplay* aDisplay,
1823     const nsStylePosition* aPosition) {
1824   // The automatic minimum size in the ratio-dependent axis of a box with a
1825   // preferred aspect ratio that is neither a replaced element nor a scroll
1826   // container is its min-content size clamped from above by its maximum size.
1827   //
1828   // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
1829   // Note: we only need to check scroll container because replaced element
1830   // doesn't go into nsBlockFrame::Reflow().
1831   return !aDisplay->IsScrollableOverflow() && aPosition->MinBSize(aWM).IsAuto();
1832 }
1833 
ComputeFinalSize(const ReflowInput & aReflowInput,BlockReflowState & aState,ReflowOutput & aMetrics,nscoord * aBEndEdgeOfChildren)1834 void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
1835                                     BlockReflowState& aState,
1836                                     ReflowOutput& aMetrics,
1837                                     nscoord* aBEndEdgeOfChildren) {
1838   WritingMode wm = aState.mReflowInput.GetWritingMode();
1839   const LogicalMargin& borderPadding = aState.BorderPadding();
1840 #ifdef NOISY_FINAL_SIZE
1841   ListTag(stdout);
1842   printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
1843          aState.mBCoord, aState.mFlags.mIsBEndMarginRoot ? "yes" : "no",
1844          aState.mPrevBEndMargin.get(), borderPadding.BStart(wm),
1845          borderPadding.BEnd(wm));
1846 #endif
1847 
1848   // Compute final inline size
1849   LogicalSize finalSize(wm);
1850   finalSize.ISize(wm) =
1851       NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.IStart(wm),
1852                                                 aReflowInput.ComputedISize()),
1853                            borderPadding.IEnd(wm));
1854 
1855   // Return block-end margin information
1856   // rbs says he hit this assertion occasionally (see bug 86947), so
1857   // just set the margin to zero and we'll figure out why later
1858   // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
1859   //             "someone else set the margin");
1860   nscoord nonCarriedOutBDirMargin = 0;
1861   if (!aState.mFlags.mIsBEndMarginRoot) {
1862     // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1863     // line with clearance and a non-zero block-start margin and all
1864     // subsequent lines are empty, then we do not allow our children's
1865     // carried out block-end margin to be carried out of us and collapse
1866     // with our own block-end margin.
1867     if (CheckForCollapsedBEndMarginFromClearanceLine()) {
1868       // Convert the children's carried out margin to something that
1869       // we will include in our height
1870       nonCarriedOutBDirMargin = aState.mPrevBEndMargin.get();
1871       aState.mPrevBEndMargin.Zero();
1872     }
1873     aMetrics.mCarriedOutBEndMargin = aState.mPrevBEndMargin;
1874   } else {
1875     aMetrics.mCarriedOutBEndMargin.Zero();
1876   }
1877 
1878   nscoord blockEndEdgeOfChildren = aState.mBCoord + nonCarriedOutBDirMargin;
1879   // Shrink wrap our height around our contents.
1880   if (aState.mFlags.mIsBEndMarginRoot ||
1881       NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) {
1882     // When we are a block-end-margin root make sure that our last
1883     // childs block-end margin is fully applied. We also do this when
1884     // we have a computed height, since in that case the carried out
1885     // margin is not going to be applied anywhere, so we should note it
1886     // here to be included in the overflow area.
1887     // Apply the margin only if there's space for it.
1888     if (blockEndEdgeOfChildren < aState.mReflowInput.AvailableBSize()) {
1889       // Truncate block-end margin if it doesn't fit to our available BSize.
1890       blockEndEdgeOfChildren =
1891           std::min(blockEndEdgeOfChildren + aState.mPrevBEndMargin.get(),
1892                    aState.mReflowInput.AvailableBSize());
1893     }
1894   }
1895   if (aState.mFlags.mBlockNeedsFloatManager) {
1896     // Include the float manager's state to properly account for the
1897     // block-end margin of any floated elements; e.g., inside a table cell.
1898     //
1899     // Note: The block coordinate returned by ClearFloats is always greater than
1900     // or equal to blockEndEdgeOfChildren.
1901     std::tie(blockEndEdgeOfChildren, std::ignore) =
1902         aState.ClearFloats(blockEndEdgeOfChildren, StyleClear::Both);
1903   }
1904 
1905   if (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) {
1906     // Note: We don't use blockEndEdgeOfChildren because it includes the
1907     // previous margin.
1908     const nscoord contentBSizeWithBStartBP =
1909         aState.mBCoord + nonCarriedOutBDirMargin;
1910     finalSize.BSize(wm) = ComputeFinalBSize(aState, contentBSizeWithBStartBP);
1911 
1912     // If the content block-size is larger than the effective computed
1913     // block-size, we extend the block-size to contain all the content.
1914     // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
1915     if (aReflowInput.mFlags.mIsBSizeSetByAspectRatio &&
1916         ShouldApplyAutomaticMinimumOnBlockAxis(wm, aReflowInput.mStyleDisplay,
1917                                                aReflowInput.mStylePosition)) {
1918       // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
1919       // the content's block-size plus our border and padding..
1920       finalSize.BSize(wm) =
1921           std::max(finalSize.BSize(wm),
1922                    contentBSizeWithBStartBP + borderPadding.BEnd(wm));
1923     }
1924 
1925     // Don't carry out a block-end margin when our BSize is fixed.
1926     //
1927     // Note: this also includes the case that aReflowInput.ComputedBSize() is
1928     // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
1929     // is replaced by the block size from aspect-ratio and inline size.
1930     aMetrics.mCarriedOutBEndMargin.Zero();
1931   } else if (!IsComboboxControlFrame() &&
1932              aReflowInput.mStyleDisplay->IsContainSize()) {
1933     // If we're size-containing and we don't have a specified size, then our
1934     // final size should actually be computed from only our border and padding,
1935     // as though we were empty.
1936     // Hence this case is a simplified version of the case below.
1937     //
1938     // NOTE: We exempt the nsComboboxControlFrame subclass from taking this
1939     // special case, because comboboxes implicitly honors the size-containment
1940     // behavior on its nsComboboxDisplayFrame child (which it shrinkwraps)
1941     // rather than on the nsComboboxControlFrame. (Moreover, the DisplayFrame
1942     // child doesn't even need any special content-size-ignoring behavior in
1943     // its reflow method, because that method just resolves "auto" BSize values
1944     // to one line-height rather than by measuring its contents' BSize.)
1945     nscoord contentBSize = 0;
1946     nscoord autoBSize =
1947         aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize);
1948     aMetrics.mCarriedOutBEndMargin.Zero();
1949     autoBSize += borderPadding.BStartEnd(wm);
1950     finalSize.BSize(wm) = autoBSize;
1951   } else if (aState.mReflowStatus.IsInlineBreakBefore()) {
1952     // Our parent is expected to push this frame to the next page/column so what
1953     // size we set here doesn't really matter.
1954     finalSize.BSize(wm) = aReflowInput.AvailableBSize();
1955   } else if (aState.mReflowStatus.IsComplete()) {
1956     nscoord contentBSize = blockEndEdgeOfChildren - borderPadding.BStart(wm);
1957     nscoord lineClampedContentBSize =
1958         ApplyLineClamp(aReflowInput, this, contentBSize);
1959     nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(lineClampedContentBSize,
1960                                                       aState.mConsumedBSize);
1961     if (autoBSize != contentBSize) {
1962       // Our min-block-size, max-block-size, or -webkit-line-clamp value made
1963       // our bsize change.  Don't carry out our kids' block-end margins.
1964       aMetrics.mCarriedOutBEndMargin.Zero();
1965     }
1966     nscoord bSize = autoBSize + borderPadding.BStartEnd(wm);
1967     if (MOZ_UNLIKELY(autoBSize > contentBSize &&
1968                      bSize > aReflowInput.AvailableBSize() &&
1969                      aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
1970       // Applying `min-size` made us overflow our available size.
1971       // Clamp it and report that we're Incomplete, or BreakBefore if we have
1972       // 'break-inside: avoid' that is applicable.
1973       bSize = aReflowInput.AvailableBSize();
1974       if (ShouldAvoidBreakInside(aReflowInput)) {
1975         aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
1976       } else {
1977         aState.mReflowStatus.SetIncomplete();
1978       }
1979     }
1980     finalSize.BSize(wm) = bSize;
1981   } else {
1982     NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
1983                  "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
1984     nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize());
1985     if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
1986       // This should never happen, but it does. See bug 414255
1987       bSize = aState.mBCoord;
1988     }
1989     const nscoord maxBSize = aReflowInput.ComputedMaxBSize();
1990     if (maxBSize != NS_UNCONSTRAINEDSIZE &&
1991         aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) {
1992       nscoord bEnd = std::max(0, maxBSize - aState.mConsumedBSize) +
1993                      borderPadding.BStart(wm);
1994       // Note that |borderPadding| has GetSkipSides applied, so we ask
1995       // aReflowInput for the actual value we'd use on a last fragment here:
1996       bEnd += aReflowInput.ComputedLogicalBorderPadding(wm).BEnd(wm);
1997       if (bEnd <= aReflowInput.AvailableBSize()) {
1998         // We actually fit after applying `max-size` so we should be
1999         // Overflow-Incomplete instead.
2000         bSize = bEnd;
2001         aState.mReflowStatus.SetOverflowIncomplete();
2002       }
2003     }
2004     finalSize.BSize(wm) = bSize;
2005   }
2006 
2007   if (IsTrueOverflowContainer()) {
2008     if (aState.mReflowStatus.IsIncomplete()) {
2009       // Overflow containers can only be overflow complete.
2010       // Note that auto height overflow containers have no normal children
2011       NS_ASSERTION(finalSize.BSize(wm) == 0,
2012                    "overflow containers must be zero-block-size");
2013       aState.mReflowStatus.SetOverflowIncomplete();
2014     }
2015   } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
2016              !aState.mReflowStatus.IsInlineBreakBefore() &&
2017              aState.mReflowStatus.IsComplete()) {
2018     // Currently only used for grid items, but could be used in other contexts.
2019     // The FragStretchBSizeProperty is our expected non-fragmented block-size
2020     // we should stretch to (for align-self:stretch etc).  In some fragmentation
2021     // cases though, the last fragment (this frame since we're complete), needs
2022     // to have extra size applied because earlier fragments consumed too much of
2023     // our computed size due to overflowing their containing block.  (E.g. this
2024     // ensures we fill the last row when a multi-row grid item is fragmented).
2025     bool found;
2026     nscoord bSize = GetProperty(FragStretchBSizeProperty(), &found);
2027     if (found) {
2028       finalSize.BSize(wm) = std::max(bSize, finalSize.BSize(wm));
2029     }
2030   }
2031 
2032   // Clamp the content size to fit within the margin-box clamp size, if any.
2033   if (MOZ_UNLIKELY(aReflowInput.mComputeSizeFlags.contains(
2034           ComputeSizeFlag::BClampMarginBoxMinSize)) &&
2035       aState.mReflowStatus.IsComplete()) {
2036     bool found;
2037     nscoord cbSize = GetProperty(BClampMarginBoxMinSizeProperty(), &found);
2038     if (found) {
2039       auto marginBoxBSize =
2040           finalSize.BSize(wm) +
2041           aReflowInput.ComputedLogicalMargin(wm).BStartEnd(wm);
2042       auto overflow = marginBoxBSize - cbSize;
2043       if (overflow > 0) {
2044         auto contentBSize = finalSize.BSize(wm) - borderPadding.BStartEnd(wm);
2045         auto newContentBSize = std::max(nscoord(0), contentBSize - overflow);
2046         // XXXmats deal with percentages better somehow?
2047         finalSize.BSize(wm) -= contentBSize - newContentBSize;
2048       }
2049     }
2050   }
2051 
2052   // Screen out negative block sizes --- can happen due to integer overflows :-(
2053   finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
2054   *aBEndEdgeOfChildren = blockEndEdgeOfChildren;
2055 
2056   if (blockEndEdgeOfChildren != finalSize.BSize(wm) - borderPadding.BEnd(wm)) {
2057     SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
2058   } else {
2059     RemoveProperty(BlockEndEdgeOfChildrenProperty());
2060   }
2061 
2062   aMetrics.SetSize(wm, finalSize);
2063 
2064 #ifdef DEBUG_blocks
2065   if ((ABSURD_SIZE(aMetrics.Width()) || ABSURD_SIZE(aMetrics.Height())) &&
2066       !GetParent()->IsAbsurdSizeAssertSuppressed()) {
2067     ListTag(stdout);
2068     printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
2069   }
2070 #endif
2071 }
2072 
ConsiderBlockEndEdgeOfChildren(OverflowAreas & aOverflowAreas,nscoord aBEndEdgeOfChildren,const nsStyleDisplay * aDisplay) const2073 void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
2074     OverflowAreas& aOverflowAreas, nscoord aBEndEdgeOfChildren,
2075     const nsStyleDisplay* aDisplay) const {
2076   const auto wm = GetWritingMode();
2077 
2078   // Factor in the block-end edge of the children.  Child frames will be added
2079   // to the overflow area as we iterate through the lines, but their margins
2080   // won't, so we need to account for block-end margins here.
2081   // REVIEW: For now, we do this for both visual and scrollable area,
2082   // although when we make scrollable overflow area not be a subset of
2083   // visual, we can change this.
2084 
2085   if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
2086     // If we are a scrolled inner frame, add our block-end padding to our
2087     // children's block-end edge.
2088     //
2089     // Note: aBEndEdgeOfChildren already includes our own block-start padding
2090     // because it is relative to our block-start edge of our border-box, which
2091     // is the same as our padding-box here.
2092     MOZ_ASSERT(GetLogicalUsedBorderAndPadding(wm) == GetLogicalUsedPadding(wm),
2093                "A scrolled inner frame shouldn't have any border!");
2094     aBEndEdgeOfChildren += GetLogicalUsedPadding(wm).BEnd(wm);
2095   }
2096 
2097   // XXX Currently, overflow areas are stored as physical rects, so we have
2098   // to handle writing modes explicitly here. If we change overflow rects
2099   // to be stored logically, this can be simplified again.
2100   if (wm.IsVertical()) {
2101     if (wm.IsVerticalLR()) {
2102       for (const auto otype : AllOverflowTypes()) {
2103         if (!(aDisplay->IsContainLayout() &&
2104               otype == OverflowType::Scrollable)) {
2105           // Layout containment should force all overflow to be ink (visual)
2106           // overflow, so if we're layout-contained, we only add our children's
2107           // block-end edge to the ink (visual) overflow -- not to the
2108           // scrollable overflow.
2109           nsRect& o = aOverflowAreas.Overflow(otype);
2110           o.width = std::max(o.XMost(), aBEndEdgeOfChildren) - o.x;
2111         }
2112       }
2113     } else {
2114       for (const auto otype : AllOverflowTypes()) {
2115         if (!(aDisplay->IsContainLayout() &&
2116               otype == OverflowType::Scrollable)) {
2117           nsRect& o = aOverflowAreas.Overflow(otype);
2118           nscoord xmost = o.XMost();
2119           o.x = std::min(o.x, xmost - aBEndEdgeOfChildren);
2120           o.width = xmost - o.x;
2121         }
2122       }
2123     }
2124   } else {
2125     for (const auto otype : AllOverflowTypes()) {
2126       if (!(aDisplay->IsContainLayout() && otype == OverflowType::Scrollable)) {
2127         nsRect& o = aOverflowAreas.Overflow(otype);
2128         o.height = std::max(o.YMost(), aBEndEdgeOfChildren) - o.y;
2129       }
2130     }
2131   }
2132 }
2133 
ComputeOverflowAreas(OverflowAreas & aOverflowAreas,nscoord aBEndEdgeOfChildren,const nsStyleDisplay * aDisplay) const2134 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas& aOverflowAreas,
2135                                         nscoord aBEndEdgeOfChildren,
2136                                         const nsStyleDisplay* aDisplay) const {
2137   // XXX_perf: This can be done incrementally.  It is currently one of
2138   // the things that makes incremental reflow O(N^2).
2139   if (ShouldApplyOverflowClipping(aDisplay) != PhysicalAxes::Both) {
2140     for (const auto& line : Lines()) {
2141       if (aDisplay->IsContainLayout()) {
2142         // If we have layout containment, we should only consider our child's
2143         // ink overflow, leaving the scrollable regions of the parent
2144         // unaffected.
2145         // Note: scrollable overflow is a subset of ink overflow,
2146         // so this has the same affect as unioning the child's visual and
2147         // scrollable overflow with its parent's ink overflow.
2148         nsRect childVisualRect = line.InkOverflowRect();
2149         OverflowAreas childVisualArea =
2150             OverflowAreas(childVisualRect, nsRect());
2151         aOverflowAreas.UnionWith(childVisualArea);
2152       } else {
2153         aOverflowAreas.UnionWith(line.GetOverflowAreas());
2154       }
2155     }
2156 
2157     // Factor an outside ::marker in; normally the ::marker will be factored
2158     // into the line-box's overflow areas. However, if the line is a block
2159     // line then it won't; if there are no lines, it won't. So just
2160     // factor it in anyway (it can't hurt if it was already done).
2161     // XXXldb Can we just fix GetOverflowArea instead?
2162     if (nsIFrame* outsideMarker = GetOutsideMarker()) {
2163       aOverflowAreas.UnionAllWith(outsideMarker->GetRect());
2164     }
2165 
2166     ConsiderBlockEndEdgeOfChildren(aOverflowAreas, aBEndEdgeOfChildren,
2167                                    aDisplay);
2168   }
2169 
2170 #ifdef NOISY_OVERFLOW_AREAS
2171   printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
2172          ToString(aOverflowAreas.InkOverflow()).c_str(),
2173          ToString(aOverflowAreas.ScrollableOverflow()).c_str());
2174 #endif
2175 }
2176 
UnionChildOverflow(OverflowAreas & aOverflowAreas)2177 void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
2178   // We need to update the overflow areas of lines manually, as they
2179   // get cached and re-used otherwise. Lines aren't exposed as normal
2180   // frame children, so calling UnionChildOverflow alone will end up
2181   // using the old cached values.
2182   for (auto& line : Lines()) {
2183     nsRect bounds = line.GetPhysicalBounds();
2184     OverflowAreas lineAreas(bounds, bounds);
2185 
2186     int32_t n = line.GetChildCount();
2187     for (nsIFrame* lineFrame = line.mFirstChild; n > 0;
2188          lineFrame = lineFrame->GetNextSibling(), --n) {
2189       ConsiderChildOverflow(lineAreas, lineFrame);
2190     }
2191 
2192     // Consider the overflow areas of the floats attached to the line as well
2193     if (line.HasFloats()) {
2194       for (nsFloatCache* fc = line.GetFirstFloat(); fc; fc = fc->Next()) {
2195         ConsiderChildOverflow(lineAreas, fc->mFloat);
2196       }
2197     }
2198 
2199     line.SetOverflowAreas(lineAreas);
2200     aOverflowAreas.UnionWith(lineAreas);
2201   }
2202 
2203   // Union with child frames, skipping the principal and float lists
2204   // since we already handled those using the line boxes.
2205   nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas,
2206                                     {kPrincipalList, kFloatList});
2207 }
2208 
ComputeCustomOverflow(OverflowAreas & aOverflowAreas)2209 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
2210   bool found;
2211   nscoord blockEndEdgeOfChildren =
2212       GetProperty(BlockEndEdgeOfChildrenProperty(), &found);
2213   if (found) {
2214     ConsiderBlockEndEdgeOfChildren(aOverflowAreas, blockEndEdgeOfChildren,
2215                                    StyleDisplay());
2216   }
2217 
2218   // Line cursor invariants depend on the overflow areas of the lines, so
2219   // we must clear the line cursor since those areas may have changed.
2220   ClearLineCursor();
2221   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
2222 }
2223 
LazyMarkLinesDirty()2224 void nsBlockFrame::LazyMarkLinesDirty() {
2225   if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)) {
2226     for (LineIterator line = LinesBegin(), line_end = LinesEnd();
2227          line != line_end; ++line) {
2228       int32_t n = line->GetChildCount();
2229       for (nsIFrame* lineFrame = line->mFirstChild; n > 0;
2230            lineFrame = lineFrame->GetNextSibling(), --n) {
2231         if (lineFrame->IsSubtreeDirty()) {
2232           // NOTE:  MarkLineDirty does more than just marking the line dirty.
2233           MarkLineDirty(line, &mLines);
2234           break;
2235         }
2236       }
2237     }
2238     RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
2239   }
2240 }
2241 
MarkLineDirty(LineIterator aLine,const nsLineList * aLineList)2242 void nsBlockFrame::MarkLineDirty(LineIterator aLine,
2243                                  const nsLineList* aLineList) {
2244   // Mark aLine dirty
2245   aLine->MarkDirty();
2246   aLine->SetInvalidateTextRuns(true);
2247 #ifdef DEBUG
2248   if (gNoisyReflow) {
2249     IndentBy(stdout, gNoiseIndent);
2250     ListTag(stdout);
2251     printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
2252   }
2253 #endif
2254 
2255   // Mark previous line dirty if it's an inline line so that it can
2256   // maybe pullup something from the line just affected.
2257   // XXX We don't need to do this if aPrevLine ends in a break-after...
2258   if (aLine != aLineList->front() && aLine->IsInline() &&
2259       aLine.prev()->IsInline()) {
2260     aLine.prev()->MarkDirty();
2261     aLine.prev()->SetInvalidateTextRuns(true);
2262 #ifdef DEBUG
2263     if (gNoisyReflow) {
2264       IndentBy(stdout, gNoiseIndent);
2265       ListTag(stdout);
2266       printf(": mark prev-line %p dirty\n",
2267              static_cast<void*>(aLine.prev().get()));
2268     }
2269 #endif
2270   }
2271 }
2272 
2273 /**
2274  * Test whether lines are certain to be aligned left so that we can make
2275  * resizing optimizations
2276  */
IsAlignedLeft(StyleTextAlign aAlignment,StyleDirection aDirection,uint8_t aUnicodeBidi,nsIFrame * aFrame)2277 static inline bool IsAlignedLeft(StyleTextAlign aAlignment,
2278                                  StyleDirection aDirection,
2279                                  uint8_t aUnicodeBidi, nsIFrame* aFrame) {
2280   return SVGUtils::IsInSVGTextSubtree(aFrame) ||
2281          StyleTextAlign::Left == aAlignment ||
2282          (((StyleTextAlign::Start == aAlignment &&
2283             StyleDirection::Ltr == aDirection) ||
2284            (StyleTextAlign::End == aAlignment &&
2285             StyleDirection::Rtl == aDirection)) &&
2286           !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
2287 }
2288 
PrepareResizeReflow(BlockReflowState & aState)2289 void nsBlockFrame::PrepareResizeReflow(BlockReflowState& aState) {
2290   // See if we can try and avoid marking all the lines as dirty
2291   // FIXME(emilio): This should be writing-mode aware, I guess.
2292   bool tryAndSkipLines =
2293       // The left content-edge must be a constant distance from the left
2294       // border-edge.
2295       !StylePadding()->mPadding.Get(eSideLeft).HasPercent();
2296 
2297 #ifdef DEBUG
2298   if (gDisableResizeOpt) {
2299     tryAndSkipLines = false;
2300   }
2301   if (gNoisyReflow) {
2302     if (!tryAndSkipLines) {
2303       IndentBy(stdout, gNoiseIndent);
2304       ListTag(stdout);
2305       printf(": marking all lines dirty: availISize=%d\n",
2306              aState.mReflowInput.AvailableISize());
2307     }
2308   }
2309 #endif
2310 
2311   if (tryAndSkipLines) {
2312     WritingMode wm = aState.mReflowInput.GetWritingMode();
2313     nscoord newAvailISize =
2314         aState.mReflowInput.ComputedLogicalBorderPadding(wm).IStart(wm) +
2315         aState.mReflowInput.ComputedISize();
2316 
2317 #ifdef DEBUG
2318     if (gNoisyReflow) {
2319       IndentBy(stdout, gNoiseIndent);
2320       ListTag(stdout);
2321       printf(": trying to avoid marking all lines dirty\n");
2322     }
2323 #endif
2324 
2325     for (LineIterator line = LinesBegin(), line_end = LinesEnd();
2326          line != line_end; ++line) {
2327       // We let child blocks make their own decisions the same
2328       // way we are here.
2329       bool isLastLine = line == mLines.back() && !GetNextInFlow();
2330       if (line->IsBlock() || line->HasFloats() ||
2331           (!isLastLine && !line->HasBreakAfter()) ||
2332           ((isLastLine || !line->IsLineWrapped())) ||
2333           line->ResizeReflowOptimizationDisabled() ||
2334           line->IsImpactedByFloat() || (line->IEnd() > newAvailISize)) {
2335         line->MarkDirty();
2336       }
2337 
2338 #ifdef REALLY_NOISY_REFLOW
2339       if (!line->IsBlock()) {
2340         printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
2341                line.get(), line->IsImpactedByFloat() ? "" : "not ");
2342       }
2343 #endif
2344 #ifdef DEBUG
2345       if (gNoisyReflow && !line->IsDirty()) {
2346         IndentBy(stdout, gNoiseIndent + 1);
2347         printf(
2348             "skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%s/%s "
2349             "xmost=%d\n",
2350             static_cast<void*>(line.get()),
2351             static_cast<void*>(
2352                 (line.next() != LinesEnd() ? line.next().get() : nullptr)),
2353             line->IsBlock() ? "block" : "inline",
2354             line->HasBreakAfter() ? "has-break-after " : "",
2355             line->HasFloats() ? "has-floats " : "",
2356             line->IsImpactedByFloat() ? "impacted " : "",
2357             line->BreakTypeToString(line->GetBreakTypeBefore()),
2358             line->BreakTypeToString(line->GetBreakTypeAfter()), line->IEnd());
2359       }
2360 #endif
2361     }
2362   } else {
2363     // Mark everything dirty
2364     for (auto& line : Lines()) {
2365       line.MarkDirty();
2366     }
2367   }
2368 }
2369 
2370 //----------------------------------------
2371 
2372 /**
2373  * Propagate reflow "damage" from from earlier lines to the current
2374  * line.  The reflow damage comes from the following sources:
2375  *  1. The regions of float damage remembered during reflow.
2376  *  2. The combination of nonzero |aDeltaBCoord| and any impact by a
2377  *     float, either the previous reflow or now.
2378  *
2379  * When entering this function, |aLine| is still at its old position and
2380  * |aDeltaBCoord| indicates how much it will later be slid (assuming it
2381  * doesn't get marked dirty and reflowed entirely).
2382  */
PropagateFloatDamage(BlockReflowState & aState,nsLineBox * aLine,nscoord aDeltaBCoord)2383 void nsBlockFrame::PropagateFloatDamage(BlockReflowState& aState,
2384                                         nsLineBox* aLine,
2385                                         nscoord aDeltaBCoord) {
2386   nsFloatManager* floatManager = aState.FloatManager();
2387   NS_ASSERTION(
2388       (aState.mReflowInput.mParentReflowInput &&
2389        aState.mReflowInput.mParentReflowInput->mFloatManager == floatManager) ||
2390           aState.mReflowInput.mBlockDelta == 0,
2391       "Bad block delta passed in");
2392 
2393   // Check to see if there are any floats; if there aren't, there can't
2394   // be any float damage
2395   if (!floatManager->HasAnyFloats()) return;
2396 
2397   // Check the damage region recorded in the float damage.
2398   if (floatManager->HasFloatDamage()) {
2399     // Need to check mBounds *and* mCombinedArea to find intersections
2400     // with aLine's floats
2401     nscoord lineBCoordBefore = aLine->BStart() + aDeltaBCoord;
2402     nscoord lineBCoordAfter = lineBCoordBefore + aLine->BSize();
2403     // Scrollable overflow should be sufficient for things that affect
2404     // layout.
2405     WritingMode wm = aState.mReflowInput.GetWritingMode();
2406     nsSize containerSize = aState.ContainerSize();
2407     LogicalRect overflow =
2408         aLine->GetOverflowArea(OverflowType::Scrollable, wm, containerSize);
2409     nscoord lineBCoordCombinedBefore = overflow.BStart(wm) + aDeltaBCoord;
2410     nscoord lineBCoordCombinedAfter =
2411         lineBCoordCombinedBefore + overflow.BSize(wm);
2412 
2413     bool isDirty =
2414         floatManager->IntersectsDamage(lineBCoordBefore, lineBCoordAfter) ||
2415         floatManager->IntersectsDamage(lineBCoordCombinedBefore,
2416                                        lineBCoordCombinedAfter);
2417     if (isDirty) {
2418       aLine->MarkDirty();
2419       return;
2420     }
2421   }
2422 
2423   // Check if the line is moving relative to the float manager
2424   if (aDeltaBCoord + aState.mReflowInput.mBlockDelta != 0) {
2425     if (aLine->IsBlock()) {
2426       // Unconditionally reflow sliding blocks; we only really need to reflow
2427       // if there's a float impacting this block, but the current float manager
2428       // makes it difficult to check that.  Therefore, we let the child block
2429       // decide what it needs to reflow.
2430       aLine->MarkDirty();
2431     } else {
2432       bool wasImpactedByFloat = aLine->IsImpactedByFloat();
2433       nsFlowAreaRect floatAvailableSpace =
2434           aState.GetFloatAvailableSpaceForBSize(aLine->BStart() + aDeltaBCoord,
2435                                                 aLine->BSize(), nullptr);
2436 
2437 #ifdef REALLY_NOISY_REFLOW
2438       printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
2439              wasImpactedByFloat, floatAvailableSpace.HasFloats());
2440 #endif
2441 
2442       // Mark the line dirty if it was or is affected by a float
2443       // We actually only really need to reflow if the amount of impact
2444       // changes, but that's not straightforward to check
2445       if (wasImpactedByFloat || floatAvailableSpace.HasFloats()) {
2446         aLine->MarkDirty();
2447       }
2448     }
2449   }
2450 }
2451 
LineHasClear(nsLineBox * aLine)2452 static bool LineHasClear(nsLineBox* aLine) {
2453   return aLine->IsBlock()
2454              ? (aLine->GetBreakTypeBefore() != StyleClear::None ||
2455                 aLine->mFirstChild->HasAnyStateBits(
2456                     NS_BLOCK_HAS_CLEAR_CHILDREN) ||
2457                 !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
2458              : aLine->HasFloatBreakAfter();
2459 }
2460 
2461 /**
2462  * Reparent a whole list of floats from aOldParent to this block.  The
2463  * floats might be taken from aOldParent's overflow list. They will be
2464  * removed from the list. They end up appended to our mFloats list.
2465  */
ReparentFloats(nsIFrame * aFirstFrame,nsBlockFrame * aOldParent,bool aReparentSiblings)2466 void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
2467                                   nsBlockFrame* aOldParent,
2468                                   bool aReparentSiblings) {
2469   nsFrameList list;
2470   aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
2471   if (list.NotEmpty()) {
2472     for (nsIFrame* f : list) {
2473       MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT),
2474                  "CollectFloats should've removed that bit");
2475       ReparentFrame(f, aOldParent, this);
2476     }
2477     mFloats.AppendFrames(nullptr, list);
2478   }
2479 }
2480 
DumpLine(const BlockReflowState & aState,nsLineBox * aLine,nscoord aDeltaBCoord,int32_t aDeltaIndent)2481 static void DumpLine(const BlockReflowState& aState, nsLineBox* aLine,
2482                      nscoord aDeltaBCoord, int32_t aDeltaIndent) {
2483 #ifdef DEBUG
2484   if (nsBlockFrame::gNoisyReflow) {
2485     nsRect ovis(aLine->InkOverflowRect());
2486     nsRect oscr(aLine->ScrollableOverflowRect());
2487     nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
2488     printf(
2489         "line=%p mBCoord=%d dirty=%s oldBounds={%d,%d,%d,%d} "
2490         "oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} "
2491         "deltaBCoord=%d mPrevBEndMargin=%d childCount=%d\n",
2492         static_cast<void*>(aLine), aState.mBCoord,
2493         aLine->IsDirty() ? "yes" : "no", aLine->IStart(), aLine->BStart(),
2494         aLine->ISize(), aLine->BSize(), ovis.x, ovis.y, ovis.width, ovis.height,
2495         oscr.x, oscr.y, oscr.width, oscr.height, aDeltaBCoord,
2496         aState.mPrevBEndMargin.get(), aLine->GetChildCount());
2497   }
2498 #endif
2499 }
2500 
LinesAreEmpty(const nsLineList & aList)2501 static bool LinesAreEmpty(const nsLineList& aList) {
2502   for (const auto& line : aList) {
2503     if (!line.IsEmpty()) {
2504       return false;
2505     }
2506   }
2507   return true;
2508 }
2509 
ReflowDirtyLines(BlockReflowState & aState)2510 void nsBlockFrame::ReflowDirtyLines(BlockReflowState& aState) {
2511   bool keepGoing = true;
2512   bool repositionViews = false;  // should we really need this?
2513   bool foundAnyClears = aState.mFloatBreakType != StyleClear::None;
2514   bool willReflowAgain = false;
2515 
2516 #ifdef DEBUG
2517   if (gNoisyReflow) {
2518     IndentBy(stdout, gNoiseIndent);
2519     ListTag(stdout);
2520     printf(": reflowing dirty lines");
2521     printf(" computedISize=%d\n", aState.mReflowInput.ComputedISize());
2522   }
2523   AutoNoisyIndenter indent(gNoisyReflow);
2524 #endif
2525 
2526   bool selfDirty = HasAnyStateBits(NS_FRAME_IS_DIRTY) ||
2527                    (aState.mReflowInput.IsBResize() &&
2528                     HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE));
2529 
2530   // Reflow our last line if our availableBSize has increased
2531   // so that we (and our last child) pull up content as necessary
2532   if (aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
2533       GetNextInFlow() &&
2534       aState.mReflowInput.AvailableBSize() >
2535           GetLogicalSize().BSize(aState.mReflowInput.GetWritingMode())) {
2536     LineIterator lastLine = LinesEnd();
2537     if (lastLine != LinesBegin()) {
2538       --lastLine;
2539       lastLine->MarkDirty();
2540     }
2541   }
2542   // the amount by which we will slide the current line if it is not
2543   // dirty
2544   nscoord deltaBCoord = 0;
2545 
2546   // whether we did NOT reflow the previous line and thus we need to
2547   // recompute the carried out margin before the line if we want to
2548   // reflow it or if its previous margin is dirty
2549   bool needToRecoverState = false;
2550   // Float continuations were reflowed in ReflowPushedFloats
2551   bool reflowedFloat =
2552       mFloats.NotEmpty() &&
2553       mFloats.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT);
2554   bool lastLineMovedUp = false;
2555   // We save up information about BR-clearance here
2556   StyleClear inlineFloatBreakType = aState.mFloatBreakType;
2557 
2558   LineIterator line = LinesBegin(), line_end = LinesEnd();
2559 
2560   // Reflow the lines that are already ours
2561   for (; line != line_end; ++line, aState.AdvanceToNextLine()) {
2562     DumpLine(aState, line, deltaBCoord, 0);
2563 #ifdef DEBUG
2564     AutoNoisyIndenter indent2(gNoisyReflow);
2565 #endif
2566 
2567     if (selfDirty) line->MarkDirty();
2568 
2569     // This really sucks, but we have to look inside any blocks that have clear
2570     // elements inside them.
2571     // XXX what can we do smarter here?
2572     if (!line->IsDirty() && line->IsBlock() &&
2573         line->mFirstChild->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN)) {
2574       line->MarkDirty();
2575     }
2576 
2577     nsIFrame* replacedBlock = nullptr;
2578     if (line->IsBlock() &&
2579         !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
2580       replacedBlock = line->mFirstChild;
2581     }
2582 
2583     // We have to reflow the line if it's a block whose clearance
2584     // might have changed, so detect that.
2585     if (!line->IsDirty() &&
2586         (line->GetBreakTypeBefore() != StyleClear::None || replacedBlock)) {
2587       nscoord curBCoord = aState.mBCoord;
2588       // See where we would be after applying any clearance due to
2589       // BRs.
2590       if (inlineFloatBreakType != StyleClear::None) {
2591         std::tie(curBCoord, std::ignore) =
2592             aState.ClearFloats(curBCoord, inlineFloatBreakType);
2593       }
2594 
2595       auto [newBCoord, result] = aState.ClearFloats(
2596           curBCoord, line->GetBreakTypeBefore(), replacedBlock);
2597 
2598       if (line->HasClearance()) {
2599         // Reflow the line if it might not have clearance anymore.
2600         if (result == ClearFloatsResult::BCoordNoChange
2601             // aState.mBCoord is the clearance point which should be the
2602             // block-start border-edge of the block frame. If sliding the
2603             // block by deltaBCoord isn't going to put it in the predicted
2604             // position, then we'd better reflow the line.
2605             || newBCoord != line->BStart() + deltaBCoord) {
2606           line->MarkDirty();
2607         }
2608       } else {
2609         // Reflow the line if the line might have clearance now.
2610         if (result != ClearFloatsResult::BCoordNoChange) {
2611           line->MarkDirty();
2612         }
2613       }
2614     }
2615 
2616     // We might have to reflow a line that is after a clearing BR.
2617     if (inlineFloatBreakType != StyleClear::None) {
2618       std::tie(aState.mBCoord, std::ignore) =
2619           aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
2620       if (aState.mBCoord != line->BStart() + deltaBCoord) {
2621         // SlideLine is not going to put the line where the clearance
2622         // put it. Reflow the line to be sure.
2623         line->MarkDirty();
2624       }
2625       inlineFloatBreakType = StyleClear::None;
2626     }
2627 
2628     bool previousMarginWasDirty = line->IsPreviousMarginDirty();
2629     if (previousMarginWasDirty) {
2630       // If the previous margin is dirty, reflow the current line
2631       line->MarkDirty();
2632       line->ClearPreviousMarginDirty();
2633     } else if (aState.ContentBSize() != NS_UNCONSTRAINEDSIZE) {
2634       const nscoord scrollableOverflowBEnd =
2635           LogicalRect(line->mWritingMode, line->ScrollableOverflowRect(),
2636                       line->mContainerSize)
2637               .BEnd(line->mWritingMode);
2638       if (scrollableOverflowBEnd + deltaBCoord > aState.ContentBEnd()) {
2639         // Lines that aren't dirty but get slid past our available block-size
2640         // constraint must be reflowed.
2641         line->MarkDirty();
2642       }
2643     }
2644 
2645     if (!line->IsDirty()) {
2646       const bool isPaginated =
2647           // Last column can be reflowed unconstrained during column balancing.
2648           // Hence the additional GetPrevInFlow() and GetNextInFlow() as a
2649           // fail-safe fallback.
2650           aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE ||
2651           GetPrevInFlow() || GetNextInFlow() ||
2652           // Table can also be reflowed unconstrained during printing.
2653           aState.mPresContext->IsPaginated();
2654       if (isPaginated) {
2655         // We are in a paginated context, i.e. in columns or pages.
2656         const bool mayContainFloats =
2657             line->IsBlock() || line->HasFloats() || line->HadFloatPushed();
2658         if (mayContainFloats) {
2659           // The following if-else conditions check whether this line -- which
2660           // might have floats in its subtree, or has floats as direct children,
2661           // or had floats pushed -- needs to be reflowed.
2662           if (deltaBCoord != 0 || aState.mReflowInput.IsBResize()) {
2663             // The distance to the block-end edge might have changed. Reflow the
2664             // line both because the breakpoints within its floats may have
2665             // changed and because we might have to push/pull the floats in
2666             // their entirety.
2667             line->MarkDirty();
2668           } else if (HasPushedFloats()) {
2669             // We had pushed floats which haven't been drained by our
2670             // next-in-flow, which means our parent is currently reflowing us
2671             // again due to clearance without creating a next-in-flow for us.
2672             // Reflow the line to redo the floats split logic to correctly set
2673             // our reflow status.
2674             line->MarkDirty();
2675           } else if (aState.mReflowInput.mFlags.mMustReflowPlaceholders) {
2676             // Reflow the line (that may containing a float's placeholder frame)
2677             // if our parent tells us to do so.
2678             line->MarkDirty();
2679           } else if (aState.mReflowInput.mFlags.mMovedBlockFragments) {
2680             // Our parent's line containing us moved to a different fragment.
2681             // Reflow the line because the decision about whether the float fits
2682             // may be different in a different fragment.
2683             line->MarkDirty();
2684           }
2685         }
2686       }
2687     }
2688 
2689     if (!line->IsDirty()) {
2690       // See if there's any reflow damage that requires that we mark the
2691       // line dirty.
2692       PropagateFloatDamage(aState, line, deltaBCoord);
2693     }
2694 
2695     // If the container size has changed, reset mContainerSize. If the
2696     // line's writing mode is not ltr, or if the line is not left-aligned, also
2697     // mark the line dirty.
2698     if (aState.ContainerSize() != line->mContainerSize) {
2699       line->mContainerSize = aState.ContainerSize();
2700 
2701       const bool isLastLine = line == mLines.back() && !GetNextInFlow();
2702       const auto align = isLastLine ? StyleText()->TextAlignForLastLine()
2703                                     : StyleText()->mTextAlign;
2704       if (line->mWritingMode.IsVertical() || line->mWritingMode.IsBidiRTL() ||
2705           !IsAlignedLeft(align,
2706                          aState.mReflowInput.mStyleVisibility->mDirection,
2707                          StyleTextReset()->mUnicodeBidi, this)) {
2708         line->MarkDirty();
2709       }
2710     }
2711 
2712     if (needToRecoverState && line->IsDirty()) {
2713       // We need to reconstruct the block-end margin only if we didn't
2714       // reflow the previous line and we do need to reflow (or repair
2715       // the block-start position of) the next line.
2716       aState.ReconstructMarginBefore(line);
2717     }
2718 
2719     bool reflowedPrevLine = !needToRecoverState;
2720     if (needToRecoverState) {
2721       needToRecoverState = false;
2722 
2723       // Update aState.mPrevChild as if we had reflowed all of the frames in
2724       // this line.
2725       if (line->IsDirty()) {
2726         NS_ASSERTION(
2727             line->mFirstChild->GetPrevSibling() == line.prev()->LastChild(),
2728             "unexpected line frames");
2729         aState.mPrevChild = line->mFirstChild->GetPrevSibling();
2730       }
2731     }
2732 
2733     // Now repair the line and update |aState.mBCoord| by calling
2734     // |ReflowLine| or |SlideLine|.
2735     // If we're going to reflow everything again, then no need to reflow
2736     // the dirty line ... unless the line has floats, in which case we'd
2737     // better reflow it now to refresh its float cache, which may contain
2738     // dangling frame pointers! Ugh! This reflow of the line may be
2739     // incorrect because we skipped reflowing previous lines (e.g., floats
2740     // may be placed incorrectly), but that's OK because we'll mark the
2741     // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
2742     if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
2743       lastLineMovedUp = true;
2744 
2745       bool maybeReflowingForFirstTime =
2746           line->IStart() == 0 && line->BStart() == 0 && line->ISize() == 0 &&
2747           line->BSize() == 0;
2748 
2749       // Compute the dirty lines "before" BEnd, after factoring in
2750       // the running deltaBCoord value - the running value is implicit in
2751       // aState.mBCoord.
2752       nscoord oldB = line->BStart();
2753       nscoord oldBMost = line->BEnd();
2754 
2755       NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
2756                    "Don't reflow blocks while willReflowAgain is true, reflow "
2757                    "of block abs-pos children depends on this");
2758 
2759       // Reflow the dirty line. If it's an incremental reflow, then force
2760       // it to invalidate the dirty area if necessary
2761       ReflowLine(aState, line, &keepGoing);
2762 
2763       if (aState.mReflowInput.WillReflowAgainForClearance()) {
2764         line->MarkDirty();
2765         willReflowAgain = true;
2766         // Note that once we've entered this state, every line that gets here
2767         // (e.g. because it has floats) gets marked dirty and reflowed again.
2768         // in the next pass. This is important, see above.
2769       }
2770 
2771       if (line->HasFloats()) {
2772         reflowedFloat = true;
2773       }
2774 
2775       if (!keepGoing) {
2776         DumpLine(aState, line, deltaBCoord, -1);
2777         if (0 == line->GetChildCount()) {
2778           DeleteLine(aState, line, line_end);
2779         }
2780         break;
2781       }
2782 
2783       // Test to see whether the margin that should be carried out
2784       // to the next line (NL) might have changed. In ReflowBlockFrame
2785       // we call nextLine->MarkPreviousMarginDirty if the block's
2786       // actual carried-out block-end margin changed. So here we only
2787       // need to worry about the following effects:
2788       // 1) the line was just created, and it might now be blocking
2789       // a carried-out block-end margin from previous lines that
2790       // used to reach NL from reaching NL
2791       // 2) the line used to be empty, and is now not empty,
2792       // thus blocking a carried-out block-end margin from previous lines
2793       // that used to reach NL from reaching NL
2794       // 3) the line wasn't empty, but now is, so a carried-out
2795       // block-end margin from previous lines that didn't used to reach NL
2796       // now does
2797       // 4) the line might have changed in a way that affects NL's
2798       // ShouldApplyBStartMargin decision. The three things that matter
2799       // are the line's emptiness, its adjacency to the block-start edge of the
2800       // block, and whether it has clearance (the latter only matters if the
2801       // block was and is adjacent to the block-start and empty).
2802       //
2803       // If the line is empty now, we can't reliably tell if the line was empty
2804       // before, so we just assume it was and do
2805       // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
2806       // redundant; if the line is empty now we don't need to check 4), but if
2807       // the line is not empty now and we're sure it wasn't empty before, any
2808       // adjacency and clearance changes are irrelevant to the result of
2809       // nextLine->ShouldApplyBStartMargin.
2810       if (line.next() != LinesEnd()) {
2811         bool maybeWasEmpty = oldB == line.next()->BStart();
2812         bool isEmpty = line->CachedIsEmpty();
2813         if (maybeReflowingForFirstTime /*1*/ ||
2814             (isEmpty || maybeWasEmpty) /*2/3/4*/) {
2815           line.next()->MarkPreviousMarginDirty();
2816           // since it's marked dirty, nobody will care about |deltaBCoord|
2817         }
2818       }
2819 
2820       // If the line was just reflowed for the first time, then its
2821       // old mBounds cannot be trusted so this deltaBCoord computation is
2822       // bogus. But that's OK because we just did
2823       // MarkPreviousMarginDirty on the next line which will force it
2824       // to be reflowed, so this computation of deltaBCoord will not be
2825       // used.
2826       deltaBCoord = line->BEnd() - oldBMost;
2827 
2828       // Now do an interrupt check. We want to do this only in the case when we
2829       // actually reflow the line, so that if we get back in here we'll get
2830       // further on the reflow before interrupting.
2831       aState.mPresContext->CheckForInterrupt(this);
2832     } else {
2833       aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
2834       // Nop except for blocks (we don't create overflow container
2835       // continuations for any inlines atm), so only checking mFirstChild
2836       // is enough
2837 
2838       lastLineMovedUp = deltaBCoord < 0;
2839 
2840       if (deltaBCoord != 0)
2841         SlideLine(aState, line, deltaBCoord);
2842       else
2843         repositionViews = true;
2844 
2845       NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
2846                    "Possibly stale float cache here!");
2847       if (willReflowAgain && line->IsBlock()) {
2848         // If we're going to reflow everything again, and this line is a block,
2849         // then there is no need to recover float state. The line may contain
2850         // other lines with floats, but in that case RecoverStateFrom would only
2851         // add floats to the float manager. We don't need to do that because
2852         // everything's going to get reflowed again "for real". Calling
2853         // RecoverStateFrom in this situation could be lethal because the
2854         // block's descendant lines may have float caches containing dangling
2855         // frame pointers. Ugh!
2856         // If this line is inline, then we need to recover its state now
2857         // to make sure that we don't forget to move its floats by deltaBCoord.
2858       } else {
2859         // XXX EVIL O(N^2) EVIL
2860         aState.RecoverStateFrom(line, deltaBCoord);
2861       }
2862 
2863       // Keep mBCoord up to date in case we're propagating reflow damage
2864       // and also because our final height may depend on it. If the
2865       // line is inlines, then only update mBCoord if the line is not
2866       // empty, because that's what PlaceLine does. (Empty blocks may
2867       // want to update mBCoord, e.g. if they have clearance.)
2868       if (line->IsBlock() || !line->CachedIsEmpty()) {
2869         aState.mBCoord = line->BEnd();
2870       }
2871 
2872       needToRecoverState = true;
2873 
2874       if (reflowedPrevLine && !line->IsBlock() &&
2875           aState.mPresContext->HasPendingInterrupt()) {
2876         // Need to make sure to pull overflows from any prev-in-flows
2877         for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
2878              inlineKid = inlineKid->PrincipalChildList().FirstChild()) {
2879           inlineKid->PullOverflowsFromPrevInFlow();
2880         }
2881       }
2882     }
2883 
2884     // Record if we need to clear floats before reflowing the next
2885     // line. Note that inlineFloatBreakType will be handled and
2886     // cleared before the next line is processed, so there is no
2887     // need to combine break types here.
2888     if (line->HasFloatBreakAfter()) {
2889       inlineFloatBreakType = line->GetBreakTypeAfter();
2890     }
2891 
2892     if (LineHasClear(line.get())) {
2893       foundAnyClears = true;
2894     }
2895 
2896     DumpLine(aState, line, deltaBCoord, -1);
2897 
2898     if (aState.mPresContext->HasPendingInterrupt()) {
2899       willReflowAgain = true;
2900       // Another option here might be to leave |line| clean if
2901       // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
2902       // that case the line really did reflow as it should have.  Not sure
2903       // whether that would be safe, so doing this for now instead.  Also not
2904       // sure whether we really want to mark all lines dirty after an
2905       // interrupt, but until we get better at propagating float damage we
2906       // really do need to do it this way; see comments inside MarkLineDirty.
2907       MarkLineDirtyForInterrupt(line);
2908     }
2909   }
2910 
2911   // Handle BR-clearance from the last line of the block
2912   if (inlineFloatBreakType != StyleClear::None) {
2913     std::tie(aState.mBCoord, std::ignore) =
2914         aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
2915   }
2916 
2917   if (needToRecoverState) {
2918     // Is this expensive?
2919     aState.ReconstructMarginBefore(line);
2920 
2921     // Update aState.mPrevChild as if we had reflowed all of the frames in
2922     // the last line.
2923     NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
2924                                          line.prev()->LastChild(),
2925                  "unexpected line frames");
2926     aState.mPrevChild = line == line_end ? mFrames.LastChild()
2927                                          : line->mFirstChild->GetPrevSibling();
2928   }
2929 
2930   // Should we really have to do this?
2931   if (repositionViews) nsContainerFrame::PlaceFrameView(this);
2932 
2933   // We can skip trying to pull up the next line if our height is constrained
2934   // (so we can report being incomplete) and there is no next in flow or we
2935   // were told not to or we know it will be futile, i.e.,
2936   // -- the next in flow is not changing
2937   // -- and we cannot have added more space for its first line to be
2938   // pulled up into,
2939   // -- it's an incremental reflow of a descendant
2940   // -- and we didn't reflow any floats (so the available space
2941   // didn't change)
2942   // -- my chain of next-in-flows either has no first line, or its first
2943   // line isn't dirty.
2944   bool heightConstrained =
2945       aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE;
2946   bool skipPull = willReflowAgain && heightConstrained;
2947   if (!skipPull && heightConstrained && aState.mNextInFlow &&
2948       (aState.mReflowInput.mFlags.mNextInFlowUntouched && !lastLineMovedUp &&
2949        !HasAnyStateBits(NS_FRAME_IS_DIRTY) && !reflowedFloat)) {
2950     // We'll place lineIter at the last line of this block, so that
2951     // nsBlockInFlowLineIterator::Next() will take us to the first
2952     // line of my next-in-flow-chain.  (But first, check that I
2953     // have any lines -- if I don't, just bail out of this
2954     // optimization.)
2955     LineIterator lineIter = this->LinesEnd();
2956     if (lineIter != this->LinesBegin()) {
2957       lineIter--;  // I have lines; step back from dummy iterator to last line.
2958       nsBlockInFlowLineIterator bifLineIter(this, lineIter);
2959 
2960       // Check for next-in-flow-chain's first line.
2961       // (First, see if there is such a line, and second, see if it's clean)
2962       if (!bifLineIter.Next() || !bifLineIter.GetLine()->IsDirty()) {
2963         skipPull = true;
2964       }
2965     }
2966   }
2967 
2968   if (skipPull && aState.mNextInFlow) {
2969     NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
2970     if (aState.mNextInFlow->IsTrueOverflowContainer()) {
2971       aState.mReflowStatus.SetOverflowIncomplete();
2972     } else {
2973       aState.mReflowStatus.SetIncomplete();
2974     }
2975   }
2976 
2977   if (!skipPull && aState.mNextInFlow) {
2978     // Pull data from a next-in-flow if there's still room for more
2979     // content here.
2980     while (keepGoing && aState.mNextInFlow) {
2981       // Grab first line from our next-in-flow
2982       nsBlockFrame* nextInFlow = aState.mNextInFlow;
2983       nsLineBox* pulledLine;
2984       nsFrameList pulledFrames;
2985       if (!nextInFlow->mLines.empty()) {
2986         RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames, &pulledLine,
2987                         &pulledFrames);
2988       } else {
2989         // Grab an overflow line if there are any
2990         FrameLines* overflowLines = nextInFlow->GetOverflowLines();
2991         if (!overflowLines) {
2992           aState.mNextInFlow =
2993               static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
2994           continue;
2995         }
2996         bool last =
2997             RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
2998                             &pulledLine, &pulledFrames);
2999         if (last) {
3000           nextInFlow->DestroyOverflowLines();
3001         }
3002       }
3003 
3004       if (pulledFrames.IsEmpty()) {
3005         // The line is empty. Try the next one.
3006         NS_ASSERTION(
3007             pulledLine->GetChildCount() == 0 && !pulledLine->mFirstChild,
3008             "bad empty line");
3009         nextInFlow->FreeLineBox(pulledLine);
3010         continue;
3011       }
3012 
3013       if (pulledLine == nextInFlow->GetLineCursor()) {
3014         nextInFlow->ClearLineCursor();
3015       }
3016       ReparentFrames(pulledFrames, nextInFlow, this);
3017       pulledLine->SetMovedFragments();
3018 
3019       NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
3020                    "Unexpected last frame");
3021       NS_ASSERTION(aState.mPrevChild || mLines.empty(),
3022                    "should have a prevchild here");
3023       NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
3024                    "Incorrect aState.mPrevChild before inserting line at end");
3025 
3026       // Shift pulledLine's frames into our mFrames list.
3027       mFrames.AppendFrames(nullptr, pulledFrames);
3028 
3029       // Add line to our line list, and set its last child as our new prev-child
3030       line = mLines.before_insert(LinesEnd(), pulledLine);
3031       aState.mPrevChild = mFrames.LastChild();
3032 
3033       // Reparent floats whose placeholders are in the line.
3034       ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
3035 
3036       DumpLine(aState, pulledLine, deltaBCoord, 0);
3037 #ifdef DEBUG
3038       AutoNoisyIndenter indent2(gNoisyReflow);
3039 #endif
3040 
3041       if (aState.mPresContext->HasPendingInterrupt()) {
3042         MarkLineDirtyForInterrupt(line);
3043       } else {
3044         // Now reflow it and any lines that it makes during it's reflow
3045         // (we have to loop here because reflowing the line may cause a new
3046         // line to be created; see SplitLine's callers for examples of
3047         // when this happens).
3048         while (line != LinesEnd()) {
3049           ReflowLine(aState, line, &keepGoing);
3050 
3051           if (aState.mReflowInput.WillReflowAgainForClearance()) {
3052             line->MarkDirty();
3053             keepGoing = false;
3054             aState.mReflowStatus.SetIncomplete();
3055             break;
3056           }
3057 
3058           DumpLine(aState, line, deltaBCoord, -1);
3059           if (!keepGoing) {
3060             if (0 == line->GetChildCount()) {
3061               DeleteLine(aState, line, line_end);
3062             }
3063             break;
3064           }
3065 
3066           if (LineHasClear(line.get())) {
3067             foundAnyClears = true;
3068           }
3069 
3070           if (aState.mPresContext->CheckForInterrupt(this)) {
3071             MarkLineDirtyForInterrupt(line);
3072             break;
3073           }
3074 
3075           // If this is an inline frame then its time to stop
3076           ++line;
3077           aState.AdvanceToNextLine();
3078         }
3079       }
3080     }
3081 
3082     if (aState.mReflowStatus.IsIncomplete()) {
3083       aState.mReflowStatus.SetNextInFlowNeedsReflow();
3084     }  // XXXfr shouldn't set this flag when nextinflow has no lines
3085   }
3086 
3087   // Handle an odd-ball case: a list-item with no lines
3088   if (mLines.empty() && HasOutsideMarker()) {
3089     ReflowOutput metrics(aState.mReflowInput);
3090     nsIFrame* marker = GetOutsideMarker();
3091     WritingMode wm = aState.mReflowInput.GetWritingMode();
3092     ReflowOutsideMarker(
3093         marker, aState, metrics,
3094         aState.mReflowInput.ComputedPhysicalBorderPadding().top);
3095     NS_ASSERTION(!MarkerIsEmpty() || metrics.BSize(wm) == 0,
3096                  "empty ::marker frame took up space");
3097 
3098     if (!MarkerIsEmpty()) {
3099       // There are no lines so we have to fake up some y motion so that
3100       // we end up with *some* height.
3101       // (Note: if we're layout-contained, we have to be sure to leave our
3102       // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
3103       // because layout-contained frames have no baseline.)
3104       if (!aState.mReflowInput.mStyleDisplay->IsContainLayout() &&
3105           metrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
3106         nscoord ascent;
3107         WritingMode wm = aState.mReflowInput.GetWritingMode();
3108         if (nsLayoutUtils::GetFirstLineBaseline(wm, marker, &ascent)) {
3109           metrics.SetBlockStartAscent(ascent);
3110         } else {
3111           metrics.SetBlockStartAscent(metrics.BSize(wm));
3112         }
3113       }
3114 
3115       RefPtr<nsFontMetrics> fm =
3116           nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
3117 
3118       nscoord minAscent = nsLayoutUtils::GetCenteredFontBaseline(
3119           fm, aState.mMinLineHeight, wm.IsLineInverted());
3120       nscoord minDescent = aState.mMinLineHeight - minAscent;
3121 
3122       aState.mBCoord +=
3123           std::max(minAscent, metrics.BlockStartAscent()) +
3124           std::max(minDescent, metrics.BSize(wm) - metrics.BlockStartAscent());
3125 
3126       nscoord offset = minAscent - metrics.BlockStartAscent();
3127       if (offset > 0) {
3128         marker->SetRect(marker->GetRect() + nsPoint(0, offset));
3129       }
3130     }
3131   }
3132 
3133   if (LinesAreEmpty(mLines) && ShouldHaveLineIfEmpty()) {
3134     aState.mBCoord += aState.mMinLineHeight;
3135   }
3136 
3137   if (foundAnyClears) {
3138     AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
3139   } else {
3140     RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
3141   }
3142 
3143 #ifdef DEBUG
3144   VerifyLines(true);
3145   VerifyOverflowSituation();
3146   if (gNoisyReflow) {
3147     IndentBy(stdout, gNoiseIndent - 1);
3148     ListTag(stdout);
3149     printf(": done reflowing dirty lines (status=%s)\n",
3150            ToString(aState.mReflowStatus).c_str());
3151   }
3152 #endif
3153 }
3154 
MarkLineDirtyForInterrupt(nsLineBox * aLine)3155 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine) {
3156   aLine->MarkDirty();
3157 
3158   // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
3159   // marked the lines that need to be marked dirty based on our
3160   // vertical resize stuff.  So we'll definitely reflow all those kids;
3161   // the only question is how they should behave.
3162   if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
3163     // Mark all our child frames dirty so we make sure to reflow them
3164     // later.
3165     int32_t n = aLine->GetChildCount();
3166     for (nsIFrame* f = aLine->mFirstChild; n > 0;
3167          f = f->GetNextSibling(), --n) {
3168       f->MarkSubtreeDirty();
3169     }
3170     // And mark all the floats whose reflows we might be skipping dirty too.
3171     if (aLine->HasFloats()) {
3172       for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
3173         fc->mFloat->MarkSubtreeDirty();
3174       }
3175     }
3176   } else {
3177     // Dirty all the descendant lines of block kids to handle float damage,
3178     // since our nsFloatManager will go away by the next time we're reflowing.
3179     // XXXbz Can we do something more like what PropagateFloatDamage does?
3180     // Would need to sort out the exact business with mBlockDelta for that....
3181     // This marks way too much dirty.  If we ever make this better, revisit
3182     // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
3183     nsBlockFrame* bf = do_QueryFrame(aLine->mFirstChild);
3184     if (bf) {
3185       MarkAllDescendantLinesDirty(bf);
3186     }
3187   }
3188 }
3189 
DeleteLine(BlockReflowState & aState,nsLineList::iterator aLine,nsLineList::iterator aLineEnd)3190 void nsBlockFrame::DeleteLine(BlockReflowState& aState,
3191                               nsLineList::iterator aLine,
3192                               nsLineList::iterator aLineEnd) {
3193   MOZ_ASSERT(0 == aLine->GetChildCount(), "can't delete !empty line");
3194   if (0 == aLine->GetChildCount()) {
3195     NS_ASSERTION(aState.mCurrentLine == aLine,
3196                  "using function more generally than designed, "
3197                  "but perhaps OK now");
3198     nsLineBox* line = aLine;
3199     aLine = mLines.erase(aLine);
3200     FreeLineBox(line);
3201     // Mark the previous margin of the next line dirty since we need to
3202     // recompute its top position.
3203     if (aLine != aLineEnd) aLine->MarkPreviousMarginDirty();
3204   }
3205 }
3206 
3207 /**
3208  * Reflow a line. The line will either contain a single block frame
3209  * or contain 1 or more inline frames. aKeepReflowGoing indicates
3210  * whether or not the caller should continue to reflow more lines.
3211  */
ReflowLine(BlockReflowState & aState,LineIterator aLine,bool * aKeepReflowGoing)3212 void nsBlockFrame::ReflowLine(BlockReflowState& aState, LineIterator aLine,
3213                               bool* aKeepReflowGoing) {
3214   MOZ_ASSERT(aLine->GetChildCount(), "reflowing empty line");
3215 
3216   // Setup the line-layout for the new line
3217   aState.mCurrentLine = aLine;
3218   aLine->ClearDirty();
3219   aLine->InvalidateCachedIsEmpty();
3220   aLine->ClearHadFloatPushed();
3221 
3222   // Now that we know what kind of line we have, reflow it
3223   if (aLine->IsBlock()) {
3224     ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
3225   } else {
3226     aLine->SetLineWrapped(false);
3227     ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
3228 
3229     // Store the line's float edges for overflow marker analysis if needed.
3230     aLine->ClearFloatEdges();
3231     if (aState.mFlags.mCanHaveOverflowMarkers) {
3232       WritingMode wm = aLine->mWritingMode;
3233       nsFlowAreaRect r = aState.GetFloatAvailableSpaceForBSize(
3234           aLine->BStart(), aLine->BSize(), nullptr);
3235       if (r.HasFloats()) {
3236         LogicalRect so = aLine->GetOverflowArea(OverflowType::Scrollable, wm,
3237                                                 aLine->mContainerSize);
3238         nscoord s = r.mRect.IStart(wm);
3239         nscoord e = r.mRect.IEnd(wm);
3240         if (so.IEnd(wm) > e || so.IStart(wm) < s) {
3241           // This line is overlapping a float - store the edges marking the area
3242           // between the floats for text-overflow analysis.
3243           aLine->SetFloatEdges(s, e);
3244         }
3245       }
3246     }
3247   }
3248 
3249   aLine->ClearMovedFragments();
3250 }
3251 
PullFrame(BlockReflowState & aState,LineIterator aLine)3252 nsIFrame* nsBlockFrame::PullFrame(BlockReflowState& aState,
3253                                   LineIterator aLine) {
3254   // First check our remaining lines.
3255   if (LinesEnd() != aLine.next()) {
3256     return PullFrameFrom(aLine, this, aLine.next());
3257   }
3258 
3259   NS_ASSERTION(
3260       !GetOverflowLines(),
3261       "Our overflow lines should have been removed at the start of reflow");
3262 
3263   // Try each next-in-flow.
3264   nsBlockFrame* nextInFlow = aState.mNextInFlow;
3265   while (nextInFlow) {
3266     if (nextInFlow->mLines.empty()) {
3267       nextInFlow->DrainSelfOverflowList();
3268     }
3269     if (!nextInFlow->mLines.empty()) {
3270       return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
3271     }
3272     nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
3273     aState.mNextInFlow = nextInFlow;
3274   }
3275 
3276   return nullptr;
3277 }
3278 
PullFrameFrom(nsLineBox * aLine,nsBlockFrame * aFromContainer,nsLineList::iterator aFromLine)3279 nsIFrame* nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
3280                                       nsBlockFrame* aFromContainer,
3281                                       nsLineList::iterator aFromLine) {
3282   nsLineBox* fromLine = aFromLine;
3283   MOZ_ASSERT(fromLine, "bad line to pull from");
3284   MOZ_ASSERT(fromLine->GetChildCount(), "empty line");
3285   MOZ_ASSERT(aLine->GetChildCount(), "empty line");
3286 
3287   NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
3288                "Disagreement about whether it's a block or not");
3289 
3290   if (fromLine->IsBlock()) {
3291     // If our line is not empty and the child in aFromLine is a block
3292     // then we cannot pull up the frame into this line. In this case
3293     // we stop pulling.
3294     return nullptr;
3295   }
3296   // Take frame from fromLine
3297   nsIFrame* frame = fromLine->mFirstChild;
3298   nsIFrame* newFirstChild = frame->GetNextSibling();
3299 
3300   if (aFromContainer != this) {
3301     // The frame is being pulled from a next-in-flow; therefore we
3302     // need to add it to our sibling list.
3303     MOZ_ASSERT(aLine == mLines.back());
3304     MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
3305                "should only pull from first line");
3306     aFromContainer->mFrames.RemoveFrame(frame);
3307 
3308     // When pushing and pulling frames we need to check for whether any
3309     // views need to be reparented.
3310     ReparentFrame(frame, aFromContainer, this);
3311     mFrames.AppendFrame(nullptr, frame);
3312 
3313     // The frame might have (or contain) floats that need to be brought
3314     // over too. (pass 'false' since there are no siblings to check)
3315     ReparentFloats(frame, aFromContainer, false);
3316   } else {
3317     MOZ_ASSERT(aLine == aFromLine.prev());
3318   }
3319 
3320   aLine->NoteFrameAdded(frame);
3321   fromLine->NoteFrameRemoved(frame);
3322 
3323   if (fromLine->GetChildCount() > 0) {
3324     // Mark line dirty now that we pulled a child
3325     fromLine->MarkDirty();
3326     fromLine->mFirstChild = newFirstChild;
3327   } else {
3328     // Free up the fromLine now that it's empty.
3329     // Its bounds might need to be redrawn, though.
3330     if (aFromLine.next() != aFromContainer->mLines.end()) {
3331       aFromLine.next()->MarkPreviousMarginDirty();
3332     }
3333     aFromContainer->mLines.erase(aFromLine);
3334     // aFromLine is now invalid
3335     aFromContainer->FreeLineBox(fromLine);
3336   }
3337 
3338 #ifdef DEBUG
3339   VerifyLines(true);
3340   VerifyOverflowSituation();
3341 #endif
3342 
3343   return frame;
3344 }
3345 
SlideLine(BlockReflowState & aState,nsLineBox * aLine,nscoord aDeltaBCoord)3346 void nsBlockFrame::SlideLine(BlockReflowState& aState, nsLineBox* aLine,
3347                              nscoord aDeltaBCoord) {
3348   MOZ_ASSERT(aDeltaBCoord != 0, "why slide a line nowhere?");
3349 
3350   // Adjust line state
3351   aLine->SlideBy(aDeltaBCoord, aState.ContainerSize());
3352 
3353   // Adjust the frames in the line
3354   MoveChildFramesOfLine(aLine, aDeltaBCoord);
3355 }
3356 
UpdateLineContainerSize(nsLineBox * aLine,const nsSize & aNewContainerSize)3357 void nsBlockFrame::UpdateLineContainerSize(nsLineBox* aLine,
3358                                            const nsSize& aNewContainerSize) {
3359   if (aNewContainerSize == aLine->mContainerSize) {
3360     return;
3361   }
3362 
3363   // Adjust line state
3364   nsSize sizeDelta = aLine->UpdateContainerSize(aNewContainerSize);
3365 
3366   // Changing container width only matters if writing mode is vertical-rl
3367   if (GetWritingMode().IsVerticalRL()) {
3368     MoveChildFramesOfLine(aLine, sizeDelta.width);
3369   }
3370 }
3371 
MoveChildFramesOfLine(nsLineBox * aLine,nscoord aDeltaBCoord)3372 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox* aLine,
3373                                          nscoord aDeltaBCoord) {
3374   // Adjust the frames in the line
3375   nsIFrame* kid = aLine->mFirstChild;
3376   if (!kid) {
3377     return;
3378   }
3379 
3380   WritingMode wm = GetWritingMode();
3381   LogicalPoint translation(wm, 0, aDeltaBCoord);
3382 
3383   if (aLine->IsBlock()) {
3384     if (aDeltaBCoord) {
3385       kid->MovePositionBy(wm, translation);
3386     }
3387 
3388     // Make sure the frame's view and any child views are updated
3389     nsContainerFrame::PlaceFrameView(kid);
3390   } else {
3391     // Adjust the block-dir coordinate of the frames in the line.
3392     // Note: we need to re-position views even if aDeltaBCoord is 0, because
3393     // one of our parent frames may have moved and so the view's position
3394     // relative to its parent may have changed.
3395     int32_t n = aLine->GetChildCount();
3396     while (--n >= 0) {
3397       if (aDeltaBCoord) {
3398         kid->MovePositionBy(wm, translation);
3399       }
3400       // Make sure the frame's view and any child views are updated
3401       nsContainerFrame::PlaceFrameView(kid);
3402       kid = kid->GetNextSibling();
3403     }
3404   }
3405 }
3406 
IsNonAutoNonZeroBSize(const StyleSize & aCoord)3407 static inline bool IsNonAutoNonZeroBSize(const StyleSize& aCoord) {
3408   // The "extremum length" values (see ExtremumLength) were originally aimed at
3409   // inline-size (or width, as it was before logicalization). For now, let them
3410   // return false here, so we treat them like 'auto' pending a real
3411   // implementation. (See bug 1126420.)
3412   //
3413   // FIXME (bug 567039, bug 527285) This isn't correct for the 'fill' value,
3414   // which should more likely (but not necessarily, depending on the available
3415   // space) be returning true.
3416   if (aCoord.BehavesLikeInitialValueOnBlockAxis()) {
3417     return false;
3418   }
3419   MOZ_ASSERT(aCoord.IsLengthPercentage());
3420   // If we evaluate the length/percent/calc at a percentage basis of
3421   // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
3422   // length, percent, or combination thereof.  Test > 0 so we clamp
3423   // negative calc() results to 0.
3424   return aCoord.AsLengthPercentage().Resolve(nscoord_MAX) > 0 ||
3425          aCoord.AsLengthPercentage().Resolve(0) > 0;
3426 }
3427 
3428 /* virtual */
IsSelfEmpty()3429 bool nsBlockFrame::IsSelfEmpty() {
3430   // Blocks which are margin-roots (including inline-blocks) cannot be treated
3431   // as empty for margin-collapsing and other purposes. They're more like
3432   // replaced elements.
3433   if (HasAnyStateBits(NS_BLOCK_MARGIN_ROOT)) {
3434     return false;
3435   }
3436 
3437   WritingMode wm = GetWritingMode();
3438   const nsStylePosition* position = StylePosition();
3439 
3440   if (IsNonAutoNonZeroBSize(position->MinBSize(wm)) ||
3441       IsNonAutoNonZeroBSize(position->BSize(wm))) {
3442     return false;
3443   }
3444 
3445   // FIXME: Bug 1646100 - Take intrinsic size into account.
3446   // FIXME: Handle the case that both inline and block sizes are auto.
3447   // https://github.com/w3c/csswg-drafts/issues/5060.
3448   // Note: block-size could be zero or auto/intrinsic keywords here.
3449   if (position->BSize(wm).BehavesLikeInitialValueOnBlockAxis() &&
3450       position->mAspectRatio.HasFiniteRatio()) {
3451     return false;
3452   }
3453 
3454   const nsStyleBorder* border = StyleBorder();
3455   const nsStylePadding* padding = StylePadding();
3456 
3457   if (border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBStart)) !=
3458           0 ||
3459       border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBEnd)) != 0 ||
3460       !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBStart(wm)) ||
3461       !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBEnd(wm))) {
3462     return false;
3463   }
3464 
3465   if (HasOutsideMarker() && !MarkerIsEmpty()) {
3466     return false;
3467   }
3468 
3469   return true;
3470 }
3471 
CachedIsEmpty()3472 bool nsBlockFrame::CachedIsEmpty() {
3473   if (!IsSelfEmpty()) {
3474     return false;
3475   }
3476   for (auto& line : mLines) {
3477     if (!line.CachedIsEmpty()) {
3478       return false;
3479     }
3480   }
3481   return true;
3482 }
3483 
IsEmpty()3484 bool nsBlockFrame::IsEmpty() {
3485   if (!IsSelfEmpty()) {
3486     return false;
3487   }
3488 
3489   return LinesAreEmpty(mLines);
3490 }
3491 
ShouldApplyBStartMargin(BlockReflowState & aState,nsLineBox * aLine)3492 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState& aState,
3493                                            nsLineBox* aLine) {
3494   if (aLine->mFirstChild->IsPageBreakFrame()) {
3495     // A page break frame consumes margins adjacent to it.
3496     // https://drafts.csswg.org/css-break/#break-margins
3497     return false;
3498   }
3499 
3500   if (aState.mFlags.mShouldApplyBStartMargin) {
3501     // Apply short-circuit check to avoid searching the line list
3502     return true;
3503   }
3504 
3505   if (!aState.IsAdjacentWithTop()) {
3506     // If we aren't at the start block-coordinate then something of non-zero
3507     // height must have been placed. Therefore the childs block-start margin
3508     // applies.
3509     aState.mFlags.mShouldApplyBStartMargin = true;
3510     return true;
3511   }
3512 
3513   // Determine if this line is "essentially" the first line
3514   LineIterator line = LinesBegin();
3515   if (aState.mFlags.mHasLineAdjacentToTop) {
3516     line = aState.mLineAdjacentToTop;
3517   }
3518   while (line != aLine) {
3519     if (!line->CachedIsEmpty() || line->HasClearance()) {
3520       // A line which precedes aLine is non-empty, or has clearance,
3521       // so therefore the block-start margin applies.
3522       aState.mFlags.mShouldApplyBStartMargin = true;
3523       return true;
3524     }
3525     // No need to apply the block-start margin if the line has floats.  We
3526     // should collapse anyway (bug 44419)
3527     ++line;
3528     aState.mFlags.mHasLineAdjacentToTop = true;
3529     aState.mLineAdjacentToTop = line;
3530   }
3531 
3532   // The line being reflowed is "essentially" the first line in the
3533   // block. Therefore its block-start margin will be collapsed by the
3534   // generational collapsing logic with its parent (us).
3535   return false;
3536 }
3537 
ReflowBlockFrame(BlockReflowState & aState,LineIterator aLine,bool * aKeepReflowGoing)3538 void nsBlockFrame::ReflowBlockFrame(BlockReflowState& aState,
3539                                     LineIterator aLine,
3540                                     bool* aKeepReflowGoing) {
3541   MOZ_ASSERT(*aKeepReflowGoing, "bad caller");
3542 
3543   nsIFrame* frame = aLine->mFirstChild;
3544   if (!frame) {
3545     NS_ASSERTION(false, "program error - unexpected empty line");
3546     return;
3547   }
3548 
3549   // Prepare the block reflow engine
3550   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
3551 
3552   StyleClear breakType = frame->StyleDisplay()->mBreakType;
3553   if (StyleClear::None != aState.mFloatBreakType) {
3554     breakType =
3555         nsLayoutUtils::CombineBreakType(breakType, aState.mFloatBreakType);
3556     aState.mFloatBreakType = StyleClear::None;
3557   }
3558 
3559   // Clear past floats before the block if the clear style is not none
3560   aLine->SetBreakTypeBefore(breakType);
3561 
3562   // See if we should apply the block-start margin. If the block frame being
3563   // reflowed is a continuation, then we don't apply its block-start margin
3564   // because it's not significant. Otherwise, dig deeper.
3565   bool applyBStartMargin =
3566       !frame->GetPrevContinuation() && ShouldApplyBStartMargin(aState, aLine);
3567   if (applyBStartMargin) {
3568     // The HasClearance setting is only valid if ShouldApplyBStartMargin
3569     // returned false (in which case the block-start margin-root set our
3570     // clearance flag). Otherwise clear it now. We'll set it later on
3571     // ourselves if necessary.
3572     aLine->ClearHasClearance();
3573   }
3574   bool treatWithClearance = aLine->HasClearance();
3575 
3576   bool mightClearFloats = breakType != StyleClear::None;
3577   nsIFrame* replacedBlock = nullptr;
3578   if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
3579     mightClearFloats = true;
3580     replacedBlock = frame;
3581   }
3582 
3583   // If our block-start margin was counted as part of some parent's block-start
3584   // margin collapse, and we are being speculatively reflowed assuming this
3585   // frame DID NOT need clearance, then we need to check that
3586   // assumption.
3587   if (!treatWithClearance && !applyBStartMargin && mightClearFloats &&
3588       aState.mReflowInput.mDiscoveredClearance) {
3589     nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.get();
3590     if (auto [clearBCoord, result] =
3591             aState.ClearFloats(curBCoord, breakType, replacedBlock);
3592         result != ClearFloatsResult::BCoordNoChange) {
3593       Unused << clearBCoord;
3594 
3595       // Only record the first frame that requires clearance
3596       if (!*aState.mReflowInput.mDiscoveredClearance) {
3597         *aState.mReflowInput.mDiscoveredClearance = frame;
3598       }
3599       aState.mPrevChild = frame;
3600       // Exactly what we do now is flexible since we'll definitely be
3601       // reflowed.
3602       return;
3603     }
3604   }
3605   if (treatWithClearance) {
3606     applyBStartMargin = true;
3607   }
3608 
3609   nsIFrame* clearanceFrame = nullptr;
3610   const nscoord startingBCoord = aState.mBCoord;
3611   const nsCollapsingMargin incomingMargin = aState.mPrevBEndMargin;
3612   nscoord clearance;
3613   // Save the original position of the frame so that we can reposition
3614   // its view as needed.
3615   nsPoint originalPosition = frame->GetPosition();
3616   while (true) {
3617     clearance = 0;
3618     nscoord bStartMargin = 0;
3619     bool mayNeedRetry = false;
3620     bool clearedFloats = false;
3621     bool clearedPushedOrSplitFloat = false;
3622     if (applyBStartMargin) {
3623       // Precompute the blocks block-start margin value so that we can get the
3624       // correct available space (there might be a float that's
3625       // already been placed below the aState.mPrevBEndMargin
3626 
3627       // Setup a reflowInput to get the style computed block-start margin
3628       // value. We'll use a reason of `resize' so that we don't fudge
3629       // any incremental reflow input.
3630 
3631       // The availSpace here is irrelevant to our needs - all we want
3632       // out if this setup is the block-start margin value which doesn't depend
3633       // on the childs available space.
3634       // XXX building a complete ReflowInput just to get the block-start
3635       // margin seems like a waste. And we do this for almost every block!
3636       WritingMode wm = frame->GetWritingMode();
3637       LogicalSize availSpace = aState.ContentSize(wm);
3638       ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput, frame,
3639                               availSpace);
3640 
3641       if (treatWithClearance) {
3642         aState.mBCoord += aState.mPrevBEndMargin.get();
3643         aState.mPrevBEndMargin.Zero();
3644       }
3645 
3646       // Now compute the collapsed margin-block-start value into
3647       // aState.mPrevBEndMargin, assuming that all child margins
3648       // collapse down to clearanceFrame.
3649       brc.ComputeCollapsedBStartMargin(reflowInput, &aState.mPrevBEndMargin,
3650                                        clearanceFrame, &mayNeedRetry);
3651 
3652       // XXX optimization; we could check the collapsing children to see if they
3653       // are sure to require clearance, and so avoid retrying them
3654 
3655       if (clearanceFrame) {
3656         // Don't allow retries on the second pass. The clearance decisions for
3657         // the blocks whose block-start margins collapse with ours are now
3658         // fixed.
3659         mayNeedRetry = false;
3660       }
3661 
3662       if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
3663         // We don't know if we need clearance and this is the first,
3664         // optimistic pass.  So determine whether *this block* needs
3665         // clearance. Note that we do not allow the decision for whether
3666         // this block has clearance to change on the second pass; that
3667         // decision is only allowed to be made under the optimistic
3668         // first pass.
3669         nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.get();
3670         if (auto [clearBCoord, result] =
3671                 aState.ClearFloats(curBCoord, breakType, replacedBlock);
3672             result != ClearFloatsResult::BCoordNoChange) {
3673           Unused << clearBCoord;
3674 
3675           // Looks like we need clearance and we didn't know about it already.
3676           // So recompute collapsed margin
3677           treatWithClearance = true;
3678           // Remember this decision, needed for incremental reflow
3679           aLine->SetHasClearance();
3680 
3681           // Apply incoming margins
3682           aState.mBCoord += aState.mPrevBEndMargin.get();
3683           aState.mPrevBEndMargin.Zero();
3684 
3685           // Compute the collapsed margin again, ignoring the incoming margin
3686           // this time
3687           mayNeedRetry = false;
3688           brc.ComputeCollapsedBStartMargin(reflowInput, &aState.mPrevBEndMargin,
3689                                            clearanceFrame, &mayNeedRetry);
3690         }
3691       }
3692 
3693       // Temporarily advance the running block-direction value so that the
3694       // GetFloatAvailableSpace method will return the right available space.
3695       // This undone as soon as the horizontal margins are computed.
3696       bStartMargin = aState.mPrevBEndMargin.get();
3697 
3698       if (treatWithClearance) {
3699         nscoord currentBCoord = aState.mBCoord;
3700         // advance mBCoord to the clear position.
3701         auto [clearBCoord, result] =
3702             aState.ClearFloats(aState.mBCoord, breakType, replacedBlock);
3703         aState.mBCoord = clearBCoord;
3704 
3705         clearedFloats = result != ClearFloatsResult::BCoordNoChange;
3706         clearedPushedOrSplitFloat =
3707             result == ClearFloatsResult::FloatsPushedOrSplit;
3708 
3709         // Compute clearance. It's the amount we need to add to the block-start
3710         // border-edge of the frame, after applying collapsed margins
3711         // from the frame and its children, to get it to line up with
3712         // the block-end of the floats. The former is
3713         // currentBCoord + bStartMargin, the latter is the current
3714         // aState.mBCoord.
3715         // Note that negative clearance is possible
3716         clearance = aState.mBCoord - (currentBCoord + bStartMargin);
3717 
3718         // Add clearance to our block-start margin while we compute available
3719         // space for the frame
3720         bStartMargin += clearance;
3721 
3722         // Note that aState.mBCoord should stay where it is: at the block-start
3723         // border-edge of the frame
3724       } else {
3725         // Advance aState.mBCoord to the block-start border-edge of the frame.
3726         aState.mBCoord += bStartMargin;
3727       }
3728     }
3729 
3730     aLine->SetLineIsImpactedByFloat(false);
3731 
3732     // Here aState.mBCoord is the block-start border-edge of the block.
3733     // Compute the available space for the block
3734     nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
3735     WritingMode wm = aState.mReflowInput.GetWritingMode();
3736     LogicalRect availSpace = aState.ComputeBlockAvailSpace(
3737         frame, floatAvailableSpace, (replacedBlock));
3738 
3739     // The check for
3740     //   (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
3741     // is to some degree out of paranoia:  if we reliably eat up block-start
3742     // margins at the top of the page as we ought to, it wouldn't be
3743     // needed.
3744     if ((!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats) &&
3745         (availSpace.BSize(wm) < 0 || clearedPushedOrSplitFloat)) {
3746       // We know already that this child block won't fit on this
3747       // page/column due to the block-start margin or the clearance.  So we
3748       // need to get out of here now.  (If we don't, most blocks will handle
3749       // things fine, and report break-before, but zero-height blocks
3750       // won't, and will thus make their parent overly-large and force
3751       // *it* to be pushed in its entirety.)
3752       aState.mBCoord = startingBCoord;
3753       aState.mPrevBEndMargin = incomingMargin;
3754       *aKeepReflowGoing = false;
3755       if (ShouldAvoidBreakInside(aState.mReflowInput)) {
3756         aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
3757         aLine->MarkDirty();
3758       } else {
3759         PushLines(aState, aLine.prev());
3760         aState.mReflowStatus.SetIncomplete();
3761       }
3762       return;
3763     }
3764 
3765     // Now put the block-dir coordinate back to the start of the
3766     // block-start-margin + clearance.
3767     aState.mBCoord -= bStartMargin;
3768     availSpace.BStart(wm) -= bStartMargin;
3769     if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) {
3770       availSpace.BSize(wm) += bStartMargin;
3771     }
3772 
3773     // Construct the reflow input for the block.
3774     Maybe<ReflowInput> childReflowInput;
3775     Maybe<LogicalSize> cbSize;
3776     LogicalSize availSize = availSpace.Size(wm);
3777     bool columnSetWrapperHasNoBSizeLeft = false;
3778     if (Style()->GetPseudoType() == PseudoStyleType::columnContent) {
3779       // Calculate the multicol containing block's block size so that the
3780       // children with percentage block size get correct percentage basis.
3781       const ReflowInput* cbReflowInput =
3782           aState.mReflowInput.mParentReflowInput->mCBReflowInput;
3783       MOZ_ASSERT(cbReflowInput->mFrame->StyleColumn()->IsColumnContainerStyle(),
3784                  "Get unexpected reflow input of multicol containing block!");
3785 
3786       // Use column-width as the containing block's inline-size, i.e. the column
3787       // content's computed inline-size.
3788       cbSize.emplace(LogicalSize(wm, aState.mReflowInput.ComputedISize(),
3789                                  cbReflowInput->ComputedBSize())
3790                          .ConvertTo(frame->GetWritingMode(), wm));
3791 
3792       // If a ColumnSetWrapper is in a balancing column content, it may be
3793       // pushed or pulled back and forth between column contents. Always add
3794       // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
3795       // can have a chance to reflow under current block size constraint.
3796       if (aState.mReflowInput.mFlags.mIsColumnBalancing &&
3797           frame->IsColumnSetWrapperFrame()) {
3798         frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
3799       }
3800     } else if (IsColumnSetWrapperFrame()) {
3801       // If we are reflowing our ColumnSet children, we want to apply our block
3802       // size constraint to the available block size when constructing reflow
3803       // input for ColumnSet so that ColumnSet can use it to compute its max
3804       // column block size.
3805       if (frame->IsColumnSetFrame()) {
3806         nscoord contentBSize = aState.mReflowInput.ComputedBSize();
3807         if (aState.mReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
3808           contentBSize =
3809               std::min(contentBSize, aState.mReflowInput.ComputedMaxBSize());
3810         }
3811         if (contentBSize != NS_UNCONSTRAINEDSIZE) {
3812           // To get the remaining content block-size, subtract the content
3813           // block-size consumed by our previous continuations.
3814           contentBSize -= aState.mConsumedBSize;
3815 
3816           // ColumnSet is not the outermost frame in the column container, so it
3817           // cannot have any margin. We don't need to consider any margin that
3818           // can be generated by "box-decoration-break: clone" as we do in
3819           // BlockReflowState::ComputeBlockAvailSpace().
3820           const nscoord availContentBSize = std::max(
3821               0, contentBSize - (aState.mBCoord - aState.ContentBStart()));
3822           if (availSize.BSize(wm) >= availContentBSize) {
3823             availSize.BSize(wm) = availContentBSize;
3824             columnSetWrapperHasNoBSizeLeft = true;
3825           }
3826         }
3827       }
3828     }
3829 
3830     childReflowInput.emplace(aState.mPresContext, aState.mReflowInput, frame,
3831                              availSize.ConvertTo(frame->GetWritingMode(), wm),
3832                              cbSize);
3833 
3834     childReflowInput->mFlags.mColumnSetWrapperHasNoBSizeLeft =
3835         columnSetWrapperHasNoBSizeLeft;
3836 
3837     if (aLine->MovedFragments()) {
3838       // We only need to set this the first reflow, since if we reflow
3839       // again (and replace childReflowInput) we'll be reflowing it
3840       // again in the same fragment as the previous time.
3841       childReflowInput->mFlags.mMovedBlockFragments = true;
3842     }
3843 
3844     nsFloatManager::SavedState floatManagerState;
3845     nsReflowStatus frameReflowStatus;
3846     do {
3847       if (floatAvailableSpace.HasFloats()) {
3848         // Set if floatAvailableSpace.HasFloats() is true for any
3849         // iteration of the loop.
3850         aLine->SetLineIsImpactedByFloat(true);
3851       }
3852 
3853       // We might need to store into mDiscoveredClearance later if it's
3854       // currently null; we want to overwrite any writes that
3855       // brc.ReflowBlock() below does, so we need to remember now
3856       // whether it's empty.
3857       const bool shouldStoreClearance =
3858           aState.mReflowInput.mDiscoveredClearance &&
3859           !*aState.mReflowInput.mDiscoveredClearance;
3860 
3861       // Reflow the block into the available space
3862       if (mayNeedRetry || replacedBlock) {
3863         aState.FloatManager()->PushState(&floatManagerState);
3864       }
3865 
3866       if (mayNeedRetry) {
3867         childReflowInput->mDiscoveredClearance = &clearanceFrame;
3868       } else if (!applyBStartMargin) {
3869         childReflowInput->mDiscoveredClearance =
3870             aState.mReflowInput.mDiscoveredClearance;
3871       }
3872 
3873       frameReflowStatus.Reset();
3874       brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
3875                       clearance, aState.IsAdjacentWithTop(), aLine.get(),
3876                       *childReflowInput, frameReflowStatus, aState);
3877 
3878       // Now the block has a height.  Using that height, get the
3879       // available space again and call ComputeBlockAvailSpace again.
3880       // If ComputeBlockAvailSpace gives a different result, we need to
3881       // reflow again.
3882       if (!replacedBlock) {
3883         break;
3884       }
3885 
3886       LogicalRect oldFloatAvailableSpaceRect(floatAvailableSpace.mRect);
3887       floatAvailableSpace = aState.GetFloatAvailableSpaceForBSize(
3888           aState.mBCoord + bStartMargin, brc.GetMetrics().BSize(wm),
3889           &floatManagerState);
3890       NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) ==
3891                        oldFloatAvailableSpaceRect.BStart(wm),
3892                    "yikes");
3893       // Restore the height to the position of the next band.
3894       floatAvailableSpace.mRect.BSize(wm) =
3895           oldFloatAvailableSpaceRect.BSize(wm);
3896       // Determine whether the available space shrunk on either side,
3897       // because (the first time round) we now know the block's height,
3898       // and it may intersect additional floats, or (on later
3899       // iterations) because narrowing the width relative to the
3900       // previous time may cause the block to become taller.  Note that
3901       // since we're reflowing the block, narrowing the width might also
3902       // make it shorter, so we must pass aCanGrow as true.
3903       if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect,
3904                                 floatAvailableSpace.mRect, true)) {
3905         // The size and position we chose before are fine (i.e., they
3906         // don't cause intersecting with floats that requires a change
3907         // in size or position), so we're done.
3908         break;
3909       }
3910 
3911       bool advanced = false;
3912       if (!aState.ReplacedBlockFitsInAvailSpace(replacedBlock,
3913                                                 floatAvailableSpace)) {
3914         // Advance to the next band.
3915         nscoord newBCoord = aState.mBCoord;
3916         if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
3917           advanced = true;
3918         }
3919         // ClearFloats might be able to advance us further once we're there.
3920         std::tie(aState.mBCoord, std::ignore) =
3921             aState.ClearFloats(newBCoord, StyleClear::None, replacedBlock);
3922 
3923         // Start over with a new available space rect at the new height.
3924         floatAvailableSpace = aState.GetFloatAvailableSpaceWithState(
3925             aState.mBCoord, ShapeType::ShapeOutside, &floatManagerState);
3926       }
3927 
3928       const LogicalRect oldAvailSpace = availSpace;
3929       availSpace = aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
3930                                                  (replacedBlock));
3931 
3932       if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) {
3933         break;
3934       }
3935 
3936       // We need another reflow.
3937       aState.FloatManager()->PopState(&floatManagerState);
3938 
3939       if (!treatWithClearance && !applyBStartMargin &&
3940           aState.mReflowInput.mDiscoveredClearance) {
3941         // We set shouldStoreClearance above to record only the first
3942         // frame that requires clearance.
3943         if (shouldStoreClearance) {
3944           *aState.mReflowInput.mDiscoveredClearance = frame;
3945         }
3946         aState.mPrevChild = frame;
3947         // Exactly what we do now is flexible since we'll definitely be
3948         // reflowed.
3949         return;
3950       }
3951 
3952       if (advanced) {
3953         // We're pushing down the border-box, so we don't apply margin anymore.
3954         // This should never cause us to move up since the call to
3955         // GetFloatAvailableSpaceForBSize above included the margin.
3956         applyBStartMargin = false;
3957         bStartMargin = 0;
3958         treatWithClearance = true;  // avoid hitting test above
3959         clearance = 0;
3960       }
3961 
3962       childReflowInput.reset();
3963       childReflowInput.emplace(
3964           aState.mPresContext, aState.mReflowInput, frame,
3965           availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
3966     } while (true);
3967 
3968     if (mayNeedRetry && clearanceFrame) {
3969       // Found a clearance frame, so we need to reflow |frame| a second time.
3970       // Restore the states and start over again.
3971       aState.FloatManager()->PopState(&floatManagerState);
3972       aState.mBCoord = startingBCoord;
3973       aState.mPrevBEndMargin = incomingMargin;
3974       continue;
3975     }
3976 
3977     aState.mPrevChild = frame;
3978 
3979     if (childReflowInput->WillReflowAgainForClearance()) {
3980       // If an ancestor of ours is going to reflow for clearance, we
3981       // need to avoid calling PlaceBlock, because it unsets dirty bits
3982       // on the child block (both itself, and through its call to
3983       // nsIFrame::DidReflow), and those dirty bits imply dirtiness for
3984       // all of the child block, including the lines it didn't reflow.
3985       NS_ASSERTION(originalPosition == frame->GetPosition(),
3986                    "we need to call PositionChildViews");
3987       return;
3988     }
3989 
3990 #if defined(REFLOW_STATUS_COVERAGE)
3991     RecordReflowStatus(true, frameReflowStatus);
3992 #endif
3993 
3994     if (frameReflowStatus.IsInlineBreakBefore()) {
3995       // None of the child block fits.
3996       *aKeepReflowGoing = false;
3997       if (ShouldAvoidBreakInside(aState.mReflowInput)) {
3998         aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
3999         aLine->MarkDirty();
4000       } else {
4001         PushLines(aState, aLine.prev());
4002         aState.mReflowStatus.SetIncomplete();
4003       }
4004     } else {
4005       // Note: line-break-after a block is a nop
4006 
4007       // Try to place the child block.
4008       // Don't force the block to fit if we have positive clearance, because
4009       // pushing it to the next page would give it more room.
4010       // Don't force the block to fit if it's impacted by a float. If it is,
4011       // then pushing it to the next page would give it more room. Note that
4012       // isImpacted doesn't include impact from the block's own floats.
4013       bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
4014                       !floatAvailableSpace.HasFloats();
4015       nsCollapsingMargin collapsedBEndMargin;
4016       OverflowAreas overflowAreas;
4017       *aKeepReflowGoing =
4018           brc.PlaceBlock(*childReflowInput, forceFit, aLine.get(),
4019                          collapsedBEndMargin, overflowAreas, frameReflowStatus);
4020       if (!frameReflowStatus.IsFullyComplete() &&
4021           ShouldAvoidBreakInside(aState.mReflowInput)) {
4022         *aKeepReflowGoing = false;
4023         aLine->MarkDirty();
4024       }
4025 
4026       if (aLine->SetCarriedOutBEndMargin(collapsedBEndMargin)) {
4027         LineIterator nextLine = aLine;
4028         ++nextLine;
4029         if (nextLine != LinesEnd()) {
4030           nextLine->MarkPreviousMarginDirty();
4031         }
4032       }
4033 
4034       aLine->SetOverflowAreas(overflowAreas);
4035       if (*aKeepReflowGoing) {
4036         // Some of the child block fit
4037 
4038         // Advance to new Y position
4039         nscoord newBCoord = aLine->BEnd();
4040         aState.mBCoord = newBCoord;
4041 
4042         // Continue the block frame now if it didn't completely fit in
4043         // the available space.
4044         if (!frameReflowStatus.IsFullyComplete()) {
4045           bool madeContinuation = CreateContinuationFor(aState, nullptr, frame);
4046 
4047           nsIFrame* nextFrame = frame->GetNextInFlow();
4048           NS_ASSERTION(nextFrame,
4049                        "We're supposed to have a next-in-flow by now");
4050 
4051           if (frameReflowStatus.IsIncomplete()) {
4052             // If nextFrame used to be an overflow container, make it a normal
4053             // block
4054             if (!madeContinuation &&
4055                 nextFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
4056               nsOverflowContinuationTracker::AutoFinish fini(
4057                   aState.mOverflowTracker, frame);
4058               nsContainerFrame* parent = nextFrame->GetParent();
4059               parent->StealFrame(nextFrame);
4060               if (parent != this) {
4061                 ReparentFrame(nextFrame, parent, this);
4062               }
4063               mFrames.InsertFrame(nullptr, frame, nextFrame);
4064               madeContinuation = true;  // needs to be added to mLines
4065               nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
4066               frameReflowStatus.SetNextInFlowNeedsReflow();
4067             }
4068 
4069             // Push continuation to a new line, but only if we actually made
4070             // one.
4071             if (madeContinuation) {
4072               nsLineBox* line = NewLineBox(nextFrame, true);
4073               mLines.after_insert(aLine, line);
4074             }
4075 
4076             PushLines(aState, aLine);
4077             aState.mReflowStatus.SetIncomplete();
4078 
4079             // If we need to reflow the continuation of the block child,
4080             // then we'd better reflow our continuation
4081             if (frameReflowStatus.NextInFlowNeedsReflow()) {
4082               aState.mReflowStatus.SetNextInFlowNeedsReflow();
4083               // We also need to make that continuation's line dirty so
4084               // it gets reflowed when we reflow our next in flow. The
4085               // nif's line must always be either a line of the nif's
4086               // parent block (only if we didn't make a continuation) or
4087               // else one of our own overflow lines. In the latter case
4088               // the line is already marked dirty, so just handle the
4089               // first case.
4090               if (!madeContinuation) {
4091                 nsBlockFrame* nifBlock = do_QueryFrame(nextFrame->GetParent());
4092                 NS_ASSERTION(
4093                     nifBlock,
4094                     "A block's child's next in flow's parent must be a block!");
4095                 for (auto& line : nifBlock->Lines()) {
4096                   if (line.Contains(nextFrame)) {
4097                     line.MarkDirty();
4098                     break;
4099                   }
4100                 }
4101               }
4102             }
4103             *aKeepReflowGoing = false;
4104 
4105             // The block-end margin for a block is only applied on the last
4106             // flow block. Since we just continued the child block frame,
4107             // we know that line->mFirstChild is not the last flow block
4108             // therefore zero out the running margin value.
4109 #ifdef NOISY_BLOCK_DIR_MARGINS
4110             ListTag(stdout);
4111             printf(": reflow incomplete, frame=");
4112             frame->ListTag(stdout);
4113             printf(" prevBEndMargin=%d, setting to zero\n",
4114                    aState.mPrevBEndMargin.get());
4115 #endif
4116             aState.mPrevBEndMargin.Zero();
4117           } else {  // frame is complete but its overflow is not complete
4118             // Disconnect the next-in-flow and put it in our overflow tracker
4119             if (!madeContinuation &&
4120                 !nextFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
4121               // It already exists, but as a normal next-in-flow, so we need
4122               // to dig it out of the child lists.
4123               nextFrame->GetParent()->StealFrame(nextFrame);
4124             } else if (madeContinuation) {
4125               mFrames.RemoveFrame(nextFrame);
4126             }
4127 
4128             // Put it in our overflow list
4129             aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
4130             aState.mReflowStatus.MergeCompletionStatusFrom(frameReflowStatus);
4131 
4132 #ifdef NOISY_BLOCK_DIR_MARGINS
4133             ListTag(stdout);
4134             printf(": reflow complete but overflow incomplete for ");
4135             frame->ListTag(stdout);
4136             printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4137                    aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
4138 #endif
4139             aState.mPrevBEndMargin = collapsedBEndMargin;
4140           }
4141         } else {  // frame is fully complete
4142 #ifdef NOISY_BLOCK_DIR_MARGINS
4143           ListTag(stdout);
4144           printf(": reflow complete for ");
4145           frame->ListTag(stdout);
4146           printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
4147                  aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
4148 #endif
4149           aState.mPrevBEndMargin = collapsedBEndMargin;
4150         }
4151 #ifdef NOISY_BLOCK_DIR_MARGINS
4152         ListTag(stdout);
4153         printf(": frame=");
4154         frame->ListTag(stdout);
4155         printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
4156                brc.GetCarriedOutBEndMargin().get(), collapsedBEndMargin.get(),
4157                aState.mPrevBEndMargin.get());
4158 #endif
4159       } else {
4160         if (!frameReflowStatus.IsFullyComplete()) {
4161           // The frame reported an incomplete status, but then it also didn't
4162           // fit.  This means we need to reflow it again so that it can
4163           // (again) report the incomplete status.
4164           frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
4165         }
4166 
4167         if ((aLine == mLines.front() && !GetPrevInFlow()) ||
4168             ShouldAvoidBreakInside(aState.mReflowInput)) {
4169           // If it's our very first line *or* we're not at the top of the page
4170           // and we have page-break-inside:avoid, then we need to be pushed to
4171           // our parent's next-in-flow.
4172           aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
4173           // When we reflow in the new position, we need to reflow this
4174           // line again.
4175           aLine->MarkDirty();
4176         } else {
4177           // Push the line that didn't fit and any lines that follow it
4178           // to our next-in-flow.
4179           PushLines(aState, aLine.prev());
4180           aState.mReflowStatus.SetIncomplete();
4181         }
4182       }
4183     }
4184     break;  // out of the reflow retry loop
4185   }
4186 
4187   // Now that we've got its final position all figured out, position any child
4188   // views it may have.  Note that the case when frame has a view got handled
4189   // by FinishReflowChild, but that function didn't have the coordinates needed
4190   // to correctly decide whether to reposition child views.
4191   if (originalPosition != frame->GetPosition() && !frame->HasView()) {
4192     nsContainerFrame::PositionChildViews(frame);
4193   }
4194 
4195 #ifdef DEBUG
4196   VerifyLines(true);
4197 #endif
4198 }
4199 
ReflowInlineFrames(BlockReflowState & aState,LineIterator aLine,bool * aKeepReflowGoing)4200 void nsBlockFrame::ReflowInlineFrames(BlockReflowState& aState,
4201                                       LineIterator aLine,
4202                                       bool* aKeepReflowGoing) {
4203   *aKeepReflowGoing = true;
4204 
4205   aLine->SetLineIsImpactedByFloat(false);
4206 
4207   // Setup initial coordinate system for reflowing the inline frames
4208   // into. Apply a previous block frame's block-end margin first.
4209   if (ShouldApplyBStartMargin(aState, aLine)) {
4210     aState.mBCoord += aState.mPrevBEndMargin.get();
4211   }
4212   nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
4213 
4214   LineReflowStatus lineReflowStatus;
4215   do {
4216     nscoord availableSpaceBSize = 0;
4217     aState.mLineBSize.reset();
4218     do {
4219       bool allowPullUp = true;
4220       nsIFrame* forceBreakInFrame = nullptr;
4221       int32_t forceBreakOffset = -1;
4222       gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak;
4223       do {
4224         nsFloatManager::SavedState floatManagerState;
4225         aState.FloatManager()->PushState(&floatManagerState);
4226 
4227         // Once upon a time we allocated the first 30 nsLineLayout objects
4228         // on the stack, and then we switched to the heap.  At that time
4229         // these objects were large (1100 bytes on a 32 bit system).
4230         // Then the nsLineLayout object was shrunk to 156 bytes by
4231         // removing some internal buffers.  Given that it is so much
4232         // smaller, the complexity of 2 different ways of allocating
4233         // no longer makes sense.  Now we always allocate on the stack.
4234         nsLineLayout lineLayout(aState.mPresContext, aState.FloatManager(),
4235                                 aState.mReflowInput, &aLine, nullptr);
4236         lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
4237         if (forceBreakInFrame) {
4238           lineLayout.ForceBreakAtPosition(forceBreakInFrame, forceBreakOffset);
4239         }
4240         DoReflowInlineFrames(aState, lineLayout, aLine, floatAvailableSpace,
4241                              availableSpaceBSize, &floatManagerState,
4242                              aKeepReflowGoing, &lineReflowStatus, allowPullUp);
4243         lineLayout.EndLineReflow();
4244 
4245         if (LineReflowStatus::RedoNoPull == lineReflowStatus ||
4246             LineReflowStatus::RedoMoreFloats == lineReflowStatus ||
4247             LineReflowStatus::RedoNextBand == lineReflowStatus) {
4248           if (lineLayout.NeedsBackup()) {
4249             NS_ASSERTION(!forceBreakInFrame,
4250                          "Backing up twice; this should never be necessary");
4251             // If there is no saved break position, then this will set
4252             // set forceBreakInFrame to null and we won't back up, which is
4253             // correct.
4254             forceBreakInFrame = lineLayout.GetLastOptionalBreakPosition(
4255                 &forceBreakOffset, &forceBreakPriority);
4256           } else {
4257             forceBreakInFrame = nullptr;
4258           }
4259           // restore the float manager state
4260           aState.FloatManager()->PopState(&floatManagerState);
4261           // Clear out float lists
4262           aState.mCurrentLineFloats.DeleteAll();
4263           aState.mNoWrapFloats.Clear();
4264           aState.mBelowCurrentLineFloats.DeleteAll();
4265         }
4266 
4267         // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
4268         allowPullUp = false;
4269       } while (LineReflowStatus::RedoNoPull == lineReflowStatus);
4270     } while (LineReflowStatus::RedoMoreFloats == lineReflowStatus);
4271   } while (LineReflowStatus::RedoNextBand == lineReflowStatus);
4272 }
4273 
PushTruncatedLine(BlockReflowState & aState,LineIterator aLine,bool * aKeepReflowGoing)4274 void nsBlockFrame::PushTruncatedLine(BlockReflowState& aState,
4275                                      LineIterator aLine,
4276                                      bool* aKeepReflowGoing) {
4277   PushLines(aState, aLine.prev());
4278   *aKeepReflowGoing = false;
4279   aState.mReflowStatus.SetIncomplete();
4280 }
4281 
DoReflowInlineFrames(BlockReflowState & aState,nsLineLayout & aLineLayout,LineIterator aLine,nsFlowAreaRect & aFloatAvailableSpace,nscoord & aAvailableSpaceBSize,nsFloatManager::SavedState * aFloatStateBeforeLine,bool * aKeepReflowGoing,LineReflowStatus * aLineReflowStatus,bool aAllowPullUp)4282 void nsBlockFrame::DoReflowInlineFrames(
4283     BlockReflowState& aState, nsLineLayout& aLineLayout, LineIterator aLine,
4284     nsFlowAreaRect& aFloatAvailableSpace, nscoord& aAvailableSpaceBSize,
4285     nsFloatManager::SavedState* aFloatStateBeforeLine, bool* aKeepReflowGoing,
4286     LineReflowStatus* aLineReflowStatus, bool aAllowPullUp) {
4287   // Forget all of the floats on the line
4288   aLine->FreeFloats(aState.mFloatCacheFreeList);
4289   aState.mFloatOverflowAreas.Clear();
4290 
4291   // We need to set this flag on the line if any of our reflow passes
4292   // are impacted by floats.
4293   if (aFloatAvailableSpace.HasFloats()) aLine->SetLineIsImpactedByFloat(true);
4294 #ifdef REALLY_NOISY_REFLOW
4295   printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
4296          aFloatAvailableSpace.HasFloats());
4297 #endif
4298 
4299   WritingMode outerWM = aState.mReflowInput.GetWritingMode();
4300   WritingMode lineWM = WritingModeForLine(outerWM, aLine->mFirstChild);
4301   LogicalRect lineRect = aFloatAvailableSpace.mRect.ConvertTo(
4302       lineWM, outerWM, aState.ContainerSize());
4303 
4304   nscoord iStart = lineRect.IStart(lineWM);
4305   nscoord availISize = lineRect.ISize(lineWM);
4306   nscoord availBSize;
4307   if (aState.mReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
4308     availBSize = NS_UNCONSTRAINEDSIZE;
4309   } else {
4310     /* XXX get the height right! */
4311     availBSize = lineRect.BSize(lineWM);
4312   }
4313 
4314   // Make sure to enable resize optimization before we call BeginLineReflow
4315   // because it might get disabled there
4316   aLine->EnableResizeReflowOptimization();
4317 
4318   aLineLayout.BeginLineReflow(iStart, aState.mBCoord, availISize, availBSize,
4319                               aFloatAvailableSpace.HasFloats(),
4320                               false, /*XXX isTopOfPage*/
4321                               lineWM, aState.mContainerSize);
4322 
4323   aState.mFlags.mIsLineLayoutEmpty = false;
4324 
4325   // XXX Unfortunately we need to know this before reflowing the first
4326   // inline frame in the line. FIX ME.
4327   if ((0 == aLineLayout.GetLineNumber()) &&
4328       (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
4329       (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
4330     aLineLayout.SetFirstLetterStyleOK(true);
4331   }
4332   NS_ASSERTION(
4333       !((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) && GetPrevContinuation()),
4334       "first letter child bit should only be on first continuation");
4335 
4336   // Reflow the frames that are already on the line first
4337   LineReflowStatus lineReflowStatus = LineReflowStatus::OK;
4338   int32_t i;
4339   nsIFrame* frame = aLine->mFirstChild;
4340 
4341   if (aFloatAvailableSpace.HasFloats()) {
4342     // There is a soft break opportunity at the start of the line, because
4343     // we can always move this line down below float(s).
4344     if (aLineLayout.NotifyOptionalBreakPosition(
4345             frame, 0, true, gfxBreakPriority::eNormalBreak)) {
4346       lineReflowStatus = LineReflowStatus::RedoNextBand;
4347     }
4348   }
4349 
4350   // need to repeatedly call GetChildCount here, because the child
4351   // count can change during the loop!
4352   for (i = 0;
4353        LineReflowStatus::OK == lineReflowStatus && i < aLine->GetChildCount();
4354        i++, frame = frame->GetNextSibling()) {
4355     ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
4356     if (LineReflowStatus::OK != lineReflowStatus) {
4357       // It is possible that one or more of next lines are empty
4358       // (because of DeleteNextInFlowChild). If so, delete them now
4359       // in case we are finished.
4360       ++aLine;
4361       while ((aLine != LinesEnd()) && (0 == aLine->GetChildCount())) {
4362         // XXX Is this still necessary now that DeleteNextInFlowChild
4363         // uses DoRemoveFrame?
4364         nsLineBox* toremove = aLine;
4365         aLine = mLines.erase(aLine);
4366         NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
4367         FreeLineBox(toremove);
4368       }
4369       --aLine;
4370 
4371       NS_ASSERTION(lineReflowStatus != LineReflowStatus::Truncated,
4372                    "ReflowInlineFrame should never determine that a line "
4373                    "needs to go to the next page/column");
4374     }
4375   }
4376 
4377   // Don't pull up new frames into lines with continuation placeholders
4378   if (aAllowPullUp) {
4379     // Pull frames and reflow them until we can't
4380     while (LineReflowStatus::OK == lineReflowStatus) {
4381       frame = PullFrame(aState, aLine);
4382       if (!frame) {
4383         break;
4384       }
4385 
4386       while (LineReflowStatus::OK == lineReflowStatus) {
4387         int32_t oldCount = aLine->GetChildCount();
4388         ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
4389         if (aLine->GetChildCount() != oldCount) {
4390           // We just created a continuation for aFrame AND its going
4391           // to end up on this line (e.g. :first-letter
4392           // situation). Therefore we have to loop here before trying
4393           // to pull another frame.
4394           frame = frame->GetNextSibling();
4395         } else {
4396           break;
4397         }
4398       }
4399     }
4400   }
4401 
4402   aState.mFlags.mIsLineLayoutEmpty = aLineLayout.LineIsEmpty();
4403 
4404   // We only need to backup if the line isn't going to be reflowed again anyway
4405   bool needsBackup = aLineLayout.NeedsBackup() &&
4406                      (lineReflowStatus == LineReflowStatus::Stop ||
4407                       lineReflowStatus == LineReflowStatus::OK);
4408   if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
4409     NS_WARNING(
4410         "We shouldn't be backing up more than once! "
4411         "Someone must have set a break opportunity beyond the available width, "
4412         "even though there were better break opportunities before it");
4413     needsBackup = false;
4414   }
4415   if (needsBackup) {
4416     // We need to try backing up to before a text run
4417     // XXX It's possible, in fact not unusual, for the break opportunity to
4418     // already be the end of the line. We should detect that and optimize to not
4419     // re-do the line.
4420     if (aLineLayout.HasOptionalBreakPosition()) {
4421       // We can back up!
4422       lineReflowStatus = LineReflowStatus::RedoNoPull;
4423     }
4424   } else {
4425     // In case we reflow this line again, remember that we don't
4426     // need to force any breaking
4427     aLineLayout.ClearOptionalBreakPosition();
4428   }
4429 
4430   if (LineReflowStatus::RedoNextBand == lineReflowStatus) {
4431     // This happens only when we have a line that is impacted by
4432     // floats and the first element in the line doesn't fit with
4433     // the floats.
4434     //
4435     // If there's block space available, we either try to reflow the line
4436     // past the current band (if it's non-zero and the band definitely won't
4437     // widen around a shape-outside), otherwise we try one pixel down. If
4438     // there's no block space available, we push the line to the next
4439     // page/column.
4440     NS_ASSERTION(
4441         NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.BSize(outerWM),
4442         "unconstrained block size on totally empty line");
4443 
4444     // See the analogous code for blocks in BlockReflowState::ClearFloats.
4445     nscoord bandBSize = aFloatAvailableSpace.mRect.BSize(outerWM);
4446     if (bandBSize > 0 ||
4447         NS_UNCONSTRAINEDSIZE == aState.mReflowInput.AvailableBSize()) {
4448       NS_ASSERTION(bandBSize == 0 || aFloatAvailableSpace.HasFloats(),
4449                    "redo line on totally empty line with non-empty band...");
4450       // We should never hit this case if we've placed floats on the
4451       // line; if we have, then the GetFloatAvailableSpace call is wrong
4452       // and needs to happen after the caller pops the float manager
4453       // state.
4454       aState.FloatManager()->AssertStateMatches(aFloatStateBeforeLine);
4455 
4456       if (!aFloatAvailableSpace.MayWiden() && bandBSize > 0) {
4457         // Move it down far enough to clear the current band.
4458         aState.mBCoord += bandBSize;
4459       } else {
4460         // Move it down by one dev pixel.
4461         aState.mBCoord += aState.mPresContext->DevPixelsToAppUnits(1);
4462       }
4463 
4464       aFloatAvailableSpace = aState.GetFloatAvailableSpace();
4465     } else {
4466       // There's nowhere to retry placing the line, so we want to push
4467       // it to the next page/column where its contents can fit not
4468       // next to a float.
4469       lineReflowStatus = LineReflowStatus::Truncated;
4470       PushTruncatedLine(aState, aLine, aKeepReflowGoing);
4471     }
4472 
4473     // XXX: a small optimization can be done here when paginating:
4474     // if the new Y coordinate is past the end of the block then
4475     // push the line and return now instead of later on after we are
4476     // past the float.
4477   } else if (LineReflowStatus::Truncated != lineReflowStatus &&
4478              LineReflowStatus::RedoNoPull != lineReflowStatus) {
4479     // If we are propagating out a break-before status then there is
4480     // no point in placing the line.
4481     if (!aState.mReflowStatus.IsInlineBreakBefore()) {
4482       if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
4483                      aFloatAvailableSpace, aAvailableSpaceBSize,
4484                      aKeepReflowGoing)) {
4485         lineReflowStatus = LineReflowStatus::RedoMoreFloats;
4486         // PlaceLine already called GetFloatAvailableSpaceForBSize or its
4487         // variant for us.
4488       }
4489     }
4490   }
4491 #ifdef DEBUG
4492   if (gNoisyReflow) {
4493     printf("Line reflow status = %s\n",
4494            LineReflowStatusToString(lineReflowStatus));
4495   }
4496 #endif
4497 
4498   if (aLineLayout.GetDirtyNextLine()) {
4499     // aLine may have been pushed to the overflow lines.
4500     FrameLines* overflowLines = GetOverflowLines();
4501     // We can't just compare iterators front() to aLine here, since they may be
4502     // in different lists.
4503     bool pushedToOverflowLines =
4504         overflowLines && overflowLines->mLines.front() == aLine.get();
4505     if (pushedToOverflowLines) {
4506       // aLine is stale, it's associated with the main line list but it should
4507       // be associated with the overflow line list now
4508       aLine = overflowLines->mLines.begin();
4509     }
4510     nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
4511     if (iter.Next() && iter.GetLine()->IsInline()) {
4512       iter.GetLine()->MarkDirty();
4513       if (iter.GetContainer() != this) {
4514         aState.mReflowStatus.SetNextInFlowNeedsReflow();
4515       }
4516     }
4517   }
4518 
4519   *aLineReflowStatus = lineReflowStatus;
4520 }
4521 
4522 /**
4523  * Reflow an inline frame. The reflow status is mapped from the frames
4524  * reflow status to the lines reflow status (not to our reflow status).
4525  * The line reflow status is simple: true means keep placing frames
4526  * on the line; false means don't (the line is done). If the line
4527  * has some sort of breaking affect then aLine's break-type will be set
4528  * to something other than StyleClear::None.
4529  */
ReflowInlineFrame(BlockReflowState & aState,nsLineLayout & aLineLayout,LineIterator aLine,nsIFrame * aFrame,LineReflowStatus * aLineReflowStatus)4530 void nsBlockFrame::ReflowInlineFrame(BlockReflowState& aState,
4531                                      nsLineLayout& aLineLayout,
4532                                      LineIterator aLine, nsIFrame* aFrame,
4533                                      LineReflowStatus* aLineReflowStatus) {
4534   MOZ_ASSERT(aFrame);
4535   *aLineReflowStatus = LineReflowStatus::OK;
4536 
4537 #ifdef NOISY_FIRST_LETTER
4538   ListTag(stdout);
4539   printf(": reflowing ");
4540   aFrame->ListTag(stdout);
4541   printf(" reflowingFirstLetter=%s\n",
4542          aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
4543 #endif
4544 
4545   if (aFrame->IsPlaceholderFrame()) {
4546     auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
4547     ph->ForgetLineIsEmptySoFar();
4548   }
4549 
4550   // Reflow the inline frame
4551   nsReflowStatus frameReflowStatus;
4552   bool pushedFrame;
4553   aLineLayout.ReflowFrame(aFrame, frameReflowStatus, nullptr, pushedFrame);
4554 
4555   if (frameReflowStatus.NextInFlowNeedsReflow()) {
4556     aLineLayout.SetDirtyNextLine();
4557   }
4558 
4559 #ifdef REALLY_NOISY_REFLOW
4560   aFrame->ListTag(stdout);
4561   printf(": status=%s\n", ToString(frameReflowStatus).c_str());
4562 #endif
4563 
4564 #if defined(REFLOW_STATUS_COVERAGE)
4565   RecordReflowStatus(false, frameReflowStatus);
4566 #endif
4567 
4568   // Send post-reflow notification
4569   aState.mPrevChild = aFrame;
4570 
4571   /* XXX
4572      This is where we need to add logic to handle some odd behavior.
4573      For one thing, we should usually place at least one thing next
4574      to a left float, even when that float takes up all the width on a line.
4575      see bug 22496
4576   */
4577 
4578   // Process the child frames reflow status. There are 5 cases:
4579   // complete, not-complete, break-before, break-after-complete,
4580   // break-after-not-complete. There are two situations: we are a
4581   // block or we are an inline. This makes a total of 10 cases
4582   // (fortunately, there is some overlap).
4583   aLine->SetBreakTypeAfter(StyleClear::None);
4584   if (frameReflowStatus.IsInlineBreak() ||
4585       StyleClear::None != aState.mFloatBreakType) {
4586     // Always abort the line reflow (because a line break is the
4587     // minimal amount of break we do).
4588     *aLineReflowStatus = LineReflowStatus::Stop;
4589 
4590     // XXX what should aLine's break-type be set to in all these cases?
4591     StyleClear breakType = frameReflowStatus.BreakType();
4592     MOZ_ASSERT(StyleClear::None != breakType ||
4593                    StyleClear::None != aState.mFloatBreakType,
4594                "bad break type");
4595 
4596     if (frameReflowStatus.IsInlineBreakBefore()) {
4597       // Break-before cases.
4598       if (aFrame == aLine->mFirstChild) {
4599         // If we break before the first frame on the line then we must
4600         // be trying to place content where there's no room (e.g. on a
4601         // line with wide floats). Inform the caller to reflow the
4602         // line after skipping past a float.
4603         *aLineReflowStatus = LineReflowStatus::RedoNextBand;
4604       } else {
4605         // It's not the first child on this line so go ahead and split
4606         // the line. We will see the frame again on the next-line.
4607         SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
4608 
4609         // If we're splitting the line because the frame didn't fit and it
4610         // was pushed, then mark the line as having word wrapped. We need to
4611         // know that if we're shrink wrapping our width
4612         if (pushedFrame) {
4613           aLine->SetLineWrapped(true);
4614         }
4615       }
4616     } else {
4617       // If a float split and its prev-in-flow was followed by a <BR>, then
4618       // combine the <BR>'s break type with the inline's break type (the inline
4619       // will be the very next frame after the split float).
4620       if (StyleClear::None != aState.mFloatBreakType) {
4621         breakType =
4622             nsLayoutUtils::CombineBreakType(breakType, aState.mFloatBreakType);
4623         aState.mFloatBreakType = StyleClear::None;
4624       }
4625       // Break-after cases
4626       if (breakType == StyleClear::Line) {
4627         if (!aLineLayout.GetLineEndsInBR()) {
4628           breakType = StyleClear::None;
4629         }
4630       }
4631       aLine->SetBreakTypeAfter(breakType);
4632       if (frameReflowStatus.IsComplete()) {
4633         // Split line, but after the frame just reflowed
4634         SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(),
4635                   aLineReflowStatus);
4636 
4637         if (frameReflowStatus.IsInlineBreakAfter() &&
4638             !aLineLayout.GetLineEndsInBR()) {
4639           aLineLayout.SetDirtyNextLine();
4640         }
4641       }
4642     }
4643   }
4644 
4645   if (!frameReflowStatus.IsFullyComplete()) {
4646     // Create a continuation for the incomplete frame. Note that the
4647     // frame may already have a continuation.
4648     CreateContinuationFor(aState, aLine, aFrame);
4649 
4650     // Remember that the line has wrapped
4651     if (!aLineLayout.GetLineEndsInBR()) {
4652       aLine->SetLineWrapped(true);
4653     }
4654 
4655     // If we just ended a first-letter frame or reflowed a placeholder then
4656     // don't split the line and don't stop the line reflow...
4657     // But if we are going to stop anyways we'd better split the line.
4658     if ((!frameReflowStatus.FirstLetterComplete() &&
4659          !aFrame->IsPlaceholderFrame()) ||
4660         *aLineReflowStatus == LineReflowStatus::Stop) {
4661       // Split line after the current frame
4662       *aLineReflowStatus = LineReflowStatus::Stop;
4663       SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(),
4664                 aLineReflowStatus);
4665     }
4666   }
4667 }
4668 
CreateContinuationFor(BlockReflowState & aState,nsLineBox * aLine,nsIFrame * aFrame)4669 bool nsBlockFrame::CreateContinuationFor(BlockReflowState& aState,
4670                                          nsLineBox* aLine, nsIFrame* aFrame) {
4671   nsIFrame* newFrame = nullptr;
4672 
4673   if (!aFrame->GetNextInFlow()) {
4674     newFrame =
4675         PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this);
4676 
4677     mFrames.InsertFrame(nullptr, aFrame, newFrame);
4678 
4679     if (aLine) {
4680       aLine->NoteFrameAdded(newFrame);
4681     }
4682   }
4683 #ifdef DEBUG
4684   VerifyLines(false);
4685 #endif
4686   return !!newFrame;
4687 }
4688 
SplitFloat(BlockReflowState & aState,nsIFrame * aFloat,const nsReflowStatus & aFloatStatus)4689 void nsBlockFrame::SplitFloat(BlockReflowState& aState, nsIFrame* aFloat,
4690                               const nsReflowStatus& aFloatStatus) {
4691   MOZ_ASSERT(!aFloatStatus.IsFullyComplete(),
4692              "why split the frame if it's fully complete?");
4693   MOZ_ASSERT(aState.mBlock == this);
4694 
4695   nsIFrame* nextInFlow = aFloat->GetNextInFlow();
4696   if (nextInFlow) {
4697     nsContainerFrame* oldParent = nextInFlow->GetParent();
4698     oldParent->StealFrame(nextInFlow);
4699     if (oldParent != this) {
4700       ReparentFrame(nextInFlow, oldParent, this);
4701     }
4702     if (!aFloatStatus.IsOverflowIncomplete()) {
4703       nextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
4704     }
4705   } else {
4706     nextInFlow =
4707         PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat, this);
4708   }
4709   if (aFloatStatus.IsOverflowIncomplete()) {
4710     nextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
4711   }
4712 
4713   StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat;
4714   if (floatStyle == StyleFloat::Left) {
4715     aState.FloatManager()->SetSplitLeftFloatAcrossBreak();
4716   } else {
4717     MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float side!");
4718     aState.FloatManager()->SetSplitRightFloatAcrossBreak();
4719   }
4720 
4721   aState.AppendPushedFloatChain(nextInFlow);
4722   if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_FLOAT_MGR)) ||
4723       MOZ_UNLIKELY(IsTrueOverflowContainer())) {
4724     aState.mReflowStatus.SetOverflowIncomplete();
4725   } else {
4726     aState.mReflowStatus.SetIncomplete();
4727   }
4728 }
4729 
GetLastFloat(nsLineBox * aLine)4730 static nsFloatCache* GetLastFloat(nsLineBox* aLine) {
4731   nsFloatCache* fc = aLine->GetFirstFloat();
4732   while (fc && fc->Next()) {
4733     fc = fc->Next();
4734   }
4735   return fc;
4736 }
4737 
CheckPlaceholderInLine(nsIFrame * aBlock,nsLineBox * aLine,nsFloatCache * aFC)4738 static bool CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine,
4739                                    nsFloatCache* aFC) {
4740   if (!aFC) return true;
4741   NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
4742                "float in a line should never be a continuation");
4743   NS_ASSERTION(!aFC->mFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT),
4744                "float in a line should never be a pushed float");
4745   nsIFrame* ph = aFC->mFloat->FirstInFlow()->GetPlaceholderFrame();
4746   for (nsIFrame* f = ph; f; f = f->GetParent()) {
4747     if (f->GetParent() == aBlock) return aLine->Contains(f);
4748   }
4749   NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
4750   return true;
4751 }
4752 
SplitLine(BlockReflowState & aState,nsLineLayout & aLineLayout,LineIterator aLine,nsIFrame * aFrame,LineReflowStatus * aLineReflowStatus)4753 void nsBlockFrame::SplitLine(BlockReflowState& aState,
4754                              nsLineLayout& aLineLayout, LineIterator aLine,
4755                              nsIFrame* aFrame,
4756                              LineReflowStatus* aLineReflowStatus) {
4757   MOZ_ASSERT(aLine->IsInline(), "illegal SplitLine on block line");
4758 
4759   int32_t pushCount =
4760       aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
4761   MOZ_ASSERT(pushCount >= 0, "bad push count");
4762 
4763 #ifdef DEBUG
4764   if (gNoisyReflow) {
4765     nsIFrame::IndentBy(stdout, gNoiseIndent);
4766     printf("split line: from line=%p pushCount=%d aFrame=",
4767            static_cast<void*>(aLine.get()), pushCount);
4768     if (aFrame) {
4769       aFrame->ListTag(stdout);
4770     } else {
4771       printf("(null)");
4772     }
4773     printf("\n");
4774     if (gReallyNoisyReflow) {
4775       aLine->List(stdout, gNoiseIndent + 1);
4776     }
4777   }
4778 #endif
4779 
4780   if (0 != pushCount) {
4781     MOZ_ASSERT(aLine->GetChildCount() > pushCount, "bad push");
4782     MOZ_ASSERT(nullptr != aFrame, "whoops");
4783 #ifdef DEBUG
4784     {
4785       nsIFrame* f = aFrame;
4786       int32_t count = pushCount;
4787       while (f && count > 0) {
4788         f = f->GetNextSibling();
4789         --count;
4790       }
4791       NS_ASSERTION(count == 0, "Not enough frames to push");
4792     }
4793 #endif
4794 
4795     // Put frames being split out into their own line
4796     nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
4797     mLines.after_insert(aLine, newLine);
4798 #ifdef DEBUG
4799     if (gReallyNoisyReflow) {
4800       newLine->List(stdout, gNoiseIndent + 1);
4801     }
4802 #endif
4803 
4804     // Let line layout know that some frames are no longer part of its
4805     // state.
4806     aLineLayout.SplitLineTo(aLine->GetChildCount());
4807 
4808     // If floats have been placed whose placeholders have been pushed to the new
4809     // line, we need to reflow the old line again. We don't want to look at the
4810     // frames in the new line, because as a large paragraph is laid out the
4811     // we'd get O(N^2) performance. So instead we just check that the last
4812     // float and the last below-current-line float are still in aLine.
4813     if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
4814         !CheckPlaceholderInLine(this, aLine,
4815                                 aState.mBelowCurrentLineFloats.Tail())) {
4816       *aLineReflowStatus = LineReflowStatus::RedoNoPull;
4817     }
4818 
4819 #ifdef DEBUG
4820     VerifyLines(true);
4821 #endif
4822   }
4823 }
4824 
IsLastLine(BlockReflowState & aState,LineIterator aLine)4825 bool nsBlockFrame::IsLastLine(BlockReflowState& aState, LineIterator aLine) {
4826   while (++aLine != LinesEnd()) {
4827     // There is another line
4828     if (0 != aLine->GetChildCount()) {
4829       // If the next line is a block line then this line is the last in a
4830       // group of inline lines.
4831       return aLine->IsBlock();
4832     }
4833     // The next line is empty, try the next one
4834   }
4835 
4836   // XXX Not sure about this part
4837   // Try our next-in-flows lines to answer the question
4838   nsBlockFrame* nextInFlow = (nsBlockFrame*)GetNextInFlow();
4839   while (nullptr != nextInFlow) {
4840     for (const auto& line : nextInFlow->Lines()) {
4841       if (0 != line.GetChildCount()) {
4842         return line.IsBlock();
4843       }
4844     }
4845     nextInFlow = (nsBlockFrame*)nextInFlow->GetNextInFlow();
4846   }
4847 
4848   // This is the last line - so don't allow justification
4849   return true;
4850 }
4851 
PlaceLine(BlockReflowState & aState,nsLineLayout & aLineLayout,LineIterator aLine,nsFloatManager::SavedState * aFloatStateBeforeLine,nsFlowAreaRect & aFlowArea,nscoord & aAvailableSpaceBSize,bool * aKeepReflowGoing)4852 bool nsBlockFrame::PlaceLine(BlockReflowState& aState,
4853                              nsLineLayout& aLineLayout, LineIterator aLine,
4854                              nsFloatManager::SavedState* aFloatStateBeforeLine,
4855                              nsFlowAreaRect& aFlowArea,
4856                              nscoord& aAvailableSpaceBSize,
4857                              bool* aKeepReflowGoing) {
4858   // Try to position the floats in a nowrap context.
4859   aLineLayout.FlushNoWrapFloats();
4860 
4861   // Trim extra white-space from the line before placing the frames
4862   aLineLayout.TrimTrailingWhiteSpace();
4863 
4864   // Vertically align the frames on this line.
4865   //
4866   // According to the CSS2 spec, section 12.6.1, the "marker" box
4867   // participates in the height calculation of the list-item box's
4868   // first line box.
4869   //
4870   // There are exactly two places a ::marker can be placed: near the
4871   // first or second line. It's only placed on the second line in a
4872   // rare case: when the first line is empty.
4873   WritingMode wm = aState.mReflowInput.GetWritingMode();
4874   bool addedMarker = false;
4875   if (HasOutsideMarker() &&
4876       ((aLine == mLines.front() &&
4877         (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
4878        (mLines.front() != mLines.back() && 0 == mLines.front()->BSize() &&
4879         aLine == mLines.begin().next()))) {
4880     ReflowOutput metrics(aState.mReflowInput);
4881     nsIFrame* marker = GetOutsideMarker();
4882     ReflowOutsideMarker(marker, aState, metrics, aState.mBCoord);
4883     NS_ASSERTION(!MarkerIsEmpty() || metrics.BSize(wm) == 0,
4884                  "empty ::marker frame took up space");
4885     aLineLayout.AddMarkerFrame(marker, metrics);
4886     addedMarker = true;
4887   }
4888   aLineLayout.VerticalAlignLine();
4889 
4890   // We want to consider the floats in the current line when determining
4891   // whether the float available space is shrunk. If mLineBSize doesn't
4892   // exist, we are in the first pass trying to place the line. Calling
4893   // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
4894   // for UpdateBand().
4895 
4896   // floatAvailableSpaceWithOldLineBSize is the float available space with
4897   // the old BSize, but including the floats that were added in this line.
4898   LogicalRect floatAvailableSpaceWithOldLineBSize =
4899       aState.mLineBSize.isNothing()
4900           ? aState.GetFloatAvailableSpace(aLine->BStart()).mRect
4901           : aState
4902                 .GetFloatAvailableSpaceForBSize(
4903                     aLine->BStart(), aState.mLineBSize.value(), nullptr)
4904                 .mRect;
4905 
4906   // As we redo for floats, we can't reduce the amount of BSize we're
4907   // checking.
4908   aAvailableSpaceBSize = std::max(aAvailableSpaceBSize, aLine->BSize());
4909   LogicalRect floatAvailableSpaceWithLineBSize =
4910       aState
4911           .GetFloatAvailableSpaceForBSize(aLine->BStart(), aAvailableSpaceBSize,
4912                                           nullptr)
4913           .mRect;
4914 
4915   // If the available space between the floats is smaller now that we
4916   // know the BSize, return false (and cause another pass with
4917   // LineReflowStatus::RedoMoreFloats).  We ensure aAvailableSpaceBSize
4918   // never decreases, which means that we can't reduce the set of floats
4919   // we intersect, which means that the available space cannot grow.
4920   if (AvailableSpaceShrunk(wm, floatAvailableSpaceWithOldLineBSize,
4921                            floatAvailableSpaceWithLineBSize, false)) {
4922     // Prepare data for redoing the line.
4923     aState.mLineBSize = Some(aLine->BSize());
4924 
4925     // Since we want to redo the line, we update aFlowArea by using the
4926     // aFloatStateBeforeLine, which is the float manager's state before the
4927     // line is placed.
4928     LogicalRect oldFloatAvailableSpace(aFlowArea.mRect);
4929     aFlowArea = aState.GetFloatAvailableSpaceForBSize(
4930         aLine->BStart(), aAvailableSpaceBSize, aFloatStateBeforeLine);
4931 
4932     NS_ASSERTION(
4933         aFlowArea.mRect.BStart(wm) == oldFloatAvailableSpace.BStart(wm),
4934         "yikes");
4935     // Restore the BSize to the position of the next band.
4936     aFlowArea.mRect.BSize(wm) = oldFloatAvailableSpace.BSize(wm);
4937 
4938     // Enforce both IStart() and IEnd() never move outwards to prevent
4939     // infinite grow-shrink loops.
4940     const nscoord iStartDiff =
4941         aFlowArea.mRect.IStart(wm) - oldFloatAvailableSpace.IStart(wm);
4942     const nscoord iEndDiff =
4943         aFlowArea.mRect.IEnd(wm) - oldFloatAvailableSpace.IEnd(wm);
4944     if (iStartDiff < 0) {
4945       aFlowArea.mRect.IStart(wm) -= iStartDiff;
4946       aFlowArea.mRect.ISize(wm) += iStartDiff;
4947     }
4948     if (iEndDiff > 0) {
4949       aFlowArea.mRect.ISize(wm) -= iEndDiff;
4950     }
4951 
4952     return false;
4953   }
4954 
4955 #ifdef DEBUG
4956   if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
4957     static nscoord lastHeight = 0;
4958     if (ABSURD_SIZE(aLine->BStart())) {
4959       lastHeight = aLine->BStart();
4960       if (abs(aLine->BStart() - lastHeight) > ABSURD_COORD / 10) {
4961         nsIFrame::ListTag(stdout);
4962         printf(": line=%p y=%d line.bounds.height=%d\n",
4963                static_cast<void*>(aLine.get()), aLine->BStart(),
4964                aLine->BSize());
4965       }
4966     } else {
4967       lastHeight = 0;
4968     }
4969   }
4970 #endif
4971 
4972   // Only block frames horizontally align their children because
4973   // inline frames "shrink-wrap" around their children (therefore
4974   // there is no extra horizontal space).
4975   const nsStyleText* styleText = StyleText();
4976 
4977   /**
4978    * We don't care checking for IsLastLine properly if we don't care (if it
4979    * can't change the used text-align value for the line).
4980    *
4981    * In other words, isLastLine really means isLastLineAndWeCare.
4982    */
4983   const bool isLastLine =
4984       !SVGUtils::IsInSVGTextSubtree(this) &&
4985       styleText->TextAlignForLastLine() != styleText->mTextAlign &&
4986       (aLineLayout.GetLineEndsInBR() || IsLastLine(aState, aLine));
4987 
4988   aLineLayout.TextAlignLine(aLine, isLastLine);
4989 
4990   // From here on, pfd->mBounds rectangles are incorrect because bidi
4991   // might have moved frames around!
4992   OverflowAreas overflowAreas;
4993   aLineLayout.RelativePositionFrames(overflowAreas);
4994   aLine->SetOverflowAreas(overflowAreas);
4995   if (addedMarker) {
4996     aLineLayout.RemoveMarkerFrame(GetOutsideMarker());
4997   }
4998 
4999   // Inline lines do not have margins themselves; however they are
5000   // impacted by prior block margins. If this line ends up having some
5001   // height then we zero out the previous block-end margin value that was
5002   // already applied to the line's starting Y coordinate. Otherwise we
5003   // leave it be so that the previous blocks block-end margin can be
5004   // collapsed with a block that follows.
5005   nscoord newBCoord;
5006 
5007   if (!aLine->CachedIsEmpty()) {
5008     // This line has some height. Therefore the application of the
5009     // previous-bottom-margin should stick.
5010     aState.mPrevBEndMargin.Zero();
5011     newBCoord = aLine->BEnd();
5012   } else {
5013     // Don't let the previous-bottom-margin value affect the newBCoord
5014     // coordinate (it was applied in ReflowInlineFrames speculatively)
5015     // since the line is empty.
5016     // We already called |ShouldApplyBStartMargin|, and if we applied it
5017     // then mShouldApplyBStartMargin is set.
5018     nscoord dy = aState.mFlags.mShouldApplyBStartMargin
5019                      ? -aState.mPrevBEndMargin.get()
5020                      : 0;
5021     newBCoord = aState.mBCoord + dy;
5022   }
5023 
5024   if (!aState.mReflowStatus.IsFullyComplete() &&
5025       ShouldAvoidBreakInside(aState.mReflowInput)) {
5026     aLine->AppendFloats(aState.mCurrentLineFloats);
5027     aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
5028     // Reflow the line again when we reflow at our new position.
5029     aLine->MarkDirty();
5030     *aKeepReflowGoing = false;
5031     return true;
5032   }
5033 
5034   // See if the line fit (our first line always does).
5035   if (mLines.front() != aLine &&
5036       aState.ContentBSize() != NS_UNCONSTRAINEDSIZE &&
5037       newBCoord > aState.ContentBEnd()) {
5038     NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
5039     if (ShouldAvoidBreakInside(aState.mReflowInput)) {
5040       // All our content doesn't fit, start on the next page.
5041       aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
5042       *aKeepReflowGoing = false;
5043     } else {
5044       // Push aLine and all of its children and anything else that
5045       // follows to our next-in-flow.
5046       PushTruncatedLine(aState, aLine, aKeepReflowGoing);
5047     }
5048     return true;
5049   }
5050 
5051   // Note that any early return before this update of aState.mBCoord
5052   // must either (a) return false or (b) set aKeepReflowGoing to false.
5053   // Otherwise we'll keep reflowing later lines at an incorrect
5054   // position, and we might not come back and clean up the damage later.
5055   aState.mBCoord = newBCoord;
5056 
5057   // Add the already placed current-line floats to the line
5058   aLine->AppendFloats(aState.mCurrentLineFloats);
5059 
5060   // Any below current line floats to place?
5061   if (aState.mBelowCurrentLineFloats.NotEmpty()) {
5062     // Reflow the below-current-line floats, which places on the line's
5063     // float list.
5064     aState.PlaceBelowCurrentLineFloats(aLine);
5065   }
5066 
5067   // When a line has floats, factor them into the overflow areas computations.
5068   if (aLine->HasFloats()) {
5069     // Union the float overflow areas (stored in aState) and the value computed
5070     // by the line layout code.
5071     OverflowAreas lineOverflowAreas = aState.mFloatOverflowAreas;
5072     lineOverflowAreas.UnionWith(aLine->GetOverflowAreas());
5073     aLine->SetOverflowAreas(lineOverflowAreas);
5074 
5075 #ifdef NOISY_OVERFLOW_AREAS
5076     printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
5077            ListTag().get(), aLine.get(),
5078            ToString(aLine->InkOverflowRect()).c_str(),
5079            ToString(aLine->ScrollableOverflowRect()).c_str());
5080 #endif
5081   }
5082 
5083   // Apply break-after clearing if necessary
5084   // This must stay in sync with |ReflowDirtyLines|.
5085   if (aLine->HasFloatBreakAfter()) {
5086     std::tie(aState.mBCoord, std::ignore) =
5087         aState.ClearFloats(aState.mBCoord, aLine->GetBreakTypeAfter());
5088   }
5089   return true;
5090 }
5091 
PushLines(BlockReflowState & aState,nsLineList::iterator aLineBefore)5092 void nsBlockFrame::PushLines(BlockReflowState& aState,
5093                              nsLineList::iterator aLineBefore) {
5094   // NOTE: aLineBefore is always a normal line, not an overflow line.
5095   // The following expression will assert otherwise.
5096   DebugOnly<bool> check = aLineBefore == mLines.begin();
5097 
5098   nsLineList::iterator overBegin(aLineBefore.next());
5099 
5100   // PushTruncatedPlaceholderLine sometimes pushes the first line.  Ugh.
5101   bool firstLine = overBegin == LinesBegin();
5102 
5103   if (overBegin != LinesEnd()) {
5104     // Remove floats in the lines from mFloats
5105     nsFrameList floats;
5106     CollectFloats(overBegin->mFirstChild, floats, true);
5107 
5108     if (floats.NotEmpty()) {
5109 #ifdef DEBUG
5110       for (nsIFrame* f : floats) {
5111         MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT),
5112                    "CollectFloats should've removed that bit");
5113       }
5114 #endif
5115       // Push the floats onto the front of the overflow out-of-flows list
5116       nsAutoOOFFrameList oofs(this);
5117       oofs.mList.InsertFrames(nullptr, nullptr, floats);
5118     }
5119 
5120     // overflow lines can already exist in some cases, in particular,
5121     // when shrinkwrapping and we discover that the shrinkwap causes
5122     // the height of some child block to grow which creates additional
5123     // overflowing content. In such cases we must prepend the new
5124     // overflow to the existing overflow.
5125     FrameLines* overflowLines = RemoveOverflowLines();
5126     if (!overflowLines) {
5127       // XXXldb use presshell arena!
5128       overflowLines = new FrameLines();
5129     }
5130     if (overflowLines) {
5131       nsIFrame* lineBeforeLastFrame;
5132       if (firstLine) {
5133         lineBeforeLastFrame = nullptr;  // removes all frames
5134       } else {
5135         nsIFrame* f = overBegin->mFirstChild;
5136         lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
5137         NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
5138                      "unexpected line frames");
5139       }
5140       nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
5141       overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames);
5142 
5143       overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
5144                                    overBegin, LinesEnd());
5145       NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
5146       // this takes ownership but it won't delete it immediately so we
5147       // can keep using it.
5148       SetOverflowLines(overflowLines);
5149 
5150       // Mark all the overflow lines dirty so that they get reflowed when
5151       // they are pulled up by our next-in-flow.
5152 
5153       // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
5154       for (LineIterator line = overflowLines->mLines.begin(),
5155                         line_end = overflowLines->mLines.end();
5156            line != line_end; ++line) {
5157         line->MarkDirty();
5158         line->MarkPreviousMarginDirty();
5159         line->SetMovedFragments();
5160         line->SetBoundsEmpty();
5161         if (line->HasFloats()) {
5162           line->FreeFloats(aState.mFloatCacheFreeList);
5163         }
5164       }
5165     }
5166   }
5167 
5168 #ifdef DEBUG
5169   VerifyOverflowSituation();
5170 #endif
5171 }
5172 
5173 // The overflowLines property is stored as a pointer to a line list,
5174 // which must be deleted.  However, the following functions all maintain
5175 // the invariant that the property is never set if the list is empty.
5176 
DrainOverflowLines()5177 bool nsBlockFrame::DrainOverflowLines() {
5178 #ifdef DEBUG
5179   VerifyOverflowSituation();
5180 #endif
5181 
5182   // Steal the prev-in-flow's overflow lines and prepend them.
5183   bool didFindOverflow = false;
5184   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
5185   if (prevBlock) {
5186     prevBlock->ClearLineCursor();
5187     FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
5188     if (overflowLines) {
5189       // Make all the frames on the overflow line list mine.
5190       ReparentFrames(overflowLines->mFrames, prevBlock, this);
5191 
5192       // Collect overflow containers from our OverflowContainers list that are
5193       // continuations from the frames we picked up from our prev-in-flow, then
5194       // prepend those to ExcessOverflowContainers to ensure the continuations
5195       // are ordered.
5196       if (GetOverflowContainers()) {
5197         nsFrameList ocContinuations;
5198         for (auto* f : overflowLines->mFrames) {
5199           auto* cont = f;
5200           bool done = false;
5201           while (!done && (cont = cont->GetNextContinuation()) &&
5202                  cont->GetParent() == this) {
5203             bool onlyChild = !cont->GetPrevSibling() && !cont->GetNextSibling();
5204             if (cont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) &&
5205                 TryRemoveFrame(OverflowContainersProperty(), cont)) {
5206               ocContinuations.AppendFrame(nullptr, cont);
5207               done = onlyChild;
5208               continue;
5209             }
5210             break;
5211           }
5212           if (done) {
5213             break;
5214           }
5215         }
5216         if (!ocContinuations.IsEmpty()) {
5217           if (nsFrameList* eoc = GetExcessOverflowContainers()) {
5218             eoc->InsertFrames(nullptr, nullptr, ocContinuations);
5219           } else {
5220             SetExcessOverflowContainers(std::move(ocContinuations));
5221           }
5222         }
5223       }
5224 
5225       // Make the overflow out-of-flow frames mine too.
5226       nsAutoOOFFrameList oofs(prevBlock);
5227       if (oofs.mList.NotEmpty()) {
5228         // In case we own any next-in-flows of any of the drained frames, then
5229         // move those to the PushedFloat list.
5230         nsFrameList pushedFloats;
5231         for (nsFrameList::Enumerator e(oofs.mList); !e.AtEnd(); e.Next()) {
5232           nsIFrame* nif = e.get()->GetNextInFlow();
5233           for (; nif && nif->GetParent() == this; nif = nif->GetNextInFlow()) {
5234             MOZ_ASSERT(nif->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT));
5235             RemoveFloat(nif);
5236             pushedFloats.AppendFrame(nullptr, nif);
5237           }
5238         }
5239         ReparentFrames(oofs.mList, prevBlock, this);
5240         mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
5241         if (!pushedFloats.IsEmpty()) {
5242           nsFrameList* pf = EnsurePushedFloats();
5243           pf->InsertFrames(nullptr, nullptr, pushedFloats);
5244         }
5245       }
5246 
5247       if (!mLines.empty()) {
5248         // Remember to recompute the margins on the first line. This will
5249         // also recompute the correct deltaBCoord if necessary.
5250         mLines.front()->MarkPreviousMarginDirty();
5251       }
5252       // The overflow lines have already been marked dirty and their previous
5253       // margins marked dirty also.
5254 
5255       // Prepend the overflow frames/lines to our principal list.
5256       mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
5257       mLines.splice(mLines.begin(), overflowLines->mLines);
5258       NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
5259       delete overflowLines;
5260       didFindOverflow = true;
5261     }
5262   }
5263 
5264   // Now append our own overflow lines.
5265   return DrainSelfOverflowList() || didFindOverflow;
5266 }
5267 
DrainSelfOverflowList()5268 bool nsBlockFrame::DrainSelfOverflowList() {
5269   UniquePtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
5270   if (!ourOverflowLines) {
5271     return false;
5272   }
5273 
5274   // No need to reparent frames in our own overflow lines/oofs, because they're
5275   // already ours. But we should put overflow floats back in mFloats.
5276   // (explicit scope to remove the OOF list before VerifyOverflowSituation)
5277   {
5278     nsAutoOOFFrameList oofs(this);
5279     if (oofs.mList.NotEmpty()) {
5280 #ifdef DEBUG
5281       for (nsIFrame* f : oofs.mList) {
5282         MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT),
5283                    "CollectFloats should've removed that bit");
5284       }
5285 #endif
5286       // The overflow floats go after our regular floats.
5287       mFloats.AppendFrames(nullptr, oofs.mList);
5288     }
5289   }
5290   if (!ourOverflowLines->mLines.empty()) {
5291     mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
5292     mLines.splice(mLines.end(), ourOverflowLines->mLines);
5293   }
5294 
5295 #ifdef DEBUG
5296   VerifyOverflowSituation();
5297 #endif
5298   return true;
5299 }
5300 
5301 /**
5302  * Pushed floats are floats whose placeholders are in a previous
5303  * continuation.  They might themselves be next-continuations of a float
5304  * that partially fit in an earlier continuation, or they might be the
5305  * first continuation of a float that couldn't be placed at all.
5306  *
5307  * Pushed floats live permanently at the beginning of a block's float
5308  * list, where they must live *before* any floats whose placeholders are
5309  * in that block.
5310  *
5311  * Temporarily, during reflow, they also live on the pushed floats list,
5312  * which only holds them between (a) when one continuation pushes them to
5313  * its pushed floats list because they don't fit and (b) when the next
5314  * continuation pulls them onto the beginning of its float list.
5315  *
5316  * DrainPushedFloats sets up pushed floats the way we need them at the
5317  * start of reflow; they are then reflowed by ReflowPushedFloats (which
5318  * might push some of them on).  Floats with placeholders in this block
5319  * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
5320  * also maintains these invariants.
5321  *
5322  * DrainSelfPushedFloats moves any pushed floats from this block's own
5323  * PushedFloats list back into mFloats.  DrainPushedFloats additionally
5324  * moves frames from its prev-in-flow's PushedFloats list into mFloats.
5325  */
DrainSelfPushedFloats()5326 void nsBlockFrame::DrainSelfPushedFloats() {
5327   // If we're getting reflowed multiple times without our
5328   // next-continuation being reflowed, we might need to pull back floats
5329   // that we just put in the list to be pushed to our next-in-flow.
5330   // We don't want to pull back any next-in-flows of floats on our own
5331   // float list, and we only need to pull back first-in-flows whose
5332   // placeholders were in earlier blocks (since first-in-flows whose
5333   // placeholders are in this block will get pulled appropriately by
5334   // AddFloat, and will then be more likely to be in the correct order).
5335   // FIXME: What if there's a continuation in our pushed floats list
5336   // whose prev-in-flow is in a previous continuation of this block
5337   // rather than this block?  Might we need to pull it back so we don't
5338   // report ourselves complete?
5339   // FIXME: Maybe we should just pull all of them back?
5340   nsPresContext* presContext = PresContext();
5341   nsFrameList* ourPushedFloats = GetPushedFloats();
5342   if (ourPushedFloats) {
5343     // When we pull back floats, we want to put them with the pushed
5344     // floats, which must live at the start of our float list, but we
5345     // want them at the end of those pushed floats.
5346     // FIXME: This isn't quite right!  What if they're all pushed floats?
5347     nsIFrame* insertionPrevSibling = nullptr; /* beginning of list */
5348     for (nsIFrame* f = mFloats.FirstChild();
5349          f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT);
5350          f = f->GetNextSibling()) {
5351       insertionPrevSibling = f;
5352     }
5353 
5354     for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
5355       next = f->GetPrevSibling();
5356 
5357       if (f->GetPrevContinuation()) {
5358         // FIXME
5359       } else {
5360         nsPlaceholderFrame* placeholder = f->GetPlaceholderFrame();
5361         nsIFrame* floatOriginalParent =
5362             presContext->PresShell()
5363                 ->FrameConstructor()
5364                 ->GetFloatContainingBlock(placeholder);
5365         if (floatOriginalParent != this) {
5366           // This is a first continuation that was pushed from one of our
5367           // previous continuations.  Take it out of the pushed floats
5368           // list and put it in our floats list, before any of our
5369           // floats, but after other pushed floats.
5370           ourPushedFloats->RemoveFrame(f);
5371           mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
5372         }
5373       }
5374     }
5375 
5376     if (ourPushedFloats->IsEmpty()) {
5377       RemovePushedFloats()->Delete(presContext->PresShell());
5378     }
5379   }
5380 }
5381 
DrainPushedFloats()5382 void nsBlockFrame::DrainPushedFloats() {
5383   DrainSelfPushedFloats();
5384 
5385   // After our prev-in-flow has completed reflow, it may have a pushed
5386   // floats list, containing floats that we need to own.  Take these.
5387   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
5388   if (prevBlock) {
5389     AutoFrameListPtr list(PresContext(), prevBlock->RemovePushedFloats());
5390     if (list && list->NotEmpty()) {
5391       mFloats.InsertFrames(this, nullptr, *list);
5392     }
5393   }
5394 }
5395 
GetOverflowLines() const5396 nsBlockFrame::FrameLines* nsBlockFrame::GetOverflowLines() const {
5397   if (!HasOverflowLines()) {
5398     return nullptr;
5399   }
5400   FrameLines* prop = GetProperty(OverflowLinesProperty());
5401   NS_ASSERTION(
5402       prop && !prop->mLines.empty() &&
5403               prop->mLines.front()->GetChildCount() == 0
5404           ? prop->mFrames.IsEmpty()
5405           : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
5406       "value should always be stored and non-empty when state set");
5407   return prop;
5408 }
5409 
RemoveOverflowLines()5410 nsBlockFrame::FrameLines* nsBlockFrame::RemoveOverflowLines() {
5411   if (!HasOverflowLines()) {
5412     return nullptr;
5413   }
5414   FrameLines* prop = TakeProperty(OverflowLinesProperty());
5415   NS_ASSERTION(
5416       prop && !prop->mLines.empty() &&
5417               prop->mLines.front()->GetChildCount() == 0
5418           ? prop->mFrames.IsEmpty()
5419           : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
5420       "value should always be stored and non-empty when state set");
5421   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
5422   return prop;
5423 }
5424 
DestroyOverflowLines()5425 void nsBlockFrame::DestroyOverflowLines() {
5426   NS_ASSERTION(HasOverflowLines(), "huh?");
5427   FrameLines* prop = TakeProperty(OverflowLinesProperty());
5428   NS_ASSERTION(prop && prop->mLines.empty(),
5429                "value should always be stored but empty when destroying");
5430   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
5431   delete prop;
5432 }
5433 
5434 // This takes ownership of aOverflowLines.
5435 // XXX We should allocate overflowLines from presShell arena!
SetOverflowLines(FrameLines * aOverflowLines)5436 void nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines) {
5437   NS_ASSERTION(aOverflowLines, "null lines");
5438   NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
5439   NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
5440                    aOverflowLines->mFrames.FirstChild(),
5441                "invalid overflow lines / frames");
5442   NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES),
5443                "Overwriting existing overflow lines");
5444 
5445   // Verify that we won't overwrite an existing overflow list
5446   NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
5447   SetProperty(OverflowLinesProperty(), aOverflowLines);
5448   AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
5449 }
5450 
GetOverflowOutOfFlows() const5451 nsFrameList* nsBlockFrame::GetOverflowOutOfFlows() const {
5452   if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
5453     return nullptr;
5454   }
5455   nsFrameList* result = GetProperty(OverflowOutOfFlowsProperty());
5456   NS_ASSERTION(result, "value should always be non-empty when state set");
5457   return result;
5458 }
5459 
5460 // This takes ownership of the frames
SetOverflowOutOfFlows(const nsFrameList & aList,nsFrameList * aPropValue)5461 void nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
5462                                          nsFrameList* aPropValue) {
5463   MOZ_ASSERT(
5464       HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) == !!aPropValue,
5465       "state does not match value");
5466 
5467   if (aList.IsEmpty()) {
5468     if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
5469       return;
5470     }
5471     nsFrameList* list = TakeProperty(OverflowOutOfFlowsProperty());
5472     NS_ASSERTION(aPropValue == list, "prop value mismatch");
5473     list->Clear();
5474     list->Delete(PresShell());
5475     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
5476   } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
5477     NS_ASSERTION(aPropValue == GetProperty(OverflowOutOfFlowsProperty()),
5478                  "prop value mismatch");
5479     *aPropValue = aList;
5480   } else {
5481     SetProperty(OverflowOutOfFlowsProperty(),
5482                 new (PresShell()) nsFrameList(aList));
5483     AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
5484   }
5485 }
5486 
GetInsideMarker() const5487 nsIFrame* nsBlockFrame::GetInsideMarker() const {
5488   if (!HasInsideMarker()) {
5489     return nullptr;
5490   }
5491   NS_ASSERTION(!HasOutsideMarker(), "invalid marker state");
5492   nsIFrame* frame = GetProperty(InsideMarkerProperty());
5493   NS_ASSERTION(frame, "bogus inside ::marker frame");
5494   return frame;
5495 }
5496 
GetOutsideMarker() const5497 nsIFrame* nsBlockFrame::GetOutsideMarker() const {
5498   nsFrameList* list = GetOutsideMarkerList();
5499   return list ? list->FirstChild() : nullptr;
5500 }
5501 
GetOutsideMarkerList() const5502 nsFrameList* nsBlockFrame::GetOutsideMarkerList() const {
5503   if (!HasOutsideMarker()) {
5504     return nullptr;
5505   }
5506   NS_ASSERTION(!HasInsideMarker(), "invalid marker state");
5507   nsFrameList* list = GetProperty(OutsideMarkerProperty());
5508   NS_ASSERTION(list && list->GetLength() == 1, "bogus outside ::marker list");
5509   return list;
5510 }
5511 
GetPushedFloats() const5512 nsFrameList* nsBlockFrame::GetPushedFloats() const {
5513   if (!HasPushedFloats()) {
5514     return nullptr;
5515   }
5516   nsFrameList* result = GetProperty(PushedFloatProperty());
5517   NS_ASSERTION(result, "value should always be non-empty when state set");
5518   return result;
5519 }
5520 
EnsurePushedFloats()5521 nsFrameList* nsBlockFrame::EnsurePushedFloats() {
5522   nsFrameList* result = GetPushedFloats();
5523   if (result) return result;
5524 
5525   result = new (PresShell()) nsFrameList;
5526   SetProperty(PushedFloatProperty(), result);
5527   AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
5528 
5529   return result;
5530 }
5531 
RemovePushedFloats()5532 nsFrameList* nsBlockFrame::RemovePushedFloats() {
5533   if (!HasPushedFloats()) {
5534     return nullptr;
5535   }
5536   nsFrameList* result = TakeProperty(PushedFloatProperty());
5537   RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
5538   NS_ASSERTION(result, "value should always be non-empty when state set");
5539   return result;
5540 }
5541 
5542 //////////////////////////////////////////////////////////////////////
5543 // Frame list manipulation routines
5544 
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)5545 void nsBlockFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) {
5546   if (aFrameList.IsEmpty()) {
5547     return;
5548   }
5549   if (aListID != kPrincipalList) {
5550     if (kFloatList == aListID) {
5551       DrainSelfPushedFloats();  // ensure the last frame is in mFloats
5552       mFloats.AppendFrames(nullptr, aFrameList);
5553       return;
5554     }
5555     MOZ_ASSERT(kNoReflowPrincipalList == aListID, "unexpected child list");
5556   }
5557 
5558   // Find the proper last-child for where the append should go
5559   nsIFrame* lastKid = mFrames.LastChild();
5560   NS_ASSERTION(
5561       (mLines.empty() ? nullptr : mLines.back()->LastChild()) == lastKid,
5562       "out-of-sync mLines / mFrames");
5563 
5564 #ifdef NOISY_REFLOW_REASON
5565   ListTag(stdout);
5566   printf(": append ");
5567   for (nsIFrame* frame : aFrameList) {
5568     frame->ListTag(out);
5569   }
5570   if (lastKid) {
5571     printf(" after ");
5572     lastKid->ListTag(stdout);
5573   }
5574   printf("\n");
5575 #endif
5576 
5577   if (SVGUtils::IsInSVGTextSubtree(this)) {
5578     MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
5579                "unexpected block frame in SVG text");
5580     // Workaround for bug 1399425 in case this bit has been removed from the
5581     // SVGTextFrame just before the parser adds more descendant nodes.
5582     GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
5583   }
5584 
5585   AddFrames(aFrameList, lastKid, nullptr);
5586   if (aListID != kNoReflowPrincipalList) {
5587     PresShell()->FrameNeedsReflow(
5588         this, IntrinsicDirty::TreeChange,
5589         NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
5590   }
5591 }
5592 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)5593 void nsBlockFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
5594                                 const nsLineList::iterator* aPrevFrameLine,
5595                                 nsFrameList& aFrameList) {
5596   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
5597                "inserting after sibling frame with different parent");
5598 
5599   if (aListID != kPrincipalList) {
5600     if (kFloatList == aListID) {
5601       DrainSelfPushedFloats();  // ensure aPrevFrame is in mFloats
5602       mFloats.InsertFrames(this, aPrevFrame, aFrameList);
5603       return;
5604     }
5605     MOZ_ASSERT(kNoReflowPrincipalList == aListID, "unexpected child list");
5606   }
5607 
5608 #ifdef NOISY_REFLOW_REASON
5609   ListTag(stdout);
5610   printf(": insert ");
5611   for (nsIFrame* frame : aFrameList) {
5612     frame->ListTag(out);
5613   }
5614   if (aPrevFrame) {
5615     printf(" after ");
5616     aPrevFrame->ListTag(stdout);
5617   }
5618   printf("\n");
5619 #endif
5620 
5621   AddFrames(aFrameList, aPrevFrame, aPrevFrameLine);
5622   if (aListID != kNoReflowPrincipalList) {
5623     PresShell()->FrameNeedsReflow(
5624         this, IntrinsicDirty::TreeChange,
5625         NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
5626   }
5627 }
5628 
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)5629 void nsBlockFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
5630 #ifdef NOISY_REFLOW_REASON
5631   ListTag(stdout);
5632   printf(": remove ");
5633   aOldFrame->ListTag(stdout);
5634   printf("\n");
5635 #endif
5636 
5637   if (aListID == kPrincipalList) {
5638     bool hasFloats = BlockHasAnyFloats(aOldFrame);
5639     DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
5640     if (hasFloats) {
5641       MarkSameFloatManagerLinesDirty(this);
5642     }
5643   } else if (kFloatList == aListID) {
5644     // Make sure to mark affected lines dirty for the float frame
5645     // we are removing; this way is a bit messy, but so is the rest of the code.
5646     // See bug 390762.
5647     NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
5648                  "RemoveFrame should not be called on pushed floats.");
5649     for (nsIFrame* f = aOldFrame;
5650          f && !f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
5651          f = f->GetNextContinuation()) {
5652       MarkSameFloatManagerLinesDirty(
5653           static_cast<nsBlockFrame*>(f->GetParent()));
5654     }
5655     DoRemoveOutOfFlowFrame(aOldFrame);
5656   } else if (kNoReflowPrincipalList == aListID) {
5657     // Skip the call to |FrameNeedsReflow| below by returning now.
5658     DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
5659     return;
5660   } else {
5661     MOZ_CRASH("unexpected child list");
5662   }
5663 
5664   PresShell()->FrameNeedsReflow(
5665       this, IntrinsicDirty::TreeChange,
5666       NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
5667 }
5668 
ShouldPutNextSiblingOnNewLine(nsIFrame * aLastFrame)5669 static bool ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame) {
5670   LayoutFrameType type = aLastFrame->Type();
5671   if (type == LayoutFrameType::Br) {
5672     return true;
5673   }
5674   // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
5675   if (type == LayoutFrameType::Text &&
5676       !aLastFrame->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING)) {
5677     return aLastFrame->HasSignificantTerminalNewline();
5678   }
5679   return false;
5680 }
5681 
AddFrames(nsFrameList & aFrameList,nsIFrame * aPrevSibling,const nsLineList::iterator * aPrevSiblingLine)5682 void nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling,
5683                              const nsLineList::iterator* aPrevSiblingLine) {
5684   // Clear our line cursor, since our lines may change.
5685   ClearLineCursor();
5686 
5687   if (aFrameList.IsEmpty()) {
5688     return;
5689   }
5690 
5691   // Attempt to find the line that contains the previous sibling
5692   nsLineList* lineList = &mLines;
5693   nsFrameList* frames = &mFrames;
5694   nsLineList::iterator prevSibLine;
5695   int32_t prevSiblingIndex;
5696   if (aPrevSiblingLine) {
5697     MOZ_ASSERT(aPrevSibling);
5698     prevSibLine = *aPrevSiblingLine;
5699     FrameLines* overflowLines = GetOverflowLines();
5700     MOZ_ASSERT(prevSibLine.IsInSameList(mLines.begin()) ||
5701                    (overflowLines &&
5702                     prevSibLine.IsInSameList(overflowLines->mLines.begin())),
5703                "must be one of our line lists");
5704     if (overflowLines) {
5705       // We need to find out which list it's actually in.  Assume that
5706       // *if* we have overflow lines, that our primary lines aren't
5707       // huge, but our overflow lines might be.
5708       nsLineList::iterator line = mLines.begin(), lineEnd = mLines.end();
5709       while (line != lineEnd) {
5710         if (line == prevSibLine) {
5711           break;
5712         }
5713         ++line;
5714       }
5715       if (line == lineEnd) {
5716         // By elimination, the line must be in our overflow lines.
5717         lineList = &overflowLines->mLines;
5718         frames = &overflowLines->mFrames;
5719       }
5720     }
5721 
5722     nsLineList::iterator nextLine = prevSibLine.next();
5723     nsIFrame* lastFrameInLine = nextLine == lineList->end()
5724                                     ? frames->LastChild()
5725                                     : nextLine->mFirstChild->GetPrevSibling();
5726     prevSiblingIndex = prevSibLine->RIndexOf(aPrevSibling, lastFrameInLine);
5727     MOZ_ASSERT(prevSiblingIndex >= 0,
5728                "aPrevSibling must be in aPrevSiblingLine");
5729   } else {
5730     prevSibLine = lineList->end();
5731     prevSiblingIndex = -1;
5732     if (aPrevSibling) {
5733       // XXX_perf This is technically O(N^2) in some cases, but by using
5734       // RFind instead of Find, we make it O(N) in the most common case,
5735       // which is appending content.
5736 
5737       // Find the line that contains the previous sibling
5738       if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
5739                                           prevSibLine, mFrames.LastChild(),
5740                                           &prevSiblingIndex)) {
5741         // Not in mLines - try overflow lines.
5742         FrameLines* overflowLines = GetOverflowLines();
5743         bool found = false;
5744         if (overflowLines) {
5745           prevSibLine = overflowLines->mLines.end();
5746           prevSiblingIndex = -1;
5747           found = nsLineBox::RFindLineContaining(
5748               aPrevSibling, overflowLines->mLines.begin(), prevSibLine,
5749               overflowLines->mFrames.LastChild(), &prevSiblingIndex);
5750         }
5751         if (MOZ_LIKELY(found)) {
5752           lineList = &overflowLines->mLines;
5753           frames = &overflowLines->mFrames;
5754         } else {
5755           // Note: defensive code! RFindLineContaining must not return
5756           // false in this case, so if it does...
5757           MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
5758           aPrevSibling = nullptr;
5759           prevSibLine = lineList->end();
5760         }
5761       }
5762     }
5763   }
5764 
5765   // Find the frame following aPrevSibling so that we can join up the
5766   // two lists of frames.
5767   if (aPrevSibling) {
5768     // Split line containing aPrevSibling in two if the insertion
5769     // point is somewhere in the middle of the line.
5770     int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
5771     if (rem) {
5772       // Split the line in two where the frame(s) are being inserted.
5773       nsLineBox* line =
5774           NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
5775       lineList->after_insert(prevSibLine, line);
5776       // Mark prevSibLine dirty and as needing textrun invalidation, since
5777       // we may be breaking up text in the line. Its previous line may also
5778       // need to be invalidated because it may be able to pull some text up.
5779       MarkLineDirty(prevSibLine, lineList);
5780       // The new line will also need its textruns recomputed because of the
5781       // frame changes.
5782       line->MarkDirty();
5783       line->SetInvalidateTextRuns(true);
5784     }
5785   } else if (!lineList->empty()) {
5786     lineList->front()->MarkDirty();
5787     lineList->front()->SetInvalidateTextRuns(true);
5788   }
5789   const nsFrameList::Slice& newFrames =
5790       frames->InsertFrames(nullptr, aPrevSibling, aFrameList);
5791 
5792   // Walk through the new frames being added and update the line data
5793   // structures to fit.
5794   for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
5795     nsIFrame* newFrame = e.get();
5796     NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
5797                  "Unexpected aPrevSibling");
5798     NS_ASSERTION(
5799         !newFrame->IsPlaceholderFrame() ||
5800             (!newFrame->IsAbsolutelyPositioned() && !newFrame->IsFloating()),
5801         "Placeholders should not float or be positioned");
5802 
5803     bool isBlock = newFrame->IsBlockOutside();
5804 
5805     // If the frame is a block frame, or if there is no previous line or if the
5806     // previous line is a block line we need to make a new line.  We also make
5807     // a new line, as an optimization, in the two cases we know we'll need it:
5808     // if the previous line ended with a <br>, or if it has significant
5809     // whitespace and ended in a newline.
5810     if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
5811         (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
5812       // Create a new line for the frame and add its line to the line
5813       // list.
5814       nsLineBox* line = NewLineBox(newFrame, isBlock);
5815       if (prevSibLine != lineList->end()) {
5816         // Append new line after prevSibLine
5817         lineList->after_insert(prevSibLine, line);
5818         ++prevSibLine;
5819       } else {
5820         // New line is going before the other lines
5821         lineList->push_front(line);
5822         prevSibLine = lineList->begin();
5823       }
5824     } else {
5825       prevSibLine->NoteFrameAdded(newFrame);
5826       // We're adding inline content to prevSibLine, so we need to mark it
5827       // dirty, ensure its textruns are recomputed, and possibly do the same
5828       // to its previous line since that line may be able to pull content up.
5829       MarkLineDirty(prevSibLine, lineList);
5830     }
5831 
5832     aPrevSibling = newFrame;
5833   }
5834 
5835 #ifdef DEBUG
5836   MOZ_ASSERT(aFrameList.IsEmpty());
5837   VerifyLines(true);
5838 #endif
5839 }
5840 
GetRubyContentPseudoFrame()5841 nsContainerFrame* nsBlockFrame::GetRubyContentPseudoFrame() {
5842   auto* firstChild = PrincipalChildList().FirstChild();
5843   if (firstChild && firstChild->IsRubyFrame() &&
5844       firstChild->Style()->GetPseudoType() ==
5845           mozilla::PseudoStyleType::blockRubyContent) {
5846     return static_cast<nsContainerFrame*>(firstChild);
5847   }
5848   return nullptr;
5849 }
5850 
GetContentInsertionFrame()5851 nsContainerFrame* nsBlockFrame::GetContentInsertionFrame() {
5852   // 'display:block ruby' use the inner (Ruby) frame for insertions.
5853   if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
5854     return rubyContentPseudoFrame;
5855   }
5856   return this;
5857 }
5858 
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)5859 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
5860     nsTArray<OwnedAnonBox>& aResult) {
5861   if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
5862     aResult.AppendElement(OwnedAnonBox(rubyContentPseudoFrame));
5863   }
5864 }
5865 
RemoveFloatFromFloatCache(nsIFrame * aFloat)5866 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat) {
5867   // Find which line contains the float, so we can update
5868   // the float cache.
5869   for (auto& line : Lines()) {
5870     if (line.IsInline() && line.RemoveFloat(aFloat)) {
5871       break;
5872     }
5873   }
5874 }
5875 
RemoveFloat(nsIFrame * aFloat)5876 void nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
5877 #ifdef DEBUG
5878   // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
5879   // frame list properties.
5880   if (!mFloats.ContainsFrame(aFloat)) {
5881     MOZ_ASSERT(
5882         (GetOverflowOutOfFlows() &&
5883          GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
5884             (GetPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat)),
5885         "aFloat is not our child or on an unexpected frame list");
5886   }
5887 #endif
5888 
5889   if (mFloats.StartRemoveFrame(aFloat)) {
5890     return;
5891   }
5892 
5893   nsFrameList* list = GetPushedFloats();
5894   if (list && list->ContinueRemoveFrame(aFloat)) {
5895 #if 0
5896     // XXXmats not yet - need to investigate BlockReflowState::mPushedFloats
5897     // first so we don't leave it pointing to a deleted list.
5898     if (list->IsEmpty()) {
5899       delete RemovePushedFloats();
5900     }
5901 #endif
5902     return;
5903   }
5904 
5905   {
5906     nsAutoOOFFrameList oofs(this);
5907     if (oofs.mList.ContinueRemoveFrame(aFloat)) {
5908       return;
5909     }
5910   }
5911 }
5912 
DoRemoveOutOfFlowFrame(nsIFrame * aFrame)5913 void nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame) {
5914   // The containing block is always the parent of aFrame.
5915   nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
5916 
5917   // Remove aFrame from the appropriate list.
5918   if (aFrame->IsAbsolutelyPositioned()) {
5919     // This also deletes the next-in-flows
5920     block->GetAbsoluteContainingBlock()->RemoveFrame(block, kAbsoluteList,
5921                                                      aFrame);
5922   } else {
5923     // First remove aFrame's next-in-flows.
5924     nsIFrame* nif = aFrame->GetNextInFlow();
5925     if (nif) {
5926       nif->GetParent()->DeleteNextInFlowChild(nif, false);
5927     }
5928     // Now remove aFrame from its child list and Destroy it.
5929     block->RemoveFloatFromFloatCache(aFrame);
5930     block->RemoveFloat(aFrame);
5931     aFrame->Destroy();
5932   }
5933 }
5934 
5935 /**
5936  * This helps us iterate over the list of all normal + overflow lines
5937  */
TryAllLines(nsLineList::iterator * aIterator,nsLineList::iterator * aStartIterator,nsLineList::iterator * aEndIterator,bool * aInOverflowLines,FrameLines ** aOverflowLines)5938 void nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
5939                                nsLineList::iterator* aStartIterator,
5940                                nsLineList::iterator* aEndIterator,
5941                                bool* aInOverflowLines,
5942                                FrameLines** aOverflowLines) {
5943   if (*aIterator == *aEndIterator) {
5944     if (!*aInOverflowLines) {
5945       // Try the overflow lines
5946       *aInOverflowLines = true;
5947       FrameLines* lines = GetOverflowLines();
5948       if (lines) {
5949         *aStartIterator = lines->mLines.begin();
5950         *aIterator = *aStartIterator;
5951         *aEndIterator = lines->mLines.end();
5952         *aOverflowLines = lines;
5953       }
5954     }
5955   }
5956 }
5957 
nsBlockInFlowLineIterator(nsBlockFrame * aFrame,LineIterator aLine)5958 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5959                                                      LineIterator aLine)
5960     : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines) {
5961   // This will assert if aLine isn't in mLines of aFrame:
5962   DebugOnly<bool> check = aLine == mFrame->LinesBegin();
5963 }
5964 
nsBlockInFlowLineIterator(nsBlockFrame * aFrame,LineIterator aLine,bool aInOverflow)5965 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5966                                                      LineIterator aLine,
5967                                                      bool aInOverflow)
5968     : mFrame(aFrame),
5969       mLine(aLine),
5970       mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
5971                             : &aFrame->mLines) {}
5972 
nsBlockInFlowLineIterator(nsBlockFrame * aFrame,bool * aFoundValidLine)5973 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5974                                                      bool* aFoundValidLine)
5975     : mFrame(aFrame), mLineList(&aFrame->mLines) {
5976   mLine = aFrame->LinesBegin();
5977   *aFoundValidLine = FindValidLine();
5978 }
5979 
UpdateFirstLetterStyle(ServoRestyleState & aRestyleState)5980 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState& aRestyleState) {
5981   nsIFrame* letterFrame = GetFirstLetter();
5982   if (!letterFrame) {
5983     return;
5984   }
5985 
5986   // Figure out what the right style parent is.  This needs to match
5987   // nsCSSFrameConstructor::CreateLetterFrame.
5988   nsIFrame* inFlowFrame = letterFrame;
5989   if (inFlowFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
5990     inFlowFrame = inFlowFrame->GetPlaceholderFrame();
5991   }
5992   nsIFrame* styleParent = CorrectStyleParentFrame(inFlowFrame->GetParent(),
5993                                                   PseudoStyleType::firstLetter);
5994   ComputedStyle* parentStyle = styleParent->Style();
5995   RefPtr<ComputedStyle> firstLetterStyle =
5996       aRestyleState.StyleSet().ResolvePseudoElementStyle(
5997           *mContent->AsElement(), PseudoStyleType::firstLetter, parentStyle);
5998   // Note that we don't need to worry about changehints for the continuation
5999   // styles: those will be handled by the styleParent already.
6000   RefPtr<ComputedStyle> continuationStyle =
6001       aRestyleState.StyleSet().ResolveStyleForFirstLetterContinuation(
6002           parentStyle);
6003   UpdateStyleOfOwnedChildFrame(letterFrame, firstLetterStyle, aRestyleState,
6004                                Some(continuationStyle.get()));
6005 
6006   // We also want to update the style on the textframe inside the first-letter.
6007   // We don't need to compute a changehint for this, though, since any changes
6008   // to it are handled by the first-letter anyway.
6009   nsIFrame* textFrame = letterFrame->PrincipalChildList().FirstChild();
6010   RefPtr<ComputedStyle> firstTextStyle =
6011       aRestyleState.StyleSet().ResolveStyleForText(textFrame->GetContent(),
6012                                                    firstLetterStyle);
6013   textFrame->SetComputedStyle(firstTextStyle);
6014 
6015   // We don't need to update style for textFrame's continuations: it's already
6016   // set up to inherit from parentStyle, which is what we want.
6017 }
6018 
FindChildContaining(nsBlockFrame * aFrame,nsIFrame * aFindFrame)6019 static nsIFrame* FindChildContaining(nsBlockFrame* aFrame,
6020                                      nsIFrame* aFindFrame) {
6021   NS_ASSERTION(aFrame, "must have frame");
6022   nsIFrame* child;
6023   while (true) {
6024     nsIFrame* block = aFrame;
6025     do {
6026       child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
6027       if (child) break;
6028       block = block->GetNextContinuation();
6029     } while (block);
6030     if (!child) return nullptr;
6031     if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
6032       break;
6033     }
6034     aFindFrame = child->GetPlaceholderFrame();
6035   }
6036 
6037   return child;
6038 }
6039 
nsBlockInFlowLineIterator(nsBlockFrame * aFrame,nsIFrame * aFindFrame,bool * aFoundValidLine)6040 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
6041                                                      nsIFrame* aFindFrame,
6042                                                      bool* aFoundValidLine)
6043     : mFrame(aFrame), mLineList(&aFrame->mLines) {
6044   *aFoundValidLine = false;
6045 
6046   nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
6047   if (!child) return;
6048 
6049   LineIterator line_end = aFrame->LinesEnd();
6050   mLine = aFrame->LinesBegin();
6051   if (mLine != line_end && mLine.next() == line_end &&
6052       !aFrame->HasOverflowLines()) {
6053     // The block has a single line - that must be it!
6054     *aFoundValidLine = true;
6055     return;
6056   }
6057 
6058   // Try to use the cursor if it exists, otherwise fall back to the first line
6059   if (nsLineBox* const cursor = aFrame->GetLineCursor()) {
6060     mLine = line_end;
6061     // Perform a simultaneous forward and reverse search starting from the
6062     // line cursor.
6063     nsBlockFrame::LineIterator line = aFrame->LinesBeginFrom(cursor);
6064     nsBlockFrame::ReverseLineIterator rline = aFrame->LinesRBeginFrom(cursor);
6065     nsBlockFrame::ReverseLineIterator rline_end = aFrame->LinesREnd();
6066     // rline is positioned on the line containing 'cursor', so it's not
6067     // rline_end. So we can safely increment it (i.e. move it to one line
6068     // earlier) to start searching there.
6069     ++rline;
6070     while (line != line_end || rline != rline_end) {
6071       if (line != line_end) {
6072         if (line->Contains(child)) {
6073           mLine = line;
6074           break;
6075         }
6076         ++line;
6077       }
6078       if (rline != rline_end) {
6079         if (rline->Contains(child)) {
6080           mLine = rline;
6081           break;
6082         }
6083         ++rline;
6084       }
6085     }
6086     if (mLine != line_end) {
6087       *aFoundValidLine = true;
6088       if (mLine != cursor) {
6089         aFrame->SetProperty(nsBlockFrame::LineCursorProperty(), mLine);
6090       }
6091       return;
6092     }
6093   } else {
6094     for (mLine = aFrame->LinesBegin(); mLine != line_end; ++mLine) {
6095       if (mLine->Contains(child)) {
6096         *aFoundValidLine = true;
6097         return;
6098       }
6099     }
6100   }
6101   // Didn't find the line
6102   MOZ_ASSERT(mLine == line_end, "mLine should be line_end at this point");
6103 
6104   // If we reach here, it means that we have not been able to find the
6105   // desired frame in our in-flow lines.  So we should start looking at
6106   // our overflow lines. In order to do that, we set mLine to the end
6107   // iterator so that FindValidLine starts to look at overflow lines,
6108   // if any.
6109 
6110   if (!FindValidLine()) return;
6111 
6112   do {
6113     if (mLine->Contains(child)) {
6114       *aFoundValidLine = true;
6115       return;
6116     }
6117   } while (Next());
6118 }
6119 
End()6120 nsBlockFrame::LineIterator nsBlockInFlowLineIterator::End() {
6121   return mLineList->end();
6122 }
6123 
IsLastLineInList()6124 bool nsBlockInFlowLineIterator::IsLastLineInList() {
6125   LineIterator end = End();
6126   return mLine != end && mLine.next() == end;
6127 }
6128 
Next()6129 bool nsBlockInFlowLineIterator::Next() {
6130   ++mLine;
6131   return FindValidLine();
6132 }
6133 
Prev()6134 bool nsBlockInFlowLineIterator::Prev() {
6135   LineIterator begin = mLineList->begin();
6136   if (mLine != begin) {
6137     --mLine;
6138     return true;
6139   }
6140   bool currentlyInOverflowLines = GetInOverflow();
6141   while (true) {
6142     if (currentlyInOverflowLines) {
6143       mLineList = &mFrame->mLines;
6144       mLine = mLineList->end();
6145       if (mLine != mLineList->begin()) {
6146         --mLine;
6147         return true;
6148       }
6149     } else {
6150       mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
6151       if (!mFrame) return false;
6152       nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
6153       if (overflowLines) {
6154         mLineList = &overflowLines->mLines;
6155         mLine = mLineList->end();
6156         NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
6157         --mLine;
6158         return true;
6159       }
6160     }
6161     currentlyInOverflowLines = !currentlyInOverflowLines;
6162   }
6163 }
6164 
FindValidLine()6165 bool nsBlockInFlowLineIterator::FindValidLine() {
6166   LineIterator end = mLineList->end();
6167   if (mLine != end) return true;
6168   bool currentlyInOverflowLines = GetInOverflow();
6169   while (true) {
6170     if (currentlyInOverflowLines) {
6171       mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
6172       if (!mFrame) return false;
6173       mLineList = &mFrame->mLines;
6174       mLine = mLineList->begin();
6175       if (mLine != mLineList->end()) return true;
6176     } else {
6177       nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
6178       if (overflowLines) {
6179         mLineList = &overflowLines->mLines;
6180         mLine = mLineList->begin();
6181         NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
6182         return true;
6183       }
6184     }
6185     currentlyInOverflowLines = !currentlyInOverflowLines;
6186   }
6187 }
6188 
6189 // This function removes aDeletedFrame and all its continuations.  It
6190 // is optimized for deleting a whole series of frames. The easy
6191 // implementation would invoke itself recursively on
6192 // aDeletedFrame->GetNextContinuation, then locate the line containing
6193 // aDeletedFrame and remove aDeletedFrame from that line. But here we
6194 // start by locating aDeletedFrame and then scanning from that point
6195 // on looking for continuations.
DoRemoveFrameInternal(nsIFrame * aDeletedFrame,uint32_t aFlags,PostDestroyData & aPostDestroyData)6196 void nsBlockFrame::DoRemoveFrameInternal(nsIFrame* aDeletedFrame,
6197                                          uint32_t aFlags,
6198                                          PostDestroyData& aPostDestroyData) {
6199   // Clear our line cursor, since our lines may change.
6200   ClearLineCursor();
6201 
6202   if (aDeletedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW |
6203                                      NS_FRAME_IS_OVERFLOW_CONTAINER)) {
6204     if (!aDeletedFrame->GetPrevInFlow()) {
6205       NS_ASSERTION(aDeletedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
6206                    "Expected out-of-flow frame");
6207       DoRemoveOutOfFlowFrame(aDeletedFrame);
6208     } else {
6209       nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame,
6210                                               (aFlags & FRAMES_ARE_EMPTY) != 0);
6211     }
6212     return;
6213   }
6214 
6215   // Find the line that contains deletedFrame
6216   nsLineList::iterator line_start = mLines.begin(), line_end = mLines.end();
6217   nsLineList::iterator line = line_start;
6218   FrameLines* overflowLines = nullptr;
6219   bool searchingOverflowList = false;
6220   // Make sure we look in the overflow lines even if the normal line
6221   // list is empty
6222   TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
6223               &overflowLines);
6224   while (line != line_end) {
6225     if (line->Contains(aDeletedFrame)) {
6226       break;
6227     }
6228     ++line;
6229     TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
6230                 &overflowLines);
6231   }
6232 
6233   if (line == line_end) {
6234     NS_ERROR("can't find deleted frame in lines");
6235     return;
6236   }
6237 
6238   if (!(aFlags & FRAMES_ARE_EMPTY)) {
6239     if (line != line_start) {
6240       line.prev()->MarkDirty();
6241       line.prev()->SetInvalidateTextRuns(true);
6242     } else if (searchingOverflowList && !mLines.empty()) {
6243       mLines.back()->MarkDirty();
6244       mLines.back()->SetInvalidateTextRuns(true);
6245     }
6246   }
6247 
6248   while (line != line_end && aDeletedFrame) {
6249     MOZ_ASSERT(this == aDeletedFrame->GetParent(), "messed up delete code");
6250     MOZ_ASSERT(line->Contains(aDeletedFrame), "frame not in line");
6251 
6252     if (!(aFlags & FRAMES_ARE_EMPTY)) {
6253       line->MarkDirty();
6254       line->SetInvalidateTextRuns(true);
6255     }
6256 
6257     // If the frame being deleted is the last one on the line then
6258     // optimize away the line->Contains(next-in-flow) call below.
6259     bool isLastFrameOnLine = 1 == line->GetChildCount();
6260     if (!isLastFrameOnLine) {
6261       LineIterator next = line.next();
6262       nsIFrame* lastFrame =
6263           next != line_end
6264               ? next->mFirstChild->GetPrevSibling()
6265               : (searchingOverflowList ? overflowLines->mFrames.LastChild()
6266                                        : mFrames.LastChild());
6267       NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
6268                    "unexpected line frames");
6269       isLastFrameOnLine = lastFrame == aDeletedFrame;
6270     }
6271 
6272     // Remove aDeletedFrame from the line
6273     if (line->mFirstChild == aDeletedFrame) {
6274       // We should be setting this to null if aDeletedFrame
6275       // is the only frame on the line. HOWEVER in that case
6276       // we will be removing the line anyway, see below.
6277       line->mFirstChild = aDeletedFrame->GetNextSibling();
6278     }
6279 
6280     // Hmm, this won't do anything if we're removing a frame in the first
6281     // overflow line... Hopefully doesn't matter
6282     --line;
6283     if (line != line_end && !line->IsBlock()) {
6284       // Since we just removed a frame that follows some inline
6285       // frames, we need to reflow the previous line.
6286       line->MarkDirty();
6287     }
6288     ++line;
6289 
6290     // Take aDeletedFrame out of the sibling list. Note that
6291     // prevSibling will only be nullptr when we are deleting the very
6292     // first frame in the main or overflow list.
6293     if (searchingOverflowList) {
6294       overflowLines->mFrames.RemoveFrame(aDeletedFrame);
6295     } else {
6296       mFrames.RemoveFrame(aDeletedFrame);
6297     }
6298 
6299     // Update the child count of the line to be accurate
6300     line->NoteFrameRemoved(aDeletedFrame);
6301 
6302     // Destroy frame; capture its next continuation first in case we need
6303     // to destroy that too.
6304     nsIFrame* deletedNextContinuation =
6305         (aFlags & REMOVE_FIXED_CONTINUATIONS)
6306             ? aDeletedFrame->GetNextContinuation()
6307             : aDeletedFrame->GetNextInFlow();
6308 #ifdef NOISY_REMOVE_FRAME
6309     printf("DoRemoveFrame: %s line=%p frame=",
6310            searchingOverflowList ? "overflow" : "normal", line.get());
6311     aDeletedFrame->ListTag(stdout);
6312     printf(" prevSibling=%p deletedNextContinuation=%p\n",
6313            aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
6314 #endif
6315 
6316     // If next-in-flow is an overflow container, must remove it first.
6317     if (deletedNextContinuation && deletedNextContinuation->HasAnyStateBits(
6318                                        NS_FRAME_IS_OVERFLOW_CONTAINER)) {
6319       deletedNextContinuation->GetParent()->DeleteNextInFlowChild(
6320           deletedNextContinuation, false);
6321       deletedNextContinuation = nullptr;
6322     }
6323 
6324     aDeletedFrame->DestroyFrom(aDeletedFrame, aPostDestroyData);
6325     aDeletedFrame = deletedNextContinuation;
6326 
6327     bool haveAdvancedToNextLine = false;
6328     // If line is empty, remove it now.
6329     if (0 == line->GetChildCount()) {
6330 #ifdef NOISY_REMOVE_FRAME
6331       printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
6332              searchingOverflowList ? "overflow" : "normal", line.get());
6333 #endif
6334       nsLineBox* cur = line;
6335       if (!searchingOverflowList) {
6336         line = mLines.erase(line);
6337         // Invalidate the space taken up by the line.
6338         // XXX We need to do this if we're removing a frame as a result of
6339         // a call to RemoveFrame(), but we may not need to do this in all
6340         // cases...
6341 #ifdef NOISY_BLOCK_INVALIDATE
6342         nsRect inkOverflow(cur->InkOverflowRect());
6343         printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow.x,
6344                inkOverflow.y, inkOverflow.width, inkOverflow.height);
6345 #endif
6346       } else {
6347         line = overflowLines->mLines.erase(line);
6348         if (overflowLines->mLines.empty()) {
6349           DestroyOverflowLines();
6350           overflowLines = nullptr;
6351           // We just invalidated our iterators.  Since we were in
6352           // the overflow lines list, which is now empty, set them
6353           // so we're at the end of the regular line list.
6354           line_start = mLines.begin();
6355           line_end = mLines.end();
6356           line = line_end;
6357         }
6358       }
6359       FreeLineBox(cur);
6360 
6361       // If we're removing a line, ReflowDirtyLines isn't going to
6362       // know that it needs to slide lines unless something is marked
6363       // dirty.  So mark the previous margin of the next line dirty if
6364       // there is one.
6365       if (line != line_end) {
6366         line->MarkPreviousMarginDirty();
6367       }
6368       haveAdvancedToNextLine = true;
6369     } else {
6370       // Make the line that just lost a frame dirty, and advance to
6371       // the next line.
6372       if (!deletedNextContinuation || isLastFrameOnLine ||
6373           !line->Contains(deletedNextContinuation)) {
6374         line->MarkDirty();
6375         ++line;
6376         haveAdvancedToNextLine = true;
6377       }
6378     }
6379 
6380     if (deletedNextContinuation) {
6381       // See if we should keep looking in the current flow's line list.
6382       if (deletedNextContinuation->GetParent() != this) {
6383         // The deceased frames continuation is not a child of the
6384         // current block. So break out of the loop so that we advance
6385         // to the next parent.
6386         //
6387         // If we have a continuation in a different block then all bets are
6388         // off regarding whether we are deleting frames without actual content,
6389         // so don't propagate FRAMES_ARE_EMPTY any further.
6390         aFlags &= ~FRAMES_ARE_EMPTY;
6391         break;
6392       }
6393 
6394       // If we advanced to the next line then check if we should switch to the
6395       // overflow line list.
6396       if (haveAdvancedToNextLine) {
6397         if (line != line_end && !searchingOverflowList &&
6398             !line->Contains(deletedNextContinuation)) {
6399           // We have advanced to the next *normal* line but the next-in-flow
6400           // is not there - force a switch to the overflow line list.
6401           line = line_end;
6402         }
6403 
6404         TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
6405                     &overflowLines);
6406 #ifdef NOISY_REMOVE_FRAME
6407         printf("DoRemoveFrame: now on %s line=%p\n",
6408                searchingOverflowList ? "overflow" : "normal", line.get());
6409 #endif
6410       }
6411     }
6412   }
6413 
6414   if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
6415     line.next()->MarkDirty();
6416     line.next()->SetInvalidateTextRuns(true);
6417   }
6418 
6419 #ifdef DEBUG
6420   VerifyLines(true);
6421   VerifyOverflowSituation();
6422 #endif
6423 
6424   // Advance to next flow block if the frame has more continuations.
6425   if (!aDeletedFrame) {
6426     return;
6427   }
6428   nsBlockFrame* nextBlock = do_QueryFrame(aDeletedFrame->GetParent());
6429   NS_ASSERTION(nextBlock, "Our child's continuation's parent is not a block?");
6430   uint32_t flags = (aFlags & REMOVE_FIXED_CONTINUATIONS);
6431   nextBlock->DoRemoveFrameInternal(aDeletedFrame, flags, aPostDestroyData);
6432 }
6433 
FindBlockLineFor(nsIFrame * aChild,nsLineList::iterator aBegin,nsLineList::iterator aEnd,nsLineList::iterator * aResult)6434 static bool FindBlockLineFor(nsIFrame* aChild, nsLineList::iterator aBegin,
6435                              nsLineList::iterator aEnd,
6436                              nsLineList::iterator* aResult) {
6437   MOZ_ASSERT(aChild->IsBlockOutside());
6438   for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
6439     MOZ_ASSERT(line->GetChildCount() > 0);
6440     if (line->IsBlock() && line->mFirstChild == aChild) {
6441       MOZ_ASSERT(line->GetChildCount() == 1);
6442       *aResult = line;
6443       return true;
6444     }
6445   }
6446   return false;
6447 }
6448 
FindInlineLineFor(nsIFrame * aChild,const nsFrameList & aFrameList,nsLineList::iterator aBegin,nsLineList::iterator aEnd,nsLineList::iterator * aResult)6449 static bool FindInlineLineFor(nsIFrame* aChild, const nsFrameList& aFrameList,
6450                               nsLineList::iterator aBegin,
6451                               nsLineList::iterator aEnd,
6452                               nsLineList::iterator* aResult) {
6453   MOZ_ASSERT(!aChild->IsBlockOutside());
6454   for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
6455     MOZ_ASSERT(line->GetChildCount() > 0);
6456     if (!line->IsBlock()) {
6457       // Optimize by comparing the line's last child first.
6458       nsLineList::iterator next = line.next();
6459       if (aChild == (next == aEnd ? aFrameList.LastChild()
6460                                   : next->mFirstChild->GetPrevSibling()) ||
6461           line->Contains(aChild)) {
6462         *aResult = line;
6463         return true;
6464       }
6465     }
6466   }
6467   return false;
6468 }
6469 
FindLineFor(nsIFrame * aChild,const nsFrameList & aFrameList,nsLineList::iterator aBegin,nsLineList::iterator aEnd,nsLineList::iterator * aResult)6470 static bool FindLineFor(nsIFrame* aChild, const nsFrameList& aFrameList,
6471                         nsLineList::iterator aBegin, nsLineList::iterator aEnd,
6472                         nsLineList::iterator* aResult) {
6473   return aChild->IsBlockOutside()
6474              ? FindBlockLineFor(aChild, aBegin, aEnd, aResult)
6475              : FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
6476 }
6477 
StealFrame(nsIFrame * aChild)6478 void nsBlockFrame::StealFrame(nsIFrame* aChild) {
6479   MOZ_ASSERT(aChild->GetParent() == this);
6480 
6481   if (aChild->IsFloating()) {
6482     RemoveFloat(aChild);
6483     return;
6484   }
6485 
6486   if (MaybeStealOverflowContainerFrame(aChild)) {
6487     return;
6488   }
6489 
6490   MOZ_ASSERT(!aChild->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
6491 
6492   nsLineList::iterator line;
6493   if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
6494     RemoveFrameFromLine(aChild, line, mFrames, mLines);
6495   } else {
6496     FrameLines* overflowLines = GetOverflowLines();
6497     DebugOnly<bool> found;
6498     found = FindLineFor(aChild, overflowLines->mFrames,
6499                         overflowLines->mLines.begin(),
6500                         overflowLines->mLines.end(), &line);
6501     MOZ_ASSERT(found, "Why can't we find aChild in our overflow lines?");
6502     RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
6503                         overflowLines->mLines);
6504     if (overflowLines->mLines.empty()) {
6505       DestroyOverflowLines();
6506     }
6507   }
6508 }
6509 
RemoveFrameFromLine(nsIFrame * aChild,nsLineList::iterator aLine,nsFrameList & aFrameList,nsLineList & aLineList)6510 void nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild,
6511                                        nsLineList::iterator aLine,
6512                                        nsFrameList& aFrameList,
6513                                        nsLineList& aLineList) {
6514   aFrameList.RemoveFrame(aChild);
6515   if (aChild == aLine->mFirstChild) {
6516     aLine->mFirstChild = aChild->GetNextSibling();
6517   }
6518   aLine->NoteFrameRemoved(aChild);
6519   if (aLine->GetChildCount() > 0) {
6520     aLine->MarkDirty();
6521   } else {
6522     // The line became empty - destroy it.
6523     nsLineBox* lineBox = aLine;
6524     aLine = aLineList.erase(aLine);
6525     if (aLine != aLineList.end()) {
6526       aLine->MarkPreviousMarginDirty();
6527     }
6528     FreeLineBox(lineBox);
6529   }
6530 }
6531 
DeleteNextInFlowChild(nsIFrame * aNextInFlow,bool aDeletingEmptyFrames)6532 void nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
6533                                          bool aDeletingEmptyFrames) {
6534   MOZ_ASSERT(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
6535 
6536   if (aNextInFlow->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW |
6537                                    NS_FRAME_IS_OVERFLOW_CONTAINER)) {
6538     nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames);
6539   } else {
6540 #ifdef DEBUG
6541     if (aDeletingEmptyFrames) {
6542       nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
6543     }
6544 #endif
6545     DoRemoveFrame(aNextInFlow, aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
6546   }
6547 }
6548 
StyleTextForLineLayout()6549 const nsStyleText* nsBlockFrame::StyleTextForLineLayout() {
6550   // Return the pointer to an unmodified style text
6551   return StyleText();
6552 }
6553 
6554 ////////////////////////////////////////////////////////////////////////
6555 // Float support
6556 
AdjustFloatAvailableSpace(BlockReflowState & aState,const LogicalRect & aFloatAvailableSpace)6557 LogicalRect nsBlockFrame::AdjustFloatAvailableSpace(
6558     BlockReflowState& aState, const LogicalRect& aFloatAvailableSpace) {
6559   WritingMode wm = aState.mReflowInput.GetWritingMode();
6560 
6561   nscoord availBSize = NS_UNCONSTRAINEDSIZE == aState.ContentBSize()
6562                            ? NS_UNCONSTRAINEDSIZE
6563                            : std::max(0, aState.ContentBEnd() - aState.mBCoord);
6564 
6565   return LogicalRect(wm, aState.ContentIStart(), aState.ContentBStart(),
6566                      aState.ContentISize(), availBSize);
6567 }
6568 
ComputeFloatISize(BlockReflowState & aState,const LogicalRect & aFloatAvailableSpace,nsIFrame * aFloat)6569 nscoord nsBlockFrame::ComputeFloatISize(BlockReflowState& aState,
6570                                         const LogicalRect& aFloatAvailableSpace,
6571                                         nsIFrame* aFloat) {
6572   MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
6573              "aFloat must be an out-of-flow frame");
6574 
6575   // Reflow the float.
6576   LogicalRect availSpace =
6577       AdjustFloatAvailableSpace(aState, aFloatAvailableSpace);
6578 
6579   WritingMode blockWM = aState.mReflowInput.GetWritingMode();
6580   WritingMode floatWM = aFloat->GetWritingMode();
6581   ReflowInput floatRS(aState.mPresContext, aState.mReflowInput, aFloat,
6582                       availSpace.Size(blockWM).ConvertTo(floatWM, blockWM));
6583 
6584   return floatRS.ComputedSizeWithMarginBorderPadding(blockWM).ISize(blockWM);
6585 }
6586 
ReflowFloat(BlockReflowState & aState,const LogicalRect & aAdjustedAvailableSpace,nsIFrame * aFloat,LogicalMargin & aFloatMargin,LogicalMargin & aFloatOffsets,bool aFloatPushedDown,nsReflowStatus & aReflowStatus)6587 void nsBlockFrame::ReflowFloat(BlockReflowState& aState,
6588                                const LogicalRect& aAdjustedAvailableSpace,
6589                                nsIFrame* aFloat, LogicalMargin& aFloatMargin,
6590                                LogicalMargin& aFloatOffsets,
6591                                bool aFloatPushedDown,
6592                                nsReflowStatus& aReflowStatus) {
6593   MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
6594              "aFloat must be an out-of-flow frame");
6595 
6596   // Reflow the float.
6597   aReflowStatus.Reset();
6598 
6599   WritingMode wm = aState.mReflowInput.GetWritingMode();
6600 #ifdef NOISY_FLOAT
6601   printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n", aFloat,
6602          this, aAdjustedAvailableSpace.IStart(wm),
6603          aAdjustedAvailableSpace.BStart(wm), aAdjustedAvailableSpace.ISize(wm),
6604          aAdjustedAvailableSpace.BSize(wm));
6605 #endif
6606 
6607   ReflowInput floatRS(
6608       aState.mPresContext, aState.mReflowInput, aFloat,
6609       aAdjustedAvailableSpace.Size(wm).ConvertTo(aFloat->GetWritingMode(), wm));
6610 
6611   // Normally the mIsTopOfPage state is copied from the parent reflow
6612   // input.  However, when reflowing a float, if we've placed other
6613   // floats that force this float *down* or *narrower*, we should unset
6614   // the mIsTopOfPage state.
6615   // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
6616   // variable below, which has the exact same effect.  Perhaps it should
6617   // be merged into that, except that the test for narrowing here is not
6618   // about adjacency with the top, so it seems misleading.
6619   if (floatRS.mFlags.mIsTopOfPage &&
6620       (aFloatPushedDown ||
6621        aAdjustedAvailableSpace.ISize(wm) != aState.ContentISize())) {
6622     floatRS.mFlags.mIsTopOfPage = false;
6623   }
6624 
6625   // Setup a block reflow context to reflow the float.
6626   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
6627 
6628   // Reflow the float
6629   bool isAdjacentWithTop = aState.IsAdjacentWithTop();
6630 
6631   nsIFrame* clearanceFrame = nullptr;
6632   do {
6633     nsCollapsingMargin margin;
6634     bool mayNeedRetry = false;
6635     floatRS.mDiscoveredClearance = nullptr;
6636     // Only first in flow gets a block-start margin.
6637     if (!aFloat->GetPrevInFlow()) {
6638       brc.ComputeCollapsedBStartMargin(floatRS, &margin, clearanceFrame,
6639                                        &mayNeedRetry);
6640 
6641       if (mayNeedRetry && !clearanceFrame) {
6642         floatRS.mDiscoveredClearance = &clearanceFrame;
6643         // We don't need to push the float manager state because the the block
6644         // has its own float manager that will be destroyed and recreated
6645       }
6646     }
6647 
6648     brc.ReflowBlock(aAdjustedAvailableSpace, true, margin, 0, isAdjacentWithTop,
6649                     nullptr, floatRS, aReflowStatus, aState);
6650   } while (clearanceFrame);
6651 
6652   if (!aReflowStatus.IsFullyComplete() && ShouldAvoidBreakInside(floatRS)) {
6653     aReflowStatus.SetInlineLineBreakBeforeAndReset();
6654   } else if (aReflowStatus.IsIncomplete() &&
6655              (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.BSize(wm))) {
6656     // An incomplete reflow status means we should split the float
6657     // if the height is constrained (bug 145305).
6658     aReflowStatus.Reset();
6659   }
6660 
6661   if (aReflowStatus.NextInFlowNeedsReflow()) {
6662     aState.mReflowStatus.SetNextInFlowNeedsReflow();
6663   }
6664 
6665   if (aFloat->IsLetterFrame()) {
6666     // We never split floating first letters; an incomplete state for
6667     // such frames simply means that there is more content to be
6668     // reflowed on the line.
6669     if (aReflowStatus.IsIncomplete()) aReflowStatus.Reset();
6670   }
6671 
6672   // Capture the margin and offsets information for the caller
6673   aFloatMargin =
6674       // float margins don't collapse
6675       floatRS.ComputedLogicalMargin(wm);
6676   aFloatOffsets = floatRS.ComputedLogicalOffsets(wm);
6677 
6678   const ReflowOutput& metrics = brc.GetMetrics();
6679 
6680   // Set the rect, make sure the view is properly sized and positioned,
6681   // and tell the frame we're done reflowing it
6682   // XXXldb This seems like the wrong place to be doing this -- shouldn't
6683   // we be doing this in BlockReflowState::FlowAndPlaceFloat after
6684   // we've positioned the float, and shouldn't we be doing the equivalent
6685   // of |PlaceFrameView| here?
6686   WritingMode metricsWM = metrics.GetWritingMode();
6687   aFloat->SetSize(metricsWM, metrics.Size(metricsWM));
6688   if (aFloat->HasView()) {
6689     nsContainerFrame::SyncFrameViewAfterReflow(
6690         aState.mPresContext, aFloat, aFloat->GetView(), metrics.InkOverflow(),
6691         ReflowChildFlags::NoMoveView);
6692   }
6693   // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same
6694   // hierarchy)
6695   aFloat->DidReflow(aState.mPresContext, &floatRS);
6696 
6697 #ifdef NOISY_FLOAT
6698   printf("end ReflowFloat %p, sized to %d,%d\n", aFloat, metrics.Width(),
6699          metrics.Height());
6700 #endif
6701 }
6702 
FindTrailingClear()6703 StyleClear nsBlockFrame::FindTrailingClear() {
6704   // find the break type of the last line
6705   for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
6706     nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
6707     LineIterator endLine = block->LinesEnd();
6708     if (endLine != block->LinesBegin()) {
6709       --endLine;
6710       return endLine->GetBreakTypeAfter();
6711     }
6712   }
6713   return StyleClear::None;
6714 }
6715 
ReflowPushedFloats(BlockReflowState & aState,OverflowAreas & aOverflowAreas)6716 void nsBlockFrame::ReflowPushedFloats(BlockReflowState& aState,
6717                                       OverflowAreas& aOverflowAreas) {
6718   // Pushed floats live at the start of our float list; see comment
6719   // above nsBlockFrame::DrainPushedFloats.
6720   nsIFrame* f = mFloats.FirstChild();
6721   nsIFrame* prev = nullptr;
6722   while (f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
6723     MOZ_ASSERT(prev == f->GetPrevSibling());
6724     // When we push a first-continuation float in a non-initial reflow,
6725     // it's possible that we end up with two continuations with the same
6726     // parent.  This happens if, on the previous reflow of the block or
6727     // a previous reflow of the line containing the block, the float was
6728     // split between continuations A and B of the parent, but on the
6729     // current reflow, none of the float can fit in A.
6730     //
6731     // When this happens, we might even have the two continuations
6732     // out-of-order due to the management of the pushed floats.  In
6733     // particular, if the float's placeholder was in a pushed line that
6734     // we reflowed before it was pushed, and we split the float during
6735     // that reflow, we might have the continuation of the float before
6736     // the float itself.  (In the general case, however, it's correct
6737     // for floats in the pushed floats list to come before floats
6738     // anchored in pushed lines; however, in this case it's wrong.  We
6739     // should probably find a way to fix it somehow, since it leads to
6740     // incorrect layout in some cases.)
6741     //
6742     // When we have these out-of-order continuations, we might hit the
6743     // next-continuation before the previous-continuation.  When that
6744     // happens, just push it.  When we reflow the next continuation,
6745     // we'll either pull all of its content back and destroy it (by
6746     // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
6747     // pull it out of its current position and push it again (and
6748     // potentially repeat this cycle for the next continuation, although
6749     // hopefully then they'll be in the right order).
6750     //
6751     // We should also need this code for the in-order case if the first
6752     // continuation of a float gets moved across more than one
6753     // continuation of the containing block.  In this case we'd manage
6754     // to push the second continuation without this check, but not the
6755     // third and later.
6756     nsIFrame* prevContinuation = f->GetPrevContinuation();
6757     if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
6758       mFloats.RemoveFrame(f);
6759       aState.AppendPushedFloatChain(f);
6760       f = !prev ? mFloats.FirstChild() : prev->GetNextSibling();
6761       continue;
6762     }
6763 
6764     // Always call FlowAndPlaceFloat; we might need to place this float if it
6765     // didn't belong to this block the last time it was reflowed.  Note that if
6766     // the float doesn't get placed, we don't consider its overflow areas.
6767     // (Not-getting-placed means it didn't fit and we pushed it instead of
6768     // placing it, and its position could be stale.)
6769     if (aState.FlowAndPlaceFloat(f)) {
6770       ConsiderChildOverflow(aOverflowAreas, f);
6771     }
6772 
6773     nsIFrame* next = !prev ? mFloats.FirstChild() : prev->GetNextSibling();
6774     if (next == f) {
6775       // We didn't push |f| so its next-sibling is next.
6776       next = f->GetNextSibling();
6777       prev = f;
6778     }  // else: we did push |f| so |prev|'s new next-sibling is next.
6779     f = next;
6780   }
6781 
6782   // If there are pushed or split floats, then we may need to continue BR
6783   // clearance
6784   if (auto [bCoord, result] = aState.ClearFloats(0, StyleClear::Both);
6785       result != ClearFloatsResult::BCoordNoChange) {
6786     Unused << bCoord;
6787     nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
6788     if (prevBlock) {
6789       aState.mFloatBreakType = prevBlock->FindTrailingClear();
6790     }
6791   }
6792 }
6793 
RecoverFloats(nsFloatManager & aFloatManager,WritingMode aWM,const nsSize & aContainerSize)6794 void nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM,
6795                                  const nsSize& aContainerSize) {
6796   // Recover our own floats
6797   nsIFrame* stop = nullptr;  // Stop before we reach pushed floats that
6798                              // belong to our next-in-flow
6799   for (nsIFrame* f = mFloats.FirstChild(); f && f != stop;
6800        f = f->GetNextSibling()) {
6801     LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize);
6802     aFloatManager.AddFloat(f, region, aWM, aContainerSize);
6803     if (!stop && f->GetNextInFlow()) stop = f->GetNextInFlow();
6804   }
6805 
6806   // Recurse into our overflow container children
6807   for (nsIFrame* oc = GetChildList(kOverflowContainersList).FirstChild(); oc;
6808        oc = oc->GetNextSibling()) {
6809     RecoverFloatsFor(oc, aFloatManager, aWM, aContainerSize);
6810   }
6811 
6812   // Recurse into our normal children
6813   for (const auto& line : Lines()) {
6814     if (line.IsBlock()) {
6815       RecoverFloatsFor(line.mFirstChild, aFloatManager, aWM, aContainerSize);
6816     }
6817   }
6818 }
6819 
RecoverFloatsFor(nsIFrame * aFrame,nsFloatManager & aFloatManager,WritingMode aWM,const nsSize & aContainerSize)6820 void nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
6821                                     nsFloatManager& aFloatManager,
6822                                     WritingMode aWM,
6823                                     const nsSize& aContainerSize) {
6824   MOZ_ASSERT(aFrame, "null frame");
6825 
6826   // Only blocks have floats
6827   nsBlockFrame* block = do_QueryFrame(aFrame);
6828   // Don't recover any state inside a block that has its own float manager
6829   // (we don't currently have any blocks like this, though, thanks to our
6830   // use of extra frames for 'overflow')
6831   if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
6832     // If the element is relatively positioned, then adjust x and y
6833     // accordingly so that we consider relatively positioned frames
6834     // at their original position.
6835 
6836     LogicalRect rect(aWM, block->GetNormalRect(), aContainerSize);
6837     nscoord lineLeft = rect.LineLeft(aWM, aContainerSize);
6838     nscoord blockStart = rect.BStart(aWM);
6839     aFloatManager.Translate(lineLeft, blockStart);
6840     block->RecoverFloats(aFloatManager, aWM, aContainerSize);
6841     aFloatManager.Translate(-lineLeft, -blockStart);
6842   }
6843 }
6844 
6845 //////////////////////////////////////////////////////////////////////
6846 // Painting, event handling
6847 
6848 #ifdef DEBUG
ComputeInkOverflowArea(nsLineList & aLines,nscoord aWidth,nscoord aHeight,nsRect & aResult)6849 static void ComputeInkOverflowArea(nsLineList& aLines, nscoord aWidth,
6850                                    nscoord aHeight, nsRect& aResult) {
6851   nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
6852   for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
6853        line != line_end; ++line) {
6854     // Compute min and max x/y values for the reflowed frame's
6855     // combined areas
6856     nsRect inkOverflow(line->InkOverflowRect());
6857     nscoord x = inkOverflow.x;
6858     nscoord y = inkOverflow.y;
6859     nscoord xmost = x + inkOverflow.width;
6860     nscoord ymost = y + inkOverflow.height;
6861     if (x < xa) {
6862       xa = x;
6863     }
6864     if (xmost > xb) {
6865       xb = xmost;
6866     }
6867     if (y < ya) {
6868       ya = y;
6869     }
6870     if (ymost > yb) {
6871       yb = ymost;
6872     }
6873   }
6874 
6875   aResult.x = xa;
6876   aResult.y = ya;
6877   aResult.width = xb - xa;
6878   aResult.height = yb - ya;
6879 }
6880 #endif
6881 
6882 #ifdef DEBUG
DebugOutputDrawLine(int32_t aDepth,nsLineBox * aLine,bool aDrawn)6883 static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
6884   if (nsBlockFrame::gNoisyDamageRepair) {
6885     nsIFrame::IndentBy(stdout, aDepth + 1);
6886     nsRect lineArea = aLine->InkOverflowRect();
6887     printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6888            aDrawn ? "draw" : "skip", static_cast<void*>(aLine), aLine->IStart(),
6889            aLine->BStart(), aLine->ISize(), aLine->BSize(), lineArea.x,
6890            lineArea.y, lineArea.width, lineArea.height);
6891   }
6892 }
6893 #endif
6894 
DisplayLine(nsDisplayListBuilder * aBuilder,nsBlockFrame::LineIterator & aLine,const bool aLineInLine,const nsDisplayListSet & aLists,nsBlockFrame * aFrame,TextOverflow * aTextOverflow,uint32_t aLineNumberForTextOverflow,int32_t aDepth,int32_t & aDrawnLines)6895 static void DisplayLine(nsDisplayListBuilder* aBuilder,
6896                         nsBlockFrame::LineIterator& aLine,
6897                         const bool aLineInLine, const nsDisplayListSet& aLists,
6898                         nsBlockFrame* aFrame, TextOverflow* aTextOverflow,
6899                         uint32_t aLineNumberForTextOverflow, int32_t aDepth,
6900                         int32_t& aDrawnLines) {
6901 #ifdef DEBUG
6902   if (nsBlockFrame::gLamePaintMetrics) {
6903     aDrawnLines++;
6904   }
6905   const bool intersect =
6906       aLine->InkOverflowRect().Intersects(aBuilder->GetDirtyRect());
6907   DebugOutputDrawLine(aDepth, aLine.get(), intersect);
6908 #endif
6909 
6910   // Collect our line's display items in a temporary nsDisplayListCollection,
6911   // so that we can apply any "text-overflow" clipping to the entire collection
6912   // without affecting previous lines.
6913   nsDisplayListCollection collection(aBuilder);
6914 
6915   // Block-level child backgrounds go on the blockBorderBackgrounds list ...
6916   // Inline-level child backgrounds go on the regular child content list.
6917   nsDisplayListSet childLists(
6918       collection,
6919       aLineInLine ? collection.Content() : collection.BlockBorderBackgrounds());
6920 
6921   auto flags =
6922       aLineInLine
6923           ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline)
6924           : nsIFrame::DisplayChildFlags();
6925 
6926   nsIFrame* kid = aLine->mFirstChild;
6927   int32_t n = aLine->GetChildCount();
6928   while (--n >= 0) {
6929     aFrame->BuildDisplayListForChild(aBuilder, kid, childLists, flags);
6930     kid = kid->GetNextSibling();
6931   }
6932 
6933   if (aTextOverflow && aLineInLine) {
6934     aTextOverflow->ProcessLine(collection, aLine.get(),
6935                                aLineNumberForTextOverflow);
6936   }
6937 
6938   collection.MoveTo(aLists);
6939 }
6940 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)6941 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
6942                                     const nsDisplayListSet& aLists) {
6943   int32_t drawnLines;  // Will only be used if set (gLamePaintMetrics).
6944   int32_t depth = 0;
6945 #ifdef DEBUG
6946   if (gNoisyDamageRepair) {
6947     nsRect dirty = aBuilder->GetDirtyRect();
6948     depth = GetDepth();
6949     nsRect ca;
6950     ::ComputeInkOverflowArea(mLines, mRect.width, mRect.height, ca);
6951     nsIFrame::IndentBy(stdout, depth);
6952     ListTag(stdout);
6953     printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6954            mRect.x, mRect.y, mRect.width, mRect.height, dirty.x, dirty.y,
6955            dirty.width, dirty.height, ca.x, ca.y, ca.width, ca.height);
6956   }
6957   PRTime start = 0;  // Initialize these variables to silence the compiler.
6958   if (gLamePaintMetrics) {
6959     start = PR_Now();
6960     drawnLines = 0;
6961   }
6962 #endif
6963 
6964   // TODO(heycam): Should we boost the load priority of any shape-outside
6965   // images using CATEGORY_DISPLAY, now that this block is being displayed?
6966   // We don't have a float manager here.
6967 
6968   DisplayBorderBackgroundOutline(aBuilder, aLists);
6969 
6970   if (GetPrevInFlow()) {
6971     DisplayOverflowContainers(aBuilder, aLists);
6972     for (nsIFrame* f : mFloats) {
6973       if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
6974         BuildDisplayListForChild(aBuilder, f, aLists);
6975       }
6976     }
6977   }
6978 
6979   aBuilder->MarkFramesForDisplayList(this, mFloats);
6980 
6981   if (HasOutsideMarker()) {
6982     // Display outside ::marker manually.
6983     BuildDisplayListForChild(aBuilder, GetOutsideMarker(), aLists);
6984   }
6985 
6986   // Prepare for text-overflow processing.
6987   Maybe<TextOverflow> textOverflow =
6988       TextOverflow::WillProcessLines(aBuilder, this);
6989 
6990   const bool hasDescendantPlaceHolders =
6991       HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
6992       ForceDescendIntoIfVisible() || aBuilder->GetIncludeAllOutOfFlows();
6993 
6994   const auto ShouldDescendIntoLine = [&](const nsRect& aLineArea) -> bool {
6995     // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
6996     // some frame trees, building display list for child lines can change it.
6997     // See bug 1552789.
6998     const bool descendAlways =
6999         HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
7000         aBuilder->GetIncludeAllOutOfFlows();
7001 
7002     return descendAlways || aLineArea.Intersects(aBuilder->GetDirtyRect()) ||
7003            (ForceDescendIntoIfVisible() &&
7004             aLineArea.Intersects(aBuilder->GetVisibleRect()));
7005   };
7006 
7007   Maybe<nscolor> backplateColor;
7008 
7009   // We'll try to draw an accessibility backplate behind text (to ensure it's
7010   // readable over any possible background-images), if all of the following
7011   // hold:
7012   //    (A) the backplate feature is preffed on
7013   //    (B) we are not honoring the document colors
7014   if (StaticPrefs::browser_display_permit_backplate() &&
7015       PresContext()->ForcingColors() && !IsComboboxControlFrame()) {
7016     backplateColor.emplace(GetBackplateColor(this));
7017   }
7018 
7019   // Don't use the line cursor if we might have a descendant placeholder ...
7020   // it might skip lines that contain placeholders but don't themselves
7021   // intersect with the dirty area.
7022   // In particular, we really want to check ShouldDescendIntoFrame()
7023   // on all our child frames, but that might be expensive.  So we
7024   // approximate it by checking it on |this|; if it's true for any
7025   // frame in our child list, it's also true for |this|.
7026   // Also skip the cursor if we're creating text overflow markers,
7027   // since we need to know what line number we're up to in order
7028   // to generate unique display item keys.
7029   // Lastly, the cursor should be skipped if we're drawing
7030   // backplates behind text. When backplating we consider consecutive
7031   // runs of text as a whole, which requires we iterate through all lines
7032   // to find our backplate size.
7033   nsLineBox* cursor =
7034       (hasDescendantPlaceHolders || textOverflow.isSome() || backplateColor)
7035           ? nullptr
7036           : GetFirstLineContaining(aBuilder->GetDirtyRect().y);
7037   LineIterator line_end = LinesEnd();
7038 
7039   TextOverflow* textOverflowPtr = textOverflow.ptrOr(nullptr);
7040 
7041   if (cursor) {
7042     for (LineIterator line = mLines.begin(cursor); line != line_end; ++line) {
7043       const nsRect lineArea = line->InkOverflowRect();
7044       if (!lineArea.IsEmpty()) {
7045         // Because we have a cursor, the combinedArea.ys are non-decreasing.
7046         // Once we've passed aDirtyRect.YMost(), we can never see it again.
7047         if (lineArea.y >= aBuilder->GetDirtyRect().YMost()) {
7048           break;
7049         }
7050         MOZ_ASSERT(textOverflow.isNothing());
7051 
7052         if (ShouldDescendIntoLine(lineArea)) {
7053           DisplayLine(aBuilder, line, line->IsInline(), aLists, this, nullptr,
7054                       0, depth, drawnLines);
7055         }
7056       }
7057     }
7058   } else {
7059     bool nonDecreasingYs = true;
7060     uint32_t lineCount = 0;
7061     nscoord lastY = INT32_MIN;
7062     nscoord lastYMost = INT32_MIN;
7063 
7064     // A frame's display list cannot contain more than one copy of a
7065     // given display item unless the items are uniquely identifiable.
7066     // Because backplate occasionally requires multiple
7067     // SolidColor items, we use an index (backplateIndex) to maintain
7068     // uniqueness among them. Note this is a mapping of index to
7069     // item, and the mapping is stable even if the dirty rect changes.
7070     uint16_t backplateIndex = 0;
7071     nsRect curBackplateArea;
7072 
7073     auto AddBackplate = [&]() {
7074       aLists.BorderBackground()->AppendNewToTopWithIndex<nsDisplaySolidColor>(
7075           aBuilder, this, backplateIndex, curBackplateArea,
7076           backplateColor.value());
7077     };
7078 
7079     for (LineIterator line = LinesBegin(); line != line_end; ++line) {
7080       const nsRect lineArea = line->InkOverflowRect();
7081       const bool lineInLine = line->IsInline();
7082 
7083       if ((lineInLine && textOverflowPtr) || ShouldDescendIntoLine(lineArea)) {
7084         DisplayLine(aBuilder, line, lineInLine, aLists, this, textOverflowPtr,
7085                     lineCount, depth, drawnLines);
7086       }
7087 
7088       if (!lineInLine && !curBackplateArea.IsEmpty()) {
7089         // If we have encountered a non-inline line but were previously
7090         // forming a backplate, we should add the backplate to the display
7091         // list as-is and render future backplates disjointly.
7092         MOZ_ASSERT(backplateColor,
7093                    "if this master switch is off, curBackplateArea "
7094                    "must be empty and we shouldn't get here");
7095         AddBackplate();
7096         backplateIndex++;
7097         curBackplateArea = nsRect();
7098       }
7099 
7100       if (!lineArea.IsEmpty()) {
7101         if (lineArea.y < lastY || lineArea.YMost() < lastYMost) {
7102           nonDecreasingYs = false;
7103         }
7104         lastY = lineArea.y;
7105         lastYMost = lineArea.YMost();
7106         if (lineInLine && backplateColor && LineHasVisibleInlineContent(line)) {
7107           nsRect lineBackplate = GetLineTextArea(line, aBuilder) +
7108                                  aBuilder->ToReferenceFrame(this);
7109           if (curBackplateArea.IsEmpty()) {
7110             curBackplateArea = lineBackplate;
7111           } else {
7112             curBackplateArea.OrWith(lineBackplate);
7113           }
7114         }
7115       }
7116       lineCount++;
7117     }
7118 
7119     if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
7120       SetupLineCursor();
7121     }
7122 
7123     if (!curBackplateArea.IsEmpty()) {
7124       AddBackplate();
7125     }
7126   }
7127 
7128   if (textOverflow.isSome()) {
7129     // Put any text-overflow:ellipsis markers on top of the non-positioned
7130     // content of the block's lines. (If we ever start sorting the Content()
7131     // list this will end up in the wrong place.)
7132     aLists.Content()->AppendToTop(&textOverflow->GetMarkers());
7133   }
7134 
7135 #ifdef DEBUG
7136   if (gLamePaintMetrics) {
7137     PRTime end = PR_Now();
7138 
7139     int32_t numLines = mLines.size();
7140     if (!numLines) numLines = 1;
7141     PRTime lines, deltaPerLine, delta;
7142     lines = int64_t(numLines);
7143     delta = end - start;
7144     deltaPerLine = delta / lines;
7145 
7146     ListTag(stdout);
7147     char buf[400];
7148     SprintfLiteral(buf,
7149                    ": %" PRId64 " elapsed (%" PRId64
7150                    " per line) lines=%d drawn=%d skip=%d",
7151                    delta, deltaPerLine, numLines, drawnLines,
7152                    numLines - drawnLines);
7153     printf("%s\n", buf);
7154   }
7155 #endif
7156 }
7157 
7158 #ifdef ACCESSIBILITY
AccessibleType()7159 a11y::AccType nsBlockFrame::AccessibleType() {
7160   if (IsTableCaption()) {
7161     return GetRect().IsEmpty() ? a11y::eNoType : a11y::eHTMLCaptionType;
7162   }
7163 
7164   // block frame may be for <hr>
7165   if (mContent->IsHTMLElement(nsGkAtoms::hr)) {
7166     return a11y::eHTMLHRType;
7167   }
7168 
7169   if (!HasMarker() || !PresContext()) {
7170     // XXXsmaug What if we're in the shadow dom?
7171     if (!mContent->GetParent()) {
7172       // Don't create accessible objects for the root content node, they are
7173       // redundant with the nsDocAccessible object created with the document
7174       // node
7175       return a11y::eNoType;
7176     }
7177 
7178     if (mContent == mContent->OwnerDoc()->GetBody()) {
7179       // Don't create accessible objects for the body, they are redundant with
7180       // the nsDocAccessible object created with the document node
7181       return a11y::eNoType;
7182     }
7183 
7184     // Not a list item with a ::marker, treat as normal HTML container.
7185     return a11y::eHyperTextType;
7186   }
7187 
7188   // Create special list item accessible since we have a ::marker.
7189   return a11y::eHTMLLiType;
7190 }
7191 #endif
7192 
ClearLineCursor()7193 void nsBlockFrame::ClearLineCursor() {
7194   if (!HasAnyStateBits(NS_BLOCK_HAS_LINE_CURSOR)) {
7195     return;
7196   }
7197 
7198   RemoveProperty(LineCursorProperty());
7199   RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
7200 }
7201 
SetupLineCursor()7202 void nsBlockFrame::SetupLineCursor() {
7203   if (HasAnyStateBits(NS_BLOCK_HAS_LINE_CURSOR) || mLines.empty()) {
7204     return;
7205   }
7206 
7207   SetProperty(LineCursorProperty(), mLines.front());
7208   AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
7209 }
7210 
GetFirstLineContaining(nscoord y)7211 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
7212   if (!HasAnyStateBits(NS_BLOCK_HAS_LINE_CURSOR)) {
7213     return nullptr;
7214   }
7215 
7216   nsLineBox* property = GetProperty(LineCursorProperty());
7217   LineIterator cursor = mLines.begin(property);
7218   nsRect cursorArea = cursor->InkOverflowRect();
7219 
7220   while ((cursorArea.IsEmpty() || cursorArea.YMost() > y) &&
7221          cursor != mLines.front()) {
7222     cursor = cursor.prev();
7223     cursorArea = cursor->InkOverflowRect();
7224   }
7225   while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y) &&
7226          cursor != mLines.back()) {
7227     cursor = cursor.next();
7228     cursorArea = cursor->InkOverflowRect();
7229   }
7230 
7231   if (cursor.get() != property) {
7232     SetProperty(LineCursorProperty(), cursor.get());
7233   }
7234 
7235   return cursor.get();
7236 }
7237 
7238 /* virtual */
ChildIsDirty(nsIFrame * aChild)7239 void nsBlockFrame::ChildIsDirty(nsIFrame* aChild) {
7240   // See if the child is absolutely positioned
7241   if (aChild->IsAbsolutelyPositioned()) {
7242     // do nothing
7243   } else if (aChild == GetOutsideMarker()) {
7244     // The ::marker lives in the first line, unless the first line has
7245     // height 0 and there is a second line, in which case it lives
7246     // in the second line.
7247     LineIterator markerLine = LinesBegin();
7248     if (markerLine != LinesEnd() && markerLine->BSize() == 0 &&
7249         markerLine != mLines.back()) {
7250       markerLine = markerLine.next();
7251     }
7252 
7253     if (markerLine != LinesEnd()) {
7254       MarkLineDirty(markerLine, &mLines);
7255     }
7256     // otherwise we have an empty line list, and ReflowDirtyLines
7257     // will handle reflowing the ::marker.
7258   } else {
7259     // Note that we should go through our children to mark lines dirty
7260     // before the next reflow.  Doing it now could make things O(N^2)
7261     // since finding the right line is O(N).
7262     // We don't need to worry about marking lines on the overflow list
7263     // as dirty; we're guaranteed to reflow them if we take them off the
7264     // overflow list.
7265     // However, we might have gotten a float, in which case we need to
7266     // reflow the line containing its placeholder.  So find the
7267     // ancestor-or-self of the placeholder that's a child of the block,
7268     // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
7269     // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7270     // We need to take some care to handle the case where a float is in
7271     // a different continuation than its placeholder, including marking
7272     // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
7273     if (!aChild->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7274       AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
7275     } else {
7276       NS_ASSERTION(aChild->IsFloating(), "should be a float");
7277       nsIFrame* thisFC = FirstContinuation();
7278       nsIFrame* placeholderPath = aChild->GetPlaceholderFrame();
7279       // SVG code sometimes sends FrameNeedsReflow notifications during
7280       // frame destruction, leading to null placeholders, but we're safe
7281       // ignoring those.
7282       if (placeholderPath) {
7283         for (;;) {
7284           nsIFrame* parent = placeholderPath->GetParent();
7285           if (parent->GetContent() == mContent &&
7286               parent->FirstContinuation() == thisFC) {
7287             parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
7288             break;
7289           }
7290           placeholderPath = parent;
7291         }
7292         placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
7293       }
7294     }
7295   }
7296 
7297   nsContainerFrame::ChildIsDirty(aChild);
7298 }
7299 
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)7300 void nsBlockFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
7301                         nsIFrame* aPrevInFlow) {
7302   // These are all the block specific frame bits, they are copied from
7303   // the prev-in-flow to a newly created next-in-flow, except for the
7304   // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
7305   constexpr nsFrameState NS_BLOCK_FLAGS_MASK =
7306       NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS |
7307       NS_BLOCK_CLIP_PAGINATED_OVERFLOW | NS_BLOCK_HAS_FIRST_LETTER_STYLE |
7308       NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER | NS_BLOCK_HAS_FIRST_LETTER_CHILD |
7309       NS_BLOCK_FRAME_HAS_INSIDE_MARKER;
7310 
7311   // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
7312   // by default.  They should only be set on the first-in-flow.
7313   constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK =
7314       NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER | NS_BLOCK_HAS_FIRST_LETTER_CHILD |
7315       NS_BLOCK_FRAME_HAS_INSIDE_MARKER;
7316 
7317   if (aPrevInFlow) {
7318     // Copy over the inherited block frame bits from the prev-in-flow.
7319     RemoveStateBits(NS_BLOCK_FLAGS_MASK);
7320     AddStateBits(aPrevInFlow->GetStateBits() &
7321                  (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
7322   }
7323 
7324   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
7325 
7326   if (!aPrevInFlow ||
7327       aPrevInFlow->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
7328     AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
7329   }
7330 
7331   // A display:flow-root box establishes a block formatting context.
7332   //
7333   // If a box has a different writing-mode value than its containing block:
7334   // ...
7335   //   If the box is a block container, then it establishes a new block
7336   //   formatting context.
7337   // (https://drafts.csswg.org/css-writing-modes/#block-flow)
7338   //
7339   // If the box has contain: paint or contain:layout (or contain:strict),
7340   // then it should also establish a formatting context.
7341   //
7342   // Per spec, a column-span always establishes a new block formatting context.
7343   if (StyleDisplay()->mDisplay == mozilla::StyleDisplay::FlowRoot ||
7344       (GetParent() &&
7345        (GetWritingMode().GetBlockDir() !=
7346             GetParent()->GetWritingMode().GetBlockDir() ||
7347         GetWritingMode().IsVerticalSideways() !=
7348             GetParent()->GetWritingMode().IsVerticalSideways())) ||
7349       StyleDisplay()->IsContainPaint() || StyleDisplay()->IsContainLayout() ||
7350       IsColumnSpan()) {
7351     AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
7352   }
7353 
7354   if (HasAllStateBits(NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
7355     AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
7356   }
7357 }
7358 
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)7359 void nsBlockFrame::SetInitialChildList(ChildListID aListID,
7360                                        nsFrameList& aChildList) {
7361   if (kFloatList == aListID) {
7362     mFloats.SetFrames(aChildList);
7363   } else if (kPrincipalList == aListID) {
7364 #ifdef DEBUG
7365     // The only times a block that is an anonymous box is allowed to have a
7366     // first-letter frame are when it's the block inside a non-anonymous cell,
7367     // the block inside a fieldset, button or column set, or a scrolled content
7368     // block, except for <select>.  Note that this means that blocks which are
7369     // the anonymous block in {ib} splits do NOT get first-letter frames.
7370     // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
7371     // of the block.
7372     auto pseudo = Style()->GetPseudoType();
7373     bool haveFirstLetterStyle =
7374         (pseudo == PseudoStyleType::NotPseudo ||
7375          (pseudo == PseudoStyleType::cellContent &&
7376           !GetParent()->Style()->IsPseudoOrAnonBox()) ||
7377          pseudo == PseudoStyleType::fieldsetContent ||
7378          pseudo == PseudoStyleType::buttonContent ||
7379          pseudo == PseudoStyleType::columnContent ||
7380          (pseudo == PseudoStyleType::scrolledContent &&
7381           !GetParent()->IsListControlFrame()) ||
7382          pseudo == PseudoStyleType::mozSVGText) &&
7383         !IsComboboxControlFrame() && !IsFrameOfType(eMathML) &&
7384         !IsColumnSetWrapperFrame() &&
7385         RefPtr<ComputedStyle>(GetFirstLetterStyle(PresContext())) != nullptr;
7386     NS_ASSERTION(haveFirstLetterStyle ==
7387                      ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
7388                  "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
7389 #endif
7390 
7391     AddFrames(aChildList, nullptr, nullptr);
7392   } else {
7393     nsContainerFrame::SetInitialChildList(aListID, aChildList);
7394   }
7395 }
7396 
SetMarkerFrameForListItem(nsIFrame * aMarkerFrame)7397 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame* aMarkerFrame) {
7398   MOZ_ASSERT(aMarkerFrame);
7399   MOZ_ASSERT(!HasAnyStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER |
7400                               NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER),
7401              "How can we have a ::marker frame already?");
7402 
7403   if (StyleList()->mListStylePosition == NS_STYLE_LIST_STYLE_POSITION_INSIDE) {
7404     SetProperty(InsideMarkerProperty(), aMarkerFrame);
7405     AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER);
7406   } else {
7407     if (nsBlockFrame* marker = do_QueryFrame(aMarkerFrame)) {
7408       // An outside ::marker needs to be an independent formatting context
7409       // to avoid being influenced by the float manager etc.
7410       marker->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
7411     }
7412     SetProperty(OutsideMarkerProperty(),
7413                 new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame));
7414     AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER);
7415   }
7416 }
7417 
MarkerIsEmpty() const7418 bool nsBlockFrame::MarkerIsEmpty() const {
7419   NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
7420                    HasOutsideMarker(),
7421                "should only care when we have an outside ::marker");
7422   nsIFrame* marker = GetMarker();
7423   const nsStyleList* list = marker->StyleList();
7424   return marker->StyleContent()->mContent.IsNone() ||
7425          (list->mCounterStyle.IsNone() && list->mListStyleImage.IsNone() &&
7426           marker->StyleContent()->ContentCount() == 0);
7427 }
7428 
ReflowOutsideMarker(nsIFrame * aMarkerFrame,BlockReflowState & aState,ReflowOutput & aMetrics,nscoord aLineTop)7429 void nsBlockFrame::ReflowOutsideMarker(nsIFrame* aMarkerFrame,
7430                                        BlockReflowState& aState,
7431                                        ReflowOutput& aMetrics,
7432                                        nscoord aLineTop) {
7433   const ReflowInput& ri = aState.mReflowInput;
7434 
7435   WritingMode markerWM = aMarkerFrame->GetWritingMode();
7436   LogicalSize availSize(markerWM);
7437   // Make up an inline-size since it doesn't really matter (XXX).
7438   availSize.ISize(markerWM) = aState.ContentISize();
7439   availSize.BSize(markerWM) = NS_UNCONSTRAINEDSIZE;
7440 
7441   ReflowInput reflowInput(aState.mPresContext, ri, aMarkerFrame, availSize,
7442                           Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap});
7443   nsReflowStatus status;
7444   aMarkerFrame->Reflow(aState.mPresContext, aMetrics, reflowInput, status);
7445 
7446   // Get the float available space using our saved state from before we
7447   // started reflowing the block, so that we ignore any floats inside
7448   // the block.
7449   // FIXME: aLineTop isn't actually set correctly by some callers, since
7450   // they reposition the line.
7451   LogicalRect floatAvailSpace =
7452       aState
7453           .GetFloatAvailableSpaceWithState(aLineTop, ShapeType::ShapeOutside,
7454                                            &aState.mFloatManagerStateBefore)
7455           .mRect;
7456   // FIXME (bug 25888): need to check the entire region that the first
7457   // line overlaps, not just the top pixel.
7458 
7459   // Place the ::marker now.  We want to place the ::marker relative to the
7460   // border-box of the associated block (using the right/left margin of
7461   // the ::marker frame as separation).  However, if a line box would be
7462   // displaced by floats that are *outside* the associated block, we
7463   // want to displace it by the same amount.  That is, we act as though
7464   // the edge of the floats is the content-edge of the block, and place
7465   // the ::marker at a position offset from there by the block's padding,
7466   // the block's border, and the ::marker frame's margin.
7467 
7468   // IStart from floatAvailSpace gives us the content/float start edge
7469   // in the current writing mode. Then we subtract out the start
7470   // border/padding and the ::marker's width and margin to offset the position.
7471   WritingMode wm = ri.GetWritingMode();
7472   // Get the ::marker's margin, converted to our writing mode so that we can
7473   // combine it with other logical values here.
7474   LogicalMargin markerMargin = reflowInput.ComputedLogicalMargin(wm);
7475   nscoord iStart = floatAvailSpace.IStart(wm) -
7476                    ri.ComputedLogicalBorderPadding(wm).IStart(wm) -
7477                    markerMargin.IEnd(wm) - aMetrics.ISize(wm);
7478 
7479   // Approximate the ::marker's position; vertical alignment will provide
7480   // the final vertical location. We pass our writing-mode here, because
7481   // it may be different from the ::marker frame's mode.
7482   nscoord bStart = floatAvailSpace.BStart(wm);
7483   aMarkerFrame->SetRect(
7484       wm,
7485       LogicalRect(wm, iStart, bStart, aMetrics.ISize(wm), aMetrics.BSize(wm)),
7486       aState.ContainerSize());
7487   aMarkerFrame->DidReflow(aState.mPresContext, &aState.mReflowInput);
7488 }
7489 
7490 // This is used to scan frames for any float placeholders, add their
7491 // floats to the list represented by aList, and remove the
7492 // floats from whatever list they might be in. We don't search descendants
7493 // that are float containing blocks.  Floats that or not children of 'this'
7494 // are ignored (they are not added to aList).
DoCollectFloats(nsIFrame * aFrame,nsFrameList & aList,bool aCollectSiblings)7495 void nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
7496                                    bool aCollectSiblings) {
7497   while (aFrame) {
7498     // Don't descend into float containing blocks.
7499     if (!aFrame->IsFloatContainingBlock()) {
7500       nsIFrame* outOfFlowFrame =
7501           aFrame->IsPlaceholderFrame()
7502               ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame)
7503               : nullptr;
7504       while (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
7505         RemoveFloat(outOfFlowFrame);
7506         // Remove the IS_PUSHED_FLOAT bit, in case |outOfFlowFrame| came from
7507         // the PushedFloats list.
7508         outOfFlowFrame->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
7509         aList.AppendFrame(nullptr, outOfFlowFrame);
7510         outOfFlowFrame = outOfFlowFrame->GetNextInFlow();
7511         // FIXME: By not pulling floats whose parent is one of our
7512         // later siblings, are we risking the pushed floats getting
7513         // out-of-order?
7514         // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
7515       }
7516 
7517       DoCollectFloats(aFrame->PrincipalChildList().FirstChild(), aList, true);
7518       DoCollectFloats(aFrame->GetChildList(kOverflowList).FirstChild(), aList,
7519                       true);
7520     }
7521     if (!aCollectSiblings) break;
7522     aFrame = aFrame->GetNextSibling();
7523   }
7524 }
7525 
CheckFloats(BlockReflowState & aState)7526 void nsBlockFrame::CheckFloats(BlockReflowState& aState) {
7527 #ifdef DEBUG
7528   // If any line is still dirty, that must mean we're going to reflow this
7529   // block again soon (e.g. because we bailed out after noticing that
7530   // clearance was imposed), so don't worry if the floats are out of sync.
7531   bool anyLineDirty = false;
7532 
7533   // Check that the float list is what we would have built
7534   AutoTArray<nsIFrame*, 8> lineFloats;
7535   for (auto& line : Lines()) {
7536     if (line.HasFloats()) {
7537       nsFloatCache* fc = line.GetFirstFloat();
7538       while (fc) {
7539         lineFloats.AppendElement(fc->mFloat);
7540         fc = fc->Next();
7541       }
7542     }
7543     if (line.IsDirty()) {
7544       anyLineDirty = true;
7545     }
7546   }
7547 
7548   AutoTArray<nsIFrame*, 8> storedFloats;
7549   bool equal = true;
7550   uint32_t i = 0;
7551   for (nsIFrame* f : mFloats) {
7552     if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
7553       continue;
7554     }
7555     storedFloats.AppendElement(f);
7556     if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
7557       equal = false;
7558     }
7559     ++i;
7560   }
7561 
7562   if ((!equal || lineFloats.Length() != storedFloats.Length()) &&
7563       !anyLineDirty) {
7564     NS_ERROR(
7565         "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
7566         "float cache");
7567 #  if defined(DEBUG_roc)
7568     nsIFrame::RootFrameList(PresContext(), stdout, 0);
7569     for (i = 0; i < lineFloats.Length(); ++i) {
7570       printf("Line float: %p\n", lineFloats.ElementAt(i));
7571     }
7572     for (i = 0; i < storedFloats.Length(); ++i) {
7573       printf("Stored float: %p\n", storedFloats.ElementAt(i));
7574     }
7575 #  endif
7576   }
7577 #endif
7578 
7579   const nsFrameList* oofs = GetOverflowOutOfFlows();
7580   if (oofs && oofs->NotEmpty()) {
7581     // Floats that were pushed should be removed from our float
7582     // manager.  Otherwise the float manager's YMost or XMost might
7583     // be larger than necessary, causing this block to get an
7584     // incorrect desired height (or width).  Some of these floats
7585     // may not actually have been added to the float manager because
7586     // they weren't reflowed before being pushed; that's OK,
7587     // RemoveRegions will ignore them. It is safe to do this here
7588     // because we know from here on the float manager will only be
7589     // used for its XMost and YMost, not to place new floats and
7590     // lines.
7591     aState.FloatManager()->RemoveTrailingRegions(oofs->FirstChild());
7592   }
7593 }
7594 
IsMarginRoot(bool * aBStartMarginRoot,bool * aBEndMarginRoot)7595 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot,
7596                                 bool* aBEndMarginRoot) {
7597   nsIFrame* parent = GetParent();
7598   if (!HasAnyStateBits(NS_BLOCK_MARGIN_ROOT)) {
7599     if (!parent || parent->IsFloatContainingBlock()) {
7600       *aBStartMarginRoot = false;
7601       *aBEndMarginRoot = false;
7602       return;
7603     }
7604   }
7605 
7606   if (parent && parent->IsColumnSetFrame()) {
7607     // The first column is a start margin root and the last column is an end
7608     // margin root.  (If the column-set is split by a column-span:all box then
7609     // the first and last column in each column-set fragment are margin roots.)
7610     *aBStartMarginRoot = GetPrevInFlow() == nullptr;
7611     *aBEndMarginRoot = GetNextInFlow() == nullptr;
7612     return;
7613   }
7614 
7615   *aBStartMarginRoot = true;
7616   *aBEndMarginRoot = true;
7617 }
7618 
7619 /* static */
BlockNeedsFloatManager(nsIFrame * aBlock)7620 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock) {
7621   MOZ_ASSERT(aBlock, "Must have a frame");
7622   NS_ASSERTION(aBlock->IsBlockFrameOrSubclass(), "aBlock must be a block");
7623 
7624   nsIFrame* parent = aBlock->GetParent();
7625   return aBlock->HasAnyStateBits(NS_BLOCK_FLOAT_MGR) ||
7626          (parent && !parent->IsFloatContainingBlock());
7627 }
7628 
7629 /* static */
BlockCanIntersectFloats(nsIFrame * aFrame)7630 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame) {
7631   return aFrame->IsBlockFrameOrSubclass() &&
7632          !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
7633          !aFrame->HasAnyStateBits(NS_BLOCK_FLOAT_MGR);
7634 }
7635 
7636 // Note that this width can vary based on the vertical position.
7637 // However, the cases where it varies are the cases where the width fits
7638 // in the available space given, which means that variation shouldn't
7639 // matter.
7640 /* static */
ISizeToClearPastFloats(const BlockReflowState & aState,const LogicalRect & aFloatAvailableSpace,nsIFrame * aFrame)7641 nsBlockFrame::ReplacedElementISizeToClear nsBlockFrame::ISizeToClearPastFloats(
7642     const BlockReflowState& aState, const LogicalRect& aFloatAvailableSpace,
7643     nsIFrame* aFrame) {
7644   nscoord inlineStartOffset, inlineEndOffset;
7645   WritingMode wm = aState.mReflowInput.GetWritingMode();
7646   SizeComputationInput offsetState(aFrame,
7647                                    aState.mReflowInput.mRenderingContext, wm,
7648                                    aState.mContentArea.ISize(wm));
7649 
7650   ReplacedElementISizeToClear result;
7651   aState.ComputeReplacedBlockOffsetsForFloats(
7652       aFrame, aFloatAvailableSpace, inlineStartOffset, inlineEndOffset);
7653   nscoord availISize =
7654       aState.mContentArea.ISize(wm) - inlineStartOffset - inlineEndOffset;
7655 
7656   // We actually don't want the min width here; see bug 427782; we only
7657   // want to displace if the width won't compute to a value small enough
7658   // to fit.
7659   // All we really need here is the result of ComputeSize, and we
7660   // could *almost* get that from an SizeComputationInput, except for the
7661   // last argument.
7662   WritingMode frWM = aFrame->GetWritingMode();
7663   LogicalSize availSpace =
7664       LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE).ConvertTo(frWM, wm);
7665   ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput, aFrame,
7666                           availSpace);
7667   result.borderBoxISize =
7668       reflowInput.ComputedSizeWithBorderPadding(wm).ISize(wm);
7669   // Use the margins from offsetState rather than reflowInput so that
7670   // they aren't reduced by ignoring margins in overconstrained cases.
7671   LogicalMargin computedMargin = offsetState.ComputedLogicalMargin(wm);
7672   result.marginIStart = computedMargin.IStart(wm);
7673   return result;
7674 }
7675 
7676 /* static */
GetNearestAncestorBlock(nsIFrame * aCandidate)7677 nsBlockFrame* nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate) {
7678   nsBlockFrame* block = nullptr;
7679   while (aCandidate) {
7680     block = do_QueryFrame(aCandidate);
7681     if (block) {
7682       // yay, candidate is a block!
7683       return block;
7684     }
7685     // Not a block. Check its parent next.
7686     aCandidate = aCandidate->GetParent();
7687   }
7688   MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
7689   return nullptr;
7690 }
7691 
ComputeFinalBSize(BlockReflowState & aState,nscoord aBEndEdgeOfChildren)7692 nscoord nsBlockFrame::ComputeFinalBSize(BlockReflowState& aState,
7693                                         nscoord aBEndEdgeOfChildren) {
7694   const WritingMode wm = aState.mReflowInput.GetWritingMode();
7695 
7696   const nscoord effectiveContentBoxBSize =
7697       GetEffectiveComputedBSize(aState.mReflowInput, aState.mConsumedBSize);
7698   const nscoord blockStartBP = aState.BorderPadding().BStart(wm);
7699   const nscoord blockEndBP = aState.BorderPadding().BEnd(wm);
7700 
7701   NS_ASSERTION(
7702       !IsTrueOverflowContainer() || (effectiveContentBoxBSize == 0 &&
7703                                      blockStartBP == 0 && blockEndBP == 0),
7704       "An overflow container's effective content-box block-size, block-start "
7705       "BP, and block-end BP should all be zero!");
7706 
7707   const nscoord effectiveContentBoxBSizeWithBStartBP =
7708       NSCoordSaturatingAdd(blockStartBP, effectiveContentBoxBSize);
7709   const nscoord effectiveBorderBoxBSize =
7710       NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP, blockEndBP);
7711 
7712   if (HasColumnSpanSiblings()) {
7713     MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
7714                "Frame constructor should've created column-span siblings!");
7715 
7716     // If a block is split by any column-spans, we calculate the final
7717     // block-size by shrinkwrapping our children's block-size for all the
7718     // fragments except for those after the final column-span, but we should
7719     // take no more than our effective border-box block-size. If there's any
7720     // leftover block-size, our next continuations will take up rest.
7721     //
7722     // We don't need to adjust aBri.mReflowStatus because our children's status
7723     // is the same as ours.
7724     return std::min(effectiveBorderBoxBSize, aBEndEdgeOfChildren);
7725   }
7726 
7727   const nscoord availBSize = aState.mReflowInput.AvailableBSize();
7728   if (availBSize == NS_UNCONSTRAINEDSIZE) {
7729     return effectiveBorderBoxBSize;
7730   }
7731 
7732   // Save our children's reflow status.
7733   const bool isChildStatusComplete = aState.mReflowStatus.IsComplete();
7734   if (isChildStatusComplete && effectiveContentBoxBSize > 0 &&
7735       effectiveBorderBoxBSize > availBSize &&
7736       ShouldAvoidBreakInside(aState.mReflowInput)) {
7737     aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
7738     return effectiveBorderBoxBSize;
7739   }
7740 
7741   const bool isBDBClone =
7742       aState.mReflowInput.mStyleBorder->mBoxDecorationBreak ==
7743       StyleBoxDecorationBreak::Clone;
7744 
7745   // The maximum value our content-box block-size can take within the given
7746   // available block-size.
7747   const nscoord maxContentBoxBSize = aState.ContentBSize();
7748 
7749   // The block-end edge of our content-box (relative to this frame's origin) if
7750   // we consumed the maximum block-size available to us (maxContentBoxBSize).
7751   const nscoord maxContentBoxBEnd = aState.ContentBEnd();
7752 
7753   // These variables are uninitialized intentionally so that the compiler can
7754   // check they are assigned in every if-else branch below.
7755   nscoord finalContentBoxBSizeWithBStartBP;
7756   bool isOurStatusComplete;
7757 
7758   if (effectiveBorderBoxBSize <= availBSize) {
7759     // Our effective border-box block-size can fit in the available block-size,
7760     // so we are complete.
7761     finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
7762     isOurStatusComplete = true;
7763   } else if (effectiveContentBoxBSizeWithBStartBP <= maxContentBoxBEnd) {
7764     // Note: The following assertion should generally hold because, for
7765     // box-decoration-break:clone, this "else if" branch is mathematically
7766     // equivalent to the initial "if".
7767     NS_ASSERTION(!isBDBClone,
7768                  "This else-if branch is handling a situation that's specific "
7769                  "to box-decoration-break:slice, i.e. a case when we can skip "
7770                  "our block-end border and padding!");
7771 
7772     // Our effective content-box block-size plus the block-start border and
7773     // padding can fit in the available block-size, but it cannot fit after
7774     // adding the block-end border and padding. Thus, we need a continuation
7775     // (unless we already weren't asking for any block-size, in which case we
7776     // stay complete to avoid looping forever).
7777     finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
7778     isOurStatusComplete = effectiveContentBoxBSize == 0;
7779   } else {
7780     // We aren't going to be able to fit our content-box in the space available
7781     // to it, which means we'll probably call ourselves incomplete to request a
7782     // continuation. But before making that decision, we check for certain
7783     // conditions which would force us to overflow beyond the available space --
7784     // these might result in us actually being complete if we're forced to
7785     // overflow far enough.
7786     if (MOZ_UNLIKELY(aState.mReflowInput.mFlags.mIsTopOfPage && isBDBClone &&
7787                      maxContentBoxBSize <= 0 &&
7788                      aBEndEdgeOfChildren == blockStartBP)) {
7789       // In this rare case, we are at the top of page/column, we have
7790       // box-decoration-break:clone and zero available block-size for our
7791       // content-box (e.g. our own block-start border and padding already exceed
7792       // the available block-size), and we didn't lay out any child to consume
7793       // our content-box block-size. To ensure we make progress (avoid looping
7794       // forever), use 1px as our content-box block-size regardless of our
7795       // effective content-box block-size, in the spirit of
7796       // https://drafts.csswg.org/css-break/#breaking-rules.
7797       finalContentBoxBSizeWithBStartBP = blockStartBP + AppUnitsPerCSSPixel();
7798       isOurStatusComplete = effectiveContentBoxBSize <= AppUnitsPerCSSPixel();
7799     } else if (aBEndEdgeOfChildren > maxContentBoxBEnd) {
7800       // We have a unbreakable child whose block-end edge exceeds the available
7801       // block-size for children.
7802       if (aBEndEdgeOfChildren >= effectiveContentBoxBSizeWithBStartBP) {
7803         // The unbreakable child's block-end edge forces us to consume all of
7804         // our effective content-box block-size.
7805         finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
7806 
7807         // Even though we've consumed all of our effective content-box
7808         // block-size, we may still need to report an incomplete status in order
7809         // to get another continuation, which will be responsible for laying out
7810         // & drawing our block-end border & padding. But if we have no such
7811         // border & padding, or if we're forced to apply that border & padding
7812         // on this frame due to box-decoration-break:clone, then we don't need
7813         // to bother with that additional continuation.
7814         isOurStatusComplete = (isBDBClone || blockEndBP == 0);
7815       } else {
7816         // The unbreakable child's block-end edge doesn't force us to consume
7817         // all of our effective content-box block-size.
7818         finalContentBoxBSizeWithBStartBP = aBEndEdgeOfChildren;
7819         isOurStatusComplete = false;
7820       }
7821     } else {
7822       // The children's block-end edge can fit in the content-box space that we
7823       // have available for it. Consume all the space that is available so that
7824       // our inline-start/inline-end borders extend all the way to the block-end
7825       // edge of column/page.
7826       finalContentBoxBSizeWithBStartBP = maxContentBoxBEnd;
7827       isOurStatusComplete = false;
7828     }
7829   }
7830 
7831   nscoord finalBorderBoxBSize = finalContentBoxBSizeWithBStartBP;
7832   if (isOurStatusComplete) {
7833     finalBorderBoxBSize = NSCoordSaturatingAdd(finalBorderBoxBSize, blockEndBP);
7834     if (isChildStatusComplete) {
7835       // We want to use children's reflow status as ours, which can be overflow
7836       // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
7837     } else {
7838       aState.mReflowStatus.SetOverflowIncomplete();
7839     }
7840   } else {
7841     NS_ASSERTION(!IsTrueOverflowContainer(),
7842                  "An overflow container should always be complete because of "
7843                  "its zero border-box block-size!");
7844     if (isBDBClone) {
7845       finalBorderBoxBSize =
7846           NSCoordSaturatingAdd(finalBorderBoxBSize, blockEndBP);
7847     }
7848     aState.mReflowStatus.SetIncomplete();
7849     if (!GetNextInFlow()) {
7850       aState.mReflowStatus.SetNextInFlowNeedsReflow();
7851     }
7852   }
7853 
7854   return finalBorderBoxBSize;
7855 }
7856 
ResolveBidi()7857 nsresult nsBlockFrame::ResolveBidi() {
7858   NS_ASSERTION(!GetPrevInFlow(),
7859                "ResolveBidi called on non-first continuation");
7860   MOZ_ASSERT(PresContext()->BidiEnabled());
7861   return nsBidiPresUtils::Resolve(this);
7862 }
7863 
UpdatePseudoElementStyles(ServoRestyleState & aRestyleState)7864 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState& aRestyleState) {
7865   // first-letter needs to be updated before first-line, because first-line can
7866   // change the style of the first-letter.
7867   if (HasFirstLetterChild()) {
7868     UpdateFirstLetterStyle(aRestyleState);
7869   }
7870 
7871   if (nsIFrame* firstLineFrame = GetFirstLineFrame()) {
7872     nsIFrame* styleParent = CorrectStyleParentFrame(firstLineFrame->GetParent(),
7873                                                     PseudoStyleType::firstLine);
7874 
7875     ComputedStyle* parentStyle = styleParent->Style();
7876     RefPtr<ComputedStyle> firstLineStyle =
7877         aRestyleState.StyleSet().ResolvePseudoElementStyle(
7878             *mContent->AsElement(), PseudoStyleType::firstLine, parentStyle);
7879 
7880     // FIXME(bz): Can we make first-line continuations be non-inheriting anon
7881     // boxes?
7882     RefPtr<ComputedStyle> continuationStyle =
7883         aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
7884             PseudoStyleType::mozLineFrame, parentStyle);
7885 
7886     UpdateStyleOfOwnedChildFrame(firstLineFrame, firstLineStyle, aRestyleState,
7887                                  Some(continuationStyle.get()));
7888 
7889     // We also want to update the styles of the first-line's descendants.  We
7890     // don't need to compute a changehint for this, though, since any changes to
7891     // them are handled by the first-line anyway.
7892     RestyleManager* manager = PresContext()->RestyleManager();
7893     for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
7894       manager->ReparentComputedStyleForFirstLine(kid);
7895     }
7896   }
7897 }
7898 
GetFirstLetter() const7899 nsIFrame* nsBlockFrame::GetFirstLetter() const {
7900   if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE)) {
7901     // Certainly no first-letter frame.
7902     return nullptr;
7903   }
7904 
7905   return GetProperty(FirstLetterProperty());
7906 }
7907 
GetFirstLineFrame() const7908 nsIFrame* nsBlockFrame::GetFirstLineFrame() const {
7909   nsIFrame* maybeFirstLine = PrincipalChildList().FirstChild();
7910   if (maybeFirstLine && maybeFirstLine->IsLineFrame()) {
7911     return maybeFirstLine;
7912   }
7913 
7914   return nullptr;
7915 }
7916 
7917 #ifdef DEBUG
VerifyLines(bool aFinalCheckOK)7918 void nsBlockFrame::VerifyLines(bool aFinalCheckOK) {
7919   if (!gVerifyLines) {
7920     return;
7921   }
7922   if (mLines.empty()) {
7923     return;
7924   }
7925 
7926   nsLineBox* cursor = GetLineCursor();
7927 
7928   // Add up the counts on each line. Also validate that IsFirstLine is
7929   // set properly.
7930   int32_t count = 0;
7931   for (const auto& line : Lines()) {
7932     if (&line == cursor) {
7933       cursor = nullptr;
7934     }
7935     if (aFinalCheckOK) {
7936       MOZ_ASSERT(line.GetChildCount(), "empty line");
7937       if (line.IsBlock()) {
7938         NS_ASSERTION(1 == line.GetChildCount(), "bad first line");
7939       }
7940     }
7941     count += line.GetChildCount();
7942   }
7943 
7944   // Then count the frames
7945   int32_t frameCount = 0;
7946   nsIFrame* frame = mLines.front()->mFirstChild;
7947   while (frame) {
7948     frameCount++;
7949     frame = frame->GetNextSibling();
7950   }
7951   NS_ASSERTION(count == frameCount, "bad line list");
7952 
7953   // Next: test that each line has right number of frames on it
7954   for (LineIterator line = LinesBegin(), line_end = LinesEnd();
7955        line != line_end;) {
7956     count = line->GetChildCount();
7957     frame = line->mFirstChild;
7958     while (--count >= 0) {
7959       frame = frame->GetNextSibling();
7960     }
7961     ++line;
7962     if ((line != line_end) && (0 != line->GetChildCount())) {
7963       NS_ASSERTION(frame == line->mFirstChild, "bad line list");
7964     }
7965   }
7966 
7967   if (cursor) {
7968     FrameLines* overflowLines = GetOverflowLines();
7969     if (overflowLines) {
7970       LineIterator line = overflowLines->mLines.begin();
7971       LineIterator line_end = overflowLines->mLines.end();
7972       for (; line != line_end; ++line) {
7973         if (line == cursor) {
7974           cursor = nullptr;
7975           break;
7976         }
7977       }
7978     }
7979   }
7980   NS_ASSERTION(!cursor, "stale LineCursorProperty");
7981 }
7982 
VerifyOverflowSituation()7983 void nsBlockFrame::VerifyOverflowSituation() {
7984   // Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames.
7985   nsFrameList* oofs = GetOverflowOutOfFlows();
7986   if (oofs) {
7987     for (nsFrameList::Enumerator e(*oofs); !e.AtEnd(); e.Next()) {
7988       nsIFrame* nif = e.get()->GetNextInFlow();
7989       MOZ_ASSERT(!nif ||
7990                  (!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif)));
7991     }
7992   }
7993 
7994   // Pushed floats must not have a next-in-flow in mFloats or mFrames.
7995   oofs = GetPushedFloats();
7996   if (oofs) {
7997     for (nsFrameList::Enumerator e(*oofs); !e.AtEnd(); e.Next()) {
7998       nsIFrame* nif = e.get()->GetNextInFlow();
7999       MOZ_ASSERT(!nif ||
8000                  (!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif)));
8001     }
8002   }
8003 
8004   // A child float next-in-flow's parent must be |this| or a next-in-flow of
8005   // |this|. Later next-in-flows must have the same or later parents.
8006   nsIFrame::ChildListID childLists[] = {nsIFrame::kFloatList,
8007                                         nsIFrame::kPushedFloatsList};
8008   for (size_t i = 0; i < ArrayLength(childLists); ++i) {
8009     nsFrameList children(GetChildList(childLists[i]));
8010     for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
8011       nsIFrame* parent = this;
8012       nsIFrame* nif = e.get()->GetNextInFlow();
8013       for (; nif; nif = nif->GetNextInFlow()) {
8014         bool found = false;
8015         for (nsIFrame* p = parent; p; p = p->GetNextInFlow()) {
8016           if (nif->GetParent() == p) {
8017             parent = p;
8018             found = true;
8019             break;
8020           }
8021         }
8022         MOZ_ASSERT(
8023             found,
8024             "next-in-flow is a child of parent earlier in the frame tree?");
8025       }
8026     }
8027   }
8028 
8029   nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
8030   while (flow) {
8031     FrameLines* overflowLines = flow->GetOverflowLines();
8032     if (overflowLines) {
8033       NS_ASSERTION(!overflowLines->mLines.empty(),
8034                    "should not be empty if present");
8035       NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
8036                    "bad overflow lines");
8037       NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
8038                        overflowLines->mFrames.FirstChild(),
8039                    "bad overflow frames / lines");
8040     }
8041     nsLineBox* cursor = flow->GetLineCursor();
8042     if (cursor) {
8043       LineIterator line = flow->LinesBegin();
8044       LineIterator line_end = flow->LinesEnd();
8045       for (; line != line_end && line != cursor; ++line)
8046         ;
8047       if (line == line_end && overflowLines) {
8048         line = overflowLines->mLines.begin();
8049         line_end = overflowLines->mLines.end();
8050         for (; line != line_end && line != cursor; ++line)
8051           ;
8052       }
8053       MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
8054     }
8055     flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
8056   }
8057 }
8058 
GetDepth() const8059 int32_t nsBlockFrame::GetDepth() const {
8060   int32_t depth = 0;
8061   nsIFrame* parent = GetParent();
8062   while (parent) {
8063     parent = parent->GetParent();
8064     depth++;
8065   }
8066   return depth;
8067 }
8068 
GetFirstLetterStyle(nsPresContext * aPresContext)8069 already_AddRefed<ComputedStyle> nsBlockFrame::GetFirstLetterStyle(
8070     nsPresContext* aPresContext) {
8071   return aPresContext->StyleSet()->ProbePseudoElementStyle(
8072       *mContent->AsElement(), PseudoStyleType::firstLetter, Style());
8073 }
8074 #endif
8075