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