Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/nsFrameTraversal.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 "nsFrameTraversal.h"
8
9
#include "nsCOMPtr.h"
10
#include "nsGkAtoms.h"
11
12
#include "nsFrameList.h"
13
#include "nsPlaceholderFrame.h"
14
#include "nsContainerFrame.h"
15
16
using namespace mozilla;
17
18
class nsFrameIterator : public nsIFrameEnumerator
19
{
20
public:
21
  typedef nsIFrame::ChildListID ChildListID;
22
23
  NS_DECL_ISUPPORTS
24
25
  virtual void First() override;
26
  virtual void Next() override;
27
  virtual nsIFrame* CurrentItem() override;
28
  virtual bool IsDone() override;
29
30
  virtual void Last() override;
31
  virtual void Prev() override;
32
33
  nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
34
                  nsIteratorType aType, bool aLockScroll, bool aFollowOOFs,
35
                  bool aSkipPopupChecks);
36
37
protected:
38
0
  virtual ~nsFrameIterator() {}
39
40
0
  void      setCurrent(nsIFrame *aFrame){mCurrent = aFrame;}
41
0
  nsIFrame *getCurrent(){return mCurrent;}
42
0
  nsIFrame *getStart(){return mStart;}
43
0
  nsIFrame *getLast(){return mLast;}
44
0
  void      setLast(nsIFrame *aFrame){mLast = aFrame;}
45
0
  int8_t    getOffEdge(){return mOffEdge;}
46
0
  void      setOffEdge(int8_t aOffEdge){mOffEdge = aOffEdge;}
47
48
  /*
49
   Our own versions of the standard frame tree navigation
50
   methods, which, if the iterator is following out-of-flows,
51
   apply the following rules for placeholder frames:
52
53
   - If a frame HAS a placeholder frame, getting its parent
54
   gets the placeholder's parent.
55
56
   - If a frame's first child or next/prev sibling IS a
57
   placeholder frame, then we instead return the real frame.
58
59
   - If a frame HAS a placeholder frame, getting its next/prev
60
   sibling gets the placeholder frame's next/prev sibling.
61
62
   These are all applied recursively to support multiple levels of
63
   placeholders.
64
   */
65
66
  nsIFrame* GetParentFrame(nsIFrame* aFrame);
67
  // like GetParentFrame but returns null once a popup frame is reached
68
  nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
69
70
  nsIFrame* GetFirstChild(nsIFrame* aFrame);
71
  nsIFrame* GetLastChild(nsIFrame* aFrame);
72
73
  nsIFrame* GetNextSibling(nsIFrame* aFrame);
74
  nsIFrame* GetPrevSibling(nsIFrame* aFrame);
75
76
  /*
77
   These methods are overridden by the bidi visual iterator to have the
78
   semantics of "get first child in visual order", "get last child in visual
79
   order", "get next sibling in visual order" and "get previous sibling in visual
80
   order".
81
  */
82
83
  virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
84
  virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
85
86
  virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
87
  virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
88
89
  /**
90
   * Return the placeholder frame for aFrame if it has one, otherwise return
91
   * aFrame itself.
92
   */
93
  nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
94
  bool      IsPopupFrame(nsIFrame* aFrame);
95
96
  nsPresContext* const mPresContext;
97
  const bool mLockScroll;
98
  const bool mFollowOOFs;
99
  const bool mSkipPopupChecks;
100
  const nsIteratorType mType;
101
102
private:
103
  nsIFrame* const mStart;
104
  nsIFrame* mCurrent;
105
  nsIFrame* mLast; //the last one that was in current;
106
  int8_t    mOffEdge; //0= no -1 to far prev, 1 to far next;
107
};
108
109
110
111
// Bidi visual iterator
112
class nsVisualIterator: public nsFrameIterator
113
{
114
public:
115
  nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart,
116
                   nsIteratorType aType, bool aLockScroll,
117
                   bool aFollowOOFs, bool aSkipPopupChecks) :
118
  nsFrameIterator(aPresContext, aStart, aType, aLockScroll,
119
0
                  aFollowOOFs, aSkipPopupChecks) {}
120
121
protected:
122
  nsIFrame* GetFirstChildInner(nsIFrame* aFrame) override;
123
  nsIFrame* GetLastChildInner(nsIFrame* aFrame) override;
124
125
  nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) override;
126
  nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) override;
