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 13 namespace blink { 14 15 #if ENABLE(ASSERT) 16 static unsigned checkConnectedSubframeCountIsConsistent(Node&); 17 #endif 18 19 void 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 38 void 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 54 void 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 69 void ChildFrameDisconnector::collectFrameOwners(ElementShadow& shadow) 70 { 71 for (ShadowRoot* root = shadow.youngestShadowRoot(); root; root = root->olderShadowRoot()) 72 collectFrameOwners(*root); 73 } 74 75 #if ENABLE(ASSERT) 76 static 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