Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsContentIterator.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 "mozilla/DebugOnly.h"
8
#include "nsISupports.h"
9
#include "nsIContentIterator.h"
10
#include "nsRange.h"
11
#include "nsIContent.h"
12
#include "nsCOMPtr.h"
13
#include "nsTArray.h"
14
#include "nsContentUtils.h"
15
#include "nsINode.h"
16
#include "nsCycleCollectionParticipant.h"
17
#include "nsElementTable.h"
18
19
using mozilla::DebugOnly;
20
using mozilla::RawRangeBoundary;
21
22
// couple of utility static functs
23
24
///////////////////////////////////////////////////////////////////////////
25
// NodeIsInTraversalRange: returns true if content is visited during
26
// the traversal of the range in the specified mode.
27
//
28
static bool
29
NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
30
                       const RawRangeBoundary& aStart,
31
                       const RawRangeBoundary& aEnd)
32
0
{
33
0
  if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet()) ||
34
0
      NS_WARN_IF(!aNode)) {
35
0
    return false;
36
0
  }
37
0
38
0
  // If a leaf node contains an end point of the traversal range, it is
39
0
  // always in the traversal range.
40
0
  if (aNode == aStart.Container() || aNode == aEnd.Container()) {
41
0
    if (aNode->IsCharacterData()) {
42
0
      return true; // text node or something
43
0
    }
44
0
    if (!aNode->HasChildren()) {
45
0
      MOZ_ASSERT(aNode != aStart.Container() || aStart.IsStartOfContainer(),
46
0
        "aStart.Container() doesn't have children and not a data node, "
47
0
        "aStart should be at the beginning of its container");
48
0
      MOZ_ASSERT(aNode != aEnd.Container() || aEnd.IsStartOfContainer(),
49
0
        "aEnd.Container() doesn't have children and not a data node, "
50
0
        "aEnd should be at the beginning of its container");
51
0
      return true;
52
0
    }
53
0
  }
54
0
55
0
  nsINode* parent = aNode->GetParentNode();
56
0
  if (!parent) {
57
0
    return false;
58
0
  }
59
0
60
0
  if (!aIsPreMode) {
61
0
    // aNode should always be content, as we have a parent, but let's just be
62
0
    // extra careful and check.
63
0
    nsIContent* content = NS_WARN_IF(!aNode->IsContent())
64
0
      ? nullptr
65
0
      : aNode->AsContent();
66
0
    // Post mode: start < node <= end.
67
0
    RawRangeBoundary afterNode(parent, content);
68
0
    return nsContentUtils::ComparePoints(aStart, afterNode) < 0 &&
69
0
           nsContentUtils::ComparePoints(aEnd, afterNode) >= 0;
70
0
  }
71
0
72
0
  // Pre mode: start <= node < end.
73
0
  RawRangeBoundary beforeNode(parent, aNode->GetPreviousSibling());
74
0
  return nsContentUtils::ComparePoints(aStart, beforeNode) <= 0 &&
75
0
         nsContentUtils::ComparePoints(aEnd, beforeNode) > 0;
76
0
}
77
78
79
80
/*
81
 *  A simple iterator class for traversing the content in "close tag" order
82
 */
83
class nsContentIterator : public nsIContentIterator
84
{
85
public:
86
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
87
  NS_DECL_CYCLE_COLLECTION_CLASS(nsContentIterator)
88
89
  explicit nsContentIterator(bool aPre);
90
91
  // nsIContentIterator interface methods ------------------------------
92
93
  virtual nsresult Init(nsINode* aRoot) override;
94
95
  virtual nsresult Init(nsRange* aRange) override;
96
97
  virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
98
                        nsINode* aEndContainer, uint32_t aEndOffset) override;
99
100
  virtual nsresult Init(const RawRangeBoundary& aStart,
101
                        const RawRangeBoundary& aEnd) override;
102
103
  virtual void First() override;
104
105
  virtual void Last() override;
106
107
  virtual void Next() override;
108
109
  virtual void Prev() override;
110
111
  virtual nsINode* GetCurrentNode() override;
112
113
  virtual bool IsDone() override;
114
115
  virtual nsresult PositionAt(nsINode* aCurNode) override;
116
117
protected:
118
  virtual ~nsContentIterator();
119
120
  /**
121
   * Callers must guarantee that:
122
   * - Neither aStartContainer nor aEndContainer is nullptr.
123
   * - aStartOffset and aEndOffset are valid for its container.
124
   * - The start point and the end point are in document order.
125
   */
126
  nsresult InitInternal(const RawRangeBoundary& aStart,
127
                        const RawRangeBoundary& aEnd);
128
129
  // Recursively get the deepest first/last child of aRoot.  This will return
130
  // aRoot itself if it has no children.
131
  nsINode* GetDeepFirstChild(nsINode* aRoot);
132
  nsIContent* GetDeepFirstChild(nsIContent* aRoot);
133
  nsINode* GetDeepLastChild(nsINode* aRoot);
134
  nsIContent* GetDeepLastChild(nsIContent* aRoot);
135
136
  // Get the next/previous sibling of aNode, or its parent's, or grandparent's,
137
  // etc.  Returns null if aNode and all its ancestors have no next/previous
138
  // sibling.
139
  nsIContent* GetNextSibling(nsINode* aNode);
140
  nsIContent* GetPrevSibling(nsINode* aNode);
141
142
  nsINode* NextNode(nsINode* aNode);
143
  nsINode* PrevNode(nsINode* aNode);
144
145
  void MakeEmpty();
146
147
  virtual void LastRelease();
148
149
  nsCOMPtr<nsINode> mCurNode;
