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/html/HTMLFrameOwnerElement.h"
     27 #include "core/inspector/InspectorInstrumentation.h"
     28 #include "wtf/Assertions.h"
     29 
     30 namespace WebCore {
     31 
     32 class ChildNodeInsertionNotifier {
     33 public:
     34     explicit ChildNodeInsertionNotifier(ContainerNode& insertionPoint)
     35         : m_insertionPoint(insertionPoint)
     36     {
     37     }
     38 
     39     void notify(Node&);
     40 
     41 private:
     42     void notifyDescendantInsertedIntoDocument(ContainerNode&);
     43     void notifyDescendantInsertedIntoTree(ContainerNode&);
     44     void notifyNodeInsertedIntoDocument(Node&);
     45     void notifyNodeInsertedIntoTree(ContainerNode&);
     46 
     47     ContainerNode& m_insertionPoint;
     48     Vector< RefPtr<Node> > m_postInsertionNotificationTargets;
     49 };
     50 
     51 class ChildNodeRemovalNotifier {
     52 public:
     53     explicit ChildNodeRemovalNotifier(ContainerNode& insertionPoint)
     54         : m_insertionPoint(insertionPoint)
     55     {
     56     }
     57 
     58     void notify(Node&);
     59 
     60 private:
     61     void notifyDescendantRemovedFromDocument(ContainerNode&);
     62     void notifyDescendantRemovedFromTree(ContainerNode&);
     63     void notifyNodeRemovedFromDocument(Node&);
     64     void notifyNodeRemovedFromTree(ContainerNode&);
     65 
     66     ContainerNode& m_insertionPoint;
     67 };
     68 
     69 namespace Private {
     70 
     71     template<class GenericNode, class GenericNodeContainer>
     72     void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer&);
     73 
     74 }
     75 
     76 // Helper functions for TreeShared-derived classes, which have a 'Node' style interface
     77 // This applies to 'ContainerNode' and 'SVGElementInstance'
     78 template<class GenericNode, class GenericNodeContainer>
     79 inline void removeDetachedChildrenInContainer(GenericNodeContainer& container)
     80 {
     81     // List of nodes to be deleted.
     82     GenericNode* head = 0;
     83     GenericNode* tail = 0;
     84 
     85     Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container);
     86 
     87     GenericNode* n;
     88     GenericNode* next;
     89     while ((n = head) != 0) {
     90         ASSERT_WITH_SECURITY_IMPLICATION(n->m_deletionHasBegun);
     91 
     92         next = n->nextSibling();
     93         n->setNextSibling(0);
     94 
     95         head = next;
     96         if (next == 0)
     97             tail = 0;
     98 
     99         if (n->hasChildNodes())
    100             Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, static_cast<GenericNodeContainer&>(*n));
    101 
    102         delete n;
    103     }
    104 }
    105 
    106 template<class GenericNode, class GenericNodeContainer>
    107 inline void appendChildToContainer(GenericNode& child, GenericNodeContainer& container)
    108 {
    109     child.setParentOrShadowHostNode(&container);
    110 
    111     GenericNode* lastChild = container.lastChild();
    112     if (lastChild) {
    113         child.setPreviousSibling(lastChild);
    114         lastChild->setNextSibling(&child);
    115     } else {
    116         container.setFirstChild(&child);
    117     }
    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; n = next) {
    161             ASSERT_WITH_SECURITY_IMPLICATION(!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 #if SECURITY_ASSERT_ENABLED
    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);
    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         Node* targetNode = m_postInsertionNotificationTargets[i].get();
    229         if (targetNode->inDocument())
    230             targetNode->didNotifySubtreeInsertionsToDocument();
    231     }
    232 }
    233 
    234 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument(Node& node)
    235 {
    236     ASSERT(m_insertionPoint.inDocument());
    237     node.removedFrom(&m_insertionPoint);
    238 
    239     if (node.isContainerNode())
    240         notifyDescendantRemovedFromDocument(toContainerNode(node));
    241 }
    242 
    243 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromTree(ContainerNode& node)
    244 {
    245     NoEventDispatchAssertion assertNoEventDispatch;
    246     ASSERT(!m_insertionPoint.inDocument());
    247 
    248     node.removedFrom(&m_insertionPoint);
    249     notifyDescendantRemovedFromTree(node);
    250 }
    251 
    252 inline void ChildNodeRemovalNotifier::notify(Node& node)
    253 {
    254     if (node.inDocument()) {
    255         notifyNodeRemovedFromDocument(node);
    256         node.document().notifyRemovePendingSheetIfNeeded();
    257     } else if (node.isContainerNode())
    258         notifyNodeRemovedFromTree(toContainerNode(node));
    259 }
    260 
    261 class ChildFrameDisconnector {
    262 public:
    263     enum DisconnectPolicy {
    264         RootAndDescendants,
    265         DescendantsOnly
    266     };
    267 
    268     explicit ChildFrameDisconnector(Node& root)
    269         : m_root(root)
    270     {
    271     }
    272 
    273     void disconnect(DisconnectPolicy = RootAndDescendants);
    274 
    275 private:
    276     void collectFrameOwners(Node& root);
    277     void collectFrameOwners(ElementShadow&);
    278     void disconnectCollectedFrameOwners();
    279 
    280     Vector<RefPtr<HTMLFrameOwnerElement>, 10> m_frameOwners;
    281     Node& m_root;
    282 };
    283 
    284 #ifndef NDEBUG
    285 unsigned assertConnectedSubrameCountIsConsistent(Node&);
    286 #endif
    287 
    288 inline void ChildFrameDisconnector::collectFrameOwners(Node& root)
    289 {
    290     if (!root.connectedSubframeCount())
    291         return;
    292 
    293     if (root.isHTMLElement() && root.isFrameOwnerElement())
    294         m_frameOwners.append(&toHTMLFrameOwnerElement(root));
    295 
    296     for (Node* child = root.firstChild(); child; child = child->nextSibling())
    297         collectFrameOwners(*child);
    298 
    299     ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0;
    300     if (shadow)
    301         collectFrameOwners(*shadow);
    302 }
    303 
    304 inline void ChildFrameDisconnector::disconnectCollectedFrameOwners()
    305 {
    306     // Must disable frame loading in the subtree so an unload handler cannot
    307     // insert more frames and create loaded frames in detached subtrees.
    308     SubframeLoadingDisabler disabler(m_root);
    309 
    310     for (unsigned i = 0; i < m_frameOwners.size(); ++i) {
    311         HTMLFrameOwnerElement* owner = m_frameOwners[i].get();
    312         // Don't need to traverse up the tree for the first owner since no
    313         // script could have moved it.
    314         if (!i || m_root.containsIncludingShadowDOM(owner))
    315             owner->disconnectContentFrame();
    316     }
    317 }
    318 
    319 inline void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
    320 {
    321 #ifndef NDEBUG
    322     assertConnectedSubrameCountIsConsistent(m_root);
    323 #endif
    324 
    325     if (!m_root.connectedSubframeCount())
    326         return;
    327 
    328     if (policy == RootAndDescendants)
    329         collectFrameOwners(m_root);
    330     else {
    331         for (Node* child = m_root.firstChild(); child; child = child->nextSibling())
    332             collectFrameOwners(*child);
    333     }
    334 
    335     disconnectCollectedFrameOwners();
    336 }
    337 
    338 } // namespace WebCore
    339 
    340 #endif // ContainerNodeAlgorithms_h
    341