• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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