Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/generic/nsLineBox.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
/* representation of one line within a block frame, a CSS line box */
8
9
#include "nsLineBox.h"
10
11
#include "mozilla/ArenaObjectID.h"
12
#include "mozilla/Assertions.h"
13
#include "mozilla/Likely.h"
14
#include "mozilla/Sprintf.h"
15
#include "mozilla/WritingModes.h"
16
#include "nsBidiPresUtils.h"
17
#include "nsFrame.h"
18
#include "nsIFrameInlines.h"
19
#include "nsPresArena.h"
20
#include "nsPrintfCString.h"
21
#include "nsWindowSizes.h"
22
23
#ifdef DEBUG
24
static int32_t ctorCount;
25
int32_t nsLineBox::GetCtorCount() { return ctorCount; }
26
#endif
27
28
#ifndef _MSC_VER
29
// static nsLineBox constant; initialized in the header file.
30
const uint32_t nsLineBox::kMinChildCountForHashtable;
31
#endif
32
33
using namespace mozilla;
34
35
nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
36
  : mFirstChild(aFrame)
37
  , mWritingMode()
38
  , mContainerSize(-1, -1)
39
  , mBounds(WritingMode()) // mBounds will be initialized with the correct
40
                           // writing mode when it is set
41
  , mFrames()
42
  , mAscent()
43
  , mAllFlags(0)
44
  , mData(nullptr)
45
0
{
46
0
  // Assert that the union elements chosen for initialisation are at
47
0
  // least as large as all other elements in their respective unions, so
48
0
  // as to ensure that no parts are missed.
49
0
  static_assert(sizeof(mFrames) >= sizeof(mChildCount), "nsLineBox init #1");
50
0
  static_assert(sizeof(mAllFlags) >= sizeof(mFlags), "nsLineBox init #2");
51
0
  static_assert(sizeof(mData) >= sizeof(mBlockData), "nsLineBox init #3");
52
0
  static_assert(sizeof(mData) >= sizeof(mInlineData), "nsLineBox init #4");
53
0
54
0
  MOZ_COUNT_CTOR(nsLineBox);
55
#ifdef DEBUG
56
  ++ctorCount;
57
  NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
58
  nsIFrame* f = aFrame;
59
  for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
60
    NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
61
                 "wrong kind of child frame");
62
  }
63
#endif
64
  static_assert(static_cast<int>(StyleClear::Max) <= 15,
65
0
                "FlagBits needs more bits to store the full range of "
66
0
                "break type ('clear') values");
67
0
  mChildCount = aCount;
68
0
  MarkDirty();
69
0
  mFlags.mBlock = aIsBlock;
70
0
}
71
72
nsLineBox::~nsLineBox()
73
0
{
74
0
  MOZ_COUNT_DTOR(nsLineBox);
75
0
  if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
76
0
    delete mFrames;
77
0
  }
78
0
  Cleanup();
79
0
}
80
81
nsLineBox*
82
NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
83
0
{
84
0
  return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
85
0
}
86
87
nsLineBox*
88
NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
89
              nsIFrame* aFrame, int32_t aCount)
90
0
{
91
0
  nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
92
0
  newLine->NoteFramesMovedFrom(aFromLine);
93
0
  newLine->mContainerSize = aFromLine->mContainerSize;
94
0
  return newLine;
95
0
}
96
97
void
98
nsLineBox::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const
99
0
{
100
0
  if (mFlags.mHasHashedFrames) {
101
0
    aSizes.mLayoutFramePropertiesSize +=
102
0
      mFrames->ShallowSizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
103
0
  }
104
0
}
105
106
void
107
nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
108
0
{
109
0
  MOZ_ASSERT(!mFlags.mHasHashedFrames);
110
0
  MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
111
0
  mFrames = aFromLine->mFrames;
112
0
  mFlags.mHasHashedFrames = 1;
113
0
  aFromLine->mFlags.mHasHashedFrames = 0;
114
0
  aFromLine->mChildCount = aFromLineNewCount;
115
0
  // remove aFromLine's frames that aren't on this line
116
0
  nsIFrame* f = aFromLine->mFirstChild;
117
0
  for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
118
0
    mFrames->RemoveEntry(f);
119
0
  }
