Roll ANGLE e754fb8..6ffeb74
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blob814980e527e1b3691dfb50c7c99fed8202bb7a80
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/command_line.h"
12 #include "base/containers/hash_tables.h"
13 #include "base/lazy_instance.h"
14 #include "content/browser/frame_host/frame_tree_node.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_factory.h"
17 #include "content/browser/frame_host/render_frame_host_impl.h"
18 #include "content/browser/frame_host/render_frame_proxy_host.h"
19 #include "content/browser/renderer_host/render_view_host_factory.h"
20 #include "content/browser/renderer_host/render_view_host_impl.h"
21 #include "content/public/common/content_switches.h"
22 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
24 namespace content {
26 namespace {
28 // Used with FrameTree::ForEach() to search for the FrameTreeNode
29 // corresponding to |frame_tree_node_id| within a specific FrameTree.
30 bool FrameTreeNodeForId(int frame_tree_node_id,
31 FrameTreeNode** out_node,
32 FrameTreeNode* node) {
33 if (node->frame_tree_node_id() == frame_tree_node_id) {
34 *out_node = node;
35 // Terminate iteration once the node has been found.
36 return false;
38 return true;
41 // Used with FrameTree::ForEach() to search for the FrameTreeNode with the given
42 // |name| within a specific FrameTree.
43 bool FrameTreeNodeForName(const std::string& name,
44 FrameTreeNode** out_node,
45 FrameTreeNode* node) {
46 if (node->frame_name() == name) {
47 *out_node = node;
48 // Terminate iteration once the node has been found.
49 return false;
51 return true;
54 bool CreateProxyForSiteInstance(const scoped_refptr<SiteInstance>& instance,
55 FrameTreeNode* node) {
56 // If a new frame is created in the current SiteInstance, other frames in
57 // that SiteInstance don't need a proxy for the new frame.
58 SiteInstance* current_instance =
59 node->render_manager()->current_frame_host()->GetSiteInstance();
60 if (current_instance != instance.get())
61 node->render_manager()->CreateRenderFrameProxy(instance.get());
62 return true;
65 // Helper function used with FrameTree::ForEach() for retrieving the total
66 // loading progress and number of frames in a frame tree.
67 bool CollectLoadProgress(double* progress,
68 int* frame_count,
69 FrameTreeNode* node) {
70 // Ignore the current frame if it has not started loading.
71 if (!node->has_started_loading())
72 return true;
74 // Collect progress.
75 *progress += node->loading_progress();
76 (*frame_count)++;
77 return true;
80 // Helper function used with FrameTree::ForEach() to reset the load progress.
81 bool ResetNodeLoadProgress(FrameTreeNode* node) {
82 node->reset_loading_progress();
83 return true;
86 // Helper function used with FrameTree::ForEach() to check if at least one of
87 // the nodes is loading.
88 bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
89 if (node->IsLoading()) {
90 // There is at least one node loading, so abort traversal.
91 *is_loading = true;
92 return false;
94 return true;
97 } // namespace
99 FrameTree::FrameTree(Navigator* navigator,
100 RenderFrameHostDelegate* render_frame_delegate,
101 RenderViewHostDelegate* render_view_delegate,
102 RenderWidgetHostDelegate* render_widget_delegate,
103 RenderFrameHostManager::Delegate* manager_delegate)
104 : render_frame_delegate_(render_frame_delegate),
105 render_view_delegate_(render_view_delegate),
106 render_widget_delegate_(render_widget_delegate),
107 manager_delegate_(manager_delegate),
108 root_(new FrameTreeNode(this,
109 navigator,
110 render_frame_delegate,
111 render_view_delegate,
112 render_widget_delegate,
113 manager_delegate,
114 // The top-level frame must always be in a
115 // document scope.
116 blink::WebTreeScopeType::Document,
117 std::string(),
118 blink::WebSandboxFlags::None)),
119 focused_frame_tree_node_id_(-1),
120 load_progress_(0.0) {
123 FrameTree::~FrameTree() {
126 FrameTreeNode* FrameTree::FindByID(int frame_tree_node_id) {
127 FrameTreeNode* node = nullptr;
128 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node));
129 return node;
132 FrameTreeNode* FrameTree::FindByRoutingID(int process_id, int routing_id) {
133 RenderFrameHostImpl* render_frame_host =
134 RenderFrameHostImpl::FromID(process_id, routing_id);
135 if (render_frame_host) {
136 FrameTreeNode* result = render_frame_host->frame_tree_node();
137 if (this == result->frame_tree())
138 return result;
141 RenderFrameProxyHost* render_frame_proxy_host =
142 RenderFrameProxyHost::FromID(process_id, routing_id);
143 if (render_frame_proxy_host) {
144 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node();
145 if (this == result->frame_tree())
146 return result;
149 return nullptr;
152 FrameTreeNode* FrameTree::FindByName(const std::string& name) {
153 if (name.empty())
154 return root_.get();
156 FrameTreeNode* node = nullptr;
157 ForEach(base::Bind(&FrameTreeNodeForName, name, &node));
158 return node;
161 void FrameTree::ForEach(
162 const base::Callback<bool(FrameTreeNode*)>& on_node) const {
163 ForEach(on_node, nullptr);
166 void FrameTree::ForEach(
167 const base::Callback<bool(FrameTreeNode*)>& on_node,
168 FrameTreeNode* skip_this_subtree) const {
169 std::queue<FrameTreeNode*> queue;
170 queue.push(root_.get());
172 while (!queue.empty()) {
173 FrameTreeNode* node = queue.front();
174 queue.pop();
175 if (skip_this_subtree == node)
176 continue;
178 if (!on_node.Run(node))
179 break;
181 for (size_t i = 0; i < node->child_count(); ++i)
182 queue.push(node->child_at(i));
186 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
187 int process_id,
188 int new_routing_id,
189 blink::WebTreeScopeType scope,
190 const std::string& frame_name,
191 blink::WebSandboxFlags sandbox_flags) {
192 // A child frame always starts with an initial empty document, which means
193 // it is in the same SiteInstance as the parent frame. Ensure that the process
194 // which requested a child frame to be added is the same as the process of the
195 // parent node.
196 // We return nullptr if this is not the case, which can happen in a race if an
197 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
198 if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
199 return nullptr;
201 scoped_ptr<FrameTreeNode> node(
202 new FrameTreeNode(this, parent->navigator(), render_frame_delegate_,
203 render_view_delegate_, render_widget_delegate_,
204 manager_delegate_, scope, frame_name, sandbox_flags));
205 FrameTreeNode* node_ptr = node.get();
206 // AddChild is what creates the RenderFrameHost.
207 parent->AddChild(node.Pass(), process_id, new_routing_id);
208 return node_ptr->current_frame_host();
211 void FrameTree::RemoveFrame(FrameTreeNode* child) {
212 FrameTreeNode* parent = child->parent();
213 if (!parent) {
214 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
215 return;
218 parent->RemoveChild(child);
221 void FrameTree::CreateProxiesForSiteInstance(
222 FrameTreeNode* source,
223 SiteInstance* site_instance) {
224 // Create the swapped out RVH for the new SiteInstance. This will create
225 // a top-level swapped out RFH as well, which will then be wrapped by a
226 // RenderFrameProxyHost.
227 if (!source->IsMainFrame()) {
228 RenderViewHostImpl* render_view_host =
229 source->frame_tree()->GetRenderViewHost(site_instance);
230 if (!render_view_host) {
231 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
232 switches::kSitePerProcess)) {
233 root()->render_manager()->CreateRenderFrameProxy(site_instance);
234 } else {
235 root()->render_manager()->CreateRenderFrame(
236 site_instance, nullptr, MSG_ROUTING_NONE,
237 CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN, nullptr);
239 } else {
240 root()->render_manager()->EnsureRenderViewInitialized(
241 source, render_view_host, site_instance);
245 scoped_refptr<SiteInstance> instance(site_instance);
247 // Proxies are created in the FrameTree in response to a node navigating to a
248 // new SiteInstance. Since |source|'s navigation will replace the currently
249 // loaded document, the entire subtree under |source| will be removed.
250 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
253 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
254 return root_->current_frame_host();
257 FrameTreeNode* FrameTree::GetFocusedFrame() {
258 return FindByID(focused_frame_tree_node_id_);
261 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
262 focused_frame_tree_node_id_ = node->frame_tree_node_id();
265 void FrameTree::SetFrameRemoveListener(
266 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
267 on_frame_removed_ = on_frame_removed;
270 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
271 int routing_id,
272 int main_frame_routing_id,
273 bool swapped_out,
274 bool hidden) {
275 RenderViewHostMap::iterator iter =
276 render_view_host_map_.find(site_instance->GetId());
277 if (iter != render_view_host_map_.end()) {
278 // If a RenderViewHost's main frame is pending deletion for this
279 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
280 // Otherwise return the existing RenderViewHost for the SiteInstance.
281 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
282 iter->second->GetMainFrame());
283 if (main_frame &&
284 main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
285 main_frame)) {
286 render_view_host_pending_shutdown_map_.insert(
287 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
288 iter->second));
289 render_view_host_map_.erase(iter);
290 } else {
291 return iter->second;
294 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
295 RenderViewHostFactory::Create(site_instance,
296 render_view_delegate_,
297 render_widget_delegate_,
298 routing_id,
299 main_frame_routing_id,
300 swapped_out,
301 hidden));
303 render_view_host_map_[site_instance->GetId()] = rvh;
304 return rvh;
307 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
308 RenderViewHostMap::iterator iter =
309 render_view_host_map_.find(site_instance->GetId());
310 if (iter == render_view_host_map_.end())
311 return nullptr;
312 return iter->second;
315 void FrameTree::AddRenderViewHostRef(RenderViewHostImpl* render_view_host) {
316 SiteInstance* site_instance = render_view_host->GetSiteInstance();
317 RenderViewHostMap::iterator iter =
318 render_view_host_map_.find(site_instance->GetId());
319 CHECK(iter != render_view_host_map_.end());
320 CHECK(iter->second == render_view_host);
322 iter->second->increment_ref_count();
325 void FrameTree::ReleaseRenderViewHostRef(RenderViewHostImpl* render_view_host) {
326 SiteInstance* site_instance = render_view_host->GetSiteInstance();
327 int32 site_instance_id = site_instance->GetId();
328 RenderViewHostMap::iterator iter =
329 render_view_host_map_.find(site_instance_id);
330 if (iter != render_view_host_map_.end() && iter->second == render_view_host) {
331 // Decrement the refcount and shutdown the RenderViewHost if no one else is
332 // using it.
333 CHECK_GT(iter->second->ref_count(), 0);
334 iter->second->decrement_ref_count();
335 if (iter->second->ref_count() == 0) {
336 iter->second->Shutdown();
337 render_view_host_map_.erase(iter);
339 } else {
340 // The RenderViewHost should be in the list of RenderViewHosts pending
341 // shutdown.
342 bool render_view_host_found = false;
343 std::pair<RenderViewHostMultiMap::iterator,
344 RenderViewHostMultiMap::iterator> result =
345 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
346 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
347 multi_iter != result.second;
348 ++multi_iter) {
349 if (multi_iter->second != render_view_host)
350 continue;
351 render_view_host_found = true;
352 // Decrement the refcount and shutdown the RenderViewHost if no one else
353 // is using it.
354 CHECK_GT(render_view_host->ref_count(), 0);
355 render_view_host->decrement_ref_count();
356 if (render_view_host->ref_count() == 0) {
357 render_view_host->Shutdown();
358 render_view_host_pending_shutdown_map_.erase(multi_iter);
360 break;
362 CHECK(render_view_host_found);
366 void FrameTree::FrameRemoved(FrameTreeNode* frame) {
367 if (frame->frame_tree_node_id() == focused_frame_tree_node_id_)
368 focused_frame_tree_node_id_ = -1;
370 // No notification for the root frame.
371 if (!frame->parent()) {
372 CHECK_EQ(frame, root_.get());
373 return;
376 // Notify observers of the frame removal.
377 if (!on_frame_removed_.is_null())
378 on_frame_removed_.Run(frame->current_frame_host());
381 void FrameTree::UpdateLoadProgress() {
382 double progress = 0.0;
383 int frame_count = 0;
385 ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
386 if (frame_count != 0)
387 progress /= frame_count;
389 if (progress <= load_progress_)
390 return;
391 load_progress_ = progress;
393 // Notify the WebContents.
394 root_->navigator()->GetDelegate()->DidChangeLoadProgress();
397 void FrameTree::ResetLoadProgress() {
398 ForEach(base::Bind(&ResetNodeLoadProgress));
399 load_progress_ = 0.0;
402 bool FrameTree::IsLoading() {
403 bool is_loading = false;
404 ForEach(base::Bind(&IsNodeLoading, &is_loading));
405 return is_loading;
408 } // namespace content