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