Remove the dependency of PasswordStore on BrowserContextKeyedService
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_link_manager.cc
blob8213dedd8701b4abb3250e952cc564b2297ef250
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 <utility>
12 #include "base/memory/scoped_ptr.h"
13 #include "chrome/browser/prerender/prerender_contents.h"
14 #include "chrome/browser/prerender/prerender_handle.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/prerender/prerender_manager_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/prerender_messages.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/session_storage_namespace.h"
22 #include "content/public/common/referrer.h"
23 #include "ui/gfx/size.h"
24 #include "url/gurl.h"
26 using base::TimeDelta;
27 using base::TimeTicks;
28 using content::RenderViewHost;
29 using content::SessionStorageNamespace;
31 namespace {
33 void Send(int child_id, IPC::Message* raw_message) {
34 using content::RenderProcessHost;
35 scoped_ptr<IPC::Message> own_message(raw_message);
37 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
38 if (!render_process_host)
39 return;
40 render_process_host->Send(own_message.release());
43 } // namespace
45 namespace prerender {
47 // Helper class to implement PrerenderContents::Observer and watch prerenders
48 // which launch other prerenders.
49 class PrerenderLinkManager::PendingPrerenderManager
50 : public PrerenderContents::Observer {
51 public:
52 explicit PendingPrerenderManager(PrerenderLinkManager* link_manager)
53 : link_manager_(link_manager) {}
55 virtual ~PendingPrerenderManager() {
56 DCHECK(observed_launchers_.empty());
57 for (std::set<PrerenderContents*>::iterator i = observed_launchers_.begin();
58 i != observed_launchers_.end(); ++i) {
59 (*i)->RemoveObserver(this);
63 void ObserveLauncher(PrerenderContents* launcher) {
64 DCHECK_EQ(FINAL_STATUS_MAX, launcher->final_status());
65 if (observed_launchers_.find(launcher) != observed_launchers_.end())
66 return;
67 observed_launchers_.insert(launcher);
68 launcher->AddObserver(this);
71 virtual void OnPrerenderStart(PrerenderContents* launcher) OVERRIDE {}
73 virtual void OnPrerenderStop(PrerenderContents* launcher) OVERRIDE {
74 observed_launchers_.erase(launcher);
75 if (launcher->final_status() == FINAL_STATUS_USED) {
76 link_manager_->StartPendingPrerendersForLauncher(launcher);
77 } else {
78 link_manager_->CancelPendingPrerendersForLauncher(launcher);
82 private:
83 // A pointer to the parent PrerenderLinkManager.
84 PrerenderLinkManager* link_manager_;
86 // The set of PrerenderContentses being observed. Lifetimes are managed by
87 // OnPrerenderStop.
88 std::set<PrerenderContents*> observed_launchers_;
91 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
92 : has_shutdown_(false),
93 manager_(manager),
94 pending_prerender_manager_(new PendingPrerenderManager(this)) {}
96 PrerenderLinkManager::~PrerenderLinkManager() {
97 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
98 i != prerenders_.end(); ++i) {
99 if (i->handle) {
100 DCHECK(!i->handle->IsPrerendering())
101 << "All running prerenders should stop at the same time as the "
102 << "PrerenderManager.";
103 delete i->handle;
104 i->handle = 0;
109 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
110 int prerender_id,
111 const GURL& url,
112 const content::Referrer& referrer,
113 const gfx::Size& size,
114 int render_view_route_id) {
115 DCHECK_EQ(static_cast<LinkPrerender*>(NULL),
116 FindByLauncherChildIdAndPrerenderId(launcher_child_id,
117 prerender_id));
118 content::RenderProcessHost* rph =
119 content::RenderProcessHost::FromID(launcher_child_id);
120 // Guests inside <webview> do not support cross-process navigation and so we
121 // do not allow guests to prerender content.
122 if (rph && rph->IsGuest())
123 return;
125 // Check if the launcher is itself an unswapped prerender.
126 PrerenderContents* prerender_contents =
127 manager_->GetPrerenderContentsForRoute(launcher_child_id,
128 render_view_route_id);
129 if (prerender_contents &&
130 prerender_contents->final_status() != FINAL_STATUS_MAX) {
131 // The launcher is a prerender about to be destroyed asynchronously, but
132 // its AddLinkRelPrerender message raced with shutdown. Ignore it.
133 DCHECK_NE(FINAL_STATUS_USED, prerender_contents->final_status());
134 return;
137 LinkPrerender
138 prerender(launcher_child_id, prerender_id, url, referrer, size,
139 render_view_route_id, manager_->GetCurrentTimeTicks(),
140 prerender_contents);
141 prerenders_.push_back(prerender);
142 if (prerender_contents)
143 pending_prerender_manager_->ObserveLauncher(prerender_contents);
144 else
145 StartPrerenders();
148 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
149 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
150 prerender_id);
151 if (!prerender)
152 return;
154 CancelPrerender(prerender);
155 StartPrerenders();
158 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
159 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
160 prerender_id);
161 if (!prerender)
162 return;
164 if (!prerender->handle) {
165 RemovePrerender(prerender);
166 return;
169 prerender->has_been_abandoned = true;
170 prerender->handle->OnNavigateAway();
171 DCHECK(prerender->handle);
173 // If the prerender is not running, remove it from the list so it does not
174 // leak. If it is running, it will send a cancel event when it stops which
175 // will remove it.
176 if (!prerender->handle->IsPrerendering())
177 RemovePrerender(prerender);
180 void PrerenderLinkManager::OnChannelClosing(int child_id) {
181 std::list<LinkPrerender>::iterator next = prerenders_.begin();
182 while (next != prerenders_.end()) {
183 std::list<LinkPrerender>::iterator it = next;
184 ++next;
186 if (child_id != it->launcher_child_id)
187 continue;
189 const size_t running_prerender_count = CountRunningPrerenders();
190 OnAbandonPrerender(child_id, it->prerender_id);
191 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
195 PrerenderLinkManager::LinkPrerender::LinkPrerender(
196 int launcher_child_id,
197 int prerender_id,
198 const GURL& url,
199 const content::Referrer& referrer,
200 const gfx::Size& size,
201 int render_view_route_id,
202 TimeTicks creation_time,
203 PrerenderContents* deferred_launcher)
204 : launcher_child_id(launcher_child_id),
205 prerender_id(prerender_id),
206 url(url),
207 referrer(referrer),
208 size(size),
209 render_view_route_id(render_view_route_id),
210 creation_time(creation_time),
211 deferred_launcher(deferred_launcher),
212 handle(NULL),
213 is_match_complete_replacement(false),
214 has_been_abandoned(false) {
217 PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
218 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
219 << "The PrerenderHandle should be destroyed before its Prerender.";
222 bool PrerenderLinkManager::IsEmpty() const {
223 return prerenders_.empty();
226 size_t PrerenderLinkManager::CountRunningPrerenders() const {
227 size_t retval = 0;
228 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
229 i != prerenders_.end(); ++i) {
230 if (i->handle && i->handle->IsPrerendering())
231 ++retval;
233 return retval;
236 void PrerenderLinkManager::StartPrerenders() {
237 if (has_shutdown_)
238 return;
240 size_t total_started_prerender_count = 0;
241 std::list<LinkPrerender*> abandoned_prerenders;
242 std::list<std::list<LinkPrerender>::iterator> pending_prerenders;
243 std::multiset<std::pair<int, int> >
244 running_launcher_and_render_view_routes;
246 // Scan the list, counting how many prerenders have handles (and so were added
247 // to the PrerenderManager). The count is done for the system as a whole, and
248 // also per launcher.
249 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
250 i != prerenders_.end(); ++i) {
251 // Skip prerenders launched by a prerender.
252 if (i->deferred_launcher)
253 continue;
254 if (!i->handle) {
255 pending_prerenders.push_back(i);
256 } else {
257 ++total_started_prerender_count;
258 if (i->has_been_abandoned) {
259 abandoned_prerenders.push_back(&(*i));
260 } else {
261 // We do not count abandoned prerenders towards their launcher, since it
262 // has already navigated on to another page.
263 std::pair<int, int> launcher_and_render_view_route(
264 i->launcher_child_id, i->render_view_route_id);
265 running_launcher_and_render_view_routes.insert(
266 launcher_and_render_view_route);
267 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
268 running_launcher_and_render_view_routes.count(
269 launcher_and_render_view_route));
273 DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id,
274 i->prerender_id));
276 DCHECK_LE(abandoned_prerenders.size(), total_started_prerender_count);
277 DCHECK_GE(manager_->config().max_link_concurrency,
278 total_started_prerender_count);
279 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
281 TimeTicks now = manager_->GetCurrentTimeTicks();
283 // Scan the pending prerenders, starting prerenders as we can.
284 for (std::list<std::list<LinkPrerender>::iterator>::const_iterator
285 i = pending_prerenders.begin(), end = pending_prerenders.end();
286 i != end; ++i) {
287 TimeDelta prerender_age = now - (*i)->creation_time;
288 if (prerender_age >= manager_->config().max_wait_to_launch) {
289 // This prerender waited too long in the queue before launching.
290 prerenders_.erase(*i);
291 continue;
294 std::pair<int, int> launcher_and_render_view_route(
295 (*i)->launcher_child_id, (*i)->render_view_route_id);
296 if (manager_->config().max_link_concurrency_per_launcher <=
297 running_launcher_and_render_view_routes.count(
298 launcher_and_render_view_route)) {
299 // This prerender's launcher is already at its limit.
300 continue;
303 if (total_started_prerender_count >=
304 manager_->config().max_link_concurrency ||
305 total_started_prerender_count >= prerenders_.size()) {
306 // The system is already at its prerender concurrency limit. Can we kill
307 // an abandoned prerender to make room?
308 if (!abandoned_prerenders.empty()) {
309 CancelPrerender(abandoned_prerenders.front());
310 --total_started_prerender_count;
311 abandoned_prerenders.pop_front();
312 } else {
313 return;
317 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
318 (*i)->launcher_child_id, (*i)->render_view_route_id,
319 (*i)->url, (*i)->referrer, (*i)->size);
320 if (!handle) {
321 // This prerender couldn't be launched, it's gone.
322 prerenders_.erase(*i);
323 continue;
326 // We have successfully started a new prerender.
327 (*i)->handle = handle;
328 ++total_started_prerender_count;
329 handle->SetObserver(this);
330 if (handle->IsPrerendering())
331 OnPrerenderStart(handle);
333 running_launcher_and_render_view_routes.insert(
334 launcher_and_render_view_route);
338 PrerenderLinkManager::LinkPrerender*
339 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
340 int prerender_id) {
341 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
342 i != prerenders_.end(); ++i) {
343 if (launcher_child_id == i->launcher_child_id &&
344 prerender_id == i->prerender_id) {
345 return &(*i);
348 return NULL;
351 PrerenderLinkManager::LinkPrerender*
352 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
353 DCHECK(prerender_handle);
354 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
355 i != prerenders_.end(); ++i) {
356 if (prerender_handle == i->handle)
357 return &(*i);
359 return NULL;
362 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
363 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
364 i != prerenders_.end(); ++i) {
365 if (&(*i) == prerender) {
366 scoped_ptr<PrerenderHandle> own_handle(i->handle);
367 i->handle = NULL;
368 prerenders_.erase(i);
369 return;
372 NOTREACHED();
375 void PrerenderLinkManager::CancelPrerender(LinkPrerender* prerender) {
376 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
377 i != prerenders_.end(); ++i) {
378 if (&(*i) == prerender) {
379 scoped_ptr<PrerenderHandle> own_handle(i->handle);
380 i->handle = NULL;
381 prerenders_.erase(i);
382 if (own_handle)
383 own_handle->OnCancel();
384 return;
387 NOTREACHED();
390 void PrerenderLinkManager::StartPendingPrerendersForLauncher(
391 PrerenderContents* launcher) {
392 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
393 i != prerenders_.end(); ++i) {
394 if (i->deferred_launcher == launcher)
395 i->deferred_launcher = NULL;
397 StartPrerenders();
400 void PrerenderLinkManager::CancelPendingPrerendersForLauncher(
401 PrerenderContents* launcher) {
402 // Remove all pending prerenders for this launcher.
403 std::vector<std::list<LinkPrerender>::iterator> to_erase;
404 for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
405 i != prerenders_.end(); ++i) {
406 if (i->deferred_launcher == launcher) {
407 DCHECK(!i->handle);
408 to_erase.push_back(i);
411 std::for_each(to_erase.begin(), to_erase.end(),
412 std::bind1st(std::mem_fun(&std::list<LinkPrerender>::erase),
413 &prerenders_));
416 void PrerenderLinkManager::Shutdown() {
417 has_shutdown_ = true;
420 // In practice, this is always called from either
421 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending
422 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom.
423 void PrerenderLinkManager::OnPrerenderStart(
424 PrerenderHandle* prerender_handle) {
425 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
426 if (!prerender)
427 return;
428 Send(prerender->launcher_child_id,
429 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
432 void PrerenderLinkManager::OnPrerenderStopLoading(
433 PrerenderHandle* prerender_handle) {
434 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
435 if (!prerender)
436 return;
438 Send(prerender->launcher_child_id,
439 new PrerenderMsg_OnPrerenderStopLoading(prerender->prerender_id));
442 void PrerenderLinkManager::OnPrerenderStop(
443 PrerenderHandle* prerender_handle) {
444 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
445 if (!prerender)
446 return;
448 // If the prerender became a match complete replacement, the stop
449 // message has already been sent.
450 if (!prerender->is_match_complete_replacement) {
451 Send(prerender->launcher_child_id,
452 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
454 RemovePrerender(prerender);
455 StartPrerenders();
458 void PrerenderLinkManager::OnPrerenderCreatedMatchCompleteReplacement(
459 PrerenderHandle* prerender_handle) {
460 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
461 if (!prerender)
462 return;
464 DCHECK(!prerender->is_match_complete_replacement);
465 prerender->is_match_complete_replacement = true;
466 Send(prerender->launcher_child_id,
467 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
468 // Do not call RemovePrerender here. The replacement needs to stay connected
469 // to the HTMLLinkElement in the renderer so it notices renderer-triggered
470 // cancelations.
473 } // namespace prerender