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