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