Home | History | Annotate | Download | only in prerender
      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 "chrome/browser/prerender/prerender_contents.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <utility>
     10 
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/favicon/favicon_tab_helper.h"
     14 #include "chrome/browser/history/history_tab_helper.h"
     15 #include "chrome/browser/history/history_types.h"
     16 #include "chrome/browser/prerender/prerender_field_trial.h"
     17 #include "chrome/browser/prerender/prerender_final_status.h"
     18 #include "chrome/browser/prerender/prerender_handle.h"
     19 #include "chrome/browser/prerender/prerender_manager.h"
     20 #include "chrome/browser/prerender/prerender_render_view_host_observer.h"
     21 #include "chrome/browser/prerender/prerender_tracker.h"
     22 #include "chrome/browser/profiles/profile.h"
     23 #include "chrome/browser/ui/browser.h"
     24 #include "chrome/browser/ui/browser_tab_contents.h"
     25 #include "chrome/common/prerender_messages.h"
     26 #include "chrome/common/url_constants.h"
     27 #include "content/public/browser/browser_child_process_host.h"
     28 #include "content/public/browser/notification_service.h"
     29 #include "content/public/browser/render_process_host.h"
     30 #include "content/public/browser/render_view_host.h"
     31 #include "content/public/browser/resource_request_details.h"
     32 #include "content/public/browser/session_storage_namespace.h"
     33 #include "content/public/browser/web_contents.h"
     34 #include "content/public/browser/web_contents_delegate.h"
     35 #include "content/public/browser/web_contents_view.h"
     36 #include "content/public/common/favicon_url.h"
     37 #include "ui/gfx/rect.h"
     38 
     39 using content::DownloadItem;
     40 using content::OpenURLParams;
     41 using content::RenderViewHost;
     42 using content::ResourceRedirectDetails;
     43 using content::SessionStorageNamespace;
     44 using content::WebContents;
     45 
     46 namespace prerender {
     47 
     48 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
     49  public:
     50   virtual PrerenderContents* CreatePrerenderContents(
     51       PrerenderManager* prerender_manager, Profile* profile,
     52       const GURL& url, const content::Referrer& referrer,
     53       Origin origin, uint8 experiment_id) OVERRIDE {
     54     return new PrerenderContents(prerender_manager, profile,
     55                                  url, referrer, origin, experiment_id);
     56   }
     57 };
     58 
     59 // WebContentsDelegateImpl -----------------------------------------------------
     60 
     61 class PrerenderContents::WebContentsDelegateImpl
     62     : public content::WebContentsDelegate {
     63  public:
     64   explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
     65       : prerender_contents_(prerender_contents) {
     66   }
     67 
     68   // content::WebContentsDelegate implementation:
     69   virtual WebContents* OpenURLFromTab(WebContents* source,
     70                                       const OpenURLParams& params) OVERRIDE {
     71     // |OpenURLFromTab| is typically called when a frame performs a navigation
     72     // that requires the browser to perform the transition instead of WebKit.
     73     // Examples include prerendering a site that redirects to an app URL,
     74     // or if --enable-strict-site-isolation is specified and the prerendered
     75     // frame redirects to a different origin.
     76     // TODO(cbentzel): Consider supporting this if it is a common case during
     77     // prerenders.
     78     prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
     79     return NULL;
     80   }
     81 
     82   virtual void CanDownload(
     83       RenderViewHost* render_view_host,
     84       int request_id,
     85       const std::string& request_method,
     86       const base::Callback<void(bool)>& callback) OVERRIDE {
     87     prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
     88     // Cancel the download.
     89     callback.Run(false);
     90   }
     91 
     92   virtual bool ShouldCreateWebContents(
     93       WebContents* web_contents,
     94       int route_id,
     95       WindowContainerType window_container_type,
     96       const string16& frame_name,
     97       const GURL& target_url,
     98       const content::Referrer& referrer,
     99       WindowOpenDisposition disposition,
    100       const WebKit::WebWindowFeatures& features,
    101       bool user_gesture,
    102       bool opener_suppressed) OVERRIDE {
    103     // Since we don't want to permit child windows that would have a
    104     // window.opener property, terminate prerendering.
    105     prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
    106     // Cancel the popup.
    107     return false;
    108   }
    109 
    110   virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
    111     // This isn't allowed because the history merge operation
    112     // does not work if there are renderer issued challenges.
    113     // TODO(cbentzel): Cancel in this case? May not need to do
    114     // since render-issued offset navigations are not guaranteed,
    115     // but indicates that the page cares about the history.
    116     return false;
    117   }
    118 
    119   virtual void JSOutOfMemory(WebContents* tab) OVERRIDE {
    120     prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY);
    121   }
    122 
    123   virtual bool ShouldSuppressDialogs() OVERRIDE {
    124     // We still want to show the user the message when they navigate to this
    125     // page, so cancel this prerender.
    126     prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
    127     // Always suppress JavaScript messages if they're triggered by a page being
    128     // prerendered.
    129     return true;
    130   }
    131 
    132   virtual void RegisterProtocolHandler(WebContents* web_contents,
    133                                        const std::string& protocol,
    134                                        const GURL& url,
    135                                        const string16& title,
    136                                        bool user_gesture) OVERRIDE {
    137     // TODO(mmenke): Consider supporting this if it is a common case during
    138     // prerenders.
    139     prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
    140   }
    141 
    142  private:
    143   PrerenderContents* prerender_contents_;
    144 };
    145 
    146 void PrerenderContents::Observer::OnPrerenderStopLoading(
    147     PrerenderContents* contents) {
    148 }
    149 
    150 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
    151     PrerenderContents* contents, PrerenderContents* replacement) {
    152 }
    153 
    154 PrerenderContents::Observer::Observer() {
    155 }
    156 
    157 PrerenderContents::Observer::~Observer() {
    158 }
    159 
    160 PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
    161     base::WeakPtr<PrerenderHandle> weak_prerender_handle,
    162     Origin origin,
    163     const GURL& url,
    164     const content::Referrer& referrer,
    165     const gfx::Size& size)
    166     : weak_prerender_handle(weak_prerender_handle),
    167       origin(origin),
    168       url(url),
    169       referrer(referrer),
    170       size(size) {
    171 }
    172 
    173 PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
    174 }
    175 
    176 void PrerenderContents::AddPendingPrerender(
    177     scoped_ptr<PendingPrerenderInfo> pending_prerender_info) {
    178   pending_prerenders_.push_back(pending_prerender_info.release());
    179 }
    180 
    181 void PrerenderContents::PrepareForUse() {
    182   NotifyPrerenderStop();
    183 
    184   SessionStorageNamespace* session_storage_namespace = NULL;
    185   if (prerender_contents_) {
    186     // TODO(ajwong): This does not correctly handle storage for isolated apps.
    187     session_storage_namespace = prerender_contents_->
    188         GetController().GetDefaultSessionStorageNamespace();
    189   }
    190   prerender_manager_->StartPendingPrerenders(
    191       child_id_, &pending_prerenders_, session_storage_namespace);
    192   pending_prerenders_.clear();
    193 }
    194 
    195 PrerenderContents::PrerenderContents(
    196     PrerenderManager* prerender_manager,
    197     Profile* profile,
    198     const GURL& url,
    199     const content::Referrer& referrer,
    200     Origin origin,
    201     uint8 experiment_id)
    202     : prerendering_has_started_(false),
    203       prerender_manager_(prerender_manager),
    204       prerender_url_(url),
    205       referrer_(referrer),
    206       profile_(profile),
    207       page_id_(0),
    208       session_storage_namespace_id_(-1),
    209       has_stopped_loading_(false),
    210       has_finished_loading_(false),
    211       final_status_(FINAL_STATUS_MAX),
    212       match_complete_status_(MATCH_COMPLETE_DEFAULT),
    213       prerendering_has_been_cancelled_(false),
    214       child_id_(-1),
    215       route_id_(-1),
    216       origin_(origin),
    217       experiment_id_(experiment_id),
    218       creator_child_id_(-1) {
    219   DCHECK(prerender_manager != NULL);
    220 }
    221 
    222 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
    223   PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
    224       prerender_url(), referrer(), origin(), experiment_id());
    225 
    226   new_contents->load_start_time_ = load_start_time_;
    227   new_contents->session_storage_namespace_id_ = session_storage_namespace_id_;
    228   new_contents->set_match_complete_status(
    229       PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
    230 
    231   const bool did_init = new_contents->Init();
    232   DCHECK(did_init);
    233   DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front());
    234   DCHECK_EQ(1u, new_contents->alias_urls_.size());
    235   new_contents->alias_urls_ = alias_urls_;
    236   new_contents->set_match_complete_status(
    237       PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
    238   NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
    239   return new_contents;
    240 }
    241 
    242 bool PrerenderContents::Init() {
    243   return AddAliasURL(prerender_url_);
    244 }
    245 
    246 // static
    247 PrerenderContents::Factory* PrerenderContents::CreateFactory() {
    248   return new PrerenderContentsFactoryImpl();
    249 }
    250 
    251 void PrerenderContents::StartPrerendering(
    252     int creator_child_id,
    253     const gfx::Size& size,
    254     SessionStorageNamespace* session_storage_namespace) {
    255   DCHECK(profile_ != NULL);
    256   DCHECK(!size.IsEmpty());
    257   DCHECK(!prerendering_has_started_);
    258   DCHECK(prerender_contents_.get() == NULL);
    259   DCHECK_EQ(-1, creator_child_id_);
    260   DCHECK(size_.IsEmpty());
    261   DCHECK_EQ(1U, alias_urls_.size());
    262 
    263   creator_child_id_ = creator_child_id;
    264   session_storage_namespace_id_ = session_storage_namespace->id();
    265   size_ = size;
    266 
    267   DCHECK(load_start_time_.is_null());
    268   load_start_time_ = base::TimeTicks::Now();
    269 
    270   // Everything after this point sets up the WebContents object and associated
    271   // RenderView for the prerender page. Don't do this for members of the
    272   // control group.
    273   if (prerender_manager_->IsControlGroup(experiment_id()))
    274     return;
    275 
    276   if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
    277       IsLocalPredictorPrerenderAlwaysControlEnabled()) {
    278     return;
    279   }
    280 
    281   prerendering_has_started_ = true;
    282 
    283   prerender_contents_.reset(CreateWebContents(session_storage_namespace));
    284   BrowserTabContents::AttachTabHelpers(prerender_contents_.get());
    285 #if defined(OS_ANDROID)
    286   // Delay icon fetching until the contents are getting swapped in
    287   // to conserve network usage in mobile devices.
    288   FaviconTabHelper::FromWebContents(
    289       prerender_contents_.get())->set_should_fetch_icons(false);
    290 #endif  // defined(OS_ANDROID)
    291   content::WebContentsObserver::Observe(prerender_contents_.get());
    292 
    293   web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
    294   prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
    295   // Set the size of the prerender WebContents.
    296   prerender_contents_->GetView()->SizeContents(size_);
    297 
    298   // Register as an observer of the RenderViewHost so we get messages.
    299   render_view_host_observer_.reset(
    300       new PrerenderRenderViewHostObserver(this, GetRenderViewHostMutable()));
    301 
    302   child_id_ = GetRenderViewHost()->GetProcess()->GetID();
    303   route_id_ = GetRenderViewHost()->GetRoutingID();
    304 
    305   // Register this with the ResourceDispatcherHost as a prerender
    306   // RenderViewHost. This must be done before the Navigate message to catch all
    307   // resource requests, but as it is on the same thread as the Navigate message
    308   // (IO) there is no race condition.
    309   AddObserver(prerender_manager()->prerender_tracker());
    310   NotifyPrerenderStart();
    311 
    312   // Close ourselves when the application is shutting down.
    313   notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
    314                               content::NotificationService::AllSources());
    315 
    316   // Register for our parent profile to shutdown, so we can shut ourselves down
    317   // as well (should only be called for OTR profiles, as we should receive
    318   // APP_TERMINATING before non-OTR profiles are destroyed).
    319   // TODO(tburkard): figure out if this is needed.
    320   notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
    321                               content::Source<Profile>(profile_));
    322 
    323   // Register to inform new RenderViews that we're prerendering.
    324   notification_registrar_.Add(
    325       this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
    326       content::Source<WebContents>(prerender_contents_.get()));
    327 
    328   // Register for redirect notifications sourced from |this|.
    329   notification_registrar_.Add(
    330       this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
    331       content::Source<WebContents>(prerender_contents_.get()));
    332 
    333   // Transfer over the user agent override.
    334   prerender_contents_.get()->SetUserAgentOverride(
    335       prerender_manager_->config().user_agent_override);
    336 
    337   content::NavigationController::LoadURLParams load_url_params(
    338       prerender_url_);
    339   load_url_params.referrer = referrer_;
    340   load_url_params.transition_type = (origin_ == ORIGIN_OMNIBOX ?
    341       content::PAGE_TRANSITION_TYPED : content::PAGE_TRANSITION_LINK);
    342   load_url_params.override_user_agent =
    343       prerender_manager_->config().is_overriding_user_agent ?
    344       content::NavigationController::UA_OVERRIDE_TRUE :
    345       content::NavigationController::UA_OVERRIDE_FALSE;
    346   prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
    347 }
    348 
    349 bool PrerenderContents::GetChildId(int* child_id) const {
    350   CHECK(child_id);
    351   DCHECK_GE(child_id_, -1);
    352   *child_id = child_id_;
    353   return child_id_ != -1;
    354 }
    355 
    356 bool PrerenderContents::GetRouteId(int* route_id) const {
    357   CHECK(route_id);
    358   DCHECK_GE(route_id_, -1);
    359   *route_id = route_id_;
    360   return route_id_ != -1;
    361 }
    362 
    363 void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
    364   DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
    365   DCHECK(final_status_ == FINAL_STATUS_MAX);
    366 
    367   final_status_ = final_status;
    368 }
    369 
    370 PrerenderContents::~PrerenderContents() {
    371   DCHECK_NE(FINAL_STATUS_MAX, final_status());
    372   DCHECK(
    373       prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
    374   DCHECK_NE(ORIGIN_MAX, origin());
    375 
    376   prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
    377       origin(), experiment_id(), match_complete_status(), final_status());
    378 
    379   // Broadcast the removal of aliases.
    380   for (content::RenderProcessHost::iterator host_iterator =
    381            content::RenderProcessHost::AllHostsIterator();
    382        !host_iterator.IsAtEnd();
    383        host_iterator.Advance()) {
    384     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
    385     host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
    386   }
    387 
    388   // If we still have a WebContents, clean up anything we need to and then
    389   // destroy it.
    390   if (prerender_contents_.get())
    391     delete ReleasePrerenderContents();
    392 }
    393 
    394 void PrerenderContents::AddObserver(Observer* observer) {
    395   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
    396   observer_list_.AddObserver(observer);
    397 }
    398 
    399 void PrerenderContents::RemoveObserver(Observer* observer) {
    400   observer_list_.RemoveObserver(observer);
    401 }
    402 
    403 void PrerenderContents::Observe(int type,
    404                                 const content::NotificationSource& source,
    405                                 const content::NotificationDetails& details) {
    406   switch (type) {
    407     case chrome::NOTIFICATION_PROFILE_DESTROYED:
    408       Destroy(FINAL_STATUS_PROFILE_DESTROYED);
    409       return;
    410 
    411     case chrome::NOTIFICATION_APP_TERMINATING:
    412       Destroy(FINAL_STATUS_APP_TERMINATING);
    413       return;
    414 
    415     case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
    416       // RESOURCE_RECEIVED_REDIRECT can come for any resource on a page.
    417       // If it's a redirect on the top-level resource, the name needs
    418       // to be remembered for future matching, and if it redirects to
    419       // an https resource, it needs to be canceled. If a subresource
    420       // is redirected, nothing changes.
    421       DCHECK_EQ(content::Source<WebContents>(source).ptr(),
    422                 prerender_contents_.get());
    423       ResourceRedirectDetails* resource_redirect_details =
    424           content::Details<ResourceRedirectDetails>(details).ptr();
    425       CHECK(resource_redirect_details);
    426       if (resource_redirect_details->resource_type ==
    427           ResourceType::MAIN_FRAME) {
    428         if (!AddAliasURL(resource_redirect_details->new_url))
    429           return;
    430       }
    431       break;
    432     }
    433 
    434     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
    435       if (prerender_contents_.get()) {
    436         DCHECK_EQ(content::Source<WebContents>(source).ptr(),
    437                   prerender_contents_.get());
    438 
    439         content::Details<RenderViewHost> new_render_view_host(details);
    440         OnRenderViewHostCreated(new_render_view_host.ptr());
    441 
    442         // When a new RenderView is created for a prerendering WebContents,
    443         // tell the new RenderView it's being used for prerendering before any
    444         // navigations occur.  Note that this is always triggered before the
    445         // first navigation, so there's no need to send the message just after
    446         // the WebContents is created.
    447         new_render_view_host->Send(
    448             new PrerenderMsg_SetIsPrerendering(
    449                 new_render_view_host->GetRoutingID(),
    450                 true));
    451 
    452         // Make sure the size of the RenderViewHost has been passed to the new
    453         // RenderView.  Otherwise, the size may not be sent until the
    454         // RenderViewReady event makes it from the render process to the UI
    455         // thread of the browser process.  When the RenderView receives its
    456         // size, is also sets itself to be visible, which would then break the
    457         // visibility API.
    458         new_render_view_host->WasResized();
    459         prerender_contents_->WasHidden();
    460       }
    461       break;
    462     }
    463 
    464     default:
    465       NOTREACHED() << "Unexpected notification sent.";
    466       break;
    467   }
    468 }
    469 
    470 void PrerenderContents::OnRenderViewHostCreated(
    471     RenderViewHost* new_render_view_host) {
    472 }
    473 
    474 size_t PrerenderContents::pending_prerender_count() const {
    475   return pending_prerenders_.size();
    476 }
    477 
    478 WebContents* PrerenderContents::CreateWebContents(
    479     SessionStorageNamespace* session_storage_namespace) {
    480   // TODO(ajwong): Remove the temporary map once prerendering is aware of
    481   // multiple session storage namespaces per tab.
    482   content::SessionStorageNamespaceMap session_storage_namespace_map;
    483   session_storage_namespace_map[std::string()] = session_storage_namespace;
    484   return WebContents::CreateWithSessionStorage(
    485       WebContents::CreateParams(profile_), session_storage_namespace_map);
    486 }
    487 
    488 void PrerenderContents::NotifyPrerenderStart() {
    489   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
    490   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
    491 }
    492 
    493 void PrerenderContents::NotifyPrerenderStopLoading() {
    494   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
    495 }
    496 
    497 void PrerenderContents::NotifyPrerenderStop() {
    498   DCHECK_NE(FINAL_STATUS_MAX, final_status_);
    499   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
    500   observer_list_.Clear();
    501 }
    502 
    503 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
    504     PrerenderContents* replacement) {
    505   FOR_EACH_OBSERVER(Observer, observer_list_,
    506                     OnPrerenderCreatedMatchCompleteReplacement(this,
    507                                                                replacement));
    508 }
    509 
    510 void PrerenderContents::DidUpdateFaviconURL(
    511     int32 page_id,
    512     const std::vector<content::FaviconURL>& urls) {
    513   VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_;
    514   for (std::vector<content::FaviconURL>::const_iterator it = urls.begin();
    515        it != urls.end(); ++it) {
    516     if (it->icon_type == content::FaviconURL::FAVICON) {
    517       icon_url_ = it->icon_url;
    518       VLOG(1) << icon_url_;
    519       return;
    520     }
    521   }
    522 }
    523 
    524 bool PrerenderContents::AddAliasURL(const GURL& url) {
    525   const bool http = url.SchemeIs(chrome::kHttpScheme);
    526   const bool https = url.SchemeIs(chrome::kHttpsScheme);
    527   if (!http && !https) {
    528     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
    529     Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
    530     return false;
    531   }
    532   if (https && !prerender_manager_->config().https_allowed) {
    533     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
    534     Destroy(FINAL_STATUS_HTTPS);
    535     return false;
    536   }
    537   if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
    538       prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
    539     Destroy(FINAL_STATUS_RECENTLY_VISITED);
    540     return false;
    541   }
    542 
    543   alias_urls_.push_back(url);
    544 
    545   for (content::RenderProcessHost::iterator host_iterator =
    546            content::RenderProcessHost::AllHostsIterator();
    547        !host_iterator.IsAtEnd();
    548        host_iterator.Advance()) {
    549     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
    550     host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
    551   }
    552 
    553   return true;
    554 }
    555 
    556 bool PrerenderContents::Matches(
    557     const GURL& url,
    558     const SessionStorageNamespace* session_storage_namespace) const {
    559   if (session_storage_namespace &&
    560       session_storage_namespace_id_ != session_storage_namespace->id()) {
    561     return false;
    562   }
    563   return std::count_if(alias_urls_.begin(), alias_urls_.end(),
    564                        std::bind2nd(std::equal_to<GURL>(), url)) != 0;
    565 }
    566 
    567 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
    568   Destroy(FINAL_STATUS_RENDERER_CRASHED);
    569 }
    570 
    571 void PrerenderContents::DidStopLoading(
    572     content::RenderViewHost* render_view_host) {
    573   has_stopped_loading_ = true;
    574   NotifyPrerenderStopLoading();
    575 }
    576 
    577 void PrerenderContents::DidStartProvisionalLoadForFrame(
    578     int64 frame_id,
    579     int64 parent_frame_id,
    580     bool is_main_frame,
    581     const GURL& validated_url,
    582     bool is_error_page,
    583     bool is_iframe_srcdoc,
    584     RenderViewHost* render_view_host) {
    585   if (is_main_frame) {
    586     if (!AddAliasURL(validated_url))
    587       return;
    588 
    589     // Usually, this event fires if the user clicks or enters a new URL.
    590     // Neither of these can happen in the case of an invisible prerender.
    591     // So the cause is: Some JavaScript caused a new URL to be loaded.  In that
    592     // case, the spinner would start again in the browser, so we must reset
    593     // has_stopped_loading_ so that the spinner won't be stopped.
    594     has_stopped_loading_ = false;
    595     has_finished_loading_ = false;
    596   }
    597 }
    598 
    599 void PrerenderContents::DidFinishLoad(int64 frame_id,
    600                                       const GURL& validated_url,
    601                                       bool is_main_frame,
    602                                       RenderViewHost* render_view_host) {
    603   if (is_main_frame)
    604     has_finished_loading_ = true;
    605 }
    606 
    607 void PrerenderContents::Destroy(FinalStatus final_status) {
    608   DCHECK_NE(final_status, FINAL_STATUS_USED);
    609 
    610   if (prerendering_has_been_cancelled_)
    611     return;
    612 
    613   if (child_id_ != -1 && route_id_ != -1) {
    614     // Cancel the prerender in the PrerenderTracker.  This is needed
    615     // because destroy may be called directly from the UI thread without calling
    616     // TryCancel().  This is difficult to completely avoid, since prerendering
    617     // can be cancelled before a RenderView is created.
    618     bool is_cancelled = prerender_manager()->prerender_tracker()->TryCancel(
    619         child_id_, route_id_, final_status);
    620     CHECK(is_cancelled);
    621 
    622     // A different final status may have been set already from another thread.
    623     // If so, use it instead.
    624     if (!prerender_manager()->prerender_tracker()->
    625             GetFinalStatus(child_id_, route_id_, &final_status)) {
    626       NOTREACHED();
    627     }
    628   }
    629   SetFinalStatus(final_status);
    630 
    631   prerendering_has_been_cancelled_ = true;
    632   prerender_manager_->AddToHistory(this);
    633   prerender_manager_->MoveEntryToPendingDelete(this, final_status);
    634 
    635   if (!prerender_manager_->IsControlGroup(experiment_id()) &&
    636       (prerendering_has_started() ||
    637        match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
    638     NotifyPrerenderStop();
    639   }
    640 
    641   // We may destroy the PrerenderContents before we have initialized the
    642   // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to
    643   // avoid any more messages being sent.
    644   if (render_view_host_observer_)
    645     render_view_host_observer_->set_prerender_contents(NULL);
    646 }
    647 
    648 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
    649   if (process_metrics_.get() == NULL) {
    650     // If a PrenderContents hasn't started prerending, don't be fully formed.
    651     if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
    652       return NULL;
    653     base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
    654     if (handle == base::kNullProcessHandle)
    655       return NULL;
    656 #if !defined(OS_MACOSX)
    657     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
    658 #else
    659     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
    660         handle,
    661         content::BrowserChildProcessHost::GetPortProvider()));
    662 #endif
    663   }
    664 
    665   return process_metrics_.get();
    666 }
    667 
    668 void PrerenderContents::DestroyWhenUsingTooManyResources() {
    669   base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
    670   if (metrics == NULL)
    671     return;
    672 
    673   size_t private_bytes, shared_bytes;
    674   if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
    675       private_bytes > prerender_manager_->config().max_bytes) {
    676     Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
    677   }
    678 }
    679 
    680 WebContents* PrerenderContents::ReleasePrerenderContents() {
    681   prerender_contents_->SetDelegate(NULL);
    682   render_view_host_observer_.reset();
    683   content::WebContentsObserver::Observe(NULL);
    684   return prerender_contents_.release();
    685 }
    686 
    687 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
    688   return const_cast<RenderViewHost*>(GetRenderViewHost());
    689 }
    690 
    691 const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
    692   if (!prerender_contents_.get())
    693     return NULL;
    694   return prerender_contents_->GetRenderViewHost();
    695 }
    696 
    697 void PrerenderContents::DidNavigate(
    698     const history::HistoryAddPageArgs& add_page_args) {
    699   add_page_vector_.push_back(add_page_args);
    700 }
    701 
    702 void PrerenderContents::CommitHistory(WebContents* tab) {
    703   HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
    704   for (size_t i = 0; i < add_page_vector_.size(); ++i)
    705     history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
    706 }
    707 
    708 Value* PrerenderContents::GetAsValue() const {
    709   if (!prerender_contents_.get())
    710     return NULL;
    711   DictionaryValue* dict_value = new DictionaryValue();
    712   dict_value->SetString("url", prerender_url_.spec());
    713   base::TimeTicks current_time = base::TimeTicks::Now();
    714   base::TimeDelta duration = current_time - load_start_time_;
    715   dict_value->SetInteger("duration", duration.InSeconds());
    716   dict_value->SetBoolean("is_loaded", prerender_contents_ &&
    717                                       !prerender_contents_->IsLoading());
    718   return dict_value;
    719 }
    720 
    721 bool PrerenderContents::IsCrossSiteNavigationPending() const {
    722   if (!prerender_contents_)
    723     return false;
    724   return (prerender_contents_->GetSiteInstance() !=
    725           prerender_contents_->GetPendingSiteInstance());
    726 }
    727 
    728 
    729 }  // namespace prerender
    730