blob: 6bb4d9fee81cac9febe2a508fb57058455e022b4 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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 "core/dom/NodeIterator.h"
27
Ben Murdochdf957042013-08-06 11:01:27 +010028#include "bindings/v8/ExceptionState.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010029#include "bindings/v8/ScriptState.h"
30#include "core/dom/Document.h"
31#include "core/dom/ExceptionCode.h"
32#include "core/dom/NodeFilter.h"
33#include "core/dom/NodeTraversal.h"
34
35namespace WebCore {
36
37NodeIterator::NodePointer::NodePointer()
38{
39}
40
41NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b)
42 : node(n)
43 , isPointerBeforeNode(b)
44{
45}
46
47void NodeIterator::NodePointer::clear()
48{
49 node.clear();
50}
51
52bool NodeIterator::NodePointer::moveToNext(Node* root)
53{
54 if (!node)
55 return false;
56 if (isPointerBeforeNode) {
57 isPointerBeforeNode = false;
58 return true;
59 }
60 node = NodeTraversal::next(node.get(), root);
61 return node;
62}
63
64bool NodeIterator::NodePointer::moveToPrevious(Node* root)
65{
66 if (!node)
67 return false;
68 if (!isPointerBeforeNode) {
69 isPointerBeforeNode = true;
70 return true;
71 }
72 node = NodeTraversal::previous(node.get(), root);
73 return node;
74}
75
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +010076NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter)
77 : Traversal(rootNode, whatToShow, filter)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010078 , m_referenceNode(root(), true)
79 , m_detached(false)
80{
81 // 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.
82 ASSERT(root()->document() || root()->nodeType() == Node::DOCUMENT_TYPE_NODE);
83 ScriptWrappable::init(this);
84 if (Document* ownerDocument = root()->document())
85 ownerDocument->attachNodeIterator(this);
86}
87
88NodeIterator::~NodeIterator()
89{
90 if (Document* ownerDocument = root()->document())
91 ownerDocument->detachNodeIterator(this);
92}
93
Ben Murdochdf957042013-08-06 11:01:27 +010094PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionState& es)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010095{
96 if (m_detached) {
Ben Murdochdf957042013-08-06 11:01:27 +010097 es.throwDOMException(InvalidStateError);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010098 return 0;
99 }
100
101 RefPtr<Node> result;
102
103 m_candidateNode = m_referenceNode;
104 while (m_candidateNode.moveToNext(root())) {
105 // NodeIterators treat the DOM tree as a flat list of nodes.
106 // In other words, FILTER_REJECT does not pass over descendants
107 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
108 RefPtr<Node> provisionalResult = m_candidateNode.node;
109 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
110 if (state && state->hadException())
111 break;
112 if (nodeWasAccepted) {
113 m_referenceNode = m_candidateNode;
114 result = provisionalResult.release();
115 break;
116 }
117 }
118
119 m_candidateNode.clear();
120 return result.release();
121}
122
Ben Murdochdf957042013-08-06 11:01:27 +0100123PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionState& es)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100124{
125 if (m_detached) {
Ben Murdochdf957042013-08-06 11:01:27 +0100126 es.throwDOMException(InvalidStateError);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100127 return 0;
128 }
129
130 RefPtr<Node> result;
131
132 m_candidateNode = m_referenceNode;
133 while (m_candidateNode.moveToPrevious(root())) {
134 // NodeIterators treat the DOM tree as a flat list of nodes.
135 // In other words, FILTER_REJECT does not pass over descendants
136 // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
137 RefPtr<Node> provisionalResult = m_candidateNode.node;
138 bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT;
139 if (state && state->hadException())
140 break;
141 if (nodeWasAccepted) {
142 m_referenceNode = m_candidateNode;
143 result = provisionalResult.release();
144 break;
145 }
146 }
147
148 m_candidateNode.clear();
149 return result.release();
150}
151
152void NodeIterator::detach()
153{
154 if (Document* ownerDocument = root()->document())
155 ownerDocument->detachNodeIterator(this);
156 m_detached = true;
157 m_referenceNode.node.clear();
158}
159
160void NodeIterator::nodeWillBeRemoved(Node* removedNode)
161{
162 updateForNodeRemoval(removedNode, m_candidateNode);
163 updateForNodeRemoval(removedNode, m_referenceNode);
164}
165
166void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const
167{
168 ASSERT(!m_detached);
169 ASSERT(removedNode);
170 ASSERT(root()->document());
171 ASSERT(root()->document() == removedNode->document());
172
173 // Iterator is not affected if the removed node is the reference node and is the root.
174 // or if removed node is not the reference node, or the ancestor of the reference node.
175 if (!removedNode->isDescendantOf(root()))
176 return;
177 bool willRemoveReferenceNode = removedNode == referenceNode.node;
178 bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode);
179 if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor)
180 return;
181
182 if (referenceNode.isPointerBeforeNode) {
183 Node* node = NodeTraversal::next(removedNode, root());
184 if (node) {
185 // Move out from under the node being removed if the new reference
186 // node is a descendant of the node being removed.
187 while (node && node->isDescendantOf(removedNode))
188 node = NodeTraversal::next(node, root());
189 if (node)
190 referenceNode.node = node;
191 } else {
192 node = NodeTraversal::previous(removedNode, root());
193 if (node) {
194 // Move out from under the node being removed if the reference node is
195 // a descendant of the node being removed.
196 if (willRemoveReferenceNodeAncestor) {
197 while (node && node->isDescendantOf(removedNode))
198 node = NodeTraversal::previous(node, root());
199 }
200 if (node) {
201 // Removing last node.
Ben Murdoch02772c62013-07-26 10:21:05 +0100202 // Need to move the pointer after the node preceding the
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100203 // new reference node.
204 referenceNode.node = node;
205 referenceNode.isPointerBeforeNode = false;
206 }
207 }
208 }
209 } else {
210 Node* node = NodeTraversal::previous(removedNode, root());
211 if (node) {
212 // Move out from under the node being removed if the reference node is
213 // a descendant of the node being removed.
214 if (willRemoveReferenceNodeAncestor) {
215 while (node && node->isDescendantOf(removedNode))
216 node = NodeTraversal::previous(node, root());
217 }
218 if (node)
219 referenceNode.node = node;
220 } else {
221 // FIXME: This branch doesn't appear to have any LayoutTests.
222 node = NodeTraversal::next(removedNode, root());
223 // Move out from under the node being removed if the reference node is
224 // a descendant of the node being removed.
225 if (willRemoveReferenceNodeAncestor) {
226 while (node && node->isDescendantOf(removedNode))
227 node = NodeTraversal::previous(node, root());
228 }
229 if (node)
230 referenceNode.node = node;
231 }
232 }
233}
234
235
236} // namespace WebCore