Fix various typos, error handling for GN auto-roller.
[chromium-blink-merge.git] / content / browser / frame_host / frame_tree.cc
blob544fd5a10674b82701609af2af08b434450d476d
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 "third_party/WebKit/public/web/WebSandboxFlags.h"
22 namespace content {
24 namespace {
26 // Used with FrameTree::ForEach() to search for the FrameTreeNode
27 // corresponding to |frame_tree_node_id| within a specific FrameTree.
28 bool FrameTreeNodeForId(int frame_tree_node_id,
29 FrameTreeNode** out_node,
30 FrameTreeNode* node) {
31 if (node->frame_tree_node_id() == frame_tree_node_id) {
32 *out_node = node;
33 // Terminate iteration once the node has been found.
34 return false;
36 return true;
39 // Used with FrameTree::ForEach() to search for the FrameTreeNode with the given
40 // |name| within a specific FrameTree.
41 bool FrameTreeNodeForName(const std::string& name,
42 FrameTreeNode** out_node,
43 FrameTreeNode* node) {
44 if (node->frame_name() == name) {
45 *out_node = node;
46 // Terminate iteration once the node has been found.
47 return false;
49 return true;
52 bool CreateProxyForSiteInstance(const scoped_refptr<SiteInstance>& instance,
53 FrameTreeNode* node) {
54 // If a new frame is created in the current SiteInstance, other frames in
55 // that SiteInstance don't need a proxy for the new frame.
56 SiteInstance* current_instance =
57 node->render_manager()->current_frame_host()->GetSiteInstance();
58 if (current_instance != instance.get())
59 node->render_manager()->CreateRenderFrameProxy(instance.get());
60 return true;
63 // Helper function used with FrameTree::ForEach() for retrieving the total
64 // loading progress and number of frames in a frame tree.
65 bool CollectLoadProgress(double* progress,
66 int* frame_count,
67 FrameTreeNode* node) {
68 // Ignore the current frame if it has not started loading.
69 if (!node->has_started_loading())
70 return true;
72 // Collect progress.
73 *progress += node->loading_progress();
74 (*frame_count)++;
75 return true;
78 // Helper function used with FrameTree::ForEach() to reset the load progress.
79 bool ResetNodeLoadProgress(FrameTreeNode* node) {
80 node->reset_loading_progress();
81 return true;
84 // Helper function used with FrameTree::ForEach() to check if at least one of
85 // the nodes is loading.
86 bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) {
87 if (node->IsLoading()) {
88 // There is at least one node loading, so abort traversal.
89 *is_loading = true;
90 return false;
92 return true;
95 } // namespace
97 FrameTree::FrameTree(Navigator* navigator,
98 RenderFrameHostDelegate* render_frame_delegate,
99 RenderViewHostDelegate* render_view_delegate,
100 RenderWidgetHostDelegate* render_widget_delegate,
101 RenderFrameHostManager::Delegate* manager_delegate)
102 : render_frame_delegate_(render_frame_delegate),
103 render_view_delegate_(render_view_delegate),
104 render_widget_delegate_(render_widget_delegate),
105 manager_delegate_(manager_delegate),
106 root_(new FrameTreeNode(this,
107 navigator,
108 render_frame_delegate,
109 render_view_delegate,
110 render_widget_delegate,
111 manager_delegate,
112 // The top-level frame must always be in a
113 // document scope.
114 blink::WebTreeScopeType::Document,
115 std::string(),
116 blink::WebSandboxFlags::None)),
117 focused_frame_tree_node_id_(-1),
118 load_progress_(0.0) {
121 FrameTree::~FrameTree() {
124 FrameTreeNode* FrameTree::FindByID(int frame_tree_node_id) {
125 FrameTreeNode* node = nullptr;
126 ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node));
127 return node;
130 FrameTreeNode* FrameTree::FindByRoutingID(int process_id, int routing_id) {
131 RenderFrameHostImpl* render_frame_host =
132 RenderFrameHostImpl::FromID(process_id, routing_id);
133 if (render_frame_host) {
134 FrameTreeNode* result = render_frame_host->frame_tree_node();
135 if (this == result->frame_tree())
136 return result;
139 RenderFrameProxyHost* render_frame_proxy_host =
140 RenderFrameProxyHost::FromID(process_id, routing_id);
141 if (render_frame_proxy_host) {
142 FrameTreeNode* result = render_frame_proxy_host->frame_tree_node();
143 if (this == result->frame_tree())
144 return result;
147 return nullptr;
150 FrameTreeNode* FrameTree::FindByName(const std::string& name) {
151 if (name.empty())
152 return root_.get();
154 FrameTreeNode* node = nullptr;
155 ForEach(base::Bind(&FrameTreeNodeForName, name, &node));
156 return node;
159 void FrameTree::ForEach(
160 const base::Callback<bool(FrameTreeNode*)>& on_node) const {
161 ForEach(on_node, nullptr);
164 void FrameTree::ForEach(
165 const base::Callback<bool(FrameTreeNode*)>& on_node,
166 FrameTreeNode* skip_this_subtree) const {
167 std::queue<FrameTreeNode*> queue;
168 queue.push(root_.get());
170 while (!queue.empty()) {
171 FrameTreeNode* node = queue.front();
172 queue.pop();
173 if (skip_this_subtree == node)
174 continue;
176 if (!on_node.Run(node))
177 break;
179 for (size_t i = 0; i < node->child_count(); ++i)
180 queue.push(node->child_at(i));
184 RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
185 int process_id,
186 int new_routing_id,
187 blink::WebTreeScopeType scope,
188 const std::string& frame_name,
189 blink::WebSandboxFlags sandbox_flags) {
190 // A child frame always starts with an initial empty document, which means
191 // it is in the same SiteInstance as the parent frame. Ensure that the process
192 // which requested a child frame to be added is the same as the process of the
193 // parent node.
194 // We return nullptr if this is not the case, which can happen in a race if an
195 // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
196 if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
197 return nullptr;
199 scoped_ptr<FrameTreeNode> node(
200 new FrameTreeNode(this, parent->navigator(), render_frame_delegate_,
201 render_view_delegate_, render_widget_delegate_,
202 manager_delegate_, scope, frame_name, sandbox_flags));
203 FrameTreeNode* node_ptr = node.get();
204 // AddChild is what creates the RenderFrameHost.
205 parent->AddChild(node.Pass(), process_id, new_routing_id);
206 return node_ptr->current_frame_host();
209 void FrameTree::RemoveFrame(FrameTreeNode* child) {
210 FrameTreeNode* parent = child->parent();
211 if (!parent) {
212 NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
213 return;
216 parent->RemoveChild(child);
219 void FrameTree::CreateProxiesForSiteInstance(
220 FrameTreeNode* source,
221 SiteInstance* site_instance) {
222 // Create the swapped out RVH for the new SiteInstance. This will create
223 // a top-level swapped out RFH as well, which will then be wrapped by a
224 // RenderFrameProxyHost.
225 if (!source || !source->IsMainFrame()) {
226 RenderViewHostImpl* render_view_host = GetRenderViewHost(site_instance);
227 if (!render_view_host) {
228 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
229 root()->render_manager()->CreateRenderFrameProxy(site_instance);
230 } else {
231 root()->render_manager()->CreateRenderFrame(
232 site_instance, nullptr, CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN,
233 nullptr);
235 } else {
236 root()->render_manager()->EnsureRenderViewInitialized(render_view_host,
237 site_instance);
241 scoped_refptr<SiteInstance> instance(site_instance);
243 // Proxies are created in the FrameTree in response to a node navigating to a
244 // new SiteInstance. Since |source|'s navigation will replace the currently
245 // loaded document, the entire subtree under |source| will be removed.
246 ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source);
249 RenderFrameHostImpl* FrameTree::GetMainFrame() const {
250 return root_->current_frame_host();
253 FrameTreeNode* FrameTree::GetFocusedFrame() {
254 return FindByID(focused_frame_tree_node_id_);
257 void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
258 focused_frame_tree_node_id_ = node->frame_tree_node_id();
261 void FrameTree::SetFrameRemoveListener(
262 const base::Callback<void(RenderFrameHost*)>& on_frame_removed) {
263 on_frame_removed_ = on_frame_removed;
266 RenderViewHostImpl* FrameTree::CreateRenderViewHost(SiteInstance* site_instance,
267 int routing_id,
268 int main_frame_routing_id,
269 bool swapped_out,
270 bool hidden) {
271 RenderViewHostMap::iterator iter =
272 render_view_host_map_.find(site_instance->GetId());
273 if (iter != render_view_host_map_.end()) {
274 // If a RenderViewHost's main frame is pending deletion for this
275 // |site_instance|, put it in the map of RenderViewHosts pending shutdown.
276 // Otherwise return the existing RenderViewHost for the SiteInstance.
277 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
278 iter->second->GetMainFrame());
279 if (main_frame &&
280 main_frame->frame_tree_node()->render_manager()->IsPendingDeletion(
281 main_frame)) {
282 render_view_host_pending_shutdown_map_.insert(
283 std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
284 iter->second));
285 render_view_host_map_.erase(iter);
286 } else {
287 return iter->second;
290 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
291 RenderViewHostFactory::Create(site_instance,
292 render_view_delegate_,
293 render_widget_delegate_,
294 routing_id,
295 main_frame_routing_id,
296 swapped_out,
297 hidden));
299 render_view_host_map_[site_instance->GetId()] = rvh;
300 return rvh;
303 RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) {
304 RenderViewHostMap::iterator iter =
305 render_view_host_map_.find(site_instance->GetId());
306 if (iter == render_view_host_map_.end())
307 return nullptr;
308 return iter->second;
311 void FrameTree::AddRenderViewHostRef(RenderViewHostImpl* render_view_host) {
312 SiteInstance* site_instance = render_view_host->GetSiteInstance();
313 RenderViewHostMap::iterator iter =
314 render_view_host_map_.find(site_instance->GetId());
315 CHECK(iter != render_view_host_map_.end());
316 CHECK(iter->second == render_view_host);
318 iter->second->increment_ref_count();
321 void FrameTree::ReleaseRenderViewHostRef(RenderViewHostImpl* render_view_host) {
322 SiteInstance* site_instance = render_view_host->GetSiteInstance();
323 int32 site_instance_id = site_instance->GetId();
324 RenderViewHostMap::iterator iter =
325 render_view_host_map_.find(site_instance_id);
326 if (iter != render_view_host_map_.end() && iter->second == render_view_host) {
327 // Decrement the refcount and shutdown the RenderViewHost if no one else is
328 // using it.
329 CHECK_GT(iter->second->ref_count(), 0);
330 iter->second->decrement_ref_count();
331 if (iter->second->ref_count() == 0) {
332 iter->second->Shutdown();
333 render_view_host_map_.erase(iter);
335 } else {
336 // The RenderViewHost should be in the list of RenderViewHosts pending
337 // shutdown.
338 bool render_view_host_found = false;
339 std::pair<RenderViewHostMultiMap::iterator,
340 RenderViewHostMultiMap::iterator> result =
341 render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
342 for (RenderViewHostMultiMap::iterator multi_iter = result.first;
343 multi_iter != result.second;
344 ++multi_iter) {
345 if (multi_iter->second != render_view_host)
346 continue;
347 render_view_host_found = true;
348 // Decrement the refcount and shutdown the RenderViewHost if no one else
349 // is using it.
350 CHECK_GT(render_view_host->ref_count(), 0);
351 render_view_host->decrement_ref_count();
352 if (render_view_host->ref_count() == 0) {
353 render_view_host->Shutdown();
354 render_view_host_pending_shutdown_map_.erase(multi_iter);
356 break;
358 CHECK(render_view_host_found);
362 void FrameTree::FrameRemoved(FrameTreeNode* frame) {
363 if (frame->frame_tree_node_id() == focused_frame_tree_node_id_)
364 focused_frame_tree_node_id_ = -1;
366 // No notification for the root frame.
367 if (!frame->parent()) {
368 CHECK_EQ(frame, root_.get());
369 return;
372 // Notify observers of the frame removal.
373 if (!on_frame_removed_.is_null())
374 on_frame_removed_.Run(frame->current_frame_host());
377 void FrameTree::UpdateLoadProgress() {
378 double progress = 0.0;
379 int frame_count = 0;
381 ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count));
382 if (frame_count != 0)
383 progress /= frame_count;
385 if (progress <= load_progress_)
386 return;
387 load_progress_ = progress;
389 // Notify the WebContents.
390 root_->navigator()->GetDelegate()->DidChangeLoadProgress();
393 void FrameTree::ResetLoadProgress() {
394 ForEach(base::Bind(&ResetNodeLoadProgress));
395 load_progress_ = 0.0;
398 bool FrameTree::IsLoading() {
399 bool is_loading = false;
400 ForEach(base::Bind(&IsNodeLoading, &is_loading));
401 return is_loading;
404 } // namespace content