• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "base/logging.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/time.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/prerender/prerender_contents.h"
13 #include "chrome/browser/prerender/prerender_final_status.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "content/browser/browser_thread.h"
16 #include "content/browser/renderer_host/render_view_host.h"
17 #include "content/browser/renderer_host/render_process_host.h"
18 #include "content/browser/renderer_host/resource_dispatcher_host.h"
19 #include "content/browser/tab_contents/render_view_host_manager.h"
20 #include "content/browser/tab_contents/tab_contents.h"
21 #include "content/common/notification_service.h"
22 #include "content/common/view_messages.h"
23 #include "googleurl/src/url_parse.h"
24 #include "googleurl/src/url_canon.h"
25 #include "googleurl/src/url_util.h"
26 
27 namespace prerender {
28 
29 // static
30 int PrerenderManager::prerenders_per_session_count_ = 0;
31 
32 // static
33 base::TimeTicks PrerenderManager::last_prefetch_seen_time_;
34 
35 // static
36 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
37     PRERENDER_MODE_ENABLED;
38 
39 // static
GetMode()40 PrerenderManager::PrerenderManagerMode PrerenderManager::GetMode() {
41   return mode_;
42 }
43 
44 // static
SetMode(PrerenderManagerMode mode)45 void PrerenderManager::SetMode(PrerenderManagerMode mode) {
46   mode_ = mode;
47 }
48 
49 // static
IsPrerenderingPossible()50 bool PrerenderManager::IsPrerenderingPossible() {
51   return
52       GetMode() == PRERENDER_MODE_ENABLED ||
53       GetMode() == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP ||
54       GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP;
55 }
56 
57 // static
IsControlGroup()58 bool PrerenderManager::IsControlGroup() {
59   return GetMode() == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP;
60 }
61 
62 // static
MaybeGetQueryStringBasedAliasURL(const GURL & url,GURL * alias_url)63 bool PrerenderManager::MaybeGetQueryStringBasedAliasURL(
64     const GURL& url, GURL* alias_url) {
65   DCHECK(alias_url);
66   url_parse::Parsed parsed;
67   url_parse::ParseStandardURL(url.spec().c_str(), url.spec().length(),
68                               &parsed);
69   url_parse::Component query = parsed.query;
70   url_parse::Component key, value;
71   while (url_parse::ExtractQueryKeyValue(url.spec().c_str(), &query, &key,
72                                          &value)) {
73     if (key.len != 3 || strncmp(url.spec().c_str() + key.begin, "url", key.len))
74       continue;
75     // We found a url= query string component.
76     if (value.len < 1)
77       continue;
78     url_canon::RawCanonOutputW<1024> decoded_url;
79     url_util::DecodeURLEscapeSequences(url.spec().c_str() + value.begin,
80                                        value.len, &decoded_url);
81     GURL new_url(string16(decoded_url.data(), decoded_url.length()));
82     if (!new_url.is_empty() && new_url.is_valid()) {
83       *alias_url = new_url;
84       return true;
85     }
86     return false;
87   }
88   return false;
89 }
90 
91 struct PrerenderManager::PrerenderContentsData {
92   PrerenderContents* contents_;
93   base::Time start_time_;
PrerenderContentsDataprerender::PrerenderManager::PrerenderContentsData94   PrerenderContentsData(PrerenderContents* contents, base::Time start_time)
95       : contents_(contents),
96         start_time_(start_time) {
97   }
98 };
99 
100 struct PrerenderManager::PendingContentsData {
PendingContentsDataprerender::PrerenderManager::PendingContentsData101   PendingContentsData(const GURL& url, const std::vector<GURL>& alias_urls,
102                       const GURL& referrer)
103       : url_(url), alias_urls_(alias_urls), referrer_(referrer) { }
~PendingContentsDataprerender::PrerenderManager::PendingContentsData104   ~PendingContentsData() {}
105   GURL url_;
106   std::vector<GURL> alias_urls_;
107   GURL referrer_;
108 };
109 
110 
PrerenderManager(Profile * profile)111 PrerenderManager::PrerenderManager(Profile* profile)
112     : rate_limit_enabled_(true),
113       enabled_(true),
114       profile_(profile),
115       max_prerender_age_(base::TimeDelta::FromSeconds(
116           kDefaultMaxPrerenderAgeSeconds)),
117       max_elements_(kDefaultMaxPrerenderElements),
118       prerender_contents_factory_(PrerenderContents::CreateFactory()),
119       last_prerender_start_time_(GetCurrentTimeTicks() -
120           base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs)) {
121 }
122 
~PrerenderManager()123 PrerenderManager::~PrerenderManager() {
124   while (!prerender_list_.empty()) {
125     PrerenderContentsData data = prerender_list_.front();
126     prerender_list_.pop_front();
127     data.contents_->set_final_status(FINAL_STATUS_MANAGER_SHUTDOWN);
128     delete data.contents_;
129   }
130 }
131 
SetPrerenderContentsFactory(PrerenderContents::Factory * prerender_contents_factory)132 void PrerenderManager::SetPrerenderContentsFactory(
133     PrerenderContents::Factory* prerender_contents_factory) {
134   prerender_contents_factory_.reset(prerender_contents_factory);
135 }
136 
AddPreload(const GURL & url,const std::vector<GURL> & alias_urls,const GURL & referrer)137 bool PrerenderManager::AddPreload(const GURL& url,
138                                   const std::vector<GURL>& alias_urls,
139                                   const GURL& referrer) {
140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
141   DeleteOldEntries();
142   if (FindEntry(url))
143     return false;
144 
145   // Local copy, since we may have to add an additional entry to it.
146   std::vector<GURL> all_alias_urls = alias_urls;
147 
148   GURL additional_alias_url;
149   if (IsControlGroup() &&
150       PrerenderManager::MaybeGetQueryStringBasedAliasURL(
151           url, &additional_alias_url))
152     all_alias_urls.push_back(additional_alias_url);
153 
154   // Do not prerender if there are too many render processes, and we would
155   // have to use an existing one.  We do not want prerendering to happen in
156   // a shared process, so that we can always reliably lower the CPU
157   // priority for prerendering.
158   // In single-process mode, ShouldTryToUseExistingProcessHost() always returns
159   // true, so that case needs to be explicitly checked for.
160   // TODO(tburkard): Figure out how to cancel prerendering in the opposite
161   // case, when a new tab is added to a process used for prerendering.
162   if (RenderProcessHost::ShouldTryToUseExistingProcessHost() &&
163       !RenderProcessHost::run_renderer_in_process()) {
164     // Only record the status if we are not in the control group.
165     if (!IsControlGroup())
166       RecordFinalStatus(FINAL_STATUS_TOO_MANY_PROCESSES);
167     return false;
168   }
169 
170   // Check if enough time has passed since the last prerender.
171   if (!DoesRateLimitAllowPrerender()) {
172     // Cancel the prerender. We could add it to the pending prerender list but
173     // this doesn't make sense as the next prerender request will be triggered
174     // by a navigation and is unlikely to be the same site.
175     RecordFinalStatus(FINAL_STATUS_RATE_LIMIT_EXCEEDED);
176 
177     return false;
178   }
179 
180   // TODO(cbentzel): Move invalid checks here instead of PrerenderContents?
181   PrerenderContentsData data(CreatePrerenderContents(url, all_alias_urls,
182                                                      referrer),
183                              GetCurrentTime());
184 
185   prerender_list_.push_back(data);
186   if (IsControlGroup()) {
187     data.contents_->set_final_status(FINAL_STATUS_CONTROL_GROUP);
188   } else {
189     last_prerender_start_time_ = GetCurrentTimeTicks();
190     data.contents_->StartPrerendering();
191   }
192   while (prerender_list_.size() > max_elements_) {
193     data = prerender_list_.front();
194     prerender_list_.pop_front();
195     data.contents_->set_final_status(FINAL_STATUS_EVICTED);
196     delete data.contents_;
197   }
198   StartSchedulingPeriodicCleanups();
199   return true;
200 }
201 
AddPendingPreload(const std::pair<int,int> & child_route_id_pair,const GURL & url,const std::vector<GURL> & alias_urls,const GURL & referrer)202 void PrerenderManager::AddPendingPreload(
203     const std::pair<int,int>& child_route_id_pair,
204     const GURL& url,
205     const std::vector<GURL>& alias_urls,
206     const GURL& referrer) {
207   // Check if this is coming from a valid prerender rvh.
208   bool is_valid_prerender = false;
209   for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
210        it != prerender_list_.end(); ++it) {
211     PrerenderContents* pc = it->contents_;
212 
213     int child_id;
214     int route_id;
215     bool has_child_id = pc->GetChildId(&child_id);
216     bool has_route_id = has_child_id && pc->GetRouteId(&route_id);
217 
218     if (has_child_id && has_route_id &&
219         child_id == child_route_id_pair.first &&
220         route_id == child_route_id_pair.second) {
221       is_valid_prerender = true;
222       break;
223     }
224   }
225 
226   // If not, we could check to see if the RenderViewHost specified by the
227   // child_route_id_pair exists and if so just start prerendering, as this
228   // suggests that the link was clicked, though this might prerender something
229   // that the user has already navigated away from. For now, we'll be
230   // conservative and skip the prerender which will mean some prerender requests
231   // from prerendered pages will be missed if the user navigates quickly.
232   if (!is_valid_prerender) {
233     RecordFinalStatus(FINAL_STATUS_PENDING_SKIPPED);
234     return;
235   }
236 
237   PendingPrerenderList::iterator it =
238       pending_prerender_list_.find(child_route_id_pair);
239   if (it == pending_prerender_list_.end()) {
240     PendingPrerenderList::value_type el = std::make_pair(child_route_id_pair,
241                                             std::vector<PendingContentsData>());
242     it = pending_prerender_list_.insert(el).first;
243   }
244 
245   it->second.push_back(PendingContentsData(url, alias_urls, referrer));
246 }
247 
DeleteOldEntries()248 void PrerenderManager::DeleteOldEntries() {
249   while (!prerender_list_.empty()) {
250     PrerenderContentsData data = prerender_list_.front();
251     if (IsPrerenderElementFresh(data.start_time_))
252       return;
253     prerender_list_.pop_front();
254     data.contents_->set_final_status(FINAL_STATUS_TIMED_OUT);
255     delete data.contents_;
256   }
257   if (prerender_list_.empty())
258     StopSchedulingPeriodicCleanups();
259 }
260 
GetEntry(const GURL & url)261 PrerenderContents* PrerenderManager::GetEntry(const GURL& url) {
262   DeleteOldEntries();
263   for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
264        it != prerender_list_.end();
265        ++it) {
266     PrerenderContents* pc = it->contents_;
267     if (pc->MatchesURL(url)) {
268       prerender_list_.erase(it);
269       return pc;
270     }
271   }
272   // Entry not found.
273   return NULL;
274 }
275 
MaybeUsePreloadedPage(TabContents * tc,const GURL & url)276 bool PrerenderManager::MaybeUsePreloadedPage(TabContents* tc, const GURL& url) {
277   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278   scoped_ptr<PrerenderContents> pc(GetEntry(url));
279   if (pc.get() == NULL)
280     return false;
281 
282   // If we are just in the control group (which can be detected by noticing
283   // that prerendering hasn't even started yet), record that this TC now would
284   // be showing a prerendered contents, but otherwise, don't do anything.
285   if (!pc->prerendering_has_started()) {
286     MarkTabContentsAsWouldBePrerendered(tc);
287     return false;
288   }
289 
290   if (!pc->load_start_time().is_null())
291     RecordTimeUntilUsed(GetCurrentTimeTicks() - pc->load_start_time());
292 
293   UMA_HISTOGRAM_COUNTS("Prerender.PrerendersPerSessionCount",
294                        ++prerenders_per_session_count_);
295   pc->set_final_status(FINAL_STATUS_USED);
296 
297   int child_id;
298   int route_id;
299   CHECK(pc->GetChildId(&child_id));
300   CHECK(pc->GetRouteId(&route_id));
301 
302   RenderViewHost* rvh = pc->render_view_host();
303   // RenderViewHosts in PrerenderContents start out hidden.
304   // Since we are actually using it now, restore it.
305   rvh->WasRestored();
306   pc->set_render_view_host(NULL);
307   rvh->Send(new ViewMsg_DisplayPrerenderedPage(rvh->routing_id()));
308   tc->SwapInRenderViewHost(rvh);
309   MarkTabContentsAsPrerendered(tc);
310 
311   // See if we have any pending prerender requests for this routing id and start
312   // the preload if we do.
313   std::pair<int, int> child_route_pair = std::make_pair(child_id, route_id);
314   PendingPrerenderList::iterator pending_it =
315       pending_prerender_list_.find(child_route_pair);
316   if (pending_it != pending_prerender_list_.end()) {
317     for (std::vector<PendingContentsData>::iterator content_it =
318             pending_it->second.begin();
319          content_it != pending_it->second.end(); ++content_it) {
320       AddPreload(content_it->url_, content_it->alias_urls_,
321                  content_it->referrer_);
322     }
323     pending_prerender_list_.erase(pending_it);
324   }
325 
326   NotificationService::current()->Notify(
327       NotificationType::PRERENDER_CONTENTS_USED,
328       Source<std::pair<int, int> >(&child_route_pair),
329       NotificationService::NoDetails());
330 
331   ViewHostMsg_FrameNavigate_Params* p = pc->navigate_params();
332   if (p != NULL)
333     tc->DidNavigate(rvh, *p);
334 
335   string16 title = pc->title();
336   if (!title.empty())
337     tc->UpdateTitle(rvh, pc->page_id(), UTF16ToWideHack(title));
338 
339   GURL icon_url = pc->icon_url();
340   if (!icon_url.is_empty()) {
341     std::vector<FaviconURL> urls;
342     urls.push_back(FaviconURL(icon_url, FaviconURL::FAVICON));
343     tc->favicon_helper().OnUpdateFaviconURL(pc->page_id(), urls);
344   }
345 
346   if (pc->has_stopped_loading())
347     tc->DidStopLoading();
348 
349   return true;
350 }
351 
RemoveEntry(PrerenderContents * entry)352 void PrerenderManager::RemoveEntry(PrerenderContents* entry) {
353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354   for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
355        it != prerender_list_.end();
356        ++it) {
357     if (it->contents_ == entry) {
358       RemovePendingPreload(entry);
359       prerender_list_.erase(it);
360       break;
361     }
362   }
363   DeleteOldEntries();
364 }
365 
GetCurrentTime() const366 base::Time PrerenderManager::GetCurrentTime() const {
367   return base::Time::Now();
368 }
369 
GetCurrentTimeTicks() const370 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const {
371   return base::TimeTicks::Now();
372 }
373 
IsPrerenderElementFresh(const base::Time start) const374 bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const {
375   base::Time now = GetCurrentTime();
376   return (now - start < max_prerender_age_);
377 }
378 
CreatePrerenderContents(const GURL & url,const std::vector<GURL> & alias_urls,const GURL & referrer)379 PrerenderContents* PrerenderManager::CreatePrerenderContents(
380     const GURL& url,
381     const std::vector<GURL>& alias_urls,
382     const GURL& referrer) {
383   return prerender_contents_factory_->CreatePrerenderContents(
384       this, profile_, url, alias_urls, referrer);
385 }
386 
387 // Helper macro for histograms.
388 #define RECORD_PLT(tag, perceived_page_load_time) { \
389     UMA_HISTOGRAM_CUSTOM_TIMES( \
390         base::FieldTrial::MakeName(std::string("Prerender.") + tag, \
391                                    "Prefetch"), \
392         perceived_page_load_time, \
393         base::TimeDelta::FromMilliseconds(10), \
394         base::TimeDelta::FromSeconds(60), \
395         100); \
396   }
397 
398 // static
RecordPerceivedPageLoadTime(base::TimeDelta perceived_page_load_time,TabContents * tab_contents)399 void PrerenderManager::RecordPerceivedPageLoadTime(
400     base::TimeDelta perceived_page_load_time,
401     TabContents* tab_contents) {
402   bool within_window = WithinWindow();
403   PrerenderManager* prerender_manager =
404       tab_contents->profile()->GetPrerenderManager();
405   if (!prerender_manager)
406     return;
407   if (!prerender_manager->is_enabled())
408     return;
409   RECORD_PLT("PerceivedPLT", perceived_page_load_time);
410   if (within_window)
411     RECORD_PLT("PerceivedPLTWindowed", perceived_page_load_time);
412   if (prerender_manager &&
413       ((mode_ == PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP &&
414         prerender_manager->WouldTabContentsBePrerendered(tab_contents)) ||
415        (mode_ == PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP &&
416         prerender_manager->IsTabContentsPrerendered(tab_contents)))) {
417     RECORD_PLT("PerceivedPLTMatched", perceived_page_load_time);
418   } else {
419     if (within_window)
420       RECORD_PLT("PerceivedPLTWindowNotMatched", perceived_page_load_time);
421   }
422 }
423 
RecordTimeUntilUsed(base::TimeDelta time_until_used)424 void PrerenderManager::RecordTimeUntilUsed(base::TimeDelta time_until_used) {
425   UMA_HISTOGRAM_CUSTOM_TIMES(
426       "Prerender.TimeUntilUsed",
427       time_until_used,
428       base::TimeDelta::FromMilliseconds(10),
429       base::TimeDelta::FromSeconds(kDefaultMaxPrerenderAgeSeconds),
430       50);
431 }
432 
is_enabled() const433 bool PrerenderManager::is_enabled() const {
434   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
435   return enabled_;
436 }
437 
set_enabled(bool enabled)438 void PrerenderManager::set_enabled(bool enabled) {
439   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
440   enabled_ = enabled;
441 }
442 
FindEntry(const GURL & url)443 PrerenderContents* PrerenderManager::FindEntry(const GURL& url) {
444   for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
445        it != prerender_list_.end();
446        ++it) {
447     if (it->contents_->MatchesURL(url))
448       return it->contents_;
449   }
450   // Entry not found.
451   return NULL;
452 }
453 
454 PrerenderManager::PendingContentsData*
FindPendingEntry(const GURL & url)455     PrerenderManager::FindPendingEntry(const GURL& url) {
456   for (PendingPrerenderList::iterator map_it = pending_prerender_list_.begin();
457        map_it != pending_prerender_list_.end();
458        ++map_it) {
459     for (std::vector<PendingContentsData>::iterator content_it =
460             map_it->second.begin();
461          content_it != map_it->second.end();
462          ++content_it) {
463       if (content_it->url_ == url) {
464         return &(*content_it);
465       }
466     }
467   }
468 
469   return NULL;
470 }
471 
472 // static
RecordPrefetchTagObserved()473 void PrerenderManager::RecordPrefetchTagObserved() {
474   // Ensure that we are in the UI thread, and post to the UI thread if
475   // necessary.
476   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
477     BrowserThread::PostTask(
478         BrowserThread::UI,
479         FROM_HERE,
480         NewRunnableFunction(
481             &PrerenderManager::RecordPrefetchTagObservedOnUIThread));
482   } else {
483     RecordPrefetchTagObservedOnUIThread();
484   }
485 }
486 
487 // static
RecordPrefetchTagObservedOnUIThread()488 void PrerenderManager::RecordPrefetchTagObservedOnUIThread() {
489   // Once we get here, we have to be on the UI thread.
490   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491 
492   // If we observe multiple tags within the 30 second window, we will still
493   // reset the window to begin at the most recent occurrence, so that we will
494   // always be in a window in the 30 seconds from each occurrence.
495   last_prefetch_seen_time_ = base::TimeTicks::Now();
496 }
497 
RemovePendingPreload(PrerenderContents * entry)498 void PrerenderManager::RemovePendingPreload(PrerenderContents* entry) {
499   int child_id;
500   int route_id;
501   bool has_child_id = entry->GetChildId(&child_id);
502   bool has_route_id = has_child_id && entry->GetRouteId(&route_id);
503 
504   // If the entry doesn't have a RenderViewHost then it didn't start
505   // prerendering and there shouldn't be any pending preloads to remove.
506   if (has_child_id && has_route_id) {
507     std::pair<int, int> child_route_pair = std::make_pair(child_id, route_id);
508     pending_prerender_list_.erase(child_route_pair);
509   }
510 }
511 
512 // static
WithinWindow()513 bool PrerenderManager::WithinWindow() {
514   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
515   if (last_prefetch_seen_time_.is_null())
516     return false;
517   base::TimeDelta elapsed_time =
518       base::TimeTicks::Now() - last_prefetch_seen_time_;
519   return elapsed_time <= base::TimeDelta::FromSeconds(kWindowDurationSeconds);
520 }
521 
DoesRateLimitAllowPrerender() const522 bool PrerenderManager::DoesRateLimitAllowPrerender() const {
523   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
524   base::TimeDelta elapsed_time =
525       GetCurrentTimeTicks() - last_prerender_start_time_;
526   UMA_HISTOGRAM_TIMES("Prerender.TimeBetweenPrerenderRequests",
527                       elapsed_time);
528   if (!rate_limit_enabled_)
529     return true;
530   return elapsed_time >
531       base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs);
532 }
533 
StartSchedulingPeriodicCleanups()534 void PrerenderManager::StartSchedulingPeriodicCleanups() {
535   if (repeating_timer_.IsRunning())
536     return;
537   repeating_timer_.Start(
538       base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs),
539       this,
540       &PrerenderManager::PeriodicCleanup);
541 }
542 
StopSchedulingPeriodicCleanups()543 void PrerenderManager::StopSchedulingPeriodicCleanups() {
544   repeating_timer_.Stop();
545 }
546 
PeriodicCleanup()547 void PrerenderManager::PeriodicCleanup() {
548   DeleteOldEntries();
549   // Grab a copy of the current PrerenderContents pointers, so that we
550   // will not interfere with potential deletions of the list.
551   std::vector<PrerenderContents*> prerender_contents;
552   for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
553        it != prerender_list_.end();
554        ++it) {
555     prerender_contents.push_back(it->contents_);
556   }
557   for (std::vector<PrerenderContents*>::iterator it =
558            prerender_contents.begin();
559        it != prerender_contents.end();
560        ++it) {
561     (*it)->DestroyWhenUsingTooManyResources();
562   }
563 }
564 
MarkTabContentsAsPrerendered(TabContents * tc)565 void PrerenderManager::MarkTabContentsAsPrerendered(TabContents* tc) {
566   prerendered_tc_set_.insert(tc);
567 }
568 
MarkTabContentsAsWouldBePrerendered(TabContents * tc)569 void PrerenderManager::MarkTabContentsAsWouldBePrerendered(TabContents* tc) {
570   would_be_prerendered_tc_set_.insert(tc);
571 }
572 
MarkTabContentsAsNotPrerendered(TabContents * tc)573 void PrerenderManager::MarkTabContentsAsNotPrerendered(TabContents* tc) {
574   prerendered_tc_set_.erase(tc);
575   would_be_prerendered_tc_set_.erase(tc);
576 }
577 
IsTabContentsPrerendered(TabContents * tc) const578 bool PrerenderManager::IsTabContentsPrerendered(TabContents* tc) const {
579   return prerendered_tc_set_.count(tc) > 0;
580 }
581 
WouldTabContentsBePrerendered(TabContents * tc) const582 bool PrerenderManager::WouldTabContentsBePrerendered(TabContents* tc) const {
583   return would_be_prerendered_tc_set_.count(tc) > 0;
584 }
585 
586 }  // namespace prerender
587