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