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/renderer_host/render_view_host_factory.h"
18 #include "content/browser/renderer_host/render_view_host_impl.h"
19 #include "content/public/browser/browser_thread.h"
25 // This is a global map between frame_tree_node_ids and pointer to
27 typedef base::hash_map
<int64
, FrameTreeNode
*> FrameTreeNodeIDMap
;
29 base::LazyInstance
<FrameTreeNodeIDMap
> g_frame_tree_node_id_map
=
30 LAZY_INSTANCE_INITIALIZER
;
32 // Used with FrameTree::ForEach() to search for the FrameTreeNode
33 // corresponding to |frame_tree_node_id| whithin a specific FrameTree.
34 bool FrameTreeNodeForId(int64 frame_tree_node_id
,
35 FrameTreeNode
** out_node
,
36 FrameTreeNode
* node
) {
37 if (node
->frame_tree_node_id() == frame_tree_node_id
) {
39 // Terminate iteration once the node has been found.
45 bool FrameTreeNodeForRoutingId(int routing_id
,
47 FrameTreeNode
** out_node
,
48 FrameTreeNode
* node
) {
49 // TODO(creis): Look through the swapped out RFHs as well.
50 if (node
->current_frame_host()->GetProcess()->GetID() == process_id
&&
51 node
->current_frame_host()->GetRoutingID() == routing_id
) {
53 // Terminate iteration once the node has been found.
59 // Iterate over the FrameTree to reset any node affected by the loss of the
60 // given RenderViewHost's process.
61 bool ResetNodesForNewProcess(RenderViewHost
* render_view_host
,
62 FrameTreeNode
* node
) {
63 if (render_view_host
== node
->current_frame_host()->render_view_host())
64 node
->ResetForNewProcess();
68 bool CreateProxyForSiteInstance(FrameTreeNode
* source_node
,
69 const scoped_refptr
<SiteInstance
>& instance
,
70 FrameTreeNode
* node
) {
71 // Skip the node that initiated the creation.
72 if (source_node
== node
)
75 node
->render_manager()->CreateRenderFrameProxy(instance
.get());
81 FrameTree::FrameTree(Navigator
* navigator
,
82 RenderFrameHostDelegate
* render_frame_delegate
,
83 RenderViewHostDelegate
* render_view_delegate
,
84 RenderWidgetHostDelegate
* render_widget_delegate
,
85 RenderFrameHostManager::Delegate
* manager_delegate
)
86 : render_frame_delegate_(render_frame_delegate
),
87 render_view_delegate_(render_view_delegate
),
88 render_widget_delegate_(render_widget_delegate
),
89 manager_delegate_(manager_delegate
),
90 root_(new FrameTreeNode(this,
92 render_frame_delegate
,
94 render_widget_delegate
,
97 focused_frame_tree_node_id_(-1) {
98 std::pair
<FrameTreeNodeIDMap::iterator
, bool> result
=
99 g_frame_tree_node_id_map
.Get().insert(
100 std::make_pair(root_
->frame_tree_node_id(), root_
.get()));
101 CHECK(result
.second
);
104 FrameTree::~FrameTree() {
105 g_frame_tree_node_id_map
.Get().erase(root_
->frame_tree_node_id());
109 FrameTreeNode
* FrameTree::GloballyFindByID(int64 frame_tree_node_id
) {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
111 FrameTreeNodeIDMap
* nodes
= g_frame_tree_node_id_map
.Pointer();
112 FrameTreeNodeIDMap::iterator it
= nodes
->find(frame_tree_node_id
);
113 return it
== nodes
->end() ? NULL
: it
->second
;
116 FrameTreeNode
* FrameTree::FindByID(int64 frame_tree_node_id
) {
117 FrameTreeNode
* node
= NULL
;
118 ForEach(base::Bind(&FrameTreeNodeForId
, frame_tree_node_id
, &node
));
122 FrameTreeNode
* FrameTree::FindByRoutingID(int routing_id
, int process_id
) {
123 FrameTreeNode
* node
= NULL
;
125 base::Bind(&FrameTreeNodeForRoutingId
, routing_id
, process_id
, &node
));
129 void FrameTree::ForEach(
130 const base::Callback
<bool(FrameTreeNode
*)>& on_node
) const {
131 std::queue
<FrameTreeNode
*> queue
;
132 queue
.push(root_
.get());
134 while (!queue
.empty()) {
135 FrameTreeNode
* node
= queue
.front();
137 if (!on_node
.Run(node
))
140 for (size_t i
= 0; i
< node
->child_count(); ++i
)
141 queue
.push(node
->child_at(i
));
145 RenderFrameHostImpl
* FrameTree::AddFrame(FrameTreeNode
* parent
,
147 const std::string
& frame_name
) {
148 scoped_ptr
<FrameTreeNode
> node(new FrameTreeNode(
149 this, parent
->navigator(), render_frame_delegate_
, render_view_delegate_
,
150 render_widget_delegate_
, manager_delegate_
, frame_name
));
151 std::pair
<FrameTreeNodeIDMap::iterator
, bool> result
=
152 g_frame_tree_node_id_map
.Get().insert(
153 std::make_pair(node
->frame_tree_node_id(), node
.get()));
154 CHECK(result
.second
);
155 FrameTreeNode
* node_ptr
= node
.get();
156 // AddChild is what creates the RenderFrameHost.
157 parent
->AddChild(node
.Pass(), new_routing_id
);
158 return node_ptr
->current_frame_host();
161 void FrameTree::RemoveFrame(FrameTreeNode
* child
) {
162 FrameTreeNode
* parent
= child
->parent();
164 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
168 // Notify observers of the frame removal.
169 RenderFrameHostImpl
* render_frame_host
= child
->current_frame_host();
170 if (!on_frame_removed_
.is_null()) {
171 on_frame_removed_
.Run(render_frame_host
);
173 g_frame_tree_node_id_map
.Get().erase(child
->frame_tree_node_id());
174 parent
->RemoveChild(child
);
177 void FrameTree::CreateProxiesForSiteInstance(
178 FrameTreeNode
* source
,
179 SiteInstance
* site_instance
) {
180 // Create the swapped out RVH for the new SiteInstance. This will create
181 // a top-level swapped out RFH as well, which will then be wrapped by a
182 // RenderFrameProxyHost.
183 if (!source
->IsMainFrame()) {
184 RenderViewHostImpl
* render_view_host
=
185 source
->frame_tree()->GetRenderViewHost(site_instance
);
186 if (!render_view_host
) {
187 root()->render_manager()->CreateRenderFrame(site_instance
,
195 scoped_refptr
<SiteInstance
> instance(site_instance
);
196 ForEach(base::Bind(&CreateProxyForSiteInstance
, source
, instance
));
199 void FrameTree::ResetForMainFrameSwap() {
200 root_
->ResetForNewProcess();
201 focused_frame_tree_node_id_
= -1;
204 void FrameTree::RenderProcessGone(RenderViewHost
* render_view_host
) {
205 // Walk the full tree looking for nodes that may be affected. Once a frame
206 // crashes, all of its child FrameTreeNodes go away.
207 // Note that the helper function may call ResetForNewProcess on a node, which
208 // clears its children before we iterate over them. That's ok, because
209 // ForEach does not add a node's children to the queue until after visiting
211 ForEach(base::Bind(&ResetNodesForNewProcess
, render_view_host
));
214 RenderFrameHostImpl
* FrameTree::GetMainFrame() const {
215 return root_
->current_frame_host();
218 FrameTreeNode
* FrameTree::GetFocusedFrame() {
219 return FindByID(focused_frame_tree_node_id_
);
222 void FrameTree::SetFocusedFrame(FrameTreeNode
* node
) {
223 focused_frame_tree_node_id_
= node
->frame_tree_node_id();
226 void FrameTree::SetFrameRemoveListener(
227 const base::Callback
<void(RenderFrameHost
*)>& on_frame_removed
) {
228 on_frame_removed_
= on_frame_removed
;
231 RenderViewHostImpl
* FrameTree::CreateRenderViewHost(SiteInstance
* site_instance
,
233 int main_frame_routing_id
,
236 DCHECK(main_frame_routing_id
!= MSG_ROUTING_NONE
);
237 RenderViewHostMap::iterator iter
=
238 render_view_host_map_
.find(site_instance
->GetId());
239 if (iter
!= render_view_host_map_
.end()) {
240 // If a RenderViewHost is pending shutdown for this |site_instance|, put it
241 // in the map of RenderViewHosts pending shutdown. Otherwise return the
242 // existing RenderViewHost for the SiteInstance.
243 if (iter
->second
->rvh_state() ==
244 RenderViewHostImpl::STATE_PENDING_SHUTDOWN
) {
245 render_view_host_pending_shutdown_map_
.insert(
246 std::pair
<int, RenderViewHostImpl
*>(site_instance
->GetId(),
248 render_view_host_map_
.erase(iter
);
253 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
254 RenderViewHostFactory::Create(site_instance
,
255 render_view_delegate_
,
256 render_widget_delegate_
,
258 main_frame_routing_id
,
262 render_view_host_map_
[site_instance
->GetId()] = rvh
;
266 RenderViewHostImpl
* FrameTree::GetRenderViewHost(SiteInstance
* site_instance
) {
267 RenderViewHostMap::iterator iter
=
268 render_view_host_map_
.find(site_instance
->GetId());
269 // TODO(creis): Mirror the frame tree so this check can't fail.
270 if (iter
== render_view_host_map_
.end())
275 void FrameTree::RegisterRenderFrameHost(
276 RenderFrameHostImpl
* render_frame_host
) {
277 SiteInstance
* site_instance
=
278 render_frame_host
->render_view_host()->GetSiteInstance();
279 RenderViewHostMap::iterator iter
=
280 render_view_host_map_
.find(site_instance
->GetId());
281 CHECK(iter
!= render_view_host_map_
.end());
283 iter
->second
->increment_ref_count();
286 void FrameTree::UnregisterRenderFrameHost(
287 RenderFrameHostImpl
* render_frame_host
) {
288 SiteInstance
* site_instance
=
289 render_frame_host
->render_view_host()->GetSiteInstance();
290 int32 site_instance_id
= site_instance
->GetId();
291 RenderViewHostMap::iterator iter
=
292 render_view_host_map_
.find(site_instance_id
);
293 if (iter
!= render_view_host_map_
.end() &&
294 iter
->second
== render_frame_host
->render_view_host()) {
295 // Decrement the refcount and shutdown the RenderViewHost if no one else is
297 CHECK_GT(iter
->second
->ref_count(), 0);
298 iter
->second
->decrement_ref_count();
299 if (iter
->second
->ref_count() == 0) {
300 iter
->second
->Shutdown();
301 render_view_host_map_
.erase(iter
);
304 // The RenderViewHost should be in the list of RenderViewHosts pending
306 bool render_view_host_found
= false;
307 std::pair
<RenderViewHostMultiMap::iterator
,
308 RenderViewHostMultiMap::iterator
> result
=
309 render_view_host_pending_shutdown_map_
.equal_range(site_instance_id
);
310 for (RenderViewHostMultiMap::iterator multi_iter
= result
.first
;
311 multi_iter
!= result
.second
;
313 if (multi_iter
->second
!= render_frame_host
->render_view_host())
315 render_view_host_found
= true;
316 RenderViewHostImpl
* rvh
= multi_iter
->second
;
317 // Decrement the refcount and shutdown the RenderViewHost if no one else
319 CHECK_GT(rvh
->ref_count(), 0);
320 rvh
->decrement_ref_count();
321 if (rvh
->ref_count() == 0) {
323 render_view_host_pending_shutdown_map_
.erase(multi_iter
);
327 CHECK(render_view_host_found
);
331 } // namespace content