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