Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 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 | |
Ben Murdoch | df95704 | 2013-08-06 11:01:27 +0100 | [diff] [blame] | 28 | #include "bindings/v8/ExceptionState.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 29 | #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 | |
| 35 | namespace WebCore { |
| 36 | |
| 37 | NodeIterator::NodePointer::NodePointer() |
| 38 | { |
| 39 | } |
| 40 | |
| 41 | NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b) |
| 42 | : node(n) |
| 43 | , isPointerBeforeNode(b) |
| 44 | { |
| 45 | } |
| 46 | |
| 47 | void NodeIterator::NodePointer::clear() |
| 48 | { |
| 49 | node.clear(); |
| 50 | } |
| 51 | |
| 52 | bool 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 | |
| 64 | bool 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) | 521d96e | 2013-06-19 11:58:24 +0100 | [diff] [blame] | 76 | NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter) |
| 77 | : Traversal(rootNode, whatToShow, filter) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 78 | , 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 | |
| 88 | NodeIterator::~NodeIterator() |
| 89 | { |
| 90 | if (Document* ownerDocument = root()->document()) |
| 91 | ownerDocument->detachNodeIterator(this); |
| 92 | } |
| 93 | |
Ben Murdoch | df95704 | 2013-08-06 11:01:27 +0100 | [diff] [blame] | 94 | PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionState& es) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 95 | { |
| 96 | if (m_detached) { |
Ben Murdoch | df95704 | 2013-08-06 11:01:27 +0100 | [diff] [blame] | 97 | es.throwDOMException(InvalidStateError); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 98 | 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 Murdoch | df95704 | 2013-08-06 11:01:27 +0100 | [diff] [blame] | 123 | PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionState& es) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 124 | { |
| 125 | if (m_detached) { |
Ben Murdoch | df95704 | 2013-08-06 11:01:27 +0100 | [diff] [blame] | 126 | es.throwDOMException(InvalidStateError); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 127 | 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 | |
| 152 | void NodeIterator::detach() |
| 153 | { |
| 154 | if (Document* ownerDocument = root()->document()) |
| 155 | ownerDocument->detachNodeIterator(this); |
| 156 | m_detached = true; |
| 157 | m_referenceNode.node.clear(); |
| 158 | } |
| 159 | |
| 160 | void NodeIterator::nodeWillBeRemoved(Node* removedNode) |
| 161 | { |
| 162 | updateForNodeRemoval(removedNode, m_candidateNode); |
| 163 | updateForNodeRemoval(removedNode, m_referenceNode); |
| 164 | } |
| 165 | |
| 166 | void 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 Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 202 | // Need to move the pointer after the node preceding the |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 203 | // 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 |