blob: f02eb7f8112349de45de79c7e0e709bba834eb3c [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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 "core/dom/Document.h"
26#include "core/dom/NodeTraversal.h"
27#include "core/html/HTMLFrameOwnerElement.h"
28#include "core/inspector/InspectorInstrumentation.h"
Ben Murdoch591b9582013-07-10 11:41:44 +010029#include "wtf/Assertions.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010030
31namespace WebCore {
32
33class ChildNodeInsertionNotifier {
34public:
35 explicit ChildNodeInsertionNotifier(ContainerNode* insertionPoint)
36 : m_insertionPoint(insertionPoint)
37 {
38 }
39
40 void notify(Node*);
41
42private:
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
52class ChildNodeRemovalNotifier {
53public:
54 explicit ChildNodeRemovalNotifier(ContainerNode* insertionPoint)
55 : m_insertionPoint(insertionPoint)
56 {
57 }
58
59 void notify(Node*);
60
61private:
62 void notifyDescendantRemovedFromDocument(ContainerNode*);
63 void notifyDescendantRemovedFromTree(ContainerNode*);
64 void notifyNodeRemovedFromDocument(Node*);
65 void notifyNodeRemovedFromTree(ContainerNode*);
66
67 ContainerNode* m_insertionPoint;
68};
69
70namespace 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'
79template<class GenericNode, class GenericNodeContainer>
80inline 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
107template<class GenericNode, class GenericNodeContainer>
108inline 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
123namespace 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
193inline 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
203inline 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
213inline 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
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100227 for (size_t i = 0; i < m_postInsertionNotificationTargets.size(); ++i)
228 m_postInsertionNotificationTargets[i]->didNotifySubtreeInsertions(m_insertionPoint);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100229}
230
231
232inline 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
241inline 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
250inline 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
259class ChildFrameDisconnector {
260public:
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
273private:
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
283unsigned assertConnectedSubrameCountIsConsistent(Node*);
284#endif
285
286inline 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
302inline 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
317inline 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