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