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 "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