Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/generic/nsFrameList.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
#include "nsFrameList.h"
8
9
#include "mozilla/ArenaObjectID.h"
10
#include "nsBidiPresUtils.h"
11
#include "nsContainerFrame.h"
12
#include "nsGkAtoms.h"
13
#include "nsILineIterator.h"
14
#include "nsIPresShell.h"
15
#include "nsLayoutUtils.h"
16
#include "nsPresContext.h"
17
18
using namespace mozilla;
19
20
namespace mozilla {
21
namespace layout {
22
namespace detail {
23
const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
24
} // namespace detail
25
} // namespace layout
26
} // namespace mozilla
27
28
void*
29
nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell)
30
0
{
31
0
  return aPresShell->AllocateByObjectID(eArenaObjectID_nsFrameList, sz);
32
0
}
33
34
void
35
nsFrameList::Delete(nsIPresShell* aPresShell)
36
0
{
37
0
  MOZ_ASSERT(this != &EmptyList(), "Shouldn't Delete() this list");
38
0
  NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
39
0
40
0
  aPresShell->FreeByObjectID(eArenaObjectID_nsFrameList, this);
41
0
}
42
43
void
44
nsFrameList::DestroyFrames()
45
0
{
46
0
  while (nsIFrame* frame = RemoveFirstChild()) {
47
0
    frame->Destroy();
48
0
  }
49
0
  mLastChild = nullptr;
50
0
}
51
52
void
53
nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot,
54
                               layout::PostFrameDestroyData& aPostDestroyData)
55
0
{
56
0
  MOZ_ASSERT(aDestructRoot, "Missing destruct root");
57
0
58
0
  while (nsIFrame* frame = RemoveFirstChild()) {
59
0
    frame->DestroyFrom(aDestructRoot, aPostDestroyData);
60
0
  }
61
0
  mLastChild = nullptr;
62
0
}
63
64
void
65
nsFrameList::SetFrames(nsIFrame* aFrameList)
66
0
{
67
0
  MOZ_ASSERT(!mFirstChild, "Losing frames");
68
0
69
0
  mFirstChild = aFrameList;
70
0
  mLastChild = nsLayoutUtils::GetLastSibling(mFirstChild);
71
0
}
72
73
void
74
nsFrameList::RemoveFrame(nsIFrame* aFrame)
75
0
{
76
0
  MOZ_ASSERT(aFrame, "null ptr");
77
#ifdef DEBUG_FRAME_LIST
78
  // ContainsFrame is O(N)
79
  MOZ_ASSERT(ContainsFrame(aFrame), "wrong list");
80
#endif
81
82
0
  nsIFrame* nextFrame = aFrame->GetNextSibling();
83
0
  if (aFrame == mFirstChild) {
84
0
    mFirstChild = nextFrame;
85
0
    aFrame->SetNextSibling(nullptr);
86
0
    if (!nextFrame) {
87
0
      mLastChild = nullptr;
88
0
    }
89
0
  }
90
0
  else {
91
0
    nsIFrame* prevSibling = aFrame->GetPrevSibling();
92
0
    NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
93
0
                 "Broken frame linkage");
94
0
    prevSibling->SetNextSibling(nextFrame);
95
0
    aFrame->SetNextSibling(nullptr);
96
0
    if (!nextFrame) {
97
0
      mLastChild = prevSibling;
98
0
    }
99
0
  }
