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/bind.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/chrome_notification_types.h"
     14 #include "chrome/browser/history/history_tab_helper.h"
     15 #include "chrome/browser/prerender/prerender_field_trial.h"
     16 #include "chrome/browser/prerender/prerender_final_status.h"
     17 #include "chrome/browser/prerender/prerender_handle.h"
     18 #include "chrome/browser/prerender/prerender_manager.h"
     19 #include "chrome/browser/prerender/prerender_manager_factory.h"
     20 #include "chrome/browser/prerender/prerender_resource_throttle.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/tab_helpers.h"
     25 #include "chrome/browser/ui/web_contents_sizer.h"
     26 #include "chrome/common/prerender_messages.h"
     27 #include "chrome/common/render_messages.h"
     28 #include "chrome/common/url_constants.h"
     29 #include "components/history/core/browser/history_types.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 "net/url_request/url_request_context_getter.h"
     42 #include "ui/base/page_transition_types.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::ResourceType;
     51 using content::SessionStorageNamespace;
     52 using content::WebContents;
     53 
     54 namespace prerender {
     55 
     56 namespace {
     57 
     58 // Internal cookie event.
     59 // Whenever a prerender interacts with the cookie store, either sending
     60 // existing cookies that existed before the prerender started, or when a cookie
     61 // is changed, we record these events for histogramming purposes.
     62 enum InternalCookieEvent {
     63   INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0,
     64   INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1,
     65   INTERNAL_COOKIE_EVENT_OTHER_SEND = 2,
     66   INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3,
     67   INTERNAL_COOKIE_EVENT_MAX
     68 };
     69 
     70 // Indicates whether existing cookies were sent, and if they were third party
     71 // cookies, and whether they were for blocking resources.
     72 // Each value may be inclusive of previous values. We only care about the
     73 // value with the highest index that has ever occurred in the course of a
     74 // prerender.
     75 enum CookieSendType {
     76   COOKIE_SEND_TYPE_NONE = 0,
     77   COOKIE_SEND_TYPE_FIRST_PARTY = 1,
     78   COOKIE_SEND_TYPE_THIRD_PARTY = 2,
     79   COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3,
     80   COOKIE_SEND_TYPE_MAX
     81 };
     82 
     83 void ResumeThrottles(
     84     std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) {
     85   for (size_t i = 0; i < throttles.size(); i++) {
     86     if (throttles[i])
     87       throttles[i]->Resume();
     88   }
     89 }
     90 
     91 }  // namespace
     92 
     93 // static
     94 const int PrerenderContents::kNumCookieStatuses =
     95     (1 << INTERNAL_COOKIE_EVENT_MAX);
     96 
     97 // static
     98 const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX;
     99 
    100 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
    101  public:
    102   virtual PrerenderContents* CreatePrerenderContents(
    103       PrerenderManager* prerender_manager, Profile* profile,
    104       const GURL& url, const content::Referrer& referrer,
    105       Origin origin, uint8 experiment_id) OVERRIDE {
    106     return new PrerenderContents(prerender_manager, profile,
    107                                  url, referrer, origin, experiment_id);
    108   }
    109 };
    110 
    111 // WebContentsDelegateImpl -----------------------------------------------------
    112 
    113 class PrerenderContents::WebContentsDelegateImpl
    114     : public content::WebContentsDelegate {
    115  public:
    116   explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
    117       : prerender_contents_(prerender_contents) {
    118   }
    119 
    120   // content::WebContentsDelegate implementation:
    121   virtual WebContents* OpenURLFromTab(WebContents* source,
    122                                       const OpenURLParams& params) OVERRIDE {
    123     // |OpenURLFromTab| is typically called when a frame performs a navigation
    124     // that requires the browser to perform the transition instead of WebKit.
    125     // Examples include prerendering a site that redirects to an app URL,
    126     // or if --enable-strict-site-isolation is specified and the prerendered
    127     // frame redirects to a different origin.
    128     // TODO(cbentzel): Consider supporting this if it is a common case during
    129     // prerenders.
    130     prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
    131     return NULL;
    132   }
    133 
    134   virtual void CloseContents(content::WebContents* contents) OVERRIDE {
    135     prerender_contents_->Destroy(FINAL_STATUS_CLOSED);
    136   }
    137 
    138   virtual void CanDownload(
    139       RenderViewHost* render_view_host,
    140       const GURL& url,
    141       const std::string& request_method,
    142       const base::Callback<void(bool)>& callback) OVERRIDE {
    143     prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
    144     // Cancel the download.
    145     callback.Run(false);
    146   }
    147 
    148    virtual bool ShouldCreateWebContents(
    149        WebContents* web_contents,
    150        int route_id,
    151        WindowContainerType window_container_type,
    152        const base::string16& frame_name,
    153        const GURL& target_url,
    154        const std::string& partition_id,
    155        SessionStorageNamespace* session_storage_namespace) OVERRIDE {
    156     // Since we don't want to permit child windows that would have a
    157     // window.opener property, terminate prerendering.
    158     prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
    159     // Cancel the popup.
    160     return false;
    161   }
    162 
    163   virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
    164     // This isn't allowed because the history merge operation
    165     // does not work if there are renderer issued challenges.
    166     // TODO(cbentzel): Cancel in this case? May not need to do
    167     // since render-issued offset navigations are not guaranteed,
    168     // but indicates that the page cares about the history.
    169     return false;
    170   }
    171 
    172   virtual bool ShouldSuppressDialogs() OVERRIDE {
    173     // We still want to show the user the message when they navigate to this
    174     // page, so cancel this prerender.
    175     prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
    176     // Always suppress JavaScript messages if they're triggered by a page being
    177     // prerendered.
    178     return true;
    179   }
    180 
    181   virtual void RegisterProtocolHandler(WebContents* web_contents,
    182                                        const std::string& protocol,
    183                                        const GURL& url,
    184                                        bool user_gesture) OVERRIDE {
    185     // TODO(mmenke): Consider supporting this if it is a common case during
    186     // prerenders.
    187     prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
    188   }
    189 
    190   virtual gfx::Size GetSizeForNewRenderView(
    191       WebContents* web_contents) const OVERRIDE {
    192     // Have to set the size of the RenderView on initialization to be sure it is
    193     // set before the RenderView is hidden on all platforms (esp. Android).
    194     return prerender_contents_->size_;
    195   }
    196 
    197  private:
    198   PrerenderContents* prerender_contents_;
    199 };
    200 
    201 void PrerenderContents::Observer::OnPrerenderStopLoading(
    202     PrerenderContents* contents) {
    203 }
    204 
    205 void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
    206     PrerenderContents* contents) {
    207 }
    208 
    209 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
    210     PrerenderContents* contents, PrerenderContents* replacement) {
    211 }
    212 
    213 PrerenderContents::Observer::Observer() {
    214 }
    215 
    216 PrerenderContents::Observer::~Observer() {
    217 }
    218 
    219 PrerenderContents::PrerenderContents(
    220     PrerenderManager* prerender_manager,
    221     Profile* profile,
    222     const GURL& url,
    223     const content::Referrer& referrer,
    224     Origin origin,
    225     uint8 experiment_id)
    226     : prerendering_has_started_(false),
    227       session_storage_namespace_id_(-1),
    228       prerender_manager_(prerender_manager),
    229       prerender_url_(url),
    230       referrer_(referrer),
    231       profile_(profile),
    232       page_id_(0),
    233       has_stopped_loading_(false),
    234       has_finished_loading_(false),
    235       final_status_(FINAL_STATUS_MAX),
    236       match_complete_status_(MATCH_COMPLETE_DEFAULT),
    237       prerendering_has_been_cancelled_(false),
    238       child_id_(-1),
    239       route_id_(-1),
    240       origin_(origin),
    241       experiment_id_(experiment_id),
    242       creator_child_id_(-1),
    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   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 = ui::PAGE_TRANSITION_LINK;
    386   if (origin_ == ORIGIN_OMNIBOX) {
    387     load_url_params.transition_type = ui::PageTransitionFromInt(
    388         ui::PAGE_TRANSITION_TYPED |
    389         ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
    390   } else if (origin_ == ORIGIN_INSTANT) {
    391     load_url_params.transition_type = ui::PageTransitionFromInt(
    392         ui::PAGE_TRANSITION_GENERATED |
    393         ui::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     content::RenderFrameHost* render_frame_host) {
    623   if (!render_frame_host->GetParent())
    624     NotifyPrerenderDomContentLoaded();
    625 }
    626 
    627 void PrerenderContents::DidStartProvisionalLoadForFrame(
    628     content::RenderFrameHost* render_frame_host,
    629     const GURL& validated_url,
    630     bool is_error_page,
    631     bool is_iframe_srcdoc) {
    632   if (!render_frame_host->GetParent()) {
    633     if (!CheckURL(validated_url))
    634       return;
    635 
    636     // Usually, this event fires if the user clicks or enters a new URL.
    637     // Neither of these can happen in the case of an invisible prerender.
    638     // So the cause is: Some JavaScript caused a new URL to be loaded.  In that
    639     // case, the spinner would start again in the browser, so we must reset
    640     // has_stopped_loading_ so that the spinner won't be stopped.
    641     has_stopped_loading_ = false;
    642     has_finished_loading_ = false;
    643   }
    644 }
    645 
    646 void PrerenderContents::DidFinishLoad(
    647     content::RenderFrameHost* render_frame_host,
    648     const GURL& validated_url) {
    649   if (!render_frame_host->GetParent())
    650     has_finished_loading_ = true;
    651 }
    652 
    653 void PrerenderContents::DidNavigateMainFrame(
    654     const content::LoadCommittedDetails& details,
    655     const content::FrameNavigateParams& params) {
    656   // If the prerender made a second navigation entry, abort the prerender. This
    657   // avoids having to correctly implement a complex history merging case (this
    658   // interacts with location.replace) and correctly synchronize with the
    659   // renderer. The final status may be monitored to see we need to revisit this
    660   // decision. This does not affect client redirects as those do not push new
    661   // history entries. (Calls to location.replace, navigations before onload, and
    662   // <meta http-equiv=refresh> with timeouts under 1 second do not create
    663   // entries in Blink.)
    664   if (prerender_contents_->GetController().GetEntryCount() > 1) {
    665     Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY);
    666     return;
    667   }
    668 
    669   // Add each redirect as an alias. |params.url| is included in
    670   // |params.redirects|.
    671   //
    672   // TODO(davidben): We do not correctly patch up history for renderer-initated
    673   // navigations which add history entries. http://crbug.com/305660.
    674   for (size_t i = 0; i < params.redirects.size(); i++) {
    675     if (!AddAliasURL(params.redirects[i]))
    676       return;
    677   }
    678 }
    679 
    680 void PrerenderContents::DidGetRedirectForResourceRequest(
    681     RenderViewHost* render_view_host,
    682     const content::ResourceRedirectDetails& details) {
    683   // DidGetRedirectForResourceRequest can come for any resource on a page.  If
    684   // it's a redirect on the top-level resource, the name needs to be remembered
    685   // for future matching, and if it redirects to an https resource, it needs to
    686   // be canceled. If a subresource is redirected, nothing changes.
    687   if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
    688     return;
    689   CheckURL(details.new_url);
    690 }
    691 
    692 void PrerenderContents::Destroy(FinalStatus final_status) {
    693   DCHECK_NE(final_status, FINAL_STATUS_USED);
    694 
    695   if (prerendering_has_been_cancelled_)
    696     return;
    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.get())
    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 base::Value* PrerenderContents::GetAsValue() const {
    778   if (!prerender_contents_.get())
    779     return NULL;
    780   base::DictionaryValue* dict_value = new base::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 void PrerenderContents::PrepareForUse() {
    798   SetFinalStatus(FINAL_STATUS_USED);
    799 
    800   if (prerender_contents_.get()) {
    801     prerender_contents_->SendToAllFrames(
    802         new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false));
    803   }
    804 
    805   NotifyPrerenderStop();
    806 
    807   BrowserThread::PostTask(
    808       BrowserThread::IO,
    809       FROM_HERE,
    810       base::Bind(&ResumeThrottles, resource_throttles_));
    811   resource_throttles_.clear();
    812 }
    813 
    814 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
    815   if (!prerender_contents())
    816     return NULL;
    817   return prerender_contents()->GetController().
    818       GetDefaultSessionStorageNamespace();
    819 }
    820 
    821 void PrerenderContents::OnCancelPrerenderForPrinting() {
    822   Destroy(FINAL_STATUS_WINDOW_PRINT);
    823 }
    824 
    825 void PrerenderContents::RecordCookieEvent(CookieEvent event,
    826                                           bool is_main_frame_http_request,
    827                                           bool is_third_party_cookie,
    828                                           bool is_for_blocking_resource,
    829                                           base::Time earliest_create_date) {
    830   // We don't care about sent cookies that were created after this prerender
    831   // started.
    832   // The reason is that for the purpose of the histograms emitted, we only care
    833   // about cookies that existed before the prerender was started, but not
    834   // about cookies that were created as part of the prerender. Using the
    835   // earliest creation timestamp of all cookies provided by the cookie monster
    836   // is a heuristic that yields the desired result pretty closely.
    837   // In particular, we pretend no other WebContents make changes to the cookies
    838   // relevant to the prerender, which may not actually always be the case, but
    839   // hopefully most of the times.
    840   if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_)
    841     return;
    842 
    843   InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX;
    844 
    845   if (is_main_frame_http_request) {
    846     if (event == COOKIE_EVENT_SEND) {
    847       internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND;
    848     } else {
    849       internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE;
    850     }
    851   } else {
    852     if (event == COOKIE_EVENT_SEND) {
    853       internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND;
    854     } else {
    855       internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE;
    856     }
    857   }
    858 
    859   DCHECK_GE(internal_event, 0);
    860   DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX);
    861 
    862   cookie_status_ |= (1 << internal_event);
    863 
    864   DCHECK_GE(cookie_status_, 0);
    865   DCHECK_LT(cookie_status_, kNumCookieStatuses);
    866 
    867   CookieSendType send_type = COOKIE_SEND_TYPE_NONE;
    868   if (event == COOKIE_EVENT_SEND) {
    869     if (!is_third_party_cookie) {
    870       send_type = COOKIE_SEND_TYPE_FIRST_PARTY;
    871     } else {
    872       if (is_for_blocking_resource) {
    873         send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE;
    874       } else {
    875         send_type = COOKIE_SEND_TYPE_THIRD_PARTY;
    876       }
    877     }
    878   }
    879   DCHECK_GE(send_type, 0);
    880   DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX);
    881 
    882   if (cookie_send_type_ < send_type)
    883     cookie_send_type_ = send_type;
    884 }
    885 
    886  void PrerenderContents::AddResourceThrottle(
    887      const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
    888    resource_throttles_.push_back(throttle);
    889  }
    890 
    891  void PrerenderContents::AddNetworkBytes(int64 bytes) {
    892    network_bytes_ += bytes;
    893  }
    894 
    895 }  // namespace prerender
    896