120
0
}
121
122
void
123
nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
124
0
{
125
0
  uint32_t fromCount = aFromLine->GetChildCount();
126
0
  uint32_t toCount = GetChildCount();
127
0
  MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
128
0
  uint32_t fromNewCount = fromCount - toCount;
129
0
  if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
130
0
    aFromLine->mChildCount = fromNewCount;
131
0
    MOZ_ASSERT(toCount < kMinChildCountForHashtable);
132
0
  } else if (fromNewCount < kMinChildCountForHashtable) {
133
0
    // aFromLine has a hash table but will not have it after moving the frames
134
0
    // so this line can steal the hash table if it needs it.
135
0
    if (toCount >= kMinChildCountForHashtable) {
136
0
      StealHashTableFrom(aFromLine, fromNewCount);
137
0
    } else {
138
0
      delete aFromLine->mFrames;
139
0
      aFromLine->mFlags.mHasHashedFrames = 0;
140
0
      aFromLine->mChildCount = fromNewCount;
141
0
    }
142
0
  } else {
143
0
    // aFromLine still needs a hash table.
144
0
    if (toCount < kMinChildCountForHashtable) {
145
0
      // remove the moved frames from it
146
0
      nsIFrame* f = mFirstChild;
147
0
      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
148
0
        aFromLine->mFrames->RemoveEntry(f);
149
0
      }
150
0
    } else if (toCount <= fromNewCount) {
151
0
      // This line needs a hash table, allocate a hash table for it since that
152
0
      // means fewer hash ops.
153
0
      nsIFrame* f = mFirstChild;
154
0
      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
155
0
        aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
156
0
      }
157
0
      SwitchToHashtable(); // toCount PutEntry
158
0
    } else {
159
0
      // This line needs a hash table, but it's fewer hash ops to steal
160
0
      // aFromLine's hash table and allocate a new hash table for that line.
161
0
      StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
162
0
      aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
163
0
    }
164
0
  }
165
0
}
166
167
void*
168
nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell)
169
0
{
170
0
  return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
171
0
}
172
173
void
174
nsLineBox::Destroy(nsIPresShell* aPresShell)
175
0
{
176
0
  this->nsLineBox::~nsLineBox();
177
0
  aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
178
0
}
179
180
void
181
nsLineBox::Cleanup()
182
0
{
183
0
  if (mData) {
184
0
    if (IsBlock()) {
185
0
      delete mBlockData;
186
0
    }
187
0
    else {
188
0
      delete mInlineData;
189
0
    }
190
0
    mData = nullptr;
191
0
  }
192
0
}
193
194
#ifdef DEBUG_FRAME_DUMP
195
static void
196
ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
197
{
198
  nsFloatCache* fc = aFloats.Head();
199
  while (fc) {
200
    nsCString str(aPrefix);
201
    nsIFrame* frame = fc->mFloat;
202
    str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
203
    if (frame) {
204
      nsAutoString frameName;
205
      frame->GetFrameName(frameName);
206
      str += NS_ConvertUTF16toUTF8(frameName).get();
207
    }
208
    else {
209
      str += "\n###!!! NULL out-of-flow frame";
210
    }
211
    fprintf_stderr(out, "%s\n", str.get());
212
    fc = fc->Next();
213
  }
214
}
215
216
/* static */ const char*
217
nsLineBox::BreakTypeToString(StyleClear aBreakType)
218
{
219
  switch (aBreakType) {
220
    case StyleClear::None: return "nobr";
221
    case StyleClear::Left: return "leftbr";
222
    case StyleClear::Right: return "rightbr";
223
    case StyleClear::Both: return "leftbr+rightbr";
224
    case StyleClear::Line: return "linebr";
225
    case StyleClear::Max: return "leftbr+rightbr+linebr";
226
  }
227
  return "unknown";
228
}
229
230
char*
231
nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
232
{
233
  snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
234
           IsBlock() ? "block" : "inline",
235
           IsDirty() ? "dirty" : "clean",
236
           IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
237
           IsImpactedByFloat() ? "impacted" : "not impacted",
238
           IsLineWrapped() ? "wrapped" : "not wrapped",
239
           BreakTypeToString(GetBreakTypeBefore()),
240
           BreakTypeToString(GetBreakTypeAfter()),
241
           mAllFlags);
242
  return aBuf;
243
}
244
245
void
246
nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
247
{
248
  nsCString str;
249
  while (aIndent-- > 0) {
250
    str += "  ";
251
  }
252
  List(out, str.get(), aFlags);
253
}
254
255
void
256
nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
257
{
258
  nsCString str(aPrefix);
259
  char cbuf[100];
260
  str += nsPrintfCString("line %p: count=%d state=%s ",
261
          static_cast<const void*>(this), GetChildCount(),
262
          StateToString(cbuf, sizeof(cbuf)));
263
  if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
264
    str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get());