100
0
}
101
102
nsFrameList
103
nsFrameList::RemoveFramesAfter(nsIFrame* aAfterFrame)
104
0
{
105
0
  if (!aAfterFrame) {
106
0
    nsFrameList result;
107
0
    result.InsertFrames(nullptr, nullptr, *this);
108
0
    return result;
109
0
  }
110
0
111
0
  MOZ_ASSERT(NotEmpty(), "illegal operation on empty list");
112
#ifdef DEBUG_FRAME_LIST
113
  MOZ_ASSERT(ContainsFrame(aAfterFrame), "wrong list");
114
#endif
115
116
0
  nsIFrame* tail = aAfterFrame->GetNextSibling();
117
0
  // if (!tail) return EmptyList();  -- worth optimizing this case?
118
0
  nsIFrame* oldLastChild = mLastChild;
119
0
  mLastChild = aAfterFrame;
120
0
  aAfterFrame->SetNextSibling(nullptr);
121
0
  return nsFrameList(tail, tail ? oldLastChild : nullptr);
122
0
}
123
124
nsIFrame*
125
nsFrameList::RemoveFirstChild()
126
0
{
127
0
  if (mFirstChild) {
128
0
    nsIFrame* firstChild = mFirstChild;
129
0
    RemoveFrame(firstChild);
130
0
    return firstChild;
131
0
  }
132
0
  return nullptr;
133
0
}
134
135
void
136
nsFrameList::DestroyFrame(nsIFrame* aFrame)
137
0
{
138
0
  MOZ_ASSERT(aFrame, "null ptr");
139
0
  RemoveFrame(aFrame);
140
0
  aFrame->Destroy();
141
0
}
142
143
nsFrameList::Slice
144
nsFrameList::InsertFrames(nsContainerFrame* aParent, nsIFrame* aPrevSibling,
145
                          nsFrameList& aFrameList)
