frame_tree.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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. 4 5#include "content/browser/frame_host/frame_tree.h" 6 7#include <queue> 8 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" 21 22namespace content { 23 24namespace { 25 26// This is a global map between frame_tree_node_ids and pointer to 27// FrameTreeNodes. 28typedef base::hash_map<int64, FrameTreeNode*> FrameTreeNodeIDMap; 29 30base::LazyInstance<FrameTreeNodeIDMap> g_frame_tree_node_id_map = 31 LAZY_INSTANCE_INITIALIZER; 32 33// Used with FrameTree::ForEach() to search for the FrameTreeNode 34// corresponding to |frame_tree_node_id| whithin a specific FrameTree. 35bool 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; 42 } 43 return true; 44} 45 46// Iterate over the FrameTree to reset any node affected by the loss of the 47// given RenderViewHost's process. 48bool ResetNodesForNewProcess(RenderViewHost* render_view_host, 49 FrameTreeNode* node) { 50 if (render_view_host == node->current_frame_host()->render_view_host()) 51 node->ResetForNewProcess(); 52 return true; 53} 54 55bool CreateProxyForSiteInstance(FrameTreeNode* source_node, 56 const scoped_refptr<SiteInstance>& instance, 57 FrameTreeNode* node) { 58 // Skip the node that initiated the creation. 59 if (source_node == node) 60 return true; 61 62 node->render_manager()->CreateRenderFrameProxy(instance.get()); 63 return true; 64} 65 66} // namespace 67 68FrameTree::FrameTree(Navigator* navigator, 69 RenderFrameHostDelegate* render_frame_delegate, 70 RenderViewHostDelegate* render_view_delegate, 71 RenderWidgetHostDelegate* render_widget_delegate, 72 RenderFrameHostManager::Delegate* manager_delegate) 73 : render_frame_delegate_(render_frame_delegate), 74 render_view_delegate_(render_view_delegate), 75 render_widget_delegate_(render_widget_delegate), 76 manager_delegate_(manager_delegate), 77 root_(new FrameTreeNode(this, 78 navigator, 79 render_frame_delegate, 80 render_view_delegate, 81 render_widget_delegate, 82 manager_delegate, 83 std::string())), 84 focused_frame_tree_node_id_(-1) { 85 std::pair<FrameTreeNodeIDMap::iterator, bool> result = 86 g_frame_tree_node_id_map.Get().insert( 87 std::make_pair(root_->frame_tree_node_id(), root_.get())); 88 CHECK(result.second); 89} 90 91FrameTree::~FrameTree() { 92 g_frame_tree_node_id_map.Get().erase(root_->frame_tree_node_id()); 93} 94 95// static 96FrameTreeNode* FrameTree::GloballyFindByID(int64 frame_tree_node_id) { 97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 98 FrameTreeNodeIDMap* nodes = g_frame_tree_node_id_map.Pointer(); 99 FrameTreeNodeIDMap::iterator it = nodes->find(frame_tree_node_id); 100 return it == nodes->end() ? NULL : it->second; 101} 102 103FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) { 104 FrameTreeNode* node = NULL; 105 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node)); 106 return node; 107} 108 109FrameTreeNode* FrameTree::FindByRoutingID(int routing_id, int process_id) { 110 RenderFrameHostImpl* render_frame_host = 111 RenderFrameHostImpl::FromID(process_id, routing_id); 112 if (render_frame_host) { 113 FrameTreeNode* result = render_frame_host->frame_tree_node(); 114 if (this == result->frame_tree()) 115 return result; 116 } 117 118 RenderFrameProxyHost* render_frame_proxy_host = 119 RenderFrameProxyHost::FromID(process_id, routing_id); 120 if (render_frame_proxy_host) { 121 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node(); 122 if (this == result->frame_tree()) 123 return result; 124 } 125 126 return NULL; 127} 128 129void FrameTree::ForEach( 130 const base::Callback<bool(FrameTreeNode*)>& on_node) const { 131 std::queue<FrameTreeNode*> queue; 132 queue.push(root_.get()); 133 134 while (!queue.empty()) { 135 FrameTreeNode* node = queue.front(); 136 queue.pop(); 137 if (!on_node.Run(node)) 138 break; 139 140 for (size_t i = 0; i < node->child_count(); ++i) 141 queue.push(node->child_at(i)); 142 } 143} 144 145RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent, 146 int new_routing_id, 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(); 159} 160 161void FrameTree::RemoveFrame(FrameTreeNode* child) { 162 FrameTreeNode* parent = child->parent(); 163 if (!parent) { 164 NOTREACHED() << "Unexpected RemoveFrame call for main frame."; 165 return; 166 } 167 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); 172 } 173 g_frame_tree_node_id_map.Get().erase(child->frame_tree_node_id()); 174 parent->RemoveChild(child); 175} 176 177void 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, 188 MSG_ROUTING_NONE, 189 true, 190 false, 191 true); 192 } 193 } 194 195 scoped_refptr<SiteInstance> instance(site_instance); 196 ForEach(base::Bind(&CreateProxyForSiteInstance, source, instance)); 197} 198 199void FrameTree::ResetForMainFrameSwap() { 200 root_->ResetForNewProcess(); 201 focused_frame_tree_node_id_ = -1; 202} 203 204void 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 210 // the node itself. 211 ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host)); 212} 213 214RenderFrameHostImpl* FrameTree::GetMainFrame() const { 215 return root_->current_frame_host(); 216} 217 218FrameTreeNode* FrameTree::GetFocusedFrame() { 219 return FindByID(focused_frame_tree_node_id_); 220} 221 222void FrameTree::SetFocusedFrame(FrameTreeNode* node) { 223 focused_frame_tree_node_id_ = node->frame_tree_node_id(); 224} 225 226void FrameTree::SetFrameRemoveListener( 227 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) { 228 on_frame_removed_ = on_frame_removed; 229} 230 231RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance, 232 int routing_id, 233 int main_frame_routing_id, 234 bool swapped_out, 235 bool hidden) { 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(), 247 iter->second)); 248 render_view_host_map_.erase(iter); 249 } else { 250 return iter->second; 251 } 252 } 253 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 254 RenderViewHostFactory::Create(site_instance, 255 render_view_delegate_, 256 render_widget_delegate_, 257 routing_id, 258 main_frame_routing_id, 259 swapped_out, 260 hidden)); 261 262 render_view_host_map_[site_instance->GetId()] = rvh; 263 return rvh; 264} 265 266RenderViewHostImpl* 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()) 271 return NULL; 272 return iter->second; 273} 274 275void 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()); 282 283 iter->second->increment_ref_count(); 284} 285 286void 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 296 // using it. 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); 302 } 303 } else { 304 // The RenderViewHost should be in the list of RenderViewHosts pending 305 // shutdown. 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; 312 ++multi_iter) { 313 if (multi_iter->second != render_frame_host->render_view_host()) 314 continue; 315 render_view_host_found = true; 316 RenderViewHostImpl* rvh = multi_iter->second; 317 // Decrement the refcount and shutdown the RenderViewHost if no one else 318 // is using it. 319 CHECK_GT(rvh->ref_count(), 0); 320 rvh->decrement_ref_count(); 321 if (rvh->ref_count() == 0) { 322 rvh->Shutdown(); 323 render_view_host_pending_shutdown_map_.erase(multi_iter); 324 } 325 break; 326 } 327 CHECK(render_view_host_found); 328 } 329} 330 331} // namespace content 332