Merge m-c to fx-team.
[gecko.git] / layout / generic / nsBlockFrame.cpp
blob14d38974ed6022797baeb38272b7a7cd12ac3218
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * rendering object for CSS display:block, inline-block, and list-item
9 * boxes, also used for various anonymous boxes
12 #include "nsBlockFrame.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/Util.h"
17 #include "nsCOMPtr.h"
18 #include "nsAbsoluteContainingBlock.h"
19 #include "nsBlockReflowContext.h"
20 #include "nsBlockReflowState.h"
21 #include "nsBulletFrame.h"
22 #include "nsLineBox.h"
23 #include "nsLineLayout.h"
24 #include "nsPlaceholderFrame.h"
25 #include "nsStyleConsts.h"
26 #include "nsFrameManager.h"
27 #include "nsPresContext.h"
28 #include "nsIPresShell.h"
29 #include "nsStyleContext.h"
30 #include "nsHTMLParts.h"
31 #include "nsGkAtoms.h"
32 #include "nsGenericHTMLElement.h"
33 #include "nsAttrValueInlines.h"
34 #include "prprf.h"
35 #include "nsFloatManager.h"
36 #include "prenv.h"
37 #include "plstr.h"
38 #include "nsError.h"
39 #include "nsAutoPtr.h"
40 #include "nsIScrollableFrame.h"
41 #include <algorithm>
42 #ifdef ACCESSIBILITY
43 #include "nsIDOMHTMLDocument.h"
44 #endif
45 #include "nsLayoutUtils.h"
46 #include "nsDisplayList.h"
47 #include "nsCSSAnonBoxes.h"
48 #include "nsCSSFrameConstructor.h"
49 #include "nsRenderingContext.h"
50 #include "TextOverflow.h"
51 #include "nsIFrameInlines.h"
53 #ifdef IBMBIDI
54 #include "nsBidiPresUtils.h"
55 #endif // IBMBIDI
57 static const int MIN_LINES_NEEDING_CURSOR = 20;
59 static const PRUnichar kDiscCharacter = 0x2022;
60 static const PRUnichar kCircleCharacter = 0x25e6;
61 static const PRUnichar kSquareCharacter = 0x25aa;
63 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
65 using namespace mozilla;
66 using namespace mozilla::css;
67 using namespace mozilla::layout;
69 #ifdef DEBUG
70 #include "nsBlockDebugFlags.h"
72 bool nsBlockFrame::gLamePaintMetrics;
73 bool nsBlockFrame::gLameReflowMetrics;
74 bool nsBlockFrame::gNoisy;
75 bool nsBlockFrame::gNoisyDamageRepair;
76 bool nsBlockFrame::gNoisyIntrinsic;
77 bool nsBlockFrame::gNoisyReflow;
78 bool nsBlockFrame::gReallyNoisyReflow;
79 bool nsBlockFrame::gNoisyFloatManager;
80 bool nsBlockFrame::gVerifyLines;
81 bool nsBlockFrame::gDisableResizeOpt;
83 int32_t nsBlockFrame::gNoiseIndent;
85 struct BlockDebugFlags {
86 const char* name;
87 bool* on;
90 static const BlockDebugFlags gFlags[] = {
91 { "reflow", &nsBlockFrame::gNoisyReflow },
92 { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
93 { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
94 { "float-manager", &nsBlockFrame::gNoisyFloatManager },
95 { "verify-lines", &nsBlockFrame::gVerifyLines },
96 { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
97 { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
98 { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
99 { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
101 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
103 static void
104 ShowDebugFlags()
106 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
107 const BlockDebugFlags* bdf = gFlags;
108 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
109 for (; bdf < end; bdf++) {
110 printf(" %s\n", bdf->name);
112 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
113 printf("names (no whitespace)\n");
116 void
117 nsBlockFrame::InitDebugFlags()
119 static bool firstTime = true;
120 if (firstTime) {
121 firstTime = false;
122 char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
123 if (flags) {
124 bool error = false;
125 for (;;) {
126 char* cm = PL_strchr(flags, ',');
127 if (cm) *cm = '\0';
129 bool found = false;
130 const BlockDebugFlags* bdf = gFlags;
131 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
132 for (; bdf < end; bdf++) {
133 if (PL_strcasecmp(bdf->name, flags) == 0) {
134 *(bdf->on) = true;
135 printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
136 gNoisy = true;
137 found = true;
138 break;
141 if (!found) {
142 error = true;
145 if (!cm) break;
146 *cm = ',';
147 flags = cm + 1;
149 if (error) {
150 ShowDebugFlags();
156 #endif
158 // add in a sanity check for absurdly deep frame trees. See bug 42138
159 // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
160 #define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
162 //----------------------------------------------------------------------
164 // Debugging support code
166 #ifdef DEBUG
167 const char* nsBlockFrame::kReflowCommandType[] = {
168 "ContentChanged",
169 "StyleChanged",
170 "ReflowDirty",
171 "Timeout",
172 "UserDefined",
174 #endif
176 #ifdef REALLY_NOISY_FIRST_LINE
177 static void
178 DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
180 fputs(gap, stdout);
181 nsFrame::ListTag(stdout, aFrame);
182 printf(": ");
183 nsStyleContext* sc = aFrame->StyleContext();
184 while (nullptr != sc) {
185 nsStyleContext* psc;
186 printf("%p ", sc);
187 psc = sc->GetParent();
188 sc = psc;
190 printf("\n");
192 #endif
194 #ifdef REFLOW_STATUS_COVERAGE
195 static void
196 RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
198 static uint32_t record[2];
200 // 0: child-is-block
201 // 1: child-is-inline
202 int index = 0;
203 if (!aChildIsBlock) index |= 1;
205 // Compute new status
206 uint32_t newS = record[index];
207 if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
208 if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
209 newS |= 1;
211 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
212 newS |= 2;
214 else {
215 newS |= 4;
218 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
219 newS |= 8;
221 else {
222 newS |= 16;
225 // Log updates to the status that yield different values
226 if (record[index] != newS) {
227 record[index] = newS;
228 printf("record(%d): %02x %02x\n", index, record[0], record[1]);
231 #endif
233 // Destructor function for the overflowLines frame property
234 static void
235 DestroyOverflowLines(void* aPropertyValue)
237 NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
240 NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
241 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
242 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
243 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
244 NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr)
246 //----------------------------------------------------------------------
248 nsIFrame*
249 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags)
251 nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
252 it->SetFlags(aFlags);
253 return it;
256 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
258 nsBlockFrame::~nsBlockFrame()
262 void
263 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
265 ClearLineCursor();
266 DestroyAbsoluteFrames(aDestructRoot);
267 mFloats.DestroyFramesFrom(aDestructRoot);
268 nsPresContext* presContext = PresContext();
269 nsIPresShell* shell = presContext->PresShell();
270 nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
271 &mFrames);
273 FramePropertyTable* props = presContext->PropertyTable();
275 if (HasPushedFloats()) {
276 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
277 PushedFloatProperty());
278 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
281 // destroy overflow lines now
282 FrameLines* overflowLines = RemoveOverflowLines();
283 if (overflowLines) {
284 nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
285 aDestructRoot, &overflowLines->mFrames);
286 delete overflowLines;
289 if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
290 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
291 OverflowOutOfFlowsProperty());
292 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
295 if (HasOutsideBullet()) {
296 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
297 OutsideBulletProperty());
298 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
301 nsBlockFrameSuper::DestroyFrom(aDestructRoot);
304 /* virtual */ nsILineIterator*
305 nsBlockFrame::GetLineIterator()
307 nsLineIterator* it = new nsLineIterator;
308 if (!it)
309 return nullptr;
311 const nsStyleVisibility* visibility = StyleVisibility();
312 nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL);
313 if (NS_FAILED(rv)) {
314 delete it;
315 return nullptr;
317 return it;
320 NS_QUERYFRAME_HEAD(nsBlockFrame)
321 NS_QUERYFRAME_ENTRY(nsBlockFrame)
322 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper)
324 nsSplittableType
325 nsBlockFrame::GetSplittableType() const
327 return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
330 #ifdef DEBUG
331 void
332 nsBlockFrame::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
334 ListGeneric(out, aIndent, aFlags);
336 fputs("<\n", out);
338 aIndent++;
340 // Output the lines
341 if (!mLines.empty()) {
342 const_line_iterator line = begin_lines(), line_end = end_lines();
343 for ( ; line != line_end; ++line) {
344 line->List(out, aIndent, aFlags);
348 // Output the overflow lines.
349 const FrameLines* overflowLines = GetOverflowLines();
350 if (overflowLines && !overflowLines->mLines.empty()) {
351 IndentBy(out, aIndent);
352 fprintf(out, "Overflow-lines %p/%p <\n", overflowLines, &overflowLines->mFrames);
353 const_line_iterator line = overflowLines->mLines.begin(),
354 line_end = overflowLines->mLines.end();
355 for ( ; line != line_end; ++line) {
356 line->List(out, aIndent + 1, aFlags);
358 IndentBy(out, aIndent);
359 fputs(">\n", out);
362 // skip the principal list - we printed the lines above
363 // skip the overflow list - we printed the overflow lines above
364 ChildListIterator lists(this);
365 ChildListIDs skip(kPrincipalList | kOverflowList);
366 for (; !lists.IsDone(); lists.Next()) {
367 if (skip.Contains(lists.CurrentID())) {
368 continue;
370 IndentBy(out, aIndent);
371 fprintf(out, "%s %p <\n", mozilla::layout::ChildListName(lists.CurrentID()),
372 &GetChildList(lists.CurrentID()));
373 nsFrameList::Enumerator childFrames(lists.CurrentList());
374 for (; !childFrames.AtEnd(); childFrames.Next()) {
375 nsIFrame* kid = childFrames.get();
376 kid->List(out, aIndent + 1, aFlags);
378 IndentBy(out, aIndent);
379 fputs(">\n", out);
382 aIndent--;
383 IndentBy(out, aIndent);
384 fputs(">\n", out);
387 NS_IMETHODIMP_(nsFrameState)
388 nsBlockFrame::GetDebugStateBits() const
390 // We don't want to include our cursor flag in the bits the
391 // regression tester looks at
392 return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
395 NS_IMETHODIMP
396 nsBlockFrame::GetFrameName(nsAString& aResult) const
398 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
400 #endif
402 nsIAtom*
403 nsBlockFrame::GetType() const
405 return nsGkAtoms::blockFrame;
408 void
409 nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey)
411 if (IsSVGText()) {
412 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame2,
413 "unexpected block frame in SVG text");
414 GetParent()->InvalidateFrame();
415 return;
417 nsBlockFrameSuper::InvalidateFrame(aDisplayItemKey);
420 void
421 nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
423 if (IsSVGText()) {
424 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame2,
425 "unexpected block frame in SVG text");
426 GetParent()->InvalidateFrame();
427 return;
429 nsBlockFrameSuper::InvalidateFrameWithRect(aRect, aDisplayItemKey);
432 nscoord
433 nsBlockFrame::GetBaseline() const
435 nscoord result;
436 if (nsLayoutUtils::GetLastLineBaseline(this, &result))
437 return result;
438 return nsFrame::GetBaseline();
441 nscoord
442 nsBlockFrame::GetCaretBaseline() const
444 nsRect contentRect = GetContentRect();
445 nsMargin bp = GetUsedBorderAndPadding();
447 if (!mLines.empty()) {
448 const_line_iterator line = begin_lines();
449 const nsLineBox* firstLine = line;
450 if (firstLine->GetChildCount()) {
451 return bp.top + firstLine->mFirstChild->GetCaretBaseline();
454 nsRefPtr<nsFontMetrics> fm;
455 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
456 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
457 return nsLayoutUtils::GetCenteredFontBaseline(fm, nsHTMLReflowState::
458 CalcLineHeight(StyleContext(), contentRect.height, inflation)) +
459 bp.top;
462 /////////////////////////////////////////////////////////////////////////////
463 // Child frame enumeration
465 const nsFrameList&
466 nsBlockFrame::GetChildList(ChildListID aListID) const
468 switch (aListID) {
469 case kPrincipalList:
470 return mFrames;
471 case kOverflowList: {
472 FrameLines* overflowLines = GetOverflowLines();
473 return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
475 case kFloatList:
476 return mFloats;
477 case kOverflowOutOfFlowList: {
478 const nsFrameList* list = GetOverflowOutOfFlows();
479 return list ? *list : nsFrameList::EmptyList();
481 case kPushedFloatsList: {
482 const nsFrameList* list = GetPushedFloats();
483 return list ? *list : nsFrameList::EmptyList();
485 case kBulletList: {
486 const nsFrameList* list = GetOutsideBulletList();
487 return list ? *list : nsFrameList::EmptyList();
489 default:
490 return nsContainerFrame::GetChildList(aListID);
494 void
495 nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const
497 nsContainerFrame::GetChildLists(aLists);
498 FrameLines* overflowLines = GetOverflowLines();
499 if (overflowLines) {
500 overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
502 const nsFrameList* list = GetOverflowOutOfFlows();
503 if (list) {
504 list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
506 mFloats.AppendIfNonempty(aLists, kFloatList);
507 list = GetOutsideBulletList();
508 if (list) {
509 list->AppendIfNonempty(aLists, kBulletList);
511 list = GetPushedFloats();
512 if (list) {
513 list->AppendIfNonempty(aLists, kPushedFloatsList);
517 /* virtual */ bool
518 nsBlockFrame::IsFloatContainingBlock() const
520 return true;
523 static void
524 ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent)
526 NS_ASSERTION(aOldParent == aFrame->GetParent(),
527 "Parent not consistent with expectations");
529 aFrame->SetParent(aNewParent);
531 // When pushing and pulling frames we need to check for whether any
532 // views need to be reparented
533 nsContainerFrame::ReparentFrameView(aFrame->PresContext(), aFrame,
534 aOldParent, aNewParent);
537 static void
538 ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent,
539 nsIFrame* aNewParent)
541 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
542 ReparentFrame(e.get(), aOldParent, aNewParent);
547 * Remove the first line from aFromLines and adjust the associated frame list
548 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
549 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
550 * that were extracted from the head of aFromFrames.
551 * aFromLines must contain at least one line, the line may be empty.
552 * @return true if aFromLines becomes empty
554 static bool
555 RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
556 nsLineBox** aOutLine, nsFrameList* aOutFrames)
558 nsLineList_iterator removedLine = aFromLines.begin();
559 *aOutLine = removedLine;
560 nsLineList_iterator next = aFromLines.erase(removedLine);
561 bool isLastLine = next == aFromLines.end();
562 nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
563 : next->mFirstChild->GetPrevSibling();
564 nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
565 *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
566 return isLastLine;
569 //////////////////////////////////////////////////////////////////////
570 // Reflow methods
572 /* virtual */ void
573 nsBlockFrame::MarkIntrinsicWidthsDirty()
575 nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
576 dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
577 dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
578 if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
579 for (nsIFrame* frame = dirtyBlock; frame;
580 frame = frame->GetNextContinuation()) {
581 frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
585 nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
588 void
589 nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState()
591 nsPresContext *presContext = PresContext();
592 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
593 return;
595 bool inflationEnabled =
596 !presContext->mInflationDisabledForShrinkWrap;
597 if (inflationEnabled !=
598 !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED)) {
599 mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
600 mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
601 if (inflationEnabled) {
602 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
603 } else {
604 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
609 /* virtual */ nscoord
610 nsBlockFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
612 nsIFrame* firstInFlow = FirstContinuation();
613 if (firstInFlow != this)
614 return firstInFlow->GetMinWidth(aRenderingContext);
616 DISPLAY_MIN_WIDTH(this, mMinWidth);
618 CheckIntrinsicCacheAgainstShrinkWrapState();
620 if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
621 return mMinWidth;
623 #ifdef DEBUG
624 if (gNoisyIntrinsic) {
625 IndentBy(stdout, gNoiseIndent);
626 ListTag(stdout);
627 printf(": GetMinWidth\n");
629 AutoNoisyIndenter indenter(gNoisyIntrinsic);
630 #endif
632 for (nsBlockFrame* curFrame = this; curFrame;
633 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
634 curFrame->LazyMarkLinesDirty();
637 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
638 ResolveBidi();
639 InlineMinWidthData data;
640 for (nsBlockFrame* curFrame = this; curFrame;
641 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
642 for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
643 line != line_end; ++line)
645 #ifdef DEBUG
646 if (gNoisyIntrinsic) {
647 IndentBy(stdout, gNoiseIndent);
648 printf("line (%s%s)\n",
649 line->IsBlock() ? "block" : "inline",
650 line->IsEmpty() ? ", empty" : "");
652 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
653 #endif
654 if (line->IsBlock()) {
655 data.ForceBreak(aRenderingContext);
656 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
657 line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
658 data.ForceBreak(aRenderingContext);
659 } else {
660 if (!curFrame->GetPrevContinuation() &&
661 line == curFrame->begin_lines()) {
662 // Only add text-indent if it has no percentages; using a
663 // percentage basis of 0 unconditionally would give strange
664 // behavior for calc(10%-3px).
665 const nsStyleCoord &indent = StyleText()->mTextIndent;
666 if (indent.ConvertsToLength())
667 data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
669 // XXX Bug NNNNNN Should probably handle percentage text-indent.
671 data.line = &line;
672 data.lineContainer = curFrame;
673 nsIFrame *kid = line->mFirstChild;
674 for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
675 ++i, kid = kid->GetNextSibling()) {
676 kid->AddInlineMinWidth(aRenderingContext, &data);
679 #ifdef DEBUG
680 if (gNoisyIntrinsic) {
681 IndentBy(stdout, gNoiseIndent);
682 printf("min: [prevLines=%d currentLine=%d]\n",
683 data.prevLines, data.currentLine);
685 #endif
688 data.ForceBreak(aRenderingContext);
690 mMinWidth = data.prevLines;
691 return mMinWidth;
694 /* virtual */ nscoord
695 nsBlockFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
697 nsIFrame* firstInFlow = FirstContinuation();
698 if (firstInFlow != this)
699 return firstInFlow->GetPrefWidth(aRenderingContext);
701 DISPLAY_PREF_WIDTH(this, mPrefWidth);
703 CheckIntrinsicCacheAgainstShrinkWrapState();
705 if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
706 return mPrefWidth;
708 #ifdef DEBUG
709 if (gNoisyIntrinsic) {
710 IndentBy(stdout, gNoiseIndent);
711 ListTag(stdout);
712 printf(": GetPrefWidth\n");
714 AutoNoisyIndenter indenter(gNoisyIntrinsic);
715 #endif
717 for (nsBlockFrame* curFrame = this; curFrame;
718 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
719 curFrame->LazyMarkLinesDirty();
722 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
723 ResolveBidi();
724 InlinePrefWidthData data;
725 for (nsBlockFrame* curFrame = this; curFrame;
726 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
727 for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
728 line != line_end; ++line)
730 #ifdef DEBUG
731 if (gNoisyIntrinsic) {
732 IndentBy(stdout, gNoiseIndent);
733 printf("line (%s%s)\n",
734 line->IsBlock() ? "block" : "inline",
735 line->IsEmpty() ? ", empty" : "");
737 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
738 #endif
739 if (line->IsBlock()) {
740 data.ForceBreak(aRenderingContext);
741 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
742 line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
743 data.ForceBreak(aRenderingContext);
744 } else {
745 if (!curFrame->GetPrevContinuation() &&
746 line == curFrame->begin_lines()) {
747 // Only add text-indent if it has no percentages; using a
748 // percentage basis of 0 unconditionally would give strange
749 // behavior for calc(10%-3px).
750 const nsStyleCoord &indent = StyleText()->mTextIndent;
751 if (indent.ConvertsToLength())
752 data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
754 // XXX Bug NNNNNN Should probably handle percentage text-indent.
756 data.line = &line;
757 data.lineContainer = curFrame;
758 nsIFrame *kid = line->mFirstChild;
759 for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
760 ++i, kid = kid->GetNextSibling()) {
761 kid->AddInlinePrefWidth(aRenderingContext, &data);
764 #ifdef DEBUG
765 if (gNoisyIntrinsic) {
766 IndentBy(stdout, gNoiseIndent);
767 printf("pref: [prevLines=%d currentLine=%d]\n",
768 data.prevLines, data.currentLine);
770 #endif
773 data.ForceBreak(aRenderingContext);
775 mPrefWidth = data.prevLines;
776 return mPrefWidth;
779 nsRect
780 nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const
782 // be conservative
783 if (StyleContext()->HasTextDecorationLines()) {
784 return GetVisualOverflowRect();
786 return ComputeSimpleTightBounds(aContext);
789 static bool
790 AvailableSpaceShrunk(const nsRect& aOldAvailableSpace,
791 const nsRect& aNewAvailableSpace)
793 if (aNewAvailableSpace.width == 0) {
794 // Positions are not significant if the width is zero.
795 return aOldAvailableSpace.width != 0;
797 NS_ASSERTION(aOldAvailableSpace.x <= aNewAvailableSpace.x &&
798 aOldAvailableSpace.XMost() >= aNewAvailableSpace.XMost(),
799 "available space should never grow");
800 return aOldAvailableSpace.width != aNewAvailableSpace.width;
803 static nsSize
804 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
805 nsSize aFrameSize)
807 // The issue here is that for a 'height' of 'auto' the reflow state
808 // code won't know how to calculate the containing block height
809 // because it's calculated bottom up. So we use our own computed
810 // size as the dimensions.
811 nsIFrame* frame = aReflowState.frame;
813 nsSize cbSize(aFrameSize);
814 // Containing block is relative to the padding edge
815 const nsMargin& border =
816 aReflowState.mComputedBorderPadding - aReflowState.mComputedPadding;
817 cbSize.width -= border.LeftRight();
818 cbSize.height -= border.TopBottom();
820 if (frame->GetParent()->GetContent() == frame->GetContent() &&
821 frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) {
822 // We are a wrapped frame for the content (and the wrapper is not the
823 // canvas frame, whose size is not meaningful here).
824 // Use the container's dimensions, if they have been precomputed.
825 // XXX This is a hack! We really should be waiting until the outermost
826 // frame is fully reflowed and using the resulting dimensions, even
827 // if they're intrinsic.
828 // In fact we should be attaching absolute children to the outermost
829 // frame and not always sticking them in block frames.
831 // First, find the reflow state for the outermost frame for this
832 // content.
833 const nsHTMLReflowState* aLastRS = &aReflowState;
834 const nsHTMLReflowState* lastButOneRS = &aReflowState;
835 while (aLastRS->parentReflowState &&
836 aLastRS->parentReflowState->frame->GetContent() == frame->GetContent()) {
837 lastButOneRS = aLastRS;
838 aLastRS = aLastRS->parentReflowState;
840 if (aLastRS != &aReflowState) {
841 // Scrollbars need to be specifically excluded, if present, because they are outside the
842 // padding-edge. We need better APIs for getting the various boxes from a frame.
843 nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRS->frame);
844 nsMargin scrollbars(0,0,0,0);
845 if (scrollFrame) {
846 scrollbars =
847 scrollFrame->GetDesiredScrollbarSizes(aLastRS->frame->PresContext(),
848 aLastRS->rendContext);
849 if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
850 scrollbars.top = scrollbars.bottom = 0;
852 if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
853 scrollbars.left = scrollbars.right = 0;
856 // We found a reflow state for the outermost wrapping frame, so use
857 // its computed metrics if available
858 if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
859 cbSize.width = std::max(0,
860 aLastRS->ComputedWidth() + aLastRS->mComputedPadding.LeftRight() - scrollbars.LeftRight());
862 if (aLastRS->ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
863 cbSize.height = std::max(0,
864 aLastRS->ComputedHeight() + aLastRS->mComputedPadding.TopBottom() - scrollbars.TopBottom());
869 return cbSize;
872 NS_IMETHODIMP
873 nsBlockFrame::Reflow(nsPresContext* aPresContext,
874 nsHTMLReflowMetrics& aMetrics,
875 const nsHTMLReflowState& aReflowState,
876 nsReflowStatus& aStatus)
878 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
879 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
880 #ifdef DEBUG
881 if (gNoisyReflow) {
882 IndentBy(stdout, gNoiseIndent);
883 ListTag(stdout);
884 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
885 aReflowState.availableWidth, aReflowState.availableHeight,
886 aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
888 AutoNoisyIndenter indent(gNoisy);
889 PRTime start = 0; // Initialize these variablies to silence the compiler.
890 int32_t ctc = 0; // We only use these if they are set (gLameReflowMetrics).
891 if (gLameReflowMetrics) {
892 start = PR_Now();
893 ctc = nsLineBox::GetCtorCount();
895 #endif
897 const nsHTMLReflowState *reflowState = &aReflowState;
898 nscoord consumedHeight = GetConsumedHeight();
899 nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState,
900 consumedHeight);
901 Maybe<nsHTMLReflowState> mutableReflowState;
902 // If we have non-auto height, we're clipping our kids and we fit,
903 // make sure our kids fit too.
904 if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
905 aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
906 ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
907 nsMargin heightExtras = aReflowState.mComputedBorderPadding;
908 if (GetSkipSides() & NS_SIDE_TOP) {
909 heightExtras.top = 0;
910 } else {
911 // Bottom margin never causes us to create continuations, so we
912 // don't need to worry about whether it fits in its entirety.
913 heightExtras.top += aReflowState.mComputedMargin.top;
916 if (effectiveComputedHeight + heightExtras.TopBottom() <=
917 aReflowState.availableHeight) {
918 mutableReflowState.construct(aReflowState);
919 mutableReflowState.ref().availableHeight = NS_UNCONSTRAINEDSIZE;
920 reflowState = mutableReflowState.addr();
924 // See comment below about oldSize. Use *only* for the
925 // abs-pos-containing-block-size-change optimization!
926 nsSize oldSize = GetSize();
928 // Should we create a float manager?
929 nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState));
931 // XXXldb If we start storing the float manager in the frame rather
932 // than keeping it around only during reflow then we should create it
933 // only when there are actually floats to manage. Otherwise things
934 // like tables will gain significant bloat.
935 bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
936 if (needFloatManager)
937 autoFloatManager.CreateFloatManager(aPresContext);
939 // OK, some lines may be reflowed. Blow away any saved line cursor
940 // because we may invalidate the nondecreasing
941 // overflowArea.VisualOverflow().y/yMost invariant, and we may even
942 // delete the line with the line cursor.
943 ClearLineCursor();
945 if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
946 return NS_OK;
949 bool topMarginRoot, bottomMarginRoot;
950 IsMarginRoot(&topMarginRoot, &bottomMarginRoot);
952 // Cache the consumed height in the block reflow state so that we don't have
953 // to continually recompute it.
954 nsBlockReflowState state(*reflowState, aPresContext, this,
955 topMarginRoot, bottomMarginRoot, needFloatManager,
956 consumedHeight);
958 #ifdef IBMBIDI
959 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
960 static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
961 #endif // IBMBIDI
963 if (RenumberLists(aPresContext)) {
964 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
967 nsresult rv = NS_OK;
969 // ALWAYS drain overflow. We never want to leave the previnflow's
970 // overflow lines hanging around; block reflow depends on the
971 // overflow line lists being cleared out between reflow passes.
972 DrainOverflowLines();
974 // Handle paginated overflow (see nsContainerFrame.h)
975 nsOverflowAreas ocBounds;
976 nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
977 if (GetPrevInFlow()) {
978 ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0,
979 ocStatus);
982 // Now that we're done cleaning up our overflow container lists, we can
983 // give |state| its nsOverflowContinuationTracker.
984 nsOverflowContinuationTracker tracker(aPresContext, this, false);
985 state.mOverflowTracker = &tracker;
987 // Drain & handle pushed floats
988 DrainPushedFloats(state);
989 nsOverflowAreas fcBounds;
990 nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
991 ReflowPushedFloats(state, fcBounds, fcStatus);
993 // If we're not dirty (which means we'll mark everything dirty later)
994 // and our width has changed, mark the lines dirty that we need to
995 // mark dirty for a resize reflow.
996 if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowState->mFlags.mHResize) {
997 PrepareResizeReflow(state);
1000 LazyMarkLinesDirty();
1002 mState &= ~NS_FRAME_FIRST_REFLOW;
1004 // Now reflow...
1005 rv = ReflowDirtyLines(state);
1007 // If we have a next-in-flow, and that next-in-flow has pushed floats from
1008 // this frame from a previous iteration of reflow, then we should not return
1009 // a status of NS_FRAME_COMPLETE, since we actually have overflow, it's just
1010 // already been handled.
1012 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
1013 // and reflow them, but just in case it does, this is a safety precaution so
1014 // we don't end up with a placeholder pointing to frames that have already
1015 // been deleted as part of removing our next-in-flow.
1016 if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
1017 nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
1018 while (nif) {
1019 if (nif->HasPushedFloatsFromPrevContinuation()) {
1020 NS_MergeReflowStatusInto(&state.mReflowStatus, NS_FRAME_NOT_COMPLETE);
1023 nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
1027 NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
1028 if (NS_FAILED(rv)) return rv;
1030 NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
1031 NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
1033 // If we end in a BR with clear and affected floats continue,
1034 // we need to continue, too.
1035 if (NS_UNCONSTRAINEDSIZE != reflowState->availableHeight &&
1036 NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
1037 state.mFloatManager->ClearContinues(FindTrailingClear())) {
1038 NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
1041 if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
1042 if (HasOverflowLines() || HasPushedFloats()) {
1043 state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1046 #ifdef DEBUG_kipp
1047 ListTag(stdout); printf(": block is not fully complete\n");
1048 #endif
1051 // Place the "marker" (bullet) frame if it is placed next to a block
1052 // child.
1054 // According to the CSS2 spec, section 12.6.1, the "marker" box
1055 // participates in the height calculation of the list-item box's
1056 // first line box.
1058 // There are exactly two places a bullet can be placed: near the
1059 // first or second line. It's only placed on the second line in a
1060 // rare case: an empty first line followed by a second line that
1061 // contains a block (example: <LI>\n<P>... ). This is where
1062 // the second case can happen.
1063 if (HasOutsideBullet() && !mLines.empty() &&
1064 (mLines.front()->IsBlock() ||
1065 (0 == mLines.front()->mBounds.height &&
1066 mLines.front() != mLines.back() &&
1067 mLines.begin().next()->IsBlock()))) {
1068 // Reflow the bullet
1069 nsHTMLReflowMetrics metrics;
1070 // XXX Use the entire line when we fix bug 25888.
1071 nsLayoutUtils::LinePosition position;
1072 bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
1073 nscoord lineTop = havePosition ? position.mTop
1074 : reflowState->mComputedBorderPadding.top;
1075 nsIFrame* bullet = GetOutsideBullet();
1076 ReflowBullet(bullet, state, metrics, lineTop);
1077 NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
1078 "empty bullet took up space");
1080 if (havePosition && !BulletIsEmpty()) {
1081 // We have some lines to align the bullet with.
1083 // Doing the alignment using the baseline will also cater for
1084 // bullets that are placed next to a child block (bug 92896)
1086 // Tall bullets won't look particularly nice here...
1087 nsRect bbox = bullet->GetRect();
1088 bbox.y = position.mBaseline - metrics.ascent;
1089 bullet->SetRect(bbox);
1091 // Otherwise just leave the bullet where it is, up against our top padding.
1094 CheckFloats(state);
1096 // Compute our final size
1097 nscoord bottomEdgeOfChildren;
1098 ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren);
1099 nsRect areaBounds = nsRect(0, 0, aMetrics.width, aMetrics.height);
1100 ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay,
1101 bottomEdgeOfChildren, aMetrics.mOverflowAreas);
1102 // Factor overflow container child bounds into the overflow area
1103 aMetrics.mOverflowAreas.UnionWith(ocBounds);
1104 // Factor pushed float child bounds into the overflow area
1105 aMetrics.mOverflowAreas.UnionWith(fcBounds);
1107 // Let the absolutely positioned container reflow any absolutely positioned
1108 // child frames that need to be reflowed, e.g., elements with a percentage
1109 // based width/height
1110 // We want to do this under either of two conditions:
1111 // 1. If we didn't do the incremental reflow above.
1112 // 2. If our size changed.
1113 // Even though it's the padding edge that's the containing block, we
1114 // can use our rect (the border edge) since if the border style
1115 // changed, the reflow would have been targeted at us so we'd satisfy
1116 // condition 1.
1117 // XXX checking oldSize is bogus, there are various reasons we might have
1118 // reflowed but our size might not have been changed to what we
1119 // asked for (e.g., we ended up being pushed to a new page)
1120 // When WillReflowAgainForClearance is true, we will reflow again without
1121 // resetting the size. Because of this, we must not reflow our abs-pos children
1122 // in that situation --- what we think is our "new size"
1123 // will not be our real new size. This also happens to be more efficient.
1124 if (HasAbsolutelyPositionedChildren()) {
1125 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
1126 bool haveInterrupt = aPresContext->HasPendingInterrupt();
1127 if (reflowState->WillReflowAgainForClearance() ||
1128 haveInterrupt) {
1129 // Make sure that when we reflow again we'll actually reflow all the abs
1130 // pos frames that might conceivably depend on our size (or all of them,
1131 // if we're dirty right now and interrupted; in that case we also need
1132 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1133 // better than that, because we don't really know what our size will be,
1134 // and it might in fact not change on the followup reflow!
1135 if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
1136 absoluteContainer->MarkAllFramesDirty();
1137 } else {
1138 absoluteContainer->MarkSizeDependentFramesDirty();
1140 } else {
1141 nsSize containingBlockSize =
1142 CalculateContainingBlockSizeForAbsolutes(*reflowState,
1143 nsSize(aMetrics.width,
1144 aMetrics.height));
1146 // Mark frames that depend on changes we just made to this frame as dirty:
1147 // Now we can assume that the padding edge hasn't moved.
1148 // We need to reflow the absolutes if one of them depends on
1149 // its placeholder position, or the containing block size in a
1150 // direction in which the containing block size might have
1151 // changed.
1152 bool cbWidthChanged = aMetrics.width != oldSize.width;
1153 bool isRoot = !GetContent()->GetParent();
1154 // If isRoot and we have auto height, then we are the initial
1155 // containing block and the containing block height is the
1156 // viewport height, which can't change during incremental
1157 // reflow.
1158 bool cbHeightChanged =
1159 !(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
1160 aMetrics.height != oldSize.height;
1162 nsRect containingBlock(nsPoint(0, 0), containingBlockSize);
1163 absoluteContainer->Reflow(this, aPresContext, *reflowState,
1164 state.mReflowStatus,
1165 containingBlock, true,
1166 cbWidthChanged, cbHeightChanged,
1167 &aMetrics.mOverflowAreas);
1169 //XXXfr Why isn't this rv (and others in this file) checked/returned?
1173 FinishAndStoreOverflow(&aMetrics);
1175 // Clear the float manager pointer in the block reflow state so we
1176 // don't waste time translating the coordinate system back on a dead
1177 // float manager.
1178 if (needFloatManager)
1179 state.mFloatManager = nullptr;
1181 aStatus = state.mReflowStatus;
1183 #ifdef DEBUG
1184 // Between when we drain pushed floats and when we complete reflow,
1185 // we're allowed to have multiple continuations of the same float on
1186 // our floats list, since a first-in-flow might get pushed to a later
1187 // continuation of its containing block. But it's not permitted
1188 // outside that time.
1189 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
1191 if (gNoisyReflow) {
1192 IndentBy(stdout, gNoiseIndent);
1193 ListTag(stdout);
1194 printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
1195 aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
1196 aMetrics.width, aMetrics.height,
1197 aMetrics.mCarriedOutBottomMargin.get());
1198 if (HasOverflowAreas()) {
1199 printf(" overflow-vis={%d,%d,%d,%d}",
1200 aMetrics.VisualOverflow().x,
1201 aMetrics.VisualOverflow().y,
1202 aMetrics.VisualOverflow().width,
1203 aMetrics.VisualOverflow().height);
1204 printf(" overflow-scr={%d,%d,%d,%d}",
1205 aMetrics.ScrollableOverflow().x,
1206 aMetrics.ScrollableOverflow().y,
1207 aMetrics.ScrollableOverflow().width,
1208 aMetrics.ScrollableOverflow().height);
1210 printf("\n");
1213 if (gLameReflowMetrics) {
1214 PRTime end = PR_Now();
1216 int32_t ectc = nsLineBox::GetCtorCount();
1217 int32_t numLines = mLines.size();
1218 if (!numLines) numLines = 1;
1219 PRTime delta, perLineDelta, lines;
1220 lines = int64_t(numLines);
1221 delta = end - start;
1222 perLineDelta = delta / lines;
1224 ListTag(stdout);
1225 char buf[400];
1226 PR_snprintf(buf, sizeof(buf),
1227 ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
1228 delta, perLineDelta, numLines, ectc - ctc);
1229 printf("%s\n", buf);
1231 #endif
1233 NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics);
1234 return rv;
1237 bool
1238 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
1240 line_iterator begin = begin_lines();
1241 line_iterator line = end_lines();
1243 while (true) {
1244 if (begin == line) {
1245 return false;
1247 --line;
1248 if (line->mBounds.height != 0 || !line->CachedIsEmpty()) {
1249 return false;
1251 if (line->HasClearance()) {
1252 return true;
1255 // not reached
1258 void
1259 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
1260 nsBlockReflowState& aState,
1261 nsHTMLReflowMetrics& aMetrics,
1262 nscoord* aBottomEdgeOfChildren)
1264 const nsMargin& borderPadding = aState.BorderPadding();
1265 #ifdef NOISY_FINAL_SIZE
1266 ListTag(stdout);
1267 printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
1268 aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
1269 aState.mPrevBottomMargin,
1270 borderPadding.top, borderPadding.bottom);
1271 #endif
1273 // Compute final width
1274 aMetrics.width =
1275 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.left,
1276 aReflowState.ComputedWidth()),
1277 borderPadding.right);
1279 // Return bottom margin information
1280 // rbs says he hit this assertion occasionally (see bug 86947), so
1281 // just set the margin to zero and we'll figure out why later
1282 //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
1283 // "someone else set the margin");
1284 nscoord nonCarriedOutVerticalMargin = 0;
1285 if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
1286 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1287 // line with clearance and a non-zero top margin and all
1288 // subsequent lines are empty, then we do not allow our children's
1289 // carried out bottom margin to be carried out of us and collapse
1290 // with our own bottom margin.
1291 if (CheckForCollapsedBottomMarginFromClearanceLine()) {
1292 // Convert the children's carried out margin to something that
1293 // we will include in our height
1294 nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
1295 aState.mPrevBottomMargin.Zero();
1297 aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
1298 } else {
1299 aMetrics.mCarriedOutBottomMargin.Zero();
1302 nscoord bottomEdgeOfChildren = aState.mY + nonCarriedOutVerticalMargin;
1303 // Shrink wrap our height around our contents.
1304 if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ||
1305 NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
1306 // When we are a bottom-margin root make sure that our last
1307 // childs bottom margin is fully applied. We also do this when
1308 // we have a computed height, since in that case the carried out
1309 // margin is not going to be applied anywhere, so we should note it
1310 // here to be included in the overflow area.
1311 // Apply the margin only if there's space for it.
1312 if (bottomEdgeOfChildren < aState.mReflowState.availableHeight)
1314 // Truncate bottom margin if it doesn't fit to our available height.
1315 bottomEdgeOfChildren =
1316 std::min(bottomEdgeOfChildren + aState.mPrevBottomMargin.get(),
1317 aState.mReflowState.availableHeight);
1320 if (aState.GetFlag(BRS_FLOAT_MGR)) {
1321 // Include the float manager's state to properly account for the
1322 // bottom margin of any floated elements; e.g., inside a table cell.
1323 nscoord floatHeight =
1324 aState.ClearFloats(bottomEdgeOfChildren, NS_STYLE_CLEAR_LEFT_AND_RIGHT,
1325 nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
1326 bottomEdgeOfChildren = std::max(bottomEdgeOfChildren, floatHeight);
1329 if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()
1330 && (mParent->GetType() != nsGkAtoms::columnSetFrame ||
1331 aReflowState.parentReflowState->availableHeight == NS_UNCONSTRAINEDSIZE)) {
1332 ComputeFinalHeight(aReflowState, &aState.mReflowStatus,
1333 aState.mY + nonCarriedOutVerticalMargin,
1334 borderPadding, aMetrics, aState.mConsumedHeight);
1335 if (!NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1336 // Use the current height; continuations will take up the rest.
1337 // Do extend the height to at least consume the available
1338 // height, otherwise our left/right borders (for example) won't
1339 // extend all the way to the break.
1340 aMetrics.height = std::max(aReflowState.availableHeight,
1341 aState.mY + nonCarriedOutVerticalMargin);
1342 // ... but don't take up more height than is available
1343 nscoord effectiveComputedHeight =
1344 GetEffectiveComputedHeight(aReflowState, aState.GetConsumedHeight());
1345 aMetrics.height = std::min(aMetrics.height,
1346 borderPadding.top + effectiveComputedHeight);
1347 // XXX It's pretty wrong that our bottom border still gets drawn on
1348 // on its own on the last-in-flow, even if we ran out of height
1349 // here. We need GetSkipSides to check whether we ran out of content
1350 // height in the current frame, not whether it's last-in-flow.
1353 // Don't carry out a bottom margin when our height is fixed.
1354 aMetrics.mCarriedOutBottomMargin.Zero();
1356 else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1357 nscoord contentHeight = bottomEdgeOfChildren - borderPadding.top;
1358 nscoord autoHeight = aReflowState.ApplyMinMaxHeight(contentHeight);
1359 if (autoHeight != contentHeight) {
1360 // Our min-height or max-height made our height change. Don't carry out
1361 // our kids' bottom margins.
1362 aMetrics.mCarriedOutBottomMargin.Zero();
1364 autoHeight += borderPadding.top + borderPadding.bottom;
1365 aMetrics.height = autoHeight;
1367 else {
1368 NS_ASSERTION(aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE,
1369 "Shouldn't be incomplete if availableHeight is UNCONSTRAINED.");
1370 aMetrics.height = std::max(aState.mY, aReflowState.availableHeight);
1371 if (aReflowState.availableHeight == NS_UNCONSTRAINEDSIZE)
1372 // This should never happen, but it does. See bug 414255
1373 aMetrics.height = aState.mY;
1376 if (IS_TRUE_OVERFLOW_CONTAINER(this) &&
1377 NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
1378 // Overflow containers can only be overflow complete.
1379 // Note that auto height overflow containers have no normal children
1380 NS_ASSERTION(aMetrics.height == 0, "overflow containers must be zero-height");
1381 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
1384 // Screen out negative heights --- can happen due to integer overflows :-(
1385 aMetrics.height = std::max(0, aMetrics.height);
1386 *aBottomEdgeOfChildren = bottomEdgeOfChildren;
1388 #ifdef DEBUG_blocks
1389 if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
1390 ListTag(stdout);
1391 printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
1393 #endif
1396 void
1397 nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds,
1398 const nsStyleDisplay* aDisplay,
1399 nscoord aBottomEdgeOfChildren,
1400 nsOverflowAreas& aOverflowAreas)
1402 // Compute the overflow areas of our children
1403 // XXX_perf: This can be done incrementally. It is currently one of
1404 // the things that makes incremental reflow O(N^2).
1405 nsOverflowAreas areas(aBounds, aBounds);
1406 if (!ShouldApplyOverflowClipping(this, aDisplay)) {
1407 for (line_iterator line = begin_lines(), line_end = end_lines();
1408 line != line_end;
1409 ++line) {
1410 areas.UnionWith(line->GetOverflowAreas());
1413 // Factor an outside bullet in; normally the bullet will be factored into
1414 // the line-box's overflow areas. However, if the line is a block
1415 // line then it won't; if there are no lines, it won't. So just
1416 // factor it in anyway (it can't hurt if it was already done).
1417 // XXXldb Can we just fix GetOverflowArea instead?
1418 nsIFrame* outsideBullet = GetOutsideBullet();
1419 if (outsideBullet) {
1420 areas.UnionAllWith(outsideBullet->GetRect());
1423 // Factor in the bottom edge of the children. Child frames will be added
1424 // to the overflow area as we iterate through the lines, but their margins
1425 // won't, so we need to account for bottom margins here.
1426 // REVIEW: For now, we do this for both visual and scrollable area,
1427 // although when we make scrollable overflow area not be a subset of
1428 // visual, we can change this.
1429 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
1430 nsRect& o = areas.Overflow(otype);
1431 o.height = std::max(o.YMost(), aBottomEdgeOfChildren) - o.y;
1434 #ifdef NOISY_COMBINED_AREA
1435 ListTag(stdout);
1436 printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height);
1437 #endif
1439 aOverflowAreas = areas;
1442 bool
1443 nsBlockFrame::UpdateOverflow()
1445 // We need to update the overflow areas of lines manually, as they
1446 // get cached and re-used otherwise. Lines aren't exposed as normal
1447 // frame children, so calling UnionChildOverflow alone will end up
1448 // using the old cached values.
1449 for (line_iterator line = begin_lines(), line_end = end_lines();
1450 line != line_end;
1451 ++line) {
1452 nsOverflowAreas lineAreas;
1454 int32_t n = line->GetChildCount();
1455 for (nsIFrame* lineFrame = line->mFirstChild;
1456 n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
1457 ConsiderChildOverflow(lineAreas, lineFrame);
1460 // Consider the overflow areas of the floats attached to the line as well
1461 if (line->HasFloats()) {
1462 for (nsFloatCache* fc = line->GetFirstFloat(); fc; fc = fc->Next()) {
1463 ConsiderChildOverflow(lineAreas, fc->mFloat);
1467 line->SetOverflowAreas(lineAreas);
1470 // Line cursor invariants depend on the overflow areas of the lines, so
1471 // we must clear the line cursor since those areas may have changed.
1472 ClearLineCursor();
1474 return nsBlockFrameSuper::UpdateOverflow();
1477 void
1478 nsBlockFrame::LazyMarkLinesDirty()
1480 if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) {
1481 for (line_iterator line = begin_lines(), line_end = end_lines();
1482 line != line_end; ++line) {
1483 int32_t n = line->GetChildCount();
1484 for (nsIFrame* lineFrame = line->mFirstChild;
1485 n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
1486 if (NS_SUBTREE_DIRTY(lineFrame)) {
1487 // NOTE: MarkLineDirty does more than just marking the line dirty.
1488 MarkLineDirty(line, &mLines);
1489 break;
1493 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
1497 void
1498 nsBlockFrame::MarkLineDirty(line_iterator aLine, const nsLineList* aLineList)
1500 // Mark aLine dirty
1501 aLine->MarkDirty();
1502 aLine->SetInvalidateTextRuns(true);
1503 #ifdef DEBUG
1504 if (gNoisyReflow) {
1505 IndentBy(stdout, gNoiseIndent);
1506 ListTag(stdout);
1507 printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
1509 #endif
1511 // Mark previous line dirty if it's an inline line so that it can
1512 // maybe pullup something from the line just affected.
1513 // XXX We don't need to do this if aPrevLine ends in a break-after...
1514 if (aLine != aLineList->front() && aLine->IsInline() &&
1515 aLine.prev()->IsInline()) {
1516 aLine.prev()->MarkDirty();
1517 aLine.prev()->SetInvalidateTextRuns(true);
1518 #ifdef DEBUG
1519 if (gNoisyReflow) {
1520 IndentBy(stdout, gNoiseIndent);
1521 ListTag(stdout);
1522 printf(": mark prev-line %p dirty\n",
1523 static_cast<void*>(aLine.prev().get()));
1525 #endif
1530 * Test whether lines are certain to be aligned left so that we can make
1531 * resizing optimizations
1533 static inline bool
1534 IsAlignedLeft(uint8_t aAlignment,
1535 uint8_t aDirection,
1536 uint8_t aUnicodeBidi,
1537 nsIFrame* aFrame)
1539 return aFrame->IsSVGText() ||
1540 NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
1541 (((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment &&
1542 NS_STYLE_DIRECTION_LTR == aDirection) ||
1543 (NS_STYLE_TEXT_ALIGN_END == aAlignment &&
1544 NS_STYLE_DIRECTION_RTL == aDirection)) &&
1545 !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
1548 void
1549 nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
1551 const nsStyleText* styleText = StyleText();
1552 const nsStyleTextReset* styleTextReset = StyleTextReset();
1553 // See if we can try and avoid marking all the lines as dirty
1554 bool tryAndSkipLines =
1555 // The block must be LTR (bug 806284)
1556 StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR &&
1557 // The text must be left-aligned.
1558 IsAlignedLeft(styleText->mTextAlign,
1559 aState.mReflowState.mStyleVisibility->mDirection,
1560 styleTextReset->mUnicodeBidi,
1561 this) &&
1562 // The left content-edge must be a constant distance from the left
1563 // border-edge.
1564 !StylePadding()->mPadding.GetLeft().HasPercent();
1566 #ifdef DEBUG
1567 if (gDisableResizeOpt) {
1568 tryAndSkipLines = false;
1570 if (gNoisyReflow) {
1571 if (!tryAndSkipLines) {
1572 IndentBy(stdout, gNoiseIndent);
1573 ListTag(stdout);
1574 printf(": marking all lines dirty: availWidth=%d textAlign=%d\n",
1575 aState.mReflowState.availableWidth,
1576 styleText->mTextAlign);
1579 #endif
1581 if (tryAndSkipLines) {
1582 nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left +
1583 aState.mReflowState.ComputedWidth();
1584 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedBorderPadding.left &&
1585 NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(),
1586 "math on NS_UNCONSTRAINEDSIZE");
1588 #ifdef DEBUG
1589 if (gNoisyReflow) {
1590 IndentBy(stdout, gNoiseIndent);
1591 ListTag(stdout);
1592 printf(": trying to avoid marking all lines dirty\n");
1594 #endif
1596 // The last line might not be aligned left even if the rest of the block is
1597 bool skipLastLine = NS_STYLE_TEXT_ALIGN_AUTO == styleText->mTextAlignLast ||
1598 IsAlignedLeft(styleText->mTextAlignLast,
1599 aState.mReflowState.mStyleVisibility->mDirection,
1600 styleTextReset->mUnicodeBidi,
1601 this);
1603 for (line_iterator line = begin_lines(), line_end = end_lines();
1604 line != line_end;
1605 ++line)
1607 // We let child blocks make their own decisions the same
1608 // way we are here.
1609 bool isLastLine = line == mLines.back() && !GetNextInFlow();
1610 if (line->IsBlock() ||
1611 line->HasFloats() ||
1612 (!isLastLine && !line->HasBreakAfter()) ||
1613 ((isLastLine || !line->IsLineWrapped()) && !skipLastLine) ||
1614 line->ResizeReflowOptimizationDisabled() ||
1615 line->IsImpactedByFloat() ||
1616 (line->mBounds.XMost() > newAvailWidth)) {
1617 line->MarkDirty();
1620 #ifdef REALLY_NOISY_REFLOW
1621 if (!line->IsBlock()) {
1622 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
1623 line.get(), line->IsImpactedByFloat() ? "" : "not ");
1625 #endif
1626 #ifdef DEBUG
1627 if (gNoisyReflow && !line->IsDirty()) {
1628 IndentBy(stdout, gNoiseIndent + 1);
1629 printf("skipped: line=%p next=%p %s %s%s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
1630 static_cast<void*>(line.get()),
1631 static_cast<void*>((line.next() != end_lines() ? line.next().get() : nullptr)),
1632 line->IsBlock() ? "block" : "inline",
1633 line->HasBreakAfter() ? "has-break-after " : "",
1634 line->HasFloats() ? "has-floats " : "",
1635 line->IsImpactedByFloat() ? "impacted " : "",
1636 skipLastLine ? "last-line-left-aligned " : "",
1637 line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
1638 line->mBounds.XMost());
1640 #endif
1643 else {
1644 // Mark everything dirty
1645 for (line_iterator line = begin_lines(), line_end = end_lines();
1646 line != line_end;
1647 ++line)
1649 line->MarkDirty();
1654 //----------------------------------------
1657 * Propagate reflow "damage" from from earlier lines to the current
1658 * line. The reflow damage comes from the following sources:
1659 * 1. The regions of float damage remembered during reflow.
1660 * 2. The combination of nonzero |aDeltaY| and any impact by a float,
1661 * either the previous reflow or now.
1663 * When entering this function, |aLine| is still at its old position and
1664 * |aDeltaY| indicates how much it will later be slid (assuming it
1665 * doesn't get marked dirty and reflowed entirely).
1667 void
1668 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
1669 nsLineBox* aLine,
1670 nscoord aDeltaY)
1672 nsFloatManager *floatManager = aState.mReflowState.mFloatManager;
1673 NS_ASSERTION((aState.mReflowState.parentReflowState &&
1674 aState.mReflowState.parentReflowState->mFloatManager == floatManager) ||
1675 aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in");
1677 // Check to see if there are any floats; if there aren't, there can't
1678 // be any float damage
1679 if (!floatManager->HasAnyFloats())
1680 return;
1682 // Check the damage region recorded in the float damage.
1683 if (floatManager->HasFloatDamage()) {
1684 // Need to check mBounds *and* mCombinedArea to find intersections
1685 // with aLine's floats
1686 nscoord lineYA = aLine->mBounds.y + aDeltaY;
1687 nscoord lineYB = lineYA + aLine->mBounds.height;
1688 // Scrollable overflow should be sufficient for things that affect
1689 // layout.
1690 nsRect overflow = aLine->GetOverflowArea(eScrollableOverflow);
1691 nscoord lineYCombinedA = overflow.y + aDeltaY;
1692 nscoord lineYCombinedB = lineYCombinedA + overflow.height;
1693 if (floatManager->IntersectsDamage(lineYA, lineYB) ||
1694 floatManager->IntersectsDamage(lineYCombinedA, lineYCombinedB)) {
1695 aLine->MarkDirty();
1696 return;
1700 // Check if the line is moving relative to the float manager
1701 if (aDeltaY + aState.mReflowState.mBlockDelta != 0) {
1702 if (aLine->IsBlock()) {
1703 // Unconditionally reflow sliding blocks; we only really need to reflow
1704 // if there's a float impacting this block, but the current float manager
1705 // makes it difficult to check that. Therefore, we let the child block
1706 // decide what it needs to reflow.
1707 aLine->MarkDirty();
1708 } else {
1709 bool wasImpactedByFloat = aLine->IsImpactedByFloat();
1710 nsFlowAreaRect floatAvailableSpace =
1711 aState.GetFloatAvailableSpaceForHeight(aLine->mBounds.y + aDeltaY,
1712 aLine->mBounds.height,
1713 nullptr);
1715 #ifdef REALLY_NOISY_REFLOW
1716 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
1717 this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
1718 #endif
1720 // Mark the line dirty if it was or is affected by a float
1721 // We actually only really need to reflow if the amount of impact
1722 // changes, but that's not straightforward to check
1723 if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
1724 aLine->MarkDirty();
1730 static bool LineHasClear(nsLineBox* aLine) {
1731 return aLine->IsBlock()
1732 ? (aLine->GetBreakTypeBefore() ||
1733 (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
1734 !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
1735 : aLine->HasFloatBreakAfter();
1740 * Reparent a whole list of floats from aOldParent to this block. The
1741 * floats might be taken from aOldParent's overflow list. They will be
1742 * removed from the list. They end up appended to our mFloats list.
1744 void
1745 nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
1746 bool aReparentSiblings) {
1747 nsFrameList list;
1748 aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
1749 if (list.NotEmpty()) {
1750 for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) {
1751 ReparentFrame(f, aOldParent, this);
1753 mFloats.AppendFrames(nullptr, list);
1757 static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine,
1758 nscoord aDeltaY, int32_t aDeltaIndent) {
1759 #ifdef DEBUG
1760 if (nsBlockFrame::gNoisyReflow) {
1761 nsRect ovis(aLine->GetVisualOverflowArea());
1762 nsRect oscr(aLine->GetScrollableOverflowArea());
1763 nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
1764 printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
1765 static_cast<void*>(aLine), aState.mY,
1766 aLine->IsDirty() ? "yes" : "no",
1767 aLine->mBounds.x, aLine->mBounds.y,
1768 aLine->mBounds.width, aLine->mBounds.height,
1769 ovis.x, ovis.y, ovis.width, ovis.height,
1770 oscr.x, oscr.y, oscr.width, oscr.height,
1771 aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount());
1773 #endif
1777 * Reflow the dirty lines
1779 nsresult
1780 nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
1782 nsresult rv = NS_OK;
1783 bool keepGoing = true;
1784 bool repositionViews = false; // should we really need this?
1785 bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE;
1786 bool willReflowAgain = false;
1788 #ifdef DEBUG
1789 if (gNoisyReflow) {
1790 IndentBy(stdout, gNoiseIndent);
1791 ListTag(stdout);
1792 printf(": reflowing dirty lines");
1793 printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth());
1795 AutoNoisyIndenter indent(gNoisyReflow);
1796 #endif
1798 bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
1799 (aState.mReflowState.mFlags.mVResize &&
1800 (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT));
1802 // Reflow our last line if our availableHeight has increased
1803 // so that we (and our last child) pull up content as necessary
1804 if (aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE
1805 && GetNextInFlow() && aState.mReflowState.availableHeight > mRect.height) {
1806 line_iterator lastLine = end_lines();
1807 if (lastLine != begin_lines()) {
1808 --lastLine;
1809 lastLine->MarkDirty();
1812 // the amount by which we will slide the current line if it is not
1813 // dirty
1814 nscoord deltaY = 0;
1816 // whether we did NOT reflow the previous line and thus we need to
1817 // recompute the carried out margin before the line if we want to
1818 // reflow it or if its previous margin is dirty
1819 bool needToRecoverState = false;
1820 // Float continuations were reflowed in ReflowPushedFloats
1821 bool reflowedFloat = mFloats.NotEmpty() &&
1822 (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
1823 bool lastLineMovedUp = false;
1824 // We save up information about BR-clearance here
1825 uint8_t inlineFloatBreakType = aState.mFloatBreakType;
1827 line_iterator line = begin_lines(), line_end = end_lines();
1829 // Reflow the lines that are already ours
1830 for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
1831 DumpLine(aState, line, deltaY, 0);
1832 #ifdef DEBUG
1833 AutoNoisyIndenter indent2(gNoisyReflow);
1834 #endif
1836 if (selfDirty)
1837 line->MarkDirty();
1839 // This really sucks, but we have to look inside any blocks that have clear
1840 // elements inside them.
1841 // XXX what can we do smarter here?
1842 if (!line->IsDirty() && line->IsBlock() &&
1843 (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
1844 line->MarkDirty();
1847 nsIFrame *replacedBlock = nullptr;
1848 if (line->IsBlock() &&
1849 !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
1850 replacedBlock = line->mFirstChild;
1853 // We have to reflow the line if it's a block whose clearance
1854 // might have changed, so detect that.
1855 if (!line->IsDirty() &&
1856 (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE ||
1857 replacedBlock)) {
1858 nscoord curY = aState.mY;
1859 // See where we would be after applying any clearance due to
1860 // BRs.
1861 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1862 curY = aState.ClearFloats(curY, inlineFloatBreakType);
1865 nscoord newY =
1866 aState.ClearFloats(curY, line->GetBreakTypeBefore(), replacedBlock);
1868 if (line->HasClearance()) {
1869 // Reflow the line if it might not have clearance anymore.
1870 if (newY == curY
1871 // aState.mY is the clearance point which should be the
1872 // top border-edge of the block frame. If sliding the
1873 // block by deltaY isn't going to put it in the predicted
1874 // position, then we'd better reflow the line.
1875 || newY != line->mBounds.y + deltaY) {
1876 line->MarkDirty();
1878 } else {
1879 // Reflow the line if the line might have clearance now.
1880 if (curY != newY) {
1881 line->MarkDirty();
1886 // We might have to reflow a line that is after a clearing BR.
1887 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1888 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
1889 if (aState.mY != line->mBounds.y + deltaY) {
1890 // SlideLine is not going to put the line where the clearance
1891 // put it. Reflow the line to be sure.
1892 line->MarkDirty();
1894 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
1897 bool previousMarginWasDirty = line->IsPreviousMarginDirty();
1898 if (previousMarginWasDirty) {
1899 // If the previous margin is dirty, reflow the current line
1900 line->MarkDirty();
1901 line->ClearPreviousMarginDirty();
1902 } else if (line->mBounds.YMost() + deltaY > aState.mBottomEdge) {
1903 // Lines that aren't dirty but get slid past our height constraint must
1904 // be reflowed.
1905 line->MarkDirty();
1908 // If we have a constrained height (i.e., breaking columns/pages),
1909 // and the distance to the bottom might have changed, then we need
1910 // to reflow any line that might have floats in it, both because the
1911 // breakpoints within those floats may have changed and because we
1912 // might have to push/pull the floats in their entirety.
1913 // FIXME: What about a deltaY or height change that forces us to
1914 // push lines? Why does that work?
1915 if (!line->IsDirty() &&
1916 aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
1917 (deltaY != 0 || aState.mReflowState.mFlags.mVResize ||
1918 aState.mReflowState.mFlags.mMustReflowPlaceholders) &&
1919 (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) {
1920 line->MarkDirty();
1923 if (!line->IsDirty()) {
1924 // See if there's any reflow damage that requires that we mark the
1925 // line dirty.
1926 PropagateFloatDamage(aState, line, deltaY);
1929 if (needToRecoverState && line->IsDirty()) {
1930 // We need to reconstruct the bottom margin only if we didn't
1931 // reflow the previous line and we do need to reflow (or repair
1932 // the top position of) the next line.
1933 aState.ReconstructMarginAbove(line);
1936 bool reflowedPrevLine = !needToRecoverState;
1937 if (needToRecoverState) {
1938 needToRecoverState = false;
1940 // Update aState.mPrevChild as if we had reflowed all of the frames in
1941 // this line.
1942 if (line->IsDirty())
1943 NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
1944 line.prev()->LastChild(), "unexpected line frames");
1945 aState.mPrevChild = line->mFirstChild->GetPrevSibling();
1948 // Now repair the line and update |aState.mY| by calling
1949 // |ReflowLine| or |SlideLine|.
1950 // If we're going to reflow everything again, then no need to reflow
1951 // the dirty line ... unless the line has floats, in which case we'd
1952 // better reflow it now to refresh its float cache, which may contain
1953 // dangling frame pointers! Ugh! This reflow of the line may be
1954 // incorrect because we skipped reflowing previous lines (e.g., floats
1955 // may be placed incorrectly), but that's OK because we'll mark the
1956 // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..."
1957 if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
1958 lastLineMovedUp = true;
1960 bool maybeReflowingForFirstTime =
1961 line->mBounds.x == 0 && line->mBounds.y == 0 &&
1962 line->mBounds.width == 0 && line->mBounds.height == 0;
1964 // Compute the dirty lines "before" YMost, after factoring in
1965 // the running deltaY value - the running value is implicit in
1966 // aState.mY.
1967 nscoord oldY = line->mBounds.y;
1968 nscoord oldYMost = line->mBounds.YMost();
1970 NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
1971 "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
1973 // Reflow the dirty line. If it's an incremental reflow, then force
1974 // it to invalidate the dirty area if necessary
1975 rv = ReflowLine(aState, line, &keepGoing);
1976 NS_ENSURE_SUCCESS(rv, rv);
1978 if (aState.mReflowState.WillReflowAgainForClearance()) {
1979 line->MarkDirty();
1980 willReflowAgain = true;
1981 // Note that once we've entered this state, every line that gets here
1982 // (e.g. because it has floats) gets marked dirty and reflowed again.
1983 // in the next pass. This is important, see above.
1986 if (line->HasFloats()) {
1987 reflowedFloat = true;
1990 if (!keepGoing) {
1991 DumpLine(aState, line, deltaY, -1);
1992 if (0 == line->GetChildCount()) {
1993 DeleteLine(aState, line, line_end);
1995 break;
1998 // Test to see whether the margin that should be carried out
1999 // to the next line (NL) might have changed. In ReflowBlockFrame
2000 // we call nextLine->MarkPreviousMarginDirty if the block's
2001 // actual carried-out bottom margin changed. So here we only
2002 // need to worry about the following effects:
2003 // 1) the line was just created, and it might now be blocking
2004 // a carried-out bottom margin from previous lines that
2005 // used to reach NL from reaching NL
2006 // 2) the line used to be empty, and is now not empty,
2007 // thus blocking a carried-out bottom margin from previous lines
2008 // that used to reach NL from reaching NL
2009 // 3) the line wasn't empty, but now is, so a carried-out
2010 // bottom margin from previous lines that didn't used to reach NL
2011 // now does
2012 // 4) the line might have changed in a way that affects NL's
2013 // ShouldApplyTopMargin decision. The three things that matter
2014 // are the line's emptiness, its adjacency to the top of the block,
2015 // and whether it has clearance (the latter only matters if the block
2016 // was and is adjacent to the top and empty).
2018 // If the line is empty now, we can't reliably tell if the line was empty
2019 // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
2020 // This means the checks in 4) are redundant; if the line is empty now
2021 // we don't need to check 4), but if the line is not empty now and we're sure
2022 // it wasn't empty before, any adjacency and clearance changes are irrelevant
2023 // to the result of nextLine->ShouldApplyTopMargin.
2024 if (line.next() != end_lines()) {
2025 bool maybeWasEmpty = oldY == line.next()->mBounds.y;
2026 bool isEmpty = line->CachedIsEmpty();
2027 if (maybeReflowingForFirstTime /*1*/ ||
2028 (isEmpty || maybeWasEmpty) /*2/3/4*/) {
2029 line.next()->MarkPreviousMarginDirty();
2030 // since it's marked dirty, nobody will care about |deltaY|
2034 // If the line was just reflowed for the first time, then its
2035 // old mBounds cannot be trusted so this deltaY computation is
2036 // bogus. But that's OK because we just did
2037 // MarkPreviousMarginDirty on the next line which will force it
2038 // to be reflowed, so this computation of deltaY will not be
2039 // used.
2040 deltaY = line->mBounds.YMost() - oldYMost;
2042 // Now do an interrupt check. We want to do this only in the case when we
2043 // actually reflow the line, so that if we get back in here we'll get
2044 // further on the reflow before interrupting.
2045 aState.mPresContext->CheckForInterrupt(this);
2046 } else {
2047 aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
2048 // Nop except for blocks (we don't create overflow container
2049 // continuations for any inlines atm), so only checking mFirstChild
2050 // is enough
2052 lastLineMovedUp = deltaY < 0;
2054 if (deltaY != 0)
2055 SlideLine(aState, line, deltaY);
2056 else
2057 repositionViews = true;
2059 NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
2060 "Possibly stale float cache here!");
2061 if (willReflowAgain && line->IsBlock()) {
2062 // If we're going to reflow everything again, and this line is a block,
2063 // then there is no need to recover float state. The line may contain
2064 // other lines with floats, but in that case RecoverStateFrom would only
2065 // add floats to the float manager. We don't need to do that because
2066 // everything's going to get reflowed again "for real". Calling
2067 // RecoverStateFrom in this situation could be lethal because the
2068 // block's descendant lines may have float caches containing dangling
2069 // frame pointers. Ugh!
2070 // If this line is inline, then we need to recover its state now
2071 // to make sure that we don't forget to move its floats by deltaY.
2072 } else {
2073 // XXX EVIL O(N^2) EVIL
2074 aState.RecoverStateFrom(line, deltaY);
2077 // Keep mY up to date in case we're propagating reflow damage
2078 // and also because our final height may depend on it. If the
2079 // line is inlines, then only update mY if the line is not
2080 // empty, because that's what PlaceLine does. (Empty blocks may
2081 // want to update mY, e.g. if they have clearance.)
2082 if (line->IsBlock() || !line->CachedIsEmpty()) {
2083 aState.mY = line->mBounds.YMost();
2086 needToRecoverState = true;
2088 if (reflowedPrevLine && !line->IsBlock() &&
2089 aState.mPresContext->HasPendingInterrupt()) {
2090 // Need to make sure to pull overflows from any prev-in-flows
2091 for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
2092 inlineKid = inlineKid->GetFirstPrincipalChild()) {
2093 inlineKid->PullOverflowsFromPrevInFlow();
2098 // Record if we need to clear floats before reflowing the next
2099 // line. Note that inlineFloatBreakType will be handled and
2100 // cleared before the next line is processed, so there is no
2101 // need to combine break types here.
2102 if (line->HasFloatBreakAfter()) {
2103 inlineFloatBreakType = line->GetBreakTypeAfter();
2106 if (LineHasClear(line.get())) {
2107 foundAnyClears = true;
2110 DumpLine(aState, line, deltaY, -1);
2112 if (aState.mPresContext->HasPendingInterrupt()) {
2113 willReflowAgain = true;
2114 // Another option here might be to leave |line| clean if
2115 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
2116 // that case the line really did reflow as it should have. Not sure
2117 // whether that would be safe, so doing this for now instead. Also not
2118 // sure whether we really want to mark all lines dirty after an
2119 // interrupt, but until we get better at propagating float damage we
2120 // really do need to do it this way; see comments inside MarkLineDirty.
2121 MarkLineDirtyForInterrupt(line);
2125 // Handle BR-clearance from the last line of the block
2126 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
2127 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
2130 if (needToRecoverState) {
2131 // Is this expensive?
2132 aState.ReconstructMarginAbove(line);
2134 // Update aState.mPrevChild as if we had reflowed all of the frames in
2135 // the last line.
2136 NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
2137 line.prev()->LastChild(), "unexpected line frames");
2138 aState.mPrevChild =
2139 line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
2142 // Should we really have to do this?
2143 if (repositionViews)
2144 nsContainerFrame::PlaceFrameView(this);
2146 // We can skip trying to pull up the next line if our height is constrained
2147 // (so we can report being incomplete) and there is no next in flow or we
2148 // were told not to or we know it will be futile, i.e.,
2149 // -- the next in flow is not changing
2150 // -- and we cannot have added more space for its first line to be
2151 // pulled up into,
2152 // -- it's an incremental reflow of a descendant
2153 // -- and we didn't reflow any floats (so the available space
2154 // didn't change)
2155 // -- my chain of next-in-flows either has no first line, or its first
2156 // line isn't dirty.
2157 bool heightConstrained =
2158 aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE;
2159 bool skipPull = willReflowAgain && heightConstrained;
2160 if (!skipPull && heightConstrained && aState.mNextInFlow &&
2161 (aState.mReflowState.mFlags.mNextInFlowUntouched &&
2162 !lastLineMovedUp &&
2163 !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
2164 !reflowedFloat)) {
2165 // We'll place lineIter at the last line of this block, so that
2166 // nsBlockInFlowLineIterator::Next() will take us to the first
2167 // line of my next-in-flow-chain. (But first, check that I
2168 // have any lines -- if I don't, just bail out of this
2169 // optimization.)
2170 line_iterator lineIter = this->end_lines();
2171 if (lineIter != this->begin_lines()) {
2172 lineIter--; // I have lines; step back from dummy iterator to last line.
2173 nsBlockInFlowLineIterator bifLineIter(this, lineIter);
2175 // Check for next-in-flow-chain's first line.
2176 // (First, see if there is such a line, and second, see if it's clean)
2177 if (!bifLineIter.Next() ||
2178 !bifLineIter.GetLine()->IsDirty()) {
2179 skipPull=true;
2184 if (skipPull && aState.mNextInFlow) {
2185 NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
2186 if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
2187 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
2188 else
2189 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
2192 if (!skipPull && aState.mNextInFlow) {
2193 // Pull data from a next-in-flow if there's still room for more
2194 // content here.
2195 while (keepGoing && aState.mNextInFlow) {
2196 // Grab first line from our next-in-flow
2197 nsBlockFrame* nextInFlow = aState.mNextInFlow;
2198 nsLineBox* pulledLine;
2199 nsFrameList pulledFrames;
2200 if (!nextInFlow->mLines.empty()) {
2201 RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames,
2202 &pulledLine, &pulledFrames);
2203 } else {
2204 // Grab an overflow line if there are any
2205 FrameLines* overflowLines = nextInFlow->GetOverflowLines();
2206 if (!overflowLines) {
2207 aState.mNextInFlow =
2208 static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
2209 continue;
2211 bool last =
2212 RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
2213 &pulledLine, &pulledFrames);
2214 if (last) {
2215 nextInFlow->DestroyOverflowLines();
2219 if (pulledFrames.IsEmpty()) {
2220 // The line is empty. Try the next one.
2221 NS_ASSERTION(pulledLine->GetChildCount() == 0 &&
2222 !pulledLine->mFirstChild, "bad empty line");
2223 nextInFlow->FreeLineBox(pulledLine);
2224 continue;
2227 if (pulledLine == nextInFlow->GetLineCursor()) {
2228 nextInFlow->ClearLineCursor();
2230 ReparentFrames(pulledFrames, nextInFlow, this);
2232 NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
2233 "Unexpected last frame");
2234 NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
2235 NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
2236 "Incorrect aState.mPrevChild before inserting line at end");
2238 // Shift pulledLine's frames into our mFrames list.
2239 mFrames.AppendFrames(nullptr, pulledFrames);
2241 // Add line to our line list, and set its last child as our new prev-child
2242 line = mLines.before_insert(end_lines(), pulledLine);
2243 aState.mPrevChild = mFrames.LastChild();
2245 // Reparent floats whose placeholders are in the line.
2246 ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
2248 DumpLine(aState, pulledLine, deltaY, 0);
2249 #ifdef DEBUG
2250 AutoNoisyIndenter indent2(gNoisyReflow);
2251 #endif
2253 if (aState.mPresContext->HasPendingInterrupt()) {
2254 MarkLineDirtyForInterrupt(line);
2255 } else {
2256 // Now reflow it and any lines that it makes during it's reflow
2257 // (we have to loop here because reflowing the line may cause a new
2258 // line to be created; see SplitLine's callers for examples of
2259 // when this happens).
2260 while (line != end_lines()) {
2261 rv = ReflowLine(aState, line, &keepGoing);
2262 NS_ENSURE_SUCCESS(rv, rv);
2264 if (aState.mReflowState.WillReflowAgainForClearance()) {
2265 line->MarkDirty();
2266 keepGoing = false;
2267 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
2268 break;
2271 DumpLine(aState, line, deltaY, -1);
2272 if (!keepGoing) {
2273 if (0 == line->GetChildCount()) {
2274 DeleteLine(aState, line, line_end);
2276 break;
2279 if (LineHasClear(line.get())) {
2280 foundAnyClears = true;
2283 if (aState.mPresContext->CheckForInterrupt(this)) {
2284 MarkLineDirtyForInterrupt(line);
2285 break;
2288 // If this is an inline frame then its time to stop
2289 ++line;
2290 aState.AdvanceToNextLine();
2295 if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
2296 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
2297 } //XXXfr shouldn't set this flag when nextinflow has no lines
2300 // Handle an odd-ball case: a list-item with no lines
2301 if (HasOutsideBullet() && mLines.empty()) {
2302 nsHTMLReflowMetrics metrics;
2303 nsIFrame* bullet = GetOutsideBullet();
2304 ReflowBullet(bullet, aState, metrics,
2305 aState.mReflowState.mComputedBorderPadding.top);
2306 NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
2307 "empty bullet took up space");
2309 if (!BulletIsEmpty()) {
2310 // There are no lines so we have to fake up some y motion so that
2311 // we end up with *some* height.
2313 if (metrics.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE &&
2314 !nsLayoutUtils::GetFirstLineBaseline(bullet, &metrics.ascent)) {
2315 metrics.ascent = metrics.height;
2318 nsRefPtr<nsFontMetrics> fm;
2319 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
2320 nsLayoutUtils::FontSizeInflationFor(this));
2321 aState.mReflowState.rendContext->SetFont(fm); // FIXME: needed?
2323 nscoord minAscent =
2324 nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight);
2325 nscoord minDescent = aState.mMinLineHeight - minAscent;
2327 aState.mY += std::max(minAscent, metrics.ascent) +
2328 std::max(minDescent, metrics.height - metrics.ascent);
2330 nscoord offset = minAscent - metrics.ascent;
2331 if (offset > 0) {
2332 bullet->SetRect(bullet->GetRect() + nsPoint(0, offset));
2337 if (foundAnyClears) {
2338 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2339 } else {
2340 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2343 #ifdef DEBUG
2344 VerifyLines(true);
2345 VerifyOverflowSituation();
2346 if (gNoisyReflow) {
2347 IndentBy(stdout, gNoiseIndent - 1);
2348 ListTag(stdout);
2349 printf(": done reflowing dirty lines (status=%x)\n",
2350 aState.mReflowStatus);
2352 #endif
2354 return rv;
2357 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
2359 nsLineList::iterator line = aBlock->begin_lines();
2360 nsLineList::iterator endLine = aBlock->end_lines();
2361 while (line != endLine) {
2362 if (line->IsBlock()) {
2363 nsIFrame* f = line->mFirstChild;
2364 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
2365 if (bf) {
2366 MarkAllDescendantLinesDirty(bf);
2369 line->MarkDirty();
2370 ++line;
2374 void
2375 nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
2377 aLine->MarkDirty();
2379 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
2380 // marked the lines that need to be marked dirty based on our
2381 // vertical resize stuff. So we'll definitely reflow all those kids;
2382 // the only question is how they should behave.
2383 if (GetStateBits() & NS_FRAME_IS_DIRTY) {
2384 // Mark all our child frames dirty so we make sure to reflow them
2385 // later.
2386 int32_t n = aLine->GetChildCount();
2387 for (nsIFrame* f = aLine->mFirstChild; n > 0;
2388 f = f->GetNextSibling(), --n) {
2389 f->AddStateBits(NS_FRAME_IS_DIRTY);
2391 // And mark all the floats whose reflows we might be skipping dirty too.
2392 if (aLine->HasFloats()) {
2393 for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
2394 fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY);
2397 } else {
2398 // Dirty all the descendant lines of block kids to handle float damage,
2399 // since our nsFloatManager will go away by the next time we're reflowing.
2400 // XXXbz Can we do something more like what PropagateFloatDamage does?
2401 // Would need to sort out the exact business with mBlockDelta for that....
2402 // This marks way too much dirty. If we ever make this better, revisit
2403 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
2404 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
2405 if (bf) {
2406 MarkAllDescendantLinesDirty(bf);
2411 void
2412 nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
2413 nsLineList::iterator aLine,
2414 nsLineList::iterator aLineEnd)
2416 NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
2417 if (0 == aLine->GetChildCount()) {
2418 NS_ASSERTION(aState.mCurrentLine == aLine,
2419 "using function more generally than designed, "
2420 "but perhaps OK now");
2421 nsLineBox* line = aLine;
2422 aLine = mLines.erase(aLine);
2423 FreeLineBox(line);
2424 // Mark the previous margin of the next line dirty since we need to
2425 // recompute its top position.
2426 if (aLine != aLineEnd)
2427 aLine->MarkPreviousMarginDirty();
2432 * Reflow a line. The line will either contain a single block frame
2433 * or contain 1 or more inline frames. aKeepReflowGoing indicates
2434 * whether or not the caller should continue to reflow more lines.
2436 nsresult
2437 nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
2438 line_iterator aLine,
2439 bool* aKeepReflowGoing)
2441 nsresult rv = NS_OK;
2443 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
2445 // Setup the line-layout for the new line
2446 aState.mCurrentLine = aLine;
2447 aLine->ClearDirty();
2448 aLine->InvalidateCachedIsEmpty();
2449 aLine->ClearHadFloatPushed();
2451 // Now that we know what kind of line we have, reflow it
2452 if (aLine->IsBlock()) {
2453 rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
2454 } else {
2455 aLine->SetLineWrapped(false);
2456 rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
2459 return rv;
2462 nsIFrame*
2463 nsBlockFrame::PullFrame(nsBlockReflowState& aState,
2464 line_iterator aLine)
2466 // First check our remaining lines.
2467 if (end_lines() != aLine.next()) {
2468 return PullFrameFrom(aLine, this, aLine.next());
2471 NS_ASSERTION(!GetOverflowLines(),
2472 "Our overflow lines should have been removed at the start of reflow");
2474 // Try each next-in-flow.
2475 nsBlockFrame* nextInFlow = aState.mNextInFlow;
2476 while (nextInFlow) {
2477 if (nextInFlow->mLines.empty()) {
2478 nextInFlow->DrainSelfOverflowList();
2480 if (!nextInFlow->mLines.empty()) {
2481 return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
2483 nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
2484 aState.mNextInFlow = nextInFlow;
2487 return nullptr;
2490 nsIFrame*
2491 nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
2492 nsBlockFrame* aFromContainer,
2493 nsLineList::iterator aFromLine)
2495 nsLineBox* fromLine = aFromLine;
2496 NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
2497 NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
2498 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
2500 NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
2501 "Disagreement about whether it's a block or not");
2503 if (fromLine->IsBlock()) {
2504 // If our line is not empty and the child in aFromLine is a block
2505 // then we cannot pull up the frame into this line. In this case
2506 // we stop pulling.
2507 return nullptr;
2509 // Take frame from fromLine
2510 nsIFrame* frame = fromLine->mFirstChild;
2511 nsIFrame* newFirstChild = frame->GetNextSibling();
2513 if (aFromContainer != this) {
2514 // The frame is being pulled from a next-in-flow; therefore we
2515 // need to add it to our sibling list.
2516 MOZ_ASSERT(aLine == mLines.back());
2517 MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
2518 "should only pull from first line");
2519 aFromContainer->mFrames.RemoveFrame(frame);
2521 // When pushing and pulling frames we need to check for whether any
2522 // views need to be reparented.
2523 ReparentFrame(frame, aFromContainer, this);
2524 mFrames.AppendFrame(nullptr, frame);
2526 // The frame might have (or contain) floats that need to be brought
2527 // over too. (pass 'false' since there are no siblings to check)
2528 ReparentFloats(frame, aFromContainer, false);
2529 } else {
2530 MOZ_ASSERT(aLine == aFromLine.prev());
2533 aLine->NoteFrameAdded(frame);
2534 fromLine->NoteFrameRemoved(frame);
2536 if (fromLine->GetChildCount() > 0) {
2537 // Mark line dirty now that we pulled a child
2538 fromLine->MarkDirty();
2539 fromLine->mFirstChild = newFirstChild;
2540 } else {
2541 // Free up the fromLine now that it's empty.
2542 // Its bounds might need to be redrawn, though.
2543 if (aFromLine.next() != aFromContainer->mLines.end()) {
2544 aFromLine.next()->MarkPreviousMarginDirty();
2546 aFromContainer->mLines.erase(aFromLine);
2547 // aFromLine is now invalid
2548 aFromContainer->FreeLineBox(fromLine);
2551 #ifdef DEBUG
2552 VerifyLines(true);
2553 VerifyOverflowSituation();
2554 #endif
2556 return frame;
2559 void
2560 nsBlockFrame::SlideLine(nsBlockReflowState& aState,
2561 nsLineBox* aLine, nscoord aDY)
2563 NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
2565 // Adjust line state
2566 aLine->SlideBy(aDY);
2568 // Adjust the frames in the line
2569 nsIFrame* kid = aLine->mFirstChild;
2570 if (!kid) {
2571 return;
2574 if (aLine->IsBlock()) {
2575 if (aDY) {
2576 kid->MovePositionBy(nsPoint(0, aDY));
2579 // Make sure the frame's view and any child views are updated
2580 nsContainerFrame::PlaceFrameView(kid);
2582 else {
2583 // Adjust the Y coordinate of the frames in the line.
2584 // Note: we need to re-position views even if aDY is 0, because
2585 // one of our parent frames may have moved and so the view's position
2586 // relative to its parent may have changed
2587 int32_t n = aLine->GetChildCount();
2588 while (--n >= 0) {
2589 if (aDY) {
2590 kid->MovePositionBy(nsPoint(0, aDY));
2592 // Make sure the frame's view and any child views are updated
2593 nsContainerFrame::PlaceFrameView(kid);
2594 kid = kid->GetNextSibling();
2599 NS_IMETHODIMP
2600 nsBlockFrame::AttributeChanged(int32_t aNameSpaceID,
2601 nsIAtom* aAttribute,
2602 int32_t aModType)
2604 nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID,
2605 aAttribute, aModType);
2607 if (NS_FAILED(rv)) {
2608 return rv;
2610 if (nsGkAtoms::start == aAttribute ||
2611 (nsGkAtoms::reversed == aAttribute && mContent->IsHTML(nsGkAtoms::ol))) {
2612 nsPresContext* presContext = PresContext();
2614 // XXX Not sure if this is necessary anymore
2615 if (RenumberLists(presContext)) {
2616 presContext->PresShell()->
2617 FrameNeedsReflow(this, nsIPresShell::eStyleChange,
2618 NS_FRAME_HAS_DIRTY_CHILDREN);
2621 else if (nsGkAtoms::value == aAttribute) {
2622 const nsStyleDisplay* styleDisplay = StyleDisplay();
2623 if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
2624 // Search for the closest ancestor that's a block frame. We
2625 // make the assumption that all related list items share a
2626 // common block parent.
2627 // XXXldb I think that's a bad assumption.
2628 nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
2630 // Tell the enclosing block frame to renumber list items within
2631 // itself
2632 if (nullptr != blockParent) {
2633 nsPresContext* presContext = PresContext();
2634 // XXX Not sure if this is necessary anymore
2635 if (blockParent->RenumberLists(presContext)) {
2636 presContext->PresShell()->
2637 FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange,
2638 NS_FRAME_HAS_DIRTY_CHILDREN);
2644 return rv;
2647 static inline bool
2648 IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
2650 if (aCoord.GetUnit() == eStyleUnit_Auto)
2651 return false;
2652 if (aCoord.IsCoordPercentCalcUnit()) {
2653 // If we evaluate the length/percent/calc at a percentage basis of
2654 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
2655 // length, percent, or combination thereof. Test > 0 so we clamp
2656 // negative calc() results to 0.
2657 return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
2658 nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
2660 NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height");
2661 return true;
2664 /* virtual */ bool
2665 nsBlockFrame::IsSelfEmpty()
2667 // Blocks which are margin-roots (including inline-blocks) cannot be treated
2668 // as empty for margin-collapsing and other purposes. They're more like
2669 // replaced elements.
2670 if (GetStateBits() & NS_BLOCK_MARGIN_ROOT)
2671 return false;
2673 const nsStylePosition* position = StylePosition();
2675 if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
2676 IsNonAutoNonZeroHeight(position->mHeight))
2677 return false;
2679 const nsStyleBorder* border = StyleBorder();
2680 const nsStylePadding* padding = StylePadding();
2681 if (border->GetComputedBorderWidth(NS_SIDE_TOP) != 0 ||
2682 border->GetComputedBorderWidth(NS_SIDE_BOTTOM) != 0 ||
2683 !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
2684 !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom())) {
2685 return false;
2688 if (HasOutsideBullet() && !BulletIsEmpty()) {
2689 return false;
2692 return true;
2695 bool
2696 nsBlockFrame::CachedIsEmpty()
2698 if (!IsSelfEmpty()) {
2699 return false;
2702 for (line_iterator line = begin_lines(), line_end = end_lines();
2703 line != line_end;
2704 ++line)
2706 if (!line->CachedIsEmpty())
2707 return false;
2710 return true;
2713 bool
2714 nsBlockFrame::IsEmpty()
2716 if (!IsSelfEmpty()) {
2717 return false;
2720 for (line_iterator line = begin_lines(), line_end = end_lines();
2721 line != line_end;
2722 ++line)
2724 if (!line->IsEmpty())
2725 return false;
2728 return true;
2731 bool
2732 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
2733 nsLineBox* aLine)
2735 if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
2736 // Apply short-circuit check to avoid searching the line list
2737 return true;
2740 if (!aState.IsAdjacentWithTop()) {
2741 // If we aren't at the top Y coordinate then something of non-zero
2742 // height must have been placed. Therefore the childs top-margin
2743 // applies.
2744 aState.SetFlag(BRS_APPLYTOPMARGIN, true);
2745 return true;
2748 // Determine if this line is "essentially" the first line
2749 line_iterator line = begin_lines();
2750 if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
2751 line = aState.mLineAdjacentToTop;
2753 while (line != aLine) {
2754 if (!line->CachedIsEmpty() || line->HasClearance()) {
2755 // A line which precedes aLine is non-empty, or has clearance,
2756 // so therefore the top margin applies.
2757 aState.SetFlag(BRS_APPLYTOPMARGIN, true);
2758 return true;
2760 // No need to apply the top margin if the line has floats. We
2761 // should collapse anyway (bug 44419)
2762 ++line;
2763 aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, true);
2764 aState.mLineAdjacentToTop = line;
2767 // The line being reflowed is "essentially" the first line in the
2768 // block. Therefore its top-margin will be collapsed by the
2769 // generational collapsing logic with its parent (us).
2770 return false;
2773 nsresult
2774 nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
2775 line_iterator aLine,
2776 bool* aKeepReflowGoing)
2778 NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
2780 nsresult rv = NS_OK;
2782 nsIFrame* frame = aLine->mFirstChild;
2783 if (!frame) {
2784 NS_ASSERTION(false, "program error - unexpected empty line");
2785 return NS_ERROR_NULL_POINTER;
2788 // Prepare the block reflow engine
2789 const nsStyleDisplay* display = frame->StyleDisplay();
2790 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
2792 uint8_t breakType = display->mBreakType;
2793 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
2794 breakType = nsLayoutUtils::CombineBreakType(breakType,
2795 aState.mFloatBreakType);
2796 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
2799 // Clear past floats before the block if the clear style is not none
2800 aLine->SetBreakTypeBefore(breakType);
2802 // See if we should apply the top margin. If the block frame being
2803 // reflowed is a continuation (non-null prev-in-flow) then we don't
2804 // apply its top margin because it's not significant. Otherwise, dig
2805 // deeper.
2806 bool applyTopMargin =
2807 !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
2809 if (applyTopMargin) {
2810 // The HasClearance setting is only valid if ShouldApplyTopMargin
2811 // returned false (in which case the top-margin-root set our
2812 // clearance flag). Otherwise clear it now. We'll set it later on
2813 // ourselves if necessary.
2814 aLine->ClearHasClearance();
2816 bool treatWithClearance = aLine->HasClearance();
2818 bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE;
2819 nsIFrame *replacedBlock = nullptr;
2820 if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
2821 mightClearFloats = true;
2822 replacedBlock = frame;
2825 // If our top margin was counted as part of some parents top-margin
2826 // collapse and we are being speculatively reflowed assuming this
2827 // frame DID NOT need clearance, then we need to check that
2828 // assumption.
2829 if (!treatWithClearance && !applyTopMargin && mightClearFloats &&
2830 aState.mReflowState.mDiscoveredClearance) {
2831 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
2832 nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
2833 if (clearY != curY) {
2834 // Looks like that assumption was invalid, we do need
2835 // clearance. Tell our ancestor so it can reflow again. It is
2836 // responsible for actually setting our clearance flag before
2837 // the next reflow.
2838 treatWithClearance = true;
2839 // Only record the first frame that requires clearance
2840 if (!*aState.mReflowState.mDiscoveredClearance) {
2841 *aState.mReflowState.mDiscoveredClearance = frame;
2843 aState.mPrevChild = frame;
2844 // Exactly what we do now is flexible since we'll definitely be
2845 // reflowed.
2846 return NS_OK;
2849 if (treatWithClearance) {
2850 applyTopMargin = true;
2853 nsIFrame* clearanceFrame = nullptr;
2854 nscoord startingY = aState.mY;
2855 nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
2856 nscoord clearance;
2857 // Save the original position of the frame so that we can reposition
2858 // its view as needed.
2859 nsPoint originalPosition = frame->GetPosition();
2860 while (true) {
2861 clearance = 0;
2862 nscoord topMargin = 0;
2863 bool mayNeedRetry = false;
2864 bool clearedFloats = false;
2865 if (applyTopMargin) {
2866 // Precompute the blocks top margin value so that we can get the
2867 // correct available space (there might be a float that's
2868 // already been placed below the aState.mPrevBottomMargin
2870 // Setup a reflowState to get the style computed margin-top
2871 // value. We'll use a reason of `resize' so that we don't fudge
2872 // any incremental reflow state.
2874 // The availSpace here is irrelevant to our needs - all we want
2875 // out if this setup is the margin-top value which doesn't depend
2876 // on the childs available space.
2877 // XXX building a complete nsHTMLReflowState just to get the margin-top
2878 // seems like a waste. And we do this for almost every block!
2879 nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
2880 nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
2881 frame, availSpace);
2883 if (treatWithClearance) {
2884 aState.mY += aState.mPrevBottomMargin.get();
2885 aState.mPrevBottomMargin.Zero();
2888 // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
2889 // that all child margins collapse down to clearanceFrame.
2890 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
2891 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
2893 // XXX optimization; we could check the collapsing children to see if they are sure
2894 // to require clearance, and so avoid retrying them
2896 if (clearanceFrame) {
2897 // Don't allow retries on the second pass. The clearance decisions for the
2898 // blocks whose top-margins collapse with ours are now fixed.
2899 mayNeedRetry = false;
2902 if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
2903 // We don't know if we need clearance and this is the first,
2904 // optimistic pass. So determine whether *this block* needs
2905 // clearance. Note that we do not allow the decision for whether
2906 // this block has clearance to change on the second pass; that
2907 // decision is only allowed to be made under the optimistic
2908 // first pass.
2909 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
2910 nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
2911 if (clearY != curY) {
2912 // Looks like we need clearance and we didn't know about it already. So
2913 // recompute collapsed margin
2914 treatWithClearance = true;
2915 // Remember this decision, needed for incremental reflow
2916 aLine->SetHasClearance();
2918 // Apply incoming margins
2919 aState.mY += aState.mPrevBottomMargin.get();
2920 aState.mPrevBottomMargin.Zero();
2922 // Compute the collapsed margin again, ignoring the incoming margin this time
2923 mayNeedRetry = false;
2924 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
2925 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
2929 // Temporarily advance the running Y value so that the
2930 // GetAvailableSpace method will return the right available
2931 // space. This undone as soon as the horizontal margins are
2932 // computed.
2933 topMargin = aState.mPrevBottomMargin.get();
2935 if (treatWithClearance) {
2936 nscoord currentY = aState.mY;
2937 // advance mY to the clear position.
2938 aState.mY = aState.ClearFloats(aState.mY, breakType, replacedBlock);
2940 clearedFloats = aState.mY != currentY;
2942 // Compute clearance. It's the amount we need to add to the top
2943 // border-edge of the frame, after applying collapsed margins
2944 // from the frame and its children, to get it to line up with
2945 // the bottom of the floats. The former is currentY + topMargin,
2946 // the latter is the current aState.mY.
2947 // Note that negative clearance is possible
2948 clearance = aState.mY - (currentY + topMargin);
2950 // Add clearance to our top margin while we compute available
2951 // space for the frame
2952 topMargin += clearance;
2954 // Note that aState.mY should stay where it is: at the top
2955 // border-edge of the frame
2956 } else {
2957 // Advance aState.mY to the top border-edge of the frame.
2958 aState.mY += topMargin;
2962 // Here aState.mY is the top border-edge of the block.
2963 // Compute the available space for the block
2964 nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
2965 #ifdef REALLY_NOISY_REFLOW
2966 printf("setting line %p isImpacted to %s\n",
2967 aLine.get(), floatAvailableSpace.mHasFloats?"true":"false");
2968 #endif
2969 aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats);
2970 nsRect availSpace;
2971 aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
2972 replacedBlock != nullptr, availSpace);
2974 // The check for
2975 // (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats)
2976 // is to some degree out of paranoia: if we reliably eat up top
2977 // margins at the top of the page as we ought to, it wouldn't be
2978 // needed.
2979 if ((!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) &&
2980 availSpace.height < 0) {
2981 // We know already that this child block won't fit on this
2982 // page/column due to the top margin or the clearance. So we need
2983 // to get out of here now. (If we don't, most blocks will handle
2984 // things fine, and report break-before, but zero-height blocks
2985 // won't, and will thus make their parent overly-large and force
2986 // *it* to be pushed in its entirety.)
2987 // Doing this means that we also don't need to worry about the
2988 // |availSpace.height += topMargin| below interacting with pushed
2989 // floats (which force nscoord_MAX clearance) to cause a
2990 // constrained height to turn into an unconstrained one.
2991 aState.mY = startingY;
2992 aState.mPrevBottomMargin = incomingMargin;
2993 *aKeepReflowGoing = false;
2994 if (ShouldAvoidBreakInside(aState.mReflowState)) {
2995 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
2996 } else {
2997 PushLines(aState, aLine.prev());
2998 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3000 return NS_OK;
3003 // Now put the Y coordinate back to the top of the top-margin +
3004 // clearance, and flow the block.
3005 aState.mY -= topMargin;
3006 availSpace.y -= topMargin;
3007 if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
3008 availSpace.height += topMargin;
3011 // Reflow the block into the available space
3012 // construct the html reflow state for the block. ReflowBlock
3013 // will initialize it
3014 nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
3015 availSpace.Size());
3016 blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
3018 nsFloatManager::SavedState floatManagerState;
3019 if (mayNeedRetry) {
3020 blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
3021 aState.mFloatManager->PushState(&floatManagerState);
3022 } else if (!applyTopMargin) {
3023 blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
3026 nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
3027 rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
3028 clearance, aState.IsAdjacentWithTop(),
3029 aLine.get(), blockHtmlRS, frameReflowStatus, aState);
3031 NS_ENSURE_SUCCESS(rv, rv);
3033 if (mayNeedRetry && clearanceFrame) {
3034 aState.mFloatManager->PopState(&floatManagerState);
3035 aState.mY = startingY;
3036 aState.mPrevBottomMargin = incomingMargin;
3037 continue;
3040 aState.mPrevChild = frame;
3042 if (blockHtmlRS.WillReflowAgainForClearance()) {
3043 // If an ancestor of ours is going to reflow for clearance, we
3044 // need to avoid calling PlaceBlock, because it unsets dirty bits
3045 // on the child block (both itself, and through its call to
3046 // nsFrame::DidReflow), and those dirty bits imply dirtiness for
3047 // all of the child block, including the lines it didn't reflow.
3048 NS_ASSERTION(originalPosition == frame->GetPosition(),
3049 "we need to call PositionChildViews");
3050 return NS_OK;
3053 #if defined(REFLOW_STATUS_COVERAGE)
3054 RecordReflowStatus(true, frameReflowStatus);
3055 #endif
3057 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3058 // None of the child block fits.
3059 *aKeepReflowGoing = false;
3060 if (ShouldAvoidBreakInside(aState.mReflowState)) {
3061 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
3062 } else {
3063 PushLines(aState, aLine.prev());
3064 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3067 else {
3068 // Note: line-break-after a block is a nop
3070 // Try to place the child block.
3071 // Don't force the block to fit if we have positive clearance, because
3072 // pushing it to the next page would give it more room.
3073 // Don't force the block to fit if it's impacted by a float. If it is,
3074 // then pushing it to the next page would give it more room. Note that
3075 // isImpacted doesn't include impact from the block's own floats.
3076 bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
3077 !floatAvailableSpace.mHasFloats;
3078 nsCollapsingMargin collapsedBottomMargin;
3079 nsOverflowAreas overflowAreas;
3080 *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
3081 collapsedBottomMargin,
3082 aLine->mBounds, overflowAreas,
3083 frameReflowStatus);
3084 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
3085 ShouldAvoidBreakInside(aState.mReflowState)) {
3086 *aKeepReflowGoing = false;
3089 if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
3090 line_iterator nextLine = aLine;
3091 ++nextLine;
3092 if (nextLine != end_lines()) {
3093 nextLine->MarkPreviousMarginDirty();
3097 aLine->SetOverflowAreas(overflowAreas);
3098 if (*aKeepReflowGoing) {
3099 // Some of the child block fit
3101 // Advance to new Y position
3102 nscoord newY = aLine->mBounds.YMost();
3103 aState.mY = newY;
3105 // Continue the block frame now if it didn't completely fit in
3106 // the available space.
3107 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
3108 bool madeContinuation =
3109 CreateContinuationFor(aState, nullptr, frame);
3111 nsIFrame* nextFrame = frame->GetNextInFlow();
3112 NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
3114 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3115 // If nextFrame used to be an overflow container, make it a normal block
3116 if (!madeContinuation &&
3117 (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
3118 nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
3119 nsContainerFrame* parent =
3120 static_cast<nsContainerFrame*>(nextFrame->GetParent());
3121 rv = parent->StealFrame(aState.mPresContext, nextFrame);
3122 NS_ENSURE_SUCCESS(rv, rv);
3123 if (parent != this)
3124 ReparentFrame(nextFrame, parent, this);
3125 mFrames.InsertFrame(nullptr, frame, nextFrame);
3126 madeContinuation = true; // needs to be added to mLines
3127 nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
3128 frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3131 // Push continuation to a new line, but only if we actually made one.
3132 if (madeContinuation) {
3133 nsLineBox* line = NewLineBox(nextFrame, true);
3134 mLines.after_insert(aLine, line);
3137 PushLines(aState, aLine);
3138 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3140 // If we need to reflow the continuation of the block child,
3141 // then we'd better reflow our continuation
3142 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
3143 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3144 // We also need to make that continuation's line dirty so
3145 // it gets reflowed when we reflow our next in flow. The
3146 // nif's line must always be either a line of the nif's
3147 // parent block (only if we didn't make a continuation) or
3148 // else one of our own overflow lines. In the latter case
3149 // the line is already marked dirty, so just handle the
3150 // first case.
3151 if (!madeContinuation) {
3152 nsBlockFrame* nifBlock =
3153 nsLayoutUtils::GetAsBlock(nextFrame->GetParent());
3154 NS_ASSERTION(nifBlock,
3155 "A block's child's next in flow's parent must be a block!");
3156 for (line_iterator line = nifBlock->begin_lines(),
3157 line_end = nifBlock->end_lines(); line != line_end; ++line) {
3158 if (line->Contains(nextFrame)) {
3159 line->MarkDirty();
3160 break;
3165 *aKeepReflowGoing = false;
3167 // The bottom margin for a block is only applied on the last
3168 // flow block. Since we just continued the child block frame,
3169 // we know that line->mFirstChild is not the last flow block
3170 // therefore zero out the running margin value.
3171 #ifdef NOISY_VERTICAL_MARGINS
3172 ListTag(stdout);
3173 printf(": reflow incomplete, frame=");
3174 nsFrame::ListTag(stdout, frame);
3175 printf(" prevBottomMargin=%d, setting to zero\n",
3176 aState.mPrevBottomMargin);
3177 #endif
3178 aState.mPrevBottomMargin.Zero();
3180 else { // frame is complete but its overflow is not complete
3181 // Disconnect the next-in-flow and put it in our overflow tracker
3182 if (!madeContinuation &&
3183 !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
3184 // It already exists, but as a normal next-in-flow, so we need
3185 // to dig it out of the child lists.
3186 nsContainerFrame* parent = static_cast<nsContainerFrame*>
3187 (nextFrame->GetParent());
3188 rv = parent->StealFrame(aState.mPresContext, nextFrame);
3189 NS_ENSURE_SUCCESS(rv, rv);
3191 else if (madeContinuation) {
3192 mFrames.RemoveFrame(nextFrame);
3195 // Put it in our overflow list
3196 aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
3197 NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
3199 #ifdef NOISY_VERTICAL_MARGINS
3200 ListTag(stdout);
3201 printf(": reflow complete but overflow incomplete for ");
3202 nsFrame::ListTag(stdout, frame);
3203 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3204 aState.mPrevBottomMargin, collapsedBottomMargin.get());
3205 #endif
3206 aState.mPrevBottomMargin = collapsedBottomMargin;
3209 else { // frame is fully complete
3210 #ifdef NOISY_VERTICAL_MARGINS
3211 ListTag(stdout);
3212 printf(": reflow complete for ");
3213 nsFrame::ListTag(stdout, frame);
3214 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3215 aState.mPrevBottomMargin, collapsedBottomMargin.get());
3216 #endif
3217 aState.mPrevBottomMargin = collapsedBottomMargin;
3219 #ifdef NOISY_VERTICAL_MARGINS
3220 ListTag(stdout);
3221 printf(": frame=");
3222 nsFrame::ListTag(stdout, frame);
3223 printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3224 brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
3225 aState.mPrevBottomMargin);
3226 #endif
3227 } else {
3228 if ((aLine == mLines.front() && !GetPrevInFlow()) ||
3229 ShouldAvoidBreakInside(aState.mReflowState)) {
3230 // If it's our very first line *or* we're not at the top of the page
3231 // and we have page-break-inside:avoid, then we need to be pushed to
3232 // our parent's next-in-flow.
3233 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
3234 } else {
3235 // Push the line that didn't fit and any lines that follow it
3236 // to our next-in-flow.
3237 PushLines(aState, aLine.prev());
3238 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3242 break; // out of the reflow retry loop
3245 // Now that we've got its final position all figured out, position any child
3246 // views it may have. Note that the case when frame has a view got handled
3247 // by FinishReflowChild, but that function didn't have the coordinates needed
3248 // to correctly decide whether to reposition child views.
3249 if (originalPosition != frame->GetPosition() && !frame->HasView()) {
3250 nsContainerFrame::PositionChildViews(frame);
3253 #ifdef DEBUG
3254 VerifyLines(true);
3255 #endif
3256 return rv;
3259 nsresult
3260 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
3261 line_iterator aLine,
3262 bool* aKeepReflowGoing)
3264 nsresult rv = NS_OK;
3265 *aKeepReflowGoing = true;
3267 aLine->SetLineIsImpactedByFloat(false);
3269 // Setup initial coordinate system for reflowing the inline frames
3270 // into. Apply a previous block frame's bottom margin first.
3271 if (ShouldApplyTopMargin(aState, aLine)) {
3272 aState.mY += aState.mPrevBottomMargin.get();
3274 nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
3276 LineReflowStatus lineReflowStatus;
3277 do {
3278 nscoord availableSpaceHeight = 0;
3279 do {
3280 bool allowPullUp = true;
3281 nsIContent* forceBreakInContent = nullptr;
3282 int32_t forceBreakOffset = -1;
3283 gfxBreakPriority forceBreakPriority = eNoBreak;
3284 do {
3285 nsFloatManager::SavedState floatManagerState;
3286 aState.mReflowState.mFloatManager->PushState(&floatManagerState);
3288 // Once upon a time we allocated the first 30 nsLineLayout objects
3289 // on the stack, and then we switched to the heap. At that time
3290 // these objects were large (1100 bytes on a 32 bit system).
3291 // Then the nsLineLayout object was shrunk to 156 bytes by
3292 // removing some internal buffers. Given that it is so much
3293 // smaller, the complexity of 2 different ways of allocating
3294 // no longer makes sense. Now we always allocate on the stack.
3295 nsLineLayout lineLayout(aState.mPresContext,
3296 aState.mReflowState.mFloatManager,
3297 &aState.mReflowState, &aLine);
3298 lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
3299 if (forceBreakInContent) {
3300 lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
3302 rv = DoReflowInlineFrames(aState, lineLayout, aLine,
3303 floatAvailableSpace, availableSpaceHeight,
3304 &floatManagerState, aKeepReflowGoing,
3305 &lineReflowStatus, allowPullUp);
3306 lineLayout.EndLineReflow();
3308 if (NS_FAILED(rv)) {
3309 return rv;
3312 if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
3313 LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus ||
3314 LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3315 if (lineLayout.NeedsBackup()) {
3316 NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary");
3317 // If there is no saved break position, then this will set
3318 // set forceBreakInContent to null and we won't back up, which is
3319 // correct.
3320 forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority);
3321 } else {
3322 forceBreakInContent = nullptr;
3324 // restore the float manager state
3325 aState.mReflowState.mFloatManager->PopState(&floatManagerState);
3326 // Clear out float lists
3327 aState.mCurrentLineFloats.DeleteAll();
3328 aState.mBelowCurrentLineFloats.DeleteAll();
3331 // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
3332 allowPullUp = false;
3333 } while (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
3334 } while (LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus);
3335 } while (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
3337 return rv;
3340 void
3341 nsBlockFrame::PushTruncatedLine(nsBlockReflowState& aState,
3342 line_iterator aLine,
3343 bool* aKeepReflowGoing)
3345 PushLines(aState, aLine.prev());
3346 *aKeepReflowGoing = false;
3347 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3350 #ifdef DEBUG
3351 static const char* LineReflowStatusNames[] = {
3352 "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
3353 "LINE_REFLOW_REDO_MORE_FLOATS",
3354 "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
3356 #endif
3358 nsresult
3359 nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
3360 nsLineLayout& aLineLayout,
3361 line_iterator aLine,
3362 nsFlowAreaRect& aFloatAvailableSpace,
3363 nscoord& aAvailableSpaceHeight,
3364 nsFloatManager::SavedState*
3365 aFloatStateBeforeLine,
3366 bool* aKeepReflowGoing,
3367 LineReflowStatus* aLineReflowStatus,
3368 bool aAllowPullUp)
3370 // Forget all of the floats on the line
3371 aLine->FreeFloats(aState.mFloatCacheFreeList);
3372 aState.mFloatOverflowAreas.Clear();
3374 // We need to set this flag on the line if any of our reflow passes
3375 // are impacted by floats.
3376 if (aFloatAvailableSpace.mHasFloats)
3377 aLine->SetLineIsImpactedByFloat(true);
3378 #ifdef REALLY_NOISY_REFLOW
3379 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
3380 this, aFloatAvailableSpace.mHasFloats);
3381 #endif
3383 nscoord x = aFloatAvailableSpace.mRect.x;
3384 nscoord availWidth = aFloatAvailableSpace.mRect.width;
3385 nscoord availHeight;
3386 if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
3387 availHeight = NS_UNCONSTRAINEDSIZE;
3389 else {
3390 /* XXX get the height right! */
3391 availHeight = aFloatAvailableSpace.mRect.height;
3394 // Make sure to enable resize optimization before we call BeginLineReflow
3395 // because it might get disabled there
3396 aLine->EnableResizeReflowOptimization();
3398 // For unicode-bidi: plaintext, we need to get the direction of the line from
3399 // the resolved paragraph level of the first frame on the line, not the block
3400 // frame, because the block frame could be split by hard line breaks into
3401 // multiple paragraphs with different base direction
3402 uint8_t direction =
3403 (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) ?
3404 nsBidiPresUtils::GetFrameBaseLevel(aLine->mFirstChild) & 1 :
3405 StyleVisibility()->mDirection;
3407 aLineLayout.BeginLineReflow(x, aState.mY,
3408 availWidth, availHeight,
3409 aFloatAvailableSpace.mHasFloats,
3410 false, /*XXX isTopOfPage*/
3411 direction);
3413 aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false);
3415 // XXX Unfortunately we need to know this before reflowing the first
3416 // inline frame in the line. FIX ME.
3417 if ((0 == aLineLayout.GetLineNumber()) &&
3418 (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
3419 (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
3420 aLineLayout.SetFirstLetterStyleOK(true);
3422 NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
3423 GetPrevContinuation()),
3424 "first letter child bit should only be on first continuation");
3426 // Reflow the frames that are already on the line first
3427 nsresult rv = NS_OK;
3428 LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
3429 int32_t i;
3430 nsIFrame* frame = aLine->mFirstChild;
3432 if (aFloatAvailableSpace.mHasFloats) {
3433 // There is a soft break opportunity at the start of the line, because
3434 // we can always move this line down below float(s).
3435 if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, true, eNormalBreak)) {
3436 lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3440 // need to repeatedly call GetChildCount here, because the child
3441 // count can change during the loop!
3442 for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
3443 i++, frame = frame->GetNextSibling()) {
3444 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3445 &lineReflowStatus);
3446 NS_ENSURE_SUCCESS(rv, rv);
3447 if (LINE_REFLOW_OK != lineReflowStatus) {
3448 // It is possible that one or more of next lines are empty
3449 // (because of DeleteNextInFlowChild). If so, delete them now
3450 // in case we are finished.
3451 ++aLine;
3452 while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
3453 // XXX Is this still necessary now that DeleteNextInFlowChild
3454 // uses DoRemoveFrame?
3455 nsLineBox *toremove = aLine;
3456 aLine = mLines.erase(aLine);
3457 NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
3458 FreeLineBox(toremove);
3460 --aLine;
3462 NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED,
3463 "ReflowInlineFrame should never determine that a line "
3464 "needs to go to the next page/column");
3468 // Don't pull up new frames into lines with continuation placeholders
3469 if (aAllowPullUp) {
3470 // Pull frames and reflow them until we can't
3471 while (LINE_REFLOW_OK == lineReflowStatus) {
3472 frame = PullFrame(aState, aLine);
3473 if (!frame) {
3474 break;
3477 while (LINE_REFLOW_OK == lineReflowStatus) {
3478 int32_t oldCount = aLine->GetChildCount();
3479 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3480 &lineReflowStatus);
3481 NS_ENSURE_SUCCESS(rv, rv);
3482 if (aLine->GetChildCount() != oldCount) {
3483 // We just created a continuation for aFrame AND its going
3484 // to end up on this line (e.g. :first-letter
3485 // situation). Therefore we have to loop here before trying
3486 // to pull another frame.
3487 frame = frame->GetNextSibling();
3489 else {
3490 break;
3496 aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, aLineLayout.LineIsEmpty());
3498 // We only need to backup if the line isn't going to be reflowed again anyway
3499 bool needsBackup = aLineLayout.NeedsBackup() &&
3500 (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK);
3501 if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
3502 NS_WARNING("We shouldn't be backing up more than once! "
3503 "Someone must have set a break opportunity beyond the available width, "
3504 "even though there were better break opportunities before it");
3505 needsBackup = false;
3507 if (needsBackup) {
3508 // We need to try backing up to before a text run
3509 int32_t offset;
3510 gfxBreakPriority breakPriority;
3511 nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset, &breakPriority);
3512 // XXX It's possible, in fact not unusual, for the break opportunity to already
3513 // be the end of the line. We should detect that and optimize to not
3514 // re-do the line.
3515 if (breakContent) {
3516 // We can back up!
3517 lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
3519 } else {
3520 // In case we reflow this line again, remember that we don't
3521 // need to force any breaking
3522 aLineLayout.ClearOptionalBreakPosition();
3525 if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3526 // This happens only when we have a line that is impacted by
3527 // floats and the first element in the line doesn't fit with
3528 // the floats.
3530 // What we do is to advance past the first float we find and
3531 // then reflow the line all over again.
3532 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.height,
3533 "unconstrained height on totally empty line");
3535 // See the analogous code for blocks in nsBlockReflowState::ClearFloats.
3536 if (aFloatAvailableSpace.mRect.height > 0) {
3537 NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
3538 "redo line on totally empty line with non-empty band...");
3539 // We should never hit this case if we've placed floats on the
3540 // line; if we have, then the GetFloatAvailableSpace call is wrong
3541 // and needs to happen after the caller pops the space manager
3542 // state.
3543 aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
3544 aState.mY += aFloatAvailableSpace.mRect.height;
3545 aFloatAvailableSpace = aState.GetFloatAvailableSpace();
3546 } else {
3547 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight,
3548 "We shouldn't be running out of height here");
3549 if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableHeight) {
3550 // just move it down a bit to try to get out of this mess
3551 aState.mY += 1;
3552 // We should never hit this case if we've placed floats on the
3553 // line; if we have, then the GetFloatAvailableSpace call is wrong
3554 // and needs to happen after the caller pops the space manager
3555 // state.
3556 aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
3557 aFloatAvailableSpace = aState.GetFloatAvailableSpace();
3558 } else {
3559 // There's nowhere to retry placing the line, so we want to push
3560 // it to the next page/column where its contents can fit not
3561 // next to a float.
3562 lineReflowStatus = LINE_REFLOW_TRUNCATED;
3563 PushTruncatedLine(aState, aLine, aKeepReflowGoing);
3567 // XXX: a small optimization can be done here when paginating:
3568 // if the new Y coordinate is past the end of the block then
3569 // push the line and return now instead of later on after we are
3570 // past the float.
3572 else if (LINE_REFLOW_TRUNCATED != lineReflowStatus &&
3573 LINE_REFLOW_REDO_NO_PULL != lineReflowStatus) {
3574 // If we are propagating out a break-before status then there is
3575 // no point in placing the line.
3576 if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
3577 if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
3578 aFloatAvailableSpace.mRect, aAvailableSpaceHeight,
3579 aKeepReflowGoing)) {
3580 lineReflowStatus = LINE_REFLOW_REDO_MORE_FLOATS;
3581 // PlaceLine already called GetAvailableSpaceForHeight for us.
3585 #ifdef DEBUG
3586 if (gNoisyReflow) {
3587 printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
3589 #endif
3591 if (aLineLayout.GetDirtyNextLine()) {
3592 // aLine may have been pushed to the overflow lines.
3593 FrameLines* overflowLines = GetOverflowLines();
3594 // We can't just compare iterators front() to aLine here, since they may be in
3595 // different lists.
3596 bool pushedToOverflowLines = overflowLines &&
3597 overflowLines->mLines.front() == aLine.get();
3598 if (pushedToOverflowLines) {
3599 // aLine is stale, it's associated with the main line list but it should
3600 // be associated with the overflow line list now
3601 aLine = overflowLines->mLines.begin();
3603 nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
3604 if (iter.Next() && iter.GetLine()->IsInline()) {
3605 iter.GetLine()->MarkDirty();
3606 if (iter.GetContainer() != this) {
3607 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3612 *aLineReflowStatus = lineReflowStatus;
3614 return rv;
3618 * Reflow an inline frame. The reflow status is mapped from the frames
3619 * reflow status to the lines reflow status (not to our reflow status).
3620 * The line reflow status is simple: true means keep placing frames
3621 * on the line; false means don't (the line is done). If the line
3622 * has some sort of breaking affect then aLine's break-type will be set
3623 * to something other than NS_STYLE_CLEAR_NONE.
3625 nsresult
3626 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
3627 nsLineLayout& aLineLayout,
3628 line_iterator aLine,
3629 nsIFrame* aFrame,
3630 LineReflowStatus* aLineReflowStatus)
3632 NS_ENSURE_ARG_POINTER(aFrame);
3634 *aLineReflowStatus = LINE_REFLOW_OK;
3636 #ifdef NOISY_FIRST_LETTER
3637 ListTag(stdout);
3638 printf(": reflowing ");
3639 nsFrame::ListTag(stdout, aFrame);
3640 printf(" reflowingFirstLetter=%s\n",
3641 aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
3642 #endif
3644 // Reflow the inline frame
3645 nsReflowStatus frameReflowStatus;
3646 bool pushedFrame;
3647 nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
3648 nullptr, pushedFrame);
3649 NS_ENSURE_SUCCESS(rv, rv);
3651 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
3652 aLineLayout.SetDirtyNextLine();
3655 NS_ENSURE_SUCCESS(rv, rv);
3656 #ifdef REALLY_NOISY_REFLOW_CHILD
3657 nsFrame::ListTag(stdout, aFrame);
3658 printf(": status=%x\n", frameReflowStatus);
3659 #endif
3661 #if defined(REFLOW_STATUS_COVERAGE)
3662 RecordReflowStatus(false, frameReflowStatus);
3663 #endif
3665 // Send post-reflow notification
3666 aState.mPrevChild = aFrame;
3668 /* XXX
3669 This is where we need to add logic to handle some odd behavior.
3670 For one thing, we should usually place at least one thing next
3671 to a left float, even when that float takes up all the width on a line.
3672 see bug 22496
3675 // Process the child frames reflow status. There are 5 cases:
3676 // complete, not-complete, break-before, break-after-complete,
3677 // break-after-not-complete. There are two situations: we are a
3678 // block or we are an inline. This makes a total of 10 cases
3679 // (fortunately, there is some overlap).
3680 aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
3681 if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
3682 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
3683 // Always abort the line reflow (because a line break is the
3684 // minimal amount of break we do).
3685 *aLineReflowStatus = LINE_REFLOW_STOP;
3687 // XXX what should aLine's break-type be set to in all these cases?
3688 uint8_t breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
3689 NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) ||
3690 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
3691 NS_ASSERTION(NS_STYLE_CLEAR_LAST_VALUE >= breakType, "invalid break type");
3693 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3694 // Break-before cases.
3695 if (aFrame == aLine->mFirstChild) {
3696 // If we break before the first frame on the line then we must
3697 // be trying to place content where there's no room (e.g. on a
3698 // line with wide floats). Inform the caller to reflow the
3699 // line after skipping past a float.
3700 *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3702 else {
3703 // It's not the first child on this line so go ahead and split
3704 // the line. We will see the frame again on the next-line.
3705 SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
3707 // If we're splitting the line because the frame didn't fit and it
3708 // was pushed, then mark the line as having word wrapped. We need to
3709 // know that if we're shrink wrapping our width
3710 if (pushedFrame) {
3711 aLine->SetLineWrapped(true);
3715 else {
3716 // If a float split and its prev-in-flow was followed by a <BR>, then combine
3717 // the <BR>'s break type with the inline's break type (the inline will be the very
3718 // next frame after the split float).
3719 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
3720 breakType = nsLayoutUtils::CombineBreakType(breakType,
3721 aState.mFloatBreakType);
3722 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
3724 // Break-after cases
3725 if (breakType == NS_STYLE_CLEAR_LINE) {
3726 if (!aLineLayout.GetLineEndsInBR()) {
3727 breakType = NS_STYLE_CLEAR_NONE;
3730 aLine->SetBreakTypeAfter(breakType);
3731 if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
3732 // Split line, but after the frame just reflowed
3733 SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3735 if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
3736 !aLineLayout.GetLineEndsInBR()) {
3737 aLineLayout.SetDirtyNextLine();
3743 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
3744 // Create a continuation for the incomplete frame. Note that the
3745 // frame may already have a continuation.
3746 CreateContinuationFor(aState, aLine, aFrame);
3748 // Remember that the line has wrapped
3749 if (!aLineLayout.GetLineEndsInBR()) {
3750 aLine->SetLineWrapped(true);
3753 // If we just ended a first-letter frame or reflowed a placeholder then
3754 // don't split the line and don't stop the line reflow...
3755 // But if we are going to stop anyways we'd better split the line.
3756 if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) &&
3757 nsGkAtoms::placeholderFrame != aFrame->GetType()) ||
3758 *aLineReflowStatus == LINE_REFLOW_STOP) {
3759 // Split line after the current frame
3760 *aLineReflowStatus = LINE_REFLOW_STOP;
3761 SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3765 return NS_OK;
3768 bool
3769 nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
3770 nsLineBox* aLine,
3771 nsIFrame* aFrame)
3773 nsIFrame* newFrame = nullptr;
3775 if (!aFrame->GetNextInFlow()) {
3776 newFrame = aState.mPresContext->PresShell()->FrameConstructor()->
3777 CreateContinuingFrame(aState.mPresContext, aFrame, this);
3779 mFrames.InsertFrame(nullptr, aFrame, newFrame);
3781 if (aLine) {
3782 aLine->NoteFrameAdded(newFrame);
3785 #ifdef DEBUG
3786 VerifyLines(false);
3787 #endif
3788 return !!newFrame;
3791 nsresult
3792 nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
3793 nsIFrame* aFloat,
3794 nsReflowStatus aFloatStatus)
3796 nsIFrame* nextInFlow = aFloat->GetNextInFlow();
3797 if (nextInFlow) {
3798 nsContainerFrame *oldParent =
3799 static_cast<nsContainerFrame*>(nextInFlow->GetParent());
3800 DebugOnly<nsresult> rv = oldParent->StealFrame(aState.mPresContext, nextInFlow);
3801 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
3802 if (oldParent != this) {
3803 ReparentFrame(nextInFlow, oldParent, this);
3805 } else {
3806 nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
3807 CreateContinuingFrame(aState.mPresContext, aFloat, this);
3809 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
3810 aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
3812 // The containing block is now overflow-incomplete.
3813 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
3815 if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
3816 aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
3817 } else {
3818 NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats ==
3819 NS_STYLE_FLOAT_RIGHT, "unexpected float side");
3820 aState.mFloatManager->SetSplitRightFloatAcrossBreak();
3823 aState.AppendPushedFloat(nextInFlow);
3824 return NS_OK;
3827 static nsFloatCache*
3828 GetLastFloat(nsLineBox* aLine)
3830 nsFloatCache* fc = aLine->GetFirstFloat();
3831 while (fc && fc->Next()) {
3832 fc = fc->Next();
3834 return fc;
3837 static bool
3838 CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
3840 if (!aFC)
3841 return true;
3842 NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
3843 "float in a line should never be a continuation");
3844 NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
3845 "float in a line should never be a pushed float");
3846 nsIFrame* ph = aBlock->PresContext()->FrameManager()->
3847 GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow());
3848 for (nsIFrame* f = ph; f; f = f->GetParent()) {
3849 if (f->GetParent() == aBlock)
3850 return aLine->Contains(f);
3852 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
3853 return true;
3856 void
3857 nsBlockFrame::SplitLine(nsBlockReflowState& aState,
3858 nsLineLayout& aLineLayout,
3859 line_iterator aLine,
3860 nsIFrame* aFrame,
3861 LineReflowStatus* aLineReflowStatus)
3863 NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
3865 int32_t pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
3866 NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count");
3868 #ifdef DEBUG
3869 if (gNoisyReflow) {
3870 nsFrame::IndentBy(stdout, gNoiseIndent);
3871 printf("split line: from line=%p pushCount=%d aFrame=",
3872 static_cast<void*>(aLine.get()), pushCount);
3873 if (aFrame) {
3874 nsFrame::ListTag(stdout, aFrame);
3876 else {
3877 printf("(null)");
3879 printf("\n");
3880 if (gReallyNoisyReflow) {
3881 aLine->List(stdout, gNoiseIndent+1);
3884 #endif
3886 if (0 != pushCount) {
3887 NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
3888 NS_ABORT_IF_FALSE(nullptr != aFrame, "whoops");
3889 #ifdef DEBUG
3891 nsIFrame *f = aFrame;
3892 int32_t count = pushCount;
3893 while (f && count > 0) {
3894 f = f->GetNextSibling();
3895 --count;
3897 NS_ASSERTION(count == 0, "Not enough frames to push");
3899 #endif
3901 // Put frames being split out into their own line
3902 nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
3903 mLines.after_insert(aLine, newLine);
3904 #ifdef DEBUG
3905 if (gReallyNoisyReflow) {
3906 newLine->List(stdout, gNoiseIndent+1);
3908 #endif
3910 // Let line layout know that some frames are no longer part of its
3911 // state.
3912 aLineLayout.SplitLineTo(aLine->GetChildCount());
3914 // If floats have been placed whose placeholders have been pushed to the new
3915 // line, we need to reflow the old line again. We don't want to look at the
3916 // frames in the new line, because as a large paragraph is laid out the
3917 // we'd get O(N^2) performance. So instead we just check that the last
3918 // float and the last below-current-line float are still in aLine.
3919 if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
3920 !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
3921 *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
3924 #ifdef DEBUG
3925 VerifyLines(true);
3926 #endif
3930 bool
3931 nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
3932 line_iterator aLine)
3934 while (++aLine != end_lines()) {
3935 // There is another line
3936 if (0 != aLine->GetChildCount()) {
3937 // If the next line is a block line then this line is the last in a
3938 // group of inline lines.
3939 return aLine->IsBlock();
3941 // The next line is empty, try the next one
3944 // XXX Not sure about this part
3945 // Try our next-in-flows lines to answer the question
3946 nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
3947 while (nullptr != nextInFlow) {
3948 for (line_iterator line = nextInFlow->begin_lines(),
3949 line_end = nextInFlow->end_lines();
3950 line != line_end;
3951 ++line)
3953 if (0 != line->GetChildCount())
3954 return line->IsBlock();
3956 nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
3959 // This is the last line - so don't allow justification
3960 return true;
3963 bool
3964 nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
3965 nsLineLayout& aLineLayout,
3966 line_iterator aLine,
3967 nsFloatManager::SavedState *aFloatStateBeforeLine,
3968 nsRect& aFloatAvailableSpace,
3969 nscoord& aAvailableSpaceHeight,
3970 bool* aKeepReflowGoing)
3972 // Trim extra white-space from the line before placing the frames
3973 aLineLayout.TrimTrailingWhiteSpace();
3975 // Vertically align the frames on this line.
3977 // According to the CSS2 spec, section 12.6.1, the "marker" box
3978 // participates in the height calculation of the list-item box's
3979 // first line box.
3981 // There are exactly two places a bullet can be placed: near the
3982 // first or second line. It's only placed on the second line in a
3983 // rare case: when the first line is empty.
3984 bool addedBullet = false;
3985 if (HasOutsideBullet() &&
3986 ((aLine == mLines.front() &&
3987 (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) ||
3988 (mLines.front() != mLines.back() &&
3989 0 == mLines.front()->mBounds.height &&
3990 aLine == mLines.begin().next()))) {
3991 nsHTMLReflowMetrics metrics;
3992 nsIFrame* bullet = GetOutsideBullet();
3993 ReflowBullet(bullet, aState, metrics, aState.mY);
3994 NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
3995 "empty bullet took up space");
3996 aLineLayout.AddBulletFrame(bullet, metrics);
3997 addedBullet = true;
3999 aLineLayout.VerticalAlignLine();
4001 // We want to compare to the available space that we would have had in
4002 // the line's height *before* we placed any floats in the line itself.
4003 // Floats that are in the line are handled during line reflow (and may
4004 // result in floats being pushed to below the line or (I HOPE???) in a
4005 // reflow with a forced break position).
4006 nsRect oldFloatAvailableSpace(aFloatAvailableSpace);
4007 // As we redo for floats, we can't reduce the amount of height we're
4008 // checking.
4009 aAvailableSpaceHeight = std::max(aAvailableSpaceHeight, aLine->mBounds.height);
4010 aFloatAvailableSpace =
4011 aState.GetFloatAvailableSpaceForHeight(aLine->mBounds.y,
4012 aAvailableSpaceHeight,
4013 aFloatStateBeforeLine).mRect;
4014 NS_ASSERTION(aFloatAvailableSpace.y == oldFloatAvailableSpace.y, "yikes");
4015 // Restore the height to the position of the next band.
4016 aFloatAvailableSpace.height = oldFloatAvailableSpace.height;
4017 // If the available space between the floats is smaller now that we
4018 // know the height, return false (and cause another pass with
4019 // LINE_REFLOW_REDO_MORE_FLOATS).
4020 if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) {
4021 return false;
4024 #ifdef DEBUG
4026 static nscoord lastHeight = 0;
4027 if (CRAZY_HEIGHT(aLine->mBounds.y)) {
4028 lastHeight = aLine->mBounds.y;
4029 if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
4030 nsFrame::ListTag(stdout);
4031 printf(": line=%p y=%d line.bounds.height=%d\n",
4032 static_cast<void*>(aLine.get()),
4033 aLine->mBounds.y, aLine->mBounds.height);
4036 else {
4037 lastHeight = 0;
4040 #endif
4042 // Only block frames horizontally align their children because
4043 // inline frames "shrink-wrap" around their children (therefore
4044 // there is no extra horizontal space).
4045 const nsStyleText* styleText = StyleText();
4048 * text-align-last defaults to the same value as text-align when
4049 * text-align-last is set to auto (except when text-align is set to justify),
4050 * so in that case we don't need to set isLastLine.
4052 * In other words, isLastLine really means isLastLineAndWeCare.
4054 bool isLastLine =
4055 !IsSVGText() &&
4056 ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
4057 NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
4058 (aLineLayout.GetLineEndsInBR() ||
4059 IsLastLine(aState, aLine)));
4060 aLineLayout.HorizontalAlignFrames(aLine->mBounds, isLastLine);
4061 // XXX: not only bidi: right alignment can be broken after
4062 // RelativePositionFrames!!!
4063 // XXXldb Is something here considering relatively positioned frames at
4064 // other than their original positions?
4065 #ifdef IBMBIDI
4066 // XXXldb Why don't we do this earlier?
4067 if (aState.mPresContext->BidiEnabled()) {
4068 if (!aState.mPresContext->IsVisualMode() ||
4069 StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
4070 nsBidiPresUtils::ReorderFrames(aLine->mFirstChild, aLine->GetChildCount());
4071 } // not visual mode
4072 } // bidi enabled
4073 #endif // IBMBIDI
4075 // From here on, pfd->mBounds rectangles are incorrect because bidi
4076 // might have moved frames around!
4077 nsOverflowAreas overflowAreas;
4078 aLineLayout.RelativePositionFrames(overflowAreas);
4079 aLine->SetOverflowAreas(overflowAreas);
4080 if (addedBullet) {
4081 aLineLayout.RemoveBulletFrame(GetOutsideBullet());
4084 // Inline lines do not have margins themselves; however they are
4085 // impacted by prior block margins. If this line ends up having some
4086 // height then we zero out the previous bottom margin value that was
4087 // already applied to the line's starting Y coordinate. Otherwise we
4088 // leave it be so that the previous blocks bottom margin can be
4089 // collapsed with a block that follows.
4090 nscoord newY;
4092 if (!aLine->CachedIsEmpty()) {
4093 // This line has some height. Therefore the application of the
4094 // previous-bottom-margin should stick.
4095 aState.mPrevBottomMargin.Zero();
4096 newY = aLine->mBounds.YMost();
4098 else {
4099 // Don't let the previous-bottom-margin value affect the newY
4100 // coordinate (it was applied in ReflowInlineFrames speculatively)
4101 // since the line is empty.
4102 // We already called |ShouldApplyTopMargin|, and if we applied it
4103 // then BRS_APPLYTOPMARGIN is set.
4104 nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
4105 ? -aState.mPrevBottomMargin.get() : 0;
4106 newY = aState.mY + dy;
4109 if (!NS_FRAME_IS_FULLY_COMPLETE(aState.mReflowStatus) &&
4110 ShouldAvoidBreakInside(aState.mReflowState)) {
4111 aLine->AppendFloats(aState.mCurrentLineFloats);
4112 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
4113 return true;
4116 // See if the line fit (our first line always does).
4117 if (mLines.front() != aLine &&
4118 newY > aState.mBottomEdge &&
4119 aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
4120 NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
4121 if (ShouldAvoidBreakInside(aState.mReflowState)) {
4122 // All our content doesn't fit, start on the next page.
4123 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
4124 } else {
4125 // Push aLine and all of its children and anything else that
4126 // follows to our next-in-flow.
4127 PushTruncatedLine(aState, aLine, aKeepReflowGoing);
4129 return true;
4132 aState.mY = newY;
4134 // Add the already placed current-line floats to the line
4135 aLine->AppendFloats(aState.mCurrentLineFloats);
4137 // Any below current line floats to place?
4138 if (aState.mBelowCurrentLineFloats.NotEmpty()) {
4139 // Reflow the below-current-line floats, which places on the line's
4140 // float list.
4141 aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine);
4142 aLine->AppendFloats(aState.mBelowCurrentLineFloats);
4145 // When a line has floats, factor them into the combined-area
4146 // computations.
4147 if (aLine->HasFloats()) {
4148 // Combine the float combined area (stored in aState) and the
4149 // value computed by the line layout code.
4150 nsOverflowAreas lineOverflowAreas;
4151 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
4152 nsRect &o = lineOverflowAreas.Overflow(otype);
4153 o = aLine->GetOverflowArea(otype);
4154 #ifdef NOISY_COMBINED_AREA
4155 ListTag(stdout);
4156 printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
4157 otype,
4158 o.x, o.y, o.width, o.height,
4159 aState.mFloatOverflowAreas.Overflow(otype).x,
4160 aState.mFloatOverflowAreas.Overflow(otype).y,
4161 aState.mFloatOverflowAreas.Overflow(otype).width,
4162 aState.mFloatOverflowAreas.Overflow(otype).height);
4163 #endif
4164 o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o);
4166 #ifdef NOISY_COMBINED_AREA
4167 printf(" ==> final lineCA=%d,%d,%d,%d\n",
4168 o.x, o.y, o.width, o.height);
4169 #endif
4171 aLine->SetOverflowAreas(lineOverflowAreas);
4174 // Apply break-after clearing if necessary
4175 // This must stay in sync with |ReflowDirtyLines|.
4176 if (aLine->HasFloatBreakAfter()) {
4177 aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
4179 return true;
4182 void
4183 nsBlockFrame::PushLines(nsBlockReflowState& aState,
4184 nsLineList::iterator aLineBefore)
4186 // NOTE: aLineBefore is always a normal line, not an overflow line.
4187 // The following expression will assert otherwise.
4188 DebugOnly<bool> check = aLineBefore == mLines.begin();
4190 nsLineList::iterator overBegin(aLineBefore.next());
4192 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
4193 bool firstLine = overBegin == begin_lines();
4195 if (overBegin != end_lines()) {
4196 // Remove floats in the lines from mFloats
4197 nsFrameList floats;
4198 CollectFloats(overBegin->mFirstChild, floats, true);
4200 if (floats.NotEmpty()) {
4201 // Push the floats onto the front of the overflow out-of-flows list
4202 nsAutoOOFFrameList oofs(this);
4203 oofs.mList.InsertFrames(nullptr, nullptr, floats);
4206 // overflow lines can already exist in some cases, in particular,
4207 // when shrinkwrapping and we discover that the shrinkwap causes
4208 // the height of some child block to grow which creates additional
4209 // overflowing content. In such cases we must prepend the new
4210 // overflow to the existing overflow.
4211 FrameLines* overflowLines = RemoveOverflowLines();
4212 if (!overflowLines) {
4213 // XXXldb use presshell arena!
4214 overflowLines = new FrameLines();
4216 if (overflowLines) {
4217 nsIFrame* lineBeforeLastFrame;
4218 if (firstLine) {
4219 lineBeforeLastFrame = nullptr; // removes all frames
4220 } else {
4221 nsIFrame* f = overBegin->mFirstChild;
4222 lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
4223 NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
4224 "unexpected line frames");
4226 nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
4227 overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames);
4229 overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
4230 overBegin, end_lines());
4231 NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
4232 // this takes ownership but it won't delete it immediately so we
4233 // can keep using it.
4234 SetOverflowLines(overflowLines);
4236 // Mark all the overflow lines dirty so that they get reflowed when
4237 // they are pulled up by our next-in-flow.
4239 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4240 for (line_iterator line = overflowLines->mLines.begin(),
4241 line_end = overflowLines->mLines.end();
4242 line != line_end;
4243 ++line)
4245 line->MarkDirty();
4246 line->MarkPreviousMarginDirty();
4247 line->mBounds.SetRect(0, 0, 0, 0);
4248 if (line->HasFloats()) {
4249 line->FreeFloats(aState.mFloatCacheFreeList);
4255 #ifdef DEBUG
4256 VerifyOverflowSituation();
4257 #endif
4260 // The overflowLines property is stored as a pointer to a line list,
4261 // which must be deleted. However, the following functions all maintain
4262 // the invariant that the property is never set if the list is empty.
4264 bool
4265 nsBlockFrame::DrainOverflowLines()
4267 #ifdef DEBUG
4268 VerifyOverflowSituation();
4269 #endif
4271 // Steal the prev-in-flow's overflow lines and prepend them.
4272 bool didFindOverflow = false;
4273 nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
4274 if (prevBlock) {
4275 prevBlock->ClearLineCursor();
4276 FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
4277 if (overflowLines) {
4278 // Make all the frames on the overflow line list mine.
4279 ReparentFrames(overflowLines->mFrames, prevBlock, this);
4281 // Make the overflow out-of-flow frames mine too.
4282 nsAutoOOFFrameList oofs(prevBlock);
4283 if (oofs.mList.NotEmpty()) {
4284 ReparentFrames(oofs.mList, prevBlock, this);
4285 mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
4288 if (!mLines.empty()) {
4289 // Remember to recompute the margins on the first line. This will
4290 // also recompute the correct deltaY if necessary.
4291 mLines.front()->MarkPreviousMarginDirty();
4293 // The overflow lines have already been marked dirty and their previous
4294 // margins marked dirty also.
4296 // Prepend the overflow frames/lines to our principal list.
4297 mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
4298 mLines.splice(mLines.begin(), overflowLines->mLines);
4299 NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
4300 delete overflowLines;
4301 didFindOverflow = true;
4305 // Now append our own overflow lines.
4306 return DrainSelfOverflowList() || didFindOverflow;
4309 bool
4310 nsBlockFrame::DrainSelfOverflowList()
4312 nsAutoPtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
4313 if (!ourOverflowLines) {
4314 return false;
4317 // No need to reparent frames in our own overflow lines/oofs, because they're
4318 // already ours. But we should put overflow floats back in mFloats.
4319 nsAutoOOFFrameList oofs(this);
4320 if (oofs.mList.NotEmpty()) {
4321 // The overflow floats go after our regular floats.
4322 mFloats.AppendFrames(nullptr, oofs.mList);
4325 if (!ourOverflowLines->mLines.empty()) {
4326 mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
4327 mLines.splice(mLines.end(), ourOverflowLines->mLines);
4329 return true;
4333 * Pushed floats are floats whose placeholders are in a previous
4334 * continuation. They might themselves be next-continuations of a float
4335 * that partially fit in an earlier continuation, or they might be the
4336 * first continuation of a float that couldn't be placed at all.
4338 * Pushed floats live permanently at the beginning of a block's float
4339 * list, where they must live *before* any floats whose placeholders are
4340 * in that block.
4342 * Temporarily, during reflow, they also live on the pushed floats list,
4343 * which only holds them between (a) when one continuation pushes them to
4344 * its pushed floats list because they don't fit and (b) when the next
4345 * continuation pulls them onto the beginning of its float list.
4347 * DrainPushedFloats sets up pushed floats the way we need them at the
4348 * start of reflow; they are then reflowed by ReflowPushedFloats (which
4349 * might push some of them on). Floats with placeholders in this block
4350 * are reflowed by (nsBlockReflowState/nsLineLayout)::AddFloat, which
4351 * also maintains these invariants.
4353 void
4354 nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState)
4356 #ifdef DEBUG
4357 // Between when we drain pushed floats and when we complete reflow,
4358 // we're allowed to have multiple continuations of the same float on
4359 // our floats list, since a first-in-flow might get pushed to a later
4360 // continuation of its containing block. But it's not permitted
4361 // outside that time.
4362 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
4363 #endif
4365 // If we're getting reflowed multiple times without our
4366 // next-continuation being reflowed, we might need to pull back floats
4367 // that we just put in the list to be pushed to our next-in-flow.
4368 // We don't want to pull back any next-in-flows of floats on our own
4369 // float list, and we only need to pull back first-in-flows whose
4370 // placeholders were in earlier blocks (since first-in-flows whose
4371 // placeholders are in this block will get pulled appropriately by
4372 // AddFloat, and will then be more likely to be in the correct order).
4373 // FIXME: What if there's a continuation in our pushed floats list
4374 // whose prev-in-flow is in a previous continuation of this block
4375 // rather than this block? Might we need to pull it back so we don't
4376 // report ourselves complete?
4377 // FIXME: Maybe we should just pull all of them back?
4378 nsPresContext* presContext = PresContext();
4379 nsFrameList* ourPushedFloats = GetPushedFloats();
4380 if (ourPushedFloats) {
4381 // When we pull back floats, we want to put them with the pushed
4382 // floats, which must live at the start of our float list, but we
4383 // want them at the end of those pushed floats.
4384 // FIXME: This isn't quite right! What if they're all pushed floats?
4385 nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
4386 for (nsIFrame* f = mFloats.FirstChild();
4387 f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
4388 f = f->GetNextSibling()) {
4389 insertionPrevSibling = f;
4392 for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
4393 next = f->GetPrevSibling();
4395 if (f->GetPrevContinuation()) {
4396 // FIXME
4397 } else {
4398 nsPlaceholderFrame *placeholder =
4399 presContext->FrameManager()->GetPlaceholderFrameFor(f);
4400 nsIFrame *floatOriginalParent = presContext->PresShell()->
4401 FrameConstructor()->GetFloatContainingBlock(placeholder);
4402 if (floatOriginalParent != this) {
4403 // This is a first continuation that was pushed from one of our
4404 // previous continuations. Take it out of the pushed floats
4405 // list and put it in our floats list, before any of our
4406 // floats, but after other pushed floats.
4407 ourPushedFloats->RemoveFrame(f);
4408 mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
4413 if (ourPushedFloats->IsEmpty()) {
4414 RemovePushedFloats()->Delete(presContext->PresShell());
4418 // After our prev-in-flow has completed reflow, it may have a pushed
4419 // floats list, containing floats that we need to own. Take these.
4420 nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
4421 if (prevBlock) {
4422 AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats());
4423 if (list && list->NotEmpty()) {
4424 mFloats.InsertFrames(this, nullptr, *list);
4429 nsBlockFrame::FrameLines*
4430 nsBlockFrame::GetOverflowLines() const
4432 if (!HasOverflowLines()) {
4433 return nullptr;
4435 FrameLines* prop =
4436 static_cast<FrameLines*>(Properties().Get(OverflowLinesProperty()));
4437 NS_ASSERTION(prop && !prop->mLines.empty() &&
4438 prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
4439 prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
4440 "value should always be stored and non-empty when state set");
4441 return prop;
4444 nsBlockFrame::FrameLines*
4445 nsBlockFrame::RemoveOverflowLines()
4447 if (!HasOverflowLines()) {
4448 return nullptr;
4450 FrameLines* prop =
4451 static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
4452 NS_ASSERTION(prop && !prop->mLines.empty() &&
4453 prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
4454 prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
4455 "value should always be stored and non-empty when state set");
4456 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4457 return prop;
4460 void
4461 nsBlockFrame::DestroyOverflowLines()
4463 NS_ASSERTION(HasOverflowLines(), "huh?");
4464 FrameLines* prop =
4465 static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
4466 NS_ASSERTION(prop && prop->mLines.empty(),
4467 "value should always be stored but empty when destroying");
4468 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4469 delete prop;
4472 // This takes ownership of aOverflowLines.
4473 // XXX We should allocate overflowLines from presShell arena!
4474 void
4475 nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines)
4477 NS_ASSERTION(aOverflowLines, "null lines");
4478 NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
4479 NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
4480 aOverflowLines->mFrames.FirstChild(),
4481 "invalid overflow lines / frames");
4482 NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
4483 "Overwriting existing overflow lines");
4485 FrameProperties props = Properties();
4486 // Verify that we won't overwrite an existing overflow list
4487 NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
4488 props.Set(OverflowLinesProperty(), aOverflowLines);
4489 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4492 nsFrameList*
4493 nsBlockFrame::GetOverflowOutOfFlows() const
4495 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4496 return nullptr;
4498 nsFrameList* result =
4499 GetPropTableFrames(PresContext(), OverflowOutOfFlowsProperty());
4500 NS_ASSERTION(result, "value should always be non-empty when state set");
4501 return result;
4504 // This takes ownership of the frames
4505 void
4506 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
4507 nsFrameList* aPropValue)
4509 NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
4510 !!aPropValue, "state does not match value");
4512 if (aList.IsEmpty()) {
4513 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4514 return;
4516 nsPresContext* pc = PresContext();
4517 nsFrameList* list = RemovePropTableFrames(pc, OverflowOutOfFlowsProperty());
4518 NS_ASSERTION(aPropValue == list, "prop value mismatch");
4519 list->Clear();
4520 list->Delete(pc->PresShell());
4521 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4523 else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
4524 NS_ASSERTION(aPropValue == GetPropTableFrames(PresContext(),
4525 OverflowOutOfFlowsProperty()),
4526 "prop value mismatch");
4527 *aPropValue = aList;
4529 else {
4530 nsPresContext* pc = PresContext();
4531 SetPropTableFrames(pc, new (pc->PresShell()) nsFrameList(aList),
4532 OverflowOutOfFlowsProperty());
4533 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4537 nsBulletFrame*
4538 nsBlockFrame::GetInsideBullet() const
4540 if (!HasInsideBullet()) {
4541 return nullptr;
4543 NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
4544 nsBulletFrame* frame =
4545 static_cast<nsBulletFrame*>(Properties().Get(InsideBulletProperty()));
4546 NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame,
4547 "bogus inside bullet frame");
4548 return frame;
4551 nsBulletFrame*
4552 nsBlockFrame::GetOutsideBullet() const
4554 nsFrameList* list = GetOutsideBulletList();
4555 return list ? static_cast<nsBulletFrame*>(list->FirstChild())
4556 : nullptr;
4559 nsFrameList*
4560 nsBlockFrame::GetOutsideBulletList() const
4562 if (!HasOutsideBullet()) {
4563 return nullptr;
4565 NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
4566 nsFrameList* list =
4567 static_cast<nsFrameList*>(Properties().Get(OutsideBulletProperty()));
4568 NS_ASSERTION(list && list->GetLength() == 1 &&
4569 list->FirstChild()->GetType() == nsGkAtoms::bulletFrame,
4570 "bogus outside bullet list");
4571 return list;
4574 nsFrameList*
4575 nsBlockFrame::GetPushedFloats() const
4577 if (!HasPushedFloats()) {
4578 return nullptr;
4580 nsFrameList* result =
4581 static_cast<nsFrameList*>(Properties().Get(PushedFloatProperty()));
4582 NS_ASSERTION(result, "value should always be non-empty when state set");
4583 return result;
4586 nsFrameList*
4587 nsBlockFrame::EnsurePushedFloats()
4589 nsFrameList *result = GetPushedFloats();
4590 if (result)
4591 return result;
4593 result = new (PresContext()->PresShell()) nsFrameList;
4594 Properties().Set(PushedFloatProperty(), result);
4595 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
4597 return result;
4600 nsFrameList*
4601 nsBlockFrame::RemovePushedFloats()
4603 if (!HasPushedFloats()) {
4604 return nullptr;
4606 nsFrameList *result =
4607 static_cast<nsFrameList*>(Properties().Remove(PushedFloatProperty()));
4608 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
4609 NS_ASSERTION(result, "value should always be non-empty when state set");
4610 return result;
4613 //////////////////////////////////////////////////////////////////////
4614 // Frame list manipulation routines
4616 NS_IMETHODIMP
4617 nsBlockFrame::AppendFrames(ChildListID aListID,
4618 nsFrameList& aFrameList)
4620 if (aFrameList.IsEmpty()) {
4621 return NS_OK;
4623 if (aListID != kPrincipalList) {
4624 if (kAbsoluteList == aListID) {
4625 return nsContainerFrame::AppendFrames(aListID, aFrameList);
4627 else if (kFloatList == aListID) {
4628 mFloats.AppendFrames(nullptr, aFrameList);
4629 return NS_OK;
4631 else {
4632 NS_ERROR("unexpected child list");
4633 return NS_ERROR_INVALID_ARG;
4637 // Find the proper last-child for where the append should go
4638 nsIFrame* lastKid = mFrames.LastChild();
4639 NS_ASSERTION((mLines.empty() ? nullptr : mLines.back()->LastChild()) ==
4640 lastKid, "out-of-sync mLines / mFrames");
4642 // Add frames after the last child
4643 #ifdef NOISY_REFLOW_REASON
4644 ListTag(stdout);
4645 printf(": append ");
4646 nsFrame::ListTag(stdout, aFrameList);
4647 if (lastKid) {
4648 printf(" after ");
4649 nsFrame::ListTag(stdout, lastKid);
4651 printf("\n");
4652 #endif
4654 AddFrames(aFrameList, lastKid);
4655 PresContext()->PresShell()->
4656 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4657 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4658 return NS_OK;
4661 NS_IMETHODIMP
4662 nsBlockFrame::InsertFrames(ChildListID aListID,
4663 nsIFrame* aPrevFrame,
4664 nsFrameList& aFrameList)
4666 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
4667 "inserting after sibling frame with different parent");
4669 if (aListID != kPrincipalList) {
4670 if (kAbsoluteList == aListID) {
4671 return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
4673 else if (kFloatList == aListID) {
4674 mFloats.InsertFrames(this, aPrevFrame, aFrameList);
4675 return NS_OK;
4677 #ifdef IBMBIDI
4678 else if (kNoReflowPrincipalList == aListID) {}
4679 #endif // IBMBIDI
4680 else {
4681 NS_ERROR("unexpected child list");
4682 return NS_ERROR_INVALID_ARG;
4686 #ifdef NOISY_REFLOW_REASON
4687 ListTag(stdout);
4688 printf(": insert ");
4689 nsFrame::ListTag(stdout, aFrameList);
4690 if (aPrevFrame) {
4691 printf(" after ");
4692 nsFrame::ListTag(stdout, aPrevFrame);
4694 printf("\n");
4695 #endif
4697 AddFrames(aFrameList, aPrevFrame);
4699 #ifdef IBMBIDI
4700 if (aListID != kNoReflowPrincipalList)
4701 #endif // IBMBIDI
4702 PresContext()->PresShell()->
4703 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4704 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4705 return NS_OK;
4708 static bool
4709 ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
4711 nsIAtom* type = aLastFrame->GetType();
4712 if (type == nsGkAtoms::brFrame) {
4713 return true;
4715 // XXX the IS_DIRTY check is a wallpaper for bug 822910.
4716 if (type == nsGkAtoms::textFrame &&
4717 !(aLastFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
4718 return aLastFrame->HasTerminalNewline() &&
4719 aLastFrame->StyleText()->NewlineIsSignificant();
4721 return false;
4724 void
4725 nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
4727 // Clear our line cursor, since our lines may change.
4728 ClearLineCursor();
4730 if (aFrameList.IsEmpty()) {
4731 return;
4734 // If we're inserting at the beginning of our list and we have an
4735 // inside bullet, insert after that bullet.
4736 if (!aPrevSibling && HasInsideBullet()) {
4737 aPrevSibling = GetInsideBullet();
4740 // Attempt to find the line that contains the previous sibling
4741 FrameLines* overflowLines;
4742 nsLineList* lineList = &mLines;
4743 nsLineList::iterator prevSibLine = lineList->end();
4744 int32_t prevSiblingIndex = -1;
4745 if (aPrevSibling) {
4746 // XXX_perf This is technically O(N^2) in some cases, but by using
4747 // RFind instead of Find, we make it O(N) in the most common case,
4748 // which is appending content.
4750 // Find the line that contains the previous sibling
4751 if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
4752 prevSibLine, mFrames.LastChild(),
4753 &prevSiblingIndex)) {
4754 // Not in mLines - try overflow lines.
4755 overflowLines = GetOverflowLines();
4756 lineList = overflowLines ? &overflowLines->mLines : nullptr;
4757 if (overflowLines) {
4758 prevSibLine = overflowLines->mLines.end();
4759 prevSiblingIndex = -1;
4760 if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
4761 prevSibLine,
4762 overflowLines->mFrames.LastChild(),
4763 &prevSiblingIndex)) {
4764 lineList = nullptr;
4767 if (!lineList) {
4768 // Note: defensive code! RFindLineContaining must not return
4769 // false in this case, so if it does...
4770 NS_NOTREACHED("prev sibling not in line list");
4771 lineList = &mLines;
4772 aPrevSibling = nullptr;
4773 prevSibLine = lineList->end();
4778 // Find the frame following aPrevSibling so that we can join up the
4779 // two lists of frames.
4780 if (aPrevSibling) {
4781 // Split line containing aPrevSibling in two if the insertion
4782 // point is somewhere in the middle of the line.
4783 int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
4784 if (rem) {
4785 // Split the line in two where the frame(s) are being inserted.
4786 nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
4787 lineList->after_insert(prevSibLine, line);
4788 // Mark prevSibLine dirty and as needing textrun invalidation, since
4789 // we may be breaking up text in the line. Its previous line may also
4790 // need to be invalidated because it may be able to pull some text up.
4791 MarkLineDirty(prevSibLine, lineList);
4792 // The new line will also need its textruns recomputed because of the
4793 // frame changes.
4794 line->MarkDirty();
4795 line->SetInvalidateTextRuns(true);
4798 else if (! lineList->empty()) {
4799 lineList->front()->MarkDirty();
4800 lineList->front()->SetInvalidateTextRuns(true);
4802 nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames;
4803 const nsFrameList::Slice& newFrames =
4804 frames.InsertFrames(nullptr, aPrevSibling, aFrameList);
4806 // Walk through the new frames being added and update the line data
4807 // structures to fit.
4808 for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
4809 nsIFrame* newFrame = e.get();
4810 NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
4811 "Unexpected aPrevSibling");
4812 NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
4813 (!newFrame->IsAbsolutelyPositioned() &&
4814 !newFrame->IsFloating()),
4815 "Placeholders should not float or be positioned");
4817 bool isBlock = newFrame->IsBlockOutside();
4819 // If the frame is a block frame, or if there is no previous line or if the
4820 // previous line is a block line we need to make a new line. We also make
4821 // a new line, as an optimization, in the two cases we know we'll need it:
4822 // if the previous line ended with a <br>, or if it has significant whitespace
4823 // and ended in a newline.
4824 if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
4825 (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
4826 // Create a new line for the frame and add its line to the line
4827 // list.
4828 nsLineBox* line = NewLineBox(newFrame, isBlock);
4829 if (prevSibLine != lineList->end()) {
4830 // Append new line after prevSibLine
4831 lineList->after_insert(prevSibLine, line);
4832 ++prevSibLine;
4834 else {
4835 // New line is going before the other lines
4836 lineList->push_front(line);
4837 prevSibLine = lineList->begin();
4840 else {
4841 prevSibLine->NoteFrameAdded(newFrame);
4842 // We're adding inline content to prevSibLine, so we need to mark it
4843 // dirty, ensure its textruns are recomputed, and possibly do the same
4844 // to its previous line since that line may be able to pull content up.
4845 MarkLineDirty(prevSibLine, lineList);
4848 aPrevSibling = newFrame;
4851 #ifdef DEBUG
4852 MOZ_ASSERT(aFrameList.IsEmpty());
4853 VerifyLines(true);
4854 #endif
4857 void
4858 nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat)
4860 // Find which line contains the float, so we can update
4861 // the float cache.
4862 line_iterator line = begin_lines(), line_end = end_lines();
4863 for ( ; line != line_end; ++line) {
4864 if (line->IsInline() && line->RemoveFloat(aFloat)) {
4865 break;
4870 void
4871 nsBlockFrame::RemoveFloat(nsIFrame* aFloat)
4873 #ifdef DEBUG
4874 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
4875 // frame list properties.
4876 if (!mFloats.ContainsFrame(aFloat)) {
4877 MOZ_ASSERT((GetOverflowOutOfFlows() &&
4878 GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
4879 (GetPushedFloats() &&
4880 GetPushedFloats()->ContainsFrame(aFloat)),
4881 "aFloat is not our child or on an unexpected frame list");
4883 #endif
4885 if (mFloats.StartRemoveFrame(aFloat)) {
4886 return;
4889 nsFrameList* list = GetPushedFloats();
4890 if (list && list->ContinueRemoveFrame(aFloat)) {
4891 #if 0
4892 // XXXmats not yet - need to investigate nsBlockReflowState::mPushedFloats
4893 // first so we don't leave it pointing to a deleted list.
4894 if (list->IsEmpty()) {
4895 delete RemovePushedFloats();
4897 #endif
4898 return;
4902 nsAutoOOFFrameList oofs(this);
4903 if (oofs.mList.ContinueRemoveFrame(aFloat)) {
4904 return;
4909 static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
4911 nsBlockFrame* blockWithFloatMgr = aBlock;
4912 while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) {
4913 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent());
4914 if (!bf) {
4915 break;
4917 blockWithFloatMgr = bf;
4920 // Mark every line at and below the line where the float was
4921 // dirty, and mark their lines dirty too. We could probably do
4922 // something more efficient --- e.g., just dirty the lines that intersect
4923 // the float vertically.
4924 MarkAllDescendantLinesDirty(blockWithFloatMgr);
4928 * Returns true if aFrame is a block that has one or more float children.
4930 static bool BlockHasAnyFloats(nsIFrame* aFrame)
4932 nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
4933 if (!block)
4934 return false;
4935 if (block->GetFirstChild(nsIFrame::kFloatList))
4936 return true;
4938 nsLineList::iterator line = block->begin_lines();
4939 nsLineList::iterator endLine = block->end_lines();
4940 while (line != endLine) {
4941 if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
4942 return true;
4943 ++line;
4945 return false;
4948 NS_IMETHODIMP
4949 nsBlockFrame::RemoveFrame(ChildListID aListID,
4950 nsIFrame* aOldFrame)
4952 nsresult rv = NS_OK;
4954 #ifdef NOISY_REFLOW_REASON
4955 ListTag(stdout);
4956 printf(": remove ");
4957 nsFrame::ListTag(stdout, aOldFrame);
4958 printf("\n");
4959 #endif
4961 if (aListID == kPrincipalList) {
4962 bool hasFloats = BlockHasAnyFloats(aOldFrame);
4963 rv = DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
4964 if (hasFloats) {
4965 MarkSameFloatManagerLinesDirty(this);
4968 else if (kAbsoluteList == aListID) {
4969 nsContainerFrame::RemoveFrame(aListID, aOldFrame);
4970 return NS_OK;
4972 else if (kFloatList == aListID) {
4973 // Make sure to mark affected lines dirty for the float frame
4974 // we are removing; this way is a bit messy, but so is the rest of the code.
4975 // See bug 390762.
4976 NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
4977 "RemoveFrame should not be called on pushed floats.");
4978 for (nsIFrame* f = aOldFrame;
4979 f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
4980 f = f->GetNextContinuation()) {
4981 MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
4983 DoRemoveOutOfFlowFrame(aOldFrame);
4985 #ifdef IBMBIDI
4986 else if (kNoReflowPrincipalList == aListID) {
4987 // Skip the call to |FrameNeedsReflow| below by returning now.
4988 return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
4990 #endif // IBMBIDI
4991 else {
4992 NS_ERROR("unexpected child list");
4993 rv = NS_ERROR_INVALID_ARG;
4996 if (NS_SUCCEEDED(rv)) {
4997 PresContext()->PresShell()->
4998 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4999 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
5001 return rv;
5004 void
5005 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
5007 // The containing block is always the parent of aFrame.
5008 nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
5010 // Remove aFrame from the appropriate list.
5011 if (aFrame->IsAbsolutelyPositioned()) {
5012 // This also deletes the next-in-flows
5013 block->GetAbsoluteContainingBlock()->RemoveFrame(block,
5014 kAbsoluteList,
5015 aFrame);
5017 else {
5018 // First remove aFrame's next-in-flows.
5019 nsIFrame* nif = aFrame->GetNextInFlow();
5020 if (nif) {
5021 static_cast<nsContainerFrame*>(nif->GetParent())
5022 ->DeleteNextInFlowChild(aFrame->PresContext(), nif, false);
5024 // Now remove aFrame from its child list and Destroy it.
5025 block->RemoveFloatFromFloatCache(aFrame);
5026 block->RemoveFloat(aFrame);
5027 aFrame->Destroy();
5032 * This helps us iterate over the list of all normal + overflow lines
5034 void
5035 nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
5036 nsLineList::iterator* aStartIterator,
5037 nsLineList::iterator* aEndIterator,
5038 bool* aInOverflowLines,
5039 FrameLines** aOverflowLines)
5041 if (*aIterator == *aEndIterator) {
5042 if (!*aInOverflowLines) {
5043 // Try the overflow lines
5044 *aInOverflowLines = true;
5045 FrameLines* lines = GetOverflowLines();
5046 if (lines) {
5047 *aStartIterator = lines->mLines.begin();
5048 *aIterator = *aStartIterator;
5049 *aEndIterator = lines->mLines.end();
5050 *aOverflowLines = lines;
5056 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5057 line_iterator aLine)
5058 : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines)
5060 // This will assert if aLine isn't in mLines of aFrame:
5061 DebugOnly<bool> check = aLine == mFrame->begin_lines();
5064 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5065 line_iterator aLine, bool aInOverflow)
5066 : mFrame(aFrame), mLine(aLine),
5067 mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
5068 : &aFrame->mLines)
5072 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5073 bool* aFoundValidLine)
5074 : mFrame(aFrame), mLineList(&aFrame->mLines)
5076 mLine = aFrame->begin_lines();
5077 *aFoundValidLine = FindValidLine();
5080 static nsIFrame*
5081 FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
5083 NS_ASSERTION(aFrame, "must have frame");
5084 nsIFrame* child;
5085 while (true) {
5086 nsIFrame* block = aFrame;
5087 do {
5088 child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
5089 if (child)
5090 break;
5091 block = block->GetNextContinuation();
5092 } while (block);
5093 if (!child)
5094 return nullptr;
5095 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
5096 break;
5097 aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child);
5100 return child;
5103 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5104 nsIFrame* aFindFrame, bool* aFoundValidLine)
5105 : mFrame(aFrame), mLineList(&aFrame->mLines)
5107 *aFoundValidLine = false;
5109 nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
5110 if (!child)
5111 return;
5113 // Try to use the cursor if it exists, otherwise fall back to the first line
5114 nsLineBox* cursor = aFrame->GetLineCursor();
5115 if (!cursor) {
5116 line_iterator iter = aFrame->begin_lines();
5117 if (iter != aFrame->end_lines()) {
5118 cursor = iter;
5122 if (cursor) {
5123 // Perform a simultaneous forward and reverse search starting from the
5124 // line cursor.
5125 nsBlockFrame::line_iterator line = aFrame->line(cursor);
5126 nsBlockFrame::reverse_line_iterator rline = aFrame->rline(cursor);
5127 nsBlockFrame::line_iterator line_end = aFrame->end_lines();
5128 nsBlockFrame::reverse_line_iterator rline_end = aFrame->rend_lines();
5129 // rline is positioned on the line containing 'cursor', so it's not
5130 // rline_end. So we can safely increment it (i.e. move it to one line
5131 // earlier) to start searching there.
5132 ++rline;
5133 while (line != line_end || rline != rline_end) {
5134 if (line != line_end) {
5135 if (line->Contains(child)) {
5136 *aFoundValidLine = true;
5137 mLine = line;
5138 return;
5140 ++line;
5142 if (rline != rline_end) {
5143 if (rline->Contains(child)) {
5144 *aFoundValidLine = true;
5145 mLine = rline;
5146 return;
5148 ++rline;
5151 // Didn't find the line
5154 // If we reach here, it means that we have not been able to find the
5155 // desired frame in our in-flow lines. So we should start looking at
5156 // our overflow lines. In order to do that, we set mLine to the end
5157 // iterator so that FindValidLine starts to look at overflow lines,
5158 // if any.
5160 mLine = aFrame->end_lines();
5162 if (!FindValidLine())
5163 return;
5165 do {
5166 if (mLine->Contains(child)) {
5167 *aFoundValidLine = true;
5168 return;
5170 } while (Next());
5173 nsBlockFrame::line_iterator
5174 nsBlockInFlowLineIterator::End()
5176 return mLineList->end();
5179 bool
5180 nsBlockInFlowLineIterator::IsLastLineInList()
5182 line_iterator end = End();
5183 return mLine != end && mLine.next() == end;
5186 bool
5187 nsBlockInFlowLineIterator::Next()
5189 ++mLine;
5190 return FindValidLine();
5193 bool
5194 nsBlockInFlowLineIterator::Prev()
5196 line_iterator begin = mLineList->begin();
5197 if (mLine != begin) {
5198 --mLine;
5199 return true;
5201 bool currentlyInOverflowLines = GetInOverflow();
5202 while (true) {
5203 if (currentlyInOverflowLines) {
5204 mLineList = &mFrame->mLines;
5205 mLine = mLineList->end();
5206 if (mLine != mLineList->begin()) {
5207 --mLine;
5208 return true;
5210 } else {
5211 mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
5212 if (!mFrame)
5213 return false;
5214 nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
5215 if (overflowLines) {
5216 mLineList = &overflowLines->mLines;
5217 mLine = mLineList->end();
5218 NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
5219 --mLine;
5220 return true;
5223 currentlyInOverflowLines = !currentlyInOverflowLines;
5227 bool
5228 nsBlockInFlowLineIterator::FindValidLine()
5230 line_iterator end = mLineList->end();
5231 if (mLine != end)
5232 return true;
5233 bool currentlyInOverflowLines = GetInOverflow();
5234 while (true) {
5235 if (currentlyInOverflowLines) {
5236 mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
5237 if (!mFrame)
5238 return false;
5239 mLineList = &mFrame->mLines;
5240 mLine = mLineList->begin();
5241 if (mLine != mLineList->end())
5242 return true;
5243 } else {
5244 nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
5245 if (overflowLines) {
5246 mLineList = &overflowLines->mLines;
5247 mLine = mLineList->begin();
5248 NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
5249 return true;
5252 currentlyInOverflowLines = !currentlyInOverflowLines;
5256 static nsresult RemoveBlockChild(nsIFrame* aFrame,
5257 bool aRemoveOnlyFluidContinuations)
5259 if (!aFrame)
5260 return NS_OK;
5262 nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent());
5263 NS_ASSERTION(nextBlock,
5264 "Our child's continuation's parent is not a block?");
5265 return nextBlock->DoRemoveFrame(aFrame,
5266 (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS));
5269 // This function removes aDeletedFrame and all its continuations. It
5270 // is optimized for deleting a whole series of frames. The easy
5271 // implementation would invoke itself recursively on
5272 // aDeletedFrame->GetNextContinuation, then locate the line containing
5273 // aDeletedFrame and remove aDeletedFrame from that line. But here we
5274 // start by locating aDeletedFrame and then scanning from that point
5275 // on looking for continuations.
5276 nsresult
5277 nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags)
5279 // Clear our line cursor, since our lines may change.
5280 ClearLineCursor();
5282 nsPresContext* presContext = PresContext();
5283 if (aDeletedFrame->GetStateBits() &
5284 (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5285 if (!aDeletedFrame->GetPrevInFlow()) {
5286 NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5287 "Expected out-of-flow frame");
5288 DoRemoveOutOfFlowFrame(aDeletedFrame);
5290 else {
5291 nsContainerFrame::DeleteNextInFlowChild(presContext, aDeletedFrame,
5292 (aFlags & FRAMES_ARE_EMPTY) != 0);
5294 return NS_OK;
5297 // Find the line that contains deletedFrame
5298 nsLineList::iterator line_start = mLines.begin(),
5299 line_end = mLines.end();
5300 nsLineList::iterator line = line_start;
5301 FrameLines* overflowLines = nullptr;
5302 bool searchingOverflowList = false;
5303 // Make sure we look in the overflow lines even if the normal line
5304 // list is empty
5305 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5306 &overflowLines);
5307 while (line != line_end) {
5308 if (line->Contains(aDeletedFrame)) {
5309 break;
5311 ++line;
5312 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5313 &overflowLines);
5316 if (line == line_end) {
5317 NS_ERROR("can't find deleted frame in lines");
5318 return NS_ERROR_FAILURE;
5321 if (!(aFlags & FRAMES_ARE_EMPTY)) {
5322 if (line != line_start) {
5323 line.prev()->MarkDirty();
5324 line.prev()->SetInvalidateTextRuns(true);
5326 else if (searchingOverflowList && !mLines.empty()) {
5327 mLines.back()->MarkDirty();
5328 mLines.back()->SetInvalidateTextRuns(true);
5332 while (line != line_end && aDeletedFrame) {
5333 NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
5334 NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
5336 if (!(aFlags & FRAMES_ARE_EMPTY)) {
5337 line->MarkDirty();
5338 line->SetInvalidateTextRuns(true);
5341 // If the frame being deleted is the last one on the line then
5342 // optimize away the line->Contains(next-in-flow) call below.
5343 bool isLastFrameOnLine = 1 == line->GetChildCount();
5344 if (!isLastFrameOnLine) {
5345 line_iterator next = line.next();
5346 nsIFrame* lastFrame = next != line_end ?
5347 next->mFirstChild->GetPrevSibling() :
5348 (searchingOverflowList ? overflowLines->mFrames.LastChild() :
5349 mFrames.LastChild());
5350 NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
5351 "unexpected line frames");
5352 isLastFrameOnLine = lastFrame == aDeletedFrame;
5355 // Remove aDeletedFrame from the line
5356 if (line->mFirstChild == aDeletedFrame) {
5357 // We should be setting this to null if aDeletedFrame
5358 // is the only frame on the line. HOWEVER in that case
5359 // we will be removing the line anyway, see below.
5360 line->mFirstChild = aDeletedFrame->GetNextSibling();
5363 // Hmm, this won't do anything if we're removing a frame in the first
5364 // overflow line... Hopefully doesn't matter
5365 --line;
5366 if (line != line_end && !line->IsBlock()) {
5367 // Since we just removed a frame that follows some inline
5368 // frames, we need to reflow the previous line.
5369 line->MarkDirty();
5371 ++line;
5373 // Take aDeletedFrame out of the sibling list. Note that
5374 // prevSibling will only be nullptr when we are deleting the very
5375 // first frame in the main or overflow list.
5376 if (searchingOverflowList) {
5377 overflowLines->mFrames.RemoveFrame(aDeletedFrame);
5378 } else {
5379 mFrames.RemoveFrame(aDeletedFrame);
5382 // Update the child count of the line to be accurate
5383 line->NoteFrameRemoved(aDeletedFrame);
5385 // Destroy frame; capture its next continuation first in case we need
5386 // to destroy that too.
5387 nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ?
5388 aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow();
5389 #ifdef NOISY_REMOVE_FRAME
5390 printf("DoRemoveFrame: %s line=%p frame=",
5391 searchingOverflowList?"overflow":"normal", line.get());
5392 nsFrame::ListTag(stdout, aDeletedFrame);
5393 printf(" prevSibling=%p deletedNextContinuation=%p\n",
5394 aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
5395 #endif
5397 // If next-in-flow is an overflow container, must remove it first.
5398 if (deletedNextContinuation &&
5399 deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
5400 static_cast<nsContainerFrame*>(deletedNextContinuation->GetParent())
5401 ->DeleteNextInFlowChild(presContext, deletedNextContinuation, false);
5402 deletedNextContinuation = nullptr;
5405 aDeletedFrame->Destroy();
5406 aDeletedFrame = deletedNextContinuation;
5408 bool haveAdvancedToNextLine = false;
5409 // If line is empty, remove it now.
5410 if (0 == line->GetChildCount()) {
5411 #ifdef NOISY_REMOVE_FRAME
5412 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
5413 searchingOverflowList?"overflow":"normal", line.get());
5414 #endif
5415 nsLineBox *cur = line;
5416 if (!searchingOverflowList) {
5417 line = mLines.erase(line);
5418 // Invalidate the space taken up by the line.
5419 // XXX We need to do this if we're removing a frame as a result of
5420 // a call to RemoveFrame(), but we may not need to do this in all
5421 // cases...
5422 #ifdef NOISY_BLOCK_INVALIDATE
5423 nsRect visOverflow(cur->GetVisualOverflowArea());
5424 printf("%p invalidate 10 (%d, %d, %d, %d)\n",
5425 this, visOverflow.x, visOverflow.y,
5426 visOverflow.width, visOverflow.height);
5427 #endif
5428 } else {
5429 line = overflowLines->mLines.erase(line);
5430 if (overflowLines->mLines.empty()) {
5431 DestroyOverflowLines();
5432 overflowLines = nullptr;
5433 // We just invalidated our iterators. Since we were in
5434 // the overflow lines list, which is now empty, set them
5435 // so we're at the end of the regular line list.
5436 line_start = mLines.begin();
5437 line_end = mLines.end();
5438 line = line_end;
5441 FreeLineBox(cur);
5443 // If we're removing a line, ReflowDirtyLines isn't going to
5444 // know that it needs to slide lines unless something is marked
5445 // dirty. So mark the previous margin of the next line dirty if
5446 // there is one.
5447 if (line != line_end) {
5448 line->MarkPreviousMarginDirty();
5450 haveAdvancedToNextLine = true;
5451 } else {
5452 // Make the line that just lost a frame dirty, and advance to
5453 // the next line.
5454 if (!deletedNextContinuation || isLastFrameOnLine ||
5455 !line->Contains(deletedNextContinuation)) {
5456 line->MarkDirty();
5457 ++line;
5458 haveAdvancedToNextLine = true;
5462 if (deletedNextContinuation) {
5463 // See if we should keep looking in the current flow's line list.
5464 if (deletedNextContinuation->GetParent() != this) {
5465 // The deceased frames continuation is not a child of the
5466 // current block. So break out of the loop so that we advance
5467 // to the next parent.
5469 // If we have a continuation in a different block then all bets are
5470 // off regarding whether we are deleting frames without actual content,
5471 // so don't propagate FRAMES_ARE_EMPTY any further.
5472 aFlags &= ~FRAMES_ARE_EMPTY;
5473 break;
5476 // If we advanced to the next line then check if we should switch to the
5477 // overflow line list.
5478 if (haveAdvancedToNextLine) {
5479 if (line != line_end && !searchingOverflowList &&
5480 !line->Contains(deletedNextContinuation)) {
5481 // We have advanced to the next *normal* line but the next-in-flow
5482 // is not there - force a switch to the overflow line list.
5483 line = line_end;
5486 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5487 &overflowLines);
5488 #ifdef NOISY_REMOVE_FRAME
5489 printf("DoRemoveFrame: now on %s line=%p\n",
5490 searchingOverflowList?"overflow":"normal", line.get());
5491 #endif
5496 if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
5497 line.next()->MarkDirty();
5498 line.next()->SetInvalidateTextRuns(true);
5501 #ifdef DEBUG
5502 VerifyLines(true);
5503 VerifyOverflowSituation();
5504 #endif
5506 // Advance to next flow block if the frame has more continuations
5507 return RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS));
5510 static bool
5511 FindBlockLineFor(nsIFrame* aChild,
5512 nsLineList::iterator aBegin,
5513 nsLineList::iterator aEnd,
5514 nsLineList::iterator* aResult)
5516 MOZ_ASSERT(aChild->IsBlockOutside());
5517 for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
5518 MOZ_ASSERT(line->GetChildCount() > 0);
5519 if (line->IsBlock() && line->mFirstChild == aChild) {
5520 MOZ_ASSERT(line->GetChildCount() == 1);
5521 *aResult = line;
5522 return true;
5525 return false;
5528 static bool
5529 FindInlineLineFor(nsIFrame* aChild,
5530 const nsFrameList& aFrameList,
5531 nsLineList::iterator aBegin,
5532 nsLineList::iterator aEnd,
5533 nsLineList::iterator* aResult)
5535 MOZ_ASSERT(!aChild->IsBlockOutside());
5536 for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
5537 MOZ_ASSERT(line->GetChildCount() > 0);
5538 if (!line->IsBlock()) {
5539 // Optimize by comparing the line's last child first.
5540 nsLineList::iterator next = line.next();
5541 if (aChild == (next == aEnd ? aFrameList.LastChild()
5542 : next->mFirstChild->GetPrevSibling()) ||
5543 line->Contains(aChild)) {
5544 *aResult = line;
5545 return true;
5549 return false;
5552 static bool
5553 FindLineFor(nsIFrame* aChild,
5554 const nsFrameList& aFrameList,
5555 nsLineList::iterator aBegin,
5556 nsLineList::iterator aEnd,
5557 nsLineList::iterator* aResult)
5559 return aChild->IsBlockOutside() ?
5560 FindBlockLineFor(aChild, aBegin, aEnd, aResult) :
5561 FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
5564 nsresult
5565 nsBlockFrame::StealFrame(nsPresContext* aPresContext,
5566 nsIFrame* aChild,
5567 bool aForceNormal)
5569 MOZ_ASSERT(aChild->GetParent() == this);
5571 if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
5572 aChild->IsFloating()) {
5573 RemoveFloat(aChild);
5574 return NS_OK;
5577 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
5578 && !aForceNormal) {
5579 return nsContainerFrame::StealFrame(aPresContext, aChild);
5582 MOZ_ASSERT(!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
5584 nsLineList::iterator line;
5585 if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
5586 RemoveFrameFromLine(aChild, line, mFrames, mLines);
5587 } else {
5588 FrameLines* overflowLines = GetOverflowLines();
5589 DebugOnly<bool> found;
5590 found = FindLineFor(aChild, overflowLines->mFrames,
5591 overflowLines->mLines.begin(),
5592 overflowLines->mLines.end(), &line);
5593 MOZ_ASSERT(found);
5594 RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
5595 overflowLines->mLines);
5596 if (overflowLines->mLines.empty()) {
5597 DestroyOverflowLines();
5601 return NS_OK;
5604 void
5605 nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
5606 nsFrameList& aFrameList, nsLineList& aLineList)
5608 aFrameList.RemoveFrame(aChild);
5609 if (aChild == aLine->mFirstChild) {
5610 aLine->mFirstChild = aChild->GetNextSibling();
5612 aLine->NoteFrameRemoved(aChild);
5613 if (aLine->GetChildCount() > 0) {
5614 aLine->MarkDirty();
5615 } else {
5616 // The line became empty - destroy it.
5617 nsLineBox* lineBox = aLine;
5618 aLine = aLineList.erase(aLine);
5619 if (aLine != aLineList.end()) {
5620 aLine->MarkPreviousMarginDirty();
5622 FreeLineBox(lineBox);
5626 void
5627 nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
5628 nsIFrame* aNextInFlow,
5629 bool aDeletingEmptyFrames)
5631 NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
5633 if (aNextInFlow->GetStateBits() &
5634 (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5635 nsContainerFrame::DeleteNextInFlowChild(aPresContext,
5636 aNextInFlow, aDeletingEmptyFrames);
5638 else {
5639 #ifdef DEBUG
5640 if (aDeletingEmptyFrames) {
5641 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
5643 #endif
5644 DoRemoveFrame(aNextInFlow,
5645 aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
5649 ////////////////////////////////////////////////////////////////////////
5650 // Float support
5652 nsRect
5653 nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState,
5654 const nsRect& aFloatAvailableSpace,
5655 nsIFrame* aFloatFrame)
5657 // Compute the available width. By default, assume the width of the
5658 // containing block.
5659 nscoord availWidth;
5660 const nsStyleDisplay* floatDisplay = aFloatFrame->StyleDisplay();
5662 if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
5663 eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
5664 availWidth = aState.mContentArea.width;
5666 else {
5667 // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
5668 // give tables only the available space
5669 // if they can shrink we may not be constrained to place
5670 // them in the next line
5671 availWidth = aFloatAvailableSpace.width;
5674 nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height
5675 ? NS_UNCONSTRAINEDSIZE
5676 : std::max(0, aState.mContentArea.YMost() - aState.mY);
5678 #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS
5679 if (availHeight != NS_UNCONSTRAINEDSIZE &&
5680 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) {
5681 // Tell the float it has unrestricted height, so it won't break.
5682 // If the float doesn't actually fit in the column it will fail to be
5683 // placed, and either move to the top of the next column or just
5684 // overflow.
5685 availHeight = NS_UNCONSTRAINEDSIZE;
5687 #endif
5689 return nsRect(aState.mContentArea.x,
5690 aState.mContentArea.y,
5691 availWidth, availHeight);
5694 nscoord
5695 nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState,
5696 const nsRect& aFloatAvailableSpace,
5697 nsIFrame* aFloat)
5699 NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5700 "aFloat must be an out-of-flow frame");
5701 // Reflow the float.
5702 nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
5703 aFloat);
5705 nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
5706 availSpace.Size());
5707 return floatRS.ComputedWidth() + floatRS.mComputedBorderPadding.LeftRight() +
5708 floatRS.mComputedMargin.LeftRight();
5711 nsresult
5712 nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
5713 const nsRect& aAdjustedAvailableSpace,
5714 nsIFrame* aFloat,
5715 nsMargin& aFloatMargin,
5716 nsMargin& aFloatOffsets,
5717 bool aFloatPushedDown,
5718 nsReflowStatus& aReflowStatus)
5720 NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5721 "aFloat must be an out-of-flow frame");
5722 // Reflow the float.
5723 aReflowStatus = NS_FRAME_COMPLETE;
5725 #ifdef NOISY_FLOAT
5726 printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
5727 aFloat, this,
5728 aFloatAvailableSpace.x, aFloatAvailableSpace.y,
5729 aFloatAvailableSpace.width, aFloatAvailableSpace.height
5731 #endif
5733 nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
5734 nsSize(aAdjustedAvailableSpace.width,
5735 aAdjustedAvailableSpace.height));
5737 // Normally the mIsTopOfPage state is copied from the parent reflow
5738 // state. However, when reflowing a float, if we've placed other
5739 // floats that force this float *down* or *narrower*, we should unset
5740 // the mIsTopOfPage state.
5741 // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
5742 // variable below, which has the exact same effect. Perhaps it should
5743 // be merged into that, except that the test for narrowing here is not
5744 // about adjacency with the top, so it seems misleading.
5745 if (floatRS.mFlags.mIsTopOfPage &&
5746 (aFloatPushedDown ||
5747 aAdjustedAvailableSpace.width != aState.mContentArea.width)) {
5748 floatRS.mFlags.mIsTopOfPage = false;
5751 // Setup a block reflow context to reflow the float.
5752 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
5754 // Reflow the float
5755 bool isAdjacentWithTop = aState.IsAdjacentWithTop();
5757 nsIFrame* clearanceFrame = nullptr;
5758 nsresult rv;
5759 do {
5760 nsCollapsingMargin margin;
5761 bool mayNeedRetry = false;
5762 floatRS.mDiscoveredClearance = nullptr;
5763 // Only first in flow gets a top margin.
5764 if (!aFloat->GetPrevInFlow()) {
5765 nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
5766 clearanceFrame, &mayNeedRetry);
5768 if (mayNeedRetry && !clearanceFrame) {
5769 floatRS.mDiscoveredClearance = &clearanceFrame;
5770 // We don't need to push the float manager state because the the block has its own
5771 // float manager that will be destroyed and recreated
5775 rv = brc.ReflowBlock(aAdjustedAvailableSpace, true, margin,
5776 0, isAdjacentWithTop,
5777 nullptr, floatRS,
5778 aReflowStatus, aState);
5779 } while (NS_SUCCEEDED(rv) && clearanceFrame);
5781 if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus) &&
5782 ShouldAvoidBreakInside(floatRS)) {
5783 aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
5784 } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
5785 (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.height)) {
5786 // An incomplete reflow status means we should split the float
5787 // if the height is constrained (bug 145305).
5788 aReflowStatus = NS_FRAME_COMPLETE;
5791 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
5792 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
5795 if (aFloat->GetType() == nsGkAtoms::letterFrame) {
5796 // We never split floating first letters; an incomplete state for
5797 // such frames simply means that there is more content to be
5798 // reflowed on the line.
5799 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
5800 aReflowStatus = NS_FRAME_COMPLETE;
5803 if (NS_FAILED(rv)) {
5804 return rv;
5807 // Capture the margin and offsets information for the caller
5808 aFloatMargin = floatRS.mComputedMargin; // float margins don't collapse
5809 aFloatOffsets = floatRS.mComputedOffsets;
5811 const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
5813 // Set the rect, make sure the view is properly sized and positioned,
5814 // and tell the frame we're done reflowing it
5815 // XXXldb This seems like the wrong place to be doing this -- shouldn't
5816 // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
5817 // we've positioned the float, and shouldn't we be doing the equivalent
5818 // of |PlaceFrameView| here?
5819 aFloat->SetSize(nsSize(metrics.width, metrics.height));
5820 if (aFloat->HasView()) {
5821 nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat,
5822 aFloat->GetView(),
5823 metrics.VisualOverflow(),
5824 NS_FRAME_NO_MOVE_VIEW);
5826 // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
5827 aFloat->DidReflow(aState.mPresContext, &floatRS,
5828 nsDidReflowStatus::FINISHED);
5830 #ifdef NOISY_FLOAT
5831 printf("end ReflowFloat %p, sized to %d,%d\n",
5832 aFloat, metrics.width, metrics.height);
5833 #endif
5835 return NS_OK;
5838 uint8_t
5839 nsBlockFrame::FindTrailingClear()
5841 // find the break type of the last line
5842 for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
5843 nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
5844 line_iterator endLine = block->end_lines();
5845 if (endLine != block->begin_lines()) {
5846 --endLine;
5847 return endLine->GetBreakTypeAfter();
5850 return NS_STYLE_CLEAR_NONE;
5853 void
5854 nsBlockFrame::ReflowPushedFloats(nsBlockReflowState& aState,
5855 nsOverflowAreas& aOverflowAreas,
5856 nsReflowStatus& aStatus)
5858 // Pushed floats live at the start of our float list; see comment
5859 // above nsBlockFrame::DrainPushedFloats.
5860 for (nsIFrame* f = mFloats.FirstChild(), *next;
5861 f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
5862 f = next) {
5863 // save next sibling now, since reflowing could push the entire
5864 // float, changing its siblings
5865 next = f->GetNextSibling();
5867 // When we push a first-continuation float in a non-initial reflow,
5868 // it's possible that we end up with two continuations with the same
5869 // parent. This happens if, on the previous reflow of the block or
5870 // a previous reflow of the line containing the block, the float was
5871 // split between continuations A and B of the parent, but on the
5872 // current reflow, none of the float can fit in A.
5874 // When this happens, we might even have the two continuations
5875 // out-of-order due to the management of the pushed floats. In
5876 // particular, if the float's placeholder was in a pushed line that
5877 // we reflowed before it was pushed, and we split the float during
5878 // that reflow, we might have the continuation of the float before
5879 // the float itself. (In the general case, however, it's correct
5880 // for floats in the pushed floats list to come before floats
5881 // anchored in pushed lines; however, in this case it's wrong. We
5882 // should probably find a way to fix it somehow, since it leads to
5883 // incorrect layout in some cases.)
5885 // When we have these out-of-order continuations, we might hit the
5886 // next-continuation before the previous-continuation. When that
5887 // happens, just push it. When we reflow the next continuation,
5888 // we'll either pull all of its content back and destroy it (by
5889 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
5890 // pull it out of its current position and push it again (and
5891 // potentially repeat this cycle for the next continuation, although
5892 // hopefully then they'll be in the right order).
5894 // We should also need this code for the in-order case if the first
5895 // continuation of a float gets moved across more than one
5896 // continuation of the containing block. In this case we'd manage
5897 // to push the second continuation without this check, but not the
5898 // third and later.
5899 nsIFrame *prevContinuation = f->GetPrevContinuation();
5900 if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
5901 mFloats.RemoveFrame(f);
5902 aState.AppendPushedFloat(f);
5903 continue;
5906 // Always call FlowAndPlaceFloat; we might need to place this float
5907 // if didn't belong to this block the last time it was reflowed.
5908 aState.FlowAndPlaceFloat(f);
5910 ConsiderChildOverflow(aOverflowAreas, f);
5913 // If there are continued floats, then we may need to continue BR clearance
5914 if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
5915 aState.mFloatBreakType = static_cast<nsBlockFrame*>(GetPrevInFlow())
5916 ->FindTrailingClear();
5920 void
5921 nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager)
5923 // Recover our own floats
5924 nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
5925 // belong to our next-in-flow
5926 for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
5927 nsRect region = nsFloatManager::GetRegionFor(f);
5928 aFloatManager.AddFloat(f, region);
5929 if (!stop && f->GetNextInFlow())
5930 stop = f->GetNextInFlow();
5933 // Recurse into our overflow container children
5934 for (nsIFrame* oc = GetFirstChild(kOverflowContainersList);
5935 oc; oc = oc->GetNextSibling()) {
5936 RecoverFloatsFor(oc, aFloatManager);
5939 // Recurse into our normal children
5940 for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) {
5941 if (line->IsBlock()) {
5942 RecoverFloatsFor(line->mFirstChild, aFloatManager);
5947 void
5948 nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
5949 nsFloatManager& aFloatManager)
5951 NS_PRECONDITION(aFrame, "null frame");
5952 // Only blocks have floats
5953 nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
5954 // Don't recover any state inside a block that has its own space manager
5955 // (we don't currently have any blocks like this, though, thanks to our
5956 // use of extra frames for 'overflow')
5957 if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
5958 // If the element is relatively positioned, then adjust x and y
5959 // accordingly so that we consider relatively positioned frames
5960 // at their original position.
5961 nsPoint pos = block->GetNormalPosition();
5962 aFloatManager.Translate(pos.x, pos.y);
5963 block->RecoverFloats(aFloatManager);
5964 aFloatManager.Translate(-pos.x, -pos.y);
5968 //////////////////////////////////////////////////////////////////////
5969 // Painting, event handling
5971 #ifdef DEBUG
5972 static void ComputeVisualOverflowArea(nsLineList& aLines,
5973 nscoord aWidth, nscoord aHeight,
5974 nsRect& aResult)
5976 nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
5977 for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
5978 line != line_end;
5979 ++line) {
5980 // Compute min and max x/y values for the reflowed frame's
5981 // combined areas
5982 nsRect visOverflow(line->GetVisualOverflowArea());
5983 nscoord x = visOverflow.x;
5984 nscoord y = visOverflow.y;
5985 nscoord xmost = x + visOverflow.width;
5986 nscoord ymost = y + visOverflow.height;
5987 if (x < xa) {
5988 xa = x;
5990 if (xmost > xb) {
5991 xb = xmost;
5993 if (y < ya) {
5994 ya = y;
5996 if (ymost > yb) {
5997 yb = ymost;
6001 aResult.x = xa;
6002 aResult.y = ya;
6003 aResult.width = xb - xa;
6004 aResult.height = yb - ya;
6006 #endif
6008 bool
6009 nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
6011 if (mContent->IsHTML() && (mContent->Tag() == nsGkAtoms::html ||
6012 mContent->Tag() == nsGkAtoms::body))
6013 return true;
6015 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
6016 bool visible;
6017 nsresult rv = aSelection->ContainsNode(node, true, &visible);
6018 return NS_SUCCEEDED(rv) && visible;
6021 #ifdef DEBUG
6022 static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
6023 if (nsBlockFrame::gNoisyDamageRepair) {
6024 nsFrame::IndentBy(stdout, aDepth+1);
6025 nsRect lineArea = aLine->GetVisualOverflowArea();
6026 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6027 aDrawn ? "draw" : "skip",
6028 static_cast<void*>(aLine),
6029 aLine->mBounds.x, aLine->mBounds.y,
6030 aLine->mBounds.width, aLine->mBounds.height,
6031 lineArea.x, lineArea.y,
6032 lineArea.width, lineArea.height);
6035 #endif
6037 static void
6038 DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
6039 const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
6040 int32_t aDepth, int32_t& aDrawnLines, const nsDisplayListSet& aLists,
6041 nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
6042 // If the line's combined area (which includes child frames that
6043 // stick outside of the line's bounding box or our bounding box)
6044 // intersects the dirty rect then paint the line.
6045 bool intersect = aLineArea.Intersects(aDirtyRect);
6046 #ifdef DEBUG
6047 if (nsBlockFrame::gLamePaintMetrics) {
6048 aDrawnLines++;
6050 DebugOutputDrawLine(aDepth, aLine.get(), intersect);
6051 #endif
6052 // The line might contain a placeholder for a visible out-of-flow, in which
6053 // case we need to descend into it. If there is such a placeholder, we will
6054 // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
6055 // In particular, we really want to check ShouldDescendIntoFrame()
6056 // on all the frames on the line, but that might be expensive. So
6057 // we approximate it by checking it on aFrame; if it's true for any
6058 // frame in the line, it's also true for aFrame.
6059 bool lineInline = aLine->IsInline();
6060 bool lineMayHaveTextOverflow = aTextOverflow && lineInline;
6061 if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
6062 !lineMayHaveTextOverflow)
6063 return;
6065 // Collect our line's display items in a temporary nsDisplayListCollection,
6066 // so that we can apply any "text-overflow" clipping to the entire collection
6067 // without affecting previous lines.
6068 nsDisplayListCollection collection;
6070 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
6071 // Inline-level child backgrounds go on the regular child content list.
6072 nsDisplayListSet childLists(collection,
6073 lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
6075 uint32_t flags = lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0;
6077 nsIFrame* kid = aLine->mFirstChild;
6078 int32_t n = aLine->GetChildCount();
6079 while (--n >= 0) {
6080 aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect,
6081 childLists, flags);
6082 kid = kid->GetNextSibling();
6085 if (lineMayHaveTextOverflow) {
6086 aTextOverflow->ProcessLine(collection, aLine.get());
6089 collection.MoveTo(aLists);
6092 void
6093 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
6094 const nsRect& aDirtyRect,
6095 const nsDisplayListSet& aLists)
6097 int32_t drawnLines; // Will only be used if set (gLamePaintMetrics).
6098 int32_t depth = 0;
6099 #ifdef DEBUG
6100 if (gNoisyDamageRepair) {
6101 depth = GetDepth();
6102 nsRect ca;
6103 ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca);
6104 nsFrame::IndentBy(stdout, depth);
6105 ListTag(stdout);
6106 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6107 mRect.x, mRect.y, mRect.width, mRect.height,
6108 aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
6109 ca.x, ca.y, ca.width, ca.height);
6111 PRTime start = 0; // Initialize these variables to silence the compiler.
6112 if (gLamePaintMetrics) {
6113 start = PR_Now();
6114 drawnLines = 0;
6116 #endif
6118 DisplayBorderBackgroundOutline(aBuilder, aLists);
6120 if (GetPrevInFlow()) {
6121 DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
6122 for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
6123 if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
6124 BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
6128 aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
6130 // Prepare for text-overflow processing.
6131 nsAutoPtr<TextOverflow> textOverflow(
6132 TextOverflow::WillProcessLines(aBuilder, this));
6134 // We'll collect our lines' display items here, & then append this to aLists.
6135 nsDisplayListCollection linesDisplayListCollection;
6137 // Don't use the line cursor if we might have a descendant placeholder ...
6138 // it might skip lines that contain placeholders but don't themselves
6139 // intersect with the dirty area.
6140 // In particular, we really want to check ShouldDescendIntoFrame()
6141 // on all our child frames, but that might be expensive. So we
6142 // approximate it by checking it on |this|; if it's true for any
6143 // frame in our child list, it's also true for |this|.
6144 nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ?
6145 nullptr : GetFirstLineContaining(aDirtyRect.y);
6146 line_iterator line_end = end_lines();
6148 if (cursor) {
6149 for (line_iterator line = mLines.begin(cursor);
6150 line != line_end;
6151 ++line) {
6152 nsRect lineArea = line->GetVisualOverflowArea();
6153 if (!lineArea.IsEmpty()) {
6154 // Because we have a cursor, the combinedArea.ys are non-decreasing.
6155 // Once we've passed aDirtyRect.YMost(), we can never see it again.
6156 if (lineArea.y >= aDirtyRect.YMost()) {
6157 break;
6159 DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
6160 linesDisplayListCollection, this, textOverflow);
6163 } else {
6164 bool nonDecreasingYs = true;
6165 int32_t lineCount = 0;
6166 nscoord lastY = INT32_MIN;
6167 nscoord lastYMost = INT32_MIN;
6168 for (line_iterator line = begin_lines();
6169 line != line_end;
6170 ++line) {
6171 nsRect lineArea = line->GetVisualOverflowArea();
6172 DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
6173 linesDisplayListCollection, this, textOverflow);
6174 if (!lineArea.IsEmpty()) {
6175 if (lineArea.y < lastY
6176 || lineArea.YMost() < lastYMost) {
6177 nonDecreasingYs = false;
6179 lastY = lineArea.y;
6180 lastYMost = lineArea.YMost();
6182 lineCount++;
6185 if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
6186 SetupLineCursor();
6190 // Pick up the resulting text-overflow markers. We append them to
6191 // PositionedDescendants just before we append the lines' display items,
6192 // so that our text-overflow markers will appear on top of this block's
6193 // normal content but below any of its its' positioned children.
6194 if (textOverflow) {
6195 aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers());
6197 linesDisplayListCollection.MoveTo(aLists);
6199 if (HasOutsideBullet()) {
6200 // Display outside bullets manually
6201 nsIFrame* bullet = GetOutsideBullet();
6202 BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists);
6205 #ifdef DEBUG
6206 if (gLamePaintMetrics) {
6207 PRTime end = PR_Now();
6209 int32_t numLines = mLines.size();
6210 if (!numLines) numLines = 1;
6211 PRTime lines, deltaPerLine, delta;
6212 lines = int64_t(numLines);
6213 delta = end - start;
6214 deltaPerLine = delta / lines;
6216 ListTag(stdout);
6217 char buf[400];
6218 PR_snprintf(buf, sizeof(buf),
6219 ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
6220 delta, deltaPerLine,
6221 numLines, drawnLines, numLines - drawnLines);
6222 printf("%s\n", buf);
6224 #endif
6227 #ifdef ACCESSIBILITY
6228 a11y::AccType
6229 nsBlockFrame::AccessibleType()
6231 // block frame may be for <hr>
6232 if (mContent->Tag() == nsGkAtoms::hr) {
6233 return a11y::eHTMLHRType;
6236 if (!HasBullet() || !PresContext()) {
6237 if (!mContent->GetParent()) {
6238 // Don't create accessible objects for the root content node, they are redundant with
6239 // the nsDocAccessible object created with the document node
6240 return a11y::eNoType;
6243 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
6244 do_QueryInterface(mContent->GetDocument());
6245 if (htmlDoc) {
6246 nsCOMPtr<nsIDOMHTMLElement> body;
6247 htmlDoc->GetBody(getter_AddRefs(body));
6248 if (SameCOMIdentity(body, mContent)) {
6249 // Don't create accessible objects for the body, they are redundant with
6250 // the nsDocAccessible object created with the document node
6251 return a11y::eNoType;
6255 // Not a bullet, treat as normal HTML container
6256 return a11y::eHyperTextType;
6259 // Create special list bullet accessible
6260 return a11y::eHTMLLiType;
6262 #endif
6264 void nsBlockFrame::ClearLineCursor()
6266 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
6267 return;
6270 Properties().Delete(LineCursorProperty());
6271 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
6274 void nsBlockFrame::SetupLineCursor()
6276 if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
6277 || mLines.empty()) {
6278 return;
6281 Properties().Set(LineCursorProperty(), mLines.front());
6282 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
6285 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
6287 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
6288 return nullptr;
6291 FrameProperties props = Properties();
6293 nsLineBox* property = static_cast<nsLineBox*>
6294 (props.Get(LineCursorProperty()));
6295 line_iterator cursor = mLines.begin(property);
6296 nsRect cursorArea = cursor->GetVisualOverflowArea();
6298 while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
6299 && cursor != mLines.front()) {
6300 cursor = cursor.prev();
6301 cursorArea = cursor->GetVisualOverflowArea();
6303 while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
6304 && cursor != mLines.back()) {
6305 cursor = cursor.next();
6306 cursorArea = cursor->GetVisualOverflowArea();
6309 if (cursor.get() != property) {
6310 props.Set(LineCursorProperty(), cursor.get());
6313 return cursor.get();
6316 /* virtual */ void
6317 nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
6319 // See if the child is absolutely positioned
6320 if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
6321 aChild->IsAbsolutelyPositioned()) {
6322 // do nothing
6323 } else if (aChild == GetOutsideBullet()) {
6324 // The bullet lives in the first line, unless the first line has
6325 // height 0 and there is a second line, in which case it lives
6326 // in the second line.
6327 line_iterator bulletLine = begin_lines();
6328 if (bulletLine != end_lines() && bulletLine->mBounds.height == 0 &&
6329 bulletLine != mLines.back()) {
6330 bulletLine = bulletLine.next();
6333 if (bulletLine != end_lines()) {
6334 MarkLineDirty(bulletLine, &mLines);
6336 // otherwise we have an empty line list, and ReflowDirtyLines
6337 // will handle reflowing the bullet.
6338 } else {
6339 // Note that we should go through our children to mark lines dirty
6340 // before the next reflow. Doing it now could make things O(N^2)
6341 // since finding the right line is O(N).
6342 // We don't need to worry about marking lines on the overflow list
6343 // as dirty; we're guaranteed to reflow them if we take them off the
6344 // overflow list.
6345 // However, we might have gotten a float, in which case we need to
6346 // reflow the line containing its placeholder. So find the
6347 // ancestor-or-self of the placeholder that's a child of the block,
6348 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
6349 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6350 // We need to take some care to handle the case where a float is in
6351 // a different continuation than its placeholder, including marking
6352 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
6353 if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
6354 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
6355 } else {
6356 NS_ASSERTION(aChild->IsFloating(), "should be a float");
6357 nsIFrame *thisFC = FirstContinuation();
6358 nsIFrame *placeholderPath =
6359 PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild);
6360 // SVG code sometimes sends FrameNeedsReflow notifications during
6361 // frame destruction, leading to null placeholders, but we're safe
6362 // ignoring those.
6363 if (placeholderPath) {
6364 for (;;) {
6365 nsIFrame *parent = placeholderPath->GetParent();
6366 if (parent->GetContent() == mContent &&
6367 parent->FirstContinuation() == thisFC) {
6368 parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
6369 break;
6371 placeholderPath = parent;
6373 placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
6378 nsBlockFrameSuper::ChildIsDirty(aChild);
6381 void
6382 nsBlockFrame::Init(nsIContent* aContent,
6383 nsIFrame* aParent,
6384 nsIFrame* aPrevInFlow)
6386 if (aPrevInFlow) {
6387 // Copy over the inherited block frame bits from the prev-in-flow.
6388 SetFlags(aPrevInFlow->GetStateBits() &
6389 (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
6392 nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow);
6394 if (!aPrevInFlow ||
6395 aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
6396 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
6398 if ((GetStateBits() &
6399 (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) ==
6400 (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
6401 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
6405 NS_IMETHODIMP
6406 nsBlockFrame::SetInitialChildList(ChildListID aListID,
6407 nsFrameList& aChildList)
6409 NS_ASSERTION(aListID != kPrincipalList ||
6410 (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET |
6411 NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0,
6412 "how can we have a bullet already?");
6414 if (kAbsoluteList == aListID) {
6415 nsContainerFrame::SetInitialChildList(aListID, aChildList);
6417 else if (kFloatList == aListID) {
6418 mFloats.SetFrames(aChildList);
6420 else {
6421 nsPresContext* presContext = PresContext();
6423 #ifdef DEBUG
6424 // The only times a block that is an anonymous box is allowed to have a
6425 // first-letter frame are when it's the block inside a non-anonymous cell,
6426 // the block inside a fieldset, a scrolled content block, or a column
6427 // content block. Note that this means that blocks which are the anonymous
6428 // block in {ib} splits do NOT get first-letter frames. Note that
6429 // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the
6430 // block.
6431 nsIAtom *pseudo = StyleContext()->GetPseudo();
6432 bool haveFirstLetterStyle =
6433 (!pseudo ||
6434 (pseudo == nsCSSAnonBoxes::cellContent &&
6435 mParent->StyleContext()->GetPseudo() == nullptr) ||
6436 pseudo == nsCSSAnonBoxes::fieldsetContent ||
6437 pseudo == nsCSSAnonBoxes::scrolledContent ||
6438 pseudo == nsCSSAnonBoxes::columnContent ||
6439 pseudo == nsCSSAnonBoxes::mozSVGText) &&
6440 !IsFrameOfType(eMathML) &&
6441 nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nullptr;
6442 NS_ASSERTION(haveFirstLetterStyle ==
6443 ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
6444 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
6445 #endif
6447 AddFrames(aChildList, nullptr);
6449 // Create a list bullet if this is a list-item. Note that this is
6450 // done here so that RenumberLists will work (it needs the bullets
6451 // to store the bullet numbers). Also note that due to various
6452 // wrapper frames (scrollframes, columns) we want to use the
6453 // outermost (primary, ideally, but it's not set yet when we get
6454 // here) frame of our content for the display check. On the other
6455 // hand, we look at ourselves for the GetPrevInFlow() check, since
6456 // for a columnset we don't want a bullet per column. Note that
6457 // the outermost frame for the content is the primary frame in
6458 // most cases; the ones when it's not (like tables) can't be
6459 // NS_STYLE_DISPLAY_LIST_ITEM).
6460 nsIFrame* possibleListItem = this;
6461 while (1) {
6462 nsIFrame* parent = possibleListItem->GetParent();
6463 if (parent->GetContent() != GetContent()) {
6464 break;
6466 possibleListItem = parent;
6468 if (NS_STYLE_DISPLAY_LIST_ITEM ==
6469 possibleListItem->StyleDisplay()->mDisplay &&
6470 !GetPrevInFlow()) {
6471 // Resolve style for the bullet frame
6472 const nsStyleList* styleList = StyleList();
6473 nsCSSPseudoElements::Type pseudoType;
6474 switch (styleList->mListStyleType) {
6475 case NS_STYLE_LIST_STYLE_DISC:
6476 case NS_STYLE_LIST_STYLE_CIRCLE:
6477 case NS_STYLE_LIST_STYLE_SQUARE:
6478 pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet;
6479 break;
6480 default:
6481 pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber;
6482 break;
6485 nsIPresShell *shell = presContext->PresShell();
6487 nsStyleContext* parentStyle =
6488 CorrectStyleParentFrame(this,
6489 nsCSSPseudoElements::GetPseudoAtom(pseudoType))->StyleContext();
6490 nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
6491 ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
6492 parentStyle);
6494 // Create bullet frame
6495 nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
6496 bullet->Init(mContent, this, nullptr);
6498 // If the list bullet frame should be positioned inside then add
6499 // it to the flow now.
6500 if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
6501 styleList->mListStylePosition) {
6502 nsFrameList bulletList(bullet, bullet);
6503 AddFrames(bulletList, nullptr);
6504 Properties().Set(InsideBulletProperty(), bullet);
6505 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
6506 } else {
6507 nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
6508 Properties().Set(OutsideBulletProperty(), bulletList);
6509 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
6514 return NS_OK;
6517 bool
6518 nsBlockFrame::BulletIsEmpty() const
6520 NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
6521 NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(),
6522 "should only care when we have an outside bullet");
6523 const nsStyleList* list = StyleList();
6524 return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE &&
6525 !list->GetListStyleImage();
6528 void
6529 nsBlockFrame::GetBulletText(nsAString& aText) const
6531 aText.Truncate();
6533 const nsStyleList* myList = StyleList();
6534 if (myList->GetListStyleImage() ||
6535 myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) {
6536 aText.Assign(kDiscCharacter);
6538 else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) {
6539 aText.Assign(kCircleCharacter);
6541 else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
6542 aText.Assign(kSquareCharacter);
6544 else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
6545 nsBulletFrame* bullet = GetBullet();
6546 if (bullet) {
6547 nsAutoString text;
6548 bullet->GetListItemText(*myList, text);
6549 aText = text;
6554 // static
6555 bool
6556 nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
6558 nsIContent* content = aFrame->GetContent();
6559 if (!content || !content->IsHTML())
6560 return false;
6562 nsIAtom *localName = content->NodeInfo()->NameAtom();
6563 return localName == nsGkAtoms::ol ||
6564 localName == nsGkAtoms::ul ||
6565 localName == nsGkAtoms::dir ||
6566 localName == nsGkAtoms::menu;
6569 bool
6570 nsBlockFrame::RenumberLists(nsPresContext* aPresContext)
6572 if (!FrameStartsCounterScope(this)) {
6573 // If this frame doesn't start a counter scope then we don't need
6574 // to renumber child list items.
6575 return false;
6578 MOZ_ASSERT(mContent->IsHTML(),
6579 "FrameStartsCounterScope should only return true for HTML elements");
6581 // Setup initial list ordinal value
6582 // XXX Map html's start property to counter-reset style
6583 int32_t ordinal = 1;
6584 int32_t increment;
6585 if (mContent->Tag() == nsGkAtoms::ol &&
6586 mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) {
6587 increment = -1;
6588 } else {
6589 increment = 1;
6592 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
6593 // Must be non-null, since FrameStartsCounterScope only returns true
6594 // for HTML elements.
6595 MOZ_ASSERT(hc, "How is mContent not HTML?");
6596 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
6597 if (attr && attr->Type() == nsAttrValue::eInteger) {
6598 ordinal = attr->GetIntegerValue();
6599 } else if (increment < 0) {
6600 // <ol reversed> case, or some other case with a negative increment: count
6601 // up the child list
6602 ordinal = 0;
6603 for (nsIContent* kid = mContent->GetFirstChild(); kid;
6604 kid = kid->GetNextSibling()) {
6605 if (kid->IsHTML(nsGkAtoms::li)) {
6606 // FIXME: This isn't right in terms of what CSS says to do for
6607 // overflow of counters (but it only matters when this node has
6608 // more than numeric_limits<int32_t>::max() children).
6609 ordinal -= increment;
6614 // Get to first-in-flow
6615 nsBlockFrame* block = static_cast<nsBlockFrame*>(FirstInFlow());
6616 return RenumberListsInBlock(aPresContext, block, &ordinal, 0, increment);
6619 bool
6620 nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext,
6621 nsBlockFrame* aBlockFrame,
6622 int32_t* aOrdinal,
6623 int32_t aDepth,
6624 int32_t aIncrement)
6626 // Examine each line in the block
6627 bool foundValidLine;
6628 nsBlockInFlowLineIterator bifLineIter(aBlockFrame, &foundValidLine);
6630 if (!foundValidLine)
6631 return false;
6633 bool renumberedABullet = false;
6635 do {
6636 nsLineList::iterator line = bifLineIter.GetLine();
6637 nsIFrame* kid = line->mFirstChild;
6638 int32_t n = line->GetChildCount();
6639 while (--n >= 0) {
6640 bool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal,
6641 aDepth, aIncrement);
6642 if (kidRenumberedABullet) {
6643 line->MarkDirty();
6644 renumberedABullet = true;
6646 kid = kid->GetNextSibling();
6648 } while (bifLineIter.Next());
6650 // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
6651 // the bullet and the caller of RenumberLists. But the caller itself
6652 // has to be responsible for setting the bit itself, since that caller
6653 // might be making a FrameNeedsReflow call, which requires that the
6654 // bit not be set yet.
6655 if (renumberedABullet && aDepth != 0) {
6656 aBlockFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
6659 return renumberedABullet;
6662 bool
6663 nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
6664 nsIFrame* aKid,
6665 int32_t* aOrdinal,
6666 int32_t aDepth,
6667 int32_t aIncrement)
6669 NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
6671 // add in a sanity check for absurdly deep frame trees. See bug 42138
6672 if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
6673 return false;
6675 // if the frame is a placeholder, then get the out of flow frame
6676 nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid);
6677 const nsStyleDisplay* display = kid->StyleDisplay();
6679 // drill down through any wrappers to the real frame
6680 kid = kid->GetContentInsertionFrame();
6682 // possible there is no content insertion frame
6683 if (!kid)
6684 return false;
6686 bool kidRenumberedABullet = false;
6688 // If the frame is a list-item and the frame implements our
6689 // block frame API then get its bullet and set the list item
6690 // ordinal.
6691 if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
6692 // Make certain that the frame is a block frame in case
6693 // something foreign has crept in.
6694 nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
6695 if (listItem) {
6696 nsBulletFrame* bullet = listItem->GetBullet();
6697 if (bullet) {
6698 bool changed;
6699 *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
6700 if (changed) {
6701 kidRenumberedABullet = true;
6703 // The ordinal changed - mark the bullet frame, and any
6704 // intermediate frames between it and the block (are there
6705 // ever any?), dirty.
6706 // The calling code will make the necessary FrameNeedsReflow
6707 // call for the list ancestor.
6708 bullet->AddStateBits(NS_FRAME_IS_DIRTY);
6709 nsIFrame *f = bullet;
6710 do {
6711 nsIFrame *parent = f->GetParent();
6712 parent->ChildIsDirty(f);
6713 f = parent;
6714 } while (f != listItem);
6718 // XXX temporary? if the list-item has child list-items they
6719 // should be numbered too; especially since the list-item is
6720 // itself (ASSUMED!) not to be a counter-resetter.
6721 bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal,
6722 aDepth + 1, aIncrement);
6723 if (meToo) {
6724 kidRenumberedABullet = true;
6728 else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
6729 if (FrameStartsCounterScope(kid)) {
6730 // Don't bother recursing into a block frame that is a new
6731 // counter scope. Any list-items in there will be handled by
6732 // it.
6734 else {
6735 // If the display=block element is a block frame then go ahead
6736 // and recurse into it, as it might have child list-items.
6737 nsBlockFrame* kidBlock = nsLayoutUtils::GetAsBlock(kid);
6738 if (kidBlock) {
6739 kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock,
6740 aOrdinal, aDepth + 1,
6741 aIncrement);
6745 return kidRenumberedABullet;
6748 void
6749 nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
6750 nsBlockReflowState& aState,
6751 nsHTMLReflowMetrics& aMetrics,
6752 nscoord aLineTop)
6754 const nsHTMLReflowState &rs = aState.mReflowState;
6756 // Reflow the bullet now
6757 nsSize availSize;
6758 // Make up a width since it doesn't really matter (XXX).
6759 availSize.width = aState.mContentArea.width;
6760 availSize.height = NS_UNCONSTRAINEDSIZE;
6762 // Get the reason right.
6763 // XXXwaterson Should this look just like the logic in
6764 // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
6765 nsHTMLReflowState reflowState(aState.mPresContext, rs,
6766 aBulletFrame, availSize);
6767 nsReflowStatus status;
6768 aBulletFrame->WillReflow(aState.mPresContext);
6769 aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
6771 // Get the float available space using our saved state from before we
6772 // started reflowing the block, so that we ignore any floats inside
6773 // the block.
6774 // FIXME: aLineTop isn't actually set correctly by some callers, since
6775 // they reposition the line.
6776 nsRect floatAvailSpace =
6777 aState.GetFloatAvailableSpaceWithState(aLineTop,
6778 &aState.mFloatManagerStateBefore)
6779 .mRect;
6780 // FIXME (bug 25888): need to check the entire region that the first
6781 // line overlaps, not just the top pixel.
6783 // Place the bullet now. We want to place the bullet relative to the
6784 // border-box of the associated block (using the right/left margin of
6785 // the bullet frame as separation). However, if a line box would be
6786 // displaced by floats that are *outside* the associated block, we
6787 // want to displace it by the same amount. That is, we act as though
6788 // the edge of the floats is the content-edge of the block, and place
6789 // the bullet at a position offset from there by the block's padding,
6790 // the block's border, and the bullet frame's margin.
6791 nscoord x;
6792 if (rs.mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR) {
6793 // The floatAvailSpace.x gives us the content/float edge. Then we
6794 // subtract out the left border/padding and the bullet's width and
6795 // margin to offset the position.
6796 x = floatAvailSpace.x - rs.mComputedBorderPadding.left
6797 - reflowState.mComputedMargin.right - aMetrics.width;
6798 } else {
6799 // The XMost() of the available space give us offsets from the left
6800 // border edge. Then we add the right border/padding and the
6801 // bullet's margin to offset the position.
6802 x = floatAvailSpace.XMost() + rs.mComputedBorderPadding.right
6803 + reflowState.mComputedMargin.left;
6806 // Approximate the bullets position; vertical alignment will provide
6807 // the final vertical location.
6808 nscoord y = aState.mContentArea.y;
6809 aBulletFrame->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
6810 aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState,
6811 nsDidReflowStatus::FINISHED);
6814 // This is used to scan frames for any float placeholders, add their
6815 // floats to the list represented by aList, and remove the
6816 // floats from whatever list they might be in. We don't search descendants
6817 // that are float containing blocks. Floats that or not children of 'this'
6818 // are ignored (they are not added to aList).
6819 void
6820 nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
6821 bool aCollectSiblings)
6823 while (aFrame) {
6824 // Don't descend into float containing blocks.
6825 if (!aFrame->IsFloatContainingBlock()) {
6826 nsIFrame *outOfFlowFrame =
6827 aFrame->GetType() == nsGkAtoms::placeholderFrame ?
6828 nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nullptr;
6829 if (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
6830 RemoveFloat(outOfFlowFrame);
6831 aList.AppendFrame(nullptr, outOfFlowFrame);
6832 // FIXME: By not pulling floats whose parent is one of our
6833 // later siblings, are we risking the pushed floats getting
6834 // out-of-order?
6835 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
6838 DoCollectFloats(aFrame->GetFirstPrincipalChild(), aList, true);
6839 DoCollectFloats(aFrame->GetFirstChild(kOverflowList), aList, true);
6841 if (!aCollectSiblings)
6842 break;
6843 aFrame = aFrame->GetNextSibling();
6847 void
6848 nsBlockFrame::CheckFloats(nsBlockReflowState& aState)
6850 #ifdef DEBUG
6851 // If any line is still dirty, that must mean we're going to reflow this
6852 // block again soon (e.g. because we bailed out after noticing that
6853 // clearance was imposed), so don't worry if the floats are out of sync.
6854 bool anyLineDirty = false;
6856 // Check that the float list is what we would have built
6857 nsAutoTArray<nsIFrame*, 8> lineFloats;
6858 for (line_iterator line = begin_lines(), line_end = end_lines();
6859 line != line_end; ++line) {
6860 if (line->HasFloats()) {
6861 nsFloatCache* fc = line->GetFirstFloat();
6862 while (fc) {
6863 lineFloats.AppendElement(fc->mFloat);
6864 fc = fc->Next();
6867 if (line->IsDirty()) {
6868 anyLineDirty = true;
6872 nsAutoTArray<nsIFrame*, 8> storedFloats;
6873 bool equal = true;
6874 uint32_t i = 0;
6875 for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
6876 if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
6877 continue;
6878 storedFloats.AppendElement(f);
6879 if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
6880 equal = false;
6882 ++i;
6885 if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) {
6886 NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
6887 #if defined(DEBUG_roc)
6888 nsFrame::RootFrameList(PresContext(), stdout, 0);
6889 for (i = 0; i < lineFloats.Length(); ++i) {
6890 printf("Line float: %p\n", lineFloats.ElementAt(i));
6892 for (i = 0; i < storedFloats.Length(); ++i) {
6893 printf("Stored float: %p\n", storedFloats.ElementAt(i));
6895 #endif
6897 #endif
6899 const nsFrameList* oofs = GetOverflowOutOfFlows();
6900 if (oofs && oofs->NotEmpty()) {
6901 // Floats that were pushed should be removed from our float
6902 // manager. Otherwise the float manager's YMost or XMost might
6903 // be larger than necessary, causing this block to get an
6904 // incorrect desired height (or width). Some of these floats
6905 // may not actually have been added to the float manager because
6906 // they weren't reflowed before being pushed; that's OK,
6907 // RemoveRegions will ignore them. It is safe to do this here
6908 // because we know from here on the float manager will only be
6909 // used for its XMost and YMost, not to place new floats and
6910 // lines.
6911 aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild());
6915 void
6916 nsBlockFrame::IsMarginRoot(bool* aTopMarginRoot, bool* aBottomMarginRoot)
6918 if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
6919 nsIFrame* parent = GetParent();
6920 if (!parent || parent->IsFloatContainingBlock()) {
6921 *aTopMarginRoot = false;
6922 *aBottomMarginRoot = false;
6923 return;
6925 if (parent->GetType() == nsGkAtoms::columnSetFrame) {
6926 *aTopMarginRoot = GetPrevInFlow() == nullptr;
6927 *aBottomMarginRoot = GetNextInFlow() == nullptr;
6928 return;
6932 *aTopMarginRoot = true;
6933 *aBottomMarginRoot = true;
6936 /* static */
6937 bool
6938 nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock)
6940 NS_PRECONDITION(aBlock, "Must have a frame");
6941 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
6943 nsIFrame* parent = aBlock->GetParent();
6944 return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) ||
6945 (parent && !parent->IsFloatContainingBlock());
6948 /* static */
6949 bool
6950 nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame)
6952 return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) &&
6953 !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
6954 !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR);
6957 // Note that this width can vary based on the vertical position.
6958 // However, the cases where it varies are the cases where the width fits
6959 // in the available space given, which means that variation shouldn't
6960 // matter.
6961 /* static */
6962 nsBlockFrame::ReplacedElementWidthToClear
6963 nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState& aState,
6964 const nsRect& aFloatAvailableSpace,
6965 nsIFrame* aFrame)
6967 nscoord leftOffset, rightOffset;
6968 nsCSSOffsetState offsetState(aFrame, aState.mReflowState.rendContext,
6969 aState.mContentArea.width);
6971 ReplacedElementWidthToClear result;
6972 aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace,
6973 leftOffset, rightOffset);
6974 nscoord availWidth = aState.mContentArea.width - leftOffset - rightOffset;
6976 // We actually don't want the min width here; see bug 427782; we only
6977 // want to displace if the width won't compute to a value small enough
6978 // to fit.
6979 // All we really need here is the result of ComputeSize, and we
6980 // could *almost* get that from an nsCSSOffsetState, except for the
6981 // last argument.
6982 nsSize availSpace(availWidth, NS_UNCONSTRAINEDSIZE);
6983 nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
6984 aFrame, availSpace);
6985 result.borderBoxWidth = reflowState.ComputedWidth() +
6986 reflowState.mComputedBorderPadding.LeftRight();
6987 // Use the margins from offsetState rather than reflowState so that
6988 // they aren't reduced by ignoring margins in overconstrained cases.
6989 result.marginLeft = offsetState.mComputedMargin.left;
6990 result.marginRight = offsetState.mComputedMargin.right;
6991 return result;
6994 /* static */
6995 nsBlockFrame*
6996 nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
6998 nsBlockFrame* block = nullptr;
6999 while(aCandidate) {
7000 block = nsLayoutUtils::GetAsBlock(aCandidate);
7001 if (block) {
7002 // yay, candidate is a block!
7003 return block;
7005 // Not a block. Check its parent next.
7006 aCandidate = aCandidate->GetParent();
7008 NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
7009 return nullptr;
7012 void
7013 nsBlockFrame::ComputeFinalHeight(const nsHTMLReflowState& aReflowState,
7014 nsReflowStatus* aStatus,
7015 nscoord aContentHeight,
7016 const nsMargin& aBorderPadding,
7017 nsHTMLReflowMetrics& aMetrics,
7018 nscoord aConsumed)
7021 // Figure out how much of the computed height should be
7022 // applied to this frame.
7023 nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState,
7024 aConsumed);
7025 NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
7026 && computedHeightLeftOver ),
7027 "overflow container must not have computedHeightLeftOver");
7029 aMetrics.height =
7030 NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.top,
7031 computedHeightLeftOver),
7032 aBorderPadding.bottom);
7034 if (NS_FRAME_IS_NOT_COMPLETE(*aStatus)
7035 && aMetrics.height < aReflowState.availableHeight) {
7036 // We ran out of height on this page but we're incomplete
7037 // Set status to complete except for overflow
7038 NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus);
7041 if (NS_FRAME_IS_COMPLETE(*aStatus)) {
7042 if (computedHeightLeftOver > 0 &&
7043 NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
7044 aMetrics.height > aReflowState.availableHeight) {
7045 if (ShouldAvoidBreakInside(aReflowState)) {
7046 *aStatus = NS_INLINE_LINE_BREAK_BEFORE();
7047 return;
7049 // We don't fit and we consumed some of the computed height,
7050 // so we should consume all the available height and then
7051 // break. If our bottom border/padding straddles the break
7052 // point, then this will increase our height and push the
7053 // border/padding to the next page/column.
7054 aMetrics.height = std::max(aReflowState.availableHeight,
7055 aContentHeight);
7056 NS_FRAME_SET_INCOMPLETE(*aStatus);
7057 if (!GetNextInFlow())
7058 *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
7063 #ifdef IBMBIDI
7064 nsresult
7065 nsBlockFrame::ResolveBidi()
7067 NS_ASSERTION(!GetPrevInFlow(),
7068 "ResolveBidi called on non-first continuation");
7070 nsPresContext* presContext = PresContext();
7071 if (!presContext->BidiEnabled()) {
7072 return NS_OK;
7075 return nsBidiPresUtils::Resolve(this);
7077 #endif
7079 #ifdef DEBUG
7080 void
7081 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
7083 if (!gVerifyLines) {
7084 return;
7086 if (mLines.empty()) {
7087 return;
7090 nsLineBox* cursor = GetLineCursor();
7092 // Add up the counts on each line. Also validate that IsFirstLine is
7093 // set properly.
7094 int32_t count = 0;
7095 line_iterator line, line_end;
7096 for (line = begin_lines(), line_end = end_lines();
7097 line != line_end;
7098 ++line) {
7099 if (line == cursor) {
7100 cursor = nullptr;
7102 if (aFinalCheckOK) {
7103 NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
7104 if (line->IsBlock()) {
7105 NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
7108 count += line->GetChildCount();
7111 // Then count the frames
7112 int32_t frameCount = 0;
7113 nsIFrame* frame = mLines.front()->mFirstChild;
7114 while (frame) {
7115 frameCount++;
7116 frame = frame->GetNextSibling();
7118 NS_ASSERTION(count == frameCount, "bad line list");
7120 // Next: test that each line has right number of frames on it
7121 for (line = begin_lines(), line_end = end_lines();
7122 line != line_end;
7124 count = line->GetChildCount();
7125 frame = line->mFirstChild;
7126 while (--count >= 0) {
7127 frame = frame->GetNextSibling();
7129 ++line;
7130 if ((line != line_end) && (0 != line->GetChildCount())) {
7131 NS_ASSERTION(frame == line->mFirstChild, "bad line list");
7135 if (cursor) {
7136 FrameLines* overflowLines = GetOverflowLines();
7137 if (overflowLines) {
7138 line_iterator line = overflowLines->mLines.begin();
7139 line_iterator line_end = overflowLines->mLines.end();
7140 for (; line != line_end; ++line) {
7141 if (line == cursor) {
7142 cursor = nullptr;
7143 break;
7148 NS_ASSERTION(!cursor, "stale LineCursorProperty");
7151 void
7152 nsBlockFrame::VerifyOverflowSituation()
7154 nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
7155 while (flow) {
7156 FrameLines* overflowLines = flow->GetOverflowLines();
7157 if (overflowLines) {
7158 NS_ASSERTION(!overflowLines->mLines.empty(),
7159 "should not be empty if present");
7160 NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
7161 "bad overflow lines");
7162 NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
7163 overflowLines->mFrames.FirstChild(),
7164 "bad overflow frames / lines");
7166 nsLineBox* cursor = flow->GetLineCursor();
7167 if (cursor) {
7168 line_iterator line = flow->begin_lines();
7169 line_iterator line_end = flow->end_lines();
7170 for (; line != line_end && line != cursor; ++line)
7172 if (line == line_end && overflowLines) {
7173 line = overflowLines->mLines.begin();
7174 line_end = overflowLines->mLines.end();
7175 for (; line != line_end && line != cursor; ++line)
7178 MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
7180 flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
7184 int32_t
7185 nsBlockFrame::GetDepth() const
7187 int32_t depth = 0;
7188 nsIFrame* parent = mParent;
7189 while (parent) {
7190 parent = parent->GetParent();
7191 depth++;
7193 return depth;
7195 #endif