146
0
{
147
0
  MOZ_ASSERT(aFrameList.NotEmpty(), "Unexpected empty list");
148
0
149
0
  if (aParent) {
150
0
    aFrameList.ApplySetParent(aParent);
151
0
  }
152
0
153
0
  NS_ASSERTION(IsEmpty() ||
154
0
               FirstChild()->GetParent() == aFrameList.FirstChild()->GetParent(),
155
0
               "frame to add has different parent");
156
0
  NS_ASSERTION(!aPrevSibling ||
157
0
               aPrevSibling->GetParent() == aFrameList.FirstChild()->GetParent(),
158
0
               "prev sibling has different parent");
159
#ifdef DEBUG_FRAME_LIST
160
  // ContainsFrame is O(N)
161
  NS_ASSERTION(!aPrevSibling || ContainsFrame(aPrevSibling),
162
               "prev sibling is not on this list");
163
#endif
164
165
0
  nsIFrame* firstNewFrame = aFrameList.FirstChild();
166
0
  nsIFrame* nextSibling;
167
0
  if (aPrevSibling) {
168
0
    nextSibling = aPrevSibling->GetNextSibling();
169
0
    aPrevSibling->SetNextSibling(firstNewFrame);
170
0
  }
171
0
  else {
172
0
    nextSibling = mFirstChild;
173
0
    mFirstChild = firstNewFrame;
174
0
  }
175
0
176
0
  nsIFrame* lastNewFrame = aFrameList.LastChild();
177
0
  lastNewFrame->SetNextSibling(nextSibling);
178
0
  if (!nextSibling) {
179
0
    mLastChild = lastNewFrame;
180
0
  }
181
0
182
0
  VerifyList();
183
0
184
0
  aFrameList.Clear();
185
0
  return Slice(*this, firstNewFrame, nextSibling);
186
0
}
187
188
nsFrameList
189
nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
190
0
{
191
0
  MOZ_ASSERT(&aLink.List() == this, "Unexpected list");
192
0
  MOZ_ASSERT(!aLink.PrevFrame() ||
193
0
             aLink.PrevFrame()->GetNextSibling() == aLink.NextFrame(),
194
0
             "Unexpected PrevFrame()");
195
0
  MOZ_ASSERT(aLink.PrevFrame() || aLink.NextFrame() == FirstChild(),
196
0
             "Unexpected NextFrame()");
197
0
  MOZ_ASSERT(!aLink.PrevFrame() || aLink.NextFrame() != FirstChild(),
198
0
             "Unexpected NextFrame()");
199
0
  MOZ_ASSERT(aLink.mEnd == nullptr,
200
0
             "Unexpected mEnd for frame link enumerator");
201
0
202
0
  nsIFrame* prev = aLink.PrevFrame();
203
0
  nsIFrame* newFirstFrame = nullptr;
204
0
  if (prev) {
205
0
    // Truncate the list after |prev| and hand the first part to our new list.
206
0
    prev->SetNextSibling(nullptr);
207
0
    newFirstFrame = mFirstChild;
208
0
    mFirstChild = aLink.NextFrame();
209
0
    if (!mFirstChild) { // we handed over the whole list
210
0
      mLastChild = nullptr;
211
0
    }
212
0
213
0
    // Now make sure aLink doesn't point to a frame we no longer have.
214
0
    aLink.mPrev = nullptr;
215
0
  }
216
0
  // else aLink is pointing to before our first frame.  Nothing to do.
217
0
218
0
  return nsFrameList(newFirstFrame, prev);
219
0
}
220
221
nsFrameList
222
nsFrameList::ExtractTail(FrameLinkEnumerator& aLink)
223
0
{
224
0
  MOZ_ASSERT(&aLink.List() == this, "Unexpected list");
225
0
  MOZ_ASSERT(!aLink.PrevFrame() ||
226
0
             aLink.PrevFrame()->GetNextSibling() == aLink.NextFrame(),
227
0
             "Unexpected PrevFrame()");
228
0
  MOZ_ASSERT(aLink.PrevFrame() || aLink.NextFrame() == FirstChild(),
229
0
             "Unexpected NextFrame()");
230
0
  MOZ_ASSERT(!aLink.PrevFrame() || aLink.NextFrame() != FirstChild(),
231
0
             "Unexpected NextFrame()");
232
0
  MOZ_ASSERT(aLink.mEnd == nullptr,
233
0
             "Unexpected mEnd for frame link enumerator");
234
0
235
0
  nsIFrame* prev = aLink.PrevFrame();
236
0
  nsIFrame* newFirstFrame;
237
0
  nsIFrame* newLastFrame;
238
0
  if (prev) {
239
0
    // Truncate the list after |prev| and hand the second part to our new list
240
0
    prev->SetNextSibling(nullptr);
241
0
    newFirstFrame = aLink.NextFrame();
242
0
    newLastFrame = newFirstFrame ? mLastChild : nullptr;
243
0
    mLastChild = prev;
244
0
  } else {
245
0
    // Hand the whole list over to our new list
246
0
    newFirstFrame = mFirstChild;
247
0
    newLastFrame = mLastChild;
248
0
    Clear();
249
0
  }
250
0
251
0
  // Now make sure aLink doesn't point to a frame we no longer have.
252
0
  aLink.mFrame = nullptr;
253
0
254
0
  MOZ_ASSERT(aLink.AtEnd(), "What's going on here?");
255
0
256
0
  return nsFrameList(newFirstFrame, newLastFrame);
257
0
}
258
259
nsIFrame*
260
nsFrameList::FrameAt(int32_t aIndex) const
261
0
{
262
0
  MOZ_ASSERT(aIndex >= 0, "invalid arg");
263
0
  if (aIndex < 0) return nullptr;
264
0
  nsIFrame* frame = mFirstChild;
265
0
  while ((aIndex-- > 0) && frame) {
266
0
    frame = frame->GetNextSibling();
267
0
  }
268
0
  return frame;
269
0
}
270
271
int32_t
272
nsFrameList::IndexOf(nsIFrame* aFrame) const
273
0
{
274
0
  int32_t count = 0;
275
0
  for (nsIFrame* f = mFirstChild; f; f = f->GetNextSibling()) {
276
0
    if (f == aFrame)
277
0
      return count;
278
0
    ++count;
279
0
  }
280
0
  return -1;
281
0
}
282
283
bool
284
nsFrameList::ContainsFrame(const nsIFrame* aFrame) const
285
0
{
286
0
  MOZ_ASSERT(aFrame, "null ptr");
287
0
288
0
  nsIFrame* frame = mFirstChild;
289
0
  while (frame) {
290
0
    if (frame == aFrame) {
291
0
      return true;
292
0
    }
293
0
    frame = frame->GetNextSibling();
294
0
  }
295
0
  return false;
296
0
}
297
298
int32_t
299
nsFrameList::GetLength() const
300
0
{
301
0
  int32_t count = 0;
302
0
  nsIFrame* frame = mFirstChild;
303
0
  while (frame) {
304
0
    count++;
305
0
    frame = frame->GetNextSibling();
306
0
  }
307
0
  return count;
308
0
}
309
310
void
311
nsFrameList::ApplySetParent(nsContainerFrame* aParent) const
312
0
{
313
0
  NS_ASSERTION(aParent, "null ptr");
314
0
315
0
  for (nsIFrame* f = FirstChild(); f; f = f->GetNextSibling()) {
316
0
    f->SetParent(aParent);
317
0
  }
318
0
}
319
320
/* static */ void
321
nsFrameList::UnhookFrameFromSiblings(nsIFrame* aFrame)
322
0
{
323
0
  MOZ_ASSERT(aFrame->GetPrevSibling() && aFrame->GetNextSibling());
324
0
  nsIFrame* const nextSibling = aFrame->GetNextSibling();
325
0
  nsIFrame* const prevSibling = aFrame->GetPrevSibling();
326
0
  aFrame->SetNextSibling(nullptr);
327
0
  prevSibling->SetNextSibling(nextSibling);
328
0
  MOZ_ASSERT(!aFrame->GetPrevSibling() && !aFrame->GetNextSibling());
329
0
}
330
331
#ifdef DEBUG_FRAME_DUMP
332
void
333
nsFrameList::List(FILE* out) const
334
{
335
  fprintf_stderr(out, "<\n");
336
  for (nsIFrame* frame = mFirstChild; frame;
337
       frame = frame->GetNextSibling()) {
338
    frame->List(out, "  ");
339
  }
340
  fprintf_stderr(out, ">\n");
341
}
342
#endif
343
344
nsIFrame*
345
nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const
346
0
{
347
0
  if (!mFirstChild)
348
0
    return nullptr;
349
0
350
0
  nsIFrame* parent = mFirstChild->GetParent();
351
0
  if (!parent)
352
0
    return aFrame ? aFrame->GetPrevSibling() : LastChild();
353
0
354
0
  nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
355
0
356
0
  nsAutoLineIterator iter = parent->GetLineIterator();
357
0
  if (!iter) {
358
0
    // Parent is not a block Frame
359
0
    if (parent->IsLineFrame()) {
360
0
      // Line frames are not bidi-splittable, so need to consider bidi reordering
361
0
      if (paraDir == NSBIDI_LTR) {
362
0
        return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
363
0
      } else { // RTL
364
0
        return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
365
0
      }
366
0
    } else {
367
0
      // Just get the next or prev sibling, depending on block and frame direction.
368
0
      if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
369
0
        return aFrame ? aFrame->GetPrevSibling() : LastChild();
370
0
      } else {
371
0
        return aFrame ? aFrame->GetNextSibling() : mFirstChild;
372
0
      }
373
0
    }
374
0
  }
