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 root()->document()->attachNodeIterator(this);
80 }
81
~NodeIterator()82 NodeIterator::~NodeIterator()
83 {
84 root()->document()->detachNodeIterator(this);
85 }
86
nextNode(ScriptState * state,ExceptionCode & ec)87 PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionCode& ec)
88 {
89 if (m_detached) {
90 ec = INVALID_STATE_ERR;
91 return 0;
92 }
93
94 RefPtr<Node> result;
95
96 m_candidateNode = m_referenceNode;
97 while (m_candidateNode.moveToNext(root())) {
98 // NodeIterators treat the DOM tree as a flat list of nodes.
99 // In other words, FILTER_REJECT does not pass over descendants
100 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
101 RefPtr<Node> provisionalResult = m_candidateNode.node;
102 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
103 if (state && state->hadException())
104 break;
105 if (nodeWasAccepted) {
106 m_referenceNode = m_candidateNode;
107 result = provisionalResult.release();
108 break;
109 }
110 }
111
112 m_candidateNode.clear();
113 return result.release();
114 }
115
previousNode(ScriptState * state,ExceptionCode & ec)116 PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionCode& ec)
117 {
118 if (m_detached) {
119 ec = INVALID_STATE_ERR;
120 return 0;
121 }
122
123 RefPtr<Node> result;
124
125 m_candidateNode = m_referenceNode;
126 while (m_candidateNode.moveToPrevious(root())) {
127 // NodeIterators treat the DOM tree as a flat list of nodes.
128 // In other words, FILTER_REJECT does not pass over descendants
129 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
130 RefPtr<Node> provisionalResult = m_candidateNode.node;
131 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
132 if (state && state->hadException())
133 break;
134 if (nodeWasAccepted) {
135 m_referenceNode = m_candidateNode;
136 result = provisionalResult.release();
137 break;
138 }
139 }
140
141 m_candidateNode.clear();
142 return result.release();
143 }
144
detach()145 void NodeIterator::detach()
146 {
147 root()->document()->detachNodeIterator(this);
148 m_detached = true;
149 m_referenceNode.node.clear();
150 }
151
nodeWillBeRemoved(Node * removedNode)152 void NodeIterator::nodeWillBeRemoved(Node* removedNode)
153 {
154 updateForNodeRemoval(removedNode, m_candidateNode);
155 updateForNodeRemoval(removedNode, m_referenceNode);
156 }
157
updateForNodeRemoval(Node * removedNode,NodePointer & referenceNode) const158 void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const
159 {
160 ASSERT(!m_detached);
161 ASSERT(removedNode);
162 ASSERT(root()->document() == removedNode->document());
163
164 // Iterator is not affected if the removed node is the reference node and is the root.
165 // or if removed node is not the reference node, or the ancestor of the reference node.
166 if (!removedNode->isDescendantOf(root()))
167 return;
168 bool willRemoveReferenceNode = removedNode == referenceNode.node;
169 bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode);
170 if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor)
171 return;
172
173 if (referenceNode.isPointerBeforeNode) {
174 Node* node = removedNode->traverseNextNode(root());
175 if (node) {
176 // Move out from under the node being removed if the reference node is
177 // a descendant of the node being removed.
178 if (willRemoveReferenceNodeAncestor) {
179 while (node && node->isDescendantOf(removedNode))
180 node = node->traverseNextNode(root());
181 }
182 if (node)
183 referenceNode.node = node;
184 } else {
185 node = removedNode->traversePreviousNode(root());
186 if (node) {
187 // Move out from under the node being removed if the reference node is
188 // a descendant of the node being removed.
189 if (willRemoveReferenceNodeAncestor) {
190 while (node && node->isDescendantOf(removedNode))
191 node = node->traversePreviousNode(root());
192 }
193 if (node) {
194 // Removing last node.
195 // Need to move the pointer after the node preceding the
196 // new reference node.
197 referenceNode.node = node;
198 referenceNode.isPointerBeforeNode = false;
199 }
200 }
201 }
202 } else {
203 Node* node = removedNode->traversePreviousNode(root());
204 if (node) {
205 // Move out from under the node being removed if the reference node is
206 // a descendant of the node being removed.
207 if (willRemoveReferenceNodeAncestor) {
208 while (node && node->isDescendantOf(removedNode))
209 node = node->traversePreviousNode(root());
210 }
211 if (node)
212 referenceNode.node = node;
213 } else {
214 node = removedNode->traverseNextNode(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 = node->traversePreviousNode(root());
220 }
221 if (node)
222 referenceNode.node = node;
223 }
224 }
225 }
226
227
228 } // namespace WebCore
229