265
  }
266
  nsRect bounds = GetPhysicalBounds();
267
  str += nsPrintfCString("{%d,%d,%d,%d} ",
268
          bounds.x, bounds.y, bounds.width, bounds.height);
269
  if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) {
270
    str += nsPrintfCString("{%s: %d,%d,%d,%d; cs=%d,%d} ",
271
                           mWritingMode.DebugString(),
272
                           IStart(), BStart(), ISize(), BSize(),
273
                           mContainerSize.width, mContainerSize.height);
274
  }
275
  if (mData &&
276
      (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
277
       !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
278
    str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
279
            mData->mOverflowAreas.VisualOverflow().x,
280
            mData->mOverflowAreas.VisualOverflow().y,
281
            mData->mOverflowAreas.VisualOverflow().width,
282
            mData->mOverflowAreas.VisualOverflow().height,
283
            mData->mOverflowAreas.ScrollableOverflow().x,
284
            mData->mOverflowAreas.ScrollableOverflow().y,
285
            mData->mOverflowAreas.ScrollableOverflow().width,
286
            mData->mOverflowAreas.ScrollableOverflow().height);
287
  }
288
  fprintf_stderr(out, "%s<\n", str.get());
289
290
  nsIFrame* frame = mFirstChild;
291
  int32_t n = GetChildCount();
292
  nsCString pfx(aPrefix);
293
  pfx += "  ";
294
  while (--n >= 0) {
295
    frame->List(out, pfx.get(), aFlags);
296
    frame = frame->GetNextSibling();
297
  }
298
299
  if (HasFloats()) {
300
    fprintf_stderr(out, "%s> floats <\n", aPrefix);
301
    ListFloats(out, pfx.get(), mInlineData->mFloats);
302
  }
303
  fprintf_stderr(out, "%s>\n", aPrefix);
304
}
305
306
nsIFrame*
307
nsLineBox::LastChild() const
308
{
309
  nsIFrame* frame = mFirstChild;
310
  int32_t n = GetChildCount() - 1;
311
  while (--n >= 0) {
312
    frame = frame->GetNextSibling();
313
  }
314
  return frame;
315
}
316
#endif
317
318
int32_t
319
nsLineBox::IndexOf(nsIFrame* aFrame) const
320
0
{
321
0
  int32_t i, n = GetChildCount();
322
0
  nsIFrame* frame = mFirstChild;
323
0
  for (i = 0; i < n; i++) {
324
0
    if (frame == aFrame) {
325
0
      return i;
326
0
    }
327
0
    frame = frame->GetNextSibling();
328
0
  }
329
0
  return -1;
330
0
}
331
332
bool
333
nsLineBox::IsEmpty() const
334
0
{
335
0
  if (IsBlock())
336
0
    return mFirstChild->IsEmpty();
337
0
338
0
  int32_t n;
339
0
  nsIFrame *kid;
340
0
  for (n = GetChildCount(), kid = mFirstChild;
341
0
       n > 0;
342
0
       --n, kid = kid->GetNextSibling())
343
0
  {
344
0
    if (!kid->IsEmpty())
345
0
      return false;
346
0
  }
347
0
  if (HasBullet()) {
348
0
    return false;
349
0
  }
350
0
  return true;
351
0
}
352
353
bool
354
nsLineBox::CachedIsEmpty()
355
0
{
356
0
  if (mFlags.mDirty) {
357
0
    return IsEmpty();
358
0
  }
359
0
360
0
  if (mFlags.mEmptyCacheValid) {
361
0
    return mFlags.mEmptyCacheState;
362
0
  }
363
0
364
0
  bool result;
365
0
  if (IsBlock()) {
366
0
    result = mFirstChild->CachedIsEmpty();
367
0
  } else {
368
0
    int32_t n;
369
0
    nsIFrame *kid;
370
0
    result = true;
371
0
    for (n = GetChildCount(), kid = mFirstChild;
372
0
         n > 0;
373
0
         --n, kid = kid->GetNextSibling())
374
0
      {
375
0
        if (!kid->CachedIsEmpty()) {
376
0
          result = false;
377
0
          break;
378
0
        }
379
0
      }
380
0
    if (HasBullet()) {
381
0
      result = false;
382
0
    }
383
0
  }
384
0
385
0
  mFlags.mEmptyCacheValid = true;
386
0
  mFlags.mEmptyCacheState = result;
387
0
  return result;
388
0
}
389
390
void
391
nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
392
                          nsIFrame* aDestructRoot, nsFrameList* aFrames,
