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