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