393
                          PostDestroyData& aPostDestroyData)
394
0
{
395
0
  nsIPresShell* shell = aPresContext->PresShell();
396
0
397
0
  // Keep our line list and frame list up to date as we
398
0
  // remove frames, in case something wants to traverse the
399
0
  // frame tree while we're destroying.
400
0
  while (!aLines.empty()) {
401
0
    nsLineBox* line = aLines.front();
402
0
    if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
403
0
      line->SwitchToCounter();  // Avoid expensive has table removals.
404
0
    }
405
0
    while (line->GetChildCount() > 0) {
406
0
      nsIFrame* child = aFrames->RemoveFirstChild();
407
0
      MOZ_DIAGNOSTIC_ASSERT(child->PresContext() == aPresContext);
408
0
      MOZ_DIAGNOSTIC_ASSERT(child == line->mFirstChild, "Lines out of sync");
409
0
      line->mFirstChild = aFrames->FirstChild();
410
0
      line->NoteFrameRemoved(child);
411
0
      child->DestroyFrom(aDestructRoot, aPostDestroyData);
412
0
    }
413
0
    MOZ_DIAGNOSTIC_ASSERT(line == aLines.front(),
414
0
                          "destroying child frames messed up our lines!");
415
0
    aLines.pop_front();
416
0
    line->Destroy(shell);
417
0
  }
418
0
}
419
420
bool
421
nsLineBox::RFindLineContaining(nsIFrame* aFrame,
422
                               const nsLineList::iterator& aBegin,
423
                               nsLineList::iterator& aEnd,
424
                               nsIFrame* aLastFrameBeforeEnd,
425
                               int32_t* aFrameIndexInLine)
426
0
{
427
0
  MOZ_ASSERT(aFrame, "null ptr");
428
0
429
0
  nsIFrame* curFrame = aLastFrameBeforeEnd;
430
0
  while (aBegin != aEnd) {
431
0
    --aEnd;
432
0
    NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
433
0
    if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
434
0
        !aEnd->Contains(aFrame)) {
435
0
      if (aEnd->mFirstChild) {
436
0
        curFrame = aEnd->mFirstChild->GetPrevSibling();
437
0
      }
438
0
      continue;
439
0
    }
440
0
    // i is the index of curFrame in aEnd
441
0
    int32_t i = aEnd->GetChildCount() - 1;
442
0
    while (i >= 0) {
443
0
      if (curFrame == aFrame) {
444
0
        *aFrameIndexInLine = i;
445
0
        return true;
446
0
      }
447
0
      --i;
448
0
      curFrame = curFrame->GetPrevSibling();
449
0
    }
450
0
    MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
451
0
  }
452
0
  *aFrameIndexInLine = -1;
453
0
  return false;
454
0
}
455
456
nsCollapsingMargin
457
nsLineBox::GetCarriedOutBEndMargin() const
458
0
{
459
0
  NS_ASSERTION(IsBlock(),
460
0
               "GetCarriedOutBEndMargin called on non-block line.");
461
0
  return (IsBlock() && mBlockData)
462
0
    ? mBlockData->mCarriedOutBEndMargin
463
0
    : nsCollapsingMargin();
464
0
}
465
466
bool
467
nsLineBox::SetCarriedOutBEndMargin(nsCollapsingMargin aValue)
468
0
{
469
0
  bool changed = false;
470
0
  if (IsBlock()) {
471
0
    if (!aValue.IsZero()) {
472
0
      if (!mBlockData) {
473
0
        mBlockData = new ExtraBlockData(GetPhysicalBounds());
474
0
      }
475
0
      changed = aValue != mBlockData->mCarriedOutBEndMargin;
476
0
      mBlockData->mCarriedOutBEndMargin = aValue;
477
0
    }
478
0
    else if (mBlockData) {
479
0
      changed = aValue != mBlockData->mCarriedOutBEndMargin;
480
0
      mBlockData->mCarriedOutBEndMargin = aValue;
481
0
      MaybeFreeData();
482
0
    }
483
0
  }
484
0
  return changed;
485
0
}
486
487
void
488
nsLineBox::MaybeFreeData()
489
0
{
490
0
  nsRect bounds = GetPhysicalBounds();
491
0
  if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
492
0
    if (IsInline()) {
493
0
      if (mInlineData->mFloats.IsEmpty()) {
494
0
        delete mInlineData;
495
0
        mInlineData = nullptr;
496
0
      }
497
0
    }
498
0
    else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
499
0
      delete mBlockData;
500
0
      mBlockData = nullptr;
501
0
    }
