GoogleURLTrackerInfoBarDelegate: Initialize uninitialized member in constructor.
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_link_manager.cc
blobd24ccc5844db8b80ecfeff3178606b076f8a4300
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/guest_view/guest_view_base.h"
17 #include "chrome/browser/prerender/prerender_contents.h"
18 #include "chrome/browser/prerender/prerender_handle.h"
19 #include "chrome/browser/prerender/prerender_manager.h"
20 #include "chrome/browser/prerender/prerender_manager_factory.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/prerender_messages.h"
23 #include "chrome/common/prerender_types.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/session_storage_namespace.h"
27 #include "content/public/common/referrer.h"
28 #include "ui/gfx/size.h"
29 #include "url/gurl.h"
31 using base::TimeDelta;
32 using base::TimeTicks;
33 using content::RenderViewHost;
34 using content::SessionStorageNamespace;
36 namespace prerender {
38 namespace {
40 bool ShouldStartRelNextPrerenders() {
41 const std::string experiment_name =
42 base::FieldTrialList::FindFullName("PrerenderRelNextTrial");
44 return experiment_name.find("Yes") != std::string::npos;
47 bool ShouldStartPrerender(const uint32 rel_types) {
48 const bool should_start_rel_next_prerenders =
49 ShouldStartRelNextPrerenders();
51 if (rel_types & PrerenderRelTypePrerender) {
52 return true;
53 } else if (should_start_rel_next_prerenders &&
54 (rel_types & PrerenderRelTypeNext) == PrerenderRelTypeNext) {
55 return true;
57 return false;
60 COMPILE_ASSERT(PrerenderRelTypePrerender == 0x1,
61 RelTypeHistogramEnum_must_match_PrerenderRelType);
62 COMPILE_ASSERT(PrerenderRelTypeNext == 0x2,
63 RelTypeHistogramEnum_must_match_PrerenderRelType);
64 enum RelTypeHistogramEnum {
65 RelTypeHistogramEnumNone = 0,
66 RelTypeHistogramEnumPrerender = PrerenderRelTypePrerender,
67 RelTypeHistogramEnumNext = PrerenderRelTypeNext,
68 RelTypeHistogramEnumPrerenderAndNext =
69 PrerenderRelTypePrerender | PrerenderRelTypeNext,
70 RelTypeHistogramEnumMax,
73 void RecordLinkManagerAdded(const uint32 rel_types) {
74 const uint32 enum_value = rel_types & (RelTypeHistogramEnumMax - 1);
75 UMA_HISTOGRAM_ENUMERATION("Prerender.RelTypesLinkAdded", enum_value,
76 RelTypeHistogramEnumMax);
79 void RecordLinkManagerStarting(const uint32 rel_types) {
80 const uint32 enum_value = rel_types & (RelTypeHistogramEnumMax - 1);
81 UMA_HISTOGRAM_ENUMERATION("Prerender.RelTypesLinkStarted", enum_value,
82 RelTypeHistogramEnumMax);
85 void Send(int child_id, IPC::Message* raw_message) {
86 using content::RenderProcessHost;
87 scoped_ptr<IPC::Message> own_message(raw_message);
89 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
90 if (!render_process_host)
91 return;
92 render_process_host->Send(own_message.release());
95 } // namespace
97 // Helper class to implement PrerenderContents::Observer and watch prerenders
98 // which launch other prerenders.
99 class PrerenderLinkManager::PendingPrerenderManager
100 : public PrerenderContents::Observer {
101 public:
102 explicit PendingPrerenderManager(PrerenderLinkManager* link_manager)
103 : link_manager_(link_manager) {}
105 virtual ~PendingPrerenderManager() {
106 DCHECK(observed_launchers_.empty());
107 for (std::set<PrerenderContents*>::iterator i = observed_launchers_.begin();
108 i != observed_launchers_.end(); ++i) {
109 (*i)->RemoveObserver(this);
113 void ObserveLauncher(PrerenderContents* launcher) {
114 DCHECK_EQ(FINAL_STATUS_MAX, launcher->final_status());
115 if (observed_launchers_.find(launcher) != observed_launchers_.end())
116 return;
117 observed_launchers_.insert(launcher);
118 launcher->AddObserver(this);
121 virtual void OnPrerenderStart(PrerenderContents* launcher) OVERRIDE {}
123 virtual void OnPrerenderStop(PrerenderContents* launcher) OVERRIDE {
124 observed_launchers_.erase(launcher);
125 if (launcher->final_status() == FINAL_STATUS_USED) {
126 link_manager_->StartPendingPrerendersForLauncher(launcher);
127 } else {
128 link_manager_->CancelPendingPrerendersForLauncher(launcher);
132 private:
133 // A pointer to the parent PrerenderLinkManager.
134 PrerenderLinkManager* link_manager_;
136 // The set of PrerenderContentses being observed. Lifetimes are managed by
137 // OnPrerenderStop.
138 std::set<PrerenderContents*> observed_launchers_;
141 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
142 : has_shutdown_(false),
143 manager_(manager),
144 pending_prerender_manager_(new PendingPrerenderManager(this)) {}
146 PrerenderLinkManager::~PrerenderLinkManager() {
147 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
148 i != prerenders_.end(); ++i) {
149 if (i->handle) {
150 DCHECK(!i->handle->IsPrerendering())
151 << "All running prerenders should stop at the same time as the "
152 << "PrerenderManager.";
153 delete i->handle;
154 i->handle = 0;
159 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
160 int prerender_id,
161 const GURL& url,
162 uint32 rel_types,
163 const content::Referrer& referrer,
164 const gfx::Size& size,
165 int render_view_route_id) {
166 DCHECK_EQ(static_cast<LinkPrerender*>(NULL),
167 FindByLauncherChildIdAndPrerenderId(launcher_child_id,
168 prerender_id));
169 content::RenderViewHost* rvh =
170 content::RenderViewHost::FromID(launcher_child_id, render_view_route_id);
171 content::WebContents* web_contents =
172 rvh ? content::WebContents::FromRenderViewHost(rvh) : NULL;
173 // Guests inside <webview> do not support cross-process navigation and so we
174 // do not allow guests to prerender content.
175 if (GuestViewBase::IsGuest(web_contents))
176 return;
178 // Check if the launcher is itself an unswapped prerender.
179 PrerenderContents* prerender_contents =
180 manager_->GetPrerenderContentsForRoute(launcher_child_id,
181 render_view_route_id);
182 if (prerender_contents &&
183 prerender_contents->final_status() != FINAL_STATUS_MAX) {
184 // The launcher is a prerender about to be destroyed asynchronously, but
185 // its AddLinkRelPrerender message raced with shutdown. Ignore it.
186 DCHECK_NE(FINAL_STATUS_USED, prerender_contents->final_status());
187 return;
190 LinkPrerender
191 prerender(launcher_child_id, prerender_id, url, rel_types, referrer, size,
192 render_view_route_id, manager_->GetCurrentTimeTicks(),
193 prerender_contents);
194 prerenders_.push_back(prerender);
195 RecordLinkManagerAdded(rel_types);
196 if (prerender_contents)
197 pending_prerender_manager_->ObserveLauncher(prerender_contents);
198 else
199 StartPrerenders();
202 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
203 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
204 prerender_id);
205 if (!prerender)
206 return;
208 CancelPrerender(prerender);
209 StartPrerenders();
212 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
213 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
214 prerender_id);
215 if (!prerender)
216 return;
218 if (!prerender->handle) {
219 RemovePrerender(prerender);
220 return;
223 prerender->has_been_abandoned = true;
224 prerender->handle->OnNavigateAway();
225 DCHECK(prerender->handle);
227 // If the prerender is not running, remove it from the list so it does not
228 // leak. If it is running, it will send a cancel event when it stops which
229 // will remove it.
230 if (!prerender->handle->IsPrerendering())
231 RemovePrerender(prerender);
234 void PrerenderLinkManager::OnChannelClosing(int child_id) {
235 std::list<LinkPrerender>::iterator next = prerenders_.begin();
236 while (next != prerenders_.end()) {
237 std::list<LinkPrerender>::iterator it = next;
238 ++next;
240 if (child_id != it->launcher_child_id)
241 continue;
243 const size_t running_prerender_count = CountRunningPrerenders();
244 OnAbandonPrerender(child_id, it->prerender_id);
245 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
249 PrerenderLinkManager::LinkPrerender::LinkPrerender(
250 int launcher_child_id,
251 int prerender_id,
252 const GURL& url,
253 uint32 rel_types,
254 const content::Referrer& referrer,
255 const gfx::Size& size,
256 int render_view_route_id,
257 TimeTicks creation_time,
258 PrerenderContents* deferred_launcher)
259 : launcher_child_id(launcher_child_id),
260 prerender_id(prerender_id),
261 url(url),
262 rel_types(rel_types),
263 referrer(referrer),
264 size(size),
265 render_view_route_id(render_view_route_id),
266 creation_time(creation_time),
267 deferred_launcher(deferred_launcher),
268 handle(NULL),
269 is_match_complete_replacement(false),
270 has_been_abandoned(false) {
273 PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
274 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
275 << "The PrerenderHandle should be destroyed before its Prerender.";
278 bool PrerenderLinkManager::IsEmpty() const {
279 return prerenders_.empty();
282 size_t PrerenderLinkManager::CountRunningPrerenders() const {
283 size_t retval = 0;
284 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
285 i != prerenders_.end(); ++i) {
286 if (i->handle && i->handle->IsPrerendering())
287 ++retval;
289 return retval;
292 void PrerenderLinkManager::StartPrerenders() {
293 if (has_shutdown_)
294 return;
296 size_t total_started_prerender_count = 0;
297 std::list<LinkPrerender*> abandoned_prerenders;
298 std::list<std::list<LinkPrerender>::iterator> pending_prerenders;
299 std::multiset<std::pair<int, int> >
300 running_launcher_and_render_view_routes;
302 // Scan the list, counting how many prerenders have handles (and so were added
303 // to the PrerenderManager). The count is done for the system as a whole, and
304 // also per launcher.
305 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
306 i != prerenders_.end(); ++i) {
307 // Skip prerenders launched by a prerender.
308 if (i->deferred_launcher)
309 continue;
310 if (!i->handle) {
311 pending_prerenders.push_back(i);
312 } else {
313 ++total_started_prerender_count;
314 if (i->has_been_abandoned) {
315 abandoned_prerenders.push_back(&(*i));
316 } else {
317 // We do not count abandoned prerenders towards their launcher, since it
318 // has already navigated on to another page.
319 std::pair<int, int> launcher_and_render_view_route(
320 i->launcher_child_id, i->render_view_route_id);
321 running_launcher_and_render_view_routes.insert(
322 launcher_and_render_view_route);
323 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
324 running_launcher_and_render_view_routes.count(
325 launcher_and_render_view_route));
329 DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id,
330 i->prerender_id));
332 DCHECK_LE(abandoned_prerenders.size(), total_started_prerender_count);
333 DCHECK_GE(manager_->config().max_link_concurrency,
334 total_started_prerender_count);
335 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
337 TimeTicks now = manager_->GetCurrentTimeTicks();
339 // Scan the pending prerenders, starting prerenders as we can.
340 for (std::list<std::list<LinkPrerender>::iterator>::const_iterator
341 i = pending_prerenders.begin(), end = pending_prerenders.end();
342 i != end; ++i) {
343 TimeDelta prerender_age = now - (*i)->creation_time;
344 if (prerender_age >= manager_->config().max_wait_to_launch) {
345 // This prerender waited too long in the queue before launching.
346 prerenders_.erase(*i);
347 continue;
350 std::pair<int, int> launcher_and_render_view_route(
351 (*i)->launcher_child_id, (*i)->render_view_route_id);
352 if (manager_->config().max_link_concurrency_per_launcher <=
353 running_launcher_and_render_view_routes.count(
354 launcher_and_render_view_route)) {
355 // This prerender's launcher is already at its limit.
356 continue;
359 if (total_started_prerender_count >=
360 manager_->config().max_link_concurrency ||
361 total_started_prerender_count >= prerenders_.size()) {
362 // The system is already at its prerender concurrency limit. Can we kill
363 // an abandoned prerender to make room?
364 if (!abandoned_prerenders.empty()) {
365 CancelPrerender(abandoned_prerenders.front());
366 --total_started_prerender_count;
367 abandoned_prerenders.pop_front();
368 } else {
369 return;
373 if (!ShouldStartPrerender((*i)->rel_types)) {
374 prerenders_.erase(*i);
375 continue;
378 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
379 (*i)->launcher_child_id, (*i)->render_view_route_id,
380 (*i)->url, (*i)->rel_types, (*i)->referrer, (*i)->size);
381 if (!handle) {
382 // This prerender couldn't be launched, it's gone.
383 prerenders_.erase(*i);
384 continue;
387 // We have successfully started a new prerender.
388 (*i)->handle = handle;
389 ++total_started_prerender_count;
390 handle->SetObserver(this);
391 if (handle->IsPrerendering())
392 OnPrerenderStart(handle);
393 RecordLinkManagerStarting((*i)->rel_types);
395 running_launcher_and_render_view_routes.insert(
396 launcher_and_render_view_route);
400 PrerenderLinkManager::LinkPrerender*
401 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
402 int prerender_id) {
403 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
404 i != prerenders_.end(); ++i) {
405 if (launcher_child_id == i->launcher_child_id &&
406 prerender_id == i->prerender_id) {
407 return &(*i);
410 return NULL;
413 PrerenderLinkManager::LinkPrerender*
414 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
415 DCHECK(prerender_handle);
416 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
417 i != prerenders_.end(); ++i) {
418 if (prerender_handle == i->handle)
419 return &(*i);
421 return NULL;
424 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
425 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
426 i != prerenders_.end(); ++i) {
427 if (&(*i) == prerender) {
428 scoped_ptr<PrerenderHandle> own_handle(i->handle);
429 i->handle = NULL;
430 prerenders_.erase(i);
431 return;
434 NOTREACHED();
437 void PrerenderLinkManager::CancelPrerender(LinkPrerender* prerender) {
438 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
439 i != prerenders_.end(); ++i) {
440 if (&(*i) == prerender) {
441 scoped_ptr<PrerenderHandle> own_handle(i->handle);
442 i->handle = NULL;
443 prerenders_.erase(i);
444 if (own_handle)
445 own_handle->OnCancel();
446 return;
449 NOTREACHED();
452 void PrerenderLinkManager::StartPendingPrerendersForLauncher(
453 PrerenderContents* launcher) {
454 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
455 i != prerenders_.end(); ++i) {
456 if (i->deferred_launcher == launcher)
457 i->deferred_launcher = NULL;
459 StartPrerenders();
462 void PrerenderLinkManager::CancelPendingPrerendersForLauncher(
463 PrerenderContents* launcher) {
464 // Remove all pending prerenders for this launcher.
465 std::vector<std::list<LinkPrerender>::iterator> to_erase;
466 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
467 i != prerenders_.end(); ++i) {
468 if (i->deferred_launcher == launcher) {
469 DCHECK(!i->handle);
470 to_erase.push_back(i);
473 std::for_each(to_erase.begin(), to_erase.end(),
474 std::bind1st(std::mem_fun(&std::list<LinkPrerender>::erase),
475 &prerenders_));
478 void PrerenderLinkManager::Shutdown() {
479 has_shutdown_ = true;
482 // In practice, this is always called from PrerenderLinkManager::OnAddPrerender.
483 void PrerenderLinkManager::OnPrerenderStart(
484 PrerenderHandle* prerender_handle) {
485 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
486 if (!prerender)
487 return;
488 Send(prerender->launcher_child_id,
489 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
492 void PrerenderLinkManager::OnPrerenderStopLoading(
493 PrerenderHandle* prerender_handle) {
494 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
495 if (!prerender)
496 return;
498 Send(prerender->launcher_child_id,
499 new PrerenderMsg_OnPrerenderStopLoading(prerender->prerender_id));
502 void PrerenderLinkManager::OnPrerenderDomContentLoaded(
503 PrerenderHandle* prerender_handle) {
504 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
505 if (!prerender)
506 return;
508 Send(prerender->launcher_child_id,
509 new PrerenderMsg_OnPrerenderDomContentLoaded(prerender->prerender_id));
512 void PrerenderLinkManager::OnPrerenderStop(
513 PrerenderHandle* prerender_handle) {
514 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
515 if (!prerender)
516 return;
518 // If the prerender became a match complete replacement, the stop
519 // message has already been sent.
520 if (!prerender->is_match_complete_replacement) {
521 Send(prerender->launcher_child_id,
522 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
524 RemovePrerender(prerender);
525 StartPrerenders();
528 void PrerenderLinkManager::OnPrerenderCreatedMatchCompleteReplacement(
529 PrerenderHandle* prerender_handle) {
530 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
531 if (!prerender)
532 return;
534 DCHECK(!prerender->is_match_complete_replacement);
535 prerender->is_match_complete_replacement = true;
536 Send(prerender->launcher_child_id,
537 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
538 // Do not call RemovePrerender here. The replacement needs to stay connected
539 // to the HTMLLinkElement in the renderer so it notices renderer-triggered
540 // cancelations.
543 } // namespace prerender