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