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_manager.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/logging.h"
     15 #include "base/memory/weak_ptr.h"
     16 #include "base/metrics/histogram.h"
     17 #include "base/prefs/pref_service.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/time/time.h"
     21 #include "base/timer/elapsed_timer.h"
     22 #include "base/values.h"
     23 #include "chrome/browser/browser_process.h"
     24 #include "chrome/browser/chrome_notification_types.h"
     25 #include "chrome/browser/common/cancelable_request.h"
     26 #include "chrome/browser/favicon/favicon_tab_helper.h"
     27 #include "chrome/browser/history/history_service_factory.h"
     28 #include "chrome/browser/net/chrome_cookie_notification_details.h"
     29 #include "chrome/browser/predictors/predictor_database.h"
     30 #include "chrome/browser/predictors/predictor_database_factory.h"
     31 #include "chrome/browser/prerender/prerender_condition.h"
     32 #include "chrome/browser/prerender/prerender_contents.h"
     33 #include "chrome/browser/prerender/prerender_field_trial.h"
     34 #include "chrome/browser/prerender/prerender_final_status.h"
     35 #include "chrome/browser/prerender/prerender_handle.h"
     36 #include "chrome/browser/prerender/prerender_histograms.h"
     37 #include "chrome/browser/prerender/prerender_history.h"
     38 #include "chrome/browser/prerender/prerender_local_predictor.h"
     39 #include "chrome/browser/prerender/prerender_manager_factory.h"
     40 #include "chrome/browser/prerender/prerender_tab_helper.h"
     41 #include "chrome/browser/prerender/prerender_tracker.h"
     42 #include "chrome/browser/prerender/prerender_util.h"
     43 #include "chrome/browser/profiles/profile.h"
     44 #include "chrome/browser/search/search.h"
     45 #include "chrome/browser/tab_contents/tab_util.h"
     46 #include "chrome/browser/ui/browser_navigator.h"
     47 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
     48 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h"
     49 #include "chrome/common/chrome_switches.h"
     50 #include "chrome/common/pref_names.h"
     51 #include "chrome/common/prerender_messages.h"
     52 #include "content/public/browser/browser_thread.h"
     53 #include "content/public/browser/devtools_agent_host.h"
     54 #include "content/public/browser/navigation_controller.h"
     55 #include "content/public/browser/notification_service.h"
     56 #include "content/public/browser/notification_source.h"
     57 #include "content/public/browser/render_process_host.h"
     58 #include "content/public/browser/render_view_host.h"
     59 #include "content/public/browser/session_storage_namespace.h"
     60 #include "content/public/browser/web_contents.h"
     61 #include "content/public/browser/web_contents_delegate.h"
     62 #include "content/public/browser/web_contents_view.h"
     63 #include "content/public/common/favicon_url.h"
     64 #include "content/public/common/url_constants.h"
     65 #include "extensions/common/constants.h"
     66 #include "net/url_request/url_request_context.h"
     67 #include "net/url_request/url_request_context_getter.h"
     68 
     69 using content::BrowserThread;
     70 using content::RenderViewHost;
     71 using content::SessionStorageNamespace;
     72 using content::WebContents;
     73 using predictors::LoggedInPredictorTable;
     74 
     75 namespace prerender {
     76 
     77 namespace {
     78 
     79 // Time interval at which periodic cleanups are performed.
     80 const int kPeriodicCleanupIntervalMs = 1000;
     81 
     82 // Valid HTTP methods for prerendering.
     83 const char* const kValidHttpMethods[] = {
     84   "GET",
     85   "HEAD",
     86   "OPTIONS",
     87   "POST",
     88   "TRACE",
     89 };
     90 
     91 // Length of prerender history, for display in chrome://net-internals
     92 const int kHistoryLength = 100;
     93 
     94 // Timeout, in ms, for a session storage namespace merge.
     95 const int kSessionStorageNamespaceMergeTimeoutMs = 500;
     96 
     97 // Indicates whether a Prerender has been cancelled such that we need
     98 // a dummy replacement for the purpose of recording the correct PPLT for
     99 // the Match Complete case.
    100 // Traditionally, "Match" means that a prerendered page was actually visited &
    101 // the prerender was used.  Our goal is to have "Match" cases line up in the
    102 // control group & the experiment group, so that we can make meaningful
    103 // comparisons of improvements.  However, in the control group, since we don't
    104 // actually perform prerenders, many of the cancellation reasons cannot be
    105 // detected.  Therefore, in the Prerender group, when we cancel for one of these
    106 // reasons, we keep track of a dummy Prerender representing what we would
    107 // have in the control group.  If that dummy prerender in the prerender group
    108 // would then be swapped in (but isn't actually b/c it's a dummy), we record
    109 // this as a MatchComplete.  This allows us to compare MatchComplete's
    110 // across Prerender & Control group which ideally should be lining up.
    111 // This ensures that there is no bias in terms of the page load times
    112 // of the pages forming the difference between the two sets.
    113 
    114 bool NeedMatchCompleteDummyForFinalStatus(FinalStatus final_status) {
    115   return final_status != FINAL_STATUS_USED &&
    116       final_status != FINAL_STATUS_TIMED_OUT &&
    117       final_status != FINAL_STATUS_MANAGER_SHUTDOWN &&
    118       final_status != FINAL_STATUS_APP_TERMINATING &&
    119       final_status != FINAL_STATUS_WINDOW_OPENER &&
    120       final_status != FINAL_STATUS_CACHE_OR_HISTORY_CLEARED &&
    121       final_status != FINAL_STATUS_CANCELLED &&
    122       final_status != FINAL_STATUS_DEVTOOLS_ATTACHED &&
    123       final_status != FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING &&
    124       final_status != FINAL_STATUS_PAGE_BEING_CAPTURED &&
    125       final_status != FINAL_STATUS_NAVIGATION_UNCOMMITTED;
    126 }
    127 
    128 void CheckIfCookiesExistForDomainResultOnUIThread(
    129     const net::CookieMonster::HasCookiesForETLDP1Callback& callback,
    130     bool cookies_exist) {
    131   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    132   callback.Run(cookies_exist);
    133 }
    134 
    135 void CheckIfCookiesExistForDomainResultOnIOThread(
    136     const net::CookieMonster::HasCookiesForETLDP1Callback& callback,
    137     bool cookies_exist) {
    138   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    139   BrowserThread::PostTask(
    140       BrowserThread::UI,
    141       FROM_HERE,
    142       base::Bind(&CheckIfCookiesExistForDomainResultOnUIThread,
    143                  callback,
    144                  cookies_exist));
    145 }
    146 
    147 void CheckIfCookiesExistForDomainOnIOThread(
    148     net::URLRequestContextGetter* rq_context,
    149     const std::string& domain_key,
    150     const net::CookieMonster::HasCookiesForETLDP1Callback& callback) {
    151   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    152   net::CookieStore* cookie_store =
    153       rq_context->GetURLRequestContext()->cookie_store();
    154   cookie_store->GetCookieMonster()->HasCookiesForETLDP1Async(
    155       domain_key,
    156       base::Bind(&CheckIfCookiesExistForDomainResultOnIOThread, callback));
    157 }
    158 
    159 }  // namespace
    160 
    161 class PrerenderManager::OnCloseWebContentsDeleter
    162     : public content::WebContentsDelegate,
    163       public base::SupportsWeakPtr<
    164           PrerenderManager::OnCloseWebContentsDeleter> {
    165  public:
    166   OnCloseWebContentsDeleter(PrerenderManager* manager,
    167                             WebContents* tab)
    168       : manager_(manager),
    169         tab_(tab),
    170         suppressed_dialog_(false) {
    171     tab_->SetDelegate(this);
    172     base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
    173         base::Bind(&OnCloseWebContentsDeleter::ScheduleWebContentsForDeletion,
    174                    AsWeakPtr(), true),
    175         base::TimeDelta::FromSeconds(kDeleteWithExtremePrejudiceSeconds));
    176   }
    177 
    178   virtual void CloseContents(WebContents* source) OVERRIDE {
    179     DCHECK_EQ(tab_, source);
    180     ScheduleWebContentsForDeletion(false);
    181   }
    182 
    183   virtual void SwappedOut(WebContents* source) OVERRIDE {
    184     DCHECK_EQ(tab_, source);
    185     ScheduleWebContentsForDeletion(false);
    186   }
    187 
    188   virtual bool ShouldSuppressDialogs() OVERRIDE {
    189     // Use this as a proxy for getting statistics on how often we fail to honor
    190     // the beforeunload event.
    191     suppressed_dialog_ = true;
    192     return true;
    193   }
    194 
    195  private:
    196   static const int kDeleteWithExtremePrejudiceSeconds = 3;
    197 
    198   void ScheduleWebContentsForDeletion(bool timeout) {
    199     UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterTimeout", timeout);
    200     UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterSuppressedDialog",
    201                           suppressed_dialog_);
    202     tab_->SetDelegate(NULL);
    203     manager_->ScheduleDeleteOldWebContents(tab_.release(), this);
    204     // |this| is deleted at this point.
    205   }
    206 
    207   PrerenderManager* manager_;
    208   scoped_ptr<WebContents> tab_;
    209   bool suppressed_dialog_;
    210 
    211   DISALLOW_COPY_AND_ASSIGN(OnCloseWebContentsDeleter);
    212 };
    213 
    214 // static
    215 int PrerenderManager::prerenders_per_session_count_ = 0;
    216 
    217 // static
    218 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
    219     PRERENDER_MODE_ENABLED;
    220 
    221 struct PrerenderManager::NavigationRecord {
    222   NavigationRecord(const GURL& url, base::TimeTicks time)
    223       : url(url),
    224         time(time) {
    225   }
    226 
    227   GURL url;
    228   base::TimeTicks time;
    229 };
    230 
    231 PrerenderManager::PrerenderedWebContentsData::
    232 PrerenderedWebContentsData(Origin origin) : origin(origin) {
    233 }
    234 
    235 PrerenderManager::WouldBePrerenderedWebContentsData::
    236 WouldBePrerenderedWebContentsData(Origin origin)
    237     : origin(origin),
    238       state(WAITING_FOR_PROVISIONAL_LOAD) {
    239 }
    240 
    241 PrerenderManager::PrerenderManager(Profile* profile,
    242                                    PrerenderTracker* prerender_tracker)
    243     : enabled_(profile && profile->GetPrefs() &&
    244           profile->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled)),
    245       profile_(profile),
    246       prerender_tracker_(prerender_tracker),
    247       prerender_contents_factory_(PrerenderContents::CreateFactory()),
    248       last_prerender_start_time_(GetCurrentTimeTicks() -
    249           base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs)),
    250       prerender_history_(new PrerenderHistory(kHistoryLength)),
    251       histograms_(new PrerenderHistograms()) {
    252   // There are some assumptions that the PrerenderManager is on the UI thread.
    253   // Any other checks simply make sure that the PrerenderManager is accessed on
    254   // the same thread that it was created on.
    255   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    256 
    257   if (IsLocalPredictorEnabled())
    258     local_predictor_.reset(new PrerenderLocalPredictor(this));
    259 
    260   if (IsLoggedInPredictorEnabled() && !profile_->IsOffTheRecord()) {
    261     predictors::PredictorDatabase* predictor_db =
    262         predictors::PredictorDatabaseFactory::GetForProfile(profile);
    263     if (predictor_db) {
    264       logged_in_predictor_table_ = predictor_db->logged_in_table();
    265       scoped_ptr<LoggedInStateMap> new_state_map(new LoggedInStateMap);
    266       LoggedInStateMap* new_state_map_ptr = new_state_map.get();
    267       BrowserThread::PostTaskAndReply(
    268           BrowserThread::DB, FROM_HERE,
    269           base::Bind(&LoggedInPredictorTable::GetAllData,
    270                      logged_in_predictor_table_,
    271                      new_state_map_ptr),
    272           base::Bind(&PrerenderManager::LoggedInPredictorDataReceived,
    273                      AsWeakPtr(),
    274                      base::Passed(&new_state_map)));
    275     }
    276   }
    277 
    278   // Certain experiments override our default config_ values.
    279   switch (PrerenderManager::GetMode()) {
    280     case PrerenderManager::PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP:
    281       config_.max_link_concurrency = 4;
    282       config_.max_link_concurrency_per_launcher = 2;
    283       break;
    284     case PrerenderManager::PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP:
    285       config_.time_to_live = base::TimeDelta::FromMinutes(15);
    286       break;
    287     default:
    288       break;
    289   }
    290 
    291   notification_registrar_.Add(
    292       this, chrome::NOTIFICATION_COOKIE_CHANGED,
    293       content::NotificationService::AllBrowserContextsAndSources());
    294 
    295   MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
    296 }
    297 
    298 PrerenderManager::~PrerenderManager() {
    299   MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
    300 
    301   // The earlier call to BrowserContextKeyedService::Shutdown() should have
    302   // emptied these vectors already.
    303   DCHECK(active_prerenders_.empty());
    304   DCHECK(to_delete_prerenders_.empty());
    305 }
    306 
    307 void PrerenderManager::Shutdown() {
    308   DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN);
    309   STLDeleteElements(&prerender_conditions_);
    310   on_close_web_contents_deleters_.clear();
    311   // Must happen before |profile_| is set to NULL as
    312   // |local_predictor_| accesses it.
    313   if (local_predictor_)
    314     local_predictor_->Shutdown();
    315   profile_ = NULL;
    316 
    317   DCHECK(active_prerenders_.empty());
    318 }
    319 
    320 PrerenderHandle* PrerenderManager::AddPrerenderFromLinkRelPrerender(
    321     int process_id,
    322     int route_id,
    323     const GURL& url,
    324     const content::Referrer& referrer,
    325     const gfx::Size& size) {
    326   Origin origin = ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN;
    327   SessionStorageNamespace* session_storage_namespace = NULL;
    328   // Unit tests pass in a process_id == -1.
    329   if (process_id != -1) {
    330     RenderViewHost* source_render_view_host =
    331         RenderViewHost::FromID(process_id, route_id);
    332     if (!source_render_view_host)
    333       return NULL;
    334     WebContents* source_web_contents =
    335         WebContents::FromRenderViewHost(source_render_view_host);
    336     if (!source_web_contents)
    337       return NULL;
    338     if (source_web_contents->GetURL().host() == url.host())
    339       origin = ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN;
    340     // TODO(ajwong): This does not correctly handle storage for isolated apps.
    341     session_storage_namespace =
    342         source_web_contents->GetController()
    343             .GetDefaultSessionStorageNamespace();
    344   }
    345 
    346   // If the prerender request comes from a recently cancelled prerender that
    347   // |this| still owns, then abort the prerender.
    348   for (ScopedVector<PrerenderData>::iterator it = to_delete_prerenders_.begin();
    349        it != to_delete_prerenders_.end(); ++it) {
    350     PrerenderContents* prerender_contents = (*it)->contents();
    351     int contents_child_id;
    352     int contents_route_id;
    353     if (prerender_contents->GetChildId(&contents_child_id) &&
    354         prerender_contents->GetRouteId(&contents_route_id)) {
    355       if (contents_child_id == process_id && contents_route_id == route_id)
    356         return NULL;
    357     }
    358   }
    359 
    360   if (PrerenderData* parent_prerender_data =
    361           FindPrerenderDataForChildAndRoute(process_id, route_id)) {
    362     // Instead of prerendering from inside of a running prerender, we will defer
    363     // this request until its launcher is made visible.
    364     if (PrerenderContents* contents = parent_prerender_data->contents()) {
    365       PrerenderHandle* prerender_handle =
    366           new PrerenderHandle(static_cast<PrerenderData*>(NULL));
    367       scoped_ptr<PrerenderContents::PendingPrerenderInfo>
    368           pending_prerender_info(new PrerenderContents::PendingPrerenderInfo(
    369               prerender_handle->weak_ptr_factory_.GetWeakPtr(),
    370               origin, url, referrer, size));
    371 
    372       contents->AddPendingPrerender(pending_prerender_info.Pass());
    373       return prerender_handle;
    374     }
    375   }
    376 
    377   return AddPrerender(origin, process_id, url, referrer, size,
    378                       session_storage_namespace);
    379 }
    380 
    381 PrerenderHandle* PrerenderManager::AddPrerenderFromOmnibox(
    382     const GURL& url,
    383     SessionStorageNamespace* session_storage_namespace,
    384     const gfx::Size& size) {
    385   if (!IsOmniboxEnabled(profile_))
    386     return NULL;
    387   return AddPrerender(ORIGIN_OMNIBOX, -1, url, content::Referrer(), size,
    388                       session_storage_namespace);
    389 }
    390 
    391 PrerenderHandle* PrerenderManager::AddPrerenderFromLocalPredictor(
    392     const GURL& url,
    393     SessionStorageNamespace* session_storage_namespace,
    394     const gfx::Size& size) {
    395   return AddPrerender(ORIGIN_LOCAL_PREDICTOR, -1, url, content::Referrer(),
    396                       size, session_storage_namespace);
    397 }
    398 
    399 PrerenderHandle* PrerenderManager::AddPrerenderFromExternalRequest(
    400     const GURL& url,
    401     const content::Referrer& referrer,
    402     SessionStorageNamespace* session_storage_namespace,
    403     const gfx::Size& size) {
    404   return AddPrerender(ORIGIN_EXTERNAL_REQUEST, -1, url, referrer, size,
    405                       session_storage_namespace);
    406 }
    407 
    408 PrerenderHandle* PrerenderManager::AddPrerenderForInstant(
    409     const GURL& url,
    410     content::SessionStorageNamespace* session_storage_namespace,
    411     const gfx::Size& size) {
    412   DCHECK(chrome::ShouldPrefetchSearchResults());
    413   return AddPrerender(ORIGIN_INSTANT, -1, url, content::Referrer(), size,
    414                       session_storage_namespace);
    415 }
    416 
    417 void PrerenderManager::DestroyPrerenderForRenderView(
    418     int process_id, int view_id, FinalStatus final_status) {
    419   DCHECK(CalledOnValidThread());
    420   if (PrerenderData* prerender_data =
    421           FindPrerenderDataForChildAndRoute(process_id, view_id)) {
    422     prerender_data->contents()->Destroy(final_status);
    423   }
    424 }
    425 
    426 void PrerenderManager::CancelAllPrerenders() {
    427   DCHECK(CalledOnValidThread());
    428   while (!active_prerenders_.empty()) {
    429     PrerenderContents* prerender_contents =
    430         active_prerenders_.front()->contents();
    431     prerender_contents->Destroy(FINAL_STATUS_CANCELLED);
    432   }
    433 }
    434 
    435 bool PrerenderManager::MaybeUsePrerenderedPage(const GURL& url,
    436                                                chrome::NavigateParams* params) {
    437   DCHECK(CalledOnValidThread());
    438 
    439   content::WebContents* web_contents = params->target_contents;
    440   DCHECK(!IsWebContentsPrerendering(web_contents, NULL));
    441 
    442   // Don't prerender if the navigation involves some special parameters.
    443   if (params->uses_post || !params->extra_headers.empty())
    444     return false;
    445 
    446   DeleteOldEntries();
    447   to_delete_prerenders_.clear();
    448 
    449   // First, try to find prerender data with the correct session storage
    450   // namespace.
    451   // TODO(ajwong): This doesn't handle isolated apps correctly.
    452   PrerenderData* prerender_data = FindPrerenderData(
    453       url,
    454       web_contents->GetController().GetDefaultSessionStorageNamespace());
    455 
    456   // If this failed, we may still find a prerender for the same URL, but a
    457   // different session storage namespace. If we do, we might have to perform
    458   // a merge.
    459   if (!prerender_data) {
    460     prerender_data = FindPrerenderData(url, NULL);
    461   } else {
    462     RecordEvent(prerender_data->contents(),
    463                 PRERENDER_EVENT_SWAPIN_CANDIDATE_NAMESPACE_MATCHES);
    464   }
    465 
    466   if (!prerender_data)
    467     return false;
    468   RecordEvent(prerender_data->contents(), PRERENDER_EVENT_SWAPIN_CANDIDATE);
    469   DCHECK(prerender_data->contents());
    470 
    471   // If there is currently a merge pending for this prerender data,
    472   // or this webcontents, do not swap in, but give the merge a chance to
    473   // finish and swap into the intended target webcontents.
    474   if (prerender_data->pending_swap())
    475     return false;
    476 
    477   RecordEvent(prerender_data->contents(),
    478               PRERENDER_EVENT_SWAPIN_NO_MERGE_PENDING);
    479   SessionStorageNamespace* target_namespace =
    480       web_contents->GetController().GetDefaultSessionStorageNamespace();
    481   SessionStorageNamespace* prerender_namespace =
    482       prerender_data->contents()->GetSessionStorageNamespace();
    483   // Only when actually prerendering is session storage namespace merging an
    484   // issue. For the control group, it will be assumed that the merge succeeded.
    485   if (prerender_namespace && prerender_namespace != target_namespace &&
    486       !prerender_namespace->IsAliasOf(target_namespace)) {
    487     if (!ShouldMergeSessionStorageNamespaces()) {
    488       RecordEvent(prerender_data->contents(),
    489                   PRERENDER_EVENT_SWAPIN_MERGING_DISABLED);
    490       return false;
    491     }
    492     RecordEvent(prerender_data->contents(),
    493                 PRERENDER_EVENT_SWAPIN_ISSUING_MERGE);
    494     prerender_data->set_pending_swap(new PendingSwap(
    495         this, web_contents, prerender_data, url,
    496         params->should_replace_current_entry));
    497     prerender_data->pending_swap()->BeginSwap();
    498     // Although this returns false, creating a PendingSwap registers with
    499     // PrerenderTracker to throttle MAIN_FRAME navigations while the swap is
    500     // pending.
    501     return false;
    502   }
    503 
    504   // No need to merge; swap synchronously.
    505   WebContents* new_web_contents = SwapInternal(
    506       url, web_contents, prerender_data,
    507       params->should_replace_current_entry);
    508   if (!new_web_contents)
    509     return false;
    510 
    511   // Record the new target_contents for the callers.
    512   params->target_contents = new_web_contents;
    513   return true;
    514 }
    515 
    516 WebContents* PrerenderManager::SwapInternal(
    517     const GURL& url,
    518     WebContents* web_contents,
    519     PrerenderData* prerender_data,
    520     bool should_replace_current_entry) {
    521   DCHECK(CalledOnValidThread());
    522   DCHECK(!IsWebContentsPrerendering(web_contents, NULL));
    523 
    524   // Only swap if the target WebContents has a CoreTabHelper delegate to swap
    525   // out of it. For a normal WebContents, this is if it is in a TabStripModel.
    526   CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
    527   if (!core_tab_helper || !core_tab_helper->delegate()) {
    528     RecordEvent(prerender_data->contents(), PRERENDER_EVENT_SWAPIN_NO_DELEGATE);
    529     return NULL;
    530   }
    531 
    532   if (IsNoSwapInExperiment(prerender_data->contents()->experiment_id()))
    533     return NULL;
    534 
    535   if (WebContents* new_web_contents =
    536       prerender_data->contents()->prerender_contents()) {
    537     if (web_contents == new_web_contents)
    538       return NULL;  // Do not swap in to ourself.
    539 
    540     // We cannot swap in if there is no last committed entry, because we would
    541     // show a blank page under an existing entry from the current tab.  Even if
    542     // there is a pending entry, it may not commit.
    543     // TODO(creis): If there is a pending navigation and no last committed
    544     // entry, we might be able to transfer the network request instead.
    545     if (!new_web_contents->GetController().CanPruneAllButLastCommitted()) {
    546       // Abort this prerender so it is not used later. http://crbug.com/292121
    547       prerender_data->contents()->Destroy(FINAL_STATUS_NAVIGATION_UNCOMMITTED);
    548       return NULL;
    549     }
    550   }
    551 
    552   // Do not use the prerendered version if there is an opener object.
    553   if (web_contents->HasOpener()) {
    554     prerender_data->contents()->Destroy(FINAL_STATUS_WINDOW_OPENER);
    555     return NULL;
    556   }
    557 
    558   // Do not swap in the prerender if the current WebContents is being captured.
    559   if (web_contents->GetCapturerCount() > 0) {
    560     prerender_data->contents()->Destroy(FINAL_STATUS_PAGE_BEING_CAPTURED);
    561     return NULL;
    562   }
    563 
    564   // If we are just in the control group (which can be detected by noticing
    565   // that prerendering hasn't even started yet), record that |web_contents| now
    566   // would be showing a prerendered contents, but otherwise, don't do anything.
    567   if (!prerender_data->contents()->prerendering_has_started()) {
    568     MarkWebContentsAsWouldBePrerendered(web_contents,
    569                                         prerender_data->contents()->origin());
    570     prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED);
    571     return NULL;
    572   }
    573 
    574   // Don't use prerendered pages if debugger is attached to the tab.
    575   // See http://crbug.com/98541
    576   if (content::DevToolsAgentHost::IsDebuggerAttached(web_contents)) {
    577     DestroyAndMarkMatchCompleteAsUsed(prerender_data->contents(),
    578                                       FINAL_STATUS_DEVTOOLS_ATTACHED);
    579     return NULL;
    580   }
    581 
    582   // If the prerendered page is in the middle of a cross-site navigation,
    583   // don't swap it in because there isn't a good way to merge histories.
    584   if (prerender_data->contents()->IsCrossSiteNavigationPending()) {
    585     DestroyAndMarkMatchCompleteAsUsed(
    586         prerender_data->contents(),
    587         FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING);
    588     return NULL;
    589   }
    590 
    591   // For bookkeeping purposes, we need to mark this WebContents to
    592   // reflect that it would have been prerendered.
    593   if (GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP) {
    594     MarkWebContentsAsWouldBePrerendered(web_contents,
    595                                         prerender_data->contents()->origin());
    596     prerender_data->contents()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED);
    597     return NULL;
    598   }
    599 
    600   int child_id, route_id;
    601   CHECK(prerender_data->contents()->GetChildId(&child_id));
    602   CHECK(prerender_data->contents()->GetRouteId(&route_id));
    603 
    604   // Try to set the prerendered page as used, so any subsequent attempts to
    605   // cancel on other threads will fail.  If this fails because the prerender
    606   // was already cancelled, possibly on another thread, fail.
    607   if (!prerender_tracker_->TryUse(child_id, route_id))
    608     return NULL;
    609 
    610   // At this point, we've determined that we will use the prerender.
    611   if (prerender_data->pending_swap())
    612     prerender_data->pending_swap()->set_swap_successful(true);
    613   ScopedVector<PrerenderData>::iterator to_erase =
    614       FindIteratorForPrerenderContents(prerender_data->contents());
    615   DCHECK(active_prerenders_.end() != to_erase);
    616   DCHECK_EQ(prerender_data, *to_erase);
    617   scoped_ptr<PrerenderContents>
    618       prerender_contents(prerender_data->ReleaseContents());
    619   active_prerenders_.erase(to_erase);
    620 
    621   if (!prerender_contents->load_start_time().is_null()) {
    622     histograms_->RecordTimeUntilUsed(
    623         prerender_contents->origin(),
    624         GetCurrentTimeTicks() - prerender_contents->load_start_time());
    625   }
    626 
    627   histograms_->RecordPerSessionCount(prerender_contents->origin(),
    628                                      ++prerenders_per_session_count_);
    629   histograms_->RecordUsedPrerender(prerender_contents->origin());
    630   prerender_contents->SetFinalStatus(FINAL_STATUS_USED);
    631 
    632   // Start pending prerender requests from the PrerenderContents, if there are
    633   // any.
    634   prerender_contents->PrepareForUse();
    635 
    636   WebContents* new_web_contents =
    637       prerender_contents->ReleasePrerenderContents();
    638   WebContents* old_web_contents = web_contents;
    639   DCHECK(new_web_contents);
    640   DCHECK(old_web_contents);
    641 
    642   MarkWebContentsAsPrerendered(new_web_contents, prerender_contents->origin());
    643 
    644   // Merge the browsing history.
    645   new_web_contents->GetController().CopyStateFromAndPrune(
    646       &old_web_contents->GetController(),
    647       should_replace_current_entry);
    648   CoreTabHelper::FromWebContents(old_web_contents)->delegate()->
    649       SwapTabContents(old_web_contents, new_web_contents);
    650   prerender_contents->CommitHistory(new_web_contents);
    651 
    652   GURL icon_url = prerender_contents->icon_url();
    653 
    654   if (!icon_url.is_empty()) {
    655 #if defined(OS_ANDROID)
    656     // Do the delayed icon fetch since we didn't download
    657     // the favicon during prerendering on mobile devices.
    658     FaviconTabHelper * favicon_tap_helper =
    659         FaviconTabHelper::FromWebContents(new_web_contents);
    660     favicon_tap_helper->set_should_fetch_icons(true);
    661     favicon_tap_helper->FetchFavicon(icon_url);
    662 #endif  // defined(OS_ANDROID)
    663 
    664     std::vector<content::FaviconURL> urls;
    665     urls.push_back(content::FaviconURL(icon_url, content::FaviconURL::FAVICON));
    666     FaviconTabHelper::FromWebContents(new_web_contents)->
    667         DidUpdateFaviconURL(prerender_contents->page_id(), urls);
    668   }
    669 
    670   // Update PPLT metrics:
    671   // If the tab has finished loading, record a PPLT of 0.
    672   // If the tab is still loading, reset its start time to the current time.
    673   PrerenderTabHelper* prerender_tab_helper =
    674       PrerenderTabHelper::FromWebContents(new_web_contents);
    675   DCHECK(prerender_tab_helper != NULL);
    676   prerender_tab_helper->PrerenderSwappedIn();
    677 
    678   if (old_web_contents->NeedToFireBeforeUnload()) {
    679     // Schedule the delete to occur after the tab has run its unload handlers.
    680     // TODO(davidben): Honor the beforeunload event. http://crbug.com/304932
    681     on_close_web_contents_deleters_.push_back(
    682         new OnCloseWebContentsDeleter(this, old_web_contents));
    683     old_web_contents->GetRenderViewHost()->
    684         FirePageBeforeUnload(false);
    685   } else {
    686     // No unload handler to run, so delete asap.
    687     ScheduleDeleteOldWebContents(old_web_contents, NULL);
    688   }
    689 
    690   // TODO(cbentzel): Should prerender_contents move to the pending delete
    691   //                 list, instead of deleting directly here?
    692   AddToHistory(prerender_contents.get());
    693   RecordNavigation(url);
    694   return new_web_contents;
    695 }
    696 
    697 void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry,
    698                                                 FinalStatus final_status) {
    699   DCHECK(CalledOnValidThread());
    700   DCHECK(entry);
    701 
    702   ScopedVector<PrerenderData>::iterator it =
    703       FindIteratorForPrerenderContents(entry);
    704   DCHECK(it != active_prerenders_.end());
    705 
    706   // If this PrerenderContents is being deleted due to a cancellation any time
    707   // after the prerender has started then we need to create a dummy replacement
    708   // for PPLT accounting purposes for the Match Complete group. This is the case
    709   // if the cancellation is for any reason that would not occur in the control
    710   // group case.
    711   if (entry->prerendering_has_started() &&
    712       entry->match_complete_status() ==
    713           PrerenderContents::MATCH_COMPLETE_DEFAULT &&
    714       NeedMatchCompleteDummyForFinalStatus(final_status) &&
    715       ActuallyPrerendering()) {
    716     // TODO(tburkard): I'd like to DCHECK that we are actually prerendering.
    717     // However, what if new conditions are added and
    718     // NeedMatchCompleteDummyForFinalStatus is not being updated.  Not sure
    719     // what's the best thing to do here.  For now, I will just check whether
    720     // we are actually prerendering.
    721     (*it)->MakeIntoMatchCompleteReplacement();
    722   } else {
    723     to_delete_prerenders_.push_back(*it);
    724     active_prerenders_.weak_erase(it);
    725   }
    726 
    727   // Destroy the old WebContents relatively promptly to reduce resource usage.
    728   PostCleanupTask();
    729 }
    730 
    731 // static
    732 void PrerenderManager::RecordPerceivedPageLoadTime(
    733     base::TimeDelta perceived_page_load_time,
    734     double fraction_plt_elapsed_at_swap_in,
    735     WebContents* web_contents,
    736     const GURL& url) {
    737   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    738   PrerenderManager* prerender_manager =
    739       PrerenderManagerFactory::GetForProfile(
    740           Profile::FromBrowserContext(web_contents->GetBrowserContext()));
    741   if (!prerender_manager)
    742     return;
    743   if (!prerender_manager->IsEnabled())
    744     return;
    745 
    746   Origin prerender_origin = ORIGIN_NONE;
    747   if (prerender_manager->IsWebContentsPrerendering(web_contents,
    748                                                    &prerender_origin)) {
    749     prerender_manager->histograms_->RecordPageLoadTimeNotSwappedIn(
    750         prerender_origin, perceived_page_load_time, url);
    751     return;
    752   }
    753 
    754   bool was_prerender = prerender_manager->IsWebContentsPrerendered(
    755       web_contents, &prerender_origin);
    756   bool was_complete_prerender = was_prerender ||
    757       prerender_manager->WouldWebContentsBePrerendered(web_contents,
    758                                                        &prerender_origin);
    759   prerender_manager->histograms_->RecordPerceivedPageLoadTime(
    760       prerender_origin, perceived_page_load_time, was_prerender,
    761       was_complete_prerender, url);
    762 
    763   if (was_prerender) {
    764     prerender_manager->histograms_->RecordPercentLoadDoneAtSwapin(
    765         prerender_origin, fraction_plt_elapsed_at_swap_in);
    766   }
    767   if (prerender_manager->local_predictor_.get()) {
    768     prerender_manager->local_predictor_->
    769         OnPLTEventForURL(url, perceived_page_load_time);
    770   }
    771 }
    772 
    773 void PrerenderManager::RecordFractionPixelsFinalAtSwapin(
    774     content::WebContents* web_contents,
    775     double fraction) {
    776   Origin origin = ORIGIN_NONE;
    777   bool is_prerendered = IsWebContentsPrerendered(web_contents, &origin);
    778   DCHECK(is_prerendered);
    779   histograms_->RecordFractionPixelsFinalAtSwapin(origin, fraction);
    780 }
    781 
    782 void PrerenderManager::set_enabled(bool enabled) {
    783   DCHECK(CalledOnValidThread());
    784   enabled_ = enabled;
    785 }
    786 
    787 // static
    788 PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() {
    789   return mode_;
    790 }
    791 
    792 // static
    793 void PrerenderManager::SetMode(PrerenderManagerMode mode) {
    794   mode_ = mode;
    795 }
    796 
    797 // static
    798 const char* PrerenderManager::GetModeString() {
    799   switch (mode_) {
    800     case PRERENDER_MODE_DISABLED:
    801       return "_Disabled";
    802     case PRERENDER_MODE_ENABLED:
    803     case PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP:
    804       return "_Enabled";
    805     case PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP:
    806       return "_Control";
    807     case PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP:
    808       return "_Multi";
    809     case PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP:
    810       return "_15MinTTL";
    811     case PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP:
    812       return "_NoUse";
    813     case PRERENDER_MODE_MAX:
    814     default:
    815       NOTREACHED() << "Invalid PrerenderManager mode.";
    816       break;
    817   };
    818   return "";
    819 }
    820 
    821 // static
    822 bool PrerenderManager::IsPrerenderingPossible() {
    823   return GetMode() != PRERENDER_MODE_DISABLED;
    824 }
    825 
    826 // static
    827 bool PrerenderManager::ActuallyPrerendering() {
    828   return IsPrerenderingPossible() && !IsControlGroup(kNoExperiment);
    829 }
    830 
    831 // static
    832 bool PrerenderManager::IsControlGroup(uint8 experiment_id) {
    833   return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP ||
    834       IsControlGroupExperiment(experiment_id);
    835 }
    836 
    837 // static
    838 bool PrerenderManager::IsNoUseGroup() {
    839   return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP;
    840 }
    841 
    842 bool PrerenderManager::IsWebContentsPrerendering(
    843     const WebContents* web_contents,
    844     Origin* origin) const {
    845   DCHECK(CalledOnValidThread());
    846   if (PrerenderContents* prerender_contents =
    847           GetPrerenderContents(web_contents)) {
    848     if (origin)
    849       *origin = prerender_contents->origin();
    850     return true;
    851   }
    852 
    853   // Also look through the pending-deletion list.
    854   for (ScopedVector<PrerenderData>::const_iterator it =
    855            to_delete_prerenders_.begin();
    856        it != to_delete_prerenders_.end();
    857        ++it) {
    858     if (PrerenderContents* prerender_contents = (*it)->contents()) {
    859       WebContents* prerender_web_contents =
    860           prerender_contents->prerender_contents();
    861       if (prerender_web_contents == web_contents) {
    862         if (origin)
    863           *origin = prerender_contents->origin();
    864         return true;
    865       }
    866     }
    867   }
    868 
    869   return false;
    870 }
    871 
    872 bool PrerenderManager::HasPrerenderedUrl(
    873     GURL url,
    874     content::WebContents* web_contents) const {
    875   content::SessionStorageNamespace* session_storage_namespace = web_contents->
    876       GetController().GetDefaultSessionStorageNamespace();
    877 
    878   for (ScopedVector<PrerenderData>::const_iterator it =
    879            active_prerenders_.begin();
    880        it != active_prerenders_.end(); ++it) {
    881     PrerenderContents* prerender_contents = (*it)->contents();
    882     if (prerender_contents->Matches(url, session_storage_namespace)) {
    883       return true;
    884     }
    885   }
    886   return false;
    887 }
    888 
    889 PrerenderContents* PrerenderManager::GetPrerenderContents(
    890     const content::WebContents* web_contents) const {
    891   DCHECK(CalledOnValidThread());
    892   for (ScopedVector<PrerenderData>::const_iterator it =
    893            active_prerenders_.begin();
    894        it != active_prerenders_.end(); ++it) {
    895     WebContents* prerender_web_contents =
    896         (*it)->contents()->prerender_contents();
    897     if (prerender_web_contents == web_contents) {
    898       return (*it)->contents();
    899     }
    900   }
    901   return NULL;
    902 }
    903 
    904 const std::vector<WebContents*>
    905 PrerenderManager::GetAllPrerenderingContents() const {
    906   DCHECK(CalledOnValidThread());
    907   std::vector<WebContents*> result;
    908 
    909   for (ScopedVector<PrerenderData>::const_iterator it =
    910            active_prerenders_.begin();
    911        it != active_prerenders_.end(); ++it) {
    912     if (WebContents* contents = (*it)->contents()->prerender_contents())
    913       result.push_back(contents);
    914   }
    915 
    916   return result;
    917 }
    918 
    919 void PrerenderManager::MarkWebContentsAsPrerendered(WebContents* web_contents,
    920                                                     Origin origin) {
    921   DCHECK(CalledOnValidThread());
    922   prerendered_web_contents_data_.insert(
    923       base::hash_map<content::WebContents*,
    924                      PrerenderedWebContentsData>::value_type(
    925                          web_contents, PrerenderedWebContentsData(origin)));
    926 }
    927 
    928 void PrerenderManager::MarkWebContentsAsWouldBePrerendered(
    929     WebContents* web_contents,
    930     Origin origin) {
    931   DCHECK(CalledOnValidThread());
    932   would_be_prerendered_map_.insert(
    933       base::hash_map<content::WebContents*,
    934                      WouldBePrerenderedWebContentsData>::value_type(
    935                          web_contents,
    936                          WouldBePrerenderedWebContentsData(origin)));
    937 }
    938 
    939 void PrerenderManager::MarkWebContentsAsNotPrerendered(
    940     WebContents* web_contents) {
    941   DCHECK(CalledOnValidThread());
    942   prerendered_web_contents_data_.erase(web_contents);
    943   base::hash_map<content::WebContents*, WouldBePrerenderedWebContentsData>::
    944       iterator it = would_be_prerendered_map_.find(web_contents);
    945   if (it != would_be_prerendered_map_.end()) {
    946     if (it->second.state ==
    947             WouldBePrerenderedWebContentsData::WAITING_FOR_PROVISIONAL_LOAD) {
    948       it->second.state =
    949           WouldBePrerenderedWebContentsData::SEEN_PROVISIONAL_LOAD;
    950     } else {
    951       would_be_prerendered_map_.erase(it);
    952     }
    953   }
    954 }
    955 
    956 bool PrerenderManager::IsWebContentsPrerendered(
    957     content::WebContents* web_contents,
    958     Origin* origin) const {
    959   DCHECK(CalledOnValidThread());
    960   base::hash_map<content::WebContents*, PrerenderedWebContentsData>::
    961       const_iterator it = prerendered_web_contents_data_.find(web_contents);
    962   if (it == prerendered_web_contents_data_.end())
    963     return false;
    964   if (origin)
    965     *origin = it->second.origin;
    966   return true;
    967 }
    968 
    969 bool PrerenderManager::WouldWebContentsBePrerendered(
    970     WebContents* web_contents,
    971     Origin* origin) const {
    972   DCHECK(CalledOnValidThread());
    973   base::hash_map<content::WebContents*, WouldBePrerenderedWebContentsData>::
    974       const_iterator it = would_be_prerendered_map_.find(web_contents);
    975   if (it == would_be_prerendered_map_.end())
    976     return false;
    977   if (origin)
    978     *origin = it->second.origin;
    979   return true;
    980 }
    981 
    982 bool PrerenderManager::HasRecentlyBeenNavigatedTo(Origin origin,
    983                                                   const GURL& url) {
    984   DCHECK(CalledOnValidThread());
    985 
    986   CleanUpOldNavigations();
    987   std::list<NavigationRecord>::const_reverse_iterator end = navigations_.rend();
    988   for (std::list<NavigationRecord>::const_reverse_iterator it =
    989            navigations_.rbegin();
    990        it != end;
    991        ++it) {
    992     if (it->url == url) {
    993       base::TimeDelta delta = GetCurrentTimeTicks() - it->time;
    994       histograms_->RecordTimeSinceLastRecentVisit(origin, delta);
    995       return true;
    996     }
    997   }
    998 
    999   return false;
   1000 }
   1001 
   1002 // static
   1003 bool PrerenderManager::IsValidHttpMethod(const std::string& method) {
   1004   // method has been canonicalized to upper case at this point so we can just
   1005   // compare them.
   1006   DCHECK_EQ(method, StringToUpperASCII(method));
   1007   for (size_t i = 0; i < arraysize(kValidHttpMethods); ++i) {
   1008     if (method.compare(kValidHttpMethods[i]) == 0)
   1009       return true;
   1010   }
   1011 
   1012   return false;
   1013 }
   1014 
   1015 // static
   1016 bool PrerenderManager::DoesURLHaveValidScheme(const GURL& url) {
   1017   return (url.SchemeIsHTTPOrHTTPS() ||
   1018           url.SchemeIs(extensions::kExtensionScheme) ||
   1019           url.SchemeIs("data"));
   1020 }
   1021 
   1022 // static
   1023 bool PrerenderManager::DoesSubresourceURLHaveValidScheme(const GURL& url) {
   1024   return DoesURLHaveValidScheme(url) || url == GURL(content::kAboutBlankURL);
   1025 }
   1026 
   1027 DictionaryValue* PrerenderManager::GetAsValue() const {
   1028   DCHECK(CalledOnValidThread());
   1029   DictionaryValue* dict_value = new DictionaryValue();
   1030   dict_value->Set("history", prerender_history_->GetEntriesAsValue());
   1031   dict_value->Set("active", GetActivePrerendersAsValue());
   1032   dict_value->SetBoolean("enabled", enabled_);
   1033   dict_value->SetBoolean("omnibox_enabled", IsOmniboxEnabled(profile_));
   1034   // If prerender is disabled via a flag this method is not even called.
   1035   std::string enabled_note;
   1036   if (IsControlGroup(kNoExperiment))
   1037     enabled_note += "(Control group: Not actually prerendering) ";
   1038   if (IsNoUseGroup())
   1039     enabled_note += "(No-use group: Not swapping in prerendered pages) ";
   1040   if (GetMode() == PRERENDER_MODE_EXPERIMENT_15MIN_TTL_GROUP)
   1041     enabled_note +=
   1042         "(15 min TTL group: Extended prerender eviction to 15 mins) ";
   1043   dict_value->SetString("enabled_note", enabled_note);
   1044   return dict_value;
   1045 }
   1046 
   1047 void PrerenderManager::ClearData(int clear_flags) {
   1048   DCHECK_GE(clear_flags, 0);
   1049   DCHECK_LT(clear_flags, CLEAR_MAX);
   1050   if (clear_flags & CLEAR_PRERENDER_CONTENTS)
   1051     DestroyAllContents(FINAL_STATUS_CACHE_OR_HISTORY_CLEARED);
   1052   // This has to be second, since destroying prerenders can add to the history.
   1053   if (clear_flags & CLEAR_PRERENDER_HISTORY)
   1054     prerender_history_->Clear();
   1055 }
   1056 
   1057 void PrerenderManager::RecordFinalStatusWithMatchCompleteStatus(
   1058     Origin origin,
   1059     uint8 experiment_id,
   1060     PrerenderContents::MatchCompleteStatus mc_status,
   1061     FinalStatus final_status) const {
   1062   histograms_->RecordFinalStatus(origin,
   1063                                  experiment_id,
   1064                                  mc_status,
   1065                                  final_status);
   1066 }
   1067 
   1068 void PrerenderManager::AddCondition(const PrerenderCondition* condition) {
   1069   prerender_conditions_.push_back(condition);
   1070 }
   1071 
   1072 void PrerenderManager::RecordNavigation(const GURL& url) {
   1073   DCHECK(CalledOnValidThread());
   1074 
   1075   navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks()));
   1076   CleanUpOldNavigations();
   1077 }
   1078 
   1079 // protected
   1080 struct PrerenderManager::PrerenderData::OrderByExpiryTime {
   1081   bool operator()(const PrerenderData* a, const PrerenderData* b) const {
   1082     return a->expiry_time() < b->expiry_time();
   1083   }
   1084 };
   1085 
   1086 PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager,
   1087                                                PrerenderContents* contents,
   1088                                                base::TimeTicks expiry_time)
   1089     : manager_(manager),
   1090       contents_(contents),
   1091       handle_count_(0),
   1092       expiry_time_(expiry_time) {
   1093   DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_);
   1094 }
   1095 
   1096 PrerenderManager::PrerenderData::~PrerenderData() {
   1097 }
   1098 
   1099 void PrerenderManager::PrerenderData::MakeIntoMatchCompleteReplacement() {
   1100   DCHECK(contents_);
   1101   contents_->set_match_complete_status(
   1102       PrerenderContents::MATCH_COMPLETE_REPLACED);
   1103   PrerenderData* to_delete = new PrerenderData(manager_, contents_.release(),
   1104                                                expiry_time_);
   1105   contents_.reset(to_delete->contents_->CreateMatchCompleteReplacement());
   1106   manager_->to_delete_prerenders_.push_back(to_delete);
   1107 }
   1108 
   1109 void PrerenderManager::PrerenderData::OnHandleCreated(PrerenderHandle* handle) {
   1110   DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_);
   1111   ++handle_count_;
   1112   contents_->AddObserver(handle);
   1113 }
   1114 
   1115 void PrerenderManager::PrerenderData::OnHandleNavigatedAway(
   1116     PrerenderHandle* handle) {
   1117   DCHECK_LT(0, handle_count_);
   1118   DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_);
   1119   // We intentionally don't decrement the handle count here, so that the
   1120   // prerender won't be canceled until it times out.
   1121   manager_->SourceNavigatedAway(this);
   1122 }
   1123 
   1124 void PrerenderManager::PrerenderData::OnHandleCanceled(
   1125     PrerenderHandle* handle) {
   1126   DCHECK_LT(0, handle_count_);
   1127   DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_);
   1128 
   1129   if (--handle_count_ == 0) {
   1130     // This will eventually remove this object from active_prerenders_.
   1131     contents_->Destroy(FINAL_STATUS_CANCELLED);
   1132   }
   1133 }
   1134 
   1135 void PrerenderManager::PrerenderData::ClearPendingSwap() {
   1136   pending_swap_.reset(NULL);
   1137 }
   1138 
   1139 PrerenderContents* PrerenderManager::PrerenderData::ReleaseContents() {
   1140   return contents_.release();
   1141 }
   1142 
   1143 PrerenderManager::PendingSwap::PendingSwap(
   1144     PrerenderManager* manager,
   1145     content::WebContents* target_contents,
   1146     PrerenderData* prerender_data,
   1147     const GURL& url,
   1148     bool should_replace_current_entry)
   1149     : content::WebContentsObserver(target_contents),
   1150       manager_(manager),
   1151       target_contents_(target_contents),
   1152       prerender_data_(prerender_data),
   1153       url_(url),
   1154       should_replace_current_entry_(should_replace_current_entry),
   1155       start_time_(base::TimeTicks::Now()),
   1156       swap_successful_(false),
   1157       weak_factory_(this) {
   1158   RenderViewCreated(target_contents->GetRenderViewHost());
   1159 }
   1160 
   1161 PrerenderManager::PendingSwap::~PendingSwap() {
   1162   for (size_t i = 0; i < rvh_ids_.size(); i++) {
   1163     manager_->prerender_tracker()->RemovePrerenderPendingSwap(
   1164         rvh_ids_[i], swap_successful_);
   1165   }
   1166 }
   1167 
   1168 void PrerenderManager::PendingSwap::BeginSwap() {
   1169   SessionStorageNamespace* target_namespace =
   1170       target_contents_->GetController().GetDefaultSessionStorageNamespace();
   1171   SessionStorageNamespace* prerender_namespace =
   1172       prerender_data_->contents()->GetSessionStorageNamespace();
   1173 
   1174   prerender_namespace->Merge(
   1175       true, prerender_data_->contents()->child_id(),
   1176       target_namespace,
   1177       base::Bind(&PrerenderManager::PendingSwap::OnMergeCompleted,
   1178                  weak_factory_.GetWeakPtr()));
   1179 
   1180   merge_timeout_.Start(
   1181       FROM_HERE,
   1182       base::TimeDelta::FromMilliseconds(
   1183           kSessionStorageNamespaceMergeTimeoutMs),
   1184       this, &PrerenderManager::PendingSwap::OnMergeTimeout);
   1185 }
   1186 
   1187 void PrerenderManager::PendingSwap::ProvisionalChangeToMainFrameUrl(
   1188         const GURL& url,
   1189         content::RenderViewHost* render_view_host) {
   1190   // We must only cancel the pending swap if the |url| navigated to is not
   1191   // the URL being attempted to be swapped in. That's because in the normal
   1192   // flow, a ProvisionalChangeToMainFrameUrl will happen for the URL attempted
   1193   // to be swapped in immediately after the pending swap has issued its merge.
   1194   if (url != url_)
   1195     prerender_data_->ClearPendingSwap();
   1196 }
   1197 
   1198 void PrerenderManager::PendingSwap::DidCommitProvisionalLoadForFrame(
   1199         int64 frame_id,
   1200         const base::string16& frame_unique_name,
   1201         bool is_main_frame,
   1202         const GURL& validated_url,
   1203         content::PageTransition transition_type,
   1204         content::RenderViewHost* render_view_host){
   1205   if (!is_main_frame)
   1206     return;
   1207   prerender_data_->ClearPendingSwap();
   1208 }
   1209 
   1210 void PrerenderManager::PendingSwap::RenderViewCreated(
   1211     content::RenderViewHost* render_view_host) {
   1212   // Record the RVH id in the tracker to install throttles on MAIN_FRAME
   1213   // requests from that route.
   1214   int child_id = render_view_host->GetProcess()->GetID();
   1215   int route_id = render_view_host->GetRoutingID();
   1216   PrerenderTracker::ChildRouteIdPair child_route_id_pair(child_id, route_id);
   1217   rvh_ids_.push_back(child_route_id_pair);
   1218   manager_->prerender_tracker()->AddPrerenderPendingSwap(
   1219       child_route_id_pair, url_);
   1220 }
   1221 
   1222 void PrerenderManager::PendingSwap::DidFailProvisionalLoad(
   1223         int64 frame_id,
   1224         const base::string16& frame_unique_name,
   1225         bool is_main_frame,
   1226         const GURL& validated_url,
   1227         int error_code,
   1228         const base::string16& error_description,
   1229         content::RenderViewHost* render_view_host) {
   1230   if (!is_main_frame)
   1231     return;
   1232   prerender_data_->ClearPendingSwap();
   1233 }
   1234 
   1235 void PrerenderManager::PendingSwap::WebContentsDestroyed(
   1236     content::WebContents* web_contents) {
   1237   prerender_data_->ClearPendingSwap();
   1238 }
   1239 
   1240 void PrerenderManager::PendingSwap::RecordEvent(PrerenderEvent event) const {
   1241   manager_->RecordEvent(prerender_data_->contents(), event);
   1242 }
   1243 
   1244 void PrerenderManager::PendingSwap::OnMergeCompleted(
   1245     SessionStorageNamespace::MergeResult result) {
   1246   UMA_HISTOGRAM_TIMES("Prerender.SessionStorageNamespaceMergeTime",
   1247                       base::TimeTicks::Now() - start_time_);
   1248   RecordEvent(PRERENDER_EVENT_MERGE_RESULT_MERGE_DONE);
   1249 
   1250   // Log the exact merge result in a histogram.
   1251   switch (result) {
   1252     case SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_FOUND:
   1253       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NAMESPACE_NOT_FOUND);
   1254       break;
   1255     case SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_ALIAS:
   1256       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NAMESPACE_NOT_ALIAS);
   1257       break;
   1258     case SessionStorageNamespace::MERGE_RESULT_NOT_LOGGING:
   1259       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NOT_LOGGING);
   1260       break;
   1261     case SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS:
   1262       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NO_TRANSACTIONS);
   1263       break;
   1264     case SessionStorageNamespace::MERGE_RESULT_TOO_MANY_TRANSACTIONS:
   1265       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_TOO_MANY_TRANSACTIONS);
   1266       break;
   1267     case SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE:
   1268       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_NOT_MERGEABLE);
   1269       break;
   1270     case SessionStorageNamespace::MERGE_RESULT_MERGEABLE:
   1271       RecordEvent(PRERENDER_EVENT_MERGE_RESULT_RESULT_MERGEABLE);
   1272       break;
   1273     default:
   1274       NOTREACHED();
   1275   }
   1276 
   1277   if (result != SessionStorageNamespace::MERGE_RESULT_MERGEABLE &&
   1278       result != SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS) {
   1279     RecordEvent(PRERENDER_EVENT_MERGE_RESULT_MERGE_FAILED);
   1280     prerender_data_->ClearPendingSwap();
   1281     return;
   1282   }
   1283 
   1284   RecordEvent(PRERENDER_EVENT_MERGE_RESULT_SWAPPING_IN);
   1285   // Note that SwapInternal, on success, will delete |prerender_data_| and
   1286   // |this|. Pass in a new GURL object rather than a reference to |url_|.
   1287   //
   1288   // TODO(davidben): See about deleting PrerenderData asynchronously so this
   1289   // behavior is more reasonable.
   1290   WebContents* new_web_contents =
   1291       manager_->SwapInternal(GURL(url_), target_contents_, prerender_data_,
   1292                              should_replace_current_entry_);
   1293   if (!new_web_contents) {
   1294     RecordEvent(PRERENDER_EVENT_MERGE_RESULT_SWAPIN_FAILED);
   1295     prerender_data_->ClearPendingSwap();
   1296   }
   1297 }
   1298 
   1299 void PrerenderManager::PendingSwap::OnMergeTimeout() {
   1300   RecordEvent(PRERENDER_EVENT_MERGE_RESULT_TIMED_OUT);
   1301   prerender_data_->ClearPendingSwap();
   1302 }
   1303 
   1304 void PrerenderManager::SetPrerenderContentsFactory(
   1305     PrerenderContents::Factory* prerender_contents_factory) {
   1306   DCHECK(CalledOnValidThread());
   1307   prerender_contents_factory_.reset(prerender_contents_factory);
   1308 }
   1309 
   1310 
   1311 void PrerenderManager::StartPendingPrerenders(
   1312     const int process_id,
   1313     ScopedVector<PrerenderContents::PendingPrerenderInfo>* pending_prerenders,
   1314     content::SessionStorageNamespace* session_storage_namespace) {
   1315   for (ScopedVector<PrerenderContents::PendingPrerenderInfo>::iterator
   1316            it = pending_prerenders->begin();
   1317        it != pending_prerenders->end(); ++it) {
   1318     PrerenderContents::PendingPrerenderInfo* info = *it;
   1319     PrerenderHandle* existing_prerender_handle =
   1320         info->weak_prerender_handle.get();
   1321     if (!existing_prerender_handle)
   1322       continue;
   1323 
   1324     DCHECK(!existing_prerender_handle->IsPrerendering());
   1325     DCHECK(process_id == -1 || session_storage_namespace);
   1326 
   1327     scoped_ptr<PrerenderHandle> new_prerender_handle(AddPrerender(
   1328         info->origin, process_id,
   1329         info->url, info->referrer, info->size,
   1330         session_storage_namespace));
   1331     if (new_prerender_handle) {
   1332       // AddPrerender has returned a new prerender handle to us. We want to make
   1333       // |existing_prerender_handle| active, so move the underlying
   1334       // PrerenderData to our new handle.
   1335       existing_prerender_handle->AdoptPrerenderDataFrom(
   1336           new_prerender_handle.get());
   1337       continue;
   1338     }
   1339   }
   1340 }
   1341 
   1342 void PrerenderManager::SourceNavigatedAway(PrerenderData* prerender_data) {
   1343   // The expiry time of our prerender data will likely change because of
   1344   // this navigation. This requires a resort of active_prerenders_.
   1345   ScopedVector<PrerenderData>::iterator it =
   1346       std::find(active_prerenders_.begin(), active_prerenders_.end(),
   1347                 prerender_data);
   1348   if (it == active_prerenders_.end())
   1349     return;
   1350 
   1351   (*it)->set_expiry_time(
   1352       std::min((*it)->expiry_time(),
   1353                GetExpiryTimeForNavigatedAwayPrerender()));
   1354   SortActivePrerenders();
   1355 }
   1356 
   1357 // private
   1358 PrerenderHandle* PrerenderManager::AddPrerender(
   1359     Origin origin,
   1360     int process_id,
   1361     const GURL& url_arg,
   1362     const content::Referrer& referrer,
   1363     const gfx::Size& size,
   1364     SessionStorageNamespace* session_storage_namespace) {
   1365   DCHECK(CalledOnValidThread());
   1366 
   1367   if (!IsEnabled())
   1368     return NULL;
   1369 
   1370   if ((origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN ||
   1371        origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) &&
   1372       IsGoogleSearchResultURL(referrer.url)) {
   1373     origin = ORIGIN_GWS_PRERENDER;
   1374   }
   1375 
   1376   GURL url = url_arg;
   1377   GURL alias_url;
   1378   uint8 experiment = GetQueryStringBasedExperiment(url_arg);
   1379   if (IsControlGroup(experiment) &&
   1380       MaybeGetQueryStringBasedAliasURL(url, &alias_url)) {
   1381     url = alias_url;
   1382   }
   1383 
   1384   // From here on, we will record a FinalStatus so we need to register with the
   1385   // histogram tracking.
   1386   histograms_->RecordPrerender(origin, url_arg);
   1387 
   1388   if (PrerenderData* preexisting_prerender_data =
   1389           FindPrerenderData(url, session_storage_namespace)) {
   1390     RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE);
   1391     return new PrerenderHandle(preexisting_prerender_data);
   1392   }
   1393 
   1394   // Do not prerender if there are too many render processes, and we would
   1395   // have to use an existing one.  We do not want prerendering to happen in
   1396   // a shared process, so that we can always reliably lower the CPU
   1397   // priority for prerendering.
   1398   // In single-process mode, ShouldTryToUseExistingProcessHost() always returns
   1399   // true, so that case needs to be explicitly checked for.
   1400   // TODO(tburkard): Figure out how to cancel prerendering in the opposite
   1401   // case, when a new tab is added to a process used for prerendering.
   1402   // On Android we do reuse processes as we have a limited number of them and we
   1403   // still want the benefits of prerendering even when several tabs are open.
   1404 #if !defined(OS_ANDROID)
   1405   if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost(
   1406           profile_, url) &&
   1407       !content::RenderProcessHost::run_renderer_in_process()) {
   1408     RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES);
   1409     return NULL;
   1410   }
   1411 #endif
   1412 
   1413   // Check if enough time has passed since the last prerender.
   1414   if (!DoesRateLimitAllowPrerender(origin)) {
   1415     // Cancel the prerender. We could add it to the pending prerender list but
   1416     // this doesn't make sense as the next prerender request will be triggered
   1417     // by a navigation and is unlikely to be the same site.
   1418     RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED);
   1419     return NULL;
   1420   }
   1421 
   1422   PrerenderContents* prerender_contents = CreatePrerenderContents(
   1423       url, referrer, origin, experiment);
   1424   DCHECK(prerender_contents);
   1425   active_prerenders_.push_back(
   1426       new PrerenderData(this, prerender_contents,
   1427                         GetExpiryTimeForNewPrerender(origin)));
   1428   if (!prerender_contents->Init()) {
   1429     DCHECK(active_prerenders_.end() ==
   1430            FindIteratorForPrerenderContents(prerender_contents));
   1431     return NULL;
   1432   }
   1433 
   1434   histograms_->RecordPrerenderStarted(origin);
   1435   DCHECK(!prerender_contents->prerendering_has_started());
   1436 
   1437   PrerenderHandle* prerender_handle =
   1438       new PrerenderHandle(active_prerenders_.back());
   1439   SortActivePrerenders();
   1440 
   1441   last_prerender_start_time_ = GetCurrentTimeTicks();
   1442 
   1443   gfx::Size contents_size =
   1444       size.IsEmpty() ? config_.default_tab_bounds.size() : size;
   1445 
   1446   prerender_contents->StartPrerendering(process_id, contents_size,
   1447                                         session_storage_namespace);
   1448 
   1449   DCHECK(IsControlGroup(experiment) ||
   1450          prerender_contents->prerendering_has_started());
   1451 
   1452   if (GetMode() == PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP)
   1453     histograms_->RecordConcurrency(active_prerenders_.size());
   1454 
   1455   // Query the history to see if the URL being prerendered has ever been
   1456   // visited before.
   1457   HistoryService* history_service = HistoryServiceFactory::GetForProfile(
   1458       profile_, Profile::EXPLICIT_ACCESS);
   1459   if (history_service) {
   1460     history_service->QueryURL(
   1461         url,
   1462         false,
   1463         &query_url_consumer_,
   1464         base::Bind(&PrerenderManager::OnHistoryServiceDidQueryURL,
   1465                    base::Unretained(this),
   1466                    origin,
   1467                    experiment));
   1468   }
   1469 
   1470   StartSchedulingPeriodicCleanups();
   1471   return prerender_handle;
   1472 }
   1473 
   1474 void PrerenderManager::StartSchedulingPeriodicCleanups() {
   1475   DCHECK(CalledOnValidThread());
   1476   if (repeating_timer_.IsRunning())
   1477     return;
   1478   repeating_timer_.Start(FROM_HERE,
   1479       base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs),
   1480       this,
   1481       &PrerenderManager::PeriodicCleanup);
   1482 }
   1483 
   1484 void PrerenderManager::StopSchedulingPeriodicCleanups() {
   1485   DCHECK(CalledOnValidThread());
   1486   repeating_timer_.Stop();
   1487 }
   1488 
   1489 void PrerenderManager::PeriodicCleanup() {
   1490   DCHECK(CalledOnValidThread());
   1491 
   1492   base::ElapsedTimer resource_timer;
   1493 
   1494   // Grab a copy of the current PrerenderContents pointers, so that we
   1495   // will not interfere with potential deletions of the list.
   1496   std::vector<PrerenderContents*>
   1497       prerender_contents(active_prerenders_.size());
   1498   std::transform(active_prerenders_.begin(), active_prerenders_.end(),
   1499                  prerender_contents.begin(),
   1500                  std::mem_fun(&PrerenderData::contents));
   1501 
   1502   // And now check for prerenders using too much memory.
   1503   std::for_each(prerender_contents.begin(), prerender_contents.end(),
   1504                 std::mem_fun(
   1505                     &PrerenderContents::DestroyWhenUsingTooManyResources));
   1506 
   1507   // Measure how long the resource checks took. http://crbug.com/305419.
   1508   UMA_HISTOGRAM_TIMES("Prerender.PeriodicCleanupResourceCheckTime",
   1509                       resource_timer.Elapsed());
   1510 
   1511   base::ElapsedTimer cleanup_timer;
   1512 
   1513   // Perform deferred cleanup work.
   1514   DeleteOldWebContents();
   1515   DeleteOldEntries();
   1516   if (active_prerenders_.empty())
   1517     StopSchedulingPeriodicCleanups();
   1518 
   1519   to_delete_prerenders_.clear();
   1520 
   1521   // Measure how long a the various cleanup tasks took. http://crbug.com/305419.
   1522   UMA_HISTOGRAM_TIMES("Prerender.PeriodicCleanupDeleteContentsTime",
   1523                       cleanup_timer.Elapsed());
   1524 }
   1525 
   1526 void PrerenderManager::PostCleanupTask() {
   1527   DCHECK(CalledOnValidThread());
   1528   base::MessageLoop::current()->PostTask(
   1529       FROM_HERE,
   1530       base::Bind(&PrerenderManager::PeriodicCleanup, AsWeakPtr()));
   1531 }
   1532 
   1533 base::TimeTicks PrerenderManager::GetExpiryTimeForNewPrerender(
   1534     Origin origin) const {
   1535   base::TimeDelta ttl = config_.time_to_live;
   1536   if (origin == ORIGIN_LOCAL_PREDICTOR)
   1537     ttl = base::TimeDelta::FromSeconds(GetLocalPredictorTTLSeconds());
   1538   return GetCurrentTimeTicks() + ttl;
   1539 }
   1540 
   1541 base::TimeTicks PrerenderManager::GetExpiryTimeForNavigatedAwayPrerender()
   1542     const {
   1543   return GetCurrentTimeTicks() + config_.abandon_time_to_live;
   1544 }
   1545 
   1546 void PrerenderManager::DeleteOldEntries() {
   1547   DCHECK(CalledOnValidThread());
   1548   while (!active_prerenders_.empty()) {
   1549     PrerenderData* prerender_data = active_prerenders_.front();
   1550     DCHECK(prerender_data);
   1551     DCHECK(prerender_data->contents());
   1552 
   1553     if (prerender_data->expiry_time() > GetCurrentTimeTicks())
   1554       return;
   1555     prerender_data->contents()->Destroy(FINAL_STATUS_TIMED_OUT);
   1556   }
   1557 }
   1558 
   1559 base::Time PrerenderManager::GetCurrentTime() const {
   1560   return base::Time::Now();
   1561 }
   1562 
   1563 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const {
   1564   return base::TimeTicks::Now();
   1565 }
   1566 
   1567 PrerenderContents* PrerenderManager::CreatePrerenderContents(
   1568     const GURL& url,
   1569     const content::Referrer& referrer,
   1570     Origin origin,
   1571     uint8 experiment_id) {
   1572   DCHECK(CalledOnValidThread());
   1573   return prerender_contents_factory_->CreatePrerenderContents(
   1574       this, profile_, url, referrer, origin, experiment_id);
   1575 }
   1576 
   1577 void PrerenderManager::SortActivePrerenders() {
   1578   std::sort(active_prerenders_.begin(), active_prerenders_.end(),
   1579             PrerenderData::OrderByExpiryTime());
   1580 }
   1581 
   1582 PrerenderManager::PrerenderData* PrerenderManager::FindPrerenderData(
   1583     const GURL& url,
   1584     const SessionStorageNamespace* session_storage_namespace) {
   1585   for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin();
   1586        it != active_prerenders_.end(); ++it) {
   1587     if ((*it)->contents()->Matches(url, session_storage_namespace))
   1588       return *it;
   1589   }
   1590   return NULL;
   1591 }
   1592 
   1593 PrerenderManager::PrerenderData*
   1594 PrerenderManager::FindPrerenderDataForChildAndRoute(
   1595     const int child_id, const int route_id) {
   1596   for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin();
   1597        it != active_prerenders_.end(); ++it) {
   1598     PrerenderContents* prerender_contents = (*it)->contents();
   1599 
   1600     int contents_child_id;
   1601     if (!prerender_contents->GetChildId(&contents_child_id))
   1602       continue;
   1603     int contents_route_id;
   1604     if (!prerender_contents->GetRouteId(&contents_route_id))
   1605       continue;
   1606 
   1607     if (contents_child_id == child_id && contents_route_id == route_id)
   1608       return *it;
   1609   }
   1610   return NULL;
   1611 }
   1612 
   1613 ScopedVector<PrerenderManager::PrerenderData>::iterator
   1614 PrerenderManager::FindIteratorForPrerenderContents(
   1615     PrerenderContents* prerender_contents) {
   1616   for (ScopedVector<PrerenderData>::iterator it = active_prerenders_.begin();
   1617        it != active_prerenders_.end(); ++it) {
   1618     if (prerender_contents == (*it)->contents())
   1619       return it;
   1620   }
   1621   return active_prerenders_.end();
   1622 }
   1623 
   1624 bool PrerenderManager::DoesRateLimitAllowPrerender(Origin origin) const {
   1625   DCHECK(CalledOnValidThread());
   1626   base::TimeDelta elapsed_time =
   1627       GetCurrentTimeTicks() - last_prerender_start_time_;
   1628   histograms_->RecordTimeBetweenPrerenderRequests(origin, elapsed_time);
   1629   if (!config_.rate_limit_enabled)
   1630     return true;
   1631   return elapsed_time >=
   1632       base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs);
   1633 }
   1634 
   1635 void PrerenderManager::DeleteOldWebContents() {
   1636   while (!old_web_contents_list_.empty()) {
   1637     WebContents* web_contents = old_web_contents_list_.front();
   1638     old_web_contents_list_.pop_front();
   1639     // TODO(dominich): should we use Instant Unload Handler here?
   1640     delete web_contents;
   1641   }
   1642 }
   1643 
   1644 void PrerenderManager::CleanUpOldNavigations() {
   1645   DCHECK(CalledOnValidThread());
   1646 
   1647   // Cutoff.  Navigations before this cutoff can be discarded.
   1648   base::TimeTicks cutoff = GetCurrentTimeTicks() -
   1649       base::TimeDelta::FromMilliseconds(kNavigationRecordWindowMs);
   1650   while (!navigations_.empty()) {
   1651     if (navigations_.front().time > cutoff)
   1652       break;
   1653     navigations_.pop_front();
   1654   }
   1655 }
   1656 
   1657 void PrerenderManager::ScheduleDeleteOldWebContents(
   1658     WebContents* tab,
   1659     OnCloseWebContentsDeleter* deleter) {
   1660   old_web_contents_list_.push_back(tab);
   1661   PostCleanupTask();
   1662 
   1663   if (deleter) {
   1664     ScopedVector<OnCloseWebContentsDeleter>::iterator i = std::find(
   1665         on_close_web_contents_deleters_.begin(),
   1666         on_close_web_contents_deleters_.end(),
   1667         deleter);
   1668     DCHECK(i != on_close_web_contents_deleters_.end());
   1669     on_close_web_contents_deleters_.erase(i);
   1670   }
   1671 }
   1672 
   1673 void PrerenderManager::AddToHistory(PrerenderContents* contents) {
   1674   PrerenderHistory::Entry entry(contents->prerender_url(),
   1675                                 contents->final_status(),
   1676                                 contents->origin(),
   1677                                 base::Time::Now());
   1678   prerender_history_->AddEntry(entry);
   1679 }
   1680 
   1681 Value* PrerenderManager::GetActivePrerendersAsValue() const {
   1682   ListValue* list_value = new ListValue();
   1683   for (ScopedVector<PrerenderData>::const_iterator it =
   1684            active_prerenders_.begin();
   1685        it != active_prerenders_.end(); ++it) {
   1686     if (Value* prerender_value = (*it)->contents()->GetAsValue())
   1687       list_value->Append(prerender_value);
   1688   }
   1689   return list_value;
   1690 }
   1691 
   1692 void PrerenderManager::DestroyAllContents(FinalStatus final_status) {
   1693   DeleteOldWebContents();
   1694   while (!active_prerenders_.empty()) {
   1695     PrerenderContents* contents = active_prerenders_.front()->contents();
   1696     contents->Destroy(final_status);
   1697   }
   1698   to_delete_prerenders_.clear();
   1699 }
   1700 
   1701 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed(
   1702     PrerenderContents* prerender_contents,
   1703     FinalStatus final_status) {
   1704   prerender_contents->set_match_complete_status(
   1705       PrerenderContents::MATCH_COMPLETE_REPLACED);
   1706   histograms_->RecordFinalStatus(prerender_contents->origin(),
   1707                                  prerender_contents->experiment_id(),
   1708                                  PrerenderContents::MATCH_COMPLETE_REPLACEMENT,
   1709                                  FINAL_STATUS_WOULD_HAVE_BEEN_USED);
   1710   prerender_contents->Destroy(final_status);
   1711 }
   1712 
   1713 void PrerenderManager::RecordFinalStatus(Origin origin,
   1714                                          uint8 experiment_id,
   1715                                          FinalStatus final_status) const {
   1716   RecordFinalStatusWithMatchCompleteStatus(
   1717       origin, experiment_id,
   1718       PrerenderContents::MATCH_COMPLETE_DEFAULT,
   1719       final_status);
   1720 }
   1721 
   1722 bool PrerenderManager::IsEnabled() const {
   1723   DCHECK(CalledOnValidThread());
   1724   if (!enabled_)
   1725     return false;
   1726   for (std::list<const PrerenderCondition*>::const_iterator it =
   1727            prerender_conditions_.begin();
   1728        it != prerender_conditions_.end();
   1729        ++it) {
   1730     const PrerenderCondition* condition = *it;
   1731     if (!condition->CanPrerender())
   1732       return false;
   1733   }
   1734   return true;
   1735 }
   1736 
   1737 PrerenderManager* FindPrerenderManagerUsingRenderProcessId(
   1738     int render_process_id) {
   1739   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1740   content::RenderProcessHost* render_process_host =
   1741       content::RenderProcessHost::FromID(render_process_id);
   1742   // Each render process is guaranteed to only hold RenderViews owned by the
   1743   // same BrowserContext. This is enforced by
   1744   // RenderProcessHost::GetExistingProcessHost.
   1745   if (!render_process_host || !render_process_host->GetBrowserContext())
   1746     return NULL;
   1747   Profile* profile = Profile::FromBrowserContext(
   1748       render_process_host->GetBrowserContext());
   1749   if (!profile)
   1750     return NULL;
   1751   return PrerenderManagerFactory::GetInstance()->GetForProfile(profile);
   1752 }
   1753 
   1754 void PrerenderManager::Observe(int type,
   1755                                const content::NotificationSource& source,
   1756                                const content::NotificationDetails& details) {
   1757   Profile* profile = content::Source<Profile>(source).ptr();
   1758   if (!profile || !profile_->IsSameProfile(profile) ||
   1759       profile->IsOffTheRecord()) {
   1760     return;
   1761   }
   1762   DCHECK(type == chrome::NOTIFICATION_COOKIE_CHANGED);
   1763   CookieChanged(content::Details<ChromeCookieDetails>(details).ptr());
   1764 }
   1765 
   1766 void PrerenderManager::OnCreatingAudioStream(int render_process_id,
   1767                                              int render_view_id) {
   1768   WebContents* tab = tab_util::GetWebContentsByID(
   1769       render_process_id, render_view_id);
   1770   if (!tab)
   1771     return;
   1772 
   1773   if (!IsWebContentsPrerendering(tab, NULL))
   1774     return;
   1775 
   1776   prerender_tracker()->TryCancel(
   1777       render_process_id, render_view_id,
   1778       prerender::FINAL_STATUS_CREATING_AUDIO_STREAM);
   1779 }
   1780 
   1781 void PrerenderManager::RecordLikelyLoginOnURL(const GURL& url) {
   1782   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1783   if (!url.SchemeIsHTTPOrHTTPS())
   1784     return;
   1785   if (logged_in_predictor_table_.get()) {
   1786     BrowserThread::PostTask(
   1787         BrowserThread::DB,
   1788         FROM_HERE,
   1789         base::Bind(&LoggedInPredictorTable::AddDomainFromURL,
   1790                    logged_in_predictor_table_,
   1791                    url));
   1792   }
   1793   std::string key = LoggedInPredictorTable::GetKey(url);
   1794   if (!logged_in_state_.get())
   1795     return;
   1796   if (logged_in_state_->count(key))
   1797     return;
   1798   (*logged_in_state_)[key] = base::Time::Now().ToInternalValue();
   1799 }
   1800 
   1801 void PrerenderManager::CheckIfLikelyLoggedInOnURL(
   1802     const GURL& url,
   1803     bool* lookup_result,
   1804     bool* database_was_present,
   1805     const base::Closure& result_cb) {
   1806   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1807   if (!logged_in_predictor_table_.get()) {
   1808     *database_was_present = false;
   1809     *lookup_result = false;
   1810     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, result_cb);
   1811     return;
   1812   }
   1813   BrowserThread::PostTaskAndReply(
   1814       BrowserThread::DB, FROM_HERE,
   1815       base::Bind(&LoggedInPredictorTable::HasUserLoggedIn,
   1816                  logged_in_predictor_table_,
   1817                  url,
   1818                  lookup_result,
   1819                  database_was_present),
   1820       result_cb);
   1821 }
   1822 
   1823 
   1824 void PrerenderManager::CookieChanged(ChromeCookieDetails* details) {
   1825   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1826 
   1827   if (!logged_in_predictor_table_.get())
   1828     return;
   1829 
   1830   // We only care when a cookie has been removed.
   1831   if (!details->removed)
   1832     return;
   1833 
   1834   std::string domain_key =
   1835       LoggedInPredictorTable::GetKeyFromDomain(details->cookie->Domain());
   1836 
   1837   // If we have no record of this domain as a potentially logged in domain,
   1838   // nothing to do here.
   1839   if (logged_in_state_.get() && logged_in_state_->count(domain_key) < 1)
   1840     return;
   1841 
   1842   net::URLRequestContextGetter* rq_context = profile_->GetRequestContext();
   1843   if (!rq_context)
   1844     return;
   1845 
   1846   BrowserThread::PostTask(
   1847       BrowserThread::IO, FROM_HERE,
   1848       base::Bind(&CheckIfCookiesExistForDomainOnIOThread,
   1849                  base::Unretained(rq_context),
   1850                  domain_key,
   1851                  base::Bind(
   1852                      &PrerenderManager::CookieChangedAnyCookiesLeftLookupResult,
   1853                      AsWeakPtr(),
   1854                      domain_key)
   1855                  ));
   1856 }
   1857 
   1858 void PrerenderManager::CookieChangedAnyCookiesLeftLookupResult(
   1859     const std::string& domain_key,
   1860     bool cookies_exist) {
   1861   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1862 
   1863   if (cookies_exist)
   1864     return;
   1865 
   1866   if (logged_in_predictor_table_.get()) {
   1867     BrowserThread::PostTask(BrowserThread::DB,
   1868                             FROM_HERE,
   1869                             base::Bind(&LoggedInPredictorTable::DeleteDomain,
   1870                                        logged_in_predictor_table_,
   1871                                        domain_key));
   1872   }
   1873 
   1874   if (logged_in_state_.get())
   1875     logged_in_state_->erase(domain_key);
   1876 }
   1877 
   1878 void PrerenderManager::LoggedInPredictorDataReceived(
   1879     scoped_ptr<LoggedInStateMap> new_map) {
   1880   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1881   logged_in_state_.swap(new_map);
   1882 }
   1883 
   1884 void PrerenderManager::RecordEvent(PrerenderContents* contents,
   1885                                    PrerenderEvent event) const {
   1886   if (!contents)
   1887     histograms_->RecordEvent(ORIGIN_NONE, kNoExperiment, event);
   1888   else
   1889     histograms_->RecordEvent(contents->origin(), contents->experiment_id(),
   1890                              event);
   1891 }
   1892 
   1893 // static
   1894 void PrerenderManager::RecordCookieEvent(int process_id,
   1895                                          int render_view_id,
   1896                                          const GURL& url,
   1897                                          const GURL& frame_url,
   1898                                          PrerenderContents::CookieEvent event,
   1899                                          const net::CookieList* cookie_list) {
   1900   RenderViewHost* rvh = RenderViewHost::FromID(process_id, render_view_id);
   1901   if (!rvh)
   1902     return;
   1903 
   1904   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
   1905   if (!web_contents)
   1906     return;
   1907 
   1908   bool is_main_frame = true;
   1909 
   1910   PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile(
   1911       Profile::FromBrowserContext(web_contents->GetBrowserContext()));
   1912 
   1913   if (!prerender_manager)
   1914     return;
   1915 
   1916   PrerenderContents* prerender_contents =
   1917       prerender_manager->GetPrerenderContents(web_contents);
   1918 
   1919   if (!prerender_contents)
   1920     return;
   1921 
   1922   base::Time earliest_create_date;
   1923   if (event == PrerenderContents::COOKIE_EVENT_SEND) {
   1924     if (!cookie_list || cookie_list->empty())
   1925       return;
   1926     for (size_t i = 0; i < cookie_list->size(); i++) {
   1927       if (earliest_create_date.is_null() ||
   1928           (*cookie_list)[i].CreationDate() < earliest_create_date) {
   1929         earliest_create_date = (*cookie_list)[i].CreationDate();
   1930       }
   1931     }
   1932   }
   1933 
   1934   prerender_contents->RecordCookieEvent(event,
   1935                                         is_main_frame && url == frame_url,
   1936                                         earliest_create_date);
   1937 }
   1938 
   1939 void PrerenderManager::RecordCookieStatus(Origin origin,
   1940                                           uint8 experiment_id,
   1941                                           int cookie_status) const {
   1942   histograms_->RecordCookieStatus(origin, experiment_id, cookie_status);
   1943 }
   1944 
   1945 void PrerenderManager::OnHistoryServiceDidQueryURL(
   1946     Origin origin,
   1947     uint8 experiment_id,
   1948     CancelableRequestProvider::Handle handle,
   1949     bool success,
   1950     const history::URLRow* url_row,
   1951     history::VisitVector* visists) {
   1952   histograms_->RecordPrerenderPageVisitedStatus(origin, experiment_id, success);
   1953 }
   1954 
   1955 }  // namespace prerender
   1956