150
  nsCOMPtr<nsINode> mFirst;
151
  nsCOMPtr<nsINode> mLast;
152
  nsCOMPtr<nsINode> mCommonParent;
153
154
  bool mIsDone;
155
  bool mPre;
156
157
private:
158
159
  // no copies or assigns  FIX ME
160
  nsContentIterator(const nsContentIterator&);
161
  nsContentIterator& operator=(const nsContentIterator&);
162
163
};
164
165
166
/******************************************************
167
 * repository cruft
168
 ******************************************************/
169
170
already_AddRefed<nsIContentIterator>
171
NS_NewContentIterator()
172
0
{
173
0
  nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(false);
174
0
  return iter.forget();
175
0
}
176
177
178
already_AddRefed<nsIContentIterator>
179
NS_NewPreContentIterator()
180
0
{
181
0
  nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(true);
182
0
  return iter.forget();
183
0
}
184
185
186
/******************************************************
187
 * XPCOM cruft
188
 ******************************************************/
189
190
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentIterator)
191
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsContentIterator,
192
                                                   LastRelease())
193
194
0
NS_INTERFACE_MAP_BEGIN(nsContentIterator)
195
0
  NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
196
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
197
0
  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsContentIterator)
198
0
NS_INTERFACE_MAP_END
199
200
NS_IMPL_CYCLE_COLLECTION(nsContentIterator,
201
                         mCurNode,
202
                         mFirst,
203
                         mLast,
204
                         mCommonParent)
205
206
void
207
nsContentIterator::LastRelease()
208
0
{
209
0
  mCurNode = nullptr;
210
0
  mFirst = nullptr;
211
0
  mLast = nullptr;
212
0
  mCommonParent = nullptr;
213
0
}
214
215
/******************************************************
216
 * constructor/destructor
217
 ******************************************************/
218
219
nsContentIterator::nsContentIterator(bool aPre)
220
  : mIsDone(false)
221
  , mPre(aPre)
222
0
{
223
0
}
224
225
226
nsContentIterator::~nsContentIterator()
227
0
{
228
0
}
229
230
231
/******************************************************
232
 * Init routines
233
 ******************************************************/
234
235
236
nsresult
237
nsContentIterator::Init(nsINode* aRoot)
238
0
{
239
0
  if (NS_WARN_IF(!aRoot)) {
240
0
    return NS_ERROR_NULL_POINTER;
241
0
  }
242
0
243
0
  mIsDone = false;
244
0
245
0
  if (mPre) {
246
0
    mFirst = aRoot;
247
0
    mLast  = GetDeepLastChild(aRoot);
248
0
    NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
249
0
  } else {
250
0
    mFirst = GetDeepFirstChild(aRoot);
251
0
    NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
252
0
    mLast  = aRoot;
253
0
  }
254
0
255
0
  mCommonParent = aRoot;
256
0
  mCurNode = mFirst;
257
0
  return NS_OK;
258
0
}
259
260
nsresult
261
nsContentIterator::Init(nsRange* aRange)
262
0
{
263
0
  mIsDone = false;
264
0
265
0
  if (NS_WARN_IF(!aRange)) {
266
0
    return NS_ERROR_INVALID_ARG;
267
0
  }
268
0
269
0
  if (NS_WARN_IF(!aRange->IsPositioned())) {
270
0
    return NS_ERROR_INVALID_ARG;
271
0
  }
272
0
273
0
  return InitInternal(aRange->StartRef().AsRaw(), aRange->EndRef().AsRaw());
274
0
}
275
276
nsresult
277
nsContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
278
                        nsINode* aEndContainer, uint32_t aEndOffset)
279
0
{
280
0
  mIsDone = false;
281
0
282
0
  if (NS_WARN_IF(!nsRange::IsValidPoints(aStartContainer, aStartOffset,
283
0
                                         aEndContainer, aEndOffset))) {
284
0
    return NS_ERROR_INVALID_ARG;
285
0
  }
286
0
287
0
  return InitInternal(RawRangeBoundary(aStartContainer, aStartOffset),
288
0
                      RawRangeBoundary(aEndContainer, aEndOffset));
289
0
}
290
291
nsresult
292
nsContentIterator::Init(const RawRangeBoundary& aStart,
293
                        const RawRangeBoundary& aEnd)
294
0
{
295
0
  mIsDone = false;
296
0
297
0
298
0
  if (NS_WARN_IF(!nsRange::IsValidPoints(aStart.Container(), aStart.Offset(),
299
0
                                         aEnd.Container(), aEnd.Offset()))) {
300
0
    return NS_ERROR_INVALID_ARG;
301
0
  }
302
0
303
0
  return InitInternal(aStart, aEnd);
304
0
}
305
306
nsresult
307
nsContentIterator::InitInternal(const RawRangeBoundary& aStart,
308
                                const RawRangeBoundary& aEnd)
