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.
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.
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.
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.
27 #include "ContainerNodeAlgorithms.h"
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&);
37 static void notifyDescendantInsertedIntoDocument(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
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);
48 if (!is<Element>(node))
51 if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
52 if (node.inDocument() && root->host() == &node)
53 notifyNodeInsertedIntoDocument(insertionPoint, *root, postInsertionNotificationTargets);
57 static void notifyDescendantInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
59 for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
60 if (is<ContainerNode>(*child))
61 notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(*child), postInsertionNotificationTargets);
64 if (ShadowRoot* root = node.shadowRoot())
65 notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets);
68 void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
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);
77 void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
79 NoEventDispatchAssertion assertNoEventDispatch;
80 ASSERT(!insertionPoint.inDocument());
82 if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
83 postInsertionNotificationTargets.append(node);
84 notifyDescendantInsertedIntoTree(insertionPoint, node, postInsertionNotificationTargets);
87 void notifyChildNodeInserted(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
89 ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
91 InspectorInstrumentation::didInsertDOMNode(node.document(), node);
93 Ref<Document> protectDocument(node.document());
94 Ref<Node> protectNode(node);
96 if (insertionPoint.inDocument())
97 notifyNodeInsertedIntoDocument(insertionPoint, node, postInsertionNotificationTargets);
98 else if (is<ContainerNode>(node))
99 notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets);
102 void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node& node)
104 ASSERT(insertionPoint.inDocument());
105 node.removedFrom(insertionPoint);
107 if (!is<ContainerNode>(node))
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());
118 if (!is<Element>(node))
121 if (node.document().cssTarget() == &node)
122 node.document().setCSSTarget(nullptr);
124 if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
125 if (!node.inDocument() && root->host() == &node)
126 notifyNodeRemovedFromDocument(insertionPoint, *root.get());
130 void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode& node)
132 NoEventDispatchAssertion assertNoEventDispatch;
133 ASSERT(!insertionPoint.inDocument());
135 node.removedFrom(insertionPoint);
137 for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
138 if (is<ContainerNode>(*child))
139 notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(*child));
142 if (!is<Element>(node))
145 if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
146 notifyNodeRemovedFromTree(insertionPoint, *root.get());
149 void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child)
151 if (!child.inDocument()) {
152 if (is<ContainerNode>(child))
153 notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(child));
156 notifyNodeRemovedFromDocument(insertionPoint, child);
157 child.document().notifyRemovePendingSheetIfNeeded();
160 void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
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);
167 next = node->nextSibling();
168 node->setNextSibling(nullptr);
169 node->setParentNode(nullptr);
170 container.setFirstChild(next);
172 next->setPreviousSibling(nullptr);
174 if (!node->refCount()) {
176 node->m_deletionHasBegun = true;
178 // Add the node to the list of nodes to be deleted.
179 // Reuse the nextSibling pointer for this purpose.
181 tail->setNextSibling(node);
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);
195 container.setLastChild(nullptr);
198 void removeDetachedChildrenInContainer(ContainerNode& container)
200 // List of nodes to be deleted.
201 Node* head = nullptr;
202 Node* tail = nullptr;
204 addChildNodesToDeletionQueue(head, tail, container);
208 while ((node = head)) {
209 ASSERT(node->m_deletionHasBegun);
211 next = node->nextSibling();
212 node->setNextSibling(nullptr);
218 if (is<ContainerNode>(node))
219 addChildNodesToDeletionQueue(head, tail, downcast<ContainerNode>(*node));
226 static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
230 if (is<Element>(node)) {
231 if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame())
234 if (ShadowRoot* root = downcast<Element>(node).shadowRoot())
235 count += assertConnectedSubrameCountIsConsistent(*root);
238 for (auto& child : childrenOfType<Element>(node))
239 count += assertConnectedSubrameCountIsConsistent(child);
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);
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);
254 static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners, ContainerNode& root)
256 auto elementDescendants = descendantsOfType<Element>(root);
257 auto it = elementDescendants.begin();
258 auto end = elementDescendants.end();
260 Element& element = *it;
261 if (!element.connectedSubframeCount()) {
262 it.traverseNextSkippingChildren();
266 if (is<HTMLFrameOwnerElement>(element))
267 frameOwners.append(downcast<HTMLFrameOwnerElement>(element));
269 if (ShadowRoot* shadowRoot = element.shadowRoot())
270 collectFrameOwners(frameOwners, *shadowRoot);
275 void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy)
278 assertConnectedSubrameCountIsConsistent(root);
280 ASSERT(root.connectedSubframeCount());
282 Vector<Ref<HTMLFrameOwnerElement>> frameOwners;
284 if (policy == RootAndDescendants) {
285 if (is<HTMLFrameOwnerElement>(root))
286 frameOwners.append(downcast<HTMLFrameOwnerElement>(root));
289 collectFrameOwners(frameOwners, root);
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);
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();