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