Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2007 Apple Inc. All rights reserved.
      3  *           (C) 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #ifndef ContainerNodeAlgorithms_h
     23 #define ContainerNodeAlgorithms_h
     24 
     25 #include "core/dom/Document.h"
     26 #include "core/dom/NodeTraversal.h"
     27 #include "core/html/HTMLFrameOwnerElement.h"
     28 #include "core/inspector/InspectorInstrumentation.h"
     29 #include "wtf/Assertions.h"
     30 
     31 namespace WebCore {
     32 
     33 class ChildNodeInsertionNotifier {
     34 public:
     35     explicit ChildNodeInsertionNotifier(ContainerNode* insertionPoint)
     36         : m_insertionPoint(insertionPoint)
     37     {
     38     }
     39 
     40     void notify(Node*);
     41 
     42 private:
     43     void notifyDescendantInsertedIntoDocument(ContainerNode*);
     44     void notifyDescendantInsertedIntoTree(ContainerNode*);
     45     void notifyNodeInsertedIntoDocument(Node*);
     46     void notifyNodeInsertedIntoTree(ContainerNode*);
     47 
     48     ContainerNode* m_insertionPoint;
     49     Vector< RefPtr<Node> > m_postInsertionNotificationTargets;
     50 };
     51 
     52 class ChildNodeRemovalNotifier {
     53 public:
     54     explicit ChildNodeRemovalNotifier(ContainerNode* insertionPoint)
     55         : m_insertionPoint(insertionPoint)
     56     {
     57     }
     58 
     59     void notify(Node*);
     60 
     61 private:
     62     void notifyDescendantRemovedFromDocument(ContainerNode*);
     63     void notifyDescendantRemovedFromTree(ContainerNode*);
     64     void notifyNodeRemovedFromDocument(Node*);
     65     void notifyNodeRemovedFromTree(ContainerNode*);
     66 
     67     ContainerNode* m_insertionPoint;
     68 };
     69 
     70 namespace Private {
     71 
     72     template<class GenericNode, class GenericNodeContainer>
     73     void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer*);
     74 
     75 }
     76 
     77 // Helper functions for TreeShared-derived classes, which have a 'Node' style interface
     78 // This applies to 'ContainerNode' and 'SVGElementInstance'
     79 template<class GenericNode, class GenericNodeContainer>
     80 inline void removeDetachedChildrenInContainer(GenericNodeContainer* container)
     81 {
     82     // List of nodes to be deleted.
     83     GenericNode* head = 0;
     84     GenericNode* tail = 0;
     85 
     86     Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container);
     87 
     88     GenericNode* n;
     89     GenericNode* next;
     90     while ((n = head) != 0) {
     91         ASSERT(n->m_deletionHasBegun);
     92 
     93         next = n->nextSibling();
     94         n->setNextSibling(0);
     95 
     96         head = next;
     97         if (next == 0)
     98             tail = 0;
     99 
    100         if (n->hasChildNodes())
    101             Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, static_cast<GenericNodeContainer*>(n));
    102 
    103         delete n;
    104     }
    105 }
    106 
    107 template<class GenericNode, class GenericNodeContainer>
    108 inline void appendChildToContainer(GenericNode* child, GenericNodeContainer* container)
    109 {
    110     child->setParentOrShadowHostNode(container);
    111 
    112     GenericNode* lastChild = container->lastChild();
    113     if (lastChild) {
    114         child->setPreviousSibling(lastChild);
    115         lastChild->setNextSibling(child);
    116     } else
    117         container->setFirstChild(child);
    118 
    119     container->setLastChild(child);
    120 }
    121 
    122 // Helper methods for removeDetachedChildrenInContainer, hidden from WebCore namespace
    123 namespace Private {
    124 
    125     template<class GenericNode, class GenericNodeContainer, bool dispatchRemovalNotification>
    126     struct NodeRemovalDispatcher {
    127         static void dispatch(GenericNode*, GenericNodeContainer*)
    128         {
    129             // no-op, by default
    130         }
    131     };
    132 
    133     template<class GenericNode, class GenericNodeContainer>
    134     struct NodeRemovalDispatcher<GenericNode, GenericNodeContainer, true> {
    135         static void dispatch(GenericNode* node, GenericNodeContainer* container)
    136         {
    137             // Clean up any TreeScope to a removed tree.
    138             if (Document* containerDocument = container->ownerDocument())
    139                 containerDocument->adoptIfNeeded(node);
    140             if (node->inDocument())
    141                 ChildNodeRemovalNotifier(container).notify(node);
    142         }
    143     };
    144 
    145     template<class GenericNode>
    146     struct ShouldDispatchRemovalNotification {
    147         static const bool value = false;
    148     };
    149 
    150     template<>
    151     struct ShouldDispatchRemovalNotification<Node> {
    152         static const bool value = true;
    153     };
    154 
    155     template<class GenericNode, class GenericNodeContainer>
    156     void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer* container)
    157     {
    158         // We have to tell all children that their parent has died.
    159         GenericNode* next = 0;
    160         for (GenericNode* n = container->firstChild(); n != 0; n = next) {
    161             ASSERT(!n->m_deletionHasBegun);
    162 
    163             next = n->nextSibling();
    164             n->setNextSibling(0);
    165             n->setParentOrShadowHostNode(0);
    166             container->setFirstChild(next);
    167             if (next)
    168                 next->setPreviousSibling(0);
    169 
    170             if (!n->refCount()) {
    171 #ifndef NDEBUG
    172                 n->m_deletionHasBegun = true;
    173 #endif
    174                 // Add the node to the list of nodes to be deleted.
    175                 // Reuse the nextSibling pointer for this purpose.
    176                 if (tail)
    177                     tail->setNextSibling(n);
    178                 else
    179                     head = n;
    180 
    181                 tail = n;
    182             } else {
    183                 RefPtr<GenericNode> protect(n); // removedFromDocument may remove remove all references to this node.
    184                 NodeRemovalDispatcher<GenericNode, GenericNodeContainer, ShouldDispatchRemovalNotification<GenericNode>::value>::dispatch(n, container);
    185             }
    186         }
    187 
    188         container->setLastChild(0);
    189     }
    190 
    191 } // namespace Private
    192 
    193 inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument(Node* node)
    194 {
    195     ASSERT(m_insertionPoint->inDocument());
    196     RefPtr<Node> protect(node);
    197     if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node->insertedInto(m_insertionPoint))
    198         m_postInsertionNotificationTargets.append(node);
    199     if (node->isContainerNode())
    200         notifyDescendantInsertedIntoDocument(toContainerNode(node));
    201 }
    202 
    203 inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree(ContainerNode* node)
    204 {
    205     NoEventDispatchAssertion assertNoEventDispatch;
    206     ASSERT(!m_insertionPoint->inDocument());
    207 
    208     if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node->insertedInto(m_insertionPoint))
    209         m_postInsertionNotificationTargets.append(node);
    210     notifyDescendantInsertedIntoTree(node);
    211 }
    212 
    213 inline void ChildNodeInsertionNotifier::notify(Node* node)
    214 {
    215     ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
    216 
    217     InspectorInstrumentation::didInsertDOMNode(node->document(), node);
    218 
    219     RefPtr<Document> protectDocument(node->document());
    220     RefPtr<Node> protectNode(node);
    221 
    222     if (m_insertionPoint->inDocument())
    223         notifyNodeInsertedIntoDocument(node);
    224     else if (node->isContainerNode())
    225         notifyNodeInsertedIntoTree(toContainerNode(node));
    226 
    227     for (size_t i = 0; i < m_postInsertionNotificationTargets.size(); ++i)
    228         m_postInsertionNotificationTargets[i]->didNotifySubtreeInsertions(m_insertionPoint);
    229 }
    230 
    231 
    232 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument(Node* node)
    233 {
    234     ASSERT(m_insertionPoint->inDocument());
    235     node->removedFrom(m_insertionPoint);
    236 
    237     if (node->isContainerNode())
    238         notifyDescendantRemovedFromDocument(toContainerNode(node));
    239 }
    240 
    241 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromTree(ContainerNode* node)
    242 {
    243     NoEventDispatchAssertion assertNoEventDispatch;
    244     ASSERT(!m_insertionPoint->inDocument());
    245 
    246     node->removedFrom(m_insertionPoint);
    247     notifyDescendantRemovedFromTree(node);
    248 }
    249 
    250 inline void ChildNodeRemovalNotifier::notify(Node* node)
    251 {
    252     if (node->inDocument()) {
    253         notifyNodeRemovedFromDocument(node);
    254         node->document()->notifyRemovePendingSheetIfNeeded();
    255     } else if (node->isContainerNode())
    256         notifyNodeRemovedFromTree(toContainerNode(node));
    257 }
    258 
    259 class ChildFrameDisconnector {
    260 public:
    261     enum DisconnectPolicy {
    262         RootAndDescendants,
    263         DescendantsOnly
    264     };
    265 
    266     explicit ChildFrameDisconnector(Node* root)
    267         : m_root(root)
    268     {
    269     }
    270 
    271     void disconnect(DisconnectPolicy = RootAndDescendants);
    272 
    273 private:
    274     void collectFrameOwners(Node* root);
    275     void collectFrameOwners(ElementShadow*);
    276     void disconnectCollectedFrameOwners();
    277 
    278     Vector<RefPtr<HTMLFrameOwnerElement>, 10> m_frameOwners;
    279     Node* m_root;
    280 };
    281 
    282 #ifndef NDEBUG
    283 unsigned assertConnectedSubrameCountIsConsistent(Node*);
    284 #endif
    285 
    286 inline void ChildFrameDisconnector::collectFrameOwners(Node* root)
    287 {
    288     if (!root->connectedSubframeCount())
    289         return;
    290 
    291     if (root->isHTMLElement() && root->isFrameOwnerElement())
    292         m_frameOwners.append(toFrameOwnerElement(root));
    293 
    294     for (Node* child = root->firstChild(); child; child = child->nextSibling())
    295         collectFrameOwners(child);
    296 
    297     ElementShadow* shadow = root->isElementNode() ? toElement(root)->shadow() : 0;
    298     if (shadow)
    299         collectFrameOwners(shadow);
    300 }
    301 
    302 inline void ChildFrameDisconnector::disconnectCollectedFrameOwners()
    303 {
    304     // Must disable frame loading in the subtree so an unload handler cannot
    305     // insert more frames and create loaded frames in detached subtrees.
    306     SubframeLoadingDisabler disabler(m_root);
    307 
    308     for (unsigned i = 0; i < m_frameOwners.size(); ++i) {
    309         HTMLFrameOwnerElement* owner = m_frameOwners[i].get();
    310         // Don't need to traverse up the tree for the first owner since no
    311         // script could have moved it.
    312         if (!i || m_root->containsIncludingShadowDOM(owner))
    313             owner->disconnectContentFrame();
    314     }
    315 }
    316 
    317 inline void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
    318 {
    319 #ifndef NDEBUG
    320     assertConnectedSubrameCountIsConsistent(m_root);
    321 #endif
    322 
    323     if (!m_root->connectedSubframeCount())
    324         return;
    325 
    326     if (policy == RootAndDescendants)
    327         collectFrameOwners(m_root);
    328     else {
    329         for (Node* child = m_root->firstChild(); child; child = child->nextSibling())
    330             collectFrameOwners(child);
    331     }
    332 
    333     disconnectCollectedFrameOwners();
    334 }
    335 
    336 } // namespace WebCore
    337 
    338 #endif // ContainerNodeAlgorithms_h
    339