375
0
376
0
  // Parent is a block frame, so use the LineIterator to find the previous visual
377
0
  // sibling on this line, or the last one on the previous line.
378
0
379
0
  int32_t thisLine;
380
0
  if (aFrame) {
381
0
    thisLine = iter->FindLineContaining(aFrame);
382
0
    if (thisLine < 0)
383
0
      return nullptr;
384
0
  } else {
385
0
    thisLine = iter->GetNumLines();
386
0
  }
387
0
388
0
  nsIFrame* frame = nullptr;
389
0
  nsIFrame* firstFrameOnLine;
390
0
  int32_t numFramesOnLine;
391
0
  nsRect lineBounds;
392
0
393
0
  if (aFrame) {
394
0
    iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds);
395
0
396
0
    if (paraDir == NSBIDI_LTR) {
397
0
      frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
398
0
    } else { // RTL
399
0
      frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
400
0
    }
401
0
  }
402
0
403
0
  if (!frame && thisLine > 0) {
404
0
    // Get the last frame of the previous line
405
0
    iter->GetLine(thisLine - 1, &firstFrameOnLine, &numFramesOnLine, lineBounds);
406
0
407
0
    if (paraDir == NSBIDI_LTR) {
408
0
      frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine, numFramesOnLine);
409
0
    } else { // RTL
410
0
      frame = nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine, numFramesOnLine);
411
0
    }
412
0
  }
413
0
  return frame;
