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