// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/frame_host/frame_tree.h" #include #include "base/bind.h" #include "base/callback.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/render_frame_host_factory.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_view_host_impl.h" namespace content { namespace { // Used with FrameTree::ForEach() to search for the FrameTreeNode // corresponding to |frame_tree_node_id|. bool FrameTreeNodeForId(int64 frame_tree_node_id, FrameTreeNode** out_node, FrameTreeNode* node) { if (node->frame_tree_node_id() == frame_tree_node_id) { *out_node = node; // Terminate iteration once the node has been found. return false; } return true; } bool FrameTreeNodeForRoutingId(int routing_id, int process_id, FrameTreeNode** out_node, FrameTreeNode* node) { // TODO(creis): Look through the swapped out RFHs as well. if (node->current_frame_host()->GetProcess()->GetID() == process_id && node->current_frame_host()->GetRoutingID() == routing_id) { *out_node = node; // Terminate iteration once the node has been found. return false; } return true; } // Iterate over the FrameTree to reset any node affected by the loss of the // given RenderViewHost's process. bool ResetNodesForNewProcess(RenderViewHost* render_view_host, FrameTreeNode* node) { if (render_view_host == node->current_frame_host()->render_view_host()) node->ResetForNewProcess(); return true; } } // namespace FrameTree::FrameTree(Navigator* navigator, RenderFrameHostDelegate* render_frame_delegate, RenderViewHostDelegate* render_view_delegate, RenderWidgetHostDelegate* render_widget_delegate, RenderFrameHostManager::Delegate* manager_delegate) : render_frame_delegate_(render_frame_delegate), render_view_delegate_(render_view_delegate), render_widget_delegate_(render_widget_delegate), manager_delegate_(manager_delegate), root_(new FrameTreeNode(this, navigator, render_frame_delegate, render_view_delegate, render_widget_delegate, manager_delegate, std::string())), focused_frame_tree_node_id_(-1) { } FrameTree::~FrameTree() { } FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) { FrameTreeNode* node = NULL; ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node)); return node; } FrameTreeNode* FrameTree::FindByRoutingID(int routing_id, int process_id) { FrameTreeNode* node = NULL; ForEach( base::Bind(&FrameTreeNodeForRoutingId, routing_id, process_id, &node)); return node; } void FrameTree::ForEach( const base::Callback& on_node) const { std::queue queue; queue.push(root_.get()); while (!queue.empty()) { FrameTreeNode* node = queue.front(); queue.pop(); if (!on_node.Run(node)) break; for (size_t i = 0; i < node->child_count(); ++i) queue.push(node->child_at(i)); } } RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent, int new_routing_id, const std::string& frame_name) { scoped_ptr node(new FrameTreeNode( this, parent->navigator(), render_frame_delegate_, render_view_delegate_, render_widget_delegate_, manager_delegate_, frame_name)); FrameTreeNode* node_ptr = node.get(); // AddChild is what creates the RenderFrameHost. parent->AddChild(node.Pass(), new_routing_id); return node_ptr->current_frame_host(); } void FrameTree::RemoveFrame(FrameTreeNode* child) { FrameTreeNode* parent = child->parent(); if (!parent) { NOTREACHED() << "Unexpected RemoveFrame call for main frame."; return; } // Notify observers of the frame removal. RenderFrameHostImpl* render_frame_host = child->current_frame_host(); if (!on_frame_removed_.is_null()) { on_frame_removed_.Run( render_frame_host->render_view_host(), render_frame_host->GetRoutingID()); } parent->RemoveChild(child); } void FrameTree::ResetForMainFrameSwap() { root_->ResetForNewProcess(); focused_frame_tree_node_id_ = -1; } void FrameTree::RenderProcessGone(RenderViewHost* render_view_host) { // Walk the full tree looking for nodes that may be affected. Once a frame // crashes, all of its child FrameTreeNodes go away. // Note that the helper function may call ResetForNewProcess on a node, which // clears its children before we iterate over them. That's ok, because // ForEach does not add a node's children to the queue until after visiting // the node itself. ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host)); } RenderFrameHostImpl* FrameTree::GetMainFrame() const { return root_->current_frame_host(); } FrameTreeNode* FrameTree::GetFocusedFrame() { return FindByID(focused_frame_tree_node_id_); } void FrameTree::SetFocusedFrame(FrameTreeNode* node) { focused_frame_tree_node_id_ = node->frame_tree_node_id(); } void FrameTree::SetFrameRemoveListener( const base::Callback& on_frame_removed) { on_frame_removed_ = on_frame_removed; } RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame( SiteInstance* site_instance, int routing_id, int main_frame_routing_id, bool swapped_out, bool hidden) { DCHECK(main_frame_routing_id != MSG_ROUTING_NONE); RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance->GetId()); if (iter != render_view_host_map_.end()) { // If a RenderViewHost is pending shutdown for this |site_instance|, put it // in the map of RenderViewHosts pending shutdown. Otherwise there should // not be a RenderViewHost for the SiteInstance. CHECK_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, iter->second->rvh_state()); render_view_host_pending_shutdown_map_.insert( std::pair(site_instance->GetId(), iter->second)); render_view_host_map_.erase(iter); } RenderViewHostImpl* rvh = static_cast( RenderViewHostFactory::Create(site_instance, render_view_delegate_, render_widget_delegate_, routing_id, main_frame_routing_id, swapped_out, hidden)); render_view_host_map_[site_instance->GetId()] = rvh; return rvh; } RenderViewHostImpl* FrameTree::GetRenderViewHostForSubFrame( SiteInstance* site_instance) { RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance->GetId()); // TODO(creis): Mirror the frame tree so this check can't fail. if (iter == render_view_host_map_.end()) return NULL; return iter->second; } void FrameTree::RegisterRenderFrameHost( RenderFrameHostImpl* render_frame_host) { SiteInstance* site_instance = render_frame_host->render_view_host()->GetSiteInstance(); RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance->GetId()); CHECK(iter != render_view_host_map_.end()); iter->second->increment_ref_count(); } void FrameTree::UnregisterRenderFrameHost( RenderFrameHostImpl* render_frame_host) { SiteInstance* site_instance = render_frame_host->render_view_host()->GetSiteInstance(); int32 site_instance_id = site_instance->GetId(); RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance_id); if (iter != render_view_host_map_.end() && iter->second == render_frame_host->render_view_host()) { // Decrement the refcount and shutdown the RenderViewHost if no one else is // using it. CHECK_GT(iter->second->ref_count(), 0); iter->second->decrement_ref_count(); if (iter->second->ref_count() == 0) { iter->second->Shutdown(); render_view_host_map_.erase(iter); } } else { // The RenderViewHost should be in the list of RenderViewHosts pending // shutdown. bool render_view_host_found = false; std::pair result = render_view_host_pending_shutdown_map_.equal_range(site_instance_id); for (RenderViewHostMultiMap::iterator multi_iter = result.first; multi_iter != result.second; ++multi_iter) { if (multi_iter->second != render_frame_host->render_view_host()) continue; render_view_host_found = true; RenderViewHostImpl* rvh = multi_iter->second; // Decrement the refcount and shutdown the RenderViewHost if no one else // is using it. CHECK_GT(rvh->ref_count(), 0); rvh->decrement_ref_count(); if (rvh->ref_count() == 0) { rvh->Shutdown(); render_view_host_pending_shutdown_map_.erase(multi_iter); } break; } CHECK(render_view_host_found); } } } // namespace content