309
0
{
310
0
  // get common content parent
311
0
  mCommonParent =
312
0
    nsContentUtils::GetCommonAncestor(aStart.Container(), aEnd.Container());
313
0
  if (NS_WARN_IF(!mCommonParent)) {
314
0
    return NS_ERROR_FAILURE;
315
0
  }
316
0
317
0
  bool startIsData = aStart.Container()->IsCharacterData();
318
0
319
0
  // Check to see if we have a collapsed range, if so, there is nothing to
320
0
  // iterate over.
321
0
  //
322
0
  // XXX: CharacterDataNodes (text nodes) are currently an exception, since
323
0
  //      we always want to be able to iterate text nodes at the end points
324
0
  //      of a range.
325
0
326
0
  if (!startIsData && aStart == aEnd) {
327
0
    MakeEmpty();
328
0
    return NS_OK;
329
0
  }
330
0
331
0
  // Handle ranges within a single character data node.
332
0
  if (startIsData && aStart.Container() == aEnd.Container()) {
333
0
    mFirst = aStart.Container()->AsContent();
334
0
    mLast = mFirst;
335
0
    mCurNode = mFirst;
336
0
337
0
    return NS_OK;
338
0
  }
339
0
340
0
  // Find first node in range.
341
0
342
0
  nsIContent* cChild = nullptr;
343
0
344
0
  // Try to get the child at our starting point. This might return null if
345
0
  // aStart is immediately after the last node in aStart.Container().
346
0
  if (!startIsData) {
347
0
    cChild = aStart.GetChildAtOffset();
348
0
  }
349
0
350
0
  if (!cChild) {
351
0
    // No children (possibly a <br> or text node), or index is after last child.
352
0
353
0
    if (mPre) {
354
0
      // XXX: In the future, if start offset is after the last
355
0
      //      character in the cdata node, should we set mFirst to
356
0
      //      the next sibling?
357
0
358
0
      // Normally we would skip the start node because the start node is outside
359
0
      // of the range in pre mode. However, if aStartOffset == 0, and the node
360
0
      // is a non-container node (e.g. <br>), we don't skip the node in this
361
0
      // case in order to address bug 1215798.
362
0
      bool startIsContainer = true;
363
0
      if (aStart.Container()->IsHTMLElement()) {
364
0
        nsAtom* name = aStart.Container()->NodeInfo()->NameAtom();
365
0
        startIsContainer =
366
0
          nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
367
0
      }
368
0
      if (!startIsData && (startIsContainer || !aStart.IsStartOfContainer())) {
369
0
        mFirst = GetNextSibling(aStart.Container());
370
0
        NS_WARNING_ASSERTION(mFirst, "GetNextSibling returned null");
371
0
372
0
        // Does mFirst node really intersect the range?  The range could be
373
0
        // 'degenerate', i.e., not collapsed but still contain no content.
374
0
        if (mFirst &&
375
0
            NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd))) {
376
0
          mFirst = nullptr;
377
0
        }
378
0
      } else {
379
0
        mFirst = aStart.Container()->AsContent();
380
0
      }
381
0
    } else {
382
0
      // post-order
383
0
      if (NS_WARN_IF(!aStart.Container()->IsContent())) {
384
0
        // What else can we do?
385
0
        mFirst = nullptr;
386
0
      } else {
387
0
        mFirst = aStart.Container()->AsContent();
388
0
      }
389
0
    }
390
0
  } else {
391
0
    if (mPre) {
392
0
      mFirst = cChild;
393
0
    } else {
394
0
      // post-order
395
0
      mFirst = GetDeepFirstChild(cChild);
396
0
      NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
397
0
398
0
      // Does mFirst node really intersect the range?  The range could be
399
0
      // 'degenerate', i.e., not collapsed but still contain no content.
400
0
401
0
      if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd)) {
402
0
        mFirst = nullptr;
403
0
      }
404
0
    }
405
0
  }
406
0
407
0
408
0
  // Find last node in range.
409
0
410
0
  bool endIsData = aEnd.Container()->IsCharacterData();
411
0
412
0
  if (endIsData || !aEnd.Container()->HasChildren() || aEnd.IsStartOfContainer()) {
413
0
    if (mPre) {
414
0
      if (NS_WARN_IF(!aEnd.Container()->IsContent())) {
415
0
        // Not much else to do here...
416
0
        mLast = nullptr;
417
0
      } else {
418
0
        // If the end node is a non-container element and the end offset is 0,
419
0
        // the last element should be the previous node (i.e., shouldn't
420
0
        // include the end node in the range).
421
0
        bool endIsContainer = true;
422
0
        if (aEnd.Container()->IsHTMLElement()) {
423
0
          nsAtom* name = aEnd.Container()->NodeInfo()->NameAtom();
424
0
          endIsContainer =
425
0
            nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
426
0
        }
427
0
        if (!endIsData && !endIsContainer && aEnd.IsStartOfContainer()) {
428
0
          mLast = PrevNode(aEnd.Container());
429
0
          NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
430
0
          if (mLast && mLast != mFirst &&
431
0
              NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
432
0
                                                 RawRangeBoundary(mFirst, 0),
433
0
                                                 aEnd))) {
434
0
            mLast = nullptr;
435
0
          }
436
0
        } else {
437
0
          mLast = aEnd.Container()->AsContent();
438
0
        }
439
0
      }
440
0
    } else {
441
0
      // post-order
442
0
      //
443
0
      // XXX: In the future, if end offset is before the first character in the
444
0
      //      cdata node, should we set mLast to the prev sibling?
445
0
446
0
      if (!endIsData) {
447
0
        mLast = GetPrevSibling(aEnd.Container());
448
0
        NS_WARNING_ASSERTION(mLast, "GetPrevSibling returned null");
449
0
450
0
        if (!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd)) {
451
0
          mLast = nullptr;
452
0
        }
453
0
      } else {
454
0
        mLast = aEnd.Container()->AsContent();
455
0
      }
456
0
    }
457
0
  } else {
458
0
    cChild = aEnd.Ref();
459
0
460
0
    if (NS_WARN_IF(!cChild)) {
461
0
      // No child at offset!
462
0
      MOZ_ASSERT_UNREACHABLE("nsContentIterator::nsContentIterator");
463
0
      return NS_ERROR_FAILURE;
464
0
    }
465
0
466
0
    if (mPre) {
467
0
      mLast  = GetDeepLastChild(cChild);
468
0
      NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
469
0
470
0
      if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd))) {
471
0
        mLast = nullptr;
472
0
      }
