Replace 0 and NULL with nullptr in WebCore/dom.
[WebKit-https.git] / Source / WebCore / dom / ContainerNodeAlgorithms.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved.
6  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
7  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) 2012 Google Inc. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "ContainerNodeAlgorithms.h"
28
29
30 namespace WebCore {
31
32 void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode&, NodeVector& postInsertionNotificationTargets);
33 void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node&, NodeVector& postInsertionNotificationTargets);
34 void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode&);
35 void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node&);
36
37 static void notifyDescendantInsertedIntoDocument(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
38 {
39     ChildNodesLazySnapshot snapshot(node);
40     while (RefPtr<Node> child = snapshot.nextNode()) {
41         // If we have been removed from the document during this loop, then
42         // we don't want to tell the rest of our children that they've been
43         // inserted into the document because they haven't.
44         if (node.inDocument() && child->parentNode() == &node)
45             notifyNodeInsertedIntoDocument(insertionPoint, *child, postInsertionNotificationTargets);
46     }
47
48     if (!is<Element>(node))
49         return;
50
51     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
52         if (node.inDocument() && root->host() == &node)
53             notifyNodeInsertedIntoDocument(insertionPoint, *root, postInsertionNotificationTargets);
54     }
55 }
56
57 static void notifyDescendantInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
58 {
59     for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
60         if (is<ContainerNode>(*child))
61             notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(*child), postInsertionNotificationTargets);
62     }
63
64     if (ShadowRoot* root = node.shadowRoot())
65         notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets);
66 }
67
68 void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
69 {
70     ASSERT(insertionPoint.inDocument());
71     if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
72         postInsertionNotificationTargets.append(node);
73     if (is<ContainerNode>(node))
74         notifyDescendantInsertedIntoDocument(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets);
75 }
76
77 void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
78 {
79     NoEventDispatchAssertion assertNoEventDispatch;
80     ASSERT(!insertionPoint.inDocument());
81
82     if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
83         postInsertionNotificationTargets.append(node);
84     notifyDescendantInsertedIntoTree(insertionPoint, node, postInsertionNotificationTargets);
85 }
86
87 void notifyChildNodeInserted(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
88 {
89     ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
90
91     InspectorInstrumentation::didInsertDOMNode(node.document(), node);
92
93     Ref<Document> protectDocument(node.document());
94     Ref<Node> protectNode(node);
95
96     if (insertionPoint.inDocument())
97         notifyNodeInsertedIntoDocument(insertionPoint, node, postInsertionNotificationTargets);
98     else if (is<ContainerNode>(node))
99         notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets);
100 }
101
102 void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node& node)
103 {
104     ASSERT(insertionPoint.inDocument());
105     node.removedFrom(insertionPoint);
106
107     if (!is<ContainerNode>(node))
108         return;
109     ChildNodesLazySnapshot snapshot(node);
110     while (RefPtr<Node> child = snapshot.nextNode()) {
111         // If we have been added to the document during this loop, then we
112         // don't want to tell the rest of our children that they've been
113         // removed from the document because they haven't.
114         if (!node.inDocument() && child->parentNode() == &node)
115             notifyNodeRemovedFromDocument(insertionPoint, *child.get());
116     }
117
118     if (!is<Element>(node))
119         return;
120
121     if (node.document().cssTarget() == &node)
122         node.document().setCSSTarget(nullptr);
123
124     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
125         if (!node.inDocument() && root->host() == &node)
126             notifyNodeRemovedFromDocument(insertionPoint, *root.get());
127     }
128 }
129
130 void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode& node)
131 {
132     NoEventDispatchAssertion assertNoEventDispatch;
133     ASSERT(!insertionPoint.inDocument());
134
135     node.removedFrom(insertionPoint);
136
137     for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
138         if (is<ContainerNode>(*child))
139             notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(*child));
140     }
141
142     if (!is<Element>(node))
143         return;
144
145     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
146         notifyNodeRemovedFromTree(insertionPoint, *root.get());
147 }
148
149 void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child)
150 {
151     if (!child.inDocument()) {
152         if (is<ContainerNode>(child))
153             notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(child));
154         return;
155     }
156     notifyNodeRemovedFromDocument(insertionPoint, child);
157     child.document().notifyRemovePendingSheetIfNeeded();
158 }
159
160 void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
161 {
162     // We have to tell all children that their parent has died.
163     Node* next = nullptr;
164     for (auto* node = container.firstChild(); node; node = next) {
165         ASSERT(!node->m_deletionHasBegun);
166
167         next = node->nextSibling();
168         node->setNextSibling(nullptr);
169         node->setParentNode(nullptr);
170         container.setFirstChild(next);
171         if (next)
172             next->setPreviousSibling(nullptr);
173
174         if (!node->refCount()) {
175 #ifndef NDEBUG
176             node->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(node);
182             else
183                 head = node;
184
185             tail = node;
186         } else {
187             Ref<Node> protect(*node); // removedFromDocument may remove remove all references to this node.
188             if (Document* containerDocument = container.ownerDocument())
189                 containerDocument->adoptIfNeeded(node);
190             if (node->inDocument())
191                 notifyChildNodeRemoved(container, *node);
192         }
193     }
194
195     container.setLastChild(nullptr);
196 }
197
198 void removeDetachedChildrenInContainer(ContainerNode& container)
199 {
200     // List of nodes to be deleted.
201     Node* head = nullptr;
202     Node* tail = nullptr;
203
204     addChildNodesToDeletionQueue(head, tail, container);
205
206     Node* node;
207     Node* next;
208     while ((node = head)) {
209         ASSERT(node->m_deletionHasBegun);
210
211         next = node->nextSibling();
212         node->setNextSibling(nullptr);
213
214         head = next;
215         if (!next)
216             tail = nullptr;
217
218         if (is<ContainerNode>(node))
219             addChildNodesToDeletionQueue(head, tail, downcast<ContainerNode>(*node));
220         
221         delete node;
222     }
223 }
224
225 #ifndef NDEBUG
226 static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
227 {
228     unsigned count = 0;
229
230     if (is<Element>(node)) {
231         if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame())
232             ++count;
233
234         if (ShadowRoot* root = downcast<Element>(node).shadowRoot())
235             count += assertConnectedSubrameCountIsConsistent(*root);
236     }
237
238     for (auto& child : childrenOfType<Element>(node))
239         count += assertConnectedSubrameCountIsConsistent(child);
240
241     // If we undercount there's possibly a security bug since we'd leave frames
242     // in subtrees outside the document.
243     ASSERT(node.connectedSubframeCount() >= count);
244
245     // If we overcount it's safe, but not optimal because it means we'll traverse
246     // through the document in disconnectSubframes looking for frames that have
247     // already been disconnected.
248     ASSERT(node.connectedSubframeCount() == count);
249
250     return count;
251 }
252 #endif
253
254 static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners, ContainerNode& root)
255 {
256     auto elementDescendants = descendantsOfType<Element>(root);
257     auto it = elementDescendants.begin();
258     auto end = elementDescendants.end();
259     while (it != end) {
260         Element& element = *it;
261         if (!element.connectedSubframeCount()) {
262             it.traverseNextSkippingChildren();
263             continue;
264         }
265
266         if (is<HTMLFrameOwnerElement>(element))
267             frameOwners.append(downcast<HTMLFrameOwnerElement>(element));
268
269         if (ShadowRoot* shadowRoot = element.shadowRoot())
270             collectFrameOwners(frameOwners, *shadowRoot);
271         ++it;
272     }
273 }
274
275 void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy)
276 {
277 #ifndef NDEBUG
278     assertConnectedSubrameCountIsConsistent(root);
279 #endif
280     ASSERT(root.connectedSubframeCount());
281
282     Vector<Ref<HTMLFrameOwnerElement>> frameOwners;
283
284     if (policy == RootAndDescendants) {
285         if (is<HTMLFrameOwnerElement>(root))
286             frameOwners.append(downcast<HTMLFrameOwnerElement>(root));
287     }
288
289     collectFrameOwners(frameOwners, root);
290
291     // Must disable frame loading in the subtree so an unload handler cannot
292     // insert more frames and create loaded frames in detached subtrees.
293     SubframeLoadingDisabler disabler(root);
294
295     bool isFirst = true;
296     for (auto& owner : frameOwners) {
297         // Don't need to traverse up the tree for the first owner since no
298         // script could have moved it.
299         if (isFirst || root.containsIncludingShadowDOM(&owner.get()))
300             owner.get().disconnectContentFrame();
301         isFirst = false;
302     }
303 }
304
305 }