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
22namespace content {
23
24namespace {
25
26// This is a global map between frame_tree_node_ids and pointer to
27// FrameTreeNodes.
28typedef base::hash_map<int64, FrameTreeNode*> FrameTreeNodeIDMap;
29
30base::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.
35bool 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.
48bool 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
55bool 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
68FrameTree::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
91FrameTree::~FrameTree() {
92  g_frame_tree_node_id_map.Get().erase(root_->frame_tree_node_id());
93}
94
95// static
96FrameTreeNode* 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
103FrameTreeNode* 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
109FrameTreeNode* 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
129void 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
145RenderFrameHostImpl* 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
169void 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
185void 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
207void FrameTree::ResetForMainFrameSwap() {
208  root_->ResetForNewProcess();
209  focused_frame_tree_node_id_ = -1;
210}
211
212void 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
222RenderFrameHostImpl* FrameTree::GetMainFrame() const {
223  return root_->current_frame_host();
224}
225
226FrameTreeNode* FrameTree::GetFocusedFrame() {
227  return FindByID(focused_frame_tree_node_id_);
228}
229
230void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
231  focused_frame_tree_node_id_ = node->frame_tree_node_id();
232}
233
234void FrameTree::SetFrameRemoveListener(
235    const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
236  on_frame_removed_ = on_frame_removed;
237}
238
239RenderViewHostImpl* 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
274RenderViewHostImpl* 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
283void 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
294void 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