473
0
    } else {
474
0
      // post-order
475
0
      mLast = cChild;
476
0
    }
477
0
  }
478
0
479
0
  // If either first or last is null, they both have to be null!
480
0
481
0
  if (!mFirst || !mLast) {
482
0
    mFirst = nullptr;
483
0
    mLast  = nullptr;
484
0
  }
485
0
486
0
  mCurNode = mFirst;
487
0
  mIsDone  = !mCurNode;
488
0
489
0
  return NS_OK;
490
0
}
491
492
void
493
nsContentIterator::MakeEmpty()
494
0
{
495
0
  mCurNode      = nullptr;
496
0
  mFirst        = nullptr;
497
0
  mLast         = nullptr;
498
0
  mCommonParent = nullptr;
499
0
  mIsDone       = true;
500
0
}
501
502
nsINode*
503
nsContentIterator::GetDeepFirstChild(nsINode* aRoot)
504
0
{
505
0
  if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
506
0
    return aRoot;
507
0
  }
508
0
509
0
  return GetDeepFirstChild(aRoot->GetFirstChild());
510
0
}
511
512
nsIContent*
513
nsContentIterator::GetDeepFirstChild(nsIContent* aRoot)
514
0
{
515
0
  if (NS_WARN_IF(!aRoot)) {
516
0
    return nullptr;
517
0
  }
518
0
519
0
  nsIContent* node = aRoot;
520
0
  nsIContent* child = node->GetFirstChild();
521
0
522
0
  while (child) {
523
0
    node = child;
524
0
    child = node->GetFirstChild();
525
0
  }
526
0
527
0
  return node;
528
0
}
529
530
nsINode*
531
nsContentIterator::GetDeepLastChild(nsINode* aRoot)
532
0
{
533
0
  if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
534
0
    return aRoot;
535
0
  }
536
0
537
0
  return GetDeepLastChild(aRoot->GetLastChild());
538
0
}
539
540
nsIContent*
541
nsContentIterator::GetDeepLastChild(nsIContent* aRoot)
542
0
{
543
0
  if (NS_WARN_IF(!aRoot)) {
544
0
    return nullptr;
545
0
  }
546
0
547
0
  nsIContent* node = aRoot;
548
0
  while (node->HasChildren()) {
549
0
    nsIContent* child = node->GetLastChild();
550
0
    node = child;
551
0
  }
552
0
  return node;
553
0
}
554
555
// Get the next sibling, or parent's next sibling, or grandpa's next sibling...
556
nsIContent*
557
nsContentIterator::GetNextSibling(nsINode* aNode)
558
0
{
559
0
  if (NS_WARN_IF(!aNode)) {
560
0
    return nullptr;
561
0
  }
562
0
563
0
  if (aNode->GetNextSibling()) {
564
0
    return aNode->GetNextSibling();
565
0
  }
566
0
567
0
  nsINode* parent = aNode->GetParentNode();
568
0
  if (NS_WARN_IF(!parent)) {
569
0
    return nullptr;
570
0
  }
571
0
572
0
  // XXX This is a hack to preserve previous behaviour: This should be fixed
573
0
  // in bug 1404916. If we were positioned on anonymous content, move to
574
0
  // the first child of our parent.
575
0
  if (parent->GetLastChild() && parent->GetLastChild() != aNode) {
576
0
    return parent->GetFirstChild();
577
0
  }
578
0
579
0
  return GetNextSibling(parent);
580
0
}
581
582
// Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
583
nsIContent*
584
nsContentIterator::GetPrevSibling(nsINode* aNode)
585
0
{
586
0
  if (NS_WARN_IF(!aNode)) {
587
0
    return nullptr;
588
0
  }
589
0
590
0
  if (aNode->GetPreviousSibling()) {
591
0
    return aNode->GetPreviousSibling();
592
0
  }
593
0
594
0
  nsINode* parent = aNode->GetParentNode();
595
0
  if (NS_WARN_IF(!parent)) {
596
0
    return nullptr;
597
0
  }
598
0
599
0
  // XXX This is a hack to preserve previous behaviour: This should be fixed
600
0
  // in bug 1404916. If we were positioned on anonymous content, move to
601
0
  // the last child of our parent.
602
0
  if (parent->GetFirstChild() && parent->GetFirstChild() != aNode) {
603
0
    return parent->GetLastChild();
604
0
  }
605
0
606
0
  return GetPrevSibling(parent);
607
0
}
608
609
nsINode*
610
nsContentIterator::NextNode(nsINode* aNode)
611
0
{
612
0
  nsINode* node = aNode;
613
0
614
0
  // if we are a Pre-order iterator, use pre-order
615
0
  if (mPre) {
616
0
    // if it has children then next node is first child
617
0
    if (node->HasChildren()) {
618
0
      nsIContent* firstChild = node->GetFirstChild();
619
0
      MOZ_ASSERT(firstChild);
620
0
621
0
      return firstChild;
622
0
    }
623
0
624
0
    // else next sibling is next
625
0
    return GetNextSibling(node);
626
0
  }
627
0
628
0
  // post-order
629
0
  nsINode* parent = node->GetParentNode();
630
0
  if (NS_WARN_IF(!parent)) {
631
0
    MOZ_ASSERT(parent, "The node is the root node but not the last node");
632
0
    mIsDone = true;
633
0
    return node;
634
0
  }
635
0
636
0
  nsIContent* sibling = node->GetNextSibling();
637
0
  if (sibling) {
638
0
    // next node is sibling's "deep left" child
639
0
    return GetDeepFirstChild(sibling);
640
0
  }
641
0
642
0
  return parent;
643
0
}
644
645
nsINode*
646
nsContentIterator::PrevNode(nsINode* aNode)
647
0
{
648
0
  nsINode* node = aNode;
649
0
650
0
  // if we are a Pre-order iterator, use pre-order
651
0
  if (mPre) {
652
0
    nsINode* parent = node->GetParentNode();
653
0
    if (NS_WARN_IF(!parent)) {
654
0
      MOZ_ASSERT(parent, "The node is the root node but not the first node");
655
0
      mIsDone = true;
656
0
      return aNode;
657
0
    }
658
0
659
0
    nsIContent* sibling = node->GetPreviousSibling();
660
0
    if (sibling) {
661
0
      return GetDeepLastChild(sibling);
662
0
    }
663
0
664
0
    return parent;
665
0
  }
666
0
667
0
  // post-order
668
0
  if (node->HasChildren()) {
669
0
    return node->GetLastChild();
670
0
  }
671
0
672
0
  // else prev sibling is previous
673
0
  return GetPrevSibling(node);
674
0
}
675
676
/******************************************************
677
 * ContentIterator routines
678
 ******************************************************/
