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