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