502
0
  }
503
0
}
504
505
// XXX get rid of this???
506
nsFloatCache*
507
nsLineBox::GetFirstFloat()
508
0
{
509
0
  MOZ_ASSERT(IsInline(), "block line can't have floats");
510
0
  return mInlineData ? mInlineData->mFloats.Head() : nullptr;
511
0
}
512
513
// XXX this might be too eager to free memory
514
void
515
nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
516
0
{
517
0
  MOZ_ASSERT(IsInline(), "block line can't have floats");
518
0
  if (IsInline() && mInlineData) {
519
0
    if (mInlineData->mFloats.NotEmpty()) {
520
0
      aFreeList.Append(mInlineData->mFloats);
521
0
    }
522
0
    MaybeFreeData();
523
0
  }
524
0
}
525
526
void
527
nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
528
0
{
529
0
  MOZ_ASSERT(IsInline(), "block line can't have floats");
530
0
  if (IsInline()) {
531
0
    if (aFreeList.NotEmpty()) {
532
0
      if (!mInlineData) {
533
0
        mInlineData = new ExtraInlineData(GetPhysicalBounds());
534
0
      }
535
0
      mInlineData->mFloats.Append(aFreeList);
536
0
    }
537
0
  }
538
0
}
539
540
bool
541
nsLineBox::RemoveFloat(nsIFrame* aFrame)
542
0
{
543
0
  MOZ_ASSERT(IsInline(), "block line can't have floats");
544
0
  if (IsInline() && mInlineData) {
545
0
    nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
546
0
    if (fc) {
547
0
      // Note: the placeholder is part of the line's child list
548
0
      // and will be removed later.
549
0
      mInlineData->mFloats.Remove(fc);
550
0
      delete fc;
551
0
      MaybeFreeData();
552
0
      return true;
553
0
    }
554
0
  }
555
0
  return false;
556
0
}
557
558
void
559
nsLineBox::SetFloatEdges(nscoord aStart, nscoord aEnd)
560
0
{
561
0
  MOZ_ASSERT(IsInline(), "block line can't have float edges");
562
0
  if (!mInlineData) {
563
0
    mInlineData = new ExtraInlineData(GetPhysicalBounds());
564
0
  }
565
0
  mInlineData->mFloatEdgeIStart = aStart;
566
0
  mInlineData->mFloatEdgeIEnd = aEnd;
567
0
}
568
569
void
570
nsLineBox::ClearFloatEdges()
571
0
{
572
0
  MOZ_ASSERT(IsInline(), "block line can't have float edges");
573
0
  if (mInlineData) {
574
0
    mInlineData->mFloatEdgeIStart = nscoord_MIN;
575
0
    mInlineData->mFloatEdgeIEnd = nscoord_MIN;
576
0
  }
577
0
}
578
579
void
580
nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
581
0
{
582
0
  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
583
0
    NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
584
0
                 "illegal width for combined area");
585
0
    NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
586
0
                 "illegal height for combined area");
587
0
  }
588
0
  nsRect bounds = GetPhysicalBounds();
589
0
  if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
590
0
      !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
591
0
    if (!mData) {
592
0
      if (IsInline()) {
593
0
        mInlineData = new ExtraInlineData(bounds);
594
0
      }
595
0
      else {
596
0
        mBlockData = new ExtraBlockData(bounds);
597
0
      }
598
0
    }
599
0
    mData->mOverflowAreas = aOverflowAreas;
600
0
  }
601
0
  else if (mData) {
602
0
    // Store away new value so that MaybeFreeData compares against
603
0
    // the right value.
604
0
    mData->mOverflowAreas = aOverflowAreas;
605
0
    MaybeFreeData();
606
0
  }
607
0
}
608
609
//----------------------------------------------------------------------
610
611
612
static nsLineBox* gDummyLines[1];
613
614
nsLineIterator::nsLineIterator()
615
0
{
616
0
  mLines = gDummyLines;
617
0
  mNumLines = 0;
618
0
  mIndex = 0;
619
0
  mRightToLeft = false;
620
0
}
621
622
nsLineIterator::~nsLineIterator()
623
0
{
624
0
  if (mLines != gDummyLines) {
625
0
    delete [] mLines;
626
0
  }
627
0
}
628
629
/* virtual */ void
630
nsLineIterator::DisposeLineIterator()
631
0
{
632
0
  delete this;
633
0
}
634
635
nsresult
636
nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
637
0
{
638
0
  mRightToLeft = aRightToLeft;
639
0
640
0
  // Count the lines
641
0
  int32_t numLines = aLines.size();
642
0
  if (0 == numLines) {
643
0
    // Use gDummyLines so that we don't need null pointer checks in
644
0
    // the accessor methods
645
0
    mLines = gDummyLines;
646
0
    return NS_OK;
647
0
  }
648
0
649
0
  // Make a linear array of the lines
650
0
  mLines = new nsLineBox*[numLines];
651
0
  if (!mLines) {
652
0
    // Use gDummyLines so that we don't need null pointer checks in
653
0
    // the accessor methods
654
0
    mLines = gDummyLines;
655
0
    return NS_ERROR_OUT_OF_MEMORY;
656
0
  }
657
0
  nsLineBox** lp = mLines;
658
0
  for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
659
0
       line != line_end;
660
0
       ++line)