679
680
void
681
nsContentIterator::First()
682
0
{
683
0
  if (mFirst) {
684
0
    mozilla::DebugOnly<nsresult> rv = PositionAt(mFirst);
685
0
    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
686
0
  }
687
0
688
0
  mIsDone = mFirst == nullptr;
689
0
}
690
691
692
void
693
nsContentIterator::Last()
694
0
{
695
0
  // Note that mLast can be nullptr if MakeEmpty() is called in Init() since
696
0
  // at that time, Init() returns NS_OK.
697
0
  if (!mLast) {
698
0
    MOZ_ASSERT(mIsDone);
699
0
    return;
700
0
  }
701
0
702
0
  mozilla::DebugOnly<nsresult> rv = PositionAt(mLast);
703
0
  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
704
0
705
0
  mIsDone = mLast == nullptr;
706
0
}
707
708
709
void
710
nsContentIterator::Next()
711
0
{
712
0
  if (mIsDone || NS_WARN_IF(!mCurNode)) {
713
0
    return;
714
0
  }
715
0
716
0
  if (mCurNode == mLast) {
717
0
    mIsDone = true;
718
0
    return;
719
0
  }
720
0
721
0
  mCurNode = NextNode(mCurNode);
722
0
}
723
724
725
void
726
nsContentIterator::Prev()
727
0
{
728
0
  if (NS_WARN_IF(mIsDone) || NS_WARN_IF(!mCurNode)) {
729
0
    return;
730
0
  }
731
0
732
0
  if (mCurNode == mFirst) {
733
0
    mIsDone = true;
734
0
    return;
735
0
  }
736
0
737
0
  mCurNode = PrevNode(mCurNode);
738
0
}
739
740
741
bool
742
nsContentIterator::IsDone()
743
0
{
744
0
  return mIsDone;
745
0
}
746
747
// Keeping arrays of indexes for the stack of nodes makes PositionAt
748
// interesting...
749
nsresult
750
nsContentIterator::PositionAt(nsINode* aCurNode)
751
0
{
752
0
  if (NS_WARN_IF(!aCurNode)) {
753
0
    return NS_ERROR_NULL_POINTER;
754
0
  }
755
0
756
0
  // take an early out if this doesn't actually change the position
757
0
  if (mCurNode == aCurNode) {
758
0
    mIsDone = false;
759
0
    return NS_OK;
760
0
  }
761
0
  mCurNode = aCurNode;
762
0
763
0
  // Check to see if the node falls within the traversal range.
764
0
765
0
  RawRangeBoundary first(mFirst, 0);
766
0
  RawRangeBoundary last(mLast, 0);
767
0
768
0
  if (mFirst && mLast) {
769
0
    if (mPre) {
770
0
      // In pre we want to record the point immediately before mFirst, which is
771
0
      // the point immediately after mFirst's previous sibling.
772
0
      first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
773
0
774
0
      // If mLast has no children, then we want to make sure to include it.
775
0
      if (!mLast->HasChildren()) {
776
0
        last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
777
0
      }
778
0
    } else {
779
0
      // If the first node has any children, we want to be immediately after the
780
0
      // last. Otherwise we want to be immediately before mFirst.
781
0
      if (mFirst->HasChildren()) {
782
0
        first.SetAfterRef(mFirst, mFirst->GetLastChild());
783
0
      } else {
784
0
        first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
785
0
      }
786
0
787
0
      // Set the last point immediately after the final node.
788
0
      last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
789
0
    }
790
0
  }
791
0
792
0
  NS_WARNING_ASSERTION(first.IsSetAndValid(), "first is not valid");
793
0
  NS_WARNING_ASSERTION(last.IsSetAndValid(), "last is not valid");
794
0
795
0
  // The end positions are always in the range even if it has no parent.  We
796
0
  // need to allow that or 'iter->Init(root)' would assert in Last() or First()
797
0
  // for example, bug 327694.
798
0
  if (mFirst != mCurNode && mLast != mCurNode &&
799
0
      (NS_WARN_IF(!first.IsSet()) || NS_WARN_IF(!last.IsSet()) ||
800
0
       NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre, first, last)))) {
801
0
    mIsDone = true;
802
0
    return NS_ERROR_FAILURE;
803
0
  }
804
0
805
0
  mIsDone = false;
806
0
  return NS_OK;
