Node::document() should return a reference.
[WebKit-https.git] / Source / WebCore / dom / NodeIterator.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no)
4  * Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6  * Copyright (C) 2004, 2008 Apple Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "NodeIterator.h"
27
28 #include "Document.h"
29 #include "ExceptionCode.h"
30 #include "NodeFilter.h"
31 #include "NodeTraversal.h"
32 #include "ScriptState.h"
33
34 namespace WebCore {
35
36 NodeIterator::NodePointer::NodePointer()
37 {
38 }
39
40 NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b)
41     : node(n)
42     , isPointerBeforeNode(b)
43 {
44 }
45
46 void NodeIterator::NodePointer::clear()
47 {
48     node.clear();
49 }
50
51 bool NodeIterator::NodePointer::moveToNext(Node* root)
52 {
53     if (!node)
54         return false;
55     if (isPointerBeforeNode) {
56         isPointerBeforeNode = false;
57         return true;
58     }
59     node = NodeTraversal::next(node.get(), root);
60     return node;
61 }
62
63 bool NodeIterator::NodePointer::moveToPrevious(Node* root)
64 {
65     if (!node)
66         return false;
67     if (!isPointerBeforeNode) {
68         isPointerBeforeNode = true;
69         return true;
70     }
71     node = NodeTraversal::previous(node.get(), root);
72     return node;
73 }
74
75 NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences)
76     : NodeIteratorBase(rootNode, whatToShow, filter, expandEntityReferences)
77     , m_referenceNode(root(), true)
78     , m_detached(false)
79 {
80     root()->document().attachNodeIterator(this);
81 }
82
83 NodeIterator::~NodeIterator()
84 {
85     root()->document().detachNodeIterator(this);
86 }
87
88 PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionCode& ec)
89 {
90     if (m_detached) {
91         ec = INVALID_STATE_ERR;
92         return 0;
93     }
94
95     RefPtr<Node> result;
96
97     m_candidateNode = m_referenceNode;
98     while (m_candidateNode.moveToNext(root())) {
99         // NodeIterators treat the DOM tree as a flat list of nodes.
100         // In other words, FILTER_REJECT does not pass over descendants
101         // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
102         RefPtr<Node> provisionalResult = m_candidateNode.node;
103         bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
104         if (state && state->hadException())
105             break;
106         if (nodeWasAccepted) {
107             m_referenceNode = m_candidateNode;
108             result = provisionalResult.release();
109             break;
110         }
111     }
112
113     m_candidateNode.clear();
114     return result.release();
115 }
116
117 PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionCode& ec)
118 {
119     if (m_detached) {
120         ec = INVALID_STATE_ERR;
121         return 0;
122     }
123
124     RefPtr<Node> result;
125
126     m_candidateNode = m_referenceNode;
127     while (m_candidateNode.moveToPrevious(root())) {
128         // NodeIterators treat the DOM tree as a flat list of nodes.
129         // In other words, FILTER_REJECT does not pass over descendants
130         // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
131         RefPtr<Node> provisionalResult = m_candidateNode.node;
132         bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
133         if (state && state->hadException())
134             break;
135         if (nodeWasAccepted) {
136             m_referenceNode = m_candidateNode;
137             result = provisionalResult.release();
138             break;
139         }
140     }
141
142     m_candidateNode.clear();
143     return result.release();
144 }
145
146 void NodeIterator::detach()
147 {
148     root()->document().detachNodeIterator(this);
149     m_detached = true;
150     m_referenceNode.node.clear();
151 }
152
153 void NodeIterator::nodeWillBeRemoved(Node* removedNode)
154 {
155     updateForNodeRemoval(removedNode, m_candidateNode);
156     updateForNodeRemoval(removedNode, m_referenceNode);
157 }
158
159 void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const
160 {
161     ASSERT(!m_detached);
162     ASSERT(removedNode);
163     ASSERT(&root()->document() == &removedNode->document());
164
165     // Iterator is not affected if the removed node is the reference node and is the root.
166     // or if removed node is not the reference node, or the ancestor of the reference node.
167     if (!removedNode->isDescendantOf(root()))
168         return;
169     bool willRemoveReferenceNode = removedNode == referenceNode.node;
170     bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode);
171     if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor)
172         return;
173
174     if (referenceNode.isPointerBeforeNode) {
175         Node* node = NodeTraversal::next(removedNode, root());
176         if (node) {
177             // Move out from under the node being removed if the new reference
178             // node is a descendant of the node being removed.
179             while (node && node->isDescendantOf(removedNode))
180                 node = NodeTraversal::next(node, root());
181             if (node)
182                 referenceNode.node = node;
183         } else {
184             node = NodeTraversal::previous(removedNode, root());
185             if (node) {
186                 // Move out from under the node being removed if the reference node is
187                 // a descendant of the node being removed.
188                 if (willRemoveReferenceNodeAncestor) {
189                     while (node && node->isDescendantOf(removedNode))
190                         node = NodeTraversal::previous(node, root());
191                 }
192                 if (node) {
193                     // Removing last node.
194                     // Need to move the pointer after the node preceding the 
195                     // new reference node.
196                     referenceNode.node = node;
197                     referenceNode.isPointerBeforeNode = false;
198                 }
199             }
200         }
201     } else {
202         Node* node = NodeTraversal::previous(removedNode, root());
203         if (node) {
204             // Move out from under the node being removed if the reference node is
205             // a descendant of the node being removed.
206             if (willRemoveReferenceNodeAncestor) {
207                 while (node && node->isDescendantOf(removedNode))
208                     node = NodeTraversal::previous(node, root());
209             }
210             if (node)
211                 referenceNode.node = node;
212         } else {
213             // FIXME: This branch doesn't appear to have any LayoutTests.
214             node = NodeTraversal::next(removedNode, root());
215             // Move out from under the node being removed if the reference node is
216             // a descendant of the node being removed.
217             if (willRemoveReferenceNodeAncestor) {
218                 while (node && node->isDescendantOf(removedNode))
219                     node = NodeTraversal::previous(node, root());
220             }
221             if (node)
222                 referenceNode.node = node;
223         }
224     }
225 }
226
227
228 } // namespace WebCore