661
0
  {
662
0
    *lp++ = line;
663
0
  }
664
0
  mNumLines = numLines;
665
0
  return NS_OK;
666
0
}
667
668
int32_t
669
nsLineIterator::GetNumLines()
670
0
{
671
0
  return mNumLines;
672
0
}
673
674
bool
675
nsLineIterator::GetDirection()
676
0
{
677
0
  return mRightToLeft;
678
0
}
679
680
NS_IMETHODIMP
681
nsLineIterator::GetLine(int32_t aLineNumber,
682
                        nsIFrame** aFirstFrameOnLine,
683
                        int32_t* aNumFramesOnLine,
684
                        nsRect& aLineBounds)
685
0
{
686
0
  NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
687
0
  NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
688
0
689
0
  if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
690
0
    *aFirstFrameOnLine = nullptr;
691
0
    *aNumFramesOnLine = 0;
692
0
    aLineBounds.SetRect(0, 0, 0, 0);
693
0
    return NS_OK;
694
0
  }
695
0
  nsLineBox* line = mLines[aLineNumber];
696
0
  *aFirstFrameOnLine = line->mFirstChild;
697
0
  *aNumFramesOnLine = line->GetChildCount();
698
0
  aLineBounds = line->GetPhysicalBounds();
699
0
700
0
  return NS_OK;
701
0
}
702
703
int32_t
704
nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
705
0
{
706
0
  MOZ_ASSERT(aStartLine <= mNumLines, "Bogus line numbers");
707
0
  int32_t lineNumber = aStartLine;
708
0
  while (lineNumber != mNumLines) {
709
0
    nsLineBox* line = mLines[lineNumber];
710
0
    if (line->Contains(aFrame)) {
711
0
      return lineNumber;
712
0
    }
713
0
    ++lineNumber;
714
0
  }
715
0
  return -1;
716
0
}
717
718
NS_IMETHODIMP
719
nsLineIterator::CheckLineOrder(int32_t                  aLine,
720
                               bool                     *aIsReordered,
721
                               nsIFrame                 **aFirstVisual,
722
                               nsIFrame                 **aLastVisual)
723
0
{
724
0
  NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
725
0
  nsLineBox* line = mLines[aLine];
726
0
727
0
  if (!line->mFirstChild) { // empty line
728
0
    *aIsReordered = false;
729
0
    *aFirstVisual = nullptr;
730
0
    *aLastVisual = nullptr;
731
0
    return NS_OK;
732
0
  }
733
0
734
0
  nsIFrame* leftmostFrame;
735
0
  nsIFrame* rightmostFrame;
736
0
  *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
737
0
738
0
  // map leftmost/rightmost to first/last according to paragraph direction
739
0
  *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
740
0
  *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
741
0
742
0
  return NS_OK;
743
0
}
744
745
NS_IMETHODIMP
746
nsLineIterator::FindFrameAt(int32_t aLineNumber,
747
                            nsPoint aPos,
748
                            nsIFrame** aFrameFound,
749
                            bool* aPosIsBeforeFirstFrame,
750
                            bool* aPosIsAfterLastFrame)
751
0
{
752
0
  MOZ_ASSERT(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
753
0
             "null OUT ptr");
754
0
755
0
  if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
756
0
    return NS_ERROR_NULL_POINTER;
757
0
  }
758
0
  if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
759
0
    return NS_ERROR_INVALID_ARG;
760
0
  }
761
0
762
0
  nsLineBox* line = mLines[aLineNumber];
763
0
  if (!line) {
764
0
    *aFrameFound = nullptr;
765
0
    *aPosIsBeforeFirstFrame = true;
766
0
    *aPosIsAfterLastFrame = false;
767
0
    return NS_OK;
768
0
  }
