Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2000 Frederik Holljen (frederik.holljen (at) hig.no)
      4  * Copyright (C) 2001 Peter Kelly (pmk (at) post.com)
      5  * Copyright (C) 2006 Samuel Weinig (sam.weinig (at) 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 
     35 NodeIterator::NodePointer::NodePointer()
     36 {
     37 }
     38 
     39 NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b)
     40     : node(n)
     41     , isPointerBeforeNode(b)
     42 {
     43 }
     44 
     45 void NodeIterator::NodePointer::clear()
     46 {
     47     node.clear();
     48 }
     49 
     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 
     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 
     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 
     82 NodeIterator::~NodeIterator()
     83 {
     84     root()->document()->detachNodeIterator(this);
     85 }
     86 
     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 
    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 
    145 void NodeIterator::detach()
    146 {
    147     root()->document()->detachNodeIterator(this);
    148     m_detached = true;
    149     m_referenceNode.node.clear();
    150 }
    151 
    152 void NodeIterator::nodeWillBeRemoved(Node* removedNode)
    153 {
    154     updateForNodeRemoval(removedNode, m_candidateNode);
    155     updateForNodeRemoval(removedNode, m_referenceNode);
    156 }
    157 
    158 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