Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_link_manager.cc
blob7726016c203f430ee05bb727dc56b28e7e8a7a64
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 <limits>
8 #include <set>
9 #include <utility>
11 #include "base/memory/scoped_ptr.h"
12 #include "chrome/browser/prerender/prerender_contents.h"
13 #include "chrome/browser/prerender/prerender_handle.h"
14 #include "chrome/browser/prerender/prerender_manager.h"
15 #include "chrome/browser/prerender/prerender_manager_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/prerender_messages.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/session_storage_namespace.h"
21 #include "content/public/common/referrer.h"
22 #include "ui/gfx/size.h"
23 #include "url/gurl.h"
25 using base::TimeDelta;
26 using base::TimeTicks;
27 using content::RenderViewHost;
28 using content::SessionStorageNamespace;
30 namespace {
32 void Send(int child_id, IPC::Message* raw_message) {
33 using content::RenderProcessHost;
34 scoped_ptr<IPC::Message> own_message(raw_message);
36 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
37 if (!render_process_host)
38 return;
39 render_process_host->Send(own_message.release());
42 } // namespace
44 namespace prerender {
46 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
47 : has_shutdown_(false),
48 manager_(manager) {
51 PrerenderLinkManager::~PrerenderLinkManager() {
52 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
53 i != prerenders_.end(); ++i) {
54 if (i->handle) {
55 DCHECK(!i->handle->IsPrerendering())
56 << "All running prerenders should stop at the same time as the "
57 << "PrerenderManager.";
58 delete i->handle;
59 i->handle = 0;
64 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
65 int prerender_id,
66 const GURL& url,
67 const content::Referrer& referrer,
68 const gfx::Size& size,
69 int render_view_route_id) {
70 DCHECK_EQ(static_cast<LinkPrerender*>(NULL),
71 FindByLauncherChildIdAndPrerenderId(launcher_child_id,
72 prerender_id));
73 content::RenderProcessHost* rph =
74 content::RenderProcessHost::FromID(launcher_child_id);
75 // Guests inside <webview> do not support cross-process navigation and so we
76 // do not allow guests to prerender content.
77 if (rph && rph->IsGuest())
78 return;
80 LinkPrerender
81 prerender(launcher_child_id, prerender_id, url, referrer, size,
82 render_view_route_id, manager_->GetCurrentTimeTicks());
83 prerenders_.push_back(prerender);
84 StartPrerenders();
87 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
88 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
89 prerender_id);
90 if (!prerender)
91 return;
93 CancelPrerender(prerender);
94 StartPrerenders();
97 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
98 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
99 prerender_id);
100 if (!prerender)
101 return;
103 if (!prerender->handle) {
104 RemovePrerender(prerender);
105 return;
108 prerender->has_been_abandoned = true;
109 prerender->handle->OnNavigateAway();
110 DCHECK(prerender->handle);
112 // If the prerender is not running, remove it from the list so it does not
113 // leak. If it is running, it will send a cancel event when it stops which
114 // will remove it.
115 if (!prerender->handle->IsPrerendering())
116 RemovePrerender(prerender);
119 void PrerenderLinkManager::OnChannelClosing(int child_id) {
120 std::list<LinkPrerender>::iterator next = prerenders_.begin();
121 while (next != prerenders_.end()) {
122 std::list<LinkPrerender>::iterator it = next;
123 ++next;
125 if (child_id != it->launcher_child_id)
126 continue;
128 const size_t running_prerender_count = CountRunningPrerenders();
129 OnAbandonPrerender(child_id, it->prerender_id);
130 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
134 PrerenderLinkManager::LinkPrerender::LinkPrerender(
135 int launcher_child_id,
136 int prerender_id,
137 const GURL& url,
138 const content::Referrer& referrer,
139 const gfx::Size& size,
140 int render_view_route_id,
141 TimeTicks creation_time) : launcher_child_id(launcher_child_id),
142 prerender_id(prerender_id),
143 url(url),
144 referrer(referrer),
145 size(size),
146 render_view_route_id(render_view_route_id),
147 creation_time(creation_time),
148 handle(NULL),
149 is_match_complete_replacement(false),
150 has_been_abandoned(false) {
153 PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
154 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
155 << "The PrerenderHandle should be destroyed before its Prerender.";
158 bool PrerenderLinkManager::IsEmpty() const {
159 return prerenders_.empty();
162 size_t PrerenderLinkManager::CountRunningPrerenders() const {
163 size_t retval = 0;
164 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
165 i != prerenders_.end(); ++i) {
166 if (i->handle && i->handle->IsPrerendering())
167 ++retval;
169 return retval;
172 void PrerenderLinkManager::StartPrerenders() {
173 if (has_shutdown_)
174 return;
176 size_t total_started_prerender_count = 0;
177 std::list<LinkPrerender*> abandoned_prerenders;
178 std::list<std::list<LinkPrerender>::iterator> pending_prerenders;
179 std::multiset<std::pair<int, int> >
180 running_launcher_and_render_view_routes;
182 // Scan the list, counting how many prerenders have handles (and so were added
183 // to the PrerenderManager). The count is done for the system as a whole, and
184 // also per launcher.
185 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
186 i != prerenders_.end(); ++i) {
187 if (!i->handle) {
188 pending_prerenders.push_back(i);
189 } else {
190 ++total_started_prerender_count;
191 if (i->has_been_abandoned) {
192 abandoned_prerenders.push_back(&(*i));
193 } else {
194 // We do not count abandoned prerenders towards their launcher, since it
195 // has already navigated on to another page.
196 std::pair<int, int> launcher_and_render_view_route(
197 i->launcher_child_id, i->render_view_route_id);
198 running_launcher_and_render_view_routes.insert(
199 launcher_and_render_view_route);
200 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
201 running_launcher_and_render_view_routes.count(
202 launcher_and_render_view_route));
206 DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id,
207 i->prerender_id));
209 DCHECK_LE(abandoned_prerenders.size(), total_started_prerender_count);
210 DCHECK_GE(manager_->config().max_link_concurrency,
211 total_started_prerender_count);
212 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
214 TimeTicks now = manager_->GetCurrentTimeTicks();
216 // Scan the pending prerenders, starting prerenders as we can.
217 for (std::list<std::list<LinkPrerender>::iterator>::const_iterator
218 i = pending_prerenders.begin(), end = pending_prerenders.end();
219 i != end; ++i) {
220 TimeDelta prerender_age = now - (*i)->creation_time;
221 if (prerender_age >= manager_->config().max_wait_to_launch) {
222 // This prerender waited too long in the queue before launching.
223 prerenders_.erase(*i);
224 continue;
227 std::pair<int, int> launcher_and_render_view_route(
228 (*i)->launcher_child_id, (*i)->render_view_route_id);
229 if (manager_->config().max_link_concurrency_per_launcher <=
230 running_launcher_and_render_view_routes.count(
231 launcher_and_render_view_route)) {
232 // This prerender's launcher is already at its limit.
233 continue;
236 if (total_started_prerender_count >=
237 manager_->config().max_link_concurrency ||
238 total_started_prerender_count >= prerenders_.size()) {
239 // The system is already at its prerender concurrency limit. Can we kill
240 // an abandoned prerender to make room?
241 if (!abandoned_prerenders.empty()) {
242 CancelPrerender(abandoned_prerenders.front());
243 --total_started_prerender_count;
244 abandoned_prerenders.pop_front();
245 } else {
246 return;
250 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
251 (*i)->launcher_child_id, (*i)->render_view_route_id,
252 (*i)->url, (*i)->referrer, (*i)->size);
253 if (!handle) {
254 // This prerender couldn't be launched, it's gone.
255 prerenders_.erase(*i);
256 continue;
259 // We have successfully started a new prerender.
260 (*i)->handle = handle;
261 ++total_started_prerender_count;
262 handle->SetObserver(this);
263 if (handle->IsPrerendering())
264 OnPrerenderStart(handle);
266 running_launcher_and_render_view_routes.insert(
267 launcher_and_render_view_route);
271 PrerenderLinkManager::LinkPrerender*
272 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
273 int prerender_id) {
274 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
275 i != prerenders_.end(); ++i) {
276 if (launcher_child_id == i->launcher_child_id &&
277 prerender_id == i->prerender_id) {
278 return &(*i);
281 return NULL;
284 PrerenderLinkManager::LinkPrerender*
285 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
286 DCHECK(prerender_handle);
287 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
288 i != prerenders_.end(); ++i) {
289 if (prerender_handle == i->handle)
290 return &(*i);
292 return NULL;
295 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
296 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
297 i != prerenders_.end(); ++i) {
298 if (&(*i) == prerender) {
299 scoped_ptr<PrerenderHandle> own_handle(i->handle);
300 i->handle = NULL;
301 prerenders_.erase(i);
302 return;
305 NOTREACHED();
308 void PrerenderLinkManager::CancelPrerender(LinkPrerender* prerender) {
309 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
310 i != prerenders_.end(); ++i) {
311 if (&(*i) == prerender) {
312 scoped_ptr<PrerenderHandle> own_handle(i->handle);
313 i->handle = NULL;
314 prerenders_.erase(i);
315 if (own_handle)
316 own_handle->OnCancel();
317 return;
320 NOTREACHED();
323 void PrerenderLinkManager::Shutdown() {
324 has_shutdown_ = true;
327 // In practice, this is always called from either
328 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending
329 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom.
330 void PrerenderLinkManager::OnPrerenderStart(
331 PrerenderHandle* prerender_handle) {
332 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
333 if (!prerender)
334 return;
335 Send(prerender->launcher_child_id,
336 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
339 void PrerenderLinkManager::OnPrerenderStopLoading(
340 PrerenderHandle* prerender_handle) {
341 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
342 if (!prerender)
343 return;
345 Send(prerender->launcher_child_id,
346 new PrerenderMsg_OnPrerenderStopLoading(prerender->prerender_id));
349 void PrerenderLinkManager::OnPrerenderStop(
350 PrerenderHandle* prerender_handle) {
351 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
352 if (!prerender)
353 return;
355 // If the prerender became a match complete replacement, the stop
356 // message has already been sent.
357 if (!prerender->is_match_complete_replacement) {
358 Send(prerender->launcher_child_id,
359 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
361 RemovePrerender(prerender);
362 StartPrerenders();
365 void PrerenderLinkManager::OnPrerenderCreatedMatchCompleteReplacement(
366 PrerenderHandle* prerender_handle) {
367 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
368 if (!prerender)
369 return;
371 DCHECK(!prerender->is_match_complete_replacement);
372 prerender->is_match_complete_replacement = true;
373 Send(prerender->launcher_child_id,
374 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
375 // Do not call RemovePrerender here. The replacement needs to stay connected
376 // to the HTMLLinkElement in the renderer so it notices renderer-triggered
377 // cancelations.
380 } // namespace prerender