1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "config.h" 6#include "core/dom/ChildFrameDisconnector.h" 7 8#include "core/dom/shadow/ElementShadow.h" 9#include "core/dom/shadow/ShadowRoot.h" 10#include "core/html/HTMLFrameOwnerElement.h" 11#include "wtf/Assertions.h" 12 13namespace blink { 14 15#if ENABLE(ASSERT) 16static unsigned checkConnectedSubframeCountIsConsistent(Node&); 17#endif 18 19void ChildFrameDisconnector::disconnect(DisconnectPolicy policy) 20{ 21#if ENABLE(ASSERT) 22 checkConnectedSubframeCountIsConsistent(root()); 23#endif 24 25 if (!root().connectedSubframeCount()) 26 return; 27 28 if (policy == RootAndDescendants) { 29 collectFrameOwners(root()); 30 } else { 31 for (Node* child = root().firstChild(); child; child = child->nextSibling()) 32 collectFrameOwners(*child); 33 } 34 35 disconnectCollectedFrameOwners(); 36} 37 38void ChildFrameDisconnector::collectFrameOwners(Node& root) 39{ 40 if (!root.connectedSubframeCount()) 41 return; 42 43 if (root.isHTMLElement() && root.isFrameOwnerElement()) 44 m_frameOwners.append(&toHTMLFrameOwnerElement(root)); 45 46 for (Node* child = root.firstChild(); child; child = child->nextSibling()) 47 collectFrameOwners(*child); 48 49 ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0; 50 if (shadow) 51 collectFrameOwners(*shadow); 52} 53 54void ChildFrameDisconnector::disconnectCollectedFrameOwners() 55{ 56 // Must disable frame loading in the subtree so an unload handler cannot 57 // insert more frames and create loaded frames in detached subtrees. 58 SubframeLoadingDisabler disabler(root()); 59 60 for (unsigned i = 0; i < m_frameOwners.size(); ++i) { 61 HTMLFrameOwnerElement* owner = m_frameOwners[i].get(); 62 // Don't need to traverse up the tree for the first owner since no 63 // script could have moved it. 64 if (!i || root().containsIncludingShadowDOM(owner)) 65 owner->disconnectContentFrame(); 66 } 67} 68 69void ChildFrameDisconnector::collectFrameOwners(ElementShadow& shadow) 70{ 71 for (ShadowRoot* root = shadow.youngestShadowRoot(); root; root = root->olderShadowRoot()) 72 collectFrameOwners(*root); 73} 74 75#if ENABLE(ASSERT) 76static unsigned checkConnectedSubframeCountIsConsistent(Node& node) 77{ 78 unsigned count = 0; 79 80 if (node.isElementNode()) { 81 if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame()) 82 count++; 83 84 if (ElementShadow* shadow = toElement(node).shadow()) { 85 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot()) 86 count += checkConnectedSubframeCountIsConsistent(*root); 87 } 88 } 89 90 for (Node* child = node.firstChild(); child; child = child->nextSibling()) 91 count += checkConnectedSubframeCountIsConsistent(*child); 92 93 // If we undercount there's possibly a security bug since we'd leave frames 94 // in subtrees outside the document. 95 ASSERT(node.connectedSubframeCount() >= count); 96 97 // If we overcount it's safe, but not optimal because it means we'll traverse 98 // through the document in ChildFrameDisconnector looking for frames that have 99 // already been disconnected. 100 ASSERT(node.connectedSubframeCount() == count); 101 102 return count; 103} 104#endif 105 106} 107