807
0
}
808
809
nsINode*
810
nsContentIterator::GetCurrentNode()
811
0
{
812
0
  if (mIsDone) {
813
0
    return nullptr;
814
0
  }
815
0
816
0
  NS_ASSERTION(mCurNode, "Null current node in an iterator that's not done!");
817
0
818
0
  return mCurNode;
819
0
}
820
821
822
823
824
825
/*====================================================================================*/
826
/*====================================================================================*/
827
828
829
830
831
832
833
/******************************************************
834
 * nsContentSubtreeIterator
835
 ******************************************************/
836
837
838
/*
839
 *  A simple iterator class for traversing the content in "top subtree" order
840
 */
841
class nsContentSubtreeIterator : public nsContentIterator
842
{
843
public:
844
0
  nsContentSubtreeIterator() : nsContentIterator(false) {}
845
846
  NS_DECL_ISUPPORTS_INHERITED
847
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsContentSubtreeIterator, nsContentIterator)
848
849
  // nsContentIterator overrides ------------------------------
850
851
  virtual nsresult Init(nsINode* aRoot) override;
852
853
  virtual nsresult Init(nsRange* aRange) override;
854
855
  virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
856
                        nsINode* aEndContainer, uint32_t aEndOffset) override;
857
858
  virtual nsresult Init(const RawRangeBoundary& aStart,
859
                        const RawRangeBoundary& aEnd) override;
860
861
  virtual void Next() override;
862
863
  virtual void Prev() override;
864
865
  virtual nsresult PositionAt(nsINode* aCurNode) override;
866
867
  // Must override these because we don't do PositionAt
868
  virtual void First() override;
869
870
  // Must override these because we don't do PositionAt
871
  virtual void Last() override;
872
873
protected:
874
0
  virtual ~nsContentSubtreeIterator() {}
875
876
  /**
877
   * Callers must guarantee that mRange isn't nullptr and is positioned.
878
   */
879
  nsresult InitWithRange();
880
881
  // Returns the highest inclusive ancestor of aNode that's in the range
882
  // (possibly aNode itself).  Returns null if aNode is null, or is not itself
883
  // in the range.  A node is in the range if (node, 0) comes strictly after
884
  // the range endpoint, and (node, node.length) comes strictly before it, so
885
  // the range's start and end nodes will never be considered "in" it.
886
  nsIContent* GetTopAncestorInRange(nsINode* aNode);
887
888
  // no copy's or assigns  FIX ME
889
  nsContentSubtreeIterator(const nsContentSubtreeIterator&);
890
  nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
891
892
  virtual void LastRelease() override;
893
894
  RefPtr<nsRange> mRange;
895
896
  AutoTArray<nsIContent*, 8> mEndNodes;
897
};
898
899
NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
900
NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
901
902
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSubtreeIterator)
903
0
NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
904
905
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator, nsContentIterator,
906
                                   mRange)
907
908
void
909
nsContentSubtreeIterator::LastRelease()
910
0
{
911
0
  mRange = nullptr;
912
0
  nsContentIterator::LastRelease();
913
0
}
914
915
/******************************************************
916
 * repository cruft
917
 ******************************************************/
918
919
already_AddRefed<nsIContentIterator>
920
NS_NewContentSubtreeIterator()
921
0
{
922
0
  nsCOMPtr<nsIContentIterator> iter = new nsContentSubtreeIterator();
923
0
  return iter.forget();
924
0
}
925
926
927
928
/******************************************************
929
 * Init routines
930
 ******************************************************/
931
932
933
nsresult
934
nsContentSubtreeIterator::Init(nsINode* aRoot)
935
0
{
936
0
  return NS_ERROR_NOT_IMPLEMENTED;
937
0
}
938
939
940
nsresult
941
nsContentSubtreeIterator::Init(nsRange* aRange)
942
0
{
943
0
  MOZ_ASSERT(aRange);
944
0
945
0
  mIsDone = false;
946
0
947
0
  if (NS_WARN_IF(!aRange->IsPositioned())) {
948
0
    return NS_ERROR_INVALID_ARG;
949
0
  }
950
0
951
0
  mRange = aRange;
952
0
953
0
  return InitWithRange();
954
0
}
955
956
nsresult
957
nsContentSubtreeIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
958
                               nsINode* aEndContainer, uint32_t aEndOffset)
959
0
{
960
0
  return Init(RawRangeBoundary(aStartContainer, aStartOffset),
961
0
              RawRangeBoundary(aEndContainer, aEndOffset));
962
0
}
963
964
nsresult
965
nsContentSubtreeIterator::Init(const RawRangeBoundary& aStart,
966
                               const RawRangeBoundary& aEnd)
967
0
{
968
0
  mIsDone = false;
969
0
970
0
  RefPtr<nsRange> range;
971
0
  nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
972
0
  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
973
0
      NS_WARN_IF(!range->IsPositioned())) {
974
0
    return NS_ERROR_INVALID_ARG;
975
0
  }
976
0
977
0
  if (NS_WARN_IF(range->StartRef() != aStart) ||
978
0
      NS_WARN_IF(range->EndRef() != aEnd)) {
979
0
    return NS_ERROR_UNEXPECTED;
980
0
  }
981
0
982
0
  mRange = std::move(range);
983
0
984
0
  return InitWithRange();
