Coverage Report

Created: 2018-09-25 14:53

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