Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blobe7c814e6b49527fae2c15cf64ed019cd2d919f97
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.
5 #include "content/browser/frame_host/frame_tree.h"
7 #include <queue>
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"
21 namespace content {
23 namespace {
25 // Used with FrameTree::ForEach() to search for the FrameTreeNode
26 // corresponding to |frame_tree_node_id| whithin a specific FrameTree.
27 bool FrameTreeNodeForId(int64 frame_tree_node_id,
28 FrameTreeNode** out_node,
29 FrameTreeNode* node) {
30 if (node->frame_tree_node_id() == frame_tree_node_id) {
31 *out_node = node;
32 // Terminate iteration once the node has been found.
33 return false;
35 return true;
38 bool CreateProxyForSiteInstance(const scoped_refptr<SiteInstance>& instance,
39 FrameTreeNode* node) {
40 // If a new frame is created in the current SiteInstance, other frames in
41 // that SiteInstance don't need a proxy for the new frame.
42 SiteInstance* current_instance =
43 node->render_manager()->current_frame_host()->GetSiteInstance();
44 if (current_instance != instance.get())
45 node->render_manager()->CreateRenderFrameProxy(instance.get());
46 return true;
49 // Helper function used with FrameTree::ForEach() for retrieving the total
50 // loading progress and number of frames in a frame tree.
51 bool CollectLoadProgress(double* progress,
52 int* frame_count,
53 FrameTreeNode* node) {
54 // Ignore the current frame if it has not started loading.
55 double frame_progress = node->loading_progress();
56 if (frame_progress == FrameTreeNode::kLoadingProgressNotStarted)
57 return true;
59 // Collect progress.
60 *progress += frame_progress;
61 (*frame_count)++;
62 return true;
65 // Helper function used with FrameTree::ForEach() to reset the load progress.
66 bool ResetNodeLoadProgress(FrameTreeNode* node) {
67 node->set_loading_progress(FrameTreeNode::kLoadingProgressNotStarted);
68 return true;
71 // Helper function used with FrameTree::ForEach() to check if at least one of
72 // the nodes is loading.
73 bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
74 if (node->IsLoading()) {
75 // There is at least one node loading, so abort traversal.
76 *is_loading = true;
77 return false;
79 return true;
82 } // namespace
84 FrameTree::FrameTree(Navigator* navigator,
85 RenderFrameHostDelegate* render_frame_delegate,
86 RenderViewHostDelegate* render_view_delegate,
87 RenderWidgetHostDelegate* render_widget_delegate,
88 RenderFrameHostManager::Delegate* manager_delegate)
89 : render_frame_delegate_(render_frame_delegate),
90 render_view_delegate_(render_view_delegate),
91 render_widget_delegate_(render_widget_delegate),
92 manager_delegate_(manager_delegate),
93 root_(new FrameTreeNode(this,
94 navigator,
95 render_frame_delegate,
96 render_view_delegate,
97 render_widget_delegate,
98 manager_delegate,
99 std::string())),
100 focused_frame_tree_node_id_(-1) {
103 FrameTree::~FrameTree() {
106 FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) {
107 FrameTreeNode* node = NULL;
108 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node));
109 return node;
112 FrameTreeNode* FrameTree::FindByRoutingID(int process_id, int routing_id) {
113 RenderFrameHostImpl* render_frame_host =
114 RenderFrameHostImpl::FromID(process_id, routing_id);
115 if (render_frame_host) {
116 FrameTreeNode* result = render_frame_host->frame_tree_node();
117 if (this == result->frame_tree())
118 return result;
121 RenderFrameProxyHost* render_frame_proxy_host =
122 RenderFrameProxyHost::FromID(process_id, routing_id);
123 if (render_frame_proxy_host) {
124 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node();
125 if (this == result->frame_tree())
126 return result;
129 return NULL;
132 void FrameTree::ForEach(
133 const base::Callback<bool(FrameTreeNode*)>& on_node) const {
134 ForEach(on_node, NULL);
137 void FrameTree::ForEach(
138 const base::Callback<bool(FrameTreeNode*)>& on_node,
139 FrameTreeNode* skip_this_subtree) const {
140 std::queue<FrameTreeNode*> queue;
141 queue.push(root_.get());
143 while (!queue.empty()) {
144 FrameTreeNode* node = queue.front();
145 queue.pop();
146 if (skip_this_subtree == node)
147 continue;
149 if (!on_node.Run(node))
150 break;
152 for (size_t i = 0; i < node->child_count(); ++i)
153 queue.push(node->child_at(i));
157 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
158 int process_id,
159 int new_routing_id,
160 const std::string& frame_name) {
161 // A child frame always starts with an initial empty document, which means
162 // it is in the same SiteInstance as the parent frame. Ensure that the process
163 // which requested a child frame to be added is the same as the process of the
164 // parent node.
165 // We return nullptr if this is not the case, which can happen in a race if an
166 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
167 if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
168 return nullptr;
170 scoped_ptr<FrameTreeNode> node(new FrameTreeNode(
171 this, parent->navigator(), render_frame_delegate_, render_view_delegate_,
172 render_widget_delegate_, manager_delegate_, frame_name));
173 FrameTreeNode* node_ptr = node.get();
174 // AddChild is what creates the RenderFrameHost.
175 parent->AddChild(node.Pass(), process_id, new_routing_id);
176 return node_ptr->current_frame_host();
179 void FrameTree::RemoveFrame(FrameTreeNode* child) {
180 FrameTreeNode* parent = child->parent();
181 if (!parent) {
182 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
183 return;
186 parent->RemoveChild(child);
189 void FrameTree::CreateProxiesForSiteInstance(
190 FrameTreeNode* source,
191 SiteInstance* site_instance) {
192 // Create the swapped out RVH for the new SiteInstance. This will create
193 // a top-level swapped out RFH as well, which will then be wrapped by a
194 // RenderFrameProxyHost.
195 if (!source->IsMainFrame()) {
196 RenderViewHostImpl* render_view_host =
197 source->frame_tree()->GetRenderViewHost(site_instance);
198 if (!render_view_host) {
199 root()->render_manager()->CreateRenderFrame(
200 site_instance, nullptr, MSG_ROUTING_NONE,
201 CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN, nullptr);
202 } else {
203 root()->render_manager()->EnsureRenderViewInitialized(
204 source, render_view_host, site_instance);
208 scoped_refptr<SiteInstance> instance(site_instance);
210 // Proxies are created in the FrameTree in response to a node navigating to a
211 // new SiteInstance. Since |source|'s navigation will replace the currently
212 // loaded document, the entire subtree under |source| will be removed.
213 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
216 void FrameTree::ResetForMainFrameSwap() {
217 root_->ResetForNewProcess();
218 focused_frame_tree_node_id_ = -1;
221 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
222 return root_->current_frame_host();
225 FrameTreeNode* FrameTree::GetFocusedFrame() {
226 return FindByID(focused_frame_tree_node_id_);
229 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
230 focused_frame_tree_node_id_ = node->frame_tree_node_id();
233 void FrameTree::SetFrameRemoveListener(
234 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
235 on_frame_removed_ = on_frame_removed;
238 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
239 int routing_id,
240 int main_frame_routing_id,
241 bool swapped_out,
242 bool hidden) {
243 DCHECK(main_frame_routing_id != MSG_ROUTING_NONE);
244 RenderViewHostMap::iterator iter =
245 render_view_host_map_.find(site_instance->GetId());
246 if (iter != render_view_host_map_.end()) {
247 // If a RenderViewHost's main frame is pending deletion for this
248 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
249 // Otherwise return the existing RenderViewHost for the SiteInstance.
250 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
251 iter->second->GetMainFrame());
252 if (main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
253 main_frame)) {
254 render_view_host_pending_shutdown_map_.insert(
255 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
256 iter->second));
257 render_view_host_map_.erase(iter);
258 } else {
259 return iter->second;
262 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
263 RenderViewHostFactory::Create(site_instance,
264 render_view_delegate_,
265 render_widget_delegate_,
266 routing_id,
267 main_frame_routing_id,
268 swapped_out,
269 hidden));
271 render_view_host_map_[site_instance->GetId()] = rvh;
272 return rvh;
275 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
276 RenderViewHostMap::iterator iter =
277 render_view_host_map_.find(site_instance->GetId());
278 // TODO(creis): Mirror the frame tree so this check can't fail.
279 if (iter == render_view_host_map_.end())
280 return NULL;
281 return iter->second;
284 void FrameTree::RegisterRenderFrameHost(
285 RenderFrameHostImpl* render_frame_host) {
286 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
287 RenderViewHostMap::iterator iter =
288 render_view_host_map_.find(site_instance->GetId());
289 CHECK(iter != render_view_host_map_.end());
291 iter->second->increment_ref_count();
294 void FrameTree::UnregisterRenderFrameHost(
295 RenderFrameHostImpl* render_frame_host) {
296 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
297 int32 site_instance_id = site_instance->GetId();
298 RenderViewHostMap::iterator iter =
299 render_view_host_map_.find(site_instance_id);
300 if (iter != render_view_host_map_.end() &&
301 iter->second == render_frame_host->render_view_host()) {
302 // Decrement the refcount and shutdown the RenderViewHost if no one else is
303 // using it.
304 CHECK_GT(iter->second->ref_count(), 0);
305 iter->second->decrement_ref_count();
306 if (iter->second->ref_count() == 0) {
307 iter->second->Shutdown();
308 render_view_host_map_.erase(iter);
310 } else {
311 // The RenderViewHost should be in the list of RenderViewHosts pending
312 // shutdown.
313 bool render_view_host_found = false;
314 std::pair<RenderViewHostMultiMap::iterator,
315 RenderViewHostMultiMap::iterator> result =
316 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
317 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
318 multi_iter != result.second;
319 ++multi_iter) {
320 if (multi_iter->second != render_frame_host->render_view_host())
321 continue;
322 render_view_host_found = true;
323 RenderViewHostImpl* rvh = multi_iter->second;
324 // Decrement the refcount and shutdown the RenderViewHost if no one else
325 // is using it.
326 CHECK_GT(rvh->ref_count(), 0);
327 rvh->decrement_ref_count();
328 if (rvh->ref_count() == 0) {
329 rvh->Shutdown();
330 render_view_host_pending_shutdown_map_.erase(multi_iter);
332 break;
334 CHECK(render_view_host_found);
338 void FrameTree::FrameRemoved(FrameTreeNode* frame) {
339 // No notification for the root frame.
340 if (!frame->parent()) {
341 CHECK_EQ(frame, root_.get());
342 return;
345 // Notify observers of the frame removal.
346 if (!on_frame_removed_.is_null())
347 on_frame_removed_.Run(frame->current_frame_host());
350 double FrameTree::GetLoadProgress() {
351 double progress = 0.0;
352 int frame_count = 0;
354 ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
355 if (frame_count != 0)
356 progress /= frame_count;
357 return progress;
360 void FrameTree::ResetLoadProgress() {
361 ForEach(base::Bind(&ResetNodeLoadProgress));
364 bool FrameTree::IsLoading() {
365 bool is_loading = false;
366 ForEach(base::Bind(&IsNodeLoading, &is_loading));
367 return is_loading;
370 } // namespace content