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
30namespace WebCore {
31
32void 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.get());
41    }
42
43    if (!node.isElementNode())
44        return;
45
46    if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) {
47        if (node.inDocument() && root->hostElement() == &node)
48            notifyNodeInsertedIntoDocument(*root.get());
49    }
50}
51
52void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node)
53{
54    for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
55        if (child->isContainerNode())
56            notifyNodeInsertedIntoTree(*toContainerNode(child));
57    }
58
59    if (ShadowRoot* root = node.shadowRoot())
60        notifyNodeInsertedIntoTree(*root);
61}
62
63void 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 (!node.isElementNode())
75        return;
76
77    if (node.document().cssTarget() == &node)
78        node.document().setCSSTarget(0);
79
80    if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) {
81        if (!node.inDocument() && root->hostElement() == &node)
82            notifyNodeRemovedFromDocument(*root.get());
83    }
84}
85
86void ChildNodeRemovalNotifier::notifyDescendantRemovedFromTree(ContainerNode& node)
87{
88    for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
89        if (child->isContainerNode())
90            notifyNodeRemovedFromTree(*toContainerNode(child));
91    }
92
93    if (!node.isElementNode())
94        return;
95
96    if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot())
97        notifyNodeRemovedFromTree(*root.get());
98}
99
100#ifndef NDEBUG
101static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
102{
103    unsigned count = 0;
104
105    if (node.isElementNode()) {
106        if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame())
107            count++;
108
109        if (ShadowRoot* root = toElement(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
129static 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 (element.isHTMLElement() && element.isFrameOwnerElement())
142            frameOwners.append(toHTMLFrameOwnerElement(element));
143
144        if (ShadowRoot* shadowRoot = element.shadowRoot())
145            collectFrameOwners(frameOwners, *shadowRoot);
146        ++it;
147    }
148}
149
150void 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 (root.isHTMLElement() && root.isFrameOwnerElement())
161            frameOwners.append(toHTMLFrameOwnerElement(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}
180