Home | History | Annotate | Download | only in frame_host
      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 
     18 namespace content {
     19 
     20 namespace {
     21 // Used with FrameTree::ForEach() to search for the FrameTreeNode
     22 // corresponding to |frame_tree_node_id|.
     23 bool 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 
     34 bool 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.
     50 bool 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 
     59 FrameTree::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 
     78 FrameTree::~FrameTree() {
     79 }
     80 
     81 FrameTreeNode* 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 
     87 FrameTreeNode* 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 
     94 void 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 
    110 RenderFrameHostImpl* 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 
    122 void 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 
    140 void FrameTree::ResetForMainFrameSwap() {
    141   root_->ResetForNewProcess();
    142   focused_frame_tree_node_id_ = -1;
    143 }
    144 
    145 void 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 
    155 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
    156   return root_->current_frame_host();
    157 }
    158 
    159 FrameTreeNode* FrameTree::GetFocusedFrame() {
    160   return FindByID(focused_frame_tree_node_id_);
    161 }
    162 
    163 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
    164   focused_frame_tree_node_id_ = node->frame_tree_node_id();
    165 }
    166 
    167 void FrameTree::SetFrameRemoveListener(
    168     const base::Callback<void(RenderViewHostImpl*, int)>& on_frame_removed) {
    169   on_frame_removed_ = on_frame_removed;
    170 }
    171 
    172 RenderViewHostImpl* 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 
    205 RenderViewHostImpl* 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 
    215 void 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 
    226 void 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