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 "core/dom/NodeIterator.h"
27
28#include "bindings/v8/ExceptionState.h"
29#include "core/dom/Document.h"
30#include "core/dom/ExceptionCode.h"
31#include "core/dom/NodeTraversal.h"
32
33namespace WebCore {
34
35NodeIterator::NodePointer::NodePointer()
36{
37}
38
39NodeIterator::NodePointer::NodePointer(PassRefPtrWillBeRawPtr<Node> n, bool b)
40    : node(n)
41    , isPointerBeforeNode(b)
42{
43}
44
45void NodeIterator::NodePointer::clear()
46{
47    node.clear();
48}
49
50bool NodeIterator::NodePointer::moveToNext(Node* root)
51{
52    if (!node)
53        return false;
54    if (isPointerBeforeNode) {
55        isPointerBeforeNode = false;
56        return true;
57    }
58    node = NodeTraversal::next(*node, root);
59    return node;
60}
61
62bool NodeIterator::NodePointer::moveToPrevious(Node* root)
63{
64    if (!node)
65        return false;
66    if (!isPointerBeforeNode) {
67        isPointerBeforeNode = true;
68        return true;
69    }
70    node = NodeTraversal::previous(*node, root);
71    return node;
72}
73
74NodeIterator::NodeIterator(PassRefPtrWillBeRawPtr<Node> rootNode, unsigned whatToShow, PassRefPtrWillBeRawPtr<NodeFilter> filter)
75    : NodeIteratorBase(rootNode, whatToShow, filter)
76    , m_referenceNode(root(), true)
77{
78    ScriptWrappable::init(this);
79    root()->document().attachNodeIterator(this);
80}
81
82NodeIterator::~NodeIterator()
83{
84#if !ENABLE(OILPAN)
85    root()->document().detachNodeIterator(this);
86#endif
87}
88
89PassRefPtrWillBeRawPtr<Node> NodeIterator::nextNode(ExceptionState& exceptionState)
90{
91    RefPtrWillBeRawPtr<Node> result = nullptr;
92
93    m_candidateNode = m_referenceNode;
94    while (m_candidateNode.moveToNext(root())) {
95        // NodeIterators treat the DOM tree as a flat list of nodes.
96        // In other words, FILTER_REJECT does not pass over descendants
97        // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
98        RefPtrWillBeRawPtr<Node> provisionalResult = m_candidateNode.node;
99        bool nodeWasAccepted = acceptNode(provisionalResult.get(), exceptionState) == NodeFilter::FILTER_ACCEPT;
100        if (exceptionState.hadException())
101            break;
102        if (nodeWasAccepted) {
103            m_referenceNode = m_candidateNode;
104            result = provisionalResult.release();
105            break;
106        }
107    }
108
109    m_candidateNode.clear();
110    return result.release();
111}
112
113PassRefPtrWillBeRawPtr<Node> NodeIterator::previousNode(ExceptionState& exceptionState)
114{
115    RefPtrWillBeRawPtr<Node> result = nullptr;
116
117    m_candidateNode = m_referenceNode;
118    while (m_candidateNode.moveToPrevious(root())) {
119        // NodeIterators treat the DOM tree as a flat list of nodes.
120        // In other words, FILTER_REJECT does not pass over descendants
121        // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP.
122        RefPtrWillBeRawPtr<Node> provisionalResult = m_candidateNode.node;
123        bool nodeWasAccepted = acceptNode(provisionalResult.get(), exceptionState) == NodeFilter::FILTER_ACCEPT;
124        if (exceptionState.hadException())
125            break;
126        if (nodeWasAccepted) {
127            m_referenceNode = m_candidateNode;
128            result = provisionalResult.release();
129            break;
130        }
131    }
132
133    m_candidateNode.clear();
134    return result.release();
135}
136
137void NodeIterator::detach()
138{
139    // This is now a no-op as per the DOM specification.
140}
141
142void NodeIterator::nodeWillBeRemoved(Node& removedNode)
143{
144    updateForNodeRemoval(removedNode, m_candidateNode);
145    updateForNodeRemoval(removedNode, m_referenceNode);
146}
147
148void NodeIterator::updateForNodeRemoval(Node& removedNode, NodePointer& referenceNode) const
149{
150    ASSERT(root()->document() == removedNode.document());
151
152    // Iterator is not affected if the removed node is the reference node and is the root.
153    // or if removed node is not the reference node, or the ancestor of the reference node.
154    if (!removedNode.isDescendantOf(root()))
155        return;
156    bool willRemoveReferenceNode = removedNode == referenceNode.node.get();
157    bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(&removedNode);
158    if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor)
159        return;
160
161    if (referenceNode.isPointerBeforeNode) {
162        Node* node = NodeTraversal::next(removedNode, root());
163        if (node) {
164            // Move out from under the node being removed if the new reference
165            // node is a descendant of the node being removed.
166            while (node && node->isDescendantOf(&removedNode))
167                node = NodeTraversal::next(*node, root());
168            if (node)
169                referenceNode.node = node;
170        } else {
171            node = NodeTraversal::previous(removedNode, root());
172            if (node) {
173                // Move out from under the node being removed if the reference node is
174                // a descendant of the node being removed.
175                if (willRemoveReferenceNodeAncestor) {
176                    while (node && node->isDescendantOf(&removedNode))
177                        node = NodeTraversal::previous(*node, root());
178                }
179                if (node) {
180                    // Removing last node.
181                    // Need to move the pointer after the node preceding the
182                    // new reference node.
183                    referenceNode.node = node;
184                    referenceNode.isPointerBeforeNode = false;
185                }
186            }
187        }
188    } else {
189        Node* node = NodeTraversal::previous(removedNode, root());
190        if (node) {
191            // Move out from under the node being removed if the reference node is
192            // a descendant of the node being removed.
193            if (willRemoveReferenceNodeAncestor) {
194                while (node && node->isDescendantOf(&removedNode))
195                    node = NodeTraversal::previous(*node, root());
196            }
197            if (node)
198                referenceNode.node = node;
199        } else {
200            // FIXME: This branch doesn't appear to have any LayoutTests.
201            node = NodeTraversal::next(removedNode, root());
202            // Move out from under the node being removed if the reference node is
203            // a descendant of the node being removed.
204            if (willRemoveReferenceNodeAncestor) {
205                while (node && node->isDescendantOf(&removedNode))
206                    node = NodeTraversal::previous(*node, root());
207            }
208            if (node)
209                referenceNode.node = node;
210        }
211    }
212}
213
214void NodeIterator::trace(Visitor* visitor)
215{
216    visitor->trace(m_referenceNode);
217    visitor->trace(m_candidateNode);
218    NodeIteratorBase::trace(visitor);
219}
220
221} // namespace WebCore
222