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