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