Move OpenURL from RenderView to RenderFrame.
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager.cc
blob5db47137cba75fcacbbca3aa3d0d191d0b5366ec
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/render_frame_host_manager.h"
7 #include <utility>
9 #include "base/command_line.h"
10 #include "base/debug/trace_event.h"
11 #include "base/logging.h"
12 #include "content/browser/child_process_security_policy_impl.h"
13 #include "content/browser/devtools/render_view_devtools_agent_host.h"
14 #include "content/browser/frame_host/cross_process_frame_connector.h"
15 #include "content/browser/frame_host/debug_urls.h"
16 #include "content/browser/frame_host/interstitial_page_impl.h"
17 #include "content/browser/frame_host/navigation_controller_impl.h"
18 #include "content/browser/frame_host/navigation_entry_impl.h"
19 #include "content/browser/frame_host/navigator.h"
20 #include "content/browser/frame_host/render_frame_host_factory.h"
21 #include "content/browser/frame_host/render_frame_host_impl.h"
22 #include "content/browser/renderer_host/cross_site_transferring_request.h"
23 #include "content/browser/renderer_host/render_process_host_impl.h"
24 #include "content/browser/renderer_host/render_view_host_factory.h"
25 #include "content/browser/renderer_host/render_view_host_impl.h"
26 #include "content/browser/site_instance_impl.h"
27 #include "content/browser/webui/web_ui_controller_factory_registry.h"
28 #include "content/browser/webui/web_ui_impl.h"
29 #include "content/common/view_messages.h"
30 #include "content/port/browser/render_widget_host_view_port.h"
31 #include "content/public/browser/content_browser_client.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/notification_types.h"
34 #include "content/public/browser/render_widget_host_iterator.h"
35 #include "content/public/browser/user_metrics.h"
36 #include "content/public/browser/web_ui_controller.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/url_constants.h"
40 namespace content {
42 RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams(
43 const GlobalRequestID& global_request_id,
44 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
45 const std::vector<GURL>& transfer_url_chain,
46 Referrer referrer,
47 PageTransition page_transition,
48 int64 frame_id,
49 bool should_replace_current_entry)
50 : global_request_id(global_request_id),
51 cross_site_transferring_request(cross_site_transferring_request.Pass()),
52 transfer_url_chain(transfer_url_chain),
53 referrer(referrer),
54 page_transition(page_transition),
55 frame_id(frame_id),
56 should_replace_current_entry(should_replace_current_entry) {
59 RenderFrameHostManager::PendingNavigationParams::~PendingNavigationParams() {}
61 bool RenderFrameHostManager::ClearRFHsPendingShutdown(FrameTreeNode* node) {
62 node->render_manager()->pending_delete_hosts_.clear();
63 return true;
66 RenderFrameHostManager::RenderFrameHostManager(
67 FrameTreeNode* frame_tree_node,
68 RenderFrameHostDelegate* render_frame_delegate,
69 RenderViewHostDelegate* render_view_delegate,
70 RenderWidgetHostDelegate* render_widget_delegate,
71 Delegate* delegate)
72 : frame_tree_node_(frame_tree_node),
73 delegate_(delegate),
74 cross_navigation_pending_(false),
75 render_frame_delegate_(render_frame_delegate),
76 render_view_delegate_(render_view_delegate),
77 render_widget_delegate_(render_widget_delegate),
78 render_frame_host_(NULL),
79 pending_render_frame_host_(NULL),
80 interstitial_page_(NULL),
81 cross_process_frame_connector_(NULL),
82 weak_factory_(this) {}
84 RenderFrameHostManager::~RenderFrameHostManager() {
85 if (pending_render_frame_host_)
86 CancelPending();
88 if (cross_process_frame_connector_)
89 delete cross_process_frame_connector_;
91 // We should always have a current RenderFrameHost except in some tests.
92 // TODO(creis): Now that we aren't using Shutdown, make render_frame_host_ and
93 // RenderFrameHostMap use scoped_ptrs.
94 RenderFrameHostImpl* render_frame_host = render_frame_host_;
95 render_frame_host_ = NULL;
96 if (render_frame_host)
97 delete render_frame_host;
99 // Delete any swapped out RenderFrameHosts.
100 for (RenderFrameHostMap::iterator iter = swapped_out_hosts_.begin();
101 iter != swapped_out_hosts_.end();
102 ++iter) {
103 delete iter->second;
107 void RenderFrameHostManager::Init(BrowserContext* browser_context,
108 SiteInstance* site_instance,
109 int view_routing_id,
110 int frame_routing_id) {
111 // Create a RenderViewHost and RenderFrameHost, once we have an instance. It
112 // is important to immediately give this SiteInstance to a RenderViewHost so
113 // that the SiteInstance is ref counted.
114 if (!site_instance)
115 site_instance = SiteInstance::Create(browser_context);
117 // TODO(creis): Make render_frame_host_ a scoped_ptr.
118 render_frame_host_ = CreateRenderFrameHost(site_instance, view_routing_id,
119 frame_routing_id, false,
120 delegate_->IsHidden());
122 // Keep track of renderer processes as they start to shut down or are
123 // crashed/killed.
124 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
125 NotificationService::AllSources());
126 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSING,
127 NotificationService::AllSources());
130 RenderViewHostImpl* RenderFrameHostManager::current_host() const {
131 if (!render_frame_host_)
132 return NULL;
133 return render_frame_host_->render_view_host();
136 RenderViewHostImpl* RenderFrameHostManager::pending_render_view_host() const {
137 if (!pending_render_frame_host_)
138 return NULL;
139 return pending_render_frame_host_->render_view_host();
142 RenderWidgetHostView* RenderFrameHostManager::GetRenderWidgetHostView() const {
143 if (interstitial_page_)
144 return interstitial_page_->GetView();
145 if (!render_frame_host_)
146 return NULL;
147 return render_frame_host_->render_view_host()->GetView();
150 void RenderFrameHostManager::SetPendingWebUI(const NavigationEntryImpl& entry) {
151 pending_web_ui_.reset(
152 delegate_->CreateWebUIForRenderManager(entry.GetURL()));
153 pending_and_current_web_ui_.reset();
155 // If we have assigned (zero or more) bindings to this NavigationEntry in the
156 // past, make sure we're not granting it different bindings than it had
157 // before. If so, note it and don't give it any bindings, to avoid a
158 // potential privilege escalation.
159 if (pending_web_ui_.get() &&
160 entry.bindings() != NavigationEntryImpl::kInvalidBindings &&
161 pending_web_ui_->GetBindings() != entry.bindings()) {
162 RecordAction(
163 base::UserMetricsAction("ProcessSwapBindingsMismatch_RVHM"));
164 pending_web_ui_.reset();
168 RenderFrameHostImpl* RenderFrameHostManager::Navigate(
169 const NavigationEntryImpl& entry) {
170 TRACE_EVENT0("browser", "RenderFrameHostManager:Navigate");
171 // Create a pending RenderFrameHost to use for the navigation.
172 RenderFrameHostImpl* dest_render_frame_host =
173 UpdateRendererStateForNavigate(entry);
174 if (!dest_render_frame_host)
175 return NULL; // We weren't able to create a pending render frame host.
177 // If the current render_frame_host_ isn't live, we should create it so
178 // that we don't show a sad tab while the dest_render_frame_host fetches
179 // its first page. (Bug 1145340)
180 if (dest_render_frame_host != render_frame_host_ &&
181 !render_frame_host_->render_view_host()->IsRenderViewLive()) {
182 // Note: we don't call InitRenderView here because we are navigating away
183 // soon anyway, and we don't have the NavigationEntry for this host.
184 delegate_->CreateRenderViewForRenderManager(
185 render_frame_host_->render_view_host(), MSG_ROUTING_NONE, NULL);
188 // If the renderer crashed, then try to create a new one to satisfy this
189 // navigation request.
190 if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) {
191 // Recreate the opener chain.
192 int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager(
193 dest_render_frame_host->render_view_host()->GetSiteInstance());
194 if (!InitRenderView(dest_render_frame_host->render_view_host(),
195 opener_route_id))
196 return NULL;
198 // Now that we've created a new renderer, be sure to hide it if it isn't
199 // our primary one. Otherwise, we might crash if we try to call Show()
200 // on it later.
201 if (dest_render_frame_host != render_frame_host_ &&
202 dest_render_frame_host->render_view_host()->GetView()) {
203 dest_render_frame_host->render_view_host()->GetView()->Hide();
204 } else if (frame_tree_node_->IsMainFrame()) {
205 // This is our primary renderer, notify here as we won't be calling
206 // CommitPending (which does the notify). We only do this for top-level
207 // frames.
208 delegate_->NotifySwappedFromRenderManager(
209 NULL, render_frame_host_->render_view_host());
213 // If entry includes the request ID of a request that is being transferred,
214 // the destination render frame will take ownership, so release ownership of
215 // the request.
216 if (pending_nav_params_ &&
217 pending_nav_params_->global_request_id ==
218 entry.transferred_global_request_id()) {
219 pending_nav_params_->cross_site_transferring_request->ReleaseRequest();
222 return dest_render_frame_host;
225 void RenderFrameHostManager::Stop() {
226 render_frame_host_->render_view_host()->Stop();
228 // If we are cross-navigating, we should stop the pending renderers. This
229 // will lead to a DidFailProvisionalLoad, which will properly destroy them.
230 if (cross_navigation_pending_) {
231 pending_render_frame_host_->render_view_host()->Send(new ViewMsg_Stop(
232 pending_render_frame_host_->render_view_host()->GetRoutingID()));
236 void RenderFrameHostManager::SetIsLoading(bool is_loading) {
237 render_frame_host_->render_view_host()->SetIsLoading(is_loading);
238 if (pending_render_frame_host_)
239 pending_render_frame_host_->render_view_host()->SetIsLoading(is_loading);
242 bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() {
243 if (!cross_navigation_pending_)
244 return true;
246 // We should always have a pending RFH when there's a cross-process navigation
247 // in progress. Sanity check this for http://crbug.com/276333.
248 CHECK(pending_render_frame_host_);
250 // If the tab becomes unresponsive during {before}unload while doing a
251 // cross-site navigation, proceed with the navigation. (This assumes that
252 // the pending RenderFrameHost is still responsive.)
253 if (render_frame_host_->render_view_host()->IsWaitingForUnloadACK()) {
254 // The request has been started and paused while we're waiting for the
255 // unload handler to finish. We'll pretend that it did. The pending
256 // renderer will then be swapped in as part of the usual DidNavigate logic.
257 // (If the unload handler later finishes, this call will be ignored because
258 // the pending_nav_params_ state will already be cleaned up.)
259 current_host()->OnSwappedOut(true);
260 } else if (render_frame_host_->render_view_host()->
261 is_waiting_for_beforeunload_ack()) {
262 // Haven't gotten around to starting the request, because we're still
263 // waiting for the beforeunload handler to finish. We'll pretend that it
264 // did finish, to let the navigation proceed. Note that there's a danger
265 // that the beforeunload handler will later finish and possibly return
266 // false (meaning the navigation should not proceed), but we'll ignore it
267 // in this case because it took too long.
268 if (pending_render_frame_host_->render_view_host()->
269 are_navigations_suspended()) {
270 pending_render_frame_host_->render_view_host()->SetNavigationsSuspended(
271 false, base::TimeTicks::Now());
274 return false;
277 // TODO(creis): Remove this in favor of SwappedOutFrame.
278 void RenderFrameHostManager::SwappedOut(RenderViewHost* render_view_host) {
279 // Make sure this is from our current RVH, and that we have a pending
280 // navigation from OnCrossSiteResponse. (There may be no pending navigation
281 // for data URLs that don't make network requests, for example.) If not,
282 // just return early and ignore.
283 if (render_view_host != render_frame_host_->render_view_host() ||
284 !pending_nav_params_.get()) {
285 pending_nav_params_.reset();
286 return;
289 // Now that the unload handler has run, we need to either initiate the
290 // pending transfer (if there is one) or resume the paused response (if not).
291 // TODO(creis): The blank swapped out page is visible during this time, but
292 // we can shorten this by delivering the response directly, rather than
293 // forcing an identical request to be made.
294 if (pending_nav_params_->cross_site_transferring_request) {
295 // Treat the last URL in the chain as the destination and the remainder as
296 // the redirect chain.
297 CHECK(pending_nav_params_->transfer_url_chain.size());
298 GURL transfer_url = pending_nav_params_->transfer_url_chain.back();
299 pending_nav_params_->transfer_url_chain.pop_back();
301 RenderFrameHostImpl* render_frame_host =
302 static_cast<RenderFrameHostImpl*>(render_view_host->GetMainFrame());
304 // We don't know whether the original request had |user_action| set to true.
305 // However, since we force the navigation to be in the current tab, it
306 // doesn't matter.
307 render_frame_host->frame_tree_node()->navigator()->RequestTransferURL(
308 render_frame_host,
309 transfer_url,
310 pending_nav_params_->transfer_url_chain,
311 pending_nav_params_->referrer,
312 pending_nav_params_->page_transition,
313 CURRENT_TAB,
314 pending_nav_params_->frame_id,
315 pending_nav_params_->global_request_id,
316 pending_nav_params_->should_replace_current_entry,
317 true);
318 } else if (pending_render_frame_host_) {
319 RenderProcessHostImpl* pending_process =
320 static_cast<RenderProcessHostImpl*>(
321 pending_render_frame_host_->GetProcess());
322 pending_process->ResumeDeferredNavigation(
323 pending_nav_params_->global_request_id);
325 pending_nav_params_.reset();
328 void RenderFrameHostManager::SwappedOutFrame(
329 RenderFrameHostImpl* render_frame_host) {
330 // Make sure this is from our current RFH, and that we have a pending
331 // navigation from OnCrossSiteResponse. (There may be no pending navigation
332 // for data URLs that don't make network requests, for example.) If not,
333 // just return early and ignore.
334 if (render_frame_host != render_frame_host_ || !pending_nav_params_.get()) {
335 pending_nav_params_.reset();
336 return;
339 // Sanity check that this is for the correct frame.
340 DCHECK_EQ(frame_tree_node_->current_frame_host()->GetRoutingID(),
341 pending_nav_params_->frame_id);
342 DCHECK_EQ(frame_tree_node_->current_frame_host()->GetProcess()->GetID(),
343 pending_nav_params_->global_request_id.child_id);
345 // Now that the unload handler has run, we need to either initiate the
346 // pending transfer (if there is one) or resume the paused response (if not).
347 // TODO(creis): The blank swapped out page is visible during this time, but
348 // we can shorten this by delivering the response directly, rather than
349 // forcing an identical request to be made.
350 if (pending_nav_params_->cross_site_transferring_request) {
351 // Treat the last URL in the chain as the destination and the remainder as
352 // the redirect chain.
353 CHECK(pending_nav_params_->transfer_url_chain.size());
354 GURL transfer_url = pending_nav_params_->transfer_url_chain.back();
355 pending_nav_params_->transfer_url_chain.pop_back();
357 // We don't know whether the original request had |user_action| set to true.
358 // However, since we force the navigation to be in the current tab, it
359 // doesn't matter.
360 render_frame_host->frame_tree_node()->navigator()->RequestTransferURL(
361 render_frame_host,
362 transfer_url,
363 pending_nav_params_->transfer_url_chain,
364 pending_nav_params_->referrer,
365 pending_nav_params_->page_transition,
366 CURRENT_TAB,
367 pending_nav_params_->frame_id,
368 pending_nav_params_->global_request_id,
369 false,
370 true);
371 } else if (pending_render_frame_host_) {
372 RenderProcessHostImpl* pending_process =
373 static_cast<RenderProcessHostImpl*>(
374 pending_render_frame_host_->GetProcess());
375 pending_process->ResumeDeferredNavigation(
376 pending_nav_params_->global_request_id);
378 pending_nav_params_.reset();
381 // TODO(creis): This should take in a RenderFrameHost.
382 void RenderFrameHostManager::DidNavigateMainFrame(
383 RenderViewHost* render_view_host) {
384 if (!cross_navigation_pending_) {
385 DCHECK(!pending_render_frame_host_);
387 // We should only hear this from our current renderer.
388 DCHECK(render_view_host == render_frame_host_->render_view_host());
390 // Even when there is no pending RVH, there may be a pending Web UI.
391 if (pending_web_ui())
392 CommitPending();
393 return;
396 if (render_view_host == pending_render_frame_host_->render_view_host()) {
397 // The pending cross-site navigation completed, so show the renderer.
398 // If it committed without sending network requests (e.g., data URLs),
399 // then we still need to swap out the old RFH first and run its unload
400 // handler. OK for that to happen in the background.
401 if (pending_render_frame_host_->render_view_host()->
402 HasPendingCrossSiteRequest())
403 SwapOutOldPage();
405 CommitPending();
406 cross_navigation_pending_ = false;
407 } else if (render_view_host == render_frame_host_->render_view_host()) {
408 // A navigation in the original page has taken place. Cancel the pending
409 // one.
410 CancelPending();
411 cross_navigation_pending_ = false;
412 } else {
413 // No one else should be sending us DidNavigate in this state.
414 DCHECK(false);
418 // TODO(creis): Take in RenderFrameHost instead, since frames can have openers.
419 void RenderFrameHostManager::DidDisownOpener(RenderViewHost* render_view_host) {
420 // Notify all swapped out hosts, including the pending RVH.
421 for (RenderFrameHostMap::iterator iter = swapped_out_hosts_.begin();
422 iter != swapped_out_hosts_.end();
423 ++iter) {
424 DCHECK_NE(iter->second->render_view_host()->GetSiteInstance(),
425 current_host()->GetSiteInstance());
426 iter->second->render_view_host()->DisownOpener();
430 void RenderFrameHostManager::RendererProcessClosing(
431 RenderProcessHost* render_process_host) {
432 // Remove any swapped out RVHs from this process, so that we don't try to
433 // swap them back in while the process is exiting. Start by finding them,
434 // since there could be more than one.
435 std::list<int> ids_to_remove;
436 for (RenderFrameHostMap::iterator iter = swapped_out_hosts_.begin();
437 iter != swapped_out_hosts_.end();
438 ++iter) {
439 if (iter->second->GetProcess() == render_process_host)
440 ids_to_remove.push_back(iter->first);
443 // Now delete them.
444 while (!ids_to_remove.empty()) {
445 delete swapped_out_hosts_[ids_to_remove.back()];
446 swapped_out_hosts_.erase(ids_to_remove.back());
447 ids_to_remove.pop_back();
451 void RenderFrameHostManager::ShouldClosePage(
452 bool for_cross_site_transition,
453 bool proceed,
454 const base::TimeTicks& proceed_time) {
455 if (for_cross_site_transition) {
456 // Ignore if we're not in a cross-site navigation.
457 if (!cross_navigation_pending_)
458 return;
460 if (proceed) {
461 // Ok to unload the current page, so proceed with the cross-site
462 // navigation. Note that if navigations are not currently suspended, it
463 // might be because the renderer was deemed unresponsive and this call was
464 // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it
465 // is ok to do nothing here.
466 if (pending_render_frame_host_ &&
467 pending_render_frame_host_->render_view_host()->
468 are_navigations_suspended()) {
469 pending_render_frame_host_->render_view_host()->
470 SetNavigationsSuspended(false, proceed_time);
472 } else {
473 // Current page says to cancel.
474 CancelPending();
475 cross_navigation_pending_ = false;
477 } else {
478 // Non-cross site transition means closing the entire tab.
479 bool proceed_to_fire_unload;
480 delegate_->BeforeUnloadFiredFromRenderManager(proceed, proceed_time,
481 &proceed_to_fire_unload);
483 if (proceed_to_fire_unload) {
484 // If we're about to close the tab and there's a pending RFH, cancel it.
485 // Otherwise, if the navigation in the pending RFH completes before the
486 // close in the current RFH, we'll lose the tab close.
487 if (pending_render_frame_host_) {
488 CancelPending();
489 cross_navigation_pending_ = false;
492 // This is not a cross-site navigation, the tab is being closed.
493 render_frame_host_->render_view_host()->ClosePage();
498 // TODO(creis): Take in a RenderFrameHost from CSRH.
499 void RenderFrameHostManager::OnCrossSiteResponse(
500 RenderViewHost* pending_render_view_host,
501 const GlobalRequestID& global_request_id,
502 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
503 const std::vector<GURL>& transfer_url_chain,
504 const Referrer& referrer,
505 PageTransition page_transition,
506 int64 frame_id,
507 bool should_replace_current_entry) {
508 // This should be called either when the pending RVH is ready to commit or
509 // when we realize that the current RVH's request requires a transfer.
510 DCHECK(pending_render_view_host == render_frame_host_->render_view_host() ||
511 pending_render_view_host ==
512 pending_render_frame_host_->render_view_host());
514 // TODO(creis): Eventually we will want to check all navigation responses
515 // here, but currently we pass information for a transfer if
516 // ShouldSwapProcessesForRedirect returned true in the network stack.
517 // In that case, we should set up a transfer after the unload handler runs.
518 // If |cross_site_transferring_request| is NULL, we will just run the unload
519 // handler and resume.
520 pending_nav_params_.reset(new PendingNavigationParams(
521 global_request_id, cross_site_transferring_request.Pass(),
522 transfer_url_chain, referrer, page_transition, frame_id,
523 should_replace_current_entry));
525 // Run the unload handler of the current page.
526 SwapOutOldPage();
529 void RenderFrameHostManager::SwapOutOldPage() {
530 // Should only see this while we have a pending renderer or transfer.
531 CHECK(cross_navigation_pending_ || pending_nav_params_.get());
533 // Tell the renderer to suppress any further modal dialogs so that we can swap
534 // it out. This must be done before canceling any current dialog, in case
535 // there is a loop creating additional dialogs.
536 render_frame_host_->render_view_host()->SuppressDialogsUntilSwapOut();
538 // Now close any modal dialogs that would prevent us from swapping out. This
539 // must be done separately from SwapOut, so that the PageGroupLoadDeferrer is
540 // no longer on the stack when we send the SwapOut message.
541 delegate_->CancelModalDialogsForRenderManager();
543 // Tell the old renderer it is being swapped out. This will fire the unload
544 // handler (without firing the beforeunload handler a second time). When the
545 // unload handler finishes and the navigation completes, we will send a
546 // message to the ResourceDispatcherHost, allowing the pending RVH's response
547 // to resume.
548 // Note: This must be done on the RFH or else we'll swap out the top-level
549 // page when subframes navigate.
550 if (frame_tree_node_->IsMainFrame()) {
551 render_frame_host_->render_view_host()->SwapOut();
552 } else {
553 // The RenderFrameHost being swapped out becomes the proxy for this
554 // frame in its parent's process. CrossProcessFrameConnector
555 // initialization only needs to happen on an initial cross-process
556 // navigation, when the RenderFrame leaves the same process as its parent.
557 // The same CrossProcessFrameConnector is used for subsequent cross-
558 // process navigations, but it will be destroyed if the Frame is
559 // navigated back to the same site instance as its parent.
560 // TODO(kenrb): This will change when RenderFrameProxyHost is created.
561 if (!cross_process_frame_connector_) {
562 cross_process_frame_connector_ =
563 new CrossProcessFrameConnector(render_frame_host_);
565 render_frame_host_->SwapOut();
568 // ResourceDispatcherHost has told us to run the onunload handler, which
569 // means it is not a download or unsafe page, and we are going to perform the
570 // navigation. Thus, we no longer need to remember that the RenderFrameHost
571 // is part of a pending cross-site request.
572 if (pending_render_frame_host_) {
573 pending_render_frame_host_->render_view_host()->
574 SetHasPendingCrossSiteRequest(false);
578 void RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance(
579 int32 site_instance_id,
580 RenderFrameHostImpl* rfh) {
581 RFHPendingDeleteMap::iterator iter =
582 pending_delete_hosts_.find(site_instance_id);
583 if (iter != pending_delete_hosts_.end() && iter->second.get() == rfh)
584 pending_delete_hosts_.erase(site_instance_id);
587 void RenderFrameHostManager::Observe(
588 int type,
589 const NotificationSource& source,
590 const NotificationDetails& details) {
591 switch (type) {
592 case NOTIFICATION_RENDERER_PROCESS_CLOSED:
593 case NOTIFICATION_RENDERER_PROCESS_CLOSING:
594 RendererProcessClosing(
595 Source<RenderProcessHost>(source).ptr());
596 break;
598 default:
599 NOTREACHED();
603 bool RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance(
604 int32 site_instance_id,
605 FrameTreeNode* node) {
606 RenderFrameHostMap::iterator iter =
607 node->render_manager()->swapped_out_hosts_.find(site_instance_id);
608 if (iter != node->render_manager()->swapped_out_hosts_.end()) {
609 RenderFrameHostImpl* swapped_out_rfh = iter->second;
610 // If the RVH is pending swap out, it needs to switch state to
611 // pending shutdown. Otherwise it is deleted.
612 if (swapped_out_rfh->render_view_host()->rvh_state() ==
613 RenderViewHostImpl::STATE_PENDING_SWAP_OUT) {
614 swapped_out_rfh->SetPendingShutdown(base::Bind(
615 &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
616 node->render_manager()->weak_factory_.GetWeakPtr(),
617 site_instance_id,
618 swapped_out_rfh));
619 RFHPendingDeleteMap::iterator pending_delete_iter =
620 node->render_manager()->pending_delete_hosts_.find(site_instance_id);
621 if (pending_delete_iter ==
622 node->render_manager()->pending_delete_hosts_.end() ||
623 pending_delete_iter->second.get() != iter->second) {
624 node->render_manager()->pending_delete_hosts_[site_instance_id] =
625 linked_ptr<RenderFrameHostImpl>(swapped_out_rfh);
627 } else {
628 delete swapped_out_rfh;
630 node->render_manager()->swapped_out_hosts_.erase(site_instance_id);
633 return true;
636 bool RenderFrameHostManager::ShouldTransitionCrossSite() {
637 // False in the single-process mode, as it makes RVHs to accumulate
638 // in swapped_out_hosts_.
639 // True if we are using process-per-site-instance (default) or
640 // process-per-site (kProcessPerSite).
641 return
642 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) &&
643 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab);
646 bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
647 const NavigationEntry* current_entry,
648 const NavigationEntryImpl* new_entry) const {
649 DCHECK(new_entry);
651 // If new_entry already has a SiteInstance, assume it is correct. We only
652 // need to force a swap if it is in a different BrowsingInstance.
653 if (new_entry->site_instance()) {
654 return !new_entry->site_instance()->IsRelatedSiteInstance(
655 render_frame_host_->GetSiteInstance());
658 // Check for reasons to swap processes even if we are in a process model that
659 // doesn't usually swap (e.g., process-per-tab). Any time we return true,
660 // the new_entry will be rendered in a new SiteInstance AND BrowsingInstance.
662 // We use the effective URL here, since that's what is used in the
663 // SiteInstance's site and when we later call IsSameWebSite. If there is no
664 // current_entry, check the current SiteInstance's site, which might already
665 // be committed to a Web UI URL (such as the NTP).
666 BrowserContext* browser_context =
667 delegate_->GetControllerForRenderManager().GetBrowserContext();
668 const GURL& current_url = (current_entry) ?
669 SiteInstanceImpl::GetEffectiveURL(browser_context,
670 current_entry->GetURL()) :
671 render_frame_host_->render_view_host()->GetSiteInstance()->GetSiteURL();
672 const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context,
673 new_entry->GetURL());
675 // Don't force a new BrowsingInstance for debug URLs that are handled in the
676 // renderer process, like javascript: or chrome://crash.
677 if (IsRendererDebugURL(new_url))
678 return false;
680 // For security, we should transition between processes when one is a Web UI
681 // page and one isn't.
682 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
683 browser_context, current_url)) {
684 // If so, force a swap if destination is not an acceptable URL for Web UI.
685 // Here, data URLs are never allowed.
686 if (!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
687 browser_context, new_url)) {
688 return true;
690 } else {
691 // Force a swap if it's a Web UI URL.
692 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
693 browser_context, new_url)) {
694 return true;
698 // Check with the content client as well. Important to pass current_url here,
699 // which uses the SiteInstance's site if there is no current_entry.
700 if (GetContentClient()->browser()->ShouldSwapBrowsingInstancesForNavigation(
701 render_frame_host_->render_view_host()->GetSiteInstance(),
702 current_url, new_url)) {
703 return true;
706 // We can't switch a RenderView between view source and non-view source mode
707 // without screwing up the session history sometimes (when navigating between
708 // "view-source:http://foo.com/" and "http://foo.com/", Blink doesn't treat
709 // it as a new navigation). So require a BrowsingInstance switch.
710 if (current_entry &&
711 current_entry->IsViewSourceMode() != new_entry->IsViewSourceMode())
712 return true;
714 return false;
717 bool RenderFrameHostManager::ShouldReuseWebUI(
718 const NavigationEntry* current_entry,
719 const NavigationEntryImpl* new_entry) const {
720 NavigationControllerImpl& controller =
721 delegate_->GetControllerForRenderManager();
722 return current_entry && web_ui_.get() &&
723 (WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
724 controller.GetBrowserContext(), current_entry->GetURL()) ==
725 WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
726 controller.GetBrowserContext(), new_entry->GetURL()));
729 SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry(
730 const NavigationEntryImpl& entry,
731 SiteInstance* current_instance,
732 bool force_browsing_instance_swap) {
733 // Determine which SiteInstance to use for navigating to |entry|.
734 const GURL& dest_url = entry.GetURL();
735 NavigationControllerImpl& controller =
736 delegate_->GetControllerForRenderManager();
737 BrowserContext* browser_context = controller.GetBrowserContext();
739 // If the entry has an instance already we should use it.
740 if (entry.site_instance()) {
741 // If we are forcing a swap, this should be in a different BrowsingInstance.
742 if (force_browsing_instance_swap) {
743 CHECK(!entry.site_instance()->IsRelatedSiteInstance(
744 render_frame_host_->GetSiteInstance()));
746 return entry.site_instance();
749 // If a swap is required, we need to force the SiteInstance AND
750 // BrowsingInstance to be different ones, using CreateForURL.
751 if (force_browsing_instance_swap)
752 return SiteInstance::CreateForURL(browser_context, dest_url);
754 // (UGLY) HEURISTIC, process-per-site only:
756 // If this navigation is generated, then it probably corresponds to a search
757 // query. Given that search results typically lead to users navigating to
758 // other sites, we don't really want to use the search engine hostname to
759 // determine the site instance for this navigation.
761 // NOTE: This can be removed once we have a way to transition between
762 // RenderViews in response to a link click.
764 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) &&
765 PageTransitionCoreTypeIs(entry.GetTransitionType(),
766 PAGE_TRANSITION_GENERATED)) {
767 return current_instance;
770 SiteInstanceImpl* current_site_instance =
771 static_cast<SiteInstanceImpl*>(current_instance);
773 // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it
774 // for this entry. We won't commit the SiteInstance to this site until the
775 // navigation commits (in DidNavigate), unless the navigation entry was
776 // restored or it's a Web UI as described below.
777 if (!current_site_instance->HasSite()) {
778 // If we've already created a SiteInstance for our destination, we don't
779 // want to use this unused SiteInstance; use the existing one. (We don't
780 // do this check if the current_instance has a site, because for now, we
781 // want to compare against the current URL and not the SiteInstance's site.
782 // In this case, there is no current URL, so comparing against the site is
783 // ok. See additional comments below.)
785 // Also, if the URL should use process-per-site mode and there is an
786 // existing process for the site, we should use it. We can call
787 // GetRelatedSiteInstance() for this, which will eagerly set the site and
788 // thus use the correct process.
789 bool use_process_per_site =
790 RenderProcessHost::ShouldUseProcessPerSite(browser_context, dest_url) &&
791 RenderProcessHostImpl::GetProcessHostForSite(browser_context, dest_url);
792 if (current_site_instance->HasRelatedSiteInstance(dest_url) ||
793 use_process_per_site) {
794 return current_site_instance->GetRelatedSiteInstance(dest_url);
797 // For extensions, Web UI URLs (such as the new tab page), and apps we do
798 // not want to use the current_instance if it has no site, since it will
799 // have a RenderProcessHost of PRIV_NORMAL. Create a new SiteInstance for
800 // this URL instead (with the correct process type).
801 if (current_site_instance->HasWrongProcessForURL(dest_url))
802 return current_site_instance->GetRelatedSiteInstance(dest_url);
804 // View-source URLs must use a new SiteInstance and BrowsingInstance.
805 // TODO(nasko): This is the same condition as later in the function. This
806 // should be taken into account when refactoring this method as part of
807 // http://crbug.com/123007.
808 if (entry.IsViewSourceMode())
809 return SiteInstance::CreateForURL(browser_context, dest_url);
811 // If we are navigating from a blank SiteInstance to a WebUI, make sure we
812 // create a new SiteInstance.
813 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
814 browser_context, dest_url)) {
815 return SiteInstance::CreateForURL(browser_context, dest_url);
818 // Normally the "site" on the SiteInstance is set lazily when the load
819 // actually commits. This is to support better process sharing in case
820 // the site redirects to some other site: we want to use the destination
821 // site in the site instance.
823 // In the case of session restore, as it loads all the pages immediately
824 // we need to set the site first, otherwise after a restore none of the
825 // pages would share renderers in process-per-site.
826 if (entry.restore_type() != NavigationEntryImpl::RESTORE_NONE)
827 current_site_instance->SetSite(dest_url);
829 return current_site_instance;
832 // Otherwise, only create a new SiteInstance for a cross-site navigation.
834 // TODO(creis): Once we intercept links and script-based navigations, we
835 // will be able to enforce that all entries in a SiteInstance actually have
836 // the same site, and it will be safe to compare the URL against the
837 // SiteInstance's site, as follows:
838 // const GURL& current_url = current_instance->site();
839 // For now, though, we're in a hybrid model where you only switch
840 // SiteInstances if you type in a cross-site URL. This means we have to
841 // compare the entry's URL to the last committed entry's URL.
842 NavigationEntry* current_entry = controller.GetLastCommittedEntry();
843 if (interstitial_page_) {
844 // The interstitial is currently the last committed entry, but we want to
845 // compare against the last non-interstitial entry.
846 current_entry = controller.GetEntryAtOffset(-1);
848 // If there is no last non-interstitial entry (and current_instance already
849 // has a site), then we must have been opened from another tab. We want
850 // to compare against the URL of the page that opened us, but we can't
851 // get to it directly. The best we can do is check against the site of
852 // the SiteInstance. This will be correct when we intercept links and
853 // script-based navigations, but for now, it could place some pages in a
854 // new process unnecessarily. We should only hit this case if a page tries
855 // to open a new tab to an interstitial-inducing URL, and then navigates
856 // the page to a different same-site URL. (This seems very unlikely in
857 // practice.)
858 const GURL& current_url = (current_entry) ? current_entry->GetURL() :
859 current_instance->GetSiteURL();
861 // View-source URLs must use a new SiteInstance and BrowsingInstance.
862 // We don't need a swap when going from view-source to a debug URL like
863 // chrome://crash, however.
864 // TODO(creis): Refactor this method so this duplicated code isn't needed.
865 // See http://crbug.com/123007.
866 if (current_entry &&
867 current_entry->IsViewSourceMode() != entry.IsViewSourceMode() &&
868 !IsRendererDebugURL(dest_url)) {
869 return SiteInstance::CreateForURL(browser_context, dest_url);
872 // Use the current SiteInstance for same site navigations, as long as the
873 // process type is correct. (The URL may have been installed as an app since
874 // the last time we visited it.)
875 if (SiteInstance::IsSameWebSite(browser_context, current_url, dest_url) &&
876 !current_site_instance->HasWrongProcessForURL(dest_url)) {
877 return current_instance;
880 // Start the new renderer in a new SiteInstance, but in the current
881 // BrowsingInstance. It is important to immediately give this new
882 // SiteInstance to a RenderViewHost (if it is different than our current
883 // SiteInstance), so that it is ref counted. This will happen in
884 // CreateRenderView.
885 return current_instance->GetRelatedSiteInstance(dest_url);
888 RenderFrameHostImpl* RenderFrameHostManager::CreateRenderFrameHost(
889 SiteInstance* site_instance,
890 int view_routing_id,
891 int frame_routing_id,
892 bool swapped_out,
893 bool hidden) {
894 if (frame_routing_id == MSG_ROUTING_NONE)
895 frame_routing_id = site_instance->GetProcess()->GetNextRoutingID();
897 // Create a RVH for main frames, or find the existing one for subframes.
898 FrameTree* frame_tree = frame_tree_node_->frame_tree();
899 RenderViewHostImpl* render_view_host = NULL;
900 if (frame_tree_node_->IsMainFrame()) {
901 render_view_host = frame_tree->CreateRenderViewHostForMainFrame(
902 site_instance, view_routing_id, frame_routing_id, swapped_out, hidden);
903 } else {
904 render_view_host = frame_tree->GetRenderViewHostForSubFrame(site_instance);
906 // If we haven't found a RVH for a subframe RFH, it's because we currently
907 // do not create top-level RFHs for pending subframe navigations. Create
908 // the RVH here for now.
909 // TODO(creis): Mirror the frame tree so this check isn't necessary.
910 if (!render_view_host) {
911 render_view_host = frame_tree->CreateRenderViewHostForMainFrame(
912 site_instance, view_routing_id, frame_routing_id, swapped_out,
913 hidden);
917 // TODO(creis): Make render_frame_host a scoped_ptr.
918 // TODO(creis): Pass hidden to RFH.
919 RenderFrameHostImpl* render_frame_host =
920 RenderFrameHostFactory::Create(render_view_host,
921 render_frame_delegate_,
922 frame_tree,
923 frame_tree_node_,
924 frame_routing_id,
925 swapped_out).release();
926 return render_frame_host;
929 int RenderFrameHostManager::CreateRenderFrame(
930 SiteInstance* instance,
931 int opener_route_id,
932 bool swapped_out,
933 bool hidden) {
934 CHECK(instance);
935 DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden.
937 // We are creating a pending or swapped out RFH here. We should never create
938 // it in the same SiteInstance as our current RFH.
939 CHECK_NE(render_frame_host_->render_view_host()->GetSiteInstance(), instance);
941 // Check if we've already created an RFH for this SiteInstance. If so, try
942 // to re-use the existing one, which has already been initialized. We'll
943 // remove it from the list of swapped out hosts if it commits.
944 RenderFrameHostImpl* new_render_frame_host =
945 GetSwappedOutRenderFrameHost(instance);
947 FrameTreeNode* parent_node = NULL;
948 if (frame_tree_node_)
949 parent_node = frame_tree_node_->parent();
951 if (new_render_frame_host) {
952 // Prevent the process from exiting while we're trying to use it.
953 if (!swapped_out) {
954 new_render_frame_host->GetProcess()->AddPendingView();
955 } else {
956 // Detect if this is a cross-process child frame that is navigating
957 // back to the same SiteInstance as its parent.
958 if (parent_node && cross_process_frame_connector_ &&
959 render_frame_host_->GetSiteInstance() == parent_node->
960 render_manager()->current_frame_host()->GetSiteInstance()) {
961 delete cross_process_frame_connector_;
962 cross_process_frame_connector_ = NULL;
965 } else {
966 // Create a new RenderFrameHost if we don't find an existing one.
967 // TODO(creis): Make new_render_frame_host a scoped_ptr.
968 new_render_frame_host = CreateRenderFrameHost(instance, MSG_ROUTING_NONE,
969 MSG_ROUTING_NONE, swapped_out,
970 hidden);
972 // If the new RFH is swapped out already, store it. Otherwise prevent the
973 // process from exiting while we're trying to navigate in it.
974 if (swapped_out) {
975 swapped_out_hosts_[instance->GetId()] = new_render_frame_host;
976 } else {
977 new_render_frame_host->GetProcess()->AddPendingView();
980 RenderViewHostImpl* render_view_host =
981 new_render_frame_host->render_view_host();
982 bool success = InitRenderView(render_view_host, opener_route_id);
983 if (success && frame_tree_node_->IsMainFrame()) {
984 // Don't show the main frame's view until we get a DidNavigate from it.
985 render_view_host->GetView()->Hide();
986 } else if (!swapped_out && pending_render_frame_host_) {
987 CancelPending();
991 // Use this as our new pending RFH if it isn't swapped out.
992 if (!swapped_out)
993 pending_render_frame_host_ = new_render_frame_host;
995 return new_render_frame_host->render_view_host()->GetRoutingID();
998 bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host,
999 int opener_route_id) {
1000 // We may have initialized this RenderViewHost for another RenderFrameHost.
1001 if (render_view_host->IsRenderViewLive())
1002 return true;
1004 // If the pending navigation is to a WebUI and the RenderView is not in a
1005 // guest process, tell the RenderViewHost about any bindings it will need
1006 // enabled.
1007 if (pending_web_ui() && !render_view_host->GetProcess()->IsGuest()) {
1008 render_view_host->AllowBindings(pending_web_ui()->GetBindings());
1009 } else {
1010 // Ensure that we don't create an unprivileged RenderView in a WebUI-enabled
1011 // process unless it's swapped out.
1012 RenderViewHostImpl* rvh_impl =
1013 static_cast<RenderViewHostImpl*>(render_view_host);
1014 if (!rvh_impl->IsSwappedOut()) {
1015 CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1016 render_view_host->GetProcess()->GetID()));
1020 return delegate_->CreateRenderViewForRenderManager(
1021 render_view_host, opener_route_id, cross_process_frame_connector_);
1024 void RenderFrameHostManager::CommitPending() {
1025 // First check whether we're going to want to focus the location bar after
1026 // this commit. We do this now because the navigation hasn't formally
1027 // committed yet, so if we've already cleared |pending_web_ui_| the call chain
1028 // this triggers won't be able to figure out what's going on.
1029 bool will_focus_location_bar = delegate_->FocusLocationBarByDefault();
1031 // We expect SwapOutOldPage to have canceled any modal dialogs and told the
1032 // renderer to suppress any further dialogs until it is swapped out. However,
1033 // crash reports indicate that it's still possible for modal dialogs to exist
1034 // at this point, which poses a risk if we delete their RenderViewHost below.
1035 // Cancel them again to be safe. http://crbug.com/324320.
1036 delegate_->CancelModalDialogsForRenderManager();
1038 // Next commit the Web UI, if any. Either replace |web_ui_| with
1039 // |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or
1040 // leave |web_ui_| as is if reusing it.
1041 DCHECK(!(pending_web_ui_.get() && pending_and_current_web_ui_.get()));
1042 if (pending_web_ui_) {
1043 web_ui_.reset(pending_web_ui_.release());
1044 } else if (!pending_and_current_web_ui_.get()) {
1045 web_ui_.reset();
1046 } else {
1047 DCHECK_EQ(pending_and_current_web_ui_.get(), web_ui_.get());
1048 pending_and_current_web_ui_.reset();
1051 // It's possible for the pending_render_frame_host_ to be NULL when we aren't
1052 // crossing process boundaries. If so, we just needed to handle the Web UI
1053 // committing above and we're done.
1054 if (!pending_render_frame_host_) {
1055 if (will_focus_location_bar)
1056 delegate_->SetFocusToLocationBar(false);
1057 return;
1060 // Remember if the page was focused so we can focus the new renderer in
1061 // that case.
1062 bool focus_render_view = !will_focus_location_bar &&
1063 render_frame_host_->render_view_host()->GetView() &&
1064 render_frame_host_->render_view_host()->GetView()->HasFocus();
1066 // TODO(creis): As long as show/hide are on RVH, we don't want to do them for
1067 // subframe navigations or they'll interfere with the top-level page.
1068 bool is_main_frame = frame_tree_node_->IsMainFrame();
1070 // Swap in the pending frame and make it active. Also ensure the FrameTree
1071 // stays in sync.
1072 RenderFrameHostImpl* old_render_frame_host = render_frame_host_;
1073 render_frame_host_ = pending_render_frame_host_;
1074 pending_render_frame_host_ = NULL;
1075 if (is_main_frame)
1076 render_frame_host_->render_view_host()->AttachToFrameTree();
1078 // The process will no longer try to exit, so we can decrement the count.
1079 render_frame_host_->GetProcess()->RemovePendingView();
1081 // If the view is gone, then this RenderViewHost died while it was hidden.
1082 // We ignored the RenderProcessGone call at the time, so we should send it now
1083 // to make sure the sad tab shows up, etc.
1084 if (!render_frame_host_->render_view_host()->GetView()) {
1085 delegate_->RenderProcessGoneFromRenderManager(
1086 render_frame_host_->render_view_host());
1087 } else if (!delegate_->IsHidden()) {
1088 render_frame_host_->render_view_host()->GetView()->Show();
1091 // If the old view is live and top-level, hide it now that the new one is
1092 // visible.
1093 int32 old_site_instance_id =
1094 old_render_frame_host->render_view_host()->GetSiteInstance()->GetId();
1095 if (old_render_frame_host->render_view_host()->GetView()) {
1096 if (is_main_frame) {
1097 old_render_frame_host->render_view_host()->GetView()->Hide();
1098 old_render_frame_host->render_view_host()->WasSwappedOut(base::Bind(
1099 &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
1100 weak_factory_.GetWeakPtr(),
1101 old_site_instance_id,
1102 old_render_frame_host));
1103 } else {
1104 // TODO(creis): We'll need to set this back to false if we navigate back.
1105 old_render_frame_host->set_swapped_out(true);
1109 // Make sure the size is up to date. (Fix for bug 1079768.)
1110 delegate_->UpdateRenderViewSizeForRenderManager();
1112 if (will_focus_location_bar) {
1113 delegate_->SetFocusToLocationBar(false);
1114 } else if (focus_render_view &&
1115 render_frame_host_->render_view_host()->GetView()) {
1116 RenderWidgetHostViewPort::FromRWHV(
1117 render_frame_host_->render_view_host()->GetView())->Focus();
1120 // Notify that we've swapped RenderFrameHosts. We do this before shutting down
1121 // the RFH so that we can clean up RendererResources related to the RFH first.
1122 // TODO(creis): Only do this on top-level RFHs for now, and later update it to
1123 // pass the RFHs.
1124 if (is_main_frame) {
1125 delegate_->NotifySwappedFromRenderManager(
1126 old_render_frame_host->render_view_host(),
1127 render_frame_host_->render_view_host());
1130 // If the pending frame was on the swapped out list, we can remove it.
1131 swapped_out_hosts_.erase(render_frame_host_->render_view_host()->
1132 GetSiteInstance()->GetId());
1134 if (old_render_frame_host->render_view_host()->IsRenderViewLive()) {
1135 // If the old RFH is live, we are swapping it out and should keep track of
1136 // it in case we navigate back to it, or it is waiting for the unload event
1137 // to execute in the background.
1138 // TODO(creis): Swap out the subframe in --site-per-process.
1139 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
1140 DCHECK(old_render_frame_host->is_swapped_out() ||
1141 !RenderViewHostImpl::IsRVHStateActive(
1142 old_render_frame_host->render_view_host()->rvh_state()));
1143 // Temp fix for http://crbug.com/90867 until we do a better cleanup to make
1144 // sure we don't get different rvh instances for the same site instance
1145 // in the same rvhmgr.
1146 // TODO(creis): Clean this up.
1147 RenderFrameHostMap::iterator iter =
1148 swapped_out_hosts_.find(old_site_instance_id);
1149 if (iter != swapped_out_hosts_.end() &&
1150 iter->second != old_render_frame_host) {
1151 // Delete the RFH that will be replaced in the map to avoid a leak.
1152 delete iter->second;
1154 // If the RenderViewHost backing the RenderFrameHost is pending shutdown,
1155 // the RenderFrameHost should be put in the map of RenderFrameHosts pending
1156 // shutdown. Otherwise, it is stored in the map of swapped out
1157 // RenderFrameHosts.
1158 if (old_render_frame_host->render_view_host()->rvh_state() ==
1159 RenderViewHostImpl::STATE_PENDING_SHUTDOWN) {
1160 swapped_out_hosts_.erase(old_site_instance_id);
1161 RFHPendingDeleteMap::iterator pending_delete_iter =
1162 pending_delete_hosts_.find(old_site_instance_id);
1163 if (pending_delete_iter == pending_delete_hosts_.end() ||
1164 pending_delete_iter->second.get() != old_render_frame_host) {
1165 pending_delete_hosts_[old_site_instance_id] =
1166 linked_ptr<RenderFrameHostImpl>(old_render_frame_host);
1168 } else {
1169 swapped_out_hosts_[old_site_instance_id] = old_render_frame_host;
1172 // If there are no active views in this SiteInstance, it means that
1173 // this RFH was the last active one in the SiteInstance. Now that we
1174 // know that all RFHs are swapped out, we can delete all the RFHs and RVHs
1175 // in this SiteInstance. We do this after ensuring the RFH is on the
1176 // swapped out list to simplify the deletion.
1177 if (!static_cast<SiteInstanceImpl*>(
1178 old_render_frame_host->render_view_host()->GetSiteInstance())->
1179 active_view_count()) {
1180 ShutdownRenderFrameHostsInSiteInstance(old_site_instance_id);
1181 // This is deleted while cleaning up the SiteInstance's views.
1182 old_render_frame_host = NULL;
1184 } else {
1185 delete old_render_frame_host;
1189 void RenderFrameHostManager::ShutdownRenderFrameHostsInSiteInstance(
1190 int32 site_instance_id) {
1191 // First remove any swapped out RFH for this SiteInstance from our own list.
1192 ClearSwappedOutRFHsInSiteInstance(site_instance_id, frame_tree_node_);
1194 // Use the safe RenderWidgetHost iterator for now to find all RenderViewHosts
1195 // in the SiteInstance, then tell their respective FrameTrees to remove all
1196 // swapped out RenderFrameHosts corresponding to them.
1197 // TODO(creis): Replace this with a RenderFrameHostIterator that protects
1198 // against use-after-frees if a later element is deleted before getting to it.
1199 scoped_ptr<RenderWidgetHostIterator> widgets(
1200 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
1201 while (RenderWidgetHost* widget = widgets->GetNextHost()) {
1202 if (!widget->IsRenderView())
1203 continue;
1204 RenderViewHostImpl* rvh =
1205 static_cast<RenderViewHostImpl*>(RenderViewHost::From(widget));
1206 if (site_instance_id == rvh->GetSiteInstance()->GetId()) {
1207 // This deletes all RenderFrameHosts using the |rvh|, which then causes
1208 // |rvh| to Shutdown.
1209 FrameTree* tree = rvh->GetDelegate()->GetFrameTree();
1210 tree->ForEach(base::Bind(
1211 &RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance,
1212 site_instance_id));
1217 RenderFrameHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
1218 const NavigationEntryImpl& entry) {
1219 // If we are currently navigating cross-process, we want to get back to normal
1220 // and then navigate as usual.
1221 if (cross_navigation_pending_) {
1222 if (pending_render_frame_host_)
1223 CancelPending();
1224 cross_navigation_pending_ = false;
1227 // render_frame_host_'s SiteInstance and new_instance will not be deleted
1228 // before the end of this method, so we don't have to worry about their ref
1229 // counts dropping to zero.
1230 SiteInstance* current_instance =
1231 render_frame_host_->render_view_host()->GetSiteInstance();
1232 SiteInstance* new_instance = current_instance;
1234 // We do not currently swap processes for navigations in webview tag guests.
1235 bool is_guest_scheme = current_instance->GetSiteURL().SchemeIs(kGuestScheme);
1237 // Determine if we need a new BrowsingInstance for this entry. If true, this
1238 // implies that it will get a new SiteInstance (and likely process), and that
1239 // other tabs in the current BrowsingInstance will be unable to script it.
1240 // This is used for cases that require a process swap even in the
1241 // process-per-tab model, such as WebUI pages.
1242 const NavigationEntry* current_entry =
1243 delegate_->GetLastCommittedNavigationEntryForRenderManager();
1244 bool force_swap = !is_guest_scheme &&
1245 ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry);
1246 if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap))
1247 new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap);
1249 // If force_swap is true, we must use a different SiteInstance. If we didn't,
1250 // we would have two RenderFrameHosts in the same SiteInstance and the same
1251 // frame, resulting in page_id conflicts for their NavigationEntries.
1252 if (force_swap)
1253 CHECK_NE(new_instance, current_instance);
1255 if (new_instance != current_instance) {
1256 // New SiteInstance: create a pending RFH to navigate.
1257 DCHECK(!cross_navigation_pending_);
1259 // This will possibly create (set to NULL) a Web UI object for the pending
1260 // page. We'll use this later to give the page special access. This must
1261 // happen before the new renderer is created below so it will get bindings.
1262 // It must also happen after the above conditional call to CancelPending(),
1263 // otherwise CancelPending may clear the pending_web_ui_ and the page will
1264 // not have its bindings set appropriately.
1265 SetPendingWebUI(entry);
1267 // Ensure that we have created RFHs for the new RFH's opener chain if
1268 // we are staying in the same BrowsingInstance. This allows the pending RFH
1269 // to send cross-process script calls to its opener(s).
1270 int opener_route_id = MSG_ROUTING_NONE;
1271 if (new_instance->IsRelatedSiteInstance(current_instance)) {
1272 opener_route_id =
1273 delegate_->CreateOpenerRenderViewsForRenderManager(new_instance);
1276 // Create a non-swapped-out pending RFH with the given opener and navigate
1277 // it.
1278 int route_id = CreateRenderFrame(new_instance, opener_route_id, false,
1279 delegate_->IsHidden());
1280 if (route_id == MSG_ROUTING_NONE)
1281 return NULL;
1283 // Check if our current RFH is live before we set up a transition.
1284 if (!render_frame_host_->render_view_host()->IsRenderViewLive()) {
1285 if (!cross_navigation_pending_) {
1286 // The current RFH is not live. There's no reason to sit around with a
1287 // sad tab or a newly created RFH while we wait for the pending RFH to
1288 // navigate. Just switch to the pending RFH now and go back to non
1289 // cross-navigating (Note that we don't care about on{before}unload
1290 // handlers if the current RFH isn't live.)
1291 CommitPending();
1292 return render_frame_host_;
1293 } else {
1294 NOTREACHED();
1295 return render_frame_host_;
1298 // Otherwise, it's safe to treat this as a pending cross-site transition.
1300 // We need to wait until the beforeunload handler has run, unless we are
1301 // transferring an existing request (in which case it has already run).
1302 // Suspend the new render view (i.e., don't let it send the cross-site
1303 // Navigate message) until we hear back from the old renderer's
1304 // beforeunload handler. If the handler returns false, we'll have to
1305 // cancel the request.
1306 DCHECK(!pending_render_frame_host_->render_view_host()->
1307 are_navigations_suspended());
1308 bool is_transfer =
1309 entry.transferred_global_request_id() != GlobalRequestID();
1310 if (is_transfer) {
1311 // We don't need to stop the old renderer or run beforeunload/unload
1312 // handlers, because those have already been done.
1313 DCHECK(pending_nav_params_->global_request_id ==
1314 entry.transferred_global_request_id());
1315 } else {
1316 // Also make sure the old render view stops, in case a load is in
1317 // progress. (We don't want to do this for transfers, since it will
1318 // interrupt the transfer with an unexpected DidStopLoading.)
1319 render_frame_host_->render_view_host()->Send(new ViewMsg_Stop(
1320 render_frame_host_->render_view_host()->GetRoutingID()));
1322 pending_render_frame_host_->render_view_host()->SetNavigationsSuspended(
1323 true, base::TimeTicks());
1325 // Tell the CrossSiteRequestManager that this RVH has a pending cross-site
1326 // request, so that ResourceDispatcherHost will know to tell us to run the
1327 // old page's unload handler before it sends the response.
1328 // TODO(creis): This needs to be on the RFH.
1329 pending_render_frame_host_->render_view_host()->
1330 SetHasPendingCrossSiteRequest(true);
1333 // We now have a pending RFH.
1334 DCHECK(!cross_navigation_pending_);
1335 cross_navigation_pending_ = true;
1337 // Unless we are transferring an existing request, we should now
1338 // tell the old render view to run its beforeunload handler, since it
1339 // doesn't otherwise know that the cross-site request is happening. This
1340 // will trigger a call to ShouldClosePage with the reply.
1341 if (!is_transfer)
1342 render_frame_host_->render_view_host()->FirePageBeforeUnload(true);
1344 return pending_render_frame_host_;
1347 // Otherwise the same SiteInstance can be used. Navigate render_frame_host_.
1348 DCHECK(!cross_navigation_pending_);
1349 if (ShouldReuseWebUI(current_entry, &entry)) {
1350 pending_web_ui_.reset();
1351 pending_and_current_web_ui_ = web_ui_->AsWeakPtr();
1352 } else {
1353 SetPendingWebUI(entry);
1355 // Make sure the new RenderViewHost has the right bindings.
1356 if (pending_web_ui() && !render_frame_host_->GetProcess()->IsGuest()) {
1357 render_frame_host_->render_view_host()->AllowBindings(
1358 pending_web_ui()->GetBindings());
1362 if (pending_web_ui() &&
1363 render_frame_host_->render_view_host()->IsRenderViewLive()) {
1364 pending_web_ui()->GetController()->RenderViewReused(
1365 render_frame_host_->render_view_host());
1368 // The renderer can exit view source mode when any error or cancellation
1369 // happen. We must overwrite to recover the mode.
1370 if (entry.IsViewSourceMode()) {
1371 render_frame_host_->render_view_host()->Send(
1372 new ViewMsg_EnableViewSourceMode(
1373 render_frame_host_->render_view_host()->GetRoutingID()));
1376 return render_frame_host_;
1379 void RenderFrameHostManager::CancelPending() {
1380 RenderFrameHostImpl* pending_render_frame_host = pending_render_frame_host_;
1381 pending_render_frame_host_ = NULL;
1383 RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
1384 pending_render_frame_host->render_view_host(),
1385 render_frame_host_->render_view_host());
1387 // We no longer need to prevent the process from exiting.
1388 pending_render_frame_host->GetProcess()->RemovePendingView();
1390 // The pending RFH may already be on the swapped out list if we started to
1391 // swap it back in and then canceled. If so, make sure it gets swapped out
1392 // again. If it's not on the swapped out list (e.g., aborting a pending
1393 // load), then it's safe to shut down.
1394 if (IsOnSwappedOutList(pending_render_frame_host)) {
1395 // Any currently suspended navigations are no longer needed.
1396 pending_render_frame_host->render_view_host()->CancelSuspendedNavigations();
1398 // TODO(creis): We need to swap out the RFH.
1399 pending_render_frame_host->render_view_host()->SwapOut();
1400 } else {
1401 // We won't be coming back, so shut this one down.
1402 delete pending_render_frame_host;
1405 pending_web_ui_.reset();
1406 pending_and_current_web_ui_.reset();
1409 void RenderFrameHostManager::RenderViewDeleted(RenderViewHost* rvh) {
1410 // We are doing this in order to work around and to track a crasher
1411 // (http://crbug.com/23411) where it seems that pending_render_frame_host_ is
1412 // deleted (not sure from where) but not NULLed.
1413 if (pending_render_frame_host_ &&
1414 rvh == pending_render_frame_host_->render_view_host()) {
1415 // If you hit this NOTREACHED, please report it in the following bug
1416 // http://crbug.com/23411 Make sure to include what you were doing when it
1417 // happened (navigating to a new page, closing a tab...) and if you can
1418 // reproduce.
1419 NOTREACHED();
1420 pending_render_frame_host_ = NULL;
1423 // Make sure deleted RVHs are not kept in the swapped out list while we are
1424 // still alive. (If render_frame_host_ is null, we're already being deleted.)
1425 if (!render_frame_host_)
1426 return;
1428 // We can't look it up by SiteInstance ID, which may no longer be valid.
1429 for (RenderFrameHostMap::iterator iter = swapped_out_hosts_.begin();
1430 iter != swapped_out_hosts_.end();
1431 ++iter) {
1432 if (iter->second->render_view_host() == rvh) {
1433 swapped_out_hosts_.erase(iter);
1434 break;
1439 bool RenderFrameHostManager::IsRVHOnSwappedOutList(
1440 RenderViewHostImpl* rvh) const {
1441 RenderFrameHostImpl* render_frame_host = GetSwappedOutRenderFrameHost(
1442 rvh->GetSiteInstance());
1443 if (!render_frame_host)
1444 return false;
1445 return IsOnSwappedOutList(render_frame_host);
1448 bool RenderFrameHostManager::IsOnSwappedOutList(
1449 RenderFrameHostImpl* rfh) const {
1450 if (!rfh->render_view_host()->GetSiteInstance())
1451 return false;
1453 RenderFrameHostMap::const_iterator iter = swapped_out_hosts_.find(
1454 rfh->render_view_host()->GetSiteInstance()->GetId());
1455 if (iter == swapped_out_hosts_.end())
1456 return false;
1458 return iter->second == rfh;
1461 RenderViewHostImpl* RenderFrameHostManager::GetSwappedOutRenderViewHost(
1462 SiteInstance* instance) const {
1463 RenderFrameHostImpl* render_frame_host =
1464 GetSwappedOutRenderFrameHost(instance);
1465 if (render_frame_host)
1466 return render_frame_host->render_view_host();
1467 return NULL;
1470 RenderFrameHostImpl* RenderFrameHostManager::GetSwappedOutRenderFrameHost(
1471 SiteInstance* instance) const {
1472 RenderFrameHostMap::const_iterator iter =
1473 swapped_out_hosts_.find(instance->GetId());
1474 if (iter != swapped_out_hosts_.end())
1475 return iter->second;
1477 return NULL;
1480 } // namespace content