Line data Source code
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 :
19 : using mozilla::DebugOnly;
20 :
21 : // couple of utility static functs
22 :
23 : ///////////////////////////////////////////////////////////////////////////
24 : // NodeToParentOffset: returns the node's parent and offset.
25 : //
26 :
27 : static nsINode*
28 0 : NodeToParentOffset(nsINode* aNode, int32_t* aOffset)
29 : {
30 0 : *aOffset = 0;
31 :
32 0 : nsINode* parent = aNode->GetParentNode();
33 :
34 0 : if (parent) {
35 0 : *aOffset = parent->IndexOf(aNode);
36 0 : NS_WARNING_ASSERTION(*aOffset >= 0, "bad offset");
37 : }
38 :
39 0 : return parent;
40 : }
41 :
42 : ///////////////////////////////////////////////////////////////////////////
43 : // NodeIsInTraversalRange: returns true if content is visited during
44 : // the traversal of the range in the specified mode.
45 : //
46 : static bool
47 0 : NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
48 : nsINode* aStartContainer, int32_t aStartOffset,
49 : nsINode* aEndContainer, int32_t aEndOffset)
50 : {
51 0 : if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer) ||
52 0 : NS_WARN_IF(!aNode)) {
53 0 : return false;
54 : }
55 :
56 : // If a leaf node contains an end point of the traversal range, it is
57 : // always in the traversal range.
58 0 : if (aNode == aStartContainer || aNode == aEndContainer) {
59 0 : if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
60 0 : return true; // text node or something
61 : }
62 0 : if (!aNode->HasChildren()) {
63 0 : MOZ_ASSERT(aNode != aStartContainer || !aStartOffset,
64 : "aStartContainer doesn't have children and not a data node, "
65 : "aStartOffset should be 0");
66 0 : MOZ_ASSERT(aNode != aEndContainer || !aEndOffset,
67 : "aEndContainer doesn't have children and not a data node, "
68 : "aEndOffset should be 0");
69 0 : return true;
70 : }
71 : }
72 :
73 0 : nsINode* parent = aNode->GetParentNode();
74 0 : if (!parent) {
75 0 : return false;
76 : }
77 :
78 0 : int32_t indx = parent->IndexOf(aNode);
79 0 : NS_WARNING_ASSERTION(indx != -1, "bad indx");
80 :
81 0 : if (!aIsPreMode) {
82 0 : ++indx;
83 : }
84 :
85 0 : return nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
86 0 : parent, indx) <= 0 &&
87 0 : nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
88 0 : parent, indx) >= 0;
89 : }
90 :
91 :
92 :
93 : /*
94 : * A simple iterator class for traversing the content in "close tag" order
95 : */
96 : class nsContentIterator : public nsIContentIterator
97 : {
98 : public:
99 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
100 0 : NS_DECL_CYCLE_COLLECTION_CLASS(nsContentIterator)
101 :
102 : explicit nsContentIterator(bool aPre);
103 :
104 : // nsIContentIterator interface methods ------------------------------
105 :
106 : virtual nsresult Init(nsINode* aRoot) override;
107 :
108 : virtual nsresult Init(nsIDOMRange* aRange) override;
109 :
110 : virtual void First() override;
111 :
112 : virtual void Last() override;
113 :
114 : virtual void Next() override;
115 :
116 : virtual void Prev() override;
117 :
118 : virtual nsINode* GetCurrentNode() override;
119 :
120 : virtual bool IsDone() override;
121 :
122 : virtual nsresult PositionAt(nsINode* aCurNode) override;
123 :
124 : protected:
125 : virtual ~nsContentIterator();
126 :
127 : // Recursively get the deepest first/last child of aRoot. This will return
128 : // aRoot itself if it has no children.
129 : nsINode* GetDeepFirstChild(nsINode* aRoot,
130 : nsTArray<int32_t>* aIndexes = nullptr);
131 : nsIContent* GetDeepFirstChild(nsIContent* aRoot,
132 : nsTArray<int32_t>* aIndexes = nullptr);
133 : nsINode* GetDeepLastChild(nsINode* aRoot,
134 : nsTArray<int32_t>* aIndexes = nullptr);
135 : nsIContent* GetDeepLastChild(nsIContent* aRoot,
136 : nsTArray<int32_t>* aIndexes = nullptr);
137 :
138 : // Get the next/previous sibling of aNode, or its parent's, or grandparent's,
139 : // etc. Returns null if aNode and all its ancestors have no next/previous
140 : // sibling.
141 : nsIContent* GetNextSibling(nsINode* aNode,
142 : nsTArray<int32_t>* aIndexes = nullptr);
143 : nsIContent* GetPrevSibling(nsINode* aNode,
144 : nsTArray<int32_t>* aIndexes = nullptr);
145 :
146 : nsINode* NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
147 : nsINode* PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
148 :
149 : // WARNING: This function is expensive
150 : nsresult RebuildIndexStack();
151 :
152 : void MakeEmpty();
153 :
154 : virtual void LastRelease();
155 :
156 : nsCOMPtr<nsINode> mCurNode;
157 : nsCOMPtr<nsINode> mFirst;
158 : nsCOMPtr<nsINode> mLast;
159 : nsCOMPtr<nsINode> mCommonParent;
160 :
161 : // used by nsContentIterator to cache indices
162 : AutoTArray<int32_t, 8> mIndexes;
163 :
164 : // used by nsSubtreeIterator to cache indices. Why put them in the base
165 : // class? Because otherwise I have to duplicate the routines GetNextSibling
166 : // etc across both classes, with slight variations for caching. Or
167 : // alternately, create a base class for the cache itself and have all the
168 : // cache manipulation go through a vptr. I think this is the best space and
169 : // speed combo, even though it's ugly.
170 : int32_t mCachedIndex;
171 : // another note about mCachedIndex: why should the subtree iterator use a
172 : // trivial cached index instead of the mre robust array of indicies (which is
173 : // what the basic content iterator uses)? The reason is that subtree
174 : // iterators do not do much transitioning between parents and children. They
175 : // tend to stay at the same level. In fact, you can prove (though I won't
176 : // attempt it here) that they change levels at most n+m times, where n is the
177 : // height of the parent hierarchy from the range start to the common
178 : // ancestor, and m is the the height of the parent hierarchy from the range
179 : // end to the common ancestor. If we used the index array, we would pay the
180 : // price up front for n, and then pay the cost for m on the fly later on.
181 : // With the simple cache, we only "pay as we go". Either way, we call
182 : // IndexOf() once for each change of level in the hierarchy. Since a trivial
183 : // index is much simpler, we use it for the subtree iterator.
184 :
185 : bool mIsDone;
186 : bool mPre;
187 :
188 : private:
189 :
190 : // no copies or assigns FIX ME
191 : nsContentIterator(const nsContentIterator&);
192 : nsContentIterator& operator=(const nsContentIterator&);
193 :
194 : };
195 :
196 :
197 : /******************************************************
198 : * repository cruft
199 : ******************************************************/
200 :
201 : already_AddRefed<nsIContentIterator>
202 0 : NS_NewContentIterator()
203 : {
204 0 : nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(false);
205 0 : return iter.forget();
206 : }
207 :
208 :
209 : already_AddRefed<nsIContentIterator>
210 0 : NS_NewPreContentIterator()
211 : {
212 0 : nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(true);
213 0 : return iter.forget();
214 : }
215 :
216 :
217 : /******************************************************
218 : * XPCOM cruft
219 : ******************************************************/
220 :
221 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentIterator)
222 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsContentIterator,
223 : LastRelease())
224 :
225 0 : NS_INTERFACE_MAP_BEGIN(nsContentIterator)
226 0 : NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
227 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
228 0 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsContentIterator)
229 0 : NS_INTERFACE_MAP_END
230 :
231 0 : NS_IMPL_CYCLE_COLLECTION(nsContentIterator,
232 : mCurNode,
233 : mFirst,
234 : mLast,
235 : mCommonParent)
236 :
237 : void
238 0 : nsContentIterator::LastRelease()
239 : {
240 0 : mCurNode = nullptr;
241 0 : mFirst = nullptr;
242 0 : mLast = nullptr;
243 0 : mCommonParent = nullptr;
244 0 : }
245 :
246 : /******************************************************
247 : * constructor/destructor
248 : ******************************************************/
249 :
250 0 : nsContentIterator::nsContentIterator(bool aPre) :
251 : // don't need to explicitly initialize |nsCOMPtr|s, they will automatically
252 : // be nullptr
253 0 : mCachedIndex(0), mIsDone(false), mPre(aPre)
254 : {
255 0 : }
256 :
257 :
258 0 : nsContentIterator::~nsContentIterator()
259 : {
260 0 : }
261 :
262 :
263 : /******************************************************
264 : * Init routines
265 : ******************************************************/
266 :
267 :
268 : nsresult
269 0 : nsContentIterator::Init(nsINode* aRoot)
270 : {
271 0 : if (NS_WARN_IF(!aRoot)) {
272 0 : return NS_ERROR_NULL_POINTER;
273 : }
274 :
275 0 : mIsDone = false;
276 0 : mIndexes.Clear();
277 :
278 0 : if (mPre) {
279 0 : mFirst = aRoot;
280 0 : mLast = GetDeepLastChild(aRoot);
281 0 : NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
282 : } else {
283 0 : mFirst = GetDeepFirstChild(aRoot);
284 0 : NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
285 0 : mLast = aRoot;
286 : }
287 :
288 0 : mCommonParent = aRoot;
289 0 : mCurNode = mFirst;
290 0 : RebuildIndexStack();
291 0 : return NS_OK;
292 : }
293 :
294 : nsresult
295 0 : nsContentIterator::Init(nsIDOMRange* aDOMRange)
296 : {
297 0 : if (NS_WARN_IF(!aDOMRange)) {
298 0 : return NS_ERROR_INVALID_ARG;
299 : }
300 0 : nsRange* range = static_cast<nsRange*>(aDOMRange);
301 :
302 0 : mIsDone = false;
303 :
304 : // get common content parent
305 0 : mCommonParent = range->GetCommonAncestor();
306 0 : if (NS_WARN_IF(!mCommonParent)) {
307 0 : return NS_ERROR_FAILURE;
308 : }
309 :
310 : // get the start node and offset
311 0 : int32_t startIndx = range->StartOffset();
312 0 : NS_WARNING_ASSERTION(startIndx >= 0, "bad startIndx");
313 0 : nsINode* startNode = range->GetStartContainer();
314 0 : if (NS_WARN_IF(!startNode)) {
315 0 : return NS_ERROR_FAILURE;
316 : }
317 :
318 : // get the end node and offset
319 0 : int32_t endIndx = range->EndOffset();
320 0 : NS_WARNING_ASSERTION(endIndx >= 0, "bad endIndx");
321 0 : nsINode* endNode = range->GetEndContainer();
322 0 : if (NS_WARN_IF(!endNode)) {
323 0 : return NS_ERROR_FAILURE;
324 : }
325 :
326 0 : bool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE);
327 :
328 : // short circuit when start node == end node
329 0 : if (startNode == endNode) {
330 : // Check to see if we have a collapsed range, if so, there is nothing to
331 : // iterate over.
332 : //
333 : // XXX: CharacterDataNodes (text nodes) are currently an exception, since
334 : // we always want to be able to iterate text nodes at the end points
335 : // of a range.
336 :
337 0 : if (!startIsData && startIndx == endIndx) {
338 0 : MakeEmpty();
339 0 : return NS_OK;
340 : }
341 :
342 0 : if (startIsData) {
343 : // It's a character data node.
344 0 : mFirst = startNode->AsContent();
345 0 : mLast = mFirst;
346 0 : mCurNode = mFirst;
347 :
348 0 : DebugOnly<nsresult> rv = RebuildIndexStack();
349 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
350 0 : return NS_OK;
351 : }
352 : }
353 :
354 : // Find first node in range.
355 :
356 0 : nsIContent* cChild = nullptr;
357 :
358 : // Valid start indices are 0 <= startIndx <= childCount. That means if start
359 : // node has no children, only offset 0 is valid.
360 0 : if (!startIsData && uint32_t(startIndx) < startNode->GetChildCount()) {
361 0 : cChild = startNode->GetChildAt(startIndx);
362 0 : NS_WARNING_ASSERTION(cChild, "GetChildAt returned null");
363 : }
364 :
365 0 : if (!cChild) {
366 : // No children (possibly a <br> or text node), or index is after last child.
367 :
368 0 : if (mPre) {
369 : // XXX: In the future, if start offset is after the last
370 : // character in the cdata node, should we set mFirst to
371 : // the next sibling?
372 :
373 : // Normally we would skip the start node because the start node is outside
374 : // of the range in pre mode. However, if startIndx == 0, it means the node
375 : // has no children, and the node may be <br> or something. We don't skip
376 : // the node in this case in order to address bug 1215798.
377 0 : if (!startIsData && startIndx) {
378 0 : mFirst = GetNextSibling(startNode);
379 0 : NS_WARNING_ASSERTION(mFirst, "GetNextSibling returned null");
380 :
381 : // Does mFirst node really intersect the range? The range could be
382 : // 'degenerate', i.e., not collapsed but still contain no content.
383 0 : if (mFirst &&
384 0 : NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode,
385 : startIndx, endNode, endIndx))) {
386 0 : mFirst = nullptr;
387 : }
388 : } else {
389 0 : mFirst = startNode->AsContent();
390 : }
391 : } else {
392 : // post-order
393 0 : if (NS_WARN_IF(!startNode->IsContent())) {
394 : // What else can we do?
395 0 : mFirst = nullptr;
396 : } else {
397 0 : mFirst = startNode->AsContent();
398 : }
399 : }
400 : } else {
401 0 : if (mPre) {
402 0 : mFirst = cChild;
403 : } else {
404 : // post-order
405 0 : mFirst = GetDeepFirstChild(cChild);
406 0 : NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
407 :
408 : // Does mFirst node really intersect the range? The range could be
409 : // 'degenerate', i.e., not collapsed but still contain no content.
410 :
411 0 : if (mFirst &&
412 0 : !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
413 0 : endNode, endIndx)) {
414 0 : mFirst = nullptr;
415 : }
416 : }
417 : }
418 :
419 :
420 : // Find last node in range.
421 :
422 0 : bool endIsData = endNode->IsNodeOfType(nsINode::eDATA_NODE);
423 :
424 0 : if (endIsData || !endNode->HasChildren() || endIndx == 0) {
425 0 : if (mPre) {
426 0 : if (NS_WARN_IF(!endNode->IsContent())) {
427 : // Not much else to do here...
428 0 : mLast = nullptr;
429 : } else {
430 : // If the end node is an empty element and the end offset is 0,
431 : // the last element should be the previous node (i.e., shouldn't
432 : // include the end node in the range).
433 0 : if (!endIsData && !endNode->HasChildren() && !endIndx) {
434 0 : mLast = PrevNode(endNode);
435 0 : NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
436 0 : if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
437 : startNode, startIndx,
438 : endNode, endIndx))) {
439 0 : mLast = nullptr;
440 : }
441 : } else {
442 0 : mLast = endNode->AsContent();
443 : }
444 : }
445 : } else {
446 : // post-order
447 : //
448 : // XXX: In the future, if end offset is before the first character in the
449 : // cdata node, should we set mLast to the prev sibling?
450 :
451 0 : if (!endIsData) {
452 0 : mLast = GetPrevSibling(endNode);
453 0 : NS_WARNING_ASSERTION(mLast, "GetPrevSibling returned null");
454 :
455 0 : if (!NodeIsInTraversalRange(mLast, mPre,
456 : startNode, startIndx,
457 : endNode, endIndx)) {
458 0 : mLast = nullptr;
459 : }
460 : } else {
461 0 : mLast = endNode->AsContent();
462 : }
463 : }
464 : } else {
465 0 : int32_t indx = endIndx;
466 :
467 0 : cChild = endNode->GetChildAt(--indx);
468 :
469 0 : if (NS_WARN_IF(!cChild)) {
470 : // No child at offset!
471 0 : NS_NOTREACHED("nsContentIterator::nsContentIterator");
472 0 : return NS_ERROR_FAILURE;
473 : }
474 :
475 0 : if (mPre) {
476 0 : mLast = GetDeepLastChild(cChild);
477 0 : NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
478 :
479 0 : if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
480 : startNode, startIndx,
481 : endNode, endIndx))) {
482 0 : mLast = nullptr;
483 : }
484 : } else {
485 : // post-order
486 0 : mLast = cChild;
487 : }
488 : }
489 :
490 : // If either first or last is null, they both have to be null!
491 :
492 0 : if (!mFirst || !mLast) {
493 0 : mFirst = nullptr;
494 0 : mLast = nullptr;
495 : }
496 :
497 0 : mCurNode = mFirst;
498 0 : mIsDone = !mCurNode;
499 :
500 0 : if (!mCurNode) {
501 0 : mIndexes.Clear();
502 : } else {
503 0 : DebugOnly<nsresult> rv = RebuildIndexStack();
504 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
505 : }
506 :
507 0 : return NS_OK;
508 : }
509 :
510 :
511 : /******************************************************
512 : * Helper routines
513 : ******************************************************/
514 : // WARNING: This function is expensive
515 : nsresult
516 0 : nsContentIterator::RebuildIndexStack()
517 : {
518 : // Make sure we start at the right indexes on the stack! Build array up
519 : // to common parent of start and end. Perhaps it's too many entries, but
520 : // that's far better than too few.
521 : nsINode* parent;
522 : nsINode* current;
523 :
524 0 : mIndexes.Clear();
525 0 : current = mCurNode;
526 0 : if (!current) {
527 0 : return NS_OK;
528 : }
529 :
530 0 : while (current != mCommonParent) {
531 0 : parent = current->GetParentNode();
532 :
533 0 : if (NS_WARN_IF(!parent)) {
534 0 : return NS_ERROR_FAILURE;
535 : }
536 :
537 0 : mIndexes.InsertElementAt(0, parent->IndexOf(current));
538 :
539 0 : current = parent;
540 : }
541 :
542 0 : return NS_OK;
543 : }
544 :
545 : void
546 0 : nsContentIterator::MakeEmpty()
547 : {
548 0 : mCurNode = nullptr;
549 0 : mFirst = nullptr;
550 0 : mLast = nullptr;
551 0 : mCommonParent = nullptr;
552 0 : mIsDone = true;
553 0 : mIndexes.Clear();
554 0 : }
555 :
556 : nsINode*
557 0 : nsContentIterator::GetDeepFirstChild(nsINode* aRoot,
558 : nsTArray<int32_t>* aIndexes)
559 : {
560 0 : if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
561 0 : return aRoot;
562 : }
563 : // We can't pass aRoot itself to the full GetDeepFirstChild, because that
564 : // will only take nsIContent and aRoot might be a document. Pass aRoot's
565 : // child, but be sure to preserve aIndexes.
566 0 : if (aIndexes) {
567 0 : aIndexes->AppendElement(0);
568 : }
569 0 : return GetDeepFirstChild(aRoot->GetFirstChild(), aIndexes);
570 : }
571 :
572 : nsIContent*
573 0 : nsContentIterator::GetDeepFirstChild(nsIContent* aRoot,
574 : nsTArray<int32_t>* aIndexes)
575 : {
576 0 : if (NS_WARN_IF(!aRoot)) {
577 0 : return nullptr;
578 : }
579 :
580 0 : nsIContent* node = aRoot;
581 0 : nsIContent* child = node->GetFirstChild();
582 :
583 0 : while (child) {
584 0 : if (aIndexes) {
585 : // Add this node to the stack of indexes
586 0 : aIndexes->AppendElement(0);
587 : }
588 0 : node = child;
589 0 : child = node->GetFirstChild();
590 : }
591 :
592 0 : return node;
593 : }
594 :
595 : nsINode*
596 0 : nsContentIterator::GetDeepLastChild(nsINode* aRoot,
597 : nsTArray<int32_t>* aIndexes)
598 : {
599 0 : if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
600 0 : return aRoot;
601 : }
602 : // We can't pass aRoot itself to the full GetDeepLastChild, because that will
603 : // only take nsIContent and aRoot might be a document. Pass aRoot's child,
604 : // but be sure to preserve aIndexes.
605 0 : if (aIndexes) {
606 0 : aIndexes->AppendElement(aRoot->GetChildCount() - 1);
607 : }
608 0 : return GetDeepLastChild(aRoot->GetLastChild(), aIndexes);
609 : }
610 :
611 : nsIContent*
612 0 : nsContentIterator::GetDeepLastChild(nsIContent* aRoot,
613 : nsTArray<int32_t>* aIndexes)
614 : {
615 0 : if (NS_WARN_IF(!aRoot)) {
616 0 : return nullptr;
617 : }
618 :
619 0 : nsIContent* node = aRoot;
620 0 : int32_t numChildren = node->GetChildCount();
621 :
622 0 : while (numChildren) {
623 0 : nsIContent* child = node->GetChildAt(--numChildren);
624 :
625 0 : if (aIndexes) {
626 : // Add this node to the stack of indexes
627 0 : aIndexes->AppendElement(numChildren);
628 : }
629 0 : numChildren = child->GetChildCount();
630 0 : node = child;
631 : }
632 :
633 0 : return node;
634 : }
635 :
636 : // Get the next sibling, or parent's next sibling, or grandpa's next sibling...
637 : nsIContent*
638 0 : nsContentIterator::GetNextSibling(nsINode* aNode,
639 : nsTArray<int32_t>* aIndexes)
640 : {
641 0 : if (NS_WARN_IF(!aNode)) {
642 0 : return nullptr;
643 : }
644 :
645 0 : nsINode* parent = aNode->GetParentNode();
646 0 : if (NS_WARN_IF(!parent)) {
647 0 : return nullptr;
648 : }
649 :
650 0 : int32_t indx = 0;
651 :
652 0 : NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
653 : "ContentIterator stack underflow");
654 0 : if (aIndexes && !aIndexes->IsEmpty()) {
655 : // use the last entry on the Indexes array for the current index
656 0 : indx = (*aIndexes)[aIndexes->Length()-1];
657 : } else {
658 0 : indx = mCachedIndex;
659 : }
660 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
661 :
662 : // reverify that the index of the current node hasn't changed.
663 : // not super cheap, but a lot cheaper than IndexOf(), and still O(1).
664 : // ignore result this time - the index may now be out of range.
665 0 : nsIContent* sib = parent->GetChildAt(indx);
666 0 : if (sib != aNode) {
667 : // someone changed our index - find the new index the painful way
668 0 : indx = parent->IndexOf(aNode);
669 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
670 : }
671 :
672 : // indx is now canonically correct
673 0 : if ((sib = parent->GetChildAt(++indx))) {
674 : // update index cache
675 0 : if (aIndexes && !aIndexes->IsEmpty()) {
676 0 : aIndexes->ElementAt(aIndexes->Length()-1) = indx;
677 : } else {
678 0 : mCachedIndex = indx;
679 : }
680 : } else {
681 0 : if (parent != mCommonParent) {
682 0 : if (aIndexes) {
683 : // pop node off the stack, go up one level and return parent or fail.
684 : // Don't leave the index empty, especially if we're
685 : // returning nullptr. This confuses other parts of the code.
686 0 : if (aIndexes->Length() > 1) {
687 0 : aIndexes->RemoveElementAt(aIndexes->Length()-1);
688 : }
689 : }
690 : }
691 :
692 : // ok to leave cache out of date here if parent == mCommonParent?
693 0 : sib = GetNextSibling(parent, aIndexes);
694 : }
695 :
696 0 : return sib;
697 : }
698 :
699 : // Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
700 : nsIContent*
701 0 : nsContentIterator::GetPrevSibling(nsINode* aNode,
702 : nsTArray<int32_t>* aIndexes)
703 : {
704 0 : if (NS_WARN_IF(!aNode)) {
705 0 : return nullptr;
706 : }
707 :
708 0 : nsINode* parent = aNode->GetParentNode();
709 0 : if (NS_WARN_IF(!parent)) {
710 0 : return nullptr;
711 : }
712 :
713 0 : int32_t indx = 0;
714 :
715 0 : NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
716 : "ContentIterator stack underflow");
717 0 : if (aIndexes && !aIndexes->IsEmpty()) {
718 : // use the last entry on the Indexes array for the current index
719 0 : indx = (*aIndexes)[aIndexes->Length()-1];
720 : } else {
721 0 : indx = mCachedIndex;
722 : }
723 :
724 : // reverify that the index of the current node hasn't changed
725 : // ignore result this time - the index may now be out of range.
726 0 : nsIContent* sib = parent->GetChildAt(indx);
727 0 : if (sib != aNode) {
728 : // someone changed our index - find the new index the painful way
729 0 : indx = parent->IndexOf(aNode);
730 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
731 : }
732 :
733 : // indx is now canonically correct
734 0 : if (indx > 0 && (sib = parent->GetChildAt(--indx))) {
735 : // update index cache
736 0 : if (aIndexes && !aIndexes->IsEmpty()) {
737 0 : aIndexes->ElementAt(aIndexes->Length()-1) = indx;
738 : } else {
739 0 : mCachedIndex = indx;
740 : }
741 0 : } else if (parent != mCommonParent) {
742 0 : if (aIndexes && !aIndexes->IsEmpty()) {
743 : // pop node off the stack, go up one level and try again.
744 0 : aIndexes->RemoveElementAt(aIndexes->Length()-1);
745 : }
746 0 : return GetPrevSibling(parent, aIndexes);
747 : }
748 :
749 0 : return sib;
750 : }
751 :
752 : nsINode*
753 0 : nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
754 : {
755 0 : nsINode* node = aNode;
756 :
757 : // if we are a Pre-order iterator, use pre-order
758 0 : if (mPre) {
759 : // if it has children then next node is first child
760 0 : if (node->HasChildren()) {
761 0 : nsIContent* firstChild = node->GetFirstChild();
762 0 : MOZ_ASSERT(firstChild);
763 :
764 : // update cache
765 0 : if (aIndexes) {
766 : // push an entry on the index stack
767 0 : aIndexes->AppendElement(0);
768 : } else {
769 0 : mCachedIndex = 0;
770 : }
771 :
772 0 : return firstChild;
773 : }
774 :
775 : // else next sibling is next
776 0 : return GetNextSibling(node, aIndexes);
777 : }
778 :
779 : // post-order
780 0 : nsINode* parent = node->GetParentNode();
781 0 : if (NS_WARN_IF(!parent)) {
782 0 : MOZ_ASSERT(parent, "The node is the root node but not the last node");
783 0 : mIsDone = true;
784 0 : return node;
785 : }
786 0 : nsIContent* sibling = nullptr;
787 0 : int32_t indx = 0;
788 :
789 : // get the cached index
790 0 : NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
791 : "ContentIterator stack underflow");
792 0 : if (aIndexes && !aIndexes->IsEmpty()) {
793 : // use the last entry on the Indexes array for the current index
794 0 : indx = (*aIndexes)[aIndexes->Length()-1];
795 : } else {
796 0 : indx = mCachedIndex;
797 : }
798 :
799 : // reverify that the index of the current node hasn't changed. not super
800 : // cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
801 : // this time - the index may now be out of range.
802 0 : if (indx >= 0) {
803 0 : sibling = parent->GetChildAt(indx);
804 : }
805 0 : if (sibling != node) {
806 : // someone changed our index - find the new index the painful way
807 0 : indx = parent->IndexOf(node);
808 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
809 : }
810 :
811 : // indx is now canonically correct
812 0 : sibling = parent->GetChildAt(++indx);
813 0 : if (sibling) {
814 : // update cache
815 0 : if (aIndexes && !aIndexes->IsEmpty()) {
816 : // replace an entry on the index stack
817 0 : aIndexes->ElementAt(aIndexes->Length()-1) = indx;
818 : } else {
819 0 : mCachedIndex = indx;
820 : }
821 :
822 : // next node is sibling's "deep left" child
823 0 : return GetDeepFirstChild(sibling, aIndexes);
824 : }
825 :
826 : // else it's the parent, update cache
827 0 : if (aIndexes) {
828 : // Pop an entry off the index stack. Don't leave the index empty,
829 : // especially if we're returning nullptr. This confuses other parts of the
830 : // code.
831 0 : if (aIndexes->Length() > 1) {
832 0 : aIndexes->RemoveElementAt(aIndexes->Length()-1);
833 : }
834 : } else {
835 : // this might be wrong, but we are better off guessing
836 0 : mCachedIndex = 0;
837 : }
838 :
839 0 : return parent;
840 : }
841 :
842 : nsINode*
843 0 : nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
844 : {
845 0 : nsINode* node = aNode;
846 :
847 : // if we are a Pre-order iterator, use pre-order
848 0 : if (mPre) {
849 0 : nsINode* parent = node->GetParentNode();
850 0 : if (NS_WARN_IF(!parent)) {
851 0 : MOZ_ASSERT(parent, "The node is the root node but not the first node");
852 0 : mIsDone = true;
853 0 : return aNode;
854 : }
855 0 : nsIContent* sibling = nullptr;
856 0 : int32_t indx = 0;
857 :
858 : // get the cached index
859 0 : NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
860 : "ContentIterator stack underflow");
861 0 : if (aIndexes && !aIndexes->IsEmpty()) {
862 : // use the last entry on the Indexes array for the current index
863 0 : indx = (*aIndexes)[aIndexes->Length()-1];
864 : } else {
865 0 : indx = mCachedIndex;
866 : }
867 :
868 : // reverify that the index of the current node hasn't changed. not super
869 : // cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
870 : // this time - the index may now be out of range.
871 0 : if (indx >= 0) {
872 0 : sibling = parent->GetChildAt(indx);
873 0 : NS_WARNING_ASSERTION(sibling, "GetChildAt returned null");
874 : }
875 :
876 0 : if (sibling != node) {
877 : // someone changed our index - find the new index the painful way
878 0 : indx = parent->IndexOf(node);
879 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
880 : }
881 :
882 : // indx is now canonically correct
883 0 : if (indx && (sibling = parent->GetChildAt(--indx))) {
884 : // update cache
885 0 : if (aIndexes && !aIndexes->IsEmpty()) {
886 : // replace an entry on the index stack
887 0 : aIndexes->ElementAt(aIndexes->Length()-1) = indx;
888 : } else {
889 0 : mCachedIndex = indx;
890 : }
891 :
892 : // prev node is sibling's "deep right" child
893 0 : return GetDeepLastChild(sibling, aIndexes);
894 : }
895 :
896 : // else it's the parent, update cache
897 0 : if (aIndexes && !aIndexes->IsEmpty()) {
898 : // pop an entry off the index stack
899 0 : aIndexes->RemoveElementAt(aIndexes->Length()-1);
900 : } else {
901 : // this might be wrong, but we are better off guessing
902 0 : mCachedIndex = 0;
903 : }
904 0 : return parent;
905 : }
906 :
907 : // post-order
908 0 : int32_t numChildren = node->GetChildCount();
909 0 : NS_WARNING_ASSERTION(numChildren >= 0, "no children");
910 :
911 : // if it has children then prev node is last child
912 0 : if (numChildren) {
913 0 : nsIContent* lastChild = node->GetLastChild();
914 0 : NS_WARNING_ASSERTION(lastChild, "GetLastChild returned null");
915 0 : numChildren--;
916 :
917 : // update cache
918 0 : if (aIndexes) {
919 : // push an entry on the index stack
920 0 : aIndexes->AppendElement(numChildren);
921 : } else {
922 0 : mCachedIndex = numChildren;
923 : }
924 :
925 0 : return lastChild;
926 : }
927 :
928 : // else prev sibling is previous
929 0 : return GetPrevSibling(node, aIndexes);
930 : }
931 :
932 : /******************************************************
933 : * ContentIterator routines
934 : ******************************************************/
935 :
936 : void
937 0 : nsContentIterator::First()
938 : {
939 0 : if (mFirst) {
940 0 : mozilla::DebugOnly<nsresult> rv = PositionAt(mFirst);
941 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
942 : }
943 :
944 0 : mIsDone = mFirst == nullptr;
945 0 : }
946 :
947 :
948 : void
949 0 : nsContentIterator::Last()
950 : {
951 : // Note that mLast can be nullptr if MakeEmpty() is called in Init() since
952 : // at that time, Init() returns NS_OK.
953 0 : if (!mLast) {
954 0 : MOZ_ASSERT(mIsDone);
955 0 : return;
956 : }
957 :
958 0 : mozilla::DebugOnly<nsresult> rv = PositionAt(mLast);
959 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
960 :
961 0 : mIsDone = mLast == nullptr;
962 : }
963 :
964 :
965 : void
966 0 : nsContentIterator::Next()
967 : {
968 0 : if (mIsDone || NS_WARN_IF(!mCurNode)) {
969 0 : return;
970 : }
971 :
972 0 : if (mCurNode == mLast) {
973 0 : mIsDone = true;
974 0 : return;
975 : }
976 :
977 0 : mCurNode = NextNode(mCurNode, &mIndexes);
978 : }
979 :
980 :
981 : void
982 0 : nsContentIterator::Prev()
983 : {
984 0 : if (NS_WARN_IF(mIsDone) || NS_WARN_IF(!mCurNode)) {
985 0 : return;
986 : }
987 :
988 0 : if (mCurNode == mFirst) {
989 0 : mIsDone = true;
990 0 : return;
991 : }
992 :
993 0 : mCurNode = PrevNode(mCurNode, &mIndexes);
994 : }
995 :
996 :
997 : bool
998 0 : nsContentIterator::IsDone()
999 : {
1000 0 : return mIsDone;
1001 : }
1002 :
1003 :
1004 : // Keeping arrays of indexes for the stack of nodes makes PositionAt
1005 : // interesting...
1006 : nsresult
1007 0 : nsContentIterator::PositionAt(nsINode* aCurNode)
1008 : {
1009 0 : if (NS_WARN_IF(!aCurNode)) {
1010 0 : return NS_ERROR_NULL_POINTER;
1011 : }
1012 :
1013 0 : nsINode* newCurNode = aCurNode;
1014 0 : nsINode* tempNode = mCurNode;
1015 :
1016 0 : mCurNode = aCurNode;
1017 : // take an early out if this doesn't actually change the position
1018 0 : if (mCurNode == tempNode) {
1019 0 : mIsDone = false; // paranoia
1020 0 : return NS_OK;
1021 : }
1022 :
1023 : // Check to see if the node falls within the traversal range.
1024 :
1025 0 : nsINode* firstNode = mFirst;
1026 0 : nsINode* lastNode = mLast;
1027 0 : int32_t firstOffset = 0, lastOffset = 0;
1028 :
1029 0 : if (firstNode && lastNode) {
1030 0 : if (mPre) {
1031 0 : firstNode = NodeToParentOffset(mFirst, &firstOffset);
1032 0 : NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
1033 0 : NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
1034 :
1035 0 : if (lastNode->GetChildCount()) {
1036 0 : lastOffset = 0;
1037 : } else {
1038 0 : lastNode = NodeToParentOffset(mLast, &lastOffset);
1039 0 : NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
1040 0 : NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
1041 0 : ++lastOffset;
1042 : }
1043 : } else {
1044 0 : uint32_t numChildren = firstNode->GetChildCount();
1045 :
1046 0 : if (numChildren) {
1047 0 : firstOffset = numChildren;
1048 0 : NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
1049 : } else {
1050 0 : firstNode = NodeToParentOffset(mFirst, &firstOffset);
1051 0 : NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
1052 0 : NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
1053 : }
1054 :
1055 0 : lastNode = NodeToParentOffset(mLast, &lastOffset);
1056 0 : NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
1057 0 : NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
1058 0 : ++lastOffset;
1059 : }
1060 : }
1061 :
1062 : // The end positions are always in the range even if it has no parent. We
1063 : // need to allow that or 'iter->Init(root)' would assert in Last() or First()
1064 : // for example, bug 327694.
1065 0 : if (mFirst != mCurNode && mLast != mCurNode &&
1066 0 : (NS_WARN_IF(!firstNode) || NS_WARN_IF(!lastNode) ||
1067 0 : NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre,
1068 : firstNode, firstOffset,
1069 : lastNode, lastOffset)))) {
1070 0 : mIsDone = true;
1071 0 : return NS_ERROR_FAILURE;
1072 : }
1073 :
1074 : // We can be at ANY node in the sequence. Need to regenerate the array of
1075 : // indexes back to the root or common parent!
1076 0 : AutoTArray<nsINode*, 8> oldParentStack;
1077 0 : AutoTArray<int32_t, 8> newIndexes;
1078 :
1079 : // Get a list of the parents up to the root, then compare the new node with
1080 : // entries in that array until we find a match (lowest common ancestor). If
1081 : // no match, use IndexOf, take the parent, and repeat. This avoids using
1082 : // IndexOf() N times on possibly large arrays. We still end up doing it a
1083 : // fair bit. It's better to use Clone() if possible.
1084 :
1085 : // we know the depth we're down (though we may not have started at the top).
1086 0 : oldParentStack.SetCapacity(mIndexes.Length() + 1);
1087 :
1088 : // We want to loop mIndexes.Length() + 1 times here, because we want to make
1089 : // sure we include mCommonParent in the oldParentStack, for use in the next
1090 : // for loop, and mIndexes only has entries for nodes from tempNode up through
1091 : // an ancestor of tempNode that's a child of mCommonParent.
1092 0 : for (int32_t i = mIndexes.Length() + 1; i > 0 && tempNode; i--) {
1093 : // Insert at head since we're walking up
1094 0 : oldParentStack.InsertElementAt(0, tempNode);
1095 :
1096 0 : nsINode* parent = tempNode->GetParentNode();
1097 :
1098 0 : if (NS_WARN_IF(!parent)) {
1099 : // this node has no parent, and thus no index
1100 0 : break;
1101 : }
1102 :
1103 0 : if (parent == mCurNode) {
1104 : // The position was moved to a parent of the current position. All we
1105 : // need to do is drop some indexes. Shortcut here.
1106 0 : mIndexes.RemoveElementsAt(mIndexes.Length() - oldParentStack.Length(),
1107 0 : oldParentStack.Length());
1108 0 : mIsDone = false;
1109 0 : return NS_OK;
1110 : }
1111 0 : tempNode = parent;
1112 : }
1113 :
1114 : // Ok. We have the array of old parents. Look for a match.
1115 0 : while (newCurNode) {
1116 0 : nsINode* parent = newCurNode->GetParentNode();
1117 :
1118 0 : if (NS_WARN_IF(!parent)) {
1119 : // this node has no parent, and thus no index
1120 0 : break;
1121 : }
1122 :
1123 0 : int32_t indx = parent->IndexOf(newCurNode);
1124 0 : NS_WARNING_ASSERTION(indx >= 0, "bad indx");
1125 :
1126 : // insert at the head!
1127 0 : newIndexes.InsertElementAt(0, indx);
1128 :
1129 : // look to see if the parent is in the stack
1130 0 : indx = oldParentStack.IndexOf(parent);
1131 0 : if (indx >= 0) {
1132 : // ok, the parent IS on the old stack! Rework things. We want
1133 : // newIndexes to replace all nodes equal to or below the match. Note
1134 : // that index oldParentStack.Length() - 1 is the last node, which is one
1135 : // BELOW the last index in the mIndexes stack. In other words, we want
1136 : // to remove elements starting at index (indx + 1).
1137 0 : int32_t numToDrop = oldParentStack.Length() - (1 + indx);
1138 0 : if (numToDrop > 0) {
1139 0 : mIndexes.RemoveElementsAt(mIndexes.Length() - numToDrop, numToDrop);
1140 : }
1141 0 : mIndexes.AppendElements(newIndexes);
1142 :
1143 0 : break;
1144 : }
1145 0 : newCurNode = parent;
1146 : }
1147 :
1148 : // phew!
1149 :
1150 0 : mIsDone = false;
1151 0 : return NS_OK;
1152 : }
1153 :
1154 : nsINode*
1155 0 : nsContentIterator::GetCurrentNode()
1156 : {
1157 0 : if (mIsDone) {
1158 0 : return nullptr;
1159 : }
1160 :
1161 0 : NS_ASSERTION(mCurNode, "Null current node in an iterator that's not done!");
1162 :
1163 0 : return mCurNode;
1164 : }
1165 :
1166 :
1167 :
1168 :
1169 :
1170 : /*====================================================================================*/
1171 : /*====================================================================================*/
1172 :
1173 :
1174 :
1175 :
1176 :
1177 :
1178 : /******************************************************
1179 : * nsContentSubtreeIterator
1180 : ******************************************************/
1181 :
1182 :
1183 : /*
1184 : * A simple iterator class for traversing the content in "top subtree" order
1185 : */
1186 : class nsContentSubtreeIterator : public nsContentIterator
1187 : {
1188 : public:
1189 0 : nsContentSubtreeIterator() : nsContentIterator(false) {}
1190 :
1191 : NS_DECL_ISUPPORTS_INHERITED
1192 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsContentSubtreeIterator, nsContentIterator)
1193 :
1194 : // nsContentIterator overrides ------------------------------
1195 :
1196 : virtual nsresult Init(nsINode* aRoot) override;
1197 :
1198 : virtual nsresult Init(nsIDOMRange* aRange) override;
1199 :
1200 : virtual void Next() override;
1201 :
1202 : virtual void Prev() override;
1203 :
1204 : virtual nsresult PositionAt(nsINode* aCurNode) override;
1205 :
1206 : // Must override these because we don't do PositionAt
1207 : virtual void First() override;
1208 :
1209 : // Must override these because we don't do PositionAt
1210 : virtual void Last() override;
1211 :
1212 : protected:
1213 0 : virtual ~nsContentSubtreeIterator() {}
1214 :
1215 : // Returns the highest inclusive ancestor of aNode that's in the range
1216 : // (possibly aNode itself). Returns null if aNode is null, or is not itself
1217 : // in the range. A node is in the range if (node, 0) comes strictly after
1218 : // the range endpoint, and (node, node.length) comes strictly before it, so
1219 : // the range's start and end nodes will never be considered "in" it.
1220 : nsIContent* GetTopAncestorInRange(nsINode* aNode);
1221 :
1222 : // no copy's or assigns FIX ME
1223 : nsContentSubtreeIterator(const nsContentSubtreeIterator&);
1224 : nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
1225 :
1226 : virtual void LastRelease() override;
1227 :
1228 : RefPtr<nsRange> mRange;
1229 :
1230 : // these arrays all typically are used and have elements
1231 : AutoTArray<nsIContent*, 8> mEndNodes;
1232 : AutoTArray<int32_t, 8> mEndOffsets;
1233 : };
1234 :
1235 0 : NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
1236 0 : NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
1237 :
1238 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator)
1239 0 : NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
1240 :
1241 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator, nsContentIterator,
1242 : mRange)
1243 :
1244 : void
1245 0 : nsContentSubtreeIterator::LastRelease()
1246 : {
1247 0 : mRange = nullptr;
1248 0 : nsContentIterator::LastRelease();
1249 0 : }
1250 :
1251 : /******************************************************
1252 : * repository cruft
1253 : ******************************************************/
1254 :
1255 : already_AddRefed<nsIContentIterator>
1256 0 : NS_NewContentSubtreeIterator()
1257 : {
1258 0 : nsCOMPtr<nsIContentIterator> iter = new nsContentSubtreeIterator();
1259 0 : return iter.forget();
1260 : }
1261 :
1262 :
1263 :
1264 : /******************************************************
1265 : * Init routines
1266 : ******************************************************/
1267 :
1268 :
1269 : nsresult
1270 0 : nsContentSubtreeIterator::Init(nsINode* aRoot)
1271 : {
1272 0 : return NS_ERROR_NOT_IMPLEMENTED;
1273 : }
1274 :
1275 :
1276 : nsresult
1277 0 : nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
1278 : {
1279 0 : MOZ_ASSERT(aRange);
1280 :
1281 0 : mIsDone = false;
1282 :
1283 0 : mRange = static_cast<nsRange*>(aRange);
1284 :
1285 : // get the start node and offset, convert to nsINode
1286 0 : mCommonParent = mRange->GetCommonAncestor();
1287 0 : nsINode* startContainer = mRange->GetStartContainer();
1288 0 : int32_t startOffset = mRange->StartOffset();
1289 0 : nsINode* endContainer = mRange->GetEndContainer();
1290 0 : int32_t endOffset = mRange->EndOffset();
1291 0 : MOZ_ASSERT(mCommonParent && startContainer && endContainer);
1292 : // Bug 767169
1293 0 : MOZ_ASSERT(uint32_t(startOffset) <= startContainer->Length() &&
1294 : uint32_t(endOffset) <= endContainer->Length());
1295 :
1296 : // short circuit when start node == end node
1297 0 : if (startContainer == endContainer) {
1298 0 : nsINode* child = startContainer->GetFirstChild();
1299 :
1300 0 : if (!child || startOffset == endOffset) {
1301 : // Text node, empty container, or collapsed
1302 0 : MakeEmpty();
1303 0 : return NS_OK;
1304 : }
1305 : }
1306 :
1307 : // cache ancestors
1308 0 : nsContentUtils::GetAncestorsAndOffsets(endContainer->AsDOMNode(), endOffset,
1309 0 : &mEndNodes, &mEndOffsets);
1310 :
1311 0 : nsIContent* firstCandidate = nullptr;
1312 0 : nsIContent* lastCandidate = nullptr;
1313 :
1314 : // find first node in range
1315 0 : int32_t offset = mRange->StartOffset();
1316 :
1317 0 : nsINode* node = nullptr;
1318 0 : if (!startContainer->GetChildCount()) {
1319 : // no children, start at the node itself
1320 0 : node = startContainer;
1321 : } else {
1322 0 : nsIContent* child = startContainer->GetChildAt(offset);
1323 0 : if (!child) {
1324 : // offset after last child
1325 0 : node = startContainer;
1326 : } else {
1327 0 : firstCandidate = child;
1328 : }
1329 : }
1330 :
1331 0 : if (!firstCandidate) {
1332 : // then firstCandidate is next node after node
1333 0 : firstCandidate = GetNextSibling(node);
1334 :
1335 0 : if (!firstCandidate) {
1336 0 : MakeEmpty();
1337 0 : return NS_OK;
1338 : }
1339 : }
1340 :
1341 0 : firstCandidate = GetDeepFirstChild(firstCandidate);
1342 :
1343 : // confirm that this first possible contained node is indeed contained. Else
1344 : // we have a range that does not fully contain any node.
1345 :
1346 : bool nodeBefore, nodeAfter;
1347 0 : MOZ_ALWAYS_SUCCEEDS(
1348 : nsRange::CompareNodeToRange(firstCandidate, mRange, &nodeBefore, &nodeAfter));
1349 :
1350 0 : if (nodeBefore || nodeAfter) {
1351 0 : MakeEmpty();
1352 0 : return NS_OK;
1353 : }
1354 :
1355 : // cool, we have the first node in the range. Now we walk up its ancestors
1356 : // to find the most senior that is still in the range. That's the real first
1357 : // node.
1358 0 : mFirst = GetTopAncestorInRange(firstCandidate);
1359 :
1360 : // now to find the last node
1361 0 : offset = mRange->EndOffset();
1362 0 : int32_t numChildren = endContainer->GetChildCount();
1363 :
1364 0 : if (offset > numChildren) {
1365 : // Can happen for text nodes
1366 0 : offset = numChildren;
1367 : }
1368 0 : if (!offset || !numChildren) {
1369 0 : node = endContainer;
1370 : } else {
1371 0 : lastCandidate = endContainer->GetChildAt(--offset);
1372 0 : NS_ASSERTION(lastCandidate,
1373 : "tree traversal trouble in nsContentSubtreeIterator::Init");
1374 : }
1375 :
1376 0 : if (!lastCandidate) {
1377 : // then lastCandidate is prev node before node
1378 0 : lastCandidate = GetPrevSibling(node);
1379 : }
1380 :
1381 0 : if (!lastCandidate) {
1382 0 : MakeEmpty();
1383 0 : return NS_OK;
1384 : }
1385 :
1386 0 : lastCandidate = GetDeepLastChild(lastCandidate);
1387 :
1388 : // confirm that this last possible contained node is indeed contained. Else
1389 : // we have a range that does not fully contain any node.
1390 :
1391 0 : MOZ_ALWAYS_SUCCEEDS(
1392 : nsRange::CompareNodeToRange(lastCandidate, mRange, &nodeBefore, &nodeAfter));
1393 :
1394 0 : if (nodeBefore || nodeAfter) {
1395 0 : MakeEmpty();
1396 0 : return NS_OK;
1397 : }
1398 :
1399 : // cool, we have the last node in the range. Now we walk up its ancestors to
1400 : // find the most senior that is still in the range. That's the real first
1401 : // node.
1402 0 : mLast = GetTopAncestorInRange(lastCandidate);
1403 :
1404 0 : mCurNode = mFirst;
1405 :
1406 0 : return NS_OK;
1407 : }
1408 :
1409 : /****************************************************************
1410 : * nsContentSubtreeIterator overrides of ContentIterator routines
1411 : ****************************************************************/
1412 :
1413 : // we can't call PositionAt in a subtree iterator...
1414 : void
1415 0 : nsContentSubtreeIterator::First()
1416 : {
1417 0 : mIsDone = mFirst == nullptr;
1418 :
1419 0 : mCurNode = mFirst;
1420 0 : }
1421 :
1422 : // we can't call PositionAt in a subtree iterator...
1423 : void
1424 0 : nsContentSubtreeIterator::Last()
1425 : {
1426 0 : mIsDone = mLast == nullptr;
1427 :
1428 0 : mCurNode = mLast;
1429 0 : }
1430 :
1431 :
1432 : void
1433 0 : nsContentSubtreeIterator::Next()
1434 : {
1435 0 : if (mIsDone || !mCurNode) {
1436 0 : return;
1437 : }
1438 :
1439 0 : if (mCurNode == mLast) {
1440 0 : mIsDone = true;
1441 0 : return;
1442 : }
1443 :
1444 0 : nsINode* nextNode = GetNextSibling(mCurNode);
1445 0 : NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!");
1446 :
1447 0 : int32_t i = mEndNodes.IndexOf(nextNode);
1448 0 : while (i != -1) {
1449 : // as long as we are finding ancestors of the endpoint of the range,
1450 : // dive down into their children
1451 0 : nextNode = nextNode->GetFirstChild();
1452 0 : NS_ASSERTION(nextNode, "Iterator error, expected a child node!");
1453 :
1454 : // should be impossible to get a null pointer. If we went all the way
1455 : // down the child chain to the bottom without finding an interior node,
1456 : // then the previous node should have been the last, which was
1457 : // was tested at top of routine.
1458 0 : i = mEndNodes.IndexOf(nextNode);
1459 : }
1460 :
1461 0 : mCurNode = nextNode;
1462 :
1463 : // This shouldn't be needed, but since our selection code can put us
1464 : // in a situation where mLast is in generated content, we need this
1465 : // to stop the iterator when we've walked past past the last node!
1466 0 : mIsDone = mCurNode == nullptr;
1467 : }
1468 :
1469 :
1470 : void
1471 0 : nsContentSubtreeIterator::Prev()
1472 : {
1473 : // Prev should be optimized to use the mStartNodes, just as Next
1474 : // uses mEndNodes.
1475 0 : if (mIsDone || !mCurNode) {
1476 0 : return;
1477 : }
1478 :
1479 0 : if (mCurNode == mFirst) {
1480 0 : mIsDone = true;
1481 0 : return;
1482 : }
1483 :
1484 : // If any of these function calls return null, so will all succeeding ones,
1485 : // so mCurNode will wind up set to null.
1486 0 : nsINode* prevNode = GetDeepFirstChild(mCurNode);
1487 :
1488 0 : prevNode = PrevNode(prevNode);
1489 :
1490 0 : prevNode = GetDeepLastChild(prevNode);
1491 :
1492 0 : mCurNode = GetTopAncestorInRange(prevNode);
1493 :
1494 : // This shouldn't be needed, but since our selection code can put us
1495 : // in a situation where mFirst is in generated content, we need this
1496 : // to stop the iterator when we've walked past past the first node!
1497 0 : mIsDone = mCurNode == nullptr;
1498 : }
1499 :
1500 :
1501 : nsresult
1502 0 : nsContentSubtreeIterator::PositionAt(nsINode* aCurNode)
1503 : {
1504 0 : NS_ERROR("Not implemented!");
1505 :
1506 0 : return NS_ERROR_NOT_IMPLEMENTED;
1507 : }
1508 :
1509 : /****************************************************************
1510 : * nsContentSubtreeIterator helper routines
1511 : ****************************************************************/
1512 :
1513 : nsIContent*
1514 0 : nsContentSubtreeIterator::GetTopAncestorInRange(nsINode* aNode)
1515 : {
1516 0 : if (!aNode || !aNode->GetParentNode()) {
1517 0 : return nullptr;
1518 : }
1519 :
1520 : // aNode has a parent, so it must be content.
1521 0 : nsIContent* content = aNode->AsContent();
1522 :
1523 : // sanity check: aNode is itself in the range
1524 : bool nodeBefore, nodeAfter;
1525 0 : nsresult res = nsRange::CompareNodeToRange(aNode, mRange,
1526 0 : &nodeBefore, &nodeAfter);
1527 0 : NS_ASSERTION(NS_SUCCEEDED(res) && !nodeBefore && !nodeAfter,
1528 : "aNode isn't in mRange, or something else weird happened");
1529 0 : if (NS_FAILED(res) || nodeBefore || nodeAfter) {
1530 0 : return nullptr;
1531 : }
1532 :
1533 0 : while (content) {
1534 0 : nsIContent* parent = content->GetParent();
1535 : // content always has a parent. If its parent is the root, however --
1536 : // i.e., either it's not content, or it is content but its own parent is
1537 : // null -- then we're finished, since we don't go up to the root.
1538 : //
1539 : // We have to special-case this because CompareNodeToRange treats the root
1540 : // node differently -- see bug 765205.
1541 0 : if (!parent || !parent->GetParentNode()) {
1542 0 : return content;
1543 : }
1544 0 : MOZ_ALWAYS_SUCCEEDS(
1545 : nsRange::CompareNodeToRange(parent, mRange, &nodeBefore, &nodeAfter));
1546 :
1547 0 : if (nodeBefore || nodeAfter) {
1548 0 : return content;
1549 : }
1550 0 : content = parent;
1551 : }
1552 :
1553 0 : MOZ_CRASH("This should only be possible if aNode was null");
1554 : }
|