Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_link_manager.cc
blob1f4d862b556ff95427f17e0d8dea501222fe3a40
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.
5 #include "chrome/browser/prerender/prerender_link_manager.h"
7 #include <functional>
8 #include <limits>
9 #include <set>
10 #include <string>
11 #include <utility>
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/histogram.h"
16 #include "chrome/browser/prerender/prerender_contents.h"
17 #include "chrome/browser/prerender/prerender_handle.h"
18 #include "chrome/browser/prerender/prerender_manager.h"
19 #include "chrome/browser/prerender/prerender_manager_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/prerender_messages.h"
22 #include "chrome/common/prerender_types.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/session_storage_namespace.h"
26 #include "content/public/common/referrer.h"
27 #include "ui/gfx/size.h"
28 #include "url/gurl.h"
30 using base::TimeDelta;
31 using base::TimeTicks;
32 using content::RenderViewHost;
33 using content::SessionStorageNamespace;
35 namespace prerender {
37 namespace {
39 bool ShouldStartRelNextPrerenders() {
40 const std::string experiment_name =
41 base::FieldTrialList::FindFullName("PrerenderRelNextTrial");
43 return experiment_name.find("Yes") != std::string::npos;
46 bool ShouldStartPrerender(const uint32 rel_types) {
47 const bool should_start_rel_next_prerenders =
48 ShouldStartRelNextPrerenders();
50 if (rel_types & PrerenderRelTypePrerender) {
51 return true;
52 } else if (should_start_rel_next_prerenders &&
53 (rel_types & PrerenderRelTypeNext) == PrerenderRelTypeNext) {
54 return true;
56 return false;
59 COMPILE_ASSERT(PrerenderRelTypePrerender == 0x1,
60 RelTypeHistogramEnum_must_match_PrerenderRelType);
61 COMPILE_ASSERT(PrerenderRelTypeNext == 0x2,
62 RelTypeHistogramEnum_must_match_PrerenderRelType);
63 enum RelTypeHistogramEnum {
64 RelTypeHistogramEnumNone = 0,
65 RelTypeHistogramEnumPrerender = PrerenderRelTypePrerender,
66 RelTypeHistogramEnumNext = PrerenderRelTypeNext,
67 RelTypeHistogramEnumPrerenderAndNext =
68 PrerenderRelTypePrerender | PrerenderRelTypeNext,
69 RelTypeHistogramEnumMax,
72 void RecordLinkManagerAdded(const uint32 rel_types) {
73 const uint32 enum_value = rel_types & (RelTypeHistogramEnumMax - 1);
74 UMA_HISTOGRAM_ENUMERATION("Prerender.RelTypesLinkAdded", enum_value,
75 RelTypeHistogramEnumMax);
78 void RecordLinkManagerStarting(const uint32 rel_types) {
79 const uint32 enum_value = rel_types & (RelTypeHistogramEnumMax - 1);
80 UMA_HISTOGRAM_ENUMERATION("Prerender.RelTypesLinkStarted", enum_value,
81 RelTypeHistogramEnumMax);
84 void Send(int child_id, IPC::Message* raw_message) {
85 using content::RenderProcessHost;
86 scoped_ptr<IPC::Message> own_message(raw_message);
88 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
89 if (!render_process_host)
90 return;
91 render_process_host->Send(own_message.release());
94 } // namespace
96 // Helper class to implement PrerenderContents::Observer and watch prerenders
97 // which launch other prerenders.
98 class PrerenderLinkManager::PendingPrerenderManager
99 : public PrerenderContents::Observer {
100 public:
101 explicit PendingPrerenderManager(PrerenderLinkManager* link_manager)
102 : link_manager_(link_manager) {}
104 virtual ~PendingPrerenderManager() {
105 DCHECK(observed_launchers_.empty());
106 for (std::set<PrerenderContents*>::iterator i = observed_launchers_.begin();
107 i != observed_launchers_.end(); ++i) {
108 (*i)->RemoveObserver(this);
112 void ObserveLauncher(PrerenderContents* launcher) {
113 DCHECK_EQ(FINAL_STATUS_MAX, launcher->final_status());
114 if (observed_launchers_.find(launcher) != observed_launchers_.end())
115 return;
116 observed_launchers_.insert(launcher);
117 launcher->AddObserver(this);
120 virtual void OnPrerenderStart(PrerenderContents* launcher) OVERRIDE {}
122 virtual void OnPrerenderStop(PrerenderContents* launcher) OVERRIDE {
123 observed_launchers_.erase(launcher);
124 if (launcher->final_status() == FINAL_STATUS_USED) {
125 link_manager_->StartPendingPrerendersForLauncher(launcher);
126 } else {
127 link_manager_->CancelPendingPrerendersForLauncher(launcher);
131 private:
132 // A pointer to the parent PrerenderLinkManager.
133 PrerenderLinkManager* link_manager_;
135 // The set of PrerenderContentses being observed. Lifetimes are managed by
136 // OnPrerenderStop.
137 std::set<PrerenderContents*> observed_launchers_;
140 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
141 : has_shutdown_(false),
142 manager_(manager),
143 pending_prerender_manager_(new PendingPrerenderManager(this)) {}
145 PrerenderLinkManager::~PrerenderLinkManager() {
146 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
147 i != prerenders_.end(); ++i) {
148 if (i->handle) {
149 DCHECK(!i->handle->IsPrerendering())
150 << "All running prerenders should stop at the same time as the "
151 << "PrerenderManager.";
152 delete i->handle;
153 i->handle = 0;
158 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
159 int prerender_id,
160 const GURL& url,
161 uint32 rel_types,
162 const content::Referrer& referrer,
163 const gfx::Size& size,
164 int render_view_route_id) {
165 DCHECK_EQ(static_cast<LinkPrerender*>(NULL),
166 FindByLauncherChildIdAndPrerenderId(launcher_child_id,
167 prerender_id));
168 content::RenderProcessHost* rph =
169 content::RenderProcessHost::FromID(launcher_child_id);
170 // Guests inside <webview> do not support cross-process navigation and so we
171 // do not allow guests to prerender content.
172 if (rph && rph->IsGuest())
173 return;
175 // Check if the launcher is itself an unswapped prerender.
176 PrerenderContents* prerender_contents =
177 manager_->GetPrerenderContentsForRoute(launcher_child_id,
178 render_view_route_id);
179 if (prerender_contents &&
180 prerender_contents->final_status() != FINAL_STATUS_MAX) {
181 // The launcher is a prerender about to be destroyed asynchronously, but
182 // its AddLinkRelPrerender message raced with shutdown. Ignore it.
183 DCHECK_NE(FINAL_STATUS_USED, prerender_contents->final_status());
184 return;
187 LinkPrerender
188 prerender(launcher_child_id, prerender_id, url, rel_types, referrer, size,
189 render_view_route_id, manager_->GetCurrentTimeTicks(),
190 prerender_contents);
191 prerenders_.push_back(prerender);
192 RecordLinkManagerAdded(rel_types);
193 if (prerender_contents)
194 pending_prerender_manager_->ObserveLauncher(prerender_contents);
195 else
196 StartPrerenders();
199 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
200 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
201 prerender_id);
202 if (!prerender)
203 return;
205 CancelPrerender(prerender);
206 StartPrerenders();
209 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
210 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
211 prerender_id);
212 if (!prerender)
213 return;
215 if (!prerender->handle) {
216 RemovePrerender(prerender);
217 return;
220 prerender->has_been_abandoned = true;
221 prerender->handle->OnNavigateAway();
222 DCHECK(prerender->handle);
224 // If the prerender is not running, remove it from the list so it does not
225 // leak. If it is running, it will send a cancel event when it stops which
226 // will remove it.
227 if (!prerender->handle->IsPrerendering())
228 RemovePrerender(prerender);
231 void PrerenderLinkManager::OnChannelClosing(int child_id) {
232 std::list<LinkPrerender>::iterator next = prerenders_.begin();
233 while (next != prerenders_.end()) {
234 std::list<LinkPrerender>::iterator it = next;
235 ++next;
237 if (child_id != it->launcher_child_id)
238 continue;
240 const size_t running_prerender_count = CountRunningPrerenders();
241 OnAbandonPrerender(child_id, it->prerender_id);
242 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
246 PrerenderLinkManager::LinkPrerender::LinkPrerender(
247 int launcher_child_id,
248 int prerender_id,
249 const GURL& url,
250 uint32 rel_types,
251 const content::Referrer& referrer,
252 const gfx::Size& size,
253 int render_view_route_id,
254 TimeTicks creation_time,
255 PrerenderContents* deferred_launcher)
256 : launcher_child_id(launcher_child_id),
257 prerender_id(prerender_id),
258 url(url),
259 rel_types(rel_types),
260 referrer(referrer),
261 size(size),
262 render_view_route_id(render_view_route_id),
263 creation_time(creation_time),
264 deferred_launcher(deferred_launcher),
265 handle(NULL),
266 is_match_complete_replacement(false),
267 has_been_abandoned(false) {
270 PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
271 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
272 << "The PrerenderHandle should be destroyed before its Prerender.";
275 bool PrerenderLinkManager::IsEmpty() const {
276 return prerenders_.empty();
279 size_t PrerenderLinkManager::CountRunningPrerenders() const {
280 size_t retval = 0;
281 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
282 i != prerenders_.end(); ++i) {
283 if (i->handle && i->handle->IsPrerendering())
284 ++retval;
286 return retval;
289 void PrerenderLinkManager::StartPrerenders() {
290 if (has_shutdown_)
291 return;
293 size_t total_started_prerender_count = 0;
294 std::list<LinkPrerender*> abandoned_prerenders;
295 std::list<std::list<LinkPrerender>::iterator> pending_prerenders;
296 std::multiset<std::pair<int, int> >
297 running_launcher_and_render_view_routes;
299 // Scan the list, counting how many prerenders have handles (and so were added
300 // to the PrerenderManager). The count is done for the system as a whole, and
301 // also per launcher.
302 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
303 i != prerenders_.end(); ++i) {
304 // Skip prerenders launched by a prerender.
305 if (i->deferred_launcher)
306 continue;
307 if (!i->handle) {
308 pending_prerenders.push_back(i);
309 } else {
310 ++total_started_prerender_count;
311 if (i->has_been_abandoned) {
312 abandoned_prerenders.push_back(&(*i));
313 } else {
314 // We do not count abandoned prerenders towards their launcher, since it
315 // has already navigated on to another page.
316 std::pair<int, int> launcher_and_render_view_route(
317 i->launcher_child_id, i->render_view_route_id);
318 running_launcher_and_render_view_routes.insert(
319 launcher_and_render_view_route);
320 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
321 running_launcher_and_render_view_routes.count(
322 launcher_and_render_view_route));
326 DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id,
327 i->prerender_id));
329 DCHECK_LE(abandoned_prerenders.size(), total_started_prerender_count);
330 DCHECK_GE(manager_->config().max_link_concurrency,
331 total_started_prerender_count);
332 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
334 TimeTicks now = manager_->GetCurrentTimeTicks();
336 // Scan the pending prerenders, starting prerenders as we can.
337 for (std::list<std::list<LinkPrerender>::iterator>::const_iterator
338 i = pending_prerenders.begin(), end = pending_prerenders.end();
339 i != end; ++i) {
340 TimeDelta prerender_age = now - (*i)->creation_time;
341 if (prerender_age >= manager_->config().max_wait_to_launch) {
342 // This prerender waited too long in the queue before launching.
343 prerenders_.erase(*i);
344 continue;
347 std::pair<int, int> launcher_and_render_view_route(
348 (*i)->launcher_child_id, (*i)->render_view_route_id);
349 if (manager_->config().max_link_concurrency_per_launcher <=
350 running_launcher_and_render_view_routes.count(
351 launcher_and_render_view_route)) {
352 // This prerender's launcher is already at its limit.
353 continue;
356 if (total_started_prerender_count >=
357 manager_->config().max_link_concurrency ||
358 total_started_prerender_count >= prerenders_.size()) {
359 // The system is already at its prerender concurrency limit. Can we kill
360 // an abandoned prerender to make room?
361 if (!abandoned_prerenders.empty()) {
362 CancelPrerender(abandoned_prerenders.front());
363 --total_started_prerender_count;
364 abandoned_prerenders.pop_front();
365 } else {
366 return;
370 if (!ShouldStartPrerender((*i)->rel_types)) {
371 prerenders_.erase(*i);
372 continue;
375 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
376 (*i)->launcher_child_id, (*i)->render_view_route_id,
377 (*i)->url, (*i)->rel_types, (*i)->referrer, (*i)->size);
378 if (!handle) {
379 // This prerender couldn't be launched, it's gone.
380 prerenders_.erase(*i);
381 continue;
384 // We have successfully started a new prerender.
385 (*i)->handle = handle;
386 ++total_started_prerender_count;
387 handle->SetObserver(this);
388 if (handle->IsPrerendering())
389 OnPrerenderStart(handle);
390 RecordLinkManagerStarting((*i)->rel_types);
392 running_launcher_and_render_view_routes.insert(
393 launcher_and_render_view_route);
397 PrerenderLinkManager::LinkPrerender*
398 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
399 int prerender_id) {
400 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
401 i != prerenders_.end(); ++i) {
402 if (launcher_child_id == i->launcher_child_id &&
403 prerender_id == i->prerender_id) {
404 return &(*i);
407 return NULL;
410 PrerenderLinkManager::LinkPrerender*
411 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
412 DCHECK(prerender_handle);
413 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
414 i != prerenders_.end(); ++i) {
415 if (prerender_handle == i->handle)
416 return &(*i);
418 return NULL;
421 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
422 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
423 i != prerenders_.end(); ++i) {
424 if (&(*i) == prerender) {
425 scoped_ptr<PrerenderHandle> own_handle(i->handle);
426 i->handle = NULL;
427 prerenders_.erase(i);
428 return;
431 NOTREACHED();
434 void PrerenderLinkManager::CancelPrerender(LinkPrerender* prerender) {
435 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
436 i != prerenders_.end(); ++i) {
437 if (&(*i) == prerender) {
438 scoped_ptr<PrerenderHandle> own_handle(i->handle);
439 i->handle = NULL;
440 prerenders_.erase(i);
441 if (own_handle)
442 own_handle->OnCancel();
443 return;
446 NOTREACHED();
449 void PrerenderLinkManager::StartPendingPrerendersForLauncher(
450 PrerenderContents* launcher) {
451 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
452 i != prerenders_.end(); ++i) {
453 if (i->deferred_launcher == launcher)
454 i->deferred_launcher = NULL;
456 StartPrerenders();
459 void PrerenderLinkManager::CancelPendingPrerendersForLauncher(
460 PrerenderContents* launcher) {
461 // Remove all pending prerenders for this launcher.
462 std::vector<std::list<LinkPrerender>::iterator> to_erase;
463 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
464 i != prerenders_.end(); ++i) {
465 if (i->deferred_launcher == launcher) {
466 DCHECK(!i->handle);
467 to_erase.push_back(i);
470 std::for_each(to_erase.begin(), to_erase.end(),
471 std::bind1st(std::mem_fun(&std::list<LinkPrerender>::erase),
472 &prerenders_));
475 void PrerenderLinkManager::Shutdown() {
476 has_shutdown_ = true;
479 // In practice, this is always called from PrerenderLinkManager::OnAddPrerender.
480 void PrerenderLinkManager::OnPrerenderStart(
481 PrerenderHandle* prerender_handle) {
482 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
483 if (!prerender)
484 return;
485 Send(prerender->launcher_child_id,
486 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
489 void PrerenderLinkManager::OnPrerenderStopLoading(
490 PrerenderHandle* prerender_handle) {
491 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
492 if (!prerender)
493 return;
495 Send(prerender->launcher_child_id,
496 new PrerenderMsg_OnPrerenderStopLoading(prerender->prerender_id));
499 void PrerenderLinkManager::OnPrerenderDomContentLoaded(
500 PrerenderHandle* prerender_handle) {
501 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
502 if (!prerender)
503 return;
505 Send(prerender->launcher_child_id,
506 new PrerenderMsg_OnPrerenderDomContentLoaded(prerender->prerender_id));
509 void PrerenderLinkManager::OnPrerenderStop(
510 PrerenderHandle* prerender_handle) {
511 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
512 if (!prerender)
513 return;
515 // If the prerender became a match complete replacement, the stop
516 // message has already been sent.
517 if (!prerender->is_match_complete_replacement) {
518 Send(prerender->launcher_child_id,
519 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
521 RemovePrerender(prerender);
522 StartPrerenders();
525 void PrerenderLinkManager::OnPrerenderCreatedMatchCompleteReplacement(
526 PrerenderHandle* prerender_handle) {
527 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
528 if (!prerender)
529 return;
531 DCHECK(!prerender->is_match_complete_replacement);
532 prerender->is_match_complete_replacement = true;
533 Send(prerender->launcher_child_id,
534 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
535 // Do not call RemovePrerender here. The replacement needs to stay connected
536 // to the HTMLLinkElement in the renderer so it notices renderer-triggered
537 // cancelations.
540 } // namespace prerender