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