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"
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"
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
,
47 PageTransition page_transition
,
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
),
54 page_transition(page_transition
),
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();
66 RenderFrameHostManager::RenderFrameHostManager(
67 FrameTreeNode
* frame_tree_node
,
68 RenderFrameHostDelegate
* render_frame_delegate
,
69 RenderViewHostDelegate
* render_view_delegate
,
70 RenderWidgetHostDelegate
* render_widget_delegate
,
72 : frame_tree_node_(frame_tree_node
),
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_
)
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();
107 void RenderFrameHostManager::Init(BrowserContext
* browser_context
,
108 SiteInstance
* site_instance
,
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.
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
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_
)
133 return render_frame_host_
->render_view_host();
136 RenderViewHostImpl
* RenderFrameHostManager::pending_render_view_host() const {
137 if (!pending_render_frame_host_
)
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_
)
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()) {
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(),
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()
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
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
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_
)
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());
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();
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
307 render_frame_host
->frame_tree_node()->navigator()->RequestTransferURL(
310 pending_nav_params_
->transfer_url_chain
,
311 pending_nav_params_
->referrer
,
312 pending_nav_params_
->page_transition
,
314 pending_nav_params_
->frame_id
,
315 pending_nav_params_
->global_request_id
,
316 pending_nav_params_
->should_replace_current_entry
,
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();
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
360 render_frame_host
->frame_tree_node()->navigator()->RequestTransferURL(
363 pending_nav_params_
->transfer_url_chain
,
364 pending_nav_params_
->referrer
,
365 pending_nav_params_
->page_transition
,
367 pending_nav_params_
->frame_id
,
368 pending_nav_params_
->global_request_id
,
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())
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())
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
411 cross_navigation_pending_
= false;
413 // No one else should be sending us DidNavigate in this state.
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();
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();
439 if (iter
->second
->GetProcess() == render_process_host
)
440 ids_to_remove
.push_back(iter
->first
);
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
,
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_
)
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
);
473 // Current page says to cancel.
475 cross_navigation_pending_
= false;
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_
) {
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
,
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.
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
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();
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(
589 const NotificationSource
& source
,
590 const NotificationDetails
& details
) {
592 case NOTIFICATION_RENDERER_PROCESS_CLOSED
:
593 case NOTIFICATION_RENDERER_PROCESS_CLOSING
:
594 RendererProcessClosing(
595 Source
<RenderProcessHost
>(source
).ptr());
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(),
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
);
628 delete swapped_out_rfh
;
630 node
->render_manager()->swapped_out_hosts_
.erase(site_instance_id
);
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).
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 {
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
))
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
)) {
691 // Force a swap if it's a Web UI URL.
692 if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
693 browser_context
, new_url
)) {
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
)) {
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.
711 current_entry
->IsViewSourceMode() != new_entry
->IsViewSourceMode())
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
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.
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
885 return current_instance
->GetRelatedSiteInstance(dest_url
);
888 RenderFrameHostImpl
* RenderFrameHostManager::CreateRenderFrameHost(
889 SiteInstance
* site_instance
,
891 int frame_routing_id
,
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
);
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
,
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_
,
925 swapped_out
).release();
926 return render_frame_host
;
929 int RenderFrameHostManager::CreateRenderFrame(
930 SiteInstance
* 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.
954 new_render_frame_host
->GetProcess()->AddPendingView();
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
;
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
,
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.
975 swapped_out_hosts_
[instance
->GetId()] = new_render_frame_host
;
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_
) {
991 // Use this as our new pending RFH if it isn't 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())
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
1007 if (pending_web_ui() && !render_view_host
->GetProcess()->IsGuest()) {
1008 render_view_host
->AllowBindings(pending_web_ui()->GetBindings());
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()) {
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);
1060 // Remember if the page was focused so we can focus the new renderer in
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
1072 RenderFrameHostImpl
* old_render_frame_host
= render_frame_host_
;
1073 render_frame_host_
= pending_render_frame_host_
;
1074 pending_render_frame_host_
= NULL
;
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
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
));
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
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
);
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
;
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())
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
,
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_
)
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.
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
)) {
1273 delegate_
->CreateOpenerRenderViewsForRenderManager(new_instance
);
1276 // Create a non-swapped-out pending RFH with the given opener and navigate
1278 int route_id
= CreateRenderFrame(new_instance
, opener_route_id
, false,
1279 delegate_
->IsHidden());
1280 if (route_id
== MSG_ROUTING_NONE
)
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.)
1292 return render_frame_host_
;
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());
1309 entry
.transferred_global_request_id() != GlobalRequestID();
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());
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.
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();
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();
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
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_
)
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();
1432 if (iter
->second
->render_view_host() == rvh
) {
1433 swapped_out_hosts_
.erase(iter
);
1439 bool RenderFrameHostManager::IsRVHOnSwappedOutList(
1440 RenderViewHostImpl
* rvh
) const {
1441 RenderFrameHostImpl
* render_frame_host
= GetSwappedOutRenderFrameHost(
1442 rvh
->GetSiteInstance());
1443 if (!render_frame_host
)
1445 return IsOnSwappedOutList(render_frame_host
);
1448 bool RenderFrameHostManager::IsOnSwappedOutList(
1449 RenderFrameHostImpl
* rfh
) const {
1450 if (!rfh
->render_view_host()->GetSiteInstance())
1453 RenderFrameHostMap::const_iterator iter
= swapped_out_hosts_
.find(
1454 rfh
->render_view_host()->GetSiteInstance()->GetId());
1455 if (iter
== swapped_out_hosts_
.end())
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();
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
;
1480 } // namespace content