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