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 22 namespace content { 23 24 namespace { 25 26 // This is a global map between frame_tree_node_ids and pointer to 27 // FrameTreeNodes. 28 typedef base::hash_map<int64, FrameTreeNode*> FrameTreeNodeIDMap; 29 30 base::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. 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; 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. 48 bool 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 55 bool 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 68 FrameTree::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 91 FrameTree::~FrameTree() { 92 g_frame_tree_node_id_map.Get().erase(root_->frame_tree_node_id()); 93 } 94 95 // static 96 FrameTreeNode* 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 103 FrameTreeNode* 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 109 FrameTreeNode* 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 129 void 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 145 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent, 146 int process_id, 147 int new_routing_id, 148 const std::string& frame_name) { 149 // A child frame always starts with an initial empty document, which means 150 // it is in the same SiteInstance as the parent frame. Ensure that the process 151 // which requested a child frame to be added is the same as the process of the 152 // parent node. 153 if (parent->current_frame_host()->GetProcess()->GetID() != process_id) 154 return nullptr; 155 156 scoped_ptr<FrameTreeNode> node(new FrameTreeNode( 157 this, parent->navigator(), render_frame_delegate_, render_view_delegate_, 158 render_widget_delegate_, manager_delegate_, frame_name)); 159 std::pair<FrameTreeNodeIDMap::iterator, bool> result = 160 g_frame_tree_node_id_map.Get().insert( 161 std::make_pair(node->frame_tree_node_id(), node.get())); 162 CHECK(result.second); 163 FrameTreeNode* node_ptr = node.get(); 164 // AddChild is what creates the RenderFrameHost. 165 parent->AddChild(node.Pass(), process_id, new_routing_id); 166 return node_ptr->current_frame_host(); 167 } 168 169 void FrameTree::RemoveFrame(FrameTreeNode* child) { 170 FrameTreeNode* parent = child->parent(); 171 if (!parent) { 172 NOTREACHED() << "Unexpected RemoveFrame call for main frame."; 173 return; 174 } 175 176 // Notify observers of the frame removal. 177 RenderFrameHostImpl* render_frame_host = child->current_frame_host(); 178 if (!on_frame_removed_.is_null()) { 179 on_frame_removed_.Run(render_frame_host); 180 } 181 g_frame_tree_node_id_map.Get().erase(child->frame_tree_node_id()); 182 parent->RemoveChild(child); 183 } 184 185 void FrameTree::CreateProxiesForSiteInstance( 186 FrameTreeNode* source, 187 SiteInstance* site_instance) { 188 // Create the swapped out RVH for the new SiteInstance. This will create 189 // a top-level swapped out RFH as well, which will then be wrapped by a 190 // RenderFrameProxyHost. 191 if (!source->IsMainFrame()) { 192 RenderViewHostImpl* render_view_host = 193 source->frame_tree()->GetRenderViewHost(site_instance); 194 if (!render_view_host) { 195 root()->render_manager()->CreateRenderFrame(site_instance, 196 MSG_ROUTING_NONE, 197 true, 198 false, 199 true); 200 } 201 } 202 203 scoped_refptr<SiteInstance> instance(site_instance); 204 ForEach(base::Bind(&CreateProxyForSiteInstance, source, instance)); 205 } 206 207 void FrameTree::ResetForMainFrameSwap() { 208 root_->ResetForNewProcess(); 209 focused_frame_tree_node_id_ = -1; 210 } 211 212 void FrameTree::RenderProcessGone(RenderViewHost* render_view_host) { 213 // Walk the full tree looking for nodes that may be affected. Once a frame 214 // crashes, all of its child FrameTreeNodes go away. 215 // Note that the helper function may call ResetForNewProcess on a node, which 216 // clears its children before we iterate over them. That's ok, because 217 // ForEach does not add a node's children to the queue until after visiting 218 // the node itself. 219 ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host)); 220 } 221 222 RenderFrameHostImpl* FrameTree::GetMainFrame() const { 223 return root_->current_frame_host(); 224 } 225 226 FrameTreeNode* FrameTree::GetFocusedFrame() { 227 return FindByID(focused_frame_tree_node_id_); 228 } 229 230 void FrameTree::SetFocusedFrame(FrameTreeNode* node) { 231 focused_frame_tree_node_id_ = node->frame_tree_node_id(); 232 } 233 234 void FrameTree::SetFrameRemoveListener( 235 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) { 236 on_frame_removed_ = on_frame_removed; 237 } 238 239 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance, 240 int routing_id, 241 int main_frame_routing_id, 242 bool swapped_out, 243 bool hidden) { 244 DCHECK(main_frame_routing_id != MSG_ROUTING_NONE); 245 RenderViewHostMap::iterator iter = 246 render_view_host_map_.find(site_instance->GetId()); 247 if (iter != render_view_host_map_.end()) { 248 // If a RenderViewHost is pending shutdown for this |site_instance|, put it 249 // in the map of RenderViewHosts pending shutdown. Otherwise return the 250 // existing RenderViewHost for the SiteInstance. 251 if (iter->second->rvh_state() == 252 RenderViewHostImpl::STATE_PENDING_SHUTDOWN) { 253 render_view_host_pending_shutdown_map_.insert( 254 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(), 255 iter->second)); 256 render_view_host_map_.erase(iter); 257 } else { 258 return iter->second; 259 } 260 } 261 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 262 RenderViewHostFactory::Create(site_instance, 263 render_view_delegate_, 264 render_widget_delegate_, 265 routing_id, 266 main_frame_routing_id, 267 swapped_out, 268 hidden)); 269 270 render_view_host_map_[site_instance->GetId()] = rvh; 271 return rvh; 272 } 273 274 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) { 275 RenderViewHostMap::iterator iter = 276 render_view_host_map_.find(site_instance->GetId()); 277 // TODO(creis): Mirror the frame tree so this check can't fail. 278 if (iter == render_view_host_map_.end()) 279 return NULL; 280 return iter->second; 281 } 282 283 void FrameTree::RegisterRenderFrameHost( 284 RenderFrameHostImpl* render_frame_host) { 285 SiteInstance* site_instance = 286 render_frame_host->render_view_host()->GetSiteInstance(); 287 RenderViewHostMap::iterator iter = 288 render_view_host_map_.find(site_instance->GetId()); 289 CHECK(iter != render_view_host_map_.end()); 290 291 iter->second->increment_ref_count(); 292 } 293 294 void FrameTree::UnregisterRenderFrameHost( 295 RenderFrameHostImpl* render_frame_host) { 296 SiteInstance* site_instance = 297 render_frame_host->render_view_host()->GetSiteInstance(); 298 int32 site_instance_id = site_instance->GetId(); 299 RenderViewHostMap::iterator iter = 300 render_view_host_map_.find(site_instance_id); 301 if (iter != render_view_host_map_.end() && 302 iter->second == render_frame_host->render_view_host()) { 303 // Decrement the refcount and shutdown the RenderViewHost if no one else is 304 // using it. 305 CHECK_GT(iter->second->ref_count(), 0); 306 iter->second->decrement_ref_count(); 307 if (iter->second->ref_count() == 0) { 308 iter->second->Shutdown(); 309 render_view_host_map_.erase(iter); 310 } 311 } else { 312 // The RenderViewHost should be in the list of RenderViewHosts pending 313 // shutdown. 314 bool render_view_host_found = false; 315 std::pair<RenderViewHostMultiMap::iterator, 316 RenderViewHostMultiMap::iterator> result = 317 render_view_host_pending_shutdown_map_.equal_range(site_instance_id); 318 for (RenderViewHostMultiMap::iterator multi_iter = result.first; 319 multi_iter != result.second; 320 ++multi_iter) { 321 if (multi_iter->second != render_frame_host->render_view_host()) 322 continue; 323 render_view_host_found = true; 324 RenderViewHostImpl* rvh = multi_iter->second; 325 // Decrement the refcount and shutdown the RenderViewHost if no one else 326 // is using it. 327 CHECK_GT(rvh->ref_count(), 0); 328 rvh->decrement_ref_count(); 329 if (rvh->ref_count() == 0) { 330 rvh->Shutdown(); 331 render_view_host_pending_shutdown_map_.erase(multi_iter); 332 } 333 break; 334 } 335 CHECK(render_view_host_found); 336 } 337 } 338 339 } // namespace content 340