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