769
0
770
0
  if (line->ISize() == 0 && line->BSize() == 0)
771
0
    return NS_ERROR_FAILURE;
772
0
773
0
  nsIFrame* frame = line->mFirstChild;
774
0
  nsIFrame* closestFromStart = nullptr;
775
0
  nsIFrame* closestFromEnd = nullptr;
776
0
777
0
  WritingMode wm = line->mWritingMode;
778
0
  nsSize containerSize = line->mContainerSize;
779
0
780
0
  LogicalPoint pos(wm, aPos, containerSize);
781
0
782
0
  int32_t n = line->GetChildCount();
783
0
  while (n--) {
784
0
    LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
785
0
    if (rect.ISize(wm) > 0) {
786
0
      // If pos.I() is inside this frame - this is it
787
0
      if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
788
0
        closestFromStart = closestFromEnd = frame;
789
0
        break;
790
0
      }
791
0
      if (rect.IStart(wm) < pos.I(wm)) {
792
0
        if (!closestFromStart ||
793
0
            rect.IEnd(wm) > closestFromStart->
794
0
                              GetLogicalRect(wm, containerSize).IEnd(wm))
795
0
          closestFromStart = frame;
796
0
      }
797
0
      else {
798
0
        if (!closestFromEnd ||
799
0
            rect.IStart(wm) < closestFromEnd->
800
0
                                GetLogicalRect(wm, containerSize).IStart(wm))
801
0
          closestFromEnd = frame;
802
0
      }
803
0
    }
804
0
    frame = frame->GetNextSibling();
805
0
  }
806
0
  if (!closestFromStart && !closestFromEnd) {
807
0
    // All frames were zero-width. Just take the first one.
808
0
    closestFromStart = closestFromEnd = line->mFirstChild;
809
0
  }
810
0
  *aPosIsBeforeFirstFrame = mRightToLeft ? !closestFromEnd : !closestFromStart;
811
0
  *aPosIsAfterLastFrame = mRightToLeft ? !closestFromStart : !closestFromEnd;
812
0
  if (closestFromStart == closestFromEnd) {
813
0
    *aFrameFound = closestFromStart;
814
0
  }
815
0
  else if (!closestFromStart) {
816
0
    *aFrameFound = closestFromEnd;
817
0
  }
818
0
  else if (!closestFromEnd) {
819
0
    *aFrameFound = closestFromStart;
820
0
  }
821
0
  else { // we're between two frames
822
0
    nscoord delta =
823
0
      closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
824
0
      closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
825
0
    if (pos.I(wm) < closestFromStart->
826
0
                      GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
827
0
      *aFrameFound = closestFromStart;
828
0
    } else {
829
0
      *aFrameFound = closestFromEnd;
830
0
    }
831
0
  }
832
0
  return NS_OK;
833
0
}
834
835
NS_IMETHODIMP
836
nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
837
0
{
838
0
  aFrame = aFrame->GetNextSibling();
839
0
  return NS_OK;
840
0
}
841
842
//----------------------------------------------------------------------
843
844
#ifdef NS_BUILD_REFCNT_LOGGING
845
nsFloatCacheList::nsFloatCacheList() :
846
  mHead(nullptr)
