frame_tree.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "content/browser/frame_host/frame_tree_node.h" 12#include "content/browser/frame_host/navigator.h" 13#include "content/browser/frame_host/render_frame_host_factory.h" 14#include "content/browser/frame_host/render_frame_host_impl.h" 15#include "content/browser/renderer_host/render_view_host_factory.h" 16#include "content/browser/renderer_host/render_view_host_impl.h" 17 18namespace content { 19 20namespace { 21// Used with FrameTree::ForEach() to search for the FrameTreeNode 22// corresponding to |frame_tree_node_id|. 23bool FrameTreeNodeForId(int64 frame_tree_node_id, 24 FrameTreeNode** out_node, 25 FrameTreeNode* node) { 26 if (node->frame_tree_node_id() == frame_tree_node_id) { 27 *out_node = node; 28 // Terminate iteration once the node has been found. 29 return false; 30 } 31 return true; 32} 33 34bool FrameTreeNodeForRoutingId(int routing_id, 35 int process_id, 36 FrameTreeNode** out_node, 37 FrameTreeNode* node) { 38 // TODO(creis): Look through the swapped out RFHs as well. 39 if (node->current_frame_host()->GetProcess()->GetID() == process_id && 40 node->current_frame_host()->GetRoutingID() == routing_id) { 41 *out_node = node; 42 // Terminate iteration once the node has been found. 43 return false; 44 } 45 return true; 46} 47 48// Iterate over the FrameTree to reset any node affected by the loss of the 49// given RenderViewHost's process. 50bool ResetNodesForNewProcess(RenderViewHost* render_view_host, 51 FrameTreeNode* node) { 52 if (render_view_host == node->current_frame_host()->render_view_host()) 53 node->ResetForNewProcess(); 54 return true; 55} 56 57} // namespace 58 59FrameTree::FrameTree(Navigator* navigator, 60 RenderFrameHostDelegate* render_frame_delegate, 61 RenderViewHostDelegate* render_view_delegate, 62 RenderWidgetHostDelegate* render_widget_delegate, 63 RenderFrameHostManager::Delegate* manager_delegate) 64 : render_frame_delegate_(render_frame_delegate), 65 render_view_delegate_(render_view_delegate), 66 render_widget_delegate_(render_widget_delegate), 67 manager_delegate_(manager_delegate), 68 root_(new FrameTreeNode(this, 69 navigator, 70 render_frame_delegate, 71 render_view_delegate, 72 render_widget_delegate, 73 manager_delegate, 74 std::string())), 75 focused_frame_tree_node_id_(-1) { 76} 77 78FrameTree::~FrameTree() { 79} 80 81FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) { 82 FrameTreeNode* node = NULL; 83 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node)); 84 return node; 85} 86 87FrameTreeNode* FrameTree::FindByRoutingID(int routing_id, int process_id) { 88 FrameTreeNode* node = NULL; 89 ForEach( 90 base::Bind(&FrameTreeNodeForRoutingId, routing_id, process_id, &node)); 91 return node; 92} 93 94void FrameTree::ForEach( 95 const base::Callback<bool(FrameTreeNode*)>& on_node) const { 96 std::queue<FrameTreeNode*> queue; 97 queue.push(root_.get()); 98 99 while (!queue.empty()) { 100 FrameTreeNode* node = queue.front(); 101 queue.pop(); 102 if (!on_node.Run(node)) 103 break; 104 105 for (size_t i = 0; i < node->child_count(); ++i) 106 queue.push(node->child_at(i)); 107 } 108} 109 110RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent, 111 int new_routing_id, 112 const std::string& frame_name) { 113 scoped_ptr<FrameTreeNode> node(new FrameTreeNode( 114 this, parent->navigator(), render_frame_delegate_, render_view_delegate_, 115 render_widget_delegate_, manager_delegate_, frame_name)); 116 FrameTreeNode* node_ptr = node.get(); 117 // AddChild is what creates the RenderFrameHost. 118 parent->AddChild(node.Pass(), new_routing_id); 119 return node_ptr->current_frame_host(); 120} 121 122void FrameTree::RemoveFrame(FrameTreeNode* child) { 123 FrameTreeNode* parent = child->parent(); 124 if (!parent) { 125 NOTREACHED() << "Unexpected RemoveFrame call for main frame."; 126 return; 127 } 128 129 // Notify observers of the frame removal. 130 RenderFrameHostImpl* render_frame_host = child->current_frame_host(); 131 if (!on_frame_removed_.is_null()) { 132 on_frame_removed_.Run( 133 render_frame_host->render_view_host(), 134 render_frame_host->GetRoutingID()); 135 } 136 137 parent->RemoveChild(child); 138} 139 140void FrameTree::ResetForMainFrameSwap() { 141 root_->ResetForNewProcess(); 142 focused_frame_tree_node_id_ = -1; 143} 144 145void FrameTree::RenderProcessGone(RenderViewHost* render_view_host) { 146 // Walk the full tree looking for nodes that may be affected. Once a frame 147 // crashes, all of its child FrameTreeNodes go away. 148 // Note that the helper function may call ResetForNewProcess on a node, which 149 // clears its children before we iterate over them. That's ok, because 150 // ForEach does not add a node's children to the queue until after visiting 151 // the node itself. 152 ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host)); 153} 154 155RenderFrameHostImpl* FrameTree::GetMainFrame() const { 156 return root_->current_frame_host(); 157} 158 159FrameTreeNode* FrameTree::GetFocusedFrame() { 160 return FindByID(focused_frame_tree_node_id_); 161} 162 163void FrameTree::SetFocusedFrame(FrameTreeNode* node) { 164 focused_frame_tree_node_id_ = node->frame_tree_node_id(); 165} 166 167void FrameTree::SetFrameRemoveListener( 168 const base::Callback<void(RenderViewHostImpl*, int)>& on_frame_removed) { 169 on_frame_removed_ = on_frame_removed; 170} 171 172RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame( 173 SiteInstance* site_instance, 174 int routing_id, 175 int main_frame_routing_id, 176 bool swapped_out, 177 bool hidden) { 178 DCHECK(main_frame_routing_id != MSG_ROUTING_NONE); 179 RenderViewHostMap::iterator iter = 180 render_view_host_map_.find(site_instance->GetId()); 181 if (iter != render_view_host_map_.end()) { 182 // If a RenderViewHost is pending shutdown for this |site_instance|, put it 183 // in the map of RenderViewHosts pending shutdown. Otherwise there should 184 // not be a RenderViewHost for the SiteInstance. 185 CHECK_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, 186 iter->second->rvh_state()); 187 render_view_host_pending_shutdown_map_.insert( 188 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(), 189 iter->second)); 190 render_view_host_map_.erase(iter); 191 } 192 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 193 RenderViewHostFactory::Create(site_instance, 194 render_view_delegate_, 195 render_widget_delegate_, 196 routing_id, 197 main_frame_routing_id, 198 swapped_out, 199 hidden)); 200 201 render_view_host_map_[site_instance->GetId()] = rvh; 202 return rvh; 203} 204 205RenderViewHostImpl* FrameTree::GetRenderViewHostForSubFrame( 206 SiteInstance* site_instance) { 207 RenderViewHostMap::iterator iter = 208 render_view_host_map_.find(site_instance->GetId()); 209 // TODO(creis): Mirror the frame tree so this check can't fail. 210 if (iter == render_view_host_map_.end()) 211 return NULL; 212 return iter->second; 213} 214 215void FrameTree::RegisterRenderFrameHost( 216 RenderFrameHostImpl* render_frame_host) { 217 SiteInstance* site_instance = 218 render_frame_host->render_view_host()->GetSiteInstance(); 219 RenderViewHostMap::iterator iter = 220 render_view_host_map_.find(site_instance->GetId()); 221 CHECK(iter != render_view_host_map_.end()); 222 223 iter->second->increment_ref_count(); 224} 225 226void FrameTree::UnregisterRenderFrameHost( 227 RenderFrameHostImpl* render_frame_host) { 228 SiteInstance* site_instance = 229 render_frame_host->render_view_host()->GetSiteInstance(); 230 int32 site_instance_id = site_instance->GetId(); 231 RenderViewHostMap::iterator iter = 232 render_view_host_map_.find(site_instance_id); 233 if (iter != render_view_host_map_.end() && 234 iter->second == render_frame_host->render_view_host()) { 235 // Decrement the refcount and shutdown the RenderViewHost if no one else is 236 // using it. 237 CHECK_GT(iter->second->ref_count(), 0); 238 iter->second->decrement_ref_count(); 239 if (iter->second->ref_count() == 0) { 240 iter->second->Shutdown(); 241 render_view_host_map_.erase(iter); 242 } 243 } else { 244 // The RenderViewHost should be in the list of RenderViewHosts pending 245 // shutdown. 246 bool render_view_host_found = false; 247 std::pair<RenderViewHostMultiMap::iterator, 248 RenderViewHostMultiMap::iterator> result = 249 render_view_host_pending_shutdown_map_.equal_range(site_instance_id); 250 for (RenderViewHostMultiMap::iterator multi_iter = result.first; 251 multi_iter != result.second; 252 ++multi_iter) { 253 if (multi_iter->second != render_frame_host->render_view_host()) 254 continue; 255 render_view_host_found = true; 256 RenderViewHostImpl* rvh = multi_iter->second; 257 // Decrement the refcount and shutdown the RenderViewHost if no one else 258 // is using it. 259 CHECK_GT(rvh->ref_count(), 0); 260 rvh->decrement_ref_count(); 261 if (rvh->ref_count() == 0) { 262 rvh->Shutdown(); 263 render_view_host_pending_shutdown_map_.erase(multi_iter); 264 } 265 break; 266 } 267 CHECK(render_view_host_found); 268 } 269} 270 271} // namespace content 272