cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blobb1ccf6f9c796d17b899eb5f90bbc2681508c5f93
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"
7 #include <queue>
9 #include "base/bind.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"
20 #include "content/public/browser/browser_thread.h"
22 namespace content {
24 namespace {
26 // This is a global map between frame_tree_node_ids and pointer to
27 // FrameTreeNodes.
28 typedef base::hash_map<int64, FrameTreeNode*> FrameTreeNodeIDMap;
30 base::LazyInstance<FrameTreeNodeIDMap> g_frame_tree_node_id_map =
31 LAZY_INSTANCE_INITIALIZER;
33 // Used with FrameTree::ForEach() to search for the FrameTreeNode
34 // corresponding to |frame_tree_node_id| whithin a specific FrameTree.
35 bool FrameTreeNodeForId(int64 frame_tree_node_id,
36 FrameTreeNode** out_node,
37 FrameTreeNode* node) {
38 if (node->frame_tree_node_id() == frame_tree_node_id) {
39 *out_node = node;
40 // Terminate iteration once the node has been found.
41 return false;
43 return true;
46 // Iterate over the FrameTree to reset any node affected by the loss of the
47 // given RenderViewHost's process.
48 bool ResetNodesForNewProcess(RenderViewHost* render_view_host,
49 FrameTreeNode* node) {
50 if (render_view_host == node->current_frame_host()->render_view_host()) {
51 // Ensure that if the frame host is reused for a new RenderFrame, it will
52 // set up the Mojo connection with that frame.
53 node->current_frame_host()->InvalidateMojoConnection();
54 node->ResetForNewProcess();
56 return true;
59 bool CreateProxyForSiteInstance(const scoped_refptr<SiteInstance>& instance,
60 FrameTreeNode* node) {
61 // If a new frame is created in the current SiteInstance, other frames in
62 // that SiteInstance don't need a proxy for the new frame.
63 SiteInstance* current_instance =
64 node->render_manager()->current_frame_host()->GetSiteInstance();
65 if (current_instance != instance.get())
66 node->render_manager()->CreateRenderFrameProxy(instance.get());
67 return true;
70 } // namespace
72 FrameTree::FrameTree(Navigator* navigator,
73 RenderFrameHostDelegate* render_frame_delegate,
74 RenderViewHostDelegate* render_view_delegate,
75 RenderWidgetHostDelegate* render_widget_delegate,
76 RenderFrameHostManager::Delegate* manager_delegate)
77 : render_frame_delegate_(render_frame_delegate),
78 render_view_delegate_(render_view_delegate),
79 render_widget_delegate_(render_widget_delegate),
80 manager_delegate_(manager_delegate),
81 root_(new FrameTreeNode(this,
82 navigator,
83 render_frame_delegate,
84 render_view_delegate,
85 render_widget_delegate,
86 manager_delegate,
87 std::string())),
88 focused_frame_tree_node_id_(-1) {
89 std::pair<FrameTreeNodeIDMap::iterator, bool> result =
90 g_frame_tree_node_id_map.Get().insert(
91 std::make_pair(root_->frame_tree_node_id(), root_.get()));
92 CHECK(result.second);
95 FrameTree::~FrameTree() {
96 g_frame_tree_node_id_map.Get().erase(root_->frame_tree_node_id());
99 // static
100 FrameTreeNode* FrameTree::GloballyFindByID(int64 frame_tree_node_id) {
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102 FrameTreeNodeIDMap* nodes = g_frame_tree_node_id_map.Pointer();
103 FrameTreeNodeIDMap::iterator it = nodes->find(frame_tree_node_id);
104 return it == nodes->end() ? NULL : it->second;
107 FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) {
108 FrameTreeNode* node = NULL;
109 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node));
110 return node;
113 FrameTreeNode* FrameTree::FindByRoutingID(int routing_id, int process_id) {
114 RenderFrameHostImpl* render_frame_host =
115 RenderFrameHostImpl::FromID(process_id, routing_id);
116 if (render_frame_host) {
117 FrameTreeNode* result = render_frame_host->frame_tree_node();
118 if (this == result->frame_tree())
119 return result;
122 RenderFrameProxyHost* render_frame_proxy_host =
123 RenderFrameProxyHost::FromID(process_id, routing_id);
124 if (render_frame_proxy_host) {
125 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node();
126 if (this == result->frame_tree())
127 return result;
130 return NULL;
133 void FrameTree::ForEach(
134 const base::Callback<bool(FrameTreeNode*)>& on_node) const {
135 ForEach(on_node, NULL);
138 void FrameTree::ForEach(
139 const base::Callback<bool(FrameTreeNode*)>& on_node,
140 FrameTreeNode* skip_this_subtree) const {
141 std::queue<FrameTreeNode*> queue;
142 queue.push(root_.get());
144 while (!queue.empty()) {
145 FrameTreeNode* node = queue.front();
146 queue.pop();
147 if (skip_this_subtree == node)
148 continue;
150 if (!on_node.Run(node))
151 break;
153 for (size_t i = 0; i < node->child_count(); ++i)
154 queue.push(node->child_at(i));
158 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
159 int process_id,
160 int new_routing_id,
161 const std::string& frame_name) {
162 // A child frame always starts with an initial empty document, which means
163 // it is in the same SiteInstance as the parent frame. Ensure that the process
164 // which requested a child frame to be added is the same as the process of the
165 // parent node.
166 if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
167 return nullptr;
169 scoped_ptr<FrameTreeNode> node(new FrameTreeNode(
170 this, parent->navigator(), render_frame_delegate_, render_view_delegate_,
171 render_widget_delegate_, manager_delegate_, frame_name));
172 std::pair<FrameTreeNodeIDMap::iterator, bool> result =
173 g_frame_tree_node_id_map.Get().insert(
174 std::make_pair(node->frame_tree_node_id(), node.get()));
175 CHECK(result.second);
176 FrameTreeNode* node_ptr = node.get();
177 // AddChild is what creates the RenderFrameHost.
178 parent->AddChild(node.Pass(), process_id, new_routing_id);
179 return node_ptr->current_frame_host();
182 void FrameTree::RemoveFrame(FrameTreeNode* child) {
183 FrameTreeNode* parent = child->parent();
184 if (!parent) {
185 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
186 return;
189 // Notify observers of the frame removal.
190 RenderFrameHostImpl* render_frame_host = child->current_frame_host();
191 if (!on_frame_removed_.is_null()) {
192 on_frame_removed_.Run(render_frame_host);
194 g_frame_tree_node_id_map.Get().erase(child->frame_tree_node_id());
195 parent->RemoveChild(child);
198 void FrameTree::CreateProxiesForSiteInstance(
199 FrameTreeNode* source,
200 SiteInstance* site_instance) {
201 // Create the swapped out RVH for the new SiteInstance. This will create
202 // a top-level swapped out RFH as well, which will then be wrapped by a
203 // RenderFrameProxyHost.
204 if (!source->IsMainFrame()) {
205 RenderViewHostImpl* render_view_host =
206 source->frame_tree()->GetRenderViewHost(site_instance);
207 if (!render_view_host) {
208 root()->render_manager()->CreateRenderFrame(site_instance,
209 MSG_ROUTING_NONE,
210 true,
211 false,
212 true);
216 scoped_refptr<SiteInstance> instance(site_instance);
218 // Proxies are created in the FrameTree in response to a node navigating to a
219 // new SiteInstance. Since |source|'s navigation will replace the currently
220 // loaded document, the entire subtree under |source| will be removed.
221 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
224 void FrameTree::ResetForMainFrameSwap() {
225 root_->ResetForNewProcess();
226 focused_frame_tree_node_id_ = -1;
229 void FrameTree::RenderProcessGone(RenderViewHost* render_view_host) {
230 // Walk the full tree looking for nodes that may be affected. Once a frame
231 // crashes, all of its child FrameTreeNodes go away.
232 // Note that the helper function may call ResetForNewProcess on a node, which
233 // clears its children before we iterate over them. That's ok, because
234 // ForEach does not add a node's children to the queue until after visiting
235 // the node itself.
236 ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host));
239 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
240 return root_->current_frame_host();
243 FrameTreeNode* FrameTree::GetFocusedFrame() {
244 return FindByID(focused_frame_tree_node_id_);
247 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
248 focused_frame_tree_node_id_ = node->frame_tree_node_id();
251 void FrameTree::SetFrameRemoveListener(
252 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
253 on_frame_removed_ = on_frame_removed;
256 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
257 int routing_id,
258 int main_frame_routing_id,
259 bool swapped_out,
260 bool hidden) {
261 DCHECK(main_frame_routing_id != MSG_ROUTING_NONE);
262 RenderViewHostMap::iterator iter =
263 render_view_host_map_.find(site_instance->GetId());
264 if (iter != render_view_host_map_.end()) {
265 // If a RenderViewHost's main frame is pending deletion for this
266 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
267 // Otherwise return the existing RenderViewHost for the SiteInstance.
268 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
269 iter->second->GetMainFrame());
270 if (main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
271 main_frame)) {
272 render_view_host_pending_shutdown_map_.insert(
273 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
274 iter->second));
275 render_view_host_map_.erase(iter);
276 } else {
277 return iter->second;
280 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
281 RenderViewHostFactory::Create(site_instance,
282 render_view_delegate_,
283 render_widget_delegate_,
284 routing_id,
285 main_frame_routing_id,
286 swapped_out,
287 hidden));
289 render_view_host_map_[site_instance->GetId()] = rvh;
290 return rvh;
293 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
294 RenderViewHostMap::iterator iter =
295 render_view_host_map_.find(site_instance->GetId());
296 // TODO(creis): Mirror the frame tree so this check can't fail.
297 if (iter == render_view_host_map_.end())
298 return NULL;
299 return iter->second;
302 void FrameTree::RegisterRenderFrameHost(
303 RenderFrameHostImpl* render_frame_host) {
304 SiteInstance* site_instance =
305 render_frame_host->render_view_host()->GetSiteInstance();
306 RenderViewHostMap::iterator iter =
307 render_view_host_map_.find(site_instance->GetId());
308 CHECK(iter != render_view_host_map_.end());
310 iter->second->increment_ref_count();
313 void FrameTree::UnregisterRenderFrameHost(
314 RenderFrameHostImpl* render_frame_host) {
315 SiteInstance* site_instance =
316 render_frame_host->render_view_host()->GetSiteInstance();
317 int32 site_instance_id = site_instance->GetId();
318 RenderViewHostMap::iterator iter =
319 render_view_host_map_.find(site_instance_id);
320 if (iter != render_view_host_map_.end() &&
321 iter->second == render_frame_host->render_view_host()) {
322 // Decrement the refcount and shutdown the RenderViewHost if no one else is
323 // using it.
324 CHECK_GT(iter->second->ref_count(), 0);
325 iter->second->decrement_ref_count();
326 if (iter->second->ref_count() == 0) {
327 iter->second->Shutdown();
328 render_view_host_map_.erase(iter);
330 } else {
331 // The RenderViewHost should be in the list of RenderViewHosts pending
332 // shutdown.
333 bool render_view_host_found = false;
334 std::pair<RenderViewHostMultiMap::iterator,
335 RenderViewHostMultiMap::iterator> result =
336 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
337 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
338 multi_iter != result.second;
339 ++multi_iter) {
340 if (multi_iter->second != render_frame_host->render_view_host())
341 continue;
342 render_view_host_found = true;
343 RenderViewHostImpl* rvh = multi_iter->second;
344 // Decrement the refcount and shutdown the RenderViewHost if no one else
345 // is using it.
346 CHECK_GT(rvh->ref_count(), 0);
347 rvh->decrement_ref_count();
348 if (rvh->ref_count() == 0) {
349 rvh->Shutdown();
350 render_view_host_pending_shutdown_map_.erase(multi_iter);
352 break;
354 CHECK(render_view_host_found);
358 } // namespace content