127
};
128
129
/************IMPLEMENTATIONS**************/
130
131
nsresult
132
NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
133
0
{
134
0
  NS_ENSURE_ARG_POINTER(aResult);
135
0
136
0
  nsCOMPtr<nsIFrameTraversal> t = new nsFrameTraversal();
137
0
  t.forget(aResult);
138
0
139
0
  return NS_OK;
140
0
}
141
142
nsresult
143
NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
144
                     nsPresContext* aPresContext,
145
                     nsIFrame *aStart,
146
                     nsIteratorType aType,
147
                     bool aVisual,
148
                     bool aLockInScrollView,
149
                     bool aFollowOOFs,
150
                     bool aSkipPopupChecks)
151
0
{
152
0
  if (!aEnumerator || !aStart)
153
0
    return NS_ERROR_NULL_POINTER;
154
0
155
0
  if (aFollowOOFs) {
156
0
    aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
157
0
  }
158
0
159
0
  nsCOMPtr<nsIFrameEnumerator> trav;
160
0
  if (aVisual) {
161
0
    trav = new nsVisualIterator(aPresContext, aStart, aType,
162
0
                                aLockInScrollView, aFollowOOFs,
163
0
                                aSkipPopupChecks);
164
0
  } else {
165
0
    trav = new nsFrameIterator(aPresContext, aStart, aType,
166
0
                               aLockInScrollView, aFollowOOFs,
167
0
                               aSkipPopupChecks);
168
0
  }
169
0
  trav.forget(aEnumerator);
170
0
  return NS_OK;
171
0
}
172
173
174
nsFrameTraversal::nsFrameTraversal()
175
0
{
176
0
}
177
178
nsFrameTraversal::~nsFrameTraversal()
179
0
{
180
0
}
181
182
NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal)
183
184
NS_IMETHODIMP
185
 nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
186
                                     nsPresContext* aPresContext,
187
                                     nsIFrame *aStart,
188
                                     int32_t aType,
189
                                     bool aVisual,
190
                                     bool aLockInScrollView,
191
                                     bool aFollowOOFs,
192
                                     bool aSkipPopupChecks)
193
0
{
194
0
  return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
195
0
                              static_cast<nsIteratorType>(aType),
196
0
                              aVisual, aLockInScrollView, aFollowOOFs,
197
0
                              aSkipPopupChecks);
198
0
}
199
200
// nsFrameIterator implementation
201
202
NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
203
204
nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
205
                                 nsIteratorType aType, bool aLockInScrollView,
206
                                 bool aFollowOOFs, bool aSkipPopupChecks)
207
: mPresContext(aPresContext),
208
  mLockScroll(aLockInScrollView),
209
  mFollowOOFs(aFollowOOFs),
210
  mSkipPopupChecks(aSkipPopupChecks),
211
  mType(aType),
212
  mStart(aStart),
213
  mCurrent(aStart),
214
  mLast(aStart),
215
  mOffEdge(0)
