1 // Copyright 2013 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.
5 #include "content/browser/frame_host/frame_tree.h"
10 #include "base/callback.h"
11 #include "base/containers/hash_tables.h"
12 #include "base/lazy_instance.h"
13 #include "content/browser/frame_host/frame_tree_node.h"
14 #include "content/browser/frame_host/navigator.h"
15 #include "content/browser/frame_host/render_frame_host_factory.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/frame_host/render_frame_proxy_host.h"
18 #include "content/browser/renderer_host/render_view_host_factory.h"
19 #include "content/browser/renderer_host/render_view_host_impl.h"
25 // Used with FrameTree::ForEach() to search for the FrameTreeNode
26 // corresponding to |frame_tree_node_id| whithin a specific FrameTree.
27 bool FrameTreeNodeForId(int64 frame_tree_node_id
,
28 FrameTreeNode
** out_node
,
29 FrameTreeNode
* node
) {
30 if (node
->frame_tree_node_id() == frame_tree_node_id
) {
32 // Terminate iteration once the node has been found.
38 bool CreateProxyForSiteInstance(const scoped_refptr
<SiteInstance
>& instance
,
39 FrameTreeNode
* node
) {
40 // If a new frame is created in the current SiteInstance, other frames in
41 // that SiteInstance don't need a proxy for the new frame.
42 SiteInstance
* current_instance
=
43 node
->render_manager()->current_frame_host()->GetSiteInstance();
44 if (current_instance
!= instance
.get())
45 node
->render_manager()->CreateRenderFrameProxy(instance
.get());
49 // Helper function used with FrameTree::ForEach() for retrieving the total
50 // loading progress and number of frames in a frame tree.
51 bool CollectLoadProgress(double* progress
,
53 FrameTreeNode
* node
) {
54 // Ignore the current frame if it has not started loading.
55 double frame_progress
= node
->loading_progress();
56 if (frame_progress
== FrameTreeNode::kLoadingProgressNotStarted
)
60 *progress
+= frame_progress
;
65 // Helper function used with FrameTree::ForEach() to reset the load progress.
66 bool ResetNodeLoadProgress(FrameTreeNode
* node
) {
67 node
->set_loading_progress(FrameTreeNode::kLoadingProgressNotStarted
);
71 // Helper function used with FrameTree::ForEach() to check if at least one of
72 // the nodes is loading.
73 bool IsNodeLoading(bool* is_loading
, FrameTreeNode
* node
) {
74 if (node
->IsLoading()) {
75 // There is at least one node loading, so abort traversal.
84 FrameTree::FrameTree(Navigator
* navigator
,
85 RenderFrameHostDelegate
* render_frame_delegate
,
86 RenderViewHostDelegate
* render_view_delegate
,
87 RenderWidgetHostDelegate
* render_widget_delegate
,
88 RenderFrameHostManager::Delegate
* manager_delegate
)
89 : render_frame_delegate_(render_frame_delegate
),
90 render_view_delegate_(render_view_delegate
),
91 render_widget_delegate_(render_widget_delegate
),
92 manager_delegate_(manager_delegate
),
93 root_(new FrameTreeNode(this,
95 render_frame_delegate
,
97 render_widget_delegate
,
100 focused_frame_tree_node_id_(-1) {
103 FrameTree::~FrameTree() {
106 FrameTreeNode
* FrameTree::FindByID(int64 frame_tree_node_id
) {
107 FrameTreeNode
* node
= NULL
;
108 ForEach(base::Bind(&FrameTreeNodeForId
, frame_tree_node_id
, &node
));
112 FrameTreeNode
* FrameTree::FindByRoutingID(int process_id
, int routing_id
) {
113 RenderFrameHostImpl
* render_frame_host
=
114 RenderFrameHostImpl::FromID(process_id
, routing_id
);
115 if (render_frame_host
) {
116 FrameTreeNode
* result
= render_frame_host
->frame_tree_node();
117 if (this == result
->frame_tree())
121 RenderFrameProxyHost
* render_frame_proxy_host
=
122 RenderFrameProxyHost::FromID(process_id
, routing_id
);
123 if (render_frame_proxy_host
) {
124 FrameTreeNode
* result
= render_frame_proxy_host
->frame_tree_node();
125 if (this == result
->frame_tree())
132 void FrameTree::ForEach(
133 const base::Callback
<bool(FrameTreeNode
*)>& on_node
) const {
134 ForEach(on_node
, NULL
);
137 void FrameTree::ForEach(
138 const base::Callback
<bool(FrameTreeNode
*)>& on_node
,
139 FrameTreeNode
* skip_this_subtree
) const {
140 std::queue
<FrameTreeNode
*> queue
;
141 queue
.push(root_
.get());
143 while (!queue
.empty()) {
144 FrameTreeNode
* node
= queue
.front();
146 if (skip_this_subtree
== node
)
149 if (!on_node
.Run(node
))
152 for (size_t i
= 0; i
< node
->child_count(); ++i
)
153 queue
.push(node
->child_at(i
));
157 RenderFrameHostImpl
* FrameTree::AddFrame(FrameTreeNode
* parent
,
160 const std::string
& frame_name
) {
161 // A child frame always starts with an initial empty document, which means
162 // it is in the same SiteInstance as the parent frame. Ensure that the process
163 // which requested a child frame to be added is the same as the process of the
165 // We return nullptr if this is not the case, which can happen in a race if an
166 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
167 if (parent
->current_frame_host()->GetProcess()->GetID() != process_id
)
170 scoped_ptr
<FrameTreeNode
> node(new FrameTreeNode(
171 this, parent
->navigator(), render_frame_delegate_
, render_view_delegate_
,
172 render_widget_delegate_
, manager_delegate_
, frame_name
));
173 FrameTreeNode
* node_ptr
= node
.get();
174 // AddChild is what creates the RenderFrameHost.
175 parent
->AddChild(node
.Pass(), process_id
, new_routing_id
);
176 return node_ptr
->current_frame_host();
179 void FrameTree::RemoveFrame(FrameTreeNode
* child
) {
180 FrameTreeNode
* parent
= child
->parent();
182 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
186 parent
->RemoveChild(child
);
189 void FrameTree::CreateProxiesForSiteInstance(
190 FrameTreeNode
* source
,
191 SiteInstance
* site_instance
) {
192 // Create the swapped out RVH for the new SiteInstance. This will create
193 // a top-level swapped out RFH as well, which will then be wrapped by a
194 // RenderFrameProxyHost.
195 if (!source
->IsMainFrame()) {
196 RenderViewHostImpl
* render_view_host
=
197 source
->frame_tree()->GetRenderViewHost(site_instance
);
198 if (!render_view_host
) {
199 root()->render_manager()->CreateRenderFrame(
200 site_instance
, nullptr, MSG_ROUTING_NONE
,
201 CREATE_RF_SWAPPED_OUT
| CREATE_RF_HIDDEN
, nullptr);
203 root()->render_manager()->EnsureRenderViewInitialized(
204 source
, render_view_host
, site_instance
);
208 scoped_refptr
<SiteInstance
> instance(site_instance
);
210 // Proxies are created in the FrameTree in response to a node navigating to a
211 // new SiteInstance. Since |source|'s navigation will replace the currently
212 // loaded document, the entire subtree under |source| will be removed.
213 ForEach(base::Bind(&CreateProxyForSiteInstance
, instance
), source
);
216 void FrameTree::ResetForMainFrameSwap() {
217 root_
->ResetForNewProcess();
218 focused_frame_tree_node_id_
= -1;
221 RenderFrameHostImpl
* FrameTree::GetMainFrame() const {
222 return root_
->current_frame_host();
225 FrameTreeNode
* FrameTree::GetFocusedFrame() {
226 return FindByID(focused_frame_tree_node_id_
);
229 void FrameTree::SetFocusedFrame(FrameTreeNode
* node
) {
230 focused_frame_tree_node_id_
= node
->frame_tree_node_id();
233 void FrameTree::SetFrameRemoveListener(
234 const base::Callback
<void(RenderFrameHost
*)>& on_frame_removed
) {
235 on_frame_removed_
= on_frame_removed
;
238 RenderViewHostImpl
* FrameTree::CreateRenderViewHost(SiteInstance
* site_instance
,
240 int main_frame_routing_id
,
243 DCHECK(main_frame_routing_id
!= MSG_ROUTING_NONE
);
244 RenderViewHostMap::iterator iter
=
245 render_view_host_map_
.find(site_instance
->GetId());
246 if (iter
!= render_view_host_map_
.end()) {
247 // If a RenderViewHost's main frame is pending deletion for this
248 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
249 // Otherwise return the existing RenderViewHost for the SiteInstance.
250 RenderFrameHostImpl
* main_frame
= static_cast<RenderFrameHostImpl
*>(
251 iter
->second
->GetMainFrame());
252 if (main_frame
->frame_tree_node()->render_manager()->IsPendingDeletion(
254 render_view_host_pending_shutdown_map_
.insert(
255 std::pair
<int, RenderViewHostImpl
*>(site_instance
->GetId(),
257 render_view_host_map_
.erase(iter
);
262 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
263 RenderViewHostFactory::Create(site_instance
,
264 render_view_delegate_
,
265 render_widget_delegate_
,
267 main_frame_routing_id
,
271 render_view_host_map_
[site_instance
->GetId()] = rvh
;
275 RenderViewHostImpl
* FrameTree::GetRenderViewHost(SiteInstance
* site_instance
) {
276 RenderViewHostMap::iterator iter
=
277 render_view_host_map_
.find(site_instance
->GetId());
278 // TODO(creis): Mirror the frame tree so this check can't fail.
279 if (iter
== render_view_host_map_
.end())
284 void FrameTree::RegisterRenderFrameHost(
285 RenderFrameHostImpl
* render_frame_host
) {
286 SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
287 RenderViewHostMap::iterator iter
=
288 render_view_host_map_
.find(site_instance
->GetId());
289 CHECK(iter
!= render_view_host_map_
.end());
291 iter
->second
->increment_ref_count();
294 void FrameTree::UnregisterRenderFrameHost(
295 RenderFrameHostImpl
* render_frame_host
) {
296 SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
297 int32 site_instance_id
= site_instance
->GetId();
298 RenderViewHostMap::iterator iter
=
299 render_view_host_map_
.find(site_instance_id
);
300 if (iter
!= render_view_host_map_
.end() &&
301 iter
->second
== render_frame_host
->render_view_host()) {
302 // Decrement the refcount and shutdown the RenderViewHost if no one else is
304 CHECK_GT(iter
->second
->ref_count(), 0);
305 iter
->second
->decrement_ref_count();
306 if (iter
->second
->ref_count() == 0) {
307 iter
->second
->Shutdown();
308 render_view_host_map_
.erase(iter
);
311 // The RenderViewHost should be in the list of RenderViewHosts pending
313 bool render_view_host_found
= false;
314 std::pair
<RenderViewHostMultiMap::iterator
,
315 RenderViewHostMultiMap::iterator
> result
=
316 render_view_host_pending_shutdown_map_
.equal_range(site_instance_id
);
317 for (RenderViewHostMultiMap::iterator multi_iter
= result
.first
;
318 multi_iter
!= result
.second
;
320 if (multi_iter
->second
!= render_frame_host
->render_view_host())
322 render_view_host_found
= true;
323 RenderViewHostImpl
* rvh
= multi_iter
->second
;
324 // Decrement the refcount and shutdown the RenderViewHost if no one else
326 CHECK_GT(rvh
->ref_count(), 0);
327 rvh
->decrement_ref_count();
328 if (rvh
->ref_count() == 0) {
330 render_view_host_pending_shutdown_map_
.erase(multi_iter
);
334 CHECK(render_view_host_found
);
338 void FrameTree::FrameRemoved(FrameTreeNode
* frame
) {
339 // No notification for the root frame.
340 if (!frame
->parent()) {
341 CHECK_EQ(frame
, root_
.get());
345 // Notify observers of the frame removal.
346 if (!on_frame_removed_
.is_null())
347 on_frame_removed_
.Run(frame
->current_frame_host());
350 double FrameTree::GetLoadProgress() {
351 double progress
= 0.0;
354 ForEach(base::Bind(&CollectLoadProgress
, &progress
, &frame_count
));
355 if (frame_count
!= 0)
356 progress
/= frame_count
;
360 void FrameTree::ResetLoadProgress() {
361 ForEach(base::Bind(&ResetNodeLoadProgress
));
364 bool FrameTree::IsLoading() {
365 bool is_loading
= false;
366 ForEach(base::Bind(&IsNodeLoading
, &is_loading
));
370 } // namespace content