985
0
}
986
987
nsresult
988
nsContentSubtreeIterator::InitWithRange()
989
0
{
990
0
  MOZ_ASSERT(mRange);
991
0
  MOZ_ASSERT(mRange->IsPositioned());
992
0
993
0
  // get the start node and offset, convert to nsINode
994
0
  mCommonParent = mRange->GetCommonAncestor();
995
0
  nsINode* startContainer = mRange->GetStartContainer();
996
0
  int32_t startOffset = mRange->StartOffset();
997
0
  nsINode* endContainer = mRange->GetEndContainer();
998
0
  int32_t endOffset = mRange->EndOffset();
999
0
  MOZ_ASSERT(mCommonParent && startContainer && endContainer);
1000
0
  // Bug 767169
1001
0
  MOZ_ASSERT(uint32_t(startOffset) <= startContainer->Length() &&
1002
0
             uint32_t(endOffset) <= endContainer->Length());
1003
0
1004
0
  // short circuit when start node == end node
1005
0
  if (startContainer == endContainer) {
1006
0
    nsINode* child = startContainer->GetFirstChild();
1007
0
1008
0
    if (!child || startOffset == endOffset) {
1009
0
      // Text node, empty container, or collapsed
1010
0
      MakeEmpty();
1011
0
      return NS_OK;
1012
0
    }
1013
0
  }
1014
0
1015
0
  // cache ancestors
1016
0
  mEndNodes.Clear();
1017
0
  nsIContent* endNode =
1018
0
    endContainer->IsContent() ? endContainer->AsContent() : nullptr;
1019
0
  while (endNode) {
1020
0
    mEndNodes.AppendElement(endNode);
1021
0
    endNode = endNode->GetParent();
1022
0
  }
1023
0
1024
0
  nsIContent* firstCandidate = nullptr;
1025
0
  nsIContent* lastCandidate = nullptr;
1026
0
1027
0
  // find first node in range
1028
0
  int32_t offset = mRange->StartOffset();
1029
0
1030
0
  nsINode* node = nullptr;
1031
0
  if (!startContainer->GetChildCount()) {
1032
0
    // no children, start at the node itself
1033
0
    node = startContainer;
1034
0
  } else {
1035
0
    nsIContent* child = mRange->GetChildAtStartOffset();
1036
0
    MOZ_ASSERT(child == startContainer->GetChildAt_Deprecated(offset));
1037
0
    if (!child) {
1038
0
      // offset after last child
1039
0
      node = startContainer;
1040
0
    } else {
1041
0
      firstCandidate = child;
1042
0
    }
1043
0
  }
1044
0
1045
0
  if (!firstCandidate) {
1046
0
    // then firstCandidate is next node after node
1047
0
    firstCandidate = GetNextSibling(node);
1048
0
1049
0
    if (!firstCandidate) {
1050
0
      MakeEmpty();
1051
0
      return NS_OK;
1052
0
    }
1053
0
  }
1054
0
1055
0
  firstCandidate = GetDeepFirstChild(firstCandidate);
1056
0
1057
0
  // confirm that this first possible contained node is indeed contained.  Else
1058
0
  // we have a range that does not fully contain any node.
1059
0
1060
0
  bool nodeBefore, nodeAfter;
1061
0
  MOZ_ALWAYS_SUCCEEDS(
1062
0
    nsRange::CompareNodeToRange(firstCandidate, mRange, &nodeBefore, &nodeAfter));
1063
0
1064
0
  if (nodeBefore || nodeAfter) {
1065
0
    MakeEmpty();
1066
0
    return NS_OK;
1067
0
  }
1068
0
1069
0
  // cool, we have the first node in the range.  Now we walk up its ancestors
1070
0
  // to find the most senior that is still in the range.  That's the real first
1071
0
  // node.
1072
0
  mFirst = GetTopAncestorInRange(firstCandidate);
1073
0
1074
0
  // now to find the last node
1075
0
  offset = mRange->EndOffset();
1076
0
  int32_t numChildren = endContainer->GetChildCount();
1077
0
1078
0
  if (offset > numChildren) {
1079
0
    // Can happen for text nodes
1080
0
    offset = numChildren;
1081
0
  }
1082
0
  if (!offset || !numChildren) {
1083
0
    node = endContainer;
1084
0
  } else {
1085
0
    lastCandidate = mRange->EndRef().Ref();
1086
0
    MOZ_ASSERT(lastCandidate == endContainer->GetChildAt_Deprecated(--offset));
1087
0
    NS_ASSERTION(lastCandidate,
1088
0
                 "tree traversal trouble in nsContentSubtreeIterator::Init");
1089
0
  }
1090
0
1091
0
  if (!lastCandidate) {
1092
0
    // then lastCandidate is prev node before node
1093
0
    lastCandidate = GetPrevSibling(node);
1094
0
  }
1095
0
1096
0
  if (!lastCandidate) {
1097
0
    MakeEmpty();
1098
0
    return NS_OK;
1099
0
  }
1100
0
1101
0
  lastCandidate = GetDeepLastChild(lastCandidate);
1102
0
1103
0
  // confirm that this last possible contained node is indeed contained.  Else
1104
0
  // we have a range that does not fully contain any node.
1105
0
1106
0
  MOZ_ALWAYS_SUCCEEDS(
1107
0
    nsRange::CompareNodeToRange(lastCandidate, mRange, &nodeBefore, &nodeAfter));
1108
0
1109
0
  if (nodeBefore || nodeAfter) {
1110
0
    MakeEmpty();
1111
0
    return NS_OK;
1112
0
  }
1113
0
1114
0
  // cool, we have the last node in the range.  Now we walk up its ancestors to
1115
0
  // find the most senior that is still in the range.  That's the real first
1116
0
  // node.
1117
0
  mLast = GetTopAncestorInRange(lastCandidate);
1118
0
1119
0
  mCurNode = mFirst;
1120
0
1121
0
  return NS_OK;
1122
0
}
1123
1124
/****************************************************************
1125
 * nsContentSubtreeIterator overrides of ContentIterator routines
1126
 ****************************************************************/
