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