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