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)17 void 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)35 void 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()49 void 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)64 static 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