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