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 "ScriptState.h"
32
33 namespace WebCore {
34
NodePointer()35 NodeIterator::NodePointer::NodePointer()
36 {
37 }
38
NodePointer(PassRefPtr<Node> n,bool b)39 NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b)
40 : node(n)
41 , isPointerBeforeNode(b)
42 {
43 }
44
clear()45 void NodeIterator::NodePointer::clear()
46 {
47 node.clear();
48 }
49
moveToNext(Node * root)50 bool NodeIterator::NodePointer::moveToNext(Node* root)
51 {
52 if (!node)
53 return false;
54 if (isPointerBeforeNode) {
55 isPointerBeforeNode = false;
56 return true;
57 }
58 node = node->traverseNextNode(root);
59 return node;
60 }
61
moveToPrevious(Node * root)62 bool NodeIterator::NodePointer::moveToPrevious(Node* root)
63 {
64 if (!node)
65 return false;
66 if (!isPointerBeforeNode) {
67 isPointerBeforeNode = true;
68 return true;
69 }
70 node = node->traversePreviousNode(root);
71 return node;
72 }
73
NodeIterator(PassRefPtr<Node> rootNode,unsigned whatToShow,PassRefPtr<NodeFilter> filter,bool expandEntityReferences)74 NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences)
75 : Traversal(rootNode, whatToShow, filter, expandEntityReferences)
76 , m_referenceNode(root(), true)
77 , m_detached(false)
78 {
79 // Document type nodes may have a null document. But since they can't have children, there is no need to listen for modifications to these.
80 ASSERT(root()->document() || root()->nodeType() == Node::DOCUMENT_TYPE_NODE);
81 if (Document* ownerDocument = root()->document())
82 ownerDocument->attachNodeIterator(this);
83 }
84
~NodeIterator()85 NodeIterator::~NodeIterator()
86 {
87 if (Document* ownerDocument = root()->document())
88 ownerDocument->detachNodeIterator(this);
89 }
90
nextNode(ScriptState * state,ExceptionCode & ec)91 PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionCode& ec)
92 {
93 if (m_detached) {
94 ec = INVALID_STATE_ERR;
95 return 0;
96 }
97
98 RefPtr<Node> result;
99
100 m_candidateNode = m_referenceNode;
101 while (m_candidateNode.moveToNext(root())) {
102 // NodeIterators treat the DOM tree as a flat list of nodes.
103 // In other words, FILTER_REJECT does not pass over descendants
104 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
105 RefPtr<Node> provisionalResult = m_candidateNode.node;
106 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
107 if (state && state->hadException())
108 break;
109 if (nodeWasAccepted) {
110 m_referenceNode = m_candidateNode;
111 result = provisionalResult.release();
112 break;
113 }
114 }
115
116 m_candidateNode.clear();
117 return result.release();
118 }
119
previousNode(ScriptState * state,ExceptionCode & ec)120 PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionCode& ec)
121 {
122 if (m_detached) {
123 ec = INVALID_STATE_ERR;
124 return 0;
125 }
126
127 RefPtr<Node> result;
128
129 m_candidateNode = m_referenceNode;
130 while (m_candidateNode.moveToPrevious(root())) {
131 // NodeIterators treat the DOM tree as a flat list of nodes.
132 // In other words, FILTER_REJECT does not pass over descendants
133 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
134 RefPtr<Node> provisionalResult = m_candidateNode.node;
135 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
136 if (state && state->hadException())
137 break;
138 if (nodeWasAccepted) {
139 m_referenceNode = m_candidateNode;
140 result = provisionalResult.release();
141 break;
142 }
143 }
144
145 m_candidateNode.clear();
146 return result.release();
147 }
148
detach()149 void NodeIterator::detach()
150 {
151 if (Document* ownerDocument = root()->document())
152 ownerDocument->detachNodeIterator(this);
153 m_detached = true;
154 m_referenceNode.node.clear();
155 }
156
nodeWillBeRemoved(Node * removedNode)157 void NodeIterator::nodeWillBeRemoved(Node* removedNode)
158 {
159 updateForNodeRemoval(removedNode, m_candidateNode);
160 updateForNodeRemoval(removedNode, m_referenceNode);
161 }
162
updateForNodeRemoval(Node * removedNode,NodePointer & referenceNode) const163 void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const
164 {
165 ASSERT(!m_detached);
166 ASSERT(removedNode);
167 ASSERT(root()->document());
168 ASSERT(root()->document() == removedNode->document());
169
170 // Iterator is not affected if the removed node is the reference node and is the root.
171 // or if removed node is not the reference node, or the ancestor of the reference node.
172 if (!removedNode->isDescendantOf(root()))
173 return;
174 bool willRemoveReferenceNode = removedNode == referenceNode.node;
175 bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode);
176 if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor)
177 return;
178
179 if (referenceNode.isPointerBeforeNode) {
180 Node* node = removedNode->traverseNextNode(root());
181 if (node) {
182 // Move out from under the node being removed if the reference node is
183 // a descendant of the node being removed.
184 if (willRemoveReferenceNodeAncestor) {
185 while (node && node->isDescendantOf(removedNode))
186 node = node->traverseNextNode(root());
187 }
188 if (node)
189 referenceNode.node = node;
190 } else {
191 node = removedNode->traversePreviousNode(root());
192 if (node) {
193 // Move out from under the node being removed if the reference node is
194 // a descendant of the node being removed.
195 if (willRemoveReferenceNodeAncestor) {
196 while (node && node->isDescendantOf(removedNode))
197 node = node->traversePreviousNode(root());
198 }
199 if (node) {
200 // Removing last node.
201 // Need to move the pointer after the node preceding the
202 // new reference node.
203 referenceNode.node = node;
204 referenceNode.isPointerBeforeNode = false;
205 }
206 }
207 }
208 } else {
209 Node* node = removedNode->traversePreviousNode(root());
210 if (node) {
211 // Move out from under the node being removed if the reference node is
212 // a descendant of the node being removed.
213 if (willRemoveReferenceNodeAncestor) {
214 while (node && node->isDescendantOf(removedNode))
215 node = node->traversePreviousNode(root());
216 }
217 if (node)
218 referenceNode.node = node;
219 } else {
220 node = removedNode->traverseNextNode(root());
221 // Move out from under the node being removed if the reference node is
222 // a descendant of the node being removed.
223 if (willRemoveReferenceNodeAncestor) {
224 while (node && node->isDescendantOf(removedNode))
225 node = node->traversePreviousNode(root());
226 }
227 if (node)
228 referenceNode.node = node;
229 }
230 }
231 }
232
233
234 } // namespace WebCore
235