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