216
0
{
217
0
  MOZ_ASSERT(!aFollowOOFs || !aStart->IsPlaceholderFrame(),
218
0
             "Caller should have resolved placeholder frame");
219
0
}
220
221
222
223
nsIFrame*
224
nsFrameIterator::CurrentItem()
225
0
{
226
0
  if (mOffEdge)
227
0
    return nullptr;
228
0
229
0
  return mCurrent;
230
0
}
231
232
233
234
bool
235
nsFrameIterator::IsDone()
236
0
{
237
0
  return mOffEdge != 0;
238
0
}
239
240
void
241
nsFrameIterator::First()
242
0
{
243
0
  mCurrent = mStart;
244
0
}
245
246
static bool
247
IsRootFrame(nsIFrame* aFrame)
248
0
{
249
0
  return aFrame->IsCanvasFrame() || aFrame->IsRootFrame();
250
0
}
251
252
void
253
nsFrameIterator::Last()
254
0
{
255
0
  nsIFrame* result;
256
0
  nsIFrame* parent = getCurrent();
257
0
  // If the current frame is a popup, don't move farther up the tree.
258
0
  // Otherwise, get the nearest root frame or popup.
259
0
  if (mSkipPopupChecks || !parent->IsMenuPopupFrame()) {
260
0
    while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
261
0
      parent = result;
262
0
  }
263
0
264
0
  while ((result = GetLastChild(parent))) {
265
0
    parent = result;
266
0
  }
267
0
268
0
  setCurrent(parent);
269
0
  if (!parent)
270
0
    setOffEdge(1);
271
0
}
272
273
void
274
nsFrameIterator::Next()
275
0
{
276
0
  // recursive-oid method to get next frame
277
0
  nsIFrame *result = nullptr;
278
0
  nsIFrame *parent = getCurrent();
279
0
  if (!parent)
280
0
    parent = getLast();
281
0
282
0
  if (mType == eLeaf) {
283
0
    // Drill down to first leaf
284
0
    while ((result = GetFirstChild(parent))) {
285
0
      parent = result;
286
0
    }
287
0
  } else if (mType == ePreOrder) {
288
0
    result = GetFirstChild(parent);
289
0
    if (result)
290
0
      parent = result;
291
0
  }
292
0
293
0
  if (parent != getCurrent()) {
294
0
    result = parent;
295
0
  } else {
296
0
    while (parent) {
297
0
      result = GetNextSibling(parent);
298
0
      if (result) {
299
0
        if (mType != ePreOrder) {
300
0
          parent = result;
301
0
          while ((result = GetFirstChild(parent))) {
302
0
            parent = result;
303
0
          }
304
0
          result = parent;
305
0
        }
306
0
        break;
307
0
      }
308
0
      else {
309
0
        result = GetParentFrameNotPopup(parent);
310
0
        if (!result || IsRootFrame(result) ||
311
0
            (mLockScroll && result->IsScrollFrame())) {
312
0
          result = nullptr;
313
0
          break;
314
0
        }
315
0
        if (mType == ePostOrder)
316
0
          break;
317
0
        parent = result;
318
0
      }
319
0
    }
320
0
  }
321
0
322
0
  setCurrent(result);
323
0
  if (!result) {
324
0
    setOffEdge(1);
325
0
    setLast(parent);
326
0
  }
327
0
}
328
329
void
330
nsFrameIterator::Prev()
331
0
{
332
0
  // recursive-oid method to get prev frame
333
0
  nsIFrame *result = nullptr;
334
0
  nsIFrame *parent = getCurrent();
335
0
  if (!parent)
336
0
    parent = getLast();
337
0
338
0
  if (mType == eLeaf) {
339
0
    // Drill down to last leaf
340
0
    while ((result = GetLastChild(parent))) {
341
0
      parent = result;
342
0
    }
343
0
  } else if (mType == ePostOrder) {
344
0
    result = GetLastChild(parent);
345
0
    if (result)
346
0
      parent = result;
347
0
  }
348
0
349
0
  if (parent != getCurrent()) {
350
0
    result = parent;
351
0
  } else {
352
0
    while (parent) {
353
0
      result = GetPrevSibling(parent);
354
0
      if (result) {
355
0
        if (mType != ePostOrder) {
356
0
          parent = result;
357
0
          while ((result = GetLastChild(parent))) {
358
0
            parent = result;
359
0
          }
360
0
          result = parent;
361
0
        }
362
0
        break;
363
0
      } else {
364
0
        result = GetParentFrameNotPopup(parent);
365
0
        if (!result || IsRootFrame(result) ||
366
0
            (mLockScroll && result->IsScrollFrame())) {
367
0
          result = nullptr;
368
0
          break;
369
0
        }
370
0
        if (mType == ePreOrder)
371
0
          break;
372
0
        parent = result;
373
0
      }
374
0
    }
375
0
  }
376
0
377
0
  setCurrent(result);
378
0
  if (!result) {
379
0
    setOffEdge(-1);
380
0
    setLast(parent);
381
0
  }
382
0
}
383
384
nsIFrame*
385
nsFrameIterator::GetParentFrame(nsIFrame* aFrame)
386
0
{
387
0
  if (mFollowOOFs)
388
0
    aFrame = GetPlaceholderFrame(aFrame);
389
0
  if (aFrame)
390
0
    return aFrame->GetParent();
391
0
392
0
  return nullptr;
393
0
}
394
395
nsIFrame*
396
nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame)
397
0
{
398
0
  if (mFollowOOFs)
399
0
    aFrame = GetPlaceholderFrame(aFrame);
400
0
  if (aFrame) {
401
0
    nsIFrame* parent = aFrame->GetParent();
402
0
    if (!IsPopupFrame(parent))
403
0
      return parent;
404
0
  }
405
0
406
0
  return nullptr;
407
0
}
408
409
nsIFrame*
410
nsFrameIterator::GetFirstChild(nsIFrame* aFrame)
411
0
{
412
0
  nsIFrame* result = GetFirstChildInner(aFrame);
413
0
  if (mLockScroll && result && result->IsScrollFrame())
414
0
    return nullptr;
415
0
  if (result && mFollowOOFs) {
416
0
    result = nsPlaceholderFrame::GetRealFrameFor(result);
417
0
418
0
    if (IsPopupFrame(result))
419
0
      result = GetNextSibling(result);
420
0
  }
421
0
  return result;
422
0
}
423
424
nsIFrame*
425
nsFrameIterator::GetLastChild(nsIFrame* aFrame)
426
0
{
427
0
  nsIFrame* result = GetLastChildInner(aFrame);
428
0
  if (mLockScroll && result && result->IsScrollFrame())
429
0
    return nullptr;
430
0
  if (result && mFollowOOFs) {
431
0
    result = nsPlaceholderFrame::GetRealFrameFor(result);
432
0
433
0
    if (IsPopupFrame(result))
434
0
      result = GetPrevSibling(result);
435
0
  }
436
0
  return result;
437
0
}
438
439
nsIFrame*
440
nsFrameIterator::GetNextSibling(nsIFrame* aFrame)
441
0
{
442
0
  nsIFrame* result = nullptr;
443
0
  if (mFollowOOFs)
444
0
    aFrame = GetPlaceholderFrame(aFrame);
445
0
  if (aFrame) {
446
0
    result = GetNextSiblingInner(aFrame);
447
0
    if (result && mFollowOOFs)
448
0
      result = nsPlaceholderFrame::GetRealFrameFor(result);
449
0
  }
450
0
451
0
  if (mFollowOOFs && IsPopupFrame(result))
452
0
    result = GetNextSibling(result);
453
0
454
0
  return result;
455
0
}
456
457
nsIFrame*
458
nsFrameIterator::GetPrevSibling(nsIFrame* aFrame)
459
0
{
460
0
  nsIFrame* result = nullptr;
461
0
  if (mFollowOOFs)
462
0
    aFrame = GetPlaceholderFrame(aFrame);
463
0
  if (aFrame) {
464
0
    result = GetPrevSiblingInner(aFrame);
465
0
    if (result && mFollowOOFs)
466
0
      result = nsPlaceholderFrame::GetRealFrameFor(result);
467
0
  }
468
0
469
0
  if (mFollowOOFs && IsPopupFrame(result))
470
0
    result = GetPrevSibling(result);
471
0
472
0
  return result;
473
0
}
474
475
nsIFrame*
476
nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame)
477
0
{
478
0
  return aFrame->PrincipalChildList().FirstChild();
479
0
}
480
481
nsIFrame*
482
nsFrameIterator::GetLastChildInner(nsIFrame* aFrame)
483
0
{
484
0
  return aFrame->PrincipalChildList().LastChild();
485
0
}
486
487
nsIFrame*
488
nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame)
489
0
{
490
0
  return aFrame->GetNextSibling();
491
0
}
492
493
nsIFrame*
494
nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame)
495
0
{
496
0
  return aFrame->GetPrevSibling();
497
0
}
498
499
500
nsIFrame*
501
nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame)
502
0
{
503
0
  if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
504
0
    return aFrame;
505
0
  }
