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/command_line.h"
12 #include "base/containers/hash_tables.h"
13 #include "base/lazy_instance.h"
14 #include "content/browser/frame_host/frame_tree_node.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_factory.h"
17 #include "content/browser/frame_host/render_frame_host_impl.h"
18 #include "content/browser/frame_host/render_frame_proxy_host.h"
19 #include "content/browser/renderer_host/render_view_host_factory.h"
20 #include "content/browser/renderer_host/render_view_host_impl.h"
21 #include "content/public/common/content_switches.h"
22 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
28 // Used with FrameTree::ForEach() to search for the FrameTreeNode
29 // corresponding to |frame_tree_node_id| within a specific FrameTree.
30 bool FrameTreeNodeForId(int frame_tree_node_id
,
31 FrameTreeNode
** out_node
,
32 FrameTreeNode
* node
) {
33 if (node
->frame_tree_node_id() == frame_tree_node_id
) {
35 // Terminate iteration once the node has been found.
41 // Used with FrameTree::ForEach() to search for the FrameTreeNode with the given
42 // |name| within a specific FrameTree.
43 bool FrameTreeNodeForName(const std::string
& name
,
44 FrameTreeNode
** out_node
,
45 FrameTreeNode
* node
) {
46 if (node
->frame_name() == name
) {
48 // Terminate iteration once the node has been found.
54 bool CreateProxyForSiteInstance(const scoped_refptr
<SiteInstance
>& instance
,
55 FrameTreeNode
* node
) {
56 // If a new frame is created in the current SiteInstance, other frames in
57 // that SiteInstance don't need a proxy for the new frame.
58 SiteInstance
* current_instance
=
59 node
->render_manager()->current_frame_host()->GetSiteInstance();
60 if (current_instance
!= instance
.get())
61 node
->render_manager()->CreateRenderFrameProxy(instance
.get());
65 // Helper function used with FrameTree::ForEach() for retrieving the total
66 // loading progress and number of frames in a frame tree.
67 bool CollectLoadProgress(double* progress
,
69 FrameTreeNode
* node
) {
70 // Ignore the current frame if it has not started loading.
71 if (!node
->has_started_loading())
75 *progress
+= node
->loading_progress();
80 // Helper function used with FrameTree::ForEach() to reset the load progress.
81 bool ResetNodeLoadProgress(FrameTreeNode
* node
) {
82 node
->reset_loading_progress();
86 // Helper function used with FrameTree::ForEach() to check if at least one of
87 // the nodes is loading.
88 bool IsNodeLoading(bool* is_loading
, FrameTreeNode
* node
) {
89 if (node
->IsLoading()) {
90 // There is at least one node loading, so abort traversal.
99 FrameTree::FrameTree(Navigator
* navigator
,
100 RenderFrameHostDelegate
* render_frame_delegate
,
101 RenderViewHostDelegate
* render_view_delegate
,
102 RenderWidgetHostDelegate
* render_widget_delegate
,
103 RenderFrameHostManager::Delegate
* manager_delegate
)
104 : render_frame_delegate_(render_frame_delegate
),
105 render_view_delegate_(render_view_delegate
),
106 render_widget_delegate_(render_widget_delegate
),
107 manager_delegate_(manager_delegate
),
108 root_(new FrameTreeNode(this,
110 render_frame_delegate
,
111 render_view_delegate
,
112 render_widget_delegate
,
114 // The top-level frame must always be in a
116 blink::WebTreeScopeType::Document
,
118 blink::WebSandboxFlags::None
)),
119 focused_frame_tree_node_id_(-1),
120 load_progress_(0.0) {
123 FrameTree::~FrameTree() {
126 FrameTreeNode
* FrameTree::FindByID(int frame_tree_node_id
) {
127 FrameTreeNode
* node
= nullptr;
128 ForEach(base::Bind(&FrameTreeNodeForId
, frame_tree_node_id
, &node
));
132 FrameTreeNode
* FrameTree::FindByRoutingID(int process_id
, int routing_id
) {
133 RenderFrameHostImpl
* render_frame_host
=
134 RenderFrameHostImpl::FromID(process_id
, routing_id
);
135 if (render_frame_host
) {
136 FrameTreeNode
* result
= render_frame_host
->frame_tree_node();
137 if (this == result
->frame_tree())
141 RenderFrameProxyHost
* render_frame_proxy_host
=
142 RenderFrameProxyHost::FromID(process_id
, routing_id
);
143 if (render_frame_proxy_host
) {
144 FrameTreeNode
* result
= render_frame_proxy_host
->frame_tree_node();
145 if (this == result
->frame_tree())
152 FrameTreeNode
* FrameTree::FindByName(const std::string
& name
) {
156 FrameTreeNode
* node
= nullptr;
157 ForEach(base::Bind(&FrameTreeNodeForName
, name
, &node
));
161 void FrameTree::ForEach(
162 const base::Callback
<bool(FrameTreeNode
*)>& on_node
) const {
163 ForEach(on_node
, nullptr);
166 void FrameTree::ForEach(
167 const base::Callback
<bool(FrameTreeNode
*)>& on_node
,
168 FrameTreeNode
* skip_this_subtree
) const {
169 std::queue
<FrameTreeNode
*> queue
;
170 queue
.push(root_
.get());
172 while (!queue
.empty()) {
173 FrameTreeNode
* node
= queue
.front();
175 if (skip_this_subtree
== node
)
178 if (!on_node
.Run(node
))
181 for (size_t i
= 0; i
< node
->child_count(); ++i
)
182 queue
.push(node
->child_at(i
));
186 RenderFrameHostImpl
* FrameTree::AddFrame(FrameTreeNode
* parent
,
189 blink::WebTreeScopeType scope
,
190 const std::string
& frame_name
,
191 blink::WebSandboxFlags sandbox_flags
) {
192 // A child frame always starts with an initial empty document, which means
193 // it is in the same SiteInstance as the parent frame. Ensure that the process
194 // which requested a child frame to be added is the same as the process of the
196 // We return nullptr if this is not the case, which can happen in a race if an
197 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
198 if (parent
->current_frame_host()->GetProcess()->GetID() != process_id
)
201 scoped_ptr
<FrameTreeNode
> node(
202 new FrameTreeNode(this, parent
->navigator(), render_frame_delegate_
,
203 render_view_delegate_
, render_widget_delegate_
,
204 manager_delegate_
, scope
, frame_name
, sandbox_flags
));
205 FrameTreeNode
* node_ptr
= node
.get();
206 // AddChild is what creates the RenderFrameHost.
207 parent
->AddChild(node
.Pass(), process_id
, new_routing_id
);
208 return node_ptr
->current_frame_host();
211 void FrameTree::RemoveFrame(FrameTreeNode
* child
) {
212 FrameTreeNode
* parent
= child
->parent();
214 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
218 parent
->RemoveChild(child
);
221 void FrameTree::CreateProxiesForSiteInstance(
222 FrameTreeNode
* source
,
223 SiteInstance
* site_instance
) {
224 // Create the swapped out RVH for the new SiteInstance. This will create
225 // a top-level swapped out RFH as well, which will then be wrapped by a
226 // RenderFrameProxyHost.
227 if (!source
->IsMainFrame()) {
228 RenderViewHostImpl
* render_view_host
=
229 source
->frame_tree()->GetRenderViewHost(site_instance
);
230 if (!render_view_host
) {
231 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
232 switches::kSitePerProcess
)) {
233 root()->render_manager()->CreateRenderFrameProxy(site_instance
);
235 root()->render_manager()->CreateRenderFrame(
236 site_instance
, nullptr, MSG_ROUTING_NONE
,
237 CREATE_RF_SWAPPED_OUT
| CREATE_RF_HIDDEN
, nullptr);
240 root()->render_manager()->EnsureRenderViewInitialized(
241 source
, render_view_host
, site_instance
);
245 scoped_refptr
<SiteInstance
> instance(site_instance
);
247 // Proxies are created in the FrameTree in response to a node navigating to a
248 // new SiteInstance. Since |source|'s navigation will replace the currently
249 // loaded document, the entire subtree under |source| will be removed.
250 ForEach(base::Bind(&CreateProxyForSiteInstance
, instance
), source
);
253 RenderFrameHostImpl
* FrameTree::GetMainFrame() const {
254 return root_
->current_frame_host();
257 FrameTreeNode
* FrameTree::GetFocusedFrame() {
258 return FindByID(focused_frame_tree_node_id_
);
261 void FrameTree::SetFocusedFrame(FrameTreeNode
* node
) {
262 focused_frame_tree_node_id_
= node
->frame_tree_node_id();
265 void FrameTree::SetFrameRemoveListener(
266 const base::Callback
<void(RenderFrameHost
*)>& on_frame_removed
) {
267 on_frame_removed_
= on_frame_removed
;
270 RenderViewHostImpl
* FrameTree::CreateRenderViewHost(SiteInstance
* site_instance
,
272 int main_frame_routing_id
,
275 RenderViewHostMap::iterator iter
=
276 render_view_host_map_
.find(site_instance
->GetId());
277 if (iter
!= render_view_host_map_
.end()) {
278 // If a RenderViewHost's main frame is pending deletion for this
279 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
280 // Otherwise return the existing RenderViewHost for the SiteInstance.
281 RenderFrameHostImpl
* main_frame
= static_cast<RenderFrameHostImpl
*>(
282 iter
->second
->GetMainFrame());
284 main_frame
->frame_tree_node()->render_manager()->IsPendingDeletion(
286 render_view_host_pending_shutdown_map_
.insert(
287 std::pair
<int, RenderViewHostImpl
*>(site_instance
->GetId(),
289 render_view_host_map_
.erase(iter
);
294 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
295 RenderViewHostFactory::Create(site_instance
,
296 render_view_delegate_
,
297 render_widget_delegate_
,
299 main_frame_routing_id
,
303 render_view_host_map_
[site_instance
->GetId()] = rvh
;
307 RenderViewHostImpl
* FrameTree::GetRenderViewHost(SiteInstance
* site_instance
) {
308 RenderViewHostMap::iterator iter
=
309 render_view_host_map_
.find(site_instance
->GetId());
310 if (iter
== render_view_host_map_
.end())
315 void FrameTree::AddRenderViewHostRef(RenderViewHostImpl
* render_view_host
) {
316 SiteInstance
* site_instance
= render_view_host
->GetSiteInstance();
317 RenderViewHostMap::iterator iter
=
318 render_view_host_map_
.find(site_instance
->GetId());
319 CHECK(iter
!= render_view_host_map_
.end());
320 CHECK(iter
->second
== render_view_host
);
322 iter
->second
->increment_ref_count();
325 void FrameTree::ReleaseRenderViewHostRef(RenderViewHostImpl
* render_view_host
) {
326 SiteInstance
* site_instance
= render_view_host
->GetSiteInstance();
327 int32 site_instance_id
= site_instance
->GetId();
328 RenderViewHostMap::iterator iter
=
329 render_view_host_map_
.find(site_instance_id
);
330 if (iter
!= render_view_host_map_
.end() && iter
->second
== render_view_host
) {
331 // Decrement the refcount and shutdown the RenderViewHost if no one else is
333 CHECK_GT(iter
->second
->ref_count(), 0);
334 iter
->second
->decrement_ref_count();
335 if (iter
->second
->ref_count() == 0) {
336 iter
->second
->Shutdown();
337 render_view_host_map_
.erase(iter
);
340 // The RenderViewHost should be in the list of RenderViewHosts pending
342 bool render_view_host_found
= false;
343 std::pair
<RenderViewHostMultiMap::iterator
,
344 RenderViewHostMultiMap::iterator
> result
=
345 render_view_host_pending_shutdown_map_
.equal_range(site_instance_id
);
346 for (RenderViewHostMultiMap::iterator multi_iter
= result
.first
;
347 multi_iter
!= result
.second
;
349 if (multi_iter
->second
!= render_view_host
)
351 render_view_host_found
= true;
352 // Decrement the refcount and shutdown the RenderViewHost if no one else
354 CHECK_GT(render_view_host
->ref_count(), 0);
355 render_view_host
->decrement_ref_count();
356 if (render_view_host
->ref_count() == 0) {
357 render_view_host
->Shutdown();
358 render_view_host_pending_shutdown_map_
.erase(multi_iter
);
362 CHECK(render_view_host_found
);
366 void FrameTree::FrameRemoved(FrameTreeNode
* frame
) {
367 if (frame
->frame_tree_node_id() == focused_frame_tree_node_id_
)
368 focused_frame_tree_node_id_
= -1;
370 // No notification for the root frame.
371 if (!frame
->parent()) {
372 CHECK_EQ(frame
, root_
.get());
376 // Notify observers of the frame removal.
377 if (!on_frame_removed_
.is_null())
378 on_frame_removed_
.Run(frame
->current_frame_host());
381 void FrameTree::UpdateLoadProgress() {
382 double progress
= 0.0;
385 ForEach(base::Bind(&CollectLoadProgress
, &progress
, &frame_count
));
386 if (frame_count
!= 0)
387 progress
/= frame_count
;
389 if (progress
<= load_progress_
)
391 load_progress_
= progress
;
393 // Notify the WebContents.
394 root_
->navigator()->GetDelegate()->DidChangeLoadProgress();
397 void FrameTree::ResetLoadProgress() {
398 ForEach(base::Bind(&ResetNodeLoadProgress
));
399 load_progress_
= 0.0;
402 bool FrameTree::IsLoading() {
403 bool is_loading
= false;
404 ForEach(base::Bind(&IsNodeLoading
, &is_loading
));
408 } // namespace content