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 "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:
ChildNodeInsertionNotifier(ContainerNode & insertionPoint)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:
ChildNodeRemovalNotifier(ContainerNode & insertionPoint)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>
removeDetachedChildrenInContainer(GenericNodeContainer & container)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>
appendChildToContainer(GenericNode & child,GenericNodeContainer & container)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 {
dispatchNodeRemovalDispatcher127 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