414
0
}
415
416
nsIFrame*
417
nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const
418
0
{
419
0
  if (!mFirstChild)
420
0
    return nullptr;
421
0
422
0
  nsIFrame* parent = mFirstChild->GetParent();
423
0
  if (!parent)
424
0
    return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
425
0
426
0
  nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
427
0
428
0
  nsAutoLineIterator iter = parent->GetLineIterator();
429
0
  if (!iter) {
430
0
    // Parent is not a block Frame
431
0
    if (parent->IsLineFrame()) {
432
0
      // Line frames are not bidi-splittable, so need to consider bidi reordering
433
0
      if (paraDir == NSBIDI_LTR) {
434
0
        return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
435
0
      } else { // RTL
436
0
        return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
437
0
      }
438
0
    } else {
439
0
      // Just get the next or prev sibling, depending on block and frame direction.
440
0
      if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
441
0
        return aFrame ? aFrame->GetNextSibling() : mFirstChild;
442
0
      } else {
443
0
        return aFrame ? aFrame->GetPrevSibling() : LastChild();
444
0
      }
445
0
    }
446
0
  }
447
0
448
0
  // Parent is a block frame, so use the LineIterator to find the next visual
449
0
  // sibling on this line, or the first one on the next line.
450
0
451
0
  int32_t thisLine;
452
0
  if (aFrame) {
453
0
    thisLine = iter->FindLineContaining(aFrame);
454
0
    if (thisLine < 0)
455
0
      return nullptr;
456
0
  } else {
457
0
    thisLine = -1;
458
0
  }
459
0
460
0
  nsIFrame* frame = nullptr;
461
0
  nsIFrame* firstFrameOnLine;
462
0
  int32_t numFramesOnLine;
463
0
  nsRect lineBounds;
464
0
465
0
  if (aFrame) {
466
0
    iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds);
467
0
468
0
    if (paraDir == NSBIDI_LTR) {
469
0
      frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
470
0
    } else { // RTL
471
0
      frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
472
0
    }
473
0
  }
474
0
475
0
  int32_t numLines = iter->GetNumLines();
476
0
  if (!frame && thisLine < numLines - 1) {
477
0
    // Get the first frame of the next line
478
0
    iter->GetLine(thisLine + 1, &firstFrameOnLine, &numFramesOnLine, lineBounds);
479
0
480
0
    if (paraDir == NSBIDI_LTR) {
481
0
      frame = nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine, numFramesOnLine);
482
0
    } else { // RTL
483
0
      frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine, numFramesOnLine);
484
0
    }
485
0
  }
486
0
  return frame;
487
0
}
488
489
#ifdef DEBUG_FRAME_LIST
490
void
491
nsFrameList::VerifyList() const
492
{
493
  NS_ASSERTION((mFirstChild == nullptr) == (mLastChild == nullptr),
494
               "bad list state");
495
496
  if (IsEmpty()) {
497
    return;
498
  }
499
500
  // Simple algorithm to find a loop in a linked list -- advance pointers
501
  // through it at speeds of 1 and 2, and if they ever get to be equal bail
502
  NS_ASSERTION(!mFirstChild->GetPrevSibling(), "bad prev sibling pointer");
503
  nsIFrame *first = mFirstChild, *second = mFirstChild;
504
  for (;;) {
505
    first = first->GetNextSibling();
506
    second = second->GetNextSibling();
507
    if (!second) {
508
      break;
509
    }
510
    NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
511
                 "bad prev sibling pointer");
512
    second = second->GetNextSibling();
513
    if (first == second) {
514
      // Loop detected!  Since second advances faster, they can't both be null;
515
      // we would have broken out of the loop long ago.
516
      NS_ERROR("loop in frame list.  This will probably hang soon.");
517
      return;
518
    }
519
    if (!second) {
520
      break;
521
    }
522
    NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
523
                 "bad prev sibling pointer");
524
  }
525
526
  NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
527
               "bogus mLastChild");
528
  // XXX we should also assert that all GetParent() are either null or
529
  // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
530
  // prevents that, e.g. table captions.
531
}
532
#endif
533
534
namespace mozilla {
535
namespace layout {
536
537
AutoFrameListPtr::~AutoFrameListPtr()
538
0
{
539
0
  if (mFrameList) {
540
0
    mFrameList->Delete(mPresContext->PresShell());
541
0
  }
542
0
}
543
544
} // namespace layout
545
} // namespace mozilla