506
0
  nsIFrame* placeholder = aFrame->GetPlaceholderFrame();
507
0
  return placeholder ? placeholder : aFrame;
508
0
}
509
510
bool
511
nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
512
0
{
513
0
  // If skipping popup checks, pretend this isn't one.
514
0
  if (mSkipPopupChecks) {
515
0
    return false;
516
0
  }
517
0
518
0
  return (aFrame &&
519
0
          aFrame->StyleDisplay()->mDisplay == StyleDisplay::MozPopup);
520
0
}
521
522
// nsVisualIterator implementation
523
524
nsIFrame*
525
nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame)
526
0
{
527
0
  return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
528
0
}
529
530
nsIFrame*
531
nsVisualIterator::GetLastChildInner(nsIFrame* aFrame)
532
0
{
533
0
  return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
534
0
}
535
536
nsIFrame*
537
nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame)
538
0
{
539
0
  nsIFrame* parent = GetParentFrame(aFrame);
540
0
  if (!parent)
541
0
    return nullptr;
542
0
  return parent->PrincipalChildList().GetNextVisualFor(aFrame);
543
0
}
544
545
nsIFrame*
546
nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame)
547
0
{
548
0
  nsIFrame* parent = GetParentFrame(aFrame);
549
0
  if (!parent)
550
0
    return nullptr;
551
0
  return parent->PrincipalChildList().GetPrevVisualFor(aFrame);
552
0
}