847
{
848
  MOZ_COUNT_CTOR(nsFloatCacheList);
849
}
850
#endif
851
852
nsFloatCacheList::~nsFloatCacheList()
853
0
{
854
0
  DeleteAll();
855
0
  MOZ_COUNT_DTOR(nsFloatCacheList);
856
0
}
857
858
void
859
nsFloatCacheList::DeleteAll()
860
0
{
861
0
  nsFloatCache* c = mHead;
862
0
  while (c) {
863
0
    nsFloatCache* next = c->Next();
864
0
    delete c;
865
0
    c = next;
866
0
  }
867
0
  mHead = nullptr;
868
0
}
869
870
nsFloatCache*
871
nsFloatCacheList::Tail() const
872
0
{
873
0
  nsFloatCache* fc = mHead;
874
0
  while (fc) {
875
0
    if (!fc->mNext) {
876
0
      break;
877
0
    }
878
0
    fc = fc->mNext;
879
0
  }
880
0
  return fc;
881
0
}
882
883
void
884
nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
885
0
{
886
0
  MOZ_ASSERT(aList.NotEmpty(), "Appending empty list will fail");
887
0
888
0
  nsFloatCache* tail = Tail();
889
0
  if (tail) {
890
0
    NS_ASSERTION(!tail->mNext, "Bogus!");
891
0
    tail->mNext = aList.mHead;
892
0
  }
893
0
  else {
894
0
    NS_ASSERTION(!mHead, "Bogus!");
895
0
    mHead = aList.mHead;
896
0
  }
897
0
  aList.mHead = nullptr;
898
0
  aList.mTail = nullptr;
899
0
}
900
901
nsFloatCache*
902
nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
903
0
{
904
0
  nsFloatCache* fc = mHead;
905
0
  while (fc) {
906
0
    if (fc->mFloat == aOutOfFlowFrame) {
907
0
      break;
908
0
    }
909
0
    fc = fc->Next();
910
0
  }
911
0
  return fc;
912
0
}
913
914
nsFloatCache*
915
nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
916
0
{
917
0
  nsFloatCache* fc = mHead;
918
0
  nsFloatCache* prev = nullptr;
919
0
  while (fc) {
920
0
    if (fc == aElement) {
921
0
      if (prev) {
922
0
        prev->mNext = fc->mNext;
923
0
      } else {
924
0
        mHead = fc->mNext;
925
0
      }
926
0
      return prev;
927
0
    }
928
0
    prev = fc;
929
0
    fc = fc->mNext;
930
0
  }
931
0
  return nullptr;
932
0
}
933
934
//----------------------------------------------------------------------
935
936
#ifdef NS_BUILD_REFCNT_LOGGING
937
nsFloatCacheFreeList::nsFloatCacheFreeList() :
938
  mTail(nullptr)
939
{
940
  MOZ_COUNT_CTOR(nsFloatCacheFreeList);
941
}
942
943
nsFloatCacheFreeList::~nsFloatCacheFreeList()
944
{
945
  MOZ_COUNT_DTOR(nsFloatCacheFreeList);
946
}
947
#endif
948
949
void
950
nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
951
0
{
952
0
  MOZ_ASSERT(aList.NotEmpty(), "Appending empty list will fail");
953
0
954
0
  if (mTail) {
955
0
    NS_ASSERTION(!mTail->mNext, "Bogus");
956
0
    mTail->mNext = aList.mHead;
957
0
  }
958
0
  else {
959
0
    NS_ASSERTION(!mHead, "Bogus");
960
0
    mHead = aList.mHead;
961
0
  }
962
0
  mTail = aList.Tail();
963
0
  aList.mHead = nullptr;
964
0
}
965
966
void
967
nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
968
0
{
969
0
  nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
970
0
  if (mTail == aElement) {
971
0
    mTail = prev;
972
0
  }
973
0
}
974
975
void
976
nsFloatCacheFreeList::DeleteAll()
977
0
{
978
0
  nsFloatCacheList::DeleteAll();
979
0
  mTail = nullptr;
980
0
}
981
982
nsFloatCache*
983
nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
984
0
{
985
0
  MOZ_ASSERT(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
986
0
             "This is a float cache, why isn't the frame out-of-flow?");
987
0
988
0
  nsFloatCache* fc = mHead;
989
0
  if (mHead) {
990
0
    if (mHead == mTail) {
991
0
      mHead = mTail = nullptr;
992
0
    }
993
0
    else {
994
0
      mHead = fc->mNext;
995
0
    }
996
0
    fc->mNext = nullptr;
997
0
  }
998
0
  else {
999
0
    fc = new nsFloatCache();
1000
0
  }
1001
0
  fc->mFloat = aFloat;
1002
0
  return fc;
1003
0
}
1004
1005
void
1006
nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
1007
0
{
1008
0
  NS_ASSERTION(!aFloat->mNext, "Bogus!");
1009
0
  aFloat->mNext = nullptr;
1010
0
  if (mTail) {
1011
0
    NS_ASSERTION(!mTail->mNext, "Bogus!");
1012
0
    mTail->mNext = aFloat;
1013
0
    mTail = aFloat;
1014
0
  }
1015
0
  else {
1016
0
    NS_ASSERTION(!mHead, "Bogus!");
1017
0
    mHead = mTail = aFloat;
1018
0
  }
1019
0
}
1020
1021
//----------------------------------------------------------------------
1022
1023
nsFloatCache::nsFloatCache()
1024
  : mFloat(nullptr),
1025
    mNext(nullptr)
1026
0
{
1027
0
  MOZ_COUNT_CTOR(nsFloatCache);
1028
0
}
1029
1030
#ifdef NS_BUILD_REFCNT_LOGGING
1031
nsFloatCache::~nsFloatCache()
1032
{
1033
  MOZ_COUNT_DTOR(nsFloatCache);
1034
}
1035
#endif