OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/prerender/prerender_manager.h" | 5 #include "chrome/browser/prerender/prerender_manager.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
163 // static | 163 // static |
164 bool PrerenderManager::is_prefetch_enabled_ = false; | 164 bool PrerenderManager::is_prefetch_enabled_ = false; |
165 | 165 |
166 // static | 166 // static |
167 int PrerenderManager::prerenders_per_session_count_ = 0; | 167 int PrerenderManager::prerenders_per_session_count_ = 0; |
168 | 168 |
169 // static | 169 // static |
170 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ = | 170 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ = |
171 PRERENDER_MODE_ENABLED; | 171 PRERENDER_MODE_ENABLED; |
172 | 172 |
173 struct PrerenderManager::PrerenderContentsData { | 173 // A PrerenderContentsData represents a running prerender. A list of WeakPtr |
174 // to these objects is kept in prerender_list_ as the canonical list of running | |
175 // prerenders. | |
176 class PrerenderManager::PrerenderContentsData : | |
177 public PrerenderHandleInterface { | |
dominich
2012/06/18 15:32:44
I don't understand why this has a PrerenderHandle
gavinp
2012/06/18 16:40:48
Every other instance of Handle in our code base is
| |
178 public: | |
179 // These STL predicates all operate on WeakPtrs, for compatibility with | |
180 // PrerenderManager::PrerenderContentsList, which contains | |
181 // base::WeakPtr<PrerenderContentsData>. | |
182 | |
183 // This predicate tests if the weak-pointed to object has been deleted. | |
184 class DeletedPredicate; | |
185 // This predicate compares the object to another PrerenderHandle for equality. | |
186 class EqualsPredicate; | |
187 // Given a |base::Time|, is the current prerender expired? | |
188 class ExpiredPredicate; | |
189 // Given an |url| and a |session_storage_namespace|, This predicate tests if | |
190 // this PrerenderContentsData | |
191 class MatchPredicate; | |
dominich
2012/06/18 15:32:44
I smell over-engineering. These were already handl
gavinp
2012/06/18 16:40:48
Yes, I hate these and plan to remove them.
On 201
| |
192 | |
193 // Creates a PrerenderContentsData for a prerender starting right now. | |
194 PrerenderContentsData(PrerenderManager* manager, | |
195 PrerenderContents* contents); | |
196 | |
197 base::WeakPtr<PrerenderContentsData> AsWeakPtr() { | |
198 return weak_factory_.GetWeakPtr(); | |
199 } | |
200 | |
201 PrerenderContents* contents() { return contents_; } | |
202 const PrerenderContents* contents() const { return contents_; } | |
203 | |
204 void set_contents(PrerenderContents* contents) { contents_ = contents; } | |
205 | |
206 bool IsExpiredAt(base::Time time) const; | |
207 bool IsDestroyed() const; | |
208 bool IsStarted() const; | |
209 | |
210 // from PrerenderHandleInterface | |
211 virtual bool DidFinishLoading() const OVERRIDE; | |
212 virtual bool IsPending() const OVERRIDE; | |
213 virtual void Destroy() OVERRIDE; | |
214 | |
215 static PrerenderContentsData* FromPrerenderHandle(PrerenderHandleInterface* in terface); | |
216 | |
217 private: | |
218 virtual ~PrerenderContentsData(); | |
219 | |
220 base::WeakPtrFactory<PrerenderContentsData> weak_factory_; | |
221 PrerenderManager const* manager_; | |
222 base::Time start_time_; | |
223 base::Time expiry_time_; | |
174 PrerenderContents* contents_; | 224 PrerenderContents* contents_; |
175 base::Time start_time_; | 225 }; |
176 int active_count_; | 226 |
177 PrerenderContentsData(PrerenderContents* contents, base::Time start_time) | 227 class PrerenderManager::PrerenderContentsData::DeletedPredicate { |
178 : contents_(contents), | 228 public: |
179 start_time_(start_time), | 229 bool operator()(base::WeakPtr<PrerenderContentsData> item) const { |
180 active_count_(1) { | 230 return !item; |
181 CHECK(contents); | |
182 } | 231 } |
183 }; | 232 }; |
184 | 233 |
234 class PrerenderManager::PrerenderContentsData::EqualsPredicate { | |
235 public: | |
236 EqualsPredicate(PrerenderHandle handle) : handle_(handle) {} | |
237 | |
238 bool operator()(const base::WeakPtr<PrerenderContentsData>& item) const { | |
239 return handle_.get() == item.get(); | |
240 } | |
241 | |
242 private: | |
243 PrerenderHandle handle_; | |
244 }; | |
245 | |
246 class PrerenderManager::PrerenderContentsData::ExpiredPredicate { | |
247 public: | |
248 ExpiredPredicate(base::Time now) : now_(now) {} | |
249 | |
250 bool operator()(base::WeakPtr<PrerenderContentsData> item) const { | |
251 return item && item->IsExpiredAt(now_); | |
252 } | |
253 private: | |
254 base::Time now_; | |
255 }; | |
256 | |
257 class PrerenderManager::PrerenderContentsData::MatchPredicate { | |
258 public: | |
259 MatchPredicate(const GURL& url, | |
260 content::SessionStorageNamespace* session_storage_namespace) | |
261 : url_(url), | |
262 session_storage_namespace_(session_storage_namespace) { | |
263 } | |
264 | |
265 bool operator()(base::WeakPtr<PrerenderContentsData> item) const { | |
266 return item && item->contents_ && | |
267 item->contents_->Matches(url_, session_storage_namespace_); | |
268 } | |
269 | |
270 private: | |
271 GURL url_; | |
272 content::SessionStorageNamespace* session_storage_namespace_; | |
273 }; | |
274 | |
275 PrerenderManager::PrerenderContentsData::PrerenderContentsData( | |
276 PrerenderManager* manager, | |
277 PrerenderContents* contents) : weak_factory_(this), | |
278 manager_(manager), | |
279 start_time_(manager_->GetCurrentTime()), | |
dominich
2012/06/18 15:32:44
why is the manager the arbiter of what the time is
| |
280 expiry_time_(start_time_ + | |
dominich
2012/06/18 15:32:44
if this can be inferred from the start_time_ there
| |
281 manager_->GetMaxAge()), | |
282 contents_(contents) { | |
283 } | |
284 | |
285 bool PrerenderManager::PrerenderContentsData::IsExpiredAt( | |
286 base::Time time) const { | |
287 return time > expiry_time_; | |
288 } | |
289 | |
290 bool PrerenderManager::PrerenderContentsData::IsDestroyed() const { | |
291 return !manager_ && !contents_; | |
292 } | |
293 | |
294 bool PrerenderManager::PrerenderContentsData::IsStarted() const { | |
295 return manager_ && contents_; | |
296 } | |
297 | |
298 bool PrerenderManager::PrerenderContentsData::DidFinishLoading() const { | |
299 return contents_ ? contents_->has_finished_loading() : false; | |
300 } | |
301 | |
302 bool PrerenderManager::PrerenderContentsData::IsPending() const { | |
303 DCHECK(!IsDestroyed()); | |
304 return manager_ && !contents_; | |
305 } | |
306 | |
307 // static | |
308 PrerenderManager::PrerenderContentsData* | |
309 PrerenderManager::PrerenderContentsData::FromPrerenderHandle( | |
310 PrerenderHandleInterface* interface) { | |
311 return static_cast<PrerenderContentsData*>(interface); | |
dominich
2012/06/18 15:32:44
Code smell. If you need a static method to cast fr
| |
312 } | |
313 | |
314 PrerenderManager::PrerenderContentsData::~PrerenderContentsData() { | |
315 DVLOG(4) << "PrerenderContentsData::~PrerenderContentsData()"; | |
316 if (contents_) | |
317 contents_->Destroy(FINAL_STATUS_CANCELLED); | |
318 } | |
319 | |
320 void PrerenderManager::PrerenderContentsData::Destroy() { | |
321 DCHECK(!IsDestroyed()); | |
322 contents_ = NULL; | |
323 manager_ = NULL; | |
324 } | |
325 | |
185 struct PrerenderManager::NavigationRecord { | 326 struct PrerenderManager::NavigationRecord { |
186 GURL url_; | 327 GURL url_; |
187 base::TimeTicks time_; | 328 base::TimeTicks time_; |
188 NavigationRecord(const GURL& url, base::TimeTicks time) | 329 NavigationRecord(const GURL& url, base::TimeTicks time) |
189 : url_(url), | 330 : url_(url), |
190 time_(time) { | 331 time_(time) { |
191 } | 332 } |
192 }; | 333 }; |
193 | 334 |
194 PrerenderManager::PrerenderManager(Profile* profile, | 335 PrerenderManager::PrerenderManager(Profile* profile, |
(...skipping 13 matching lines...) Expand all Loading... | |
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
209 } | 350 } |
210 | 351 |
211 PrerenderManager::~PrerenderManager() { | 352 PrerenderManager::~PrerenderManager() { |
212 } | 353 } |
213 | 354 |
214 void PrerenderManager::Shutdown() { | 355 void PrerenderManager::Shutdown() { |
215 DoShutdown(); | 356 DoShutdown(); |
216 } | 357 } |
217 | 358 |
218 bool PrerenderManager::AddPrerenderFromLinkRelPrerender( | 359 PrerenderHandle PrerenderManager::AddPrerenderFromLinkRelPrerender( |
219 int process_id, | 360 int process_id, |
220 int route_id, | 361 int route_id, |
221 const GURL& url, | 362 const GURL& url, |
222 const content::Referrer& referrer, | 363 const content::Referrer& referrer, |
223 const gfx::Size& size) { | 364 const gfx::Size& size) { |
224 #if defined(OS_ANDROID) | 365 #if defined(OS_ANDROID) |
225 // TODO(jcivelli): http://crbug.com/113322 We should have an option to disable | 366 // TODO(jcivelli): http://crbug.com/113322 We should have an option to disable |
226 // link-prerender and enable omnibox-prerender only. | 367 // link-prerender and enable omnibox-prerender only. |
227 return false; | 368 return PrerenderHandle(); |
228 #else | 369 #else |
229 std::pair<int, int> child_route_id_pair(process_id, route_id); | 370 if (PrerenderContentsData* contents_data = |
230 PrerenderContentsDataList::iterator it = | 371 FindContentsDataForChildAndRouteId(process_id, route_id)) { |
231 FindPrerenderContentsForChildRouteIdPair(child_route_id_pair); | 372 // If we are attempting to prerender from inside of a prerender, make a |
232 if (it != prerender_list_.end()) { | 373 // pending prerender in the contents, which will launch again when the |
233 // Instead of prerendering from inside of a running prerender, we will defer | 374 // user navigates to the launcher. |
234 // this request until its launcher is made visible. | 375 return contents_data->contents()->AddPendingPrerender(url, referrer, size); |
dominich
2012/06/18 15:32:44
Ah, now I see why you need this to return a valid
| |
235 it->contents_->AddPendingPrerender(url, referrer, size); | |
236 return true; | |
237 } | 376 } |
238 | 377 |
239 // Unit tests pass in a process_id == -1. | 378 // Unit tests pass in a process_id == -1. |
240 RenderViewHost* source_render_view_host = NULL; | 379 RenderViewHost* source_render_view_host = NULL; |
241 SessionStorageNamespace* session_storage_namespace = NULL; | 380 SessionStorageNamespace* session_storage_namespace = NULL; |
242 if (process_id != -1) { | 381 if (process_id != -1) { |
243 source_render_view_host = | 382 source_render_view_host = |
244 RenderViewHost::FromID(process_id, route_id); | 383 RenderViewHost::FromID(process_id, route_id); |
245 if (!source_render_view_host || !source_render_view_host->GetView()) | 384 if (!source_render_view_host || !source_render_view_host->GetView()) |
246 return false; | 385 return PrerenderHandle(); |
247 session_storage_namespace = | 386 session_storage_namespace = |
248 source_render_view_host->GetSessionStorageNamespace(); | 387 source_render_view_host->GetSessionStorageNamespace(); |
249 } | 388 } |
250 | 389 |
251 return AddPrerender(ORIGIN_LINK_REL_PRERENDER, | 390 return AddPrerender(ORIGIN_LINK_REL_PRERENDER, |
252 process_id, url, referrer, size, | 391 process_id, url, referrer, size, |
253 session_storage_namespace); | 392 session_storage_namespace); |
254 #endif | 393 #endif |
255 } | 394 } |
256 | 395 |
257 bool PrerenderManager::AddPrerenderFromOmnibox( | 396 PrerenderHandle PrerenderManager::AddPrerenderFromOmnibox( |
258 const GURL& url, | 397 const GURL& url, |
259 SessionStorageNamespace* session_storage_namespace) { | 398 SessionStorageNamespace* session_storage_namespace) { |
260 if (!IsOmniboxEnabled(profile_)) | 399 if (!IsOmniboxEnabled(profile_)) |
261 return false; | 400 return PrerenderHandle(); |
262 return AddPrerender(ORIGIN_OMNIBOX, -1, url, | 401 return AddPrerender(ORIGIN_OMNIBOX, -1, url, |
263 content::Referrer(), gfx::Size(), | 402 content::Referrer(), gfx::Size(), |
264 session_storage_namespace); | 403 session_storage_namespace); |
265 } | 404 } |
266 | 405 |
267 void PrerenderManager::MaybeCancelPrerender(const GURL& url) { | |
268 PrerenderContentsDataList::iterator it = FindPrerenderContentsForURL(url); | |
269 if (it == prerender_list_.end()) | |
270 return; | |
271 PrerenderContentsData& prerender_contents_data = *it; | |
272 if (--prerender_contents_data.active_count_ == 0) | |
273 prerender_contents_data.contents_->Destroy(FINAL_STATUS_CANCELLED); | |
274 } | |
275 | |
276 void PrerenderManager::DestroyPrerenderForRenderView( | 406 void PrerenderManager::DestroyPrerenderForRenderView( |
277 int process_id, int view_id, FinalStatus final_status) { | 407 int process_id, int view_id, FinalStatus final_status) { |
278 DCHECK(CalledOnValidThread()); | 408 DCHECK(CalledOnValidThread()); |
279 PrerenderContentsDataList::iterator it = | 409 if (PrerenderContentsData* contents_data = |
280 FindPrerenderContentsForChildRouteIdPair( | 410 FindContentsDataForChildAndRouteId(process_id, view_id)) { |
281 std::make_pair(process_id, view_id)); | 411 contents_data->contents()->Destroy(final_status); |
282 if (it != prerender_list_.end()) { | |
283 PrerenderContents* prerender_contents = it->contents_; | |
284 prerender_contents->Destroy(final_status); | |
285 } | 412 } |
286 } | 413 } |
287 | 414 |
288 void PrerenderManager::CancelAllPrerenders() { | 415 void PrerenderManager::CancelAllPrerenders() { |
289 DCHECK(CalledOnValidThread()); | 416 DCHECK(CalledOnValidThread()); |
290 while (!prerender_list_.empty()) { | 417 while (!prerender_list_.empty()) { |
291 PrerenderContentsData data = prerender_list_.front(); | 418 if (PrerenderContentsData* contents_data = prerender_list_.front()) { |
292 DCHECK(data.contents_); | 419 DCHECK(contents_data->contents()); |
293 data.contents_->Destroy(FINAL_STATUS_CANCELLED); | 420 contents_data->contents()->Destroy(FINAL_STATUS_CANCELLED); |
294 } | 421 } |
295 } | |
296 | |
297 void PrerenderManager::CancelOmniboxPrerenders() { | |
298 DCHECK(CalledOnValidThread()); | |
299 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); | |
300 it != prerender_list_.end(); ) { | |
301 PrerenderContentsDataList::iterator cur = it++; | |
302 if (cur->contents_->origin() == ORIGIN_OMNIBOX) | |
303 cur->contents_->Destroy(FINAL_STATUS_CANCELLED); | |
304 } | 422 } |
305 } | 423 } |
306 | 424 |
307 bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, | 425 bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, |
308 const GURL& url) { | 426 const GURL& url) { |
427 DCHECK(web_contents); | |
309 DCHECK(CalledOnValidThread()); | 428 DCHECK(CalledOnValidThread()); |
310 DCHECK(!IsWebContentsPrerendering(web_contents)); | 429 DCHECK(!IsWebContentsPrerendering(web_contents)); |
311 | 430 |
431 DVLOG(6) << "entering MaybeUsePrerenderedPage..."; | |
432 | |
433 RenderViewHost* old_render_view_host = web_contents->GetRenderViewHost(); | |
434 content::SessionStorageNamespace* old_session_storage_namespace = | |
435 old_render_view_host->GetSessionStorageNamespace(); | |
436 | |
312 scoped_ptr<PrerenderContents> prerender_contents( | 437 scoped_ptr<PrerenderContents> prerender_contents( |
313 GetEntryButNotSpecifiedWC(url, web_contents)); | 438 ClaimPrerender(web_contents, url, old_session_storage_namespace)); |
439 | |
314 if (prerender_contents.get() == NULL) | 440 if (prerender_contents.get() == NULL) |
315 return false; | 441 return false; |
316 | 442 |
443 | |
444 DVLOG(2) << "MaybeUsePrerenderedPage claimed a prerender"; | |
445 | |
317 // Do not use the prerendered version if there is an opener object. | 446 // Do not use the prerendered version if there is an opener object. |
318 if (web_contents->HasOpener()) { | 447 if (web_contents->HasOpener()) { |
319 prerender_contents.release()->Destroy(FINAL_STATUS_WINDOW_OPENER); | 448 prerender_contents.release()->Destroy(FINAL_STATUS_WINDOW_OPENER); |
449 DVLOG(3) << "... but it's a window opener."; | |
320 return false; | 450 return false; |
321 } | 451 } |
322 | 452 |
323 // Even if we match, the location.hash might be different. Record this as a | 453 |
324 // separate final status. | 454 |
325 GURL matching_url; | 455 if (!prerender_contents->Matches(url, old_session_storage_namespace)) { |
326 bool url_matches = prerender_contents->MatchesURL(url, &matching_url); | 456 NOTREACHED() << "Something didn't match."; |
327 DCHECK(url_matches); | 457 DVLOG(3) << "... but there's a bad session storage namespace"; |
328 if (url_matches && url.ref() != matching_url.ref()) { | |
329 prerender_contents.release()->Destroy(FINAL_STATUS_FRAGMENT_MISMATCH); | |
330 return false; | 458 return false; |
331 } | 459 } |
332 | 460 |
333 // If we are just in the control group (which can be detected by noticing | 461 // If we are just in the control group (which can be detected by noticing |
334 // that prerendering hasn't even started yet), record that |web_contents| now | 462 // that prerendering hasn't even started yet), record that |web_contents| now |
335 // would be showing a prerendered contents, but otherwise, don't do anything. | 463 // would be showing a prerendered contents, but otherwise, don't do anything. |
336 if (!prerender_contents->prerendering_has_started()) { | 464 if (!prerender_contents->prerendering_has_started()) { |
337 MarkWebContentsAsWouldBePrerendered(web_contents); | 465 MarkWebContentsAsWouldBePrerendered(web_contents); |
338 prerender_contents.release()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); | 466 prerender_contents.release()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); |
467 DVLOG(3) << "... but we're in the control group, so we'll destroy it unused. "; | |
339 return false; | 468 return false; |
340 } | 469 } |
341 | 470 |
342 // Don't use prerendered pages if debugger is attached to the tab. | 471 // Don't use prerendered pages if debugger is attached to the tab. |
343 // See http://crbug.com/98541 | 472 // See http://crbug.com/98541 |
344 if (content::DevToolsAgentHostRegistry::IsDebuggerAttached(web_contents)) { | 473 if (content::DevToolsAgentHostRegistry::IsDebuggerAttached(web_contents)) { |
345 DestroyAndMarkMatchCompleteAsUsed(prerender_contents.release(), | 474 DestroyAndMarkMatchCompleteAsUsed(prerender_contents.release(), |
346 FINAL_STATUS_DEVTOOLS_ATTACHED); | 475 FINAL_STATUS_DEVTOOLS_ATTACHED); |
476 DVLOG(3) << "... but the dev tools are attached."; | |
347 return false; | 477 return false; |
348 } | 478 } |
349 | 479 |
350 // If the prerendered page is in the middle of a cross-site navigation, | 480 // If the prerendered page is in the middle of a cross-site navigation, |
351 // don't swap it in because there isn't a good way to merge histories. | 481 // don't swap it in because there isn't a good way to merge histories. |
352 if (prerender_contents->IsCrossSiteNavigationPending()) { | 482 if (prerender_contents->IsCrossSiteNavigationPending()) { |
353 DestroyAndMarkMatchCompleteAsUsed( | 483 DestroyAndMarkMatchCompleteAsUsed( |
354 prerender_contents.release(), | 484 prerender_contents.release(), |
355 FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING); | 485 FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING); |
486 DVLOG(3) << "... but cross site navigation is pending."; | |
356 return false; | 487 return false; |
357 } | 488 } |
358 | 489 |
359 // If the session storage namespaces don't match, cancel the prerender. | |
360 RenderViewHost* old_render_view_host = web_contents->GetRenderViewHost(); | |
361 RenderViewHost* new_render_view_host = | |
362 prerender_contents->prerender_contents()->web_contents()-> | |
363 GetRenderViewHost(); | |
364 DCHECK(old_render_view_host); | |
365 DCHECK(new_render_view_host); | |
366 if (old_render_view_host->GetSessionStorageNamespace() != | |
367 new_render_view_host->GetSessionStorageNamespace()) { | |
368 DestroyAndMarkMatchCompleteAsUsed( | |
369 prerender_contents.release(), | |
370 FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH); | |
371 return false; | |
372 } | |
373 | |
374 // If we don't want to use prerenders at all, we are done. | 490 // If we don't want to use prerenders at all, we are done. |
375 // For bookkeeping purposes, we need to mark this WebContents to | 491 // For bookkeeping purposes, we need to mark this WebContents to |
376 // reflect that it would have been prerendered. | 492 // reflect that it would have been prerendered. |
377 if (GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP) { | 493 if (GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP) { |
378 MarkWebContentsAsWouldBePrerendered(web_contents); | 494 MarkWebContentsAsWouldBePrerendered(web_contents); |
379 prerender_contents.release()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); | 495 prerender_contents.release()->Destroy(FINAL_STATUS_WOULD_HAVE_BEEN_USED); |
496 DVLOG(3) << "... we are in the no use experimental group..."; | |
380 return false; | 497 return false; |
381 } | 498 } |
382 | 499 |
383 int child_id, route_id; | 500 int child_id, route_id; |
384 CHECK(prerender_contents->GetChildId(&child_id)); | 501 DCHECK(prerender_contents->GetChildId(&child_id)); |
385 CHECK(prerender_contents->GetRouteId(&route_id)); | 502 DCHECK(prerender_contents->GetRouteId(&route_id)); |
386 | 503 |
387 // Try to set the prerendered page as used, so any subsequent attempts to | 504 // Try to set the prerendered page as used, so any subsequent attempts to |
388 // cancel on other threads will fail. If this fails because the prerender | 505 // cancel on other threads will fail. If this fails because the prerender |
389 // was already cancelled, possibly on another thread, fail. | 506 // was already cancelled, possibly on another thread, fail. |
390 if (!prerender_tracker_->TryUse(child_id, route_id)) | 507 if (!prerender_tracker_->TryUse(child_id, route_id)) { |
508 DVLOG(3) << "... prerender_tracker_->TryUse failed!"; | |
391 return false; | 509 return false; |
510 } | |
392 | 511 |
393 if (!prerender_contents->load_start_time().is_null()) { | 512 if (!prerender_contents->load_start_time().is_null()) { |
394 histograms_->RecordTimeUntilUsed(GetCurrentTimeTicks() - | 513 histograms_->RecordTimeUntilUsed(GetCurrentTimeTicks() - |
395 prerender_contents->load_start_time(), | 514 prerender_contents->load_start_time(), |
396 GetMaxAge()); | 515 GetMaxAge()); |
397 } | 516 } |
398 | 517 |
399 histograms_->RecordPerSessionCount(++prerenders_per_session_count_); | 518 histograms_->RecordPerSessionCount(++prerenders_per_session_count_); |
400 histograms_->RecordUsedPrerender(prerender_contents->origin()); | 519 histograms_->RecordUsedPrerender(prerender_contents->origin()); |
401 prerender_contents->set_final_status(FINAL_STATUS_USED); | 520 prerender_contents->set_final_status(FINAL_STATUS_USED); |
402 | 521 |
522 RenderViewHost* new_render_view_host = | |
523 prerender_contents->prerender_contents()->web_contents()-> | |
524 GetRenderViewHost(); | |
525 | |
403 new_render_view_host->Send( | 526 new_render_view_host->Send( |
404 new PrerenderMsg_SetIsPrerendering(new_render_view_host->GetRoutingID(), | 527 new PrerenderMsg_SetIsPrerendering(new_render_view_host->GetRoutingID(), |
405 false)); | 528 false)); |
406 | 529 |
407 TabContents* new_tab_contents = | 530 TabContents* new_tab_contents = |
408 prerender_contents->ReleasePrerenderContents(); | 531 prerender_contents->ReleasePrerenderContents(); |
409 TabContents* old_tab_contents = TabContents::FromWebContents(web_contents); | 532 TabContents* old_tab_contents = TabContents::FromWebContents(web_contents); |
410 DCHECK(new_tab_contents); | 533 DCHECK(new_tab_contents); |
411 DCHECK(old_tab_contents); | 534 DCHECK(old_tab_contents); |
412 | 535 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
448 FirePageBeforeUnload(false); | 571 FirePageBeforeUnload(false); |
449 } else { | 572 } else { |
450 // No unload handler to run, so delete asap. | 573 // No unload handler to run, so delete asap. |
451 ScheduleDeleteOldTabContents(old_tab_contents, NULL); | 574 ScheduleDeleteOldTabContents(old_tab_contents, NULL); |
452 } | 575 } |
453 | 576 |
454 // TODO(cbentzel): Should prerender_contents move to the pending delete | 577 // TODO(cbentzel): Should prerender_contents move to the pending delete |
455 // list, instead of deleting directly here? | 578 // list, instead of deleting directly here? |
456 AddToHistory(prerender_contents.get()); | 579 AddToHistory(prerender_contents.get()); |
457 RecordNavigation(url); | 580 RecordNavigation(url); |
581 DVLOG(3) << "... success!"; | |
458 return true; | 582 return true; |
459 } | 583 } |
460 | 584 |
461 void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, | 585 void PrerenderManager::MoveEntryToPendingDelete(WeakPrerenderHandle prerender, |
462 FinalStatus final_status) { | 586 FinalStatus final_status) { |
587 if (!prerender) | |
588 return; | |
463 DCHECK(CalledOnValidThread()); | 589 DCHECK(CalledOnValidThread()); |
464 DCHECK(entry); | 590 |
465 // Confirm this entry has not already been moved to the pending delete list. | 591 DVLOG(2) << "MoveEntryToPendingDelete"; |
592 DVLOG(2) << "... final_status = \"" << NameFromFinalStatus(final_status) << "\ ""; | |
593 | |
594 PrerenderContentsData* contents_data = | |
595 PrerenderContentsData::FromPrerenderHandle(prerender.get()); | |
596 DVLOG(3) << "... contents()->prerender_url() = " | |
597 << contents_data->contents()->prerender_url().spec(); | |
598 DCHECK(contents_data->contents()); | |
599 // Confirm this prerender has not already been moved to the pending delete | |
600 // list. | |
466 DCHECK_EQ(0, std::count(pending_delete_list_.begin(), | 601 DCHECK_EQ(0, std::count(pending_delete_list_.begin(), |
467 pending_delete_list_.end(), entry)); | 602 pending_delete_list_.end(), |
603 contents_data->contents())); | |
468 | 604 |
469 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); | 605 bool swapped_in_dummy_replacement = false; |
470 it != prerender_list_.end(); | |
471 ++it) { | |
472 if (it->contents_ == entry) { | |
473 bool swapped_in_dummy_replacement = false; | |
474 | 606 |
475 // If this PrerenderContents is being deleted due to a cancellation, | 607 // If this PrerenderContents is being deleted due to a cancellation, |
476 // we need to create a dummy replacement for PPLT accounting purposes | 608 // we need to create a dummy replacement for PPLT accounting purposes |
477 // for the Match Complete group. | 609 // for the Match Complete group. |
478 // This is the case if the cancellation is for any reason that would not | 610 // This is the case if the cancellation is for any reason that would not |
479 // occur in the control group case. | 611 // occur in the control group case. |
480 if (entry->match_complete_status() == | 612 if (contents_data->contents()->match_complete_status() == |
dominich
2012/06/18 15:32:44
If you cache contents_data->contents() in a const
gavinp
2012/06/18 16:40:48
Yes on the minimized changes, but probably not so
| |
481 PrerenderContents::MATCH_COMPLETE_DEFAULT && | 613 PrerenderContents::MATCH_COMPLETE_DEFAULT && |
482 NeedMatchCompleteDummyForFinalStatus(final_status) && | 614 NeedMatchCompleteDummyForFinalStatus(final_status) && |
483 ActuallyPrerendering()) { | 615 ActuallyPrerendering()) { |
484 // TODO(tburkard): I'd like to DCHECK that we are actually prerendering. | 616 DVLOG(8) << "swapping!"; |
485 // However, what if new conditions are added and | 617 // TODO(tburkard): I'd like to DCHECK that we are actually prerendering. |
486 // NeedMatchCompleteDummyForFinalStatus, is not being updated. Not sure | 618 // However, what if new conditions are added and |
487 // what's the best thing to do here. For now, I will just check whether | 619 // NeedMatchCompleteDummyForFinalStatus, is not being updated. Not sure |
488 // we are actually prerendering. | 620 // what's the best thing to do here. For now, I will just check whether |
489 entry->set_match_complete_status( | 621 // we are actually prerendering. |
490 PrerenderContents::MATCH_COMPLETE_REPLACED); | 622 contents_data->contents()->set_match_complete_status( |
491 if (PrerenderContents* dummy_replacement_prerender_contents = | 623 PrerenderContents::MATCH_COMPLETE_REPLACED); |
492 CreatePrerenderContents(entry->prerender_url(), | 624 if (PrerenderContents* dummy_replacement_prerender_contents = |
493 entry->referrer(), | 625 CreatePrerenderContents( |
494 entry->origin(), | 626 contents_data->contents()->prerender_url(), |
495 entry->experiment_id())) { | 627 contents_data->contents()->referrer(), |
496 dummy_replacement_prerender_contents->set_match_complete_status( | 628 contents_data->contents()->origin(), |
497 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING); | 629 contents_data->contents()->experiment_id())) { |
498 if (!dummy_replacement_prerender_contents->Init()) | 630 DVLOG(8) << "really swapping"; |
499 break; | 631 dummy_replacement_prerender_contents->set_match_complete_status( |
500 dummy_replacement_prerender_contents-> | 632 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING); |
501 AddAliasURLsFromOtherPrerenderContents(entry); | 633 if (dummy_replacement_prerender_contents->Init(prerender)) { |
502 dummy_replacement_prerender_contents->set_match_complete_status( | 634 DVLOG(8) << "i totally am going to swap"; |
503 PrerenderContents::MATCH_COMPLETE_REPLACEMENT); | 635 dummy_replacement_prerender_contents-> |
504 it->contents_ = dummy_replacement_prerender_contents; | 636 AddAliasURLsFromOtherPrerenderContents(contents_data->contents()); |
505 swapped_in_dummy_replacement = true; | 637 dummy_replacement_prerender_contents->set_match_complete_status( |
506 } | 638 PrerenderContents::MATCH_COMPLETE_REPLACEMENT); |
639 contents_data->set_contents(dummy_replacement_prerender_contents); | |
640 swapped_in_dummy_replacement = true; | |
507 } | 641 } |
508 if (!swapped_in_dummy_replacement) | |
509 prerender_list_.erase(it); | |
510 break; | |
511 } | 642 } |
512 } | 643 } |
513 AddToHistory(entry); | 644 if (!swapped_in_dummy_replacement) { |
514 pending_delete_list_.push_back(entry); | 645 DCHECK_EQ(1, std::count(prerender_list_.begin(), prerender_list_.end(), |
646 contents_data)); | |
647 prerender_list_.erase( | |
648 std::find(prerender_list_.begin(), prerender_list_.end(), | |
649 contents_data)); | |
650 } | |
651 | |
652 AddToHistory(contents_data->contents()); | |
653 pending_delete_list_.push_back(contents_data->contents()); | |
654 contents_data->Destroy(); | |
515 | 655 |
516 // Destroy the old WebContents relatively promptly to reduce resource usage, | 656 // Destroy the old WebContents relatively promptly to reduce resource usage, |
517 // and in the case of HTML5 media, reduce the change of playing any sound. | 657 // and in the case of HTML5 media, reduce the change of playing any sound. |
518 PostCleanupTask(); | 658 PostCleanupTask(); |
519 } | 659 } |
520 | 660 |
521 // static | 661 // static |
522 void PrerenderManager::RecordPerceivedPageLoadTime( | 662 void PrerenderManager::RecordPerceivedPageLoadTime( |
523 base::TimeDelta perceived_page_load_time, | 663 base::TimeDelta perceived_page_load_time, |
524 double fraction_plt_elapsed_at_swap_in, | 664 double fraction_plt_elapsed_at_swap_in, |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
616 bool PrerenderManager::IsNoUseGroup() { | 756 bool PrerenderManager::IsNoUseGroup() { |
617 return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; | 757 return GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP; |
618 } | 758 } |
619 | 759 |
620 bool PrerenderManager::IsWebContentsPrerendering( | 760 bool PrerenderManager::IsWebContentsPrerendering( |
621 WebContents* web_contents) const { | 761 WebContents* web_contents) const { |
622 DCHECK(CalledOnValidThread()); | 762 DCHECK(CalledOnValidThread()); |
623 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); | 763 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
624 it != prerender_list_.end(); | 764 it != prerender_list_.end(); |
625 ++it) { | 765 ++it) { |
626 TabContents* prerender_tab_contents = it->contents_->prerender_contents(); | 766 const PrerenderContentsData* contents_data = *it; |
627 if (prerender_tab_contents && | 767 if (!contents_data) continue; |
628 prerender_tab_contents->web_contents() == web_contents) { | 768 TabContents* prerender_tab_contents = |
769 contents_data->contents()->prerender_contents(); | |
770 if (!prerender_tab_contents) continue; | |
771 if (prerender_tab_contents->web_contents() == web_contents) | |
629 return true; | 772 return true; |
630 } | |
631 } | 773 } |
632 | 774 |
633 // Also look through the pending-deletion list. | 775 // Also look through the pending-deletion list. |
634 for (std::list<PrerenderContents*>::const_iterator it = | 776 for (std::list<PrerenderContents*>::const_iterator it = |
635 pending_delete_list_.begin(); | 777 pending_delete_list_.begin(); |
636 it != pending_delete_list_.end(); | 778 it != pending_delete_list_.end(); |
637 ++it) { | 779 ++it) { |
638 TabContents* prerender_tab_contents = (*it)->prerender_contents(); | 780 TabContents* prerender_tab_contents = (*it)->prerender_contents(); |
639 if (prerender_tab_contents && | 781 if (prerender_tab_contents && |
640 prerender_tab_contents->web_contents() == web_contents) | 782 prerender_tab_contents->web_contents() == web_contents) |
641 return true; | 783 return true; |
642 } | 784 } |
643 | 785 |
644 return false; | 786 return false; |
645 } | 787 } |
646 | 788 |
647 bool PrerenderManager::DidPrerenderFinishLoading(const GURL& url) const { | |
648 DCHECK(CalledOnValidThread()); | |
649 PrerenderContents* contents = FindEntry(url); | |
650 return contents ? contents->has_finished_loading() : false; | |
651 } | |
652 | |
653 void PrerenderManager::MarkWebContentsAsPrerendered(WebContents* web_contents) { | 789 void PrerenderManager::MarkWebContentsAsPrerendered(WebContents* web_contents) { |
654 DCHECK(CalledOnValidThread()); | 790 DCHECK(CalledOnValidThread()); |
655 prerendered_tab_contents_set_.insert(web_contents); | 791 prerendered_tab_contents_set_.insert(web_contents); |
656 } | 792 } |
657 | 793 |
658 void PrerenderManager::MarkWebContentsAsWouldBePrerendered( | 794 void PrerenderManager::MarkWebContentsAsWouldBePrerendered( |
659 WebContents* web_contents) { | 795 WebContents* web_contents) { |
660 DCHECK(CalledOnValidThread()); | 796 DCHECK(CalledOnValidThread()); |
661 would_be_prerendered_map_[web_contents] = true; | 797 would_be_prerendered_map_[web_contents] = true; |
662 } | 798 } |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
758 histograms_->RecordFinalStatus(origin, | 894 histograms_->RecordFinalStatus(origin, |
759 experiment_id, | 895 experiment_id, |
760 mc_status, | 896 mc_status, |
761 final_status); | 897 final_status); |
762 } | 898 } |
763 | 899 |
764 void PrerenderManager::AddCondition(const PrerenderCondition* condition) { | 900 void PrerenderManager::AddCondition(const PrerenderCondition* condition) { |
765 prerender_conditions_.push_back(condition); | 901 prerender_conditions_.push_back(condition); |
766 } | 902 } |
767 | 903 |
768 bool PrerenderManager::IsPendingEntry(const GURL& url) const { | |
769 DCHECK(CalledOnValidThread()); | |
770 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); | |
771 it != prerender_list_.end(); | |
772 ++it) { | |
773 if (it->contents_->IsPendingEntry(url)) | |
774 return true; | |
775 } | |
776 return false; | |
777 } | |
778 | |
779 bool PrerenderManager::IsPrerendering(const GURL& url) const { | |
780 DCHECK(CalledOnValidThread()); | |
781 return (FindEntry(url) != NULL); | |
782 } | |
783 | |
784 void PrerenderManager::RecordNavigation(const GURL& url) { | 904 void PrerenderManager::RecordNavigation(const GURL& url) { |
785 DCHECK(CalledOnValidThread()); | 905 DCHECK(CalledOnValidThread()); |
786 | 906 |
787 navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks())); | 907 navigations_.push_back(NavigationRecord(url, GetCurrentTimeTicks())); |
788 CleanUpOldNavigations(); | 908 CleanUpOldNavigations(); |
789 } | 909 } |
790 | 910 |
791 // protected | 911 // protected |
792 void PrerenderManager::SetPrerenderContentsFactory( | 912 void PrerenderManager::SetPrerenderContentsFactory( |
793 PrerenderContents::Factory* prerender_contents_factory) { | 913 PrerenderContents::Factory* prerender_contents_factory) { |
794 DCHECK(CalledOnValidThread()); | 914 DCHECK(CalledOnValidThread()); |
795 prerender_contents_factory_.reset(prerender_contents_factory); | 915 prerender_contents_factory_.reset(prerender_contents_factory); |
796 } | 916 } |
797 | 917 |
798 void PrerenderManager::DoShutdown() { | 918 void PrerenderManager::DoShutdown() { |
799 DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN); | 919 DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN); |
800 STLDeleteElements(&prerender_conditions_); | 920 STLDeleteElements(&prerender_conditions_); |
801 on_close_tab_contents_deleters_.reset(); | 921 on_close_tab_contents_deleters_.reset(); |
802 profile_ = NULL; | 922 profile_ = NULL; |
803 } | 923 } |
804 | 924 |
805 // private | 925 const PrerenderContents* PrerenderManager::FindPrerender( |
806 bool PrerenderManager::AddPrerender( | 926 const GURL& url) const { |
927 DVLOG(4) << "PrerenderManager::FindPrerender"; | |
928 DVLOG(5) << "... prerender_list_.size() = " << prerender_list_.size(); | |
929 | |
930 for (PrerenderContentsDataList::const_iterator | |
931 it = prerender_list_.begin(), end = prerender_list_.end(); | |
932 it != end; ++it) { | |
933 if (!*it || !(*it)->contents()) continue; | |
934 if ((*it)->contents()->Matches(url, NULL)) | |
dominich
2012/06/18 15:32:44
nit: if (*it && (*it)->contents() && (*it)->conten
| |
935 return (*it)->contents(); | |
936 } | |
937 return NULL; | |
938 } | |
939 | |
940 PrerenderContents* PrerenderManager::ClaimPrerender( | |
941 content::WebContents* web_contents, | |
942 const GURL& url, | |
943 content::SessionStorageNamespace* session_storage_namespace) { | |
944 for (PrerenderContentsDataList::iterator | |
945 it = prerender_list_.begin(), end = prerender_list_.end(); | |
946 it != end; ++it) { | |
947 if (!*it || !(*it)->contents()) continue; | |
948 PrerenderContents* contents = (*it)->contents(); | |
949 if (contents->Matches(url, session_storage_namespace) && | |
950 !IsNoSwapInExperiment(contents->experiment_id()) && | |
951 contents->prerender_contents()->web_contents() != web_contents) { | |
952 (*it)->Destroy(); | |
953 prerender_list_.erase(it); | |
954 return contents; | |
955 } | |
956 } | |
957 return NULL; | |
958 } | |
959 | |
960 PrerenderHandle PrerenderManager::AddPrerender( | |
807 Origin origin, | 961 Origin origin, |
808 int process_id, | 962 int process_id, |
809 const GURL& url_arg, | 963 const GURL& url_arg, |
810 const content::Referrer& referrer, | 964 const content::Referrer& referrer, |
811 const gfx::Size& size, | 965 const gfx::Size& size, |
812 SessionStorageNamespace* session_storage_namespace) { | 966 SessionStorageNamespace* session_storage_namespace) { |
967 DVLOG(4) << "PrerenderManager::AddPrerender"; | |
813 DCHECK(CalledOnValidThread()); | 968 DCHECK(CalledOnValidThread()); |
814 | 969 |
815 if (!IsEnabled()) | 970 if (!IsEnabled()) |
816 return false; | 971 return PrerenderHandle(); |
817 | 972 |
818 if (origin == ORIGIN_LINK_REL_PRERENDER && | 973 if (origin == ORIGIN_LINK_REL_PRERENDER && |
819 IsGoogleSearchResultURL(referrer.url)) { | 974 IsGoogleSearchResultURL(referrer.url)) { |
820 origin = ORIGIN_GWS_PRERENDER; | 975 origin = ORIGIN_GWS_PRERENDER; |
821 } | 976 } |
822 | 977 |
823 DeleteOldEntries(); | 978 DeleteOldEntries(); |
824 DeletePendingDeleteEntries(); | 979 DeletePendingDeleteEntries(); |
825 | 980 |
826 GURL url = url_arg; | 981 GURL url = url_arg; |
827 GURL alias_url; | 982 GURL alias_url; |
828 uint8 experiment = GetQueryStringBasedExperiment(url_arg); | 983 uint8 experiment = GetQueryStringBasedExperiment(url_arg); |
829 bool control_group_behavior = | 984 bool control_group_behavior = |
830 IsControlGroup() || IsControlGroupExperiment(experiment); | 985 IsControlGroup() || IsControlGroupExperiment(experiment); |
831 if (control_group_behavior && | 986 if (control_group_behavior && |
832 MaybeGetQueryStringBasedAliasURL(url, &alias_url)) { | 987 MaybeGetQueryStringBasedAliasURL(url, &alias_url)) { |
833 url = alias_url; | 988 url = alias_url; |
834 } | 989 } |
835 | 990 |
836 // From here on, we will record a FinalStatus so we need to register with the | 991 // From here on, we will record a FinalStatus so we need to register with the |
837 // histogram tracking. | 992 // histogram tracking. |
838 histograms_->RecordPrerender(origin, url_arg); | 993 histograms_->RecordPrerender(origin, url_arg); |
839 | 994 |
840 if (PrerenderContentsData* prerender_contents_data = FindEntryData(url)) { | 995 PrerenderContentsDataList::const_iterator duplicate = |
841 ++prerender_contents_data->active_count_; | 996 std::find_if(prerender_list_.begin(), prerender_list_.end(), |
997 PrerenderContentsData::MatchPredicate( | |
998 url, session_storage_namespace)); | |
999 if (duplicate != prerender_list_.end()) { | |
842 RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE); | 1000 RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE); |
843 return true; | 1001 return PrerenderHandle(duplicate->get()); |
844 } | 1002 } |
845 | 1003 |
846 // Do not prerender if there are too many render processes, and we would | 1004 // Do not prerender if there are too many render processes, and we would |
847 // have to use an existing one. We do not want prerendering to happen in | 1005 // have to use an existing one. We do not want prerendering to happen in |
848 // a shared process, so that we can always reliably lower the CPU | 1006 // a shared process, so that we can always reliably lower the CPU |
849 // priority for prerendering. | 1007 // priority for prerendering. |
850 // In single-process mode, ShouldTryToUseExistingProcessHost() always returns | 1008 // In single-process mode, ShouldTryToUseExistingProcessHost() always returns |
851 // true, so that case needs to be explicitly checked for. | 1009 // true, so that case needs to be explicitly checked for. |
852 // TODO(tburkard): Figure out how to cancel prerendering in the opposite | 1010 // TODO(tburkard): Figure out how to cancel prerendering in the opposite |
853 // case, when a new tab is added to a process used for prerendering. | 1011 // case, when a new tab is added to a process used for prerendering. |
854 // On Android we do reuse processes as we have a limited number of them and we | 1012 // On Android we do reuse processes as we have a limited number of them and we |
855 // still want the benefits of prerendering even when several tabs are open. | 1013 // still want the benefits of prerendering even when several tabs are open. |
856 #if !defined(OS_ANDROID) | 1014 #if !defined(OS_ANDROID) |
857 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost( | 1015 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost( |
858 profile_, url) && | 1016 profile_, url) && |
859 !content::RenderProcessHost::run_renderer_in_process()) { | 1017 !content::RenderProcessHost::run_renderer_in_process()) { |
860 RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); | 1018 RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); |
861 return false; | 1019 return PrerenderHandle(); |
862 } | 1020 } |
863 #endif | 1021 #endif |
864 | 1022 |
865 // Check if enough time has passed since the last prerender. | 1023 // Check if enough time has passed since the last prerender. |
866 if (!DoesRateLimitAllowPrerender()) { | 1024 if (!DoesRateLimitAllowPrerender()) { |
867 // Cancel the prerender. We could add it to the pending prerender list but | 1025 // Cancel the prerender. We could add it to the pending prerender list but |
868 // this doesn't make sense as the next prerender request will be triggered | 1026 // this doesn't make sense as the next prerender request will be triggered |
869 // by a navigation and is unlikely to be the same site. | 1027 // by a navigation and is unlikely to be the same site. |
870 RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); | 1028 RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED); |
871 return false; | 1029 return PrerenderHandle(); |
872 } | 1030 } |
873 | 1031 |
874 PrerenderContents* prerender_contents = CreatePrerenderContents( | 1032 PrerenderContents* prerender_contents = CreatePrerenderContents( |
875 url, referrer, origin, experiment); | 1033 url, referrer, origin, experiment); |
876 if (!prerender_contents || !prerender_contents->Init()) | 1034 |
877 return false; | 1035 // TODO(cbentzel): Move invalid checks here instead of PrerenderContents? |
1036 PrerenderContentsData* data; | |
dominich
2012/06/18 15:32:44
PrerenderContentsData* data =
new PrerenderCon
| |
1037 PrerenderHandle handle( | |
1038 data = new PrerenderContentsData(this, prerender_contents)); | |
1039 | |
1040 DVLOG(5) << "handle initial ref_count_ = " << handle->ref_count_; | |
1041 | |
1042 if (!prerender_contents || !prerender_contents->Init(handle->AsWeakPtr())) | |
1043 return PrerenderHandle(); | |
878 | 1044 |
879 histograms_->RecordPrerenderStarted(origin); | 1045 histograms_->RecordPrerenderStarted(origin); |
880 | 1046 |
881 // TODO(cbentzel): Move invalid checks here instead of PrerenderContents? | 1047 prerender_list_.push_back(data->AsWeakPtr()); |
882 PrerenderContentsData data(prerender_contents, GetCurrentTime()); | |
883 | |
884 prerender_list_.push_back(data); | |
885 | 1048 |
886 last_prerender_start_time_ = GetCurrentTimeTicks(); | 1049 last_prerender_start_time_ = GetCurrentTimeTicks(); |
887 | 1050 |
888 data.contents_->StartPrerendering(process_id, size, session_storage_namespace, | 1051 data->contents()->StartPrerendering( |
889 control_group_behavior); | 1052 process_id, size, session_storage_namespace, control_group_behavior); |
1053 | |
1054 // Compact the list of prerenders by removing all of the deleted | |
1055 // PrerenderContentsData objects, so that the size() will equal the | |
1056 // number of running prerenders. | |
1057 prerender_list_.erase( | |
1058 std::remove_if(prerender_list_.begin(), prerender_list_.end(), | |
1059 PrerenderContentsData::DeletedPredicate()), | |
1060 prerender_list_.end()); | |
890 | 1061 |
891 while (prerender_list_.size() > config_.max_elements) { | 1062 while (prerender_list_.size() > config_.max_elements) { |
892 data = prerender_list_.front(); | 1063 data = prerender_list_.front(); |
893 prerender_list_.pop_front(); | 1064 prerender_list_.pop_front(); |
894 data.contents_->Destroy(FINAL_STATUS_EVICTED); | 1065 data->contents()->Destroy(FINAL_STATUS_EVICTED); |
895 } | 1066 } |
896 StartSchedulingPeriodicCleanups(); | 1067 StartSchedulingPeriodicCleanups(); |
897 return true; | 1068 DVLOG(5) << "on exit ref_count_ = " << handle->ref_count_; |
898 } | 1069 return handle; |
899 | |
900 PrerenderContents* PrerenderManager::GetEntry(const GURL& url) { | |
901 return GetEntryButNotSpecifiedWC(url, NULL); | |
902 } | |
903 | |
904 PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC( | |
905 const GURL& url, | |
906 WebContents* wc) { | |
907 DCHECK(CalledOnValidThread()); | |
908 DeleteOldEntries(); | |
909 DeletePendingDeleteEntries(); | |
910 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); | |
911 it != prerender_list_.end(); | |
912 ++it) { | |
913 PrerenderContents* prerender_contents = it->contents_; | |
914 if (prerender_contents->MatchesURL(url, NULL) && | |
915 !IsNoSwapInExperiment(prerender_contents->experiment_id())) { | |
916 if (!prerender_contents->prerender_contents() || | |
917 !wc || | |
918 prerender_contents->prerender_contents()->web_contents() != wc) { | |
919 prerender_list_.erase(it); | |
920 return prerender_contents; | |
921 } | |
922 } | |
923 } | |
924 // Entry not found. | |
925 return NULL; | |
926 } | 1070 } |
927 | 1071 |
928 void PrerenderManager::StartSchedulingPeriodicCleanups() { | 1072 void PrerenderManager::StartSchedulingPeriodicCleanups() { |
929 DCHECK(CalledOnValidThread()); | 1073 DCHECK(CalledOnValidThread()); |
930 if (repeating_timer_.IsRunning()) | 1074 if (repeating_timer_.IsRunning()) |
931 return; | 1075 return; |
932 repeating_timer_.Start(FROM_HERE, | 1076 repeating_timer_.Start(FROM_HERE, |
933 base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs), | 1077 base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs), |
934 this, | 1078 this, |
935 &PrerenderManager::PeriodicCleanup); | 1079 &PrerenderManager::PeriodicCleanup); |
936 } | 1080 } |
937 | 1081 |
938 void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() { | 1082 void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() { |
939 if (!prerender_list_.empty()) | 1083 if (!prerender_list_.empty()) |
940 return; | 1084 return; |
941 | 1085 |
942 DCHECK(CalledOnValidThread()); | 1086 DCHECK(CalledOnValidThread()); |
943 repeating_timer_.Stop(); | 1087 repeating_timer_.Stop(); |
944 } | 1088 } |
945 | 1089 |
946 void PrerenderManager::PeriodicCleanup() { | 1090 void PrerenderManager::PeriodicCleanup() { |
947 DCHECK(CalledOnValidThread()); | 1091 DCHECK(CalledOnValidThread()); |
948 DeleteOldTabContents(); | 1092 DeleteOldTabContents(); |
949 DeleteOldEntries(); | 1093 DeleteOldEntries(); |
950 | 1094 |
951 // Grab a copy of the current PrerenderContents pointers, so that we | 1095 // Grab a copy of the current PrerenderContents pointers, so that we |
952 // will not interfere with potential deletions of the list. | 1096 // will not interfere with potential deletions of the list. |
953 std::vector<PrerenderContents*> prerender_contents; | 1097 typedef std::vector<PrerenderContents*> PrerenderContentsVector; |
1098 PrerenderContentsVector prerender_contents; | |
1099 prerender_contents.reserve(prerender_list_.size()); | |
954 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); | 1100 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); |
955 it != prerender_list_.end(); | 1101 it != prerender_list_.end(); |
956 ++it) { | 1102 ++it) { |
957 DCHECK(it->contents_); | 1103 if (!*it || !(*it)->contents()) continue; |
958 prerender_contents.push_back(it->contents_); | 1104 prerender_contents.push_back((*it)->contents()); |
959 } | 1105 } |
960 for (std::vector<PrerenderContents*>::iterator it = | 1106 for (PrerenderContentsVector::iterator it = prerender_contents.begin(); |
961 prerender_contents.begin(); | 1107 it != prerender_contents.end(); ++it) { |
962 it != prerender_contents.end(); | |
963 ++it) { | |
964 (*it)->DestroyWhenUsingTooManyResources(); | 1108 (*it)->DestroyWhenUsingTooManyResources(); |
965 } | 1109 } |
966 | 1110 |
967 DeletePendingDeleteEntries(); | 1111 DeletePendingDeleteEntries(); |
968 } | 1112 } |
969 | 1113 |
970 void PrerenderManager::PostCleanupTask() { | 1114 void PrerenderManager::PostCleanupTask() { |
971 DCHECK(CalledOnValidThread()); | 1115 DCHECK(CalledOnValidThread()); |
972 MessageLoop::current()->PostTask( | 1116 MessageLoop::current()->PostTask( |
973 FROM_HERE, | 1117 FROM_HERE, |
974 base::Bind(&PrerenderManager::PeriodicCleanup, | 1118 base::Bind(&PrerenderManager::PeriodicCleanup, |
975 weak_factory_.GetWeakPtr())); | 1119 weak_factory_.GetWeakPtr())); |
976 } | 1120 } |
977 | 1121 |
978 base::TimeDelta PrerenderManager::GetMaxAge() const { | 1122 base::TimeDelta PrerenderManager::GetMaxAge() const { |
979 return (GetMode() == PRERENDER_MODE_EXPERIMENT_5MIN_TTL_GROUP ? | 1123 return (GetMode() == PRERENDER_MODE_EXPERIMENT_5MIN_TTL_GROUP ? |
980 base::TimeDelta::FromSeconds(300) : config_.max_age); | 1124 base::TimeDelta::FromSeconds(300) : config_.max_age); |
981 } | 1125 } |
982 | 1126 |
983 bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const { | |
984 DCHECK(CalledOnValidThread()); | |
985 base::Time now = GetCurrentTime(); | |
986 return (now - start < GetMaxAge()); | |
987 } | |
988 | |
989 void PrerenderManager::DeleteOldEntries() { | 1127 void PrerenderManager::DeleteOldEntries() { |
990 DCHECK(CalledOnValidThread()); | 1128 DCHECK(CalledOnValidThread()); |
991 while (!prerender_list_.empty()) { | 1129 PrerenderContentsData::ExpiredPredicate expired_predicate(GetCurrentTime()); |
dominich
2012/06/18 15:32:44
this code is longer and more complicated than the
gavinp
2012/06/18 16:40:48
Yes. I hate the predicates too.
| |
992 PrerenderContentsData data = prerender_list_.front(); | 1130 |
993 if (IsPrerenderElementFresh(data.start_time_)) | 1131 PrerenderContentsDataList::iterator it = |
994 return; | 1132 std::find_if(prerender_list_.begin(), prerender_list_.end(), |
995 data.contents_->Destroy(FINAL_STATUS_TIMED_OUT); | 1133 expired_predicate); |
1134 while (it != prerender_list_.end()) { | |
1135 (*it)->contents()->Destroy(FINAL_STATUS_TIMED_OUT); | |
1136 | |
1137 ++it; | |
1138 it = std::find_if(it, prerender_list_.end(), expired_predicate); | |
996 } | 1139 } |
1140 | |
997 MaybeStopSchedulingPeriodicCleanups(); | 1141 MaybeStopSchedulingPeriodicCleanups(); |
998 } | 1142 } |
999 | 1143 |
1144 // virtual (to allow testing) | |
1000 base::Time PrerenderManager::GetCurrentTime() const { | 1145 base::Time PrerenderManager::GetCurrentTime() const { |
1001 return base::Time::Now(); | 1146 return base::Time::Now(); |
1002 } | 1147 } |
1003 | 1148 |
1149 // virtual (to allow testing) | |
1004 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const { | 1150 base::TimeTicks PrerenderManager::GetCurrentTimeTicks() const { |
1005 return base::TimeTicks::Now(); | 1151 return base::TimeTicks::Now(); |
1006 } | 1152 } |
1007 | 1153 |
1154 // virtual (to allow testing) | |
1008 PrerenderContents* PrerenderManager::CreatePrerenderContents( | 1155 PrerenderContents* PrerenderManager::CreatePrerenderContents( |
1009 const GURL& url, | 1156 const GURL& url, |
1010 const content::Referrer& referrer, | 1157 const content::Referrer& referrer, |
1011 Origin origin, | 1158 Origin origin, |
1012 uint8 experiment_id) { | 1159 uint8 experiment_id) { |
1013 DCHECK(CalledOnValidThread()); | 1160 DCHECK(CalledOnValidThread()); |
1014 return prerender_contents_factory_->CreatePrerenderContents( | 1161 return prerender_contents_factory_->CreatePrerenderContents( |
1015 this, prerender_tracker_, profile_, url, | 1162 this, prerender_tracker_, profile_, url, |
1016 referrer, origin, experiment_id); | 1163 referrer, origin, experiment_id); |
1017 } | 1164 } |
1018 | 1165 |
1019 void PrerenderManager::DeletePendingDeleteEntries() { | 1166 void PrerenderManager::DeletePendingDeleteEntries() { |
1020 while (!pending_delete_list_.empty()) { | 1167 while (!pending_delete_list_.empty()) { |
1021 PrerenderContents* contents = pending_delete_list_.front(); | 1168 PrerenderContents* contents = pending_delete_list_.front(); |
1022 pending_delete_list_.pop_front(); | 1169 pending_delete_list_.pop_front(); |
1023 delete contents; | 1170 delete contents; |
1024 } | 1171 } |
1025 } | 1172 } |
1026 | 1173 |
1027 PrerenderManager::PrerenderContentsData* PrerenderManager::FindEntryData( | 1174 const PrerenderManager::PrerenderContentsData* |
1028 const GURL& url) { | 1175 PrerenderManager::FindContentsData(PrerenderHandle handle) const { |
1029 DCHECK(CalledOnValidThread()); | 1176 DCHECK(CalledOnValidThread()); |
1030 PrerenderContentsDataList::iterator it = FindPrerenderContentsForURL(url); | 1177 // Confirm this handle occurs at most once in our prerender list. |
1178 DCHECK_GE(1, std::count_if(prerender_list_.begin(), prerender_list_.end(), | |
dominich
2012/06/18 15:32:44
pointless test - find_if would return prerender_li
| |
1179 PrerenderContentsData::EqualsPredicate(handle))); | |
1180 PrerenderContentsDataList::const_iterator it = | |
1181 std::find_if(prerender_list_.begin(), prerender_list_.end(), | |
1182 PrerenderContentsData::EqualsPredicate(handle)); | |
1031 if (it == prerender_list_.end()) | 1183 if (it == prerender_list_.end()) |
1032 return NULL; | 1184 return NULL; |
1033 PrerenderContentsData& prerender_contents_data = *it; | 1185 // Make sure this WeakPtr is still dereferencable. This only needs to be a |
1034 return &prerender_contents_data; | 1186 // DCHECK because the in-scope PrerenderHandle should hold a ref which keeps |
1187 // this WeakPtr as being valid. | |
1188 DCHECK(*it); | |
1189 | |
1190 return *it; | |
1035 } | 1191 } |
1036 | 1192 |
1037 PrerenderContents* PrerenderManager::FindEntry(const GURL& url) const { | 1193 PrerenderManager::PrerenderContentsData* |
1038 DCHECK(CalledOnValidThread()); | 1194 PrerenderManager::FindContentsDataMutable(PrerenderHandle handle) { |
1039 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); | 1195 return const_cast<PrerenderManager::PrerenderContentsData*>( |
dominich
2012/06/18 15:32:44
Code smell: const_cast?
| |
1040 it != prerender_list_.end(); | 1196 FindContentsData(handle)); |
1041 ++it) { | 1197 } |
1042 if (it->contents_->MatchesURL(url, NULL)) | 1198 |
1043 return it->contents_; | 1199 PrerenderManager::PrerenderContentsData* |
1200 PrerenderManager::FindContentsDataForChildAndRouteId(const int child_id, | |
1201 const int route_id) { | |
1202 for (PrerenderContentsDataList::iterator | |
1203 it = prerender_list_.begin(), end = prerender_list_.end(); | |
1204 it != end; ++it) { | |
1205 if (!*it) continue; | |
dominich
2012/06/18 15:32:44
nit:
int contents_child_id, contents_route_id;
if
| |
1206 int contents_child_id; | |
1207 if (!(*it)->contents()->GetChildId(&contents_child_id)) continue; | |
1208 int contents_route_id; | |
1209 if (!(*it)->contents()->GetRouteId(&contents_route_id)) continue; | |
1210 if (child_id == contents_child_id && route_id == contents_route_id) { | |
1211 return *it; | |
1212 } | |
1044 } | 1213 } |
1045 // Entry not found. | |
1046 return NULL; | 1214 return NULL; |
1047 } | 1215 } |
1048 | 1216 |
1049 PrerenderManager::PrerenderContentsDataList::iterator | |
1050 PrerenderManager::FindPrerenderContentsForChildRouteIdPair( | |
1051 const std::pair<int, int>& child_route_id_pair) { | |
1052 PrerenderContentsDataList::iterator it = prerender_list_.begin(); | |
1053 for (; it != prerender_list_.end(); ++it) { | |
1054 PrerenderContents* prerender_contents = it->contents_; | |
1055 | |
1056 int child_id; | |
1057 int route_id; | |
1058 bool has_child_id = prerender_contents->GetChildId(&child_id); | |
1059 bool has_route_id = has_child_id && | |
1060 prerender_contents->GetRouteId(&route_id); | |
1061 | |
1062 if (has_child_id && has_route_id && | |
1063 child_id == child_route_id_pair.first && | |
1064 route_id == child_route_id_pair.second) { | |
1065 break; | |
1066 } | |
1067 } | |
1068 return it; | |
1069 } | |
1070 | |
1071 PrerenderManager::PrerenderContentsDataList::iterator | |
1072 PrerenderManager::FindPrerenderContentsForURL(const GURL& url) { | |
1073 for (PrerenderContentsDataList::iterator it = prerender_list_.begin(); | |
1074 it != prerender_list_.end(); ++it) { | |
1075 if (it->contents_->MatchesURL(url, NULL)) | |
1076 return it; | |
1077 } | |
1078 return prerender_list_.end(); | |
1079 } | |
1080 | |
1081 bool PrerenderManager::DoesRateLimitAllowPrerender() const { | 1217 bool PrerenderManager::DoesRateLimitAllowPrerender() const { |
1082 DCHECK(CalledOnValidThread()); | 1218 DCHECK(CalledOnValidThread()); |
1083 base::TimeDelta elapsed_time = | 1219 base::TimeDelta elapsed_time = |
1084 GetCurrentTimeTicks() - last_prerender_start_time_; | 1220 GetCurrentTimeTicks() - last_prerender_start_time_; |
1085 histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time); | 1221 histograms_->RecordTimeBetweenPrerenderRequests(elapsed_time); |
1086 if (!config_.rate_limit_enabled) | 1222 if (!config_.rate_limit_enabled) |
1087 return true; | 1223 return true; |
1088 return elapsed_time > | 1224 return elapsed_time > |
1089 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); | 1225 base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); |
1090 } | 1226 } |
(...skipping 30 matching lines...) Expand all Loading... | |
1121 ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find( | 1257 ScopedVector<OnCloseTabContentsDeleter>::iterator i = std::find( |
1122 on_close_tab_contents_deleters_.begin(), | 1258 on_close_tab_contents_deleters_.begin(), |
1123 on_close_tab_contents_deleters_.end(), | 1259 on_close_tab_contents_deleters_.end(), |
1124 deleter); | 1260 deleter); |
1125 DCHECK(i != on_close_tab_contents_deleters_.end()); | 1261 DCHECK(i != on_close_tab_contents_deleters_.end()); |
1126 on_close_tab_contents_deleters_.erase(i); | 1262 on_close_tab_contents_deleters_.erase(i); |
1127 } | 1263 } |
1128 } | 1264 } |
1129 | 1265 |
1130 void PrerenderManager::AddToHistory(PrerenderContents* contents) { | 1266 void PrerenderManager::AddToHistory(PrerenderContents* contents) { |
1267 DVLOG(4) << "PrerenderManager::AddToHistory"; | |
1268 DVLOG(5) << "... contents = " << contents; | |
1269 DVLOG(5) << "... fs = " << contents->final_status(); | |
1131 PrerenderHistory::Entry entry(contents->prerender_url(), | 1270 PrerenderHistory::Entry entry(contents->prerender_url(), |
1132 contents->final_status(), | 1271 contents->final_status(), |
1133 contents->origin(), | 1272 contents->origin(), |
1134 base::Time::Now()); | 1273 base::Time::Now()); |
1135 prerender_history_->AddEntry(entry); | 1274 prerender_history_->AddEntry(entry); |
1136 } | 1275 } |
1137 | 1276 |
1138 Value* PrerenderManager::GetActivePrerendersAsValue() const { | 1277 Value* PrerenderManager::GetActivePrerendersAsValue() const { |
1139 ListValue* list_value = new ListValue(); | 1278 ListValue* list_value = new ListValue(); |
1140 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); | 1279 for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin(); |
1141 it != prerender_list_.end(); | 1280 it != prerender_list_.end(); |
1142 ++it) { | 1281 ++it) { |
1143 Value* prerender_value = it->contents_->GetAsValue(); | 1282 if (!*it || (*it)->IsStarted()) continue; |
1144 if (!prerender_value) | 1283 Value* prerender_value = (*it)->contents()->GetAsValue(); |
1145 continue; | 1284 if (!prerender_value) continue; |
1146 list_value->Append(prerender_value); | 1285 list_value->Append(prerender_value); |
1147 } | 1286 } |
1148 return list_value; | 1287 return list_value; |
1149 } | 1288 } |
1150 | 1289 |
1151 void PrerenderManager::DestroyAllContents(FinalStatus final_status) { | 1290 void PrerenderManager::DestroyAllContents(FinalStatus final_status) { |
1152 DeleteOldTabContents(); | 1291 DeleteOldTabContents(); |
1153 while (!prerender_list_.empty()) { | 1292 while (!prerender_list_.empty()) { |
1154 PrerenderContentsData data = prerender_list_.front(); | 1293 if (PrerenderContentsData* data = prerender_list_.front()) { |
1294 if (data->contents()) { | |
1295 DVLOG(4) << "destroy .. contents = " << data->contents(); | |
1296 DVLOG(4) << "fs = " << data->contents()->final_status(); | |
1297 } | |
1298 if (data->IsStarted()) | |
1299 data->contents()->Destroy(final_status); | |
dominich
2012/06/18 15:32:44
We shouldn't have a valid contents if we haven't s
| |
1300 } | |
1155 prerender_list_.pop_front(); | 1301 prerender_list_.pop_front(); |
1156 data.contents_->Destroy(final_status); | |
1157 } | 1302 } |
1158 DeletePendingDeleteEntries(); | 1303 DeletePendingDeleteEntries(); |
1159 } | 1304 } |
1160 | 1305 |
1161 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed( | 1306 void PrerenderManager::DestroyAndMarkMatchCompleteAsUsed( |
1162 PrerenderContents* prerender_contents, | 1307 PrerenderContents* prerender_contents, |
1163 FinalStatus final_status) { | 1308 FinalStatus final_status) { |
1164 prerender_contents->set_match_complete_status( | 1309 prerender_contents->set_match_complete_status( |
1165 PrerenderContents::MATCH_COMPLETE_REPLACED); | 1310 PrerenderContents::MATCH_COMPLETE_REPLACED); |
1166 histograms_->RecordFinalStatus(prerender_contents->origin(), | 1311 histograms_->RecordFinalStatus(prerender_contents->origin(), |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1205 if (!render_process_host || !render_process_host->GetBrowserContext()) | 1350 if (!render_process_host || !render_process_host->GetBrowserContext()) |
1206 return NULL; | 1351 return NULL; |
1207 Profile* profile = Profile::FromBrowserContext( | 1352 Profile* profile = Profile::FromBrowserContext( |
1208 render_process_host->GetBrowserContext()); | 1353 render_process_host->GetBrowserContext()); |
1209 if (!profile) | 1354 if (!profile) |
1210 return NULL; | 1355 return NULL; |
1211 return PrerenderManagerFactory::GetInstance()->GetForProfile(profile); | 1356 return PrerenderManagerFactory::GetInstance()->GetForProfile(profile); |
1212 } | 1357 } |
1213 | 1358 |
1214 } // namespace prerender | 1359 } // namespace prerender |
OLD | NEW |