Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blob460e2a09e4a5d4220701c7009d283918fcbb76c4
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>
8 #include <utility>
10 #include "base/bind.h"
11 #include "base/callback.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/common/site_isolation_policy.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 || !source->IsMainFrame()) {
228 RenderViewHostImpl* render_view_host = GetRenderViewHost(site_instance);
229 if (!render_view_host) {
230 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
231 root()->render_manager()->CreateRenderFrameProxy(site_instance);
232 } else {
233 root()->render_manager()->CreateRenderFrame(
234 site_instance, nullptr, CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN,
235 nullptr);
237 } else {
238 root()->render_manager()->EnsureRenderViewInitialized(render_view_host,
239 site_instance);
243 scoped_refptr<SiteInstance> instance(site_instance);
245 // Proxies are created in the FrameTree in response to a node navigating to a
246 // new SiteInstance. Since |source|'s navigation will replace the currently
247 // loaded document, the entire subtree under |source| will be removed.
248 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
251 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
252 return root_->current_frame_host();
255 FrameTreeNode* FrameTree::GetFocusedFrame() {
256 return FindByID(focused_frame_tree_node_id_);
259 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
260 node->set_last_focus_time(base::TimeTicks::Now());
261 focused_frame_tree_node_id_ = node->frame_tree_node_id();
264 void FrameTree::SetFrameRemoveListener(
265 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
266 on_frame_removed_ = on_frame_removed;
269 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
270 int32 routing_id,
271 int32 main_frame_routing_id,
272 bool swapped_out,
273 bool hidden) {
274 RenderViewHostMap::iterator iter =
275 render_view_host_map_.find(site_instance->GetId());
276 if (iter != render_view_host_map_.end()) {
277 // If a RenderViewHost's main frame is pending deletion for this
278 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
279 // Otherwise return the existing RenderViewHost for the SiteInstance.
280 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
281 iter->second->GetMainFrame());
282 if (main_frame &&
283 main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
284 main_frame)) {
285 render_view_host_pending_shutdown_map_.insert(
286 std::make_pair(site_instance->GetId(), iter->second));
287 render_view_host_map_.erase(iter);
288 } else {
289 return iter->second;
292 RenderViewHostImpl* rvh =
293 static_cast<RenderViewHostImpl*>(RenderViewHostFactory::Create(
294 site_instance, render_view_delegate_, render_widget_delegate_,
295 routing_id, main_frame_routing_id, swapped_out, hidden));
297 render_view_host_map_[site_instance->GetId()] = rvh;
298 return rvh;
301 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
302 RenderViewHostMap::iterator iter =
303 render_view_host_map_.find(site_instance->GetId());
304 if (iter == render_view_host_map_.end())
305 return nullptr;
306 return iter->second;
309 void FrameTree::AddRenderViewHostRef(RenderViewHostImpl* render_view_host) {
310 SiteInstance* site_instance = render_view_host->GetSiteInstance();
311 RenderViewHostMap::iterator iter =
312 render_view_host_map_.find(site_instance->GetId());
313 CHECK(iter != render_view_host_map_.end());
314 CHECK(iter->second == render_view_host);
316 iter->second->increment_ref_count();
319 void FrameTree::ReleaseRenderViewHostRef(RenderViewHostImpl* render_view_host) {
320 SiteInstance* site_instance = render_view_host->GetSiteInstance();
321 int32 site_instance_id = site_instance->GetId();
322 RenderViewHostMap::iterator iter =
323 render_view_host_map_.find(site_instance_id);
324 if (iter != render_view_host_map_.end() && iter->second == render_view_host) {
325 // Decrement the refcount and shutdown the RenderViewHost if no one else is
326 // using it.
327 CHECK_GT(iter->second->ref_count(), 0);
328 iter->second->decrement_ref_count();
329 if (iter->second->ref_count() == 0) {
330 iter->second->Shutdown();
331 render_view_host_map_.erase(iter);
333 } else {
334 // The RenderViewHost should be in the list of RenderViewHosts pending
335 // shutdown.
336 bool render_view_host_found = false;
337 std::pair<RenderViewHostMultiMap::iterator,
338 RenderViewHostMultiMap::iterator> result =
339 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
340 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
341 multi_iter != result.second;
342 ++multi_iter) {
343 if (multi_iter->second != render_view_host)
344 continue;
345 render_view_host_found = true;
346 // Decrement the refcount and shutdown the RenderViewHost if no one else
347 // is using it.
348 CHECK_GT(render_view_host->ref_count(), 0);
349 render_view_host->decrement_ref_count();
350 if (render_view_host->ref_count() == 0) {
351 render_view_host->Shutdown();
352 render_view_host_pending_shutdown_map_.erase(multi_iter);
354 break;
356 CHECK(render_view_host_found);
360 void FrameTree::FrameRemoved(FrameTreeNode* frame) {
361 if (frame->frame_tree_node_id() == focused_frame_tree_node_id_)
362 focused_frame_tree_node_id_ = -1;
364 // No notification for the root frame.
365 if (!frame->parent()) {
366 CHECK_EQ(frame, root_.get());
367 return;
370 // Notify observers of the frame removal.
371 if (!on_frame_removed_.is_null())
372 on_frame_removed_.Run(frame->current_frame_host());
375 void FrameTree::UpdateLoadProgress() {
376 double progress = 0.0;
377 int frame_count = 0;
379 ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
380 if (frame_count != 0)
381 progress /= frame_count;
383 if (progress <= load_progress_)
384 return;
385 load_progress_ = progress;
387 // Notify the WebContents.
388 root_->navigator()->GetDelegate()->DidChangeLoadProgress();
391 void FrameTree::ResetLoadProgress() {
392 ForEach(base::Bind(&ResetNodeLoadProgress));
393 load_progress_ = 0.0;
396 bool FrameTree::IsLoading() {
397 bool is_loading = false;
398 ForEach(base::Bind(&IsNodeLoading, &is_loading));
399 return is_loading;
402 } // namespace content