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