Make USB permissions work in the new permission message system
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blob64774ffbfff8d67af88ea002d53a80399cce90bb
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"
20 #include "content/common/site_isolation_policy.h"
21 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
23 namespace content {
25 namespace {
27 // Used with FrameTree::ForEach() to search for the FrameTreeNode
28 // corresponding to |frame_tree_node_id| within a specific FrameTree.
29 bool FrameTreeNodeForId(int frame_tree_node_id,
30 FrameTreeNode** out_node,
31 FrameTreeNode* node) {
32 if (node->frame_tree_node_id() == frame_tree_node_id) {
33 *out_node = node;
34 // Terminate iteration once the node has been found.
35 return false;
37 return true;
40 // Used with FrameTree::ForEach() to search for the FrameTreeNode with the given
41 // |name| within a specific FrameTree.
42 bool FrameTreeNodeForName(const std::string& name,
43 FrameTreeNode** out_node,
44 FrameTreeNode* node) {
45 if (node->frame_name() == name) {
46 *out_node = node;
47 // Terminate iteration once the node has been found.
48 return false;
50 return true;
53 bool CreateProxyForSiteInstance(const scoped_refptr<SiteInstance>& instance,
54 FrameTreeNode* node) {
55 // If a new frame is created in the current SiteInstance, other frames in
56 // that SiteInstance don't need a proxy for the new frame.
57 SiteInstance* current_instance =
58 node->render_manager()->current_frame_host()->GetSiteInstance();
59 if (current_instance != instance.get())
60 node->render_manager()->CreateRenderFrameProxy(instance.get());
61 return true;
64 // Helper function used with FrameTree::ForEach() for retrieving the total
65 // loading progress and number of frames in a frame tree.
66 bool CollectLoadProgress(double* progress,
67 int* frame_count,
68 FrameTreeNode* node) {
69 // Ignore the current frame if it has not started loading.
70 if (!node->has_started_loading())
71 return true;
73 // Collect progress.
74 *progress += node->loading_progress();
75 (*frame_count)++;
76 return true;
79 // Helper function used with FrameTree::ForEach() to reset the load progress.
80 bool ResetNodeLoadProgress(FrameTreeNode* node) {
81 node->reset_loading_progress();
82 return true;
85 // Helper function used with FrameTree::ForEach() to check if at least one of
86 // the nodes is loading.
87 bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
88 if (node->IsLoading()) {
89 // There is at least one node loading, so abort traversal.
90 *is_loading = true;
91 return false;
93 return true;
96 } // namespace
98 FrameTree::FrameTree(Navigator* navigator,
99 RenderFrameHostDelegate* render_frame_delegate,
100 RenderViewHostDelegate* render_view_delegate,
101 RenderWidgetHostDelegate* render_widget_delegate,
102 RenderFrameHostManager::Delegate* manager_delegate)
103 : render_frame_delegate_(render_frame_delegate),
104 render_view_delegate_(render_view_delegate),
105 render_widget_delegate_(render_widget_delegate),
106 manager_delegate_(manager_delegate),
107 root_(new FrameTreeNode(this,
108 navigator,
109 render_frame_delegate,
110 render_view_delegate,
111 render_widget_delegate,
112 manager_delegate,
113 // The top-level frame must always be in a
114 // document scope.
115 blink::WebTreeScopeType::Document,
116 std::string(),
117 blink::WebSandboxFlags::None)),
118 focused_frame_tree_node_id_(-1),
119 load_progress_(0.0) {
122 FrameTree::~FrameTree() {
125 FrameTreeNode* FrameTree::FindByID(int frame_tree_node_id) {
126 FrameTreeNode* node = nullptr;
127 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node));
128 return node;
131 FrameTreeNode* FrameTree::FindByRoutingID(int process_id, int routing_id) {
132 RenderFrameHostImpl* render_frame_host =
133 RenderFrameHostImpl::FromID(process_id, routing_id);
134 if (render_frame_host) {
135 FrameTreeNode* result = render_frame_host->frame_tree_node();
136 if (this == result->frame_tree())
137 return result;
140 RenderFrameProxyHost* render_frame_proxy_host =
141 RenderFrameProxyHost::FromID(process_id, routing_id);
142 if (render_frame_proxy_host) {
143 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node();
144 if (this == result->frame_tree())
145 return result;
148 return nullptr;
151 FrameTreeNode* FrameTree::FindByName(const std::string& name) {
152 if (name.empty())
153 return root_.get();
155 FrameTreeNode* node = nullptr;
156 ForEach(base::Bind(&FrameTreeNodeForName, name, &node));
157 return node;
160 void FrameTree::ForEach(
161 const base::Callback<bool(FrameTreeNode*)>& on_node) const {
162 ForEach(on_node, nullptr);
165 void FrameTree::ForEach(
166 const base::Callback<bool(FrameTreeNode*)>& on_node,
167 FrameTreeNode* skip_this_subtree) const {
168 std::queue<FrameTreeNode*> queue;
169 queue.push(root_.get());
171 while (!queue.empty()) {
172 FrameTreeNode* node = queue.front();
173 queue.pop();
174 if (skip_this_subtree == node)
175 continue;
177 if (!on_node.Run(node))
178 break;
180 for (size_t i = 0; i < node->child_count(); ++i)
181 queue.push(node->child_at(i));
185 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
186 int process_id,
187 int new_routing_id,
188 blink::WebTreeScopeType scope,
189 const std::string& frame_name,
190 blink::WebSandboxFlags sandbox_flags) {
191 // A child frame always starts with an initial empty document, which means
192 // it is in the same SiteInstance as the parent frame. Ensure that the process
193 // which requested a child frame to be added is the same as the process of the
194 // parent node.
195 // We return nullptr if this is not the case, which can happen in a race if an
196 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
197 if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
198 return nullptr;
200 scoped_ptr<FrameTreeNode> node(
201 new FrameTreeNode(this, parent->navigator(), render_frame_delegate_,
202 render_view_delegate_, render_widget_delegate_,
203 manager_delegate_, scope, frame_name, sandbox_flags));
204 FrameTreeNode* node_ptr = node.get();
205 // AddChild is what creates the RenderFrameHost.
206 parent->AddChild(node.Pass(), process_id, new_routing_id);
207 return node_ptr->current_frame_host();
210 void FrameTree::RemoveFrame(FrameTreeNode* child) {
211 FrameTreeNode* parent = child->parent();
212 if (!parent) {
213 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
214 return;
217 parent->RemoveChild(child);
220 void FrameTree::CreateProxiesForSiteInstance(
221 FrameTreeNode* source,
222 SiteInstance* site_instance) {
223 // Create the swapped out RVH for the new SiteInstance. This will create
224 // a top-level swapped out RFH as well, which will then be wrapped by a
225 // RenderFrameProxyHost.
226 if (!source || !source->IsMainFrame()) {
227 RenderViewHostImpl* render_view_host = GetRenderViewHost(site_instance);
228 if (!render_view_host) {
229 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
230 root()->render_manager()->CreateRenderFrameProxy(site_instance);
231 } else {
232 root()->render_manager()->CreateRenderFrame(
233 site_instance, nullptr, CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN,
234 nullptr);
236 } else {
237 root()->render_manager()->EnsureRenderViewInitialized(render_view_host,
238 site_instance);
242 scoped_refptr<SiteInstance> instance(site_instance);
244 // Proxies are created in the FrameTree in response to a node navigating to a
245 // new SiteInstance. Since |source|'s navigation will replace the currently
246 // loaded document, the entire subtree under |source| will be removed.
247 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
250 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
251 return root_->current_frame_host();
254 FrameTreeNode* FrameTree::GetFocusedFrame() {
255 return FindByID(focused_frame_tree_node_id_);
258 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
259 node->set_last_focus_time(base::TimeTicks::Now());
260 focused_frame_tree_node_id_ = node->frame_tree_node_id();
263 void FrameTree::SetFrameRemoveListener(
264 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
265 on_frame_removed_ = on_frame_removed;
268 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
269 int routing_id,
270 int main_frame_routing_id,
271 bool swapped_out,
272 bool hidden) {
273 RenderViewHostMap::iterator iter =
274 render_view_host_map_.find(site_instance->GetId());
275 if (iter != render_view_host_map_.end()) {
276 // If a RenderViewHost's main frame is pending deletion for this
277 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
278 // Otherwise return the existing RenderViewHost for the SiteInstance.
279 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
280 iter->second->GetMainFrame());
281 if (main_frame &&
282 main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
283 main_frame)) {
284 render_view_host_pending_shutdown_map_.insert(
285 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
286 iter->second));
287 render_view_host_map_.erase(iter);
288 } else {
289 return iter->second;
292 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
293 RenderViewHostFactory::Create(site_instance,
294 render_view_delegate_,
295 render_widget_delegate_,
296 routing_id,
297 main_frame_routing_id,
298 swapped_out,
299 hidden));
301 render_view_host_map_[site_instance->GetId()] = rvh;
302 return rvh;
305 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
306 RenderViewHostMap::iterator iter =
307 render_view_host_map_.find(site_instance->GetId());
308 if (iter == render_view_host_map_.end())
309 return nullptr;
310 return iter->second;
313 void FrameTree::AddRenderViewHostRef(RenderViewHostImpl* render_view_host) {
314 SiteInstance* site_instance = render_view_host->GetSiteInstance();
315 RenderViewHostMap::iterator iter =
316 render_view_host_map_.find(site_instance->GetId());
317 CHECK(iter != render_view_host_map_.end());
318 CHECK(iter->second == render_view_host);
320 iter->second->increment_ref_count();
323 void FrameTree::ReleaseRenderViewHostRef(RenderViewHostImpl* render_view_host) {
324 SiteInstance* site_instance = render_view_host->GetSiteInstance();
325 int32 site_instance_id = site_instance->GetId();
326 RenderViewHostMap::iterator iter =
327 render_view_host_map_.find(site_instance_id);
328 if (iter != render_view_host_map_.end() && iter->second == render_view_host) {
329 // Decrement the refcount and shutdown the RenderViewHost if no one else is
330 // using it.
331 CHECK_GT(iter->second->ref_count(), 0);
332 iter->second->decrement_ref_count();
333 if (iter->second->ref_count() == 0) {
334 iter->second->Shutdown();
335 render_view_host_map_.erase(iter);
337 } else {
338 // The RenderViewHost should be in the list of RenderViewHosts pending
339 // shutdown.
340 bool render_view_host_found = false;
341 std::pair<RenderViewHostMultiMap::iterator,
342 RenderViewHostMultiMap::iterator> result =
343 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
344 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
345 multi_iter != result.second;
346 ++multi_iter) {
347 if (multi_iter->second != render_view_host)
348 continue;
349 render_view_host_found = true;
350 // Decrement the refcount and shutdown the RenderViewHost if no one else
351 // is using it.
352 CHECK_GT(render_view_host->ref_count(), 0);
353 render_view_host->decrement_ref_count();
354 if (render_view_host->ref_count() == 0) {
355 render_view_host->Shutdown();
356 render_view_host_pending_shutdown_map_.erase(multi_iter);
358 break;
360 CHECK(render_view_host_found);
364 void FrameTree::FrameRemoved(FrameTreeNode* frame) {
365 if (frame->frame_tree_node_id() == focused_frame_tree_node_id_)
366 focused_frame_tree_node_id_ = -1;
368 // No notification for the root frame.
369 if (!frame->parent()) {
370 CHECK_EQ(frame, root_.get());
371 return;
374 // Notify observers of the frame removal.
375 if (!on_frame_removed_.is_null())
376 on_frame_removed_.Run(frame->current_frame_host());
379 void FrameTree::UpdateLoadProgress() {
380 double progress = 0.0;
381 int frame_count = 0;
383 ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
384 if (frame_count != 0)
385 progress /= frame_count;
387 if (progress <= load_progress_)
388 return;
389 load_progress_ = progress;
391 // Notify the WebContents.
392 root_->navigator()->GetDelegate()->DidChangeLoadProgress();
395 void FrameTree::ResetLoadProgress() {
396 ForEach(base::Bind(&ResetNodeLoadProgress));
397 load_progress_ = 0.0;
400 bool FrameTree::IsLoading() {
401 bool is_loading = false;
402 ForEach(base::Bind(&IsNodeLoading, &is_loading));
403 return is_loading;
406 } // namespace content