031d3cd21b63ec808036698786120d24efa2e563
[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 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 ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument(ContainerNode& node)
33 {
34     ChildNodesLazySnapshot snapshot(node);
35     while (RefPtr<Node> child = snapshot.nextNode()) {
36         // If we have been removed from the document during this loop, then
37         // we don't want to tell the rest of our children that they've been
38         // inserted into the document because they haven't.
39         if (node.inDocument() && child->parentNode() == &node)
40             notifyNodeInsertedIntoDocument(*child);
41     }
42
43     if (!is<Element>(node))
44         return;
45
46     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
47         if (node.inDocument() && root->hostElement() == &node)
48             notifyNodeInsertedIntoDocument(*root);
49     }
50 }
51
52 void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node)
53 {
54     for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
55         if (is<ContainerNode>(child))
56             notifyNodeInsertedIntoTree(downcast<ContainerNode>(*child));
57     }
58
59     if (ShadowRoot* root = node.shadowRoot())
60         notifyNodeInsertedIntoTree(*root);
61 }
62
63 void ChildNodeRemovalNotifier::notifyDescendantRemovedFromDocument(ContainerNode& node)
64 {
65     ChildNodesLazySnapshot snapshot(node);
66     while (RefPtr<Node> child = snapshot.nextNode()) {
67         // If we have been added to the document during this loop, then we
68         // don't want to tell the rest of our children that they've been
69         // removed from the document because they haven't.
70         if (!node.inDocument() && child->parentNode() == &node)
71             notifyNodeRemovedFromDocument(*child.get());
72     }
73
74     if (!is<Element>(node))
75         return;
76
77     if (node.document().cssTarget() == &node)
78         node.document().setCSSTarget(0);
79
80     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
81         if (!node.inDocument() && root->hostElement() == &node)
82             notifyNodeRemovedFromDocument(*root.get());
83     }
84 }
85
86 void ChildNodeRemovalNotifier::notifyDescendantRemovedFromTree(ContainerNode& node)
87 {
88     for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
89         if (is<ContainerNode>(child))
90             notifyNodeRemovedFromTree(downcast<ContainerNode>(*child));
91     }
92
93     if (!is<Element>(node))
94         return;
95
96     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
97         notifyNodeRemovedFromTree(*root.get());
98 }
99
100 #ifndef NDEBUG
101 static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
102 {
103     unsigned count = 0;
104
105     if (is<Element>(node)) {
106         if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame())
107             ++count;
108
109         if (ShadowRoot* root = downcast<Element>(node).shadowRoot())
110             count += assertConnectedSubrameCountIsConsistent(*root);
111     }
112
113     for (auto& child : childrenOfType<Element>(node))
114         count += assertConnectedSubrameCountIsConsistent(child);
115
116     // If we undercount there's possibly a security bug since we'd leave frames
117     // in subtrees outside the document.
118     ASSERT(node.connectedSubframeCount() >= count);
119
120     // If we overcount it's safe, but not optimal because it means we'll traverse
121     // through the document in disconnectSubframes looking for frames that have
122     // already been disconnected.
123     ASSERT(node.connectedSubframeCount() == count);
124
125     return count;
126 }
127 #endif
128
129 static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners, ContainerNode& root)
130 {
131     auto elementDescendants = descendantsOfType<Element>(root);
132     auto it = elementDescendants.begin();
133     auto end = elementDescendants.end();
134     while (it != end) {
135         Element& element = *it;
136         if (!element.connectedSubframeCount()) {
137             it.traverseNextSkippingChildren();
138             continue;
139         }
140
141         if (is<HTMLFrameOwnerElement>(element))
142             frameOwners.append(downcast<HTMLFrameOwnerElement>(element));
143
144         if (ShadowRoot* shadowRoot = element.shadowRoot())
145             collectFrameOwners(frameOwners, *shadowRoot);
146         ++it;
147     }
148 }
149
150 void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy)
151 {
152 #ifndef NDEBUG
153     assertConnectedSubrameCountIsConsistent(root);
154 #endif
155     ASSERT(root.connectedSubframeCount());
156
157     Vector<Ref<HTMLFrameOwnerElement>> frameOwners;
158
159     if (policy == RootAndDescendants) {
160         if (is<HTMLFrameOwnerElement>(root))
161             frameOwners.append(downcast<HTMLFrameOwnerElement>(root));
162     }
163
164     collectFrameOwners(frameOwners, root);
165
166     // Must disable frame loading in the subtree so an unload handler cannot
167     // insert more frames and create loaded frames in detached subtrees.
168     SubframeLoadingDisabler disabler(root);
169
170     for (unsigned i = 0; i < frameOwners.size(); ++i) {
171         auto& owner = frameOwners[i].get();
172         // Don't need to traverse up the tree for the first owner since no
173         // script could have moved it.
174         if (!i || root.containsIncludingShadowDOM(&owner))
175             owner.disconnectContentFrame();
176     }
177 }
178
179 }