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_tracker.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/ui/browser.h"
     23 #include "chrome/browser/ui/browser_tab_contents.h"
     24 #include "chrome/common/prerender_messages.h"
     25 #include "chrome/common/render_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_frame_host.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "content/public/browser/render_view_host.h"
     32 #include "content/public/browser/resource_request_details.h"
     33 #include "content/public/browser/session_storage_namespace.h"
     34 #include "content/public/browser/web_contents.h"
     35 #include "content/public/browser/web_contents_delegate.h"
     36 #include "content/public/browser/web_contents_view.h"
     37 #include "content/public/common/favicon_url.h"
     38 #include "content/public/common/frame_navigate_params.h"
     39 #include "ui/gfx/rect.h"
     40 
     41 using content::DownloadItem;
     42 using content::OpenURLParams;
     43 using content::RenderViewHost;
     44 using content::ResourceRedirectDetails;
     45 using content::SessionStorageNamespace;
     46 using content::WebContents;
     47 
     48 namespace prerender {
     49 
     50 namespace {
     51 
     52 // Internal cookie event.
     53 // Whenever a prerender interacts with the cookie store, either sending
     54 // existing cookies that existed before the prerender started, or when a cookie
     55 // is changed, we record these events for histogramming purposes.
     56 enum InternalCookieEvent {
     57   INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0,
     58   INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1,
     59   INTERNAL_COOKIE_EVENT_OTHER_SEND = 2,
     60   INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3,
     61   INTERNAL_COOKIE_EVENT_MAX
     62 };
     63 
     64 }  // namespace
     65 
     66 // static
     67 const int PrerenderContents::kNumCookieStatuses =
     68     (1 << INTERNAL_COOKIE_EVENT_MAX);
     69 
     70 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
     71  public:
     72   virtual PrerenderContents* CreatePrerenderContents(
     73       PrerenderManager* prerender_manager, Profile* profile,
     74       const GURL& url, const content::Referrer& referrer,
     75       Origin origin, uint8 experiment_id) OVERRIDE {
     76     return new PrerenderContents(prerender_manager, profile,
     77                                  url, referrer, origin, experiment_id);
     78   }
     79 };
     80 
     81 // WebContentsDelegateImpl -----------------------------------------------------
     82 
     83 class PrerenderContents::WebContentsDelegateImpl
     84     : public content::WebContentsDelegate {
     85  public:
     86   explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
     87       : prerender_contents_(prerender_contents) {
     88   }
     89 
     90   // content::WebContentsDelegate implementation:
     91   virtual WebContents* OpenURLFromTab(WebContents* source,
     92                                       const OpenURLParams& params) OVERRIDE {
     93     // |OpenURLFromTab| is typically called when a frame performs a navigation
     94     // that requires the browser to perform the transition instead of WebKit.
     95     // Examples include prerendering a site that redirects to an app URL,
     96     // or if --enable-strict-site-isolation is specified and the prerendered
     97     // frame redirects to a different origin.
     98     // TODO(cbentzel): Consider supporting this if it is a common case during
     99     // prerenders.
    100     prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
    101     return NULL;
    102   }
    103 
    104   virtual void CanDownload(
    105       RenderViewHost* render_view_host,
    106       int request_id,
    107       const std::string& request_method,
    108       const base::Callback<void(bool)>& callback) OVERRIDE {
    109     prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
    110     // Cancel the download.
    111     callback.Run(false);
    112   }
    113 
    114   virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
    115     // This isn't allowed because the history merge operation
    116     // does not work if there are renderer issued challenges.
    117     // TODO(cbentzel): Cancel in this case? May not need to do
    118     // since render-issued offset navigations are not guaranteed,
    119     // but indicates that the page cares about the history.
    120     return false;
    121   }
    122 
    123   virtual void JSOutOfMemory(WebContents* tab) OVERRIDE {
    124     prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY);
    125   }
    126 
    127   virtual bool ShouldSuppressDialogs() OVERRIDE {
    128     // We still want to show the user the message when they navigate to this
    129     // page, so cancel this prerender.
    130     prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
    131     // Always suppress JavaScript messages if they're triggered by a page being
    132     // prerendered.
    133     return true;
    134   }
    135 
    136   virtual void RegisterProtocolHandler(WebContents* web_contents,
    137                                        const std::string& protocol,
    138                                        const GURL& url,
    139                                        const base::string16& title,
    140                                        bool user_gesture) OVERRIDE {
    141     // TODO(mmenke): Consider supporting this if it is a common case during
    142     // prerenders.
    143     prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
    144   }
    145 
    146   virtual gfx::Size GetSizeForNewRenderView(
    147       const WebContents* web_contents) const OVERRIDE {
    148     // Have to set the size of the RenderView on initialization to be sure it is
    149     // set before the RenderView is hidden on all platforms (esp. Android).
    150     return prerender_contents_->size_;
    151   }
    152 
    153  private:
    154   PrerenderContents* prerender_contents_;
    155 };
    156 
    157 void PrerenderContents::Observer::OnPrerenderStopLoading(
    158     PrerenderContents* contents) {
    159 }
    160 
    161 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
    162     PrerenderContents* contents, PrerenderContents* replacement) {
    163 }
    164 
    165 PrerenderContents::Observer::Observer() {
    166 }
    167 
    168 PrerenderContents::Observer::~Observer() {
    169 }
    170 
    171 PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
    172     base::WeakPtr<PrerenderHandle> weak_prerender_handle,
    173     Origin origin,
    174     const GURL& url,
    175     const content::Referrer& referrer,
    176     const gfx::Size& size)
    177     : weak_prerender_handle(weak_prerender_handle),
    178       origin(origin),
    179       url(url),
    180       referrer(referrer),
    181       size(size) {
    182 }
    183 
    184 PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
    185 }
    186 
    187 void PrerenderContents::AddPendingPrerender(
    188     scoped_ptr<PendingPrerenderInfo> pending_prerender_info) {
    189   pending_prerenders_.push_back(pending_prerender_info.release());
    190 }
    191 
    192 void PrerenderContents::PrepareForUse() {
    193   for (std::set<content::RenderFrameHost*>::iterator i =
    194            render_frame_hosts_.begin(); i != render_frame_hosts_.end(); ++i) {
    195     (*i)->Send(new PrerenderMsg_SetIsPrerendering((*i)->GetRoutingID(), false));
    196   }
    197   render_frame_hosts_.clear();
    198 
    199   NotifyPrerenderStop();
    200 
    201   SessionStorageNamespace* session_storage_namespace = NULL;
    202   if (prerender_contents_) {
    203     // TODO(ajwong): This does not correctly handle storage for isolated apps.
    204     session_storage_namespace = prerender_contents_->
    205         GetController().GetDefaultSessionStorageNamespace();
    206   }
    207   prerender_manager_->StartPendingPrerenders(
    208       child_id_, &pending_prerenders_, session_storage_namespace);
    209   pending_prerenders_.clear();
    210 }
    211 
    212 PrerenderContents::PrerenderContents(
    213     PrerenderManager* prerender_manager,
    214     Profile* profile,
    215     const GURL& url,
    216     const content::Referrer& referrer,
    217     Origin origin,
    218     uint8 experiment_id)
    219     : prerendering_has_started_(false),
    220       session_storage_namespace_id_(-1),
    221       prerender_manager_(prerender_manager),
    222       prerender_url_(url),
    223       referrer_(referrer),
    224       profile_(profile),
    225       page_id_(0),
    226       has_stopped_loading_(false),
    227       has_finished_loading_(false),
    228       final_status_(FINAL_STATUS_MAX),
    229       match_complete_status_(MATCH_COMPLETE_DEFAULT),
    230       prerendering_has_been_cancelled_(false),
    231       child_id_(-1),
    232       route_id_(-1),
    233       origin_(origin),
    234       experiment_id_(experiment_id),
    235       creator_child_id_(-1),
    236       cookie_status_(0) {
    237   DCHECK(prerender_manager != NULL);
    238 }
    239 
    240 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
    241   PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
    242       prerender_url(), referrer(), origin(), experiment_id());
    243 
    244   new_contents->load_start_time_ = load_start_time_;
    245   new_contents->session_storage_namespace_id_ = session_storage_namespace_id_;
    246   new_contents->set_match_complete_status(
    247       PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
    248 
    249   const bool did_init = new_contents->Init();
    250   DCHECK(did_init);
    251   DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front());
    252   DCHECK_EQ(1u, new_contents->alias_urls_.size());
    253   new_contents->alias_urls_ = alias_urls_;
    254   new_contents->set_match_complete_status(
    255       PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
    256   NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
    257   return new_contents;
    258 }
    259 
    260 bool PrerenderContents::Init() {
    261   return AddAliasURL(prerender_url_);
    262 }
    263 
    264 // static
    265 PrerenderContents::Factory* PrerenderContents::CreateFactory() {
    266   return new PrerenderContentsFactoryImpl();
    267 }
    268 
    269 void PrerenderContents::StartPrerendering(
    270     int creator_child_id,
    271     const gfx::Size& size,
    272     SessionStorageNamespace* session_storage_namespace) {
    273   DCHECK(profile_ != NULL);
    274   DCHECK(!size.IsEmpty());
    275   DCHECK(!prerendering_has_started_);
    276   DCHECK(prerender_contents_.get() == NULL);
    277   DCHECK_EQ(-1, creator_child_id_);
    278   DCHECK(size_.IsEmpty());
    279   DCHECK_EQ(1U, alias_urls_.size());
    280 
    281   creator_child_id_ = creator_child_id;
    282   session_storage_namespace_id_ = session_storage_namespace->id();
    283   size_ = size;
    284 
    285   DCHECK(load_start_time_.is_null());
    286   load_start_time_ = base::TimeTicks::Now();
    287   start_time_ = base::Time::Now();
    288 
    289   // Everything after this point sets up the WebContents object and associated
    290   // RenderView for the prerender page. Don't do this for members of the
    291   // control group.
    292   if (prerender_manager_->IsControlGroup(experiment_id()))
    293     return;
    294 
    295   if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
    296       IsLocalPredictorPrerenderAlwaysControlEnabled()) {
    297     return;
    298   }
    299 
    300   prerendering_has_started_ = true;
    301 
    302   alias_session_storage_namespace = session_storage_namespace->CreateAlias();
    303   prerender_contents_.reset(
    304       CreateWebContents(alias_session_storage_namespace.get()));
    305   BrowserTabContents::AttachTabHelpers(prerender_contents_.get());
    306 #if defined(OS_ANDROID)
    307   // Delay icon fetching until the contents are getting swapped in
    308   // to conserve network usage in mobile devices.
    309   FaviconTabHelper::FromWebContents(
    310       prerender_contents_.get())->set_should_fetch_icons(false);
    311 #endif  // defined(OS_ANDROID)
    312   content::WebContentsObserver::Observe(prerender_contents_.get());
    313 
    314   web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
    315   prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
    316   // Set the size of the prerender WebContents.
    317   prerender_contents_->GetView()->SizeContents(size_);
    318 
    319   child_id_ = GetRenderViewHost()->GetProcess()->GetID();
    320   route_id_ = GetRenderViewHost()->GetRoutingID();
    321 
    322   // Log transactions to see if we could merge session storage namespaces in
    323   // the event of a mismatch.
    324   alias_session_storage_namespace->AddTransactionLogProcessId(child_id_);
    325 
    326   // Register this with the ResourceDispatcherHost as a prerender
    327   // RenderViewHost. This must be done before the Navigate message to catch all
    328   // resource requests, but as it is on the same thread as the Navigate message
    329   // (IO) there is no race condition.
    330   AddObserver(prerender_manager()->prerender_tracker());
    331   NotifyPrerenderStart();
    332 
    333   // Close ourselves when the application is shutting down.
    334   notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
    335                               content::NotificationService::AllSources());
    336 
    337   // Register for our parent profile to shutdown, so we can shut ourselves down
    338   // as well (should only be called for OTR profiles, as we should receive
    339   // APP_TERMINATING before non-OTR profiles are destroyed).
    340   // TODO(tburkard): figure out if this is needed.
    341   notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
    342                               content::Source<Profile>(profile_));
    343 
    344   // Register to inform new RenderViews that we're prerendering.
    345   notification_registrar_.Add(
    346       this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
    347       content::Source<WebContents>(prerender_contents_.get()));
    348 
    349   // Transfer over the user agent override.
    350   prerender_contents_.get()->SetUserAgentOverride(
    351       prerender_manager_->config().user_agent_override);
    352 
    353   content::NavigationController::LoadURLParams load_url_params(
    354       prerender_url_);
    355   load_url_params.referrer = referrer_;
    356   load_url_params.transition_type =
    357       ((origin_ == ORIGIN_OMNIBOX || origin_ == ORIGIN_INSTANT) ?
    358           content::PAGE_TRANSITION_TYPED : content::PAGE_TRANSITION_LINK);
    359   load_url_params.override_user_agent =
    360       prerender_manager_->config().is_overriding_user_agent ?
    361       content::NavigationController::UA_OVERRIDE_TRUE :
    362       content::NavigationController::UA_OVERRIDE_FALSE;
    363   prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
    364 }
    365 
    366 bool PrerenderContents::GetChildId(int* child_id) const {
    367   CHECK(child_id);
    368   DCHECK_GE(child_id_, -1);
    369   *child_id = child_id_;
    370   return child_id_ != -1;
    371 }
    372 
    373 bool PrerenderContents::GetRouteId(int* route_id) const {
    374   CHECK(route_id);
    375   DCHECK_GE(route_id_, -1);
    376   *route_id = route_id_;
    377   return route_id_ != -1;
    378 }
    379 
    380 void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
    381   DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
    382   DCHECK(final_status_ == FINAL_STATUS_MAX);
    383 
    384   final_status_ = final_status;
    385 }
    386 
    387 PrerenderContents::~PrerenderContents() {
    388   DCHECK_NE(FINAL_STATUS_MAX, final_status());
    389   DCHECK(
    390       prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
    391   DCHECK_NE(ORIGIN_MAX, origin());
    392   // Since a lot of prerenders terminate before any meaningful cookie action
    393   // would have happened, only record the cookie status for prerenders who
    394   // were used, cancelled, or timed out.
    395   if (prerendering_has_started_ &&
    396       (final_status() == FINAL_STATUS_USED ||
    397        final_status() == FINAL_STATUS_TIMED_OUT ||
    398        final_status() == FINAL_STATUS_CANCELLED)) {
    399     prerender_manager_->RecordCookieStatus(origin(), experiment_id(),
    400                                            cookie_status_);
    401   }
    402   prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
    403       origin(), experiment_id(), match_complete_status(), final_status());
    404 
    405   // Broadcast the removal of aliases.
    406   for (content::RenderProcessHost::iterator host_iterator =
    407            content::RenderProcessHost::AllHostsIterator();
    408        !host_iterator.IsAtEnd();
    409        host_iterator.Advance()) {
    410     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
    411     host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
    412   }
    413 
    414   // If we still have a WebContents, clean up anything we need to and then
    415   // destroy it.
    416   if (prerender_contents_.get())
    417     delete ReleasePrerenderContents();
    418 }
    419 
    420 void PrerenderContents::AddObserver(Observer* observer) {
    421   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
    422   observer_list_.AddObserver(observer);
    423 }
    424 
    425 void PrerenderContents::RemoveObserver(Observer* observer) {
    426   observer_list_.RemoveObserver(observer);
    427 }
    428 
    429 void PrerenderContents::Observe(int type,
    430                                 const content::NotificationSource& source,
    431                                 const content::NotificationDetails& details) {
    432   switch (type) {
    433     case chrome::NOTIFICATION_PROFILE_DESTROYED:
    434       Destroy(FINAL_STATUS_PROFILE_DESTROYED);
    435       return;
    436 
    437     case chrome::NOTIFICATION_APP_TERMINATING:
    438       Destroy(FINAL_STATUS_APP_TERMINATING);
    439       return;
    440 
    441     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
    442       if (prerender_contents_.get()) {
    443         DCHECK_EQ(content::Source<WebContents>(source).ptr(),
    444                   prerender_contents_.get());
    445 
    446         content::Details<RenderViewHost> new_render_view_host(details);
    447         OnRenderViewHostCreated(new_render_view_host.ptr());
    448 
    449         // Make sure the size of the RenderViewHost has been passed to the new
    450         // RenderView.  Otherwise, the size may not be sent until the
    451         // RenderViewReady event makes it from the render process to the UI
    452         // thread of the browser process.  When the RenderView receives its
    453         // size, is also sets itself to be visible, which would then break the
    454         // visibility API.
    455         new_render_view_host->WasResized();
    456         prerender_contents_->WasHidden();
    457       }
    458       break;
    459     }
    460 
    461     default:
    462       NOTREACHED() << "Unexpected notification sent.";
    463       break;
    464   }
    465 }
    466 
    467 void PrerenderContents::OnRenderViewHostCreated(
    468     RenderViewHost* new_render_view_host) {
    469 }
    470 
    471 size_t PrerenderContents::pending_prerender_count() const {
    472   return pending_prerenders_.size();
    473 }
    474 
    475 WebContents* PrerenderContents::CreateWebContents(
    476     SessionStorageNamespace* session_storage_namespace) {
    477   // TODO(ajwong): Remove the temporary map once prerendering is aware of
    478   // multiple session storage namespaces per tab.
    479   content::SessionStorageNamespaceMap session_storage_namespace_map;
    480   session_storage_namespace_map[std::string()] = session_storage_namespace;
    481   return WebContents::CreateWithSessionStorage(
    482       WebContents::CreateParams(profile_), session_storage_namespace_map);
    483 }
    484 
    485 void PrerenderContents::NotifyPrerenderStart() {
    486   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
    487   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
    488 }
    489 
    490 void PrerenderContents::NotifyPrerenderStopLoading() {
    491   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
    492 }
    493 
    494 void PrerenderContents::NotifyPrerenderStop() {
    495   DCHECK_NE(FINAL_STATUS_MAX, final_status_);
    496   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
    497   observer_list_.Clear();
    498 }
    499 
    500 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
    501     PrerenderContents* replacement) {
    502   FOR_EACH_OBSERVER(Observer, observer_list_,
    503                     OnPrerenderCreatedMatchCompleteReplacement(this,
    504                                                                replacement));
    505 }
    506 
    507 void PrerenderContents::DidUpdateFaviconURL(
    508     int32 page_id,
    509     const std::vector<content::FaviconURL>& urls) {
    510   VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_;
    511   for (std::vector<content::FaviconURL>::const_iterator it = urls.begin();
    512        it != urls.end(); ++it) {
    513     if (it->icon_type == content::FaviconURL::FAVICON) {
    514       icon_url_ = it->icon_url;
    515       VLOG(1) << icon_url_;
    516       return;
    517     }
    518   }
    519 }
    520 
    521 bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
    522   bool handled = true;
    523   // The following messages we do want to consume.
    524   IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message)
    525     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting,
    526                         OnCancelPrerenderForPrinting)
    527     IPC_MESSAGE_UNHANDLED(handled = false)
    528   IPC_END_MESSAGE_MAP()
    529 
    530   return handled;
    531 }
    532 
    533 bool PrerenderContents::CheckURL(const GURL& url) {
    534   const bool http = url.SchemeIs(content::kHttpScheme);
    535   const bool https = url.SchemeIs(content::kHttpsScheme);
    536   if (!http && !https) {
    537     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
    538     Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
    539     return false;
    540   }
    541   if (https && !prerender_manager_->config().https_allowed) {
    542     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
    543     Destroy(FINAL_STATUS_HTTPS);
    544     return false;
    545   }
    546   if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
    547       prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
    548     Destroy(FINAL_STATUS_RECENTLY_VISITED);
    549     return false;
    550   }
    551   return true;
    552 }
    553 
    554 bool PrerenderContents::AddAliasURL(const GURL& url) {
    555   if (!CheckURL(url))
    556     return false;
    557 
    558   alias_urls_.push_back(url);
    559 
    560   for (content::RenderProcessHost::iterator host_iterator =
    561            content::RenderProcessHost::AllHostsIterator();
    562        !host_iterator.IsAtEnd();
    563        host_iterator.Advance()) {
    564     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
    565     host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
    566   }
    567 
    568   return true;
    569 }
    570 
    571 bool PrerenderContents::Matches(
    572     const GURL& url,
    573     const SessionStorageNamespace* session_storage_namespace) const {
    574   if (session_storage_namespace &&
    575       session_storage_namespace_id_ != session_storage_namespace->id()) {
    576     return false;
    577   }
    578   return std::count_if(alias_urls_.begin(), alias_urls_.end(),
    579                        std::bind2nd(std::equal_to<GURL>(), url)) != 0;
    580 }
    581 
    582 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
    583   Destroy(FINAL_STATUS_RENDERER_CRASHED);
    584 }
    585 
    586 void PrerenderContents::RenderFrameCreated(
    587     content::RenderFrameHost* render_frame_host) {
    588   render_frame_hosts_.insert(render_frame_host);
    589   // When a new RenderFrame is created for a prerendering WebContents, tell the
    590   // new RenderFrame it's being used for prerendering before any navigations
    591   // occur.  Note that this is always triggered before the first navigation, so
    592   // there's no need to send the message just after the WebContents is created.
    593   render_frame_host->Send(new PrerenderMsg_SetIsPrerendering(
    594       render_frame_host->GetRoutingID(), true));
    595 }
    596 
    597 void PrerenderContents::RenderFrameDeleted(
    598     content::RenderFrameHost* render_frame_host) {
    599   render_frame_hosts_.erase(render_frame_host);
    600 }
    601 
    602 void PrerenderContents::DidStopLoading(
    603     content::RenderViewHost* render_view_host) {
    604   has_stopped_loading_ = true;
    605   NotifyPrerenderStopLoading();
    606 }
    607 
    608 void PrerenderContents::DidStartProvisionalLoadForFrame(
    609     int64 frame_id,
    610     int64 parent_frame_id,
    611     bool is_main_frame,
    612     const GURL& validated_url,
    613     bool is_error_page,
    614     bool is_iframe_srcdoc,
    615     RenderViewHost* render_view_host) {
    616   if (is_main_frame) {
    617     if (!CheckURL(validated_url))
    618       return;
    619 
    620     // Usually, this event fires if the user clicks or enters a new URL.
    621     // Neither of these can happen in the case of an invisible prerender.
    622     // So the cause is: Some JavaScript caused a new URL to be loaded.  In that
    623     // case, the spinner would start again in the browser, so we must reset
    624     // has_stopped_loading_ so that the spinner won't be stopped.
    625     has_stopped_loading_ = false;
    626     has_finished_loading_ = false;
    627   }
    628 }
    629 
    630 void PrerenderContents::DidFinishLoad(int64 frame_id,
    631                                       const GURL& validated_url,
    632                                       bool is_main_frame,
    633                                       RenderViewHost* render_view_host) {
    634   if (is_main_frame)
    635     has_finished_loading_ = true;
    636 }
    637 
    638 void PrerenderContents::DidNavigateMainFrame(
    639     const content::LoadCommittedDetails& details,
    640     const content::FrameNavigateParams& params) {
    641   // If the prerender made a second navigation entry, abort the prerender. This
    642   // avoids having to correctly implement a complex history merging case (this
    643   // interacts with location.replace) and correctly synchronize with the
    644   // renderer. The final status may be monitored to see we need to revisit this
    645   // decision. This does not affect client redirects as those do not push new
    646   // history entries. (Calls to location.replace, navigations before onload, and
    647   // <meta http-equiv=refresh> with timeouts under 1 second do not create
    648   // entries in Blink.)
    649   if (prerender_contents_->GetController().GetEntryCount() > 1) {
    650     Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY);
    651     return;
    652   }
    653 
    654   // Add each redirect as an alias. |params.url| is included in
    655   // |params.redirects|.
    656   //
    657   // TODO(davidben): We do not correctly patch up history for renderer-initated
    658   // navigations which add history entries. http://crbug.com/305660.
    659   for (size_t i = 0; i < params.redirects.size(); i++) {
    660     if (!AddAliasURL(params.redirects[i]))
    661       return;
    662   }
    663 }
    664 
    665 void PrerenderContents::DidGetRedirectForResourceRequest(
    666     const content::ResourceRedirectDetails& details) {
    667   // DidGetRedirectForResourceRequest can come for any resource on a page.  If
    668   // it's a redirect on the top-level resource, the name needs to be remembered
    669   // for future matching, and if it redirects to an https resource, it needs to
    670   // be canceled. If a subresource is redirected, nothing changes.
    671   if (details.resource_type != ResourceType::MAIN_FRAME)
    672     return;
    673   CheckURL(details.new_url);
    674 }
    675 
    676 void PrerenderContents::Destroy(FinalStatus final_status) {
    677   DCHECK_NE(final_status, FINAL_STATUS_USED);
    678 
    679   if (prerendering_has_been_cancelled_)
    680     return;
    681 
    682   if (child_id_ != -1 && route_id_ != -1) {
    683     // Cancel the prerender in the PrerenderTracker.  This is needed
    684     // because destroy may be called directly from the UI thread without calling
    685     // TryCancel().  This is difficult to completely avoid, since prerendering
    686     // can be cancelled before a RenderView is created.
    687     bool is_cancelled = prerender_manager()->prerender_tracker()->TryCancel(
    688         child_id_, route_id_, final_status);
    689     CHECK(is_cancelled);
    690 
    691     // A different final status may have been set already from another thread.
    692     // If so, use it instead.
    693     if (!prerender_manager()->prerender_tracker()->
    694             GetFinalStatus(child_id_, route_id_, &final_status)) {
    695       NOTREACHED();
    696     }
    697   }
    698   SetFinalStatus(final_status);
    699 
    700   prerendering_has_been_cancelled_ = true;
    701   prerender_manager_->AddToHistory(this);
    702   prerender_manager_->MoveEntryToPendingDelete(this, final_status);
    703 
    704   // Note that if this PrerenderContents was made into a MatchComplete
    705   // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
    706   // not reach the PrerenderHandle. Rather
    707   // OnPrerenderCreatedMatchCompleteReplacement will propogate that
    708   // information to the referer.
    709   if (!prerender_manager_->IsControlGroup(experiment_id()) &&
    710       (prerendering_has_started() ||
    711        match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
    712     NotifyPrerenderStop();
    713   }
    714 }
    715 
    716 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
    717   if (process_metrics_.get() == NULL) {
    718     // If a PrenderContents hasn't started prerending, don't be fully formed.
    719     if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
    720       return NULL;
    721     base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
    722     if (handle == base::kNullProcessHandle)
    723       return NULL;
    724 #if !defined(OS_MACOSX)
    725     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
    726 #else
    727     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
    728         handle,
    729         content::BrowserChildProcessHost::GetPortProvider()));
    730 #endif
    731   }
    732 
    733   return process_metrics_.get();
    734 }
    735 
    736 void PrerenderContents::DestroyWhenUsingTooManyResources() {
    737   base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
    738   if (metrics == NULL)
    739     return;
    740 
    741   size_t private_bytes, shared_bytes;
    742   if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
    743       private_bytes > prerender_manager_->config().max_bytes) {
    744     Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
    745   }
    746 }
    747 
    748 WebContents* PrerenderContents::ReleasePrerenderContents() {
    749   prerender_contents_->SetDelegate(NULL);
    750   content::WebContentsObserver::Observe(NULL);
    751   if (alias_session_storage_namespace)
    752     alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_);
    753   return prerender_contents_.release();
    754 }
    755 
    756 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
    757   return const_cast<RenderViewHost*>(GetRenderViewHost());
    758 }
    759 
    760 const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
    761   if (!prerender_contents_.get())
    762     return NULL;
    763   return prerender_contents_->GetRenderViewHost();
    764 }
    765 
    766 void PrerenderContents::DidNavigate(
    767     const history::HistoryAddPageArgs& add_page_args) {
    768   add_page_vector_.push_back(add_page_args);
    769 }
    770 
    771 void PrerenderContents::CommitHistory(WebContents* tab) {
    772   HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
    773   for (size_t i = 0; i < add_page_vector_.size(); ++i)
    774     history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
    775 }
    776 
    777 Value* PrerenderContents::GetAsValue() const {
    778   if (!prerender_contents_.get())
    779     return NULL;
    780   DictionaryValue* dict_value = new DictionaryValue();
    781   dict_value->SetString("url", prerender_url_.spec());
    782   base::TimeTicks current_time = base::TimeTicks::Now();
    783   base::TimeDelta duration = current_time - load_start_time_;
    784   dict_value->SetInteger("duration", duration.InSeconds());
    785   dict_value->SetBoolean("is_loaded", prerender_contents_ &&
    786                                       !prerender_contents_->IsLoading());
    787   return dict_value;
    788 }
    789 
    790 bool PrerenderContents::IsCrossSiteNavigationPending() const {
    791   if (!prerender_contents_)
    792     return false;
    793   return (prerender_contents_->GetSiteInstance() !=
    794           prerender_contents_->GetPendingSiteInstance());
    795 }
    796 
    797 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
    798   if (!prerender_contents())
    799     return NULL;
    800   return prerender_contents()->GetController().
    801       GetDefaultSessionStorageNamespace();
    802 }
    803 
    804 void PrerenderContents::OnCancelPrerenderForPrinting() {
    805   Destroy(FINAL_STATUS_WINDOW_PRINT);
    806 }
    807 
    808 void PrerenderContents::RecordCookieEvent(CookieEvent event,
    809                                           bool is_main_frame_http_request,
    810                                           base::Time earliest_create_date) {
    811   // We don't care about sent cookies that were created after this prerender
    812   // started.
    813   // The reason is that for the purpose of the histograms emitted, we only care
    814   // about cookies that existed before the prerender was started, but not
    815   // about cookies that were created as part of the prerender. Using the
    816   // earliest creation timestamp of all cookies provided by the cookie monster
    817   // is a heuristic that yields the desired result pretty closely.
    818   // In particular, we pretend no other WebContents make changes to the cookies
    819   // relevant to the prerender, which may not actually always be the case, but
    820   // hopefully most of the times.
    821   if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_)
    822     return;
    823 
    824   InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX;
    825 
    826   if (is_main_frame_http_request) {
    827     if (event == COOKIE_EVENT_SEND) {
    828       internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND;
    829     } else {
    830       internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE;
    831     }
    832   } else {
    833     if (event == COOKIE_EVENT_SEND) {
    834       internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND;
    835     } else {
    836       internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE;
    837     }
    838   }
    839 
    840   DCHECK_GE(internal_event, 0);
    841   DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX);
    842 
    843   cookie_status_ |= (1 << internal_event);
    844 
    845   DCHECK_GE(cookie_status_, 0);
    846   DCHECK_LT(cookie_status_, kNumCookieStatuses);
    847 }
    848 
    849 }  // namespace prerender
    850