1127
1128
// we can't call PositionAt in a subtree iterator...
1129
void
1130
nsContentSubtreeIterator::First()
1131
0
{
1132
0
  mIsDone = mFirst == nullptr;
1133
0
1134
0
  mCurNode = mFirst;
1135
0
}
1136
1137
// we can't call PositionAt in a subtree iterator...
1138
void
1139
nsContentSubtreeIterator::Last()
1140
0
{
1141
0
  mIsDone = mLast == nullptr;
1142
0
1143
0
  mCurNode = mLast;
1144
0
}
1145
1146
1147
void
1148
nsContentSubtreeIterator::Next()
1149
0
{
1150
0
  if (mIsDone || !mCurNode) {
1151
0
    return;
1152
0
  }
1153
0
1154
0
  if (mCurNode == mLast) {
1155
0
    mIsDone = true;
1156
0
    return;
1157
0
  }
1158
0
1159
0
  nsINode* nextNode = GetNextSibling(mCurNode);
1160
0
  NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!");
1161
0
1162
0
  int32_t i = mEndNodes.IndexOf(nextNode);
1163
0
  while (i != -1) {
1164
0
    // as long as we are finding ancestors of the endpoint of the range,
1165
0
    // dive down into their children
1166
0
    nextNode = nextNode->GetFirstChild();
1167
0
    NS_ASSERTION(nextNode, "Iterator error, expected a child node!");
1168
0
1169
0
    // should be impossible to get a null pointer.  If we went all the way
1170
0
    // down the child chain to the bottom without finding an interior node,
1171
0
    // then the previous node should have been the last, which was
1172
0
    // was tested at top of routine.
1173
0
    i = mEndNodes.IndexOf(nextNode);
1174
0
  }
1175
0
1176
0
  mCurNode = nextNode;
1177
0
1178
0
  // This shouldn't be needed, but since our selection code can put us
1179
0
  // in a situation where mLast is in generated content, we need this
1180
0
  // to stop the iterator when we've walked past past the last node!
1181
0
  mIsDone = mCurNode == nullptr;
1182
0
}
1183
1184
1185
void
1186
nsContentSubtreeIterator::Prev()
1187
0
{
1188
0
  // Prev should be optimized to use the mStartNodes, just as Next
1189
0
  // uses mEndNodes.
1190
0
  if (mIsDone || !mCurNode) {
1191
0
    return;
1192
0
  }
1193
0
1194
0
  if (mCurNode == mFirst) {
1195
0
    mIsDone = true;
1196
0
    return;
1197
0
  }
1198
0
1199
0
  // If any of these function calls return null, so will all succeeding ones,
1200
0
  // so mCurNode will wind up set to null.
1201
0
  nsINode* prevNode = GetDeepFirstChild(mCurNode);
1202
0
1203
0
  prevNode = PrevNode(prevNode);
1204
0
1205
0
  prevNode = GetDeepLastChild(prevNode);
1206
0
1207
0
  mCurNode = GetTopAncestorInRange(prevNode);
1208
0
1209
0
  // This shouldn't be needed, but since our selection code can put us
1210
0
  // in a situation where mFirst is in generated content, we need this
1211
0
  // to stop the iterator when we've walked past past the first node!
1212
0
  mIsDone = mCurNode == nullptr;
1213
0
}
1214
1215
1216
nsresult
1217
nsContentSubtreeIterator::PositionAt(nsINode* aCurNode)
1218
0
{
1219
0
  NS_ERROR("Not implemented!");
1220
0
1221
0
  return NS_ERROR_NOT_IMPLEMENTED;
1222
0
}
1223
1224
/****************************************************************
1225
 * nsContentSubtreeIterator helper routines
1226
 ****************************************************************/
1227
1228
nsIContent*
1229
nsContentSubtreeIterator::GetTopAncestorInRange(nsINode* aNode)
1230
0
{
1231
0
  if (!aNode || !aNode->GetParentNode()) {
1232
0
    return nullptr;
1233
0
  }
1234
0
1235
0
  // aNode has a parent, so it must be content.
1236
0
  nsIContent* content = aNode->AsContent();
1237
0
1238
0
  // sanity check: aNode is itself in the range
1239
0
  bool nodeBefore, nodeAfter;
1240
0
  nsresult res = nsRange::CompareNodeToRange(aNode, mRange,
1241
0
                                             &nodeBefore, &nodeAfter);
1242
0
  NS_ASSERTION(NS_SUCCEEDED(res) && !nodeBefore && !nodeAfter,
1243
0
               "aNode isn't in mRange, or something else weird happened");
1244
0
  if (NS_FAILED(res) || nodeBefore || nodeAfter) {
1245
0
    return nullptr;
1246
0
  }
1247
0
1248
0
  while (content) {
1249
0
    nsIContent* parent = content->GetParent();
1250
0
    // content always has a parent.  If its parent is the root, however --
1251
0
    // i.e., either it's not content, or it is content but its own parent is
1252
0
    // null -- then we're finished, since we don't go up to the root.
1253
0
    //
1254
0
    // We have to special-case this because CompareNodeToRange treats the root
1255
0
    // node differently -- see bug 765205.
1256
0
    if (!parent || !parent->GetParentNode()) {
1257
0
      return content;
1258
0
    }
1259
0
    MOZ_ALWAYS_SUCCEEDS(
1260
0
      nsRange::CompareNodeToRange(parent, mRange, &nodeBefore, &nodeAfter));
1261
0
1262
0
    if (nodeBefore || nodeAfter) {
1263
0
      return content;
1264
0
    }
1265
0
    content = parent;
1266
0
  }
1267
0
1268
0
  MOZ_CRASH("This should only be possible if aNode was null");
1269
0
}