layout/generic/nsBlockFrame.cpp

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

mercurial