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 "third_party/blink/renderer/core/dom/child_frame_disconnector.h" 6 7 #include "third_party/blink/renderer/core/dom/shadow_root.h" 8 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" 9 #include "third_party/blink/renderer/platform/wtf/assertions.h" 10 11 namespace blink { 12 13 #if DCHECK_IS_ON() 14 static unsigned CheckConnectedSubframeCountIsConsistent(Node&); 15 #endif 16 Disconnect(DisconnectPolicy policy)17void ChildFrameDisconnector::Disconnect(DisconnectPolicy policy) { 18 #if DCHECK_IS_ON() 19 CheckConnectedSubframeCountIsConsistent(Root()); 20 #endif 21 22 if (!Root().ConnectedSubframeCount()) 23 return; 24 25 if (policy == kRootAndDescendants) { 26 CollectFrameOwners(Root()); 27 } else { 28 for (Node* child = Root().firstChild(); child; child = child->nextSibling()) 29 CollectFrameOwners(*child); 30 } 31 32 DisconnectCollectedFrameOwners(); 33 } 34 CollectFrameOwners(Node & root)35void ChildFrameDisconnector::CollectFrameOwners(Node& root) { 36 if (!root.ConnectedSubframeCount()) 37 return; 38 39 if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(root)) 40 frame_owners_.push_back(frame_owner); 41 42 for (Node* child = root.firstChild(); child; child = child->nextSibling()) 43 CollectFrameOwners(*child); 44 45 if (ShadowRoot* shadow_root = root.GetShadowRoot()) 46 CollectFrameOwners(*shadow_root); 47 } 48 DisconnectCollectedFrameOwners()49void ChildFrameDisconnector::DisconnectCollectedFrameOwners() { 50 // Must disable frame loading in the subtree so an unload handler cannot 51 // insert more frames and create loaded frames in detached subtrees. 52 SubframeLoadingDisabler disabler(Root()); 53 54 for (unsigned i = 0; i < frame_owners_.size(); ++i) { 55 HTMLFrameOwnerElement* owner = frame_owners_[i].Get(); 56 // Don't need to traverse up the tree for the first owner since no 57 // script could have moved it. 58 if (!i || Root().IsShadowIncludingInclusiveAncestorOf(*owner)) 59 owner->DisconnectContentFrame(); 60 } 61 } 62 63 #if DCHECK_IS_ON() CheckConnectedSubframeCountIsConsistent(Node & node)64static unsigned CheckConnectedSubframeCountIsConsistent(Node& node) { 65 unsigned count = 0; 66 if (auto* element = DynamicTo<Element>(node)) { 67 auto* frame_owner_element = DynamicTo<HTMLFrameOwnerElement>(node); 68 if (frame_owner_element && frame_owner_element->ContentFrame()) 69 count++; 70 71 if (ShadowRoot* root = element->GetShadowRoot()) 72 count += CheckConnectedSubframeCountIsConsistent(*root); 73 } 74 75 for (Node* child = node.firstChild(); child; child = child->nextSibling()) 76 count += CheckConnectedSubframeCountIsConsistent(*child); 77 78 // If we undercount there's possibly a security bug since we'd leave frames 79 // in subtrees outside the document. 80 DCHECK_GE(node.ConnectedSubframeCount(), count); 81 82 // If we overcount it's safe, but not optimal because it means we'll traverse 83 // through the document in ChildFrameDisconnector looking for frames that have 84 // already been disconnected. 85 DCHECK_EQ(node.ConnectedSubframeCount(), count); 86 87 return count; 88 } 89 #endif 90 91 } // namespace blink 92