Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: chrome/browser/prerender/prerender_link_manager.cc

Issue 11551003: Change multi-prerender API to include per launcher slots. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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_link_manager.h" 5 #include "chrome/browser/prerender/prerender_link_manager.h"
6 6
7 #include <limits> 7 #include <limits>
8 #include <set>
8 #include <utility> 9 #include <utility>
9 10
10 #include "base/memory/scoped_ptr.h" 11 #include "base/memory/scoped_ptr.h"
11 #include "chrome/browser/prerender/prerender_contents.h" 12 #include "chrome/browser/prerender/prerender_contents.h"
12 #include "chrome/browser/prerender/prerender_handle.h" 13 #include "chrome/browser/prerender/prerender_handle.h"
13 #include "chrome/browser/prerender/prerender_manager.h" 14 #include "chrome/browser/prerender/prerender_manager.h"
14 #include "chrome/browser/prerender/prerender_manager_factory.h" 15 #include "chrome/browser/prerender/prerender_manager_factory.h"
15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/prerender_messages.h" 17 #include "chrome/common/prerender_messages.h"
17 #include "content/public/browser/render_process_host.h" 18 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h" 19 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/session_storage_namespace.h" 20 #include "content/public/browser/session_storage_namespace.h"
20 #include "content/public/common/referrer.h" 21 #include "content/public/common/referrer.h"
21 #include "googleurl/src/gurl.h" 22 #include "googleurl/src/gurl.h"
22 #include "ui/gfx/size.h" 23 #include "ui/gfx/size.h"
23 24
25 using base::TimeDelta;
26 using base::TimeTicks;
24 using content::RenderViewHost; 27 using content::RenderViewHost;
25 using content::SessionStorageNamespace; 28 using content::SessionStorageNamespace;
26 29
27 namespace { 30 namespace {
28 31
29 void Send(int child_id, IPC::Message* raw_message) { 32 void Send(int child_id, IPC::Message* raw_message) {
30 using content::RenderProcessHost; 33 using content::RenderProcessHost;
31 scoped_ptr<IPC::Message> own_message(raw_message); 34 scoped_ptr<IPC::Message> own_message(raw_message);
32 35
33 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id); 36 RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id);
34 if (!render_process_host) 37 if (!render_process_host)
35 return; 38 return;
36 render_process_host->Send(own_message.release()); 39 render_process_host->Send(own_message.release());
37 } 40 }
38 41
39 } // namespace 42 } // namespace
40 43
41 namespace prerender { 44 namespace prerender {
42 45
43 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager) 46 PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
44 : manager_(manager) { 47 : has_shutdown_(false),
48 manager_(manager) {
45 } 49 }
46 50
47 PrerenderLinkManager::~PrerenderLinkManager() { 51 PrerenderLinkManager::~PrerenderLinkManager() {
48 for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); 52 for (std::list<Prerender>::iterator i = prerenders_.begin();
49 it != ids_to_handle_map_.end(); 53 i != prerenders_.end(); ++i) {
50 ++it) { 54 if (i->handle) {
51 PrerenderHandle* prerender_handle = it->second; 55 DCHECK(!i->handle->IsPrerendering())
52 DCHECK(!prerender_handle->IsPrerendering()) 56 << "All running prerenders should stop at the same time as the "
53 << "All running prerenders should stop at the same time as the " 57 << "PrerenderManager.";
54 << "PrerenderManager."; 58 delete i->handle;
55 delete prerender_handle; 59 i->handle = 0;
56 } 60 }
57 } 61 }
58 62 }
59 bool PrerenderLinkManager::OnAddPrerender(int child_id, 63
64 void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
60 int prerender_id, 65 int prerender_id,
61 const GURL& url, 66 const GURL& url,
62 const content::Referrer& referrer, 67 const content::Referrer& referrer,
63 const gfx::Size& size, 68 const gfx::Size& size,
64 int render_view_route_id) { 69 int render_view_route_id) {
65 DVLOG(2) << "OnAddPrerender, child_id = " << child_id 70 Prerender prerender(launcher_child_id, prerender_id, url, referrer, size,
66 << ", prerender_id = " << prerender_id 71 render_view_route_id, manager_->GetCurrentTimeTicks());
67 << ", url = " << url.spec(); 72 prerenders_.push_back(prerender);
68 DVLOG(3) << "... referrer url = " << referrer.url.spec() 73 StartPrerenders();
69 << ", size = (" << size.width() << ", " << size.height() << ")"
70 << ", render_view_route_id = " << render_view_route_id;
71
72
73 PrerenderHandle* prerender_handle =
74 manager_->AddPrerenderFromLinkRelPrerender(
75 child_id, render_view_route_id, url, referrer, size);
76 if (!prerender_handle)
77 return false;
78
79 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
80 DCHECK_EQ(0u, ids_to_handle_map_.count(child_and_prerender_id));
81 ids_to_handle_map_[child_and_prerender_id] = prerender_handle;
82
83 // If we are given a prerender that is already prerendering, we have missed
84 // the start event.
85 if (prerender_handle->IsPrerendering())
86 OnPrerenderStart(prerender_handle);
87 prerender_handle->SetObserver(this);
88 return true;
89 } 74 }
90 75
91 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { 76 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
92 DVLOG(2) << "OnCancelPrerender, child_id = " << child_id 77 Prerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
93 << ", prerender_id = " << prerender_id; 78 prerender_id);
94 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); 79 if (!prerender)
95 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = 80 return;
96 ids_to_handle_map_.find(child_and_prerender_id); 81
97 if (id_to_handle_iter == ids_to_handle_map_.end()) { 82 // Remove the handle from the PrerenderLinkManager before we cancel this
98 DVLOG(5) << "... canceling a prerender that doesn't exist."; 83 // prerender, to avoid reentering the PrerenderLinkManager, sending events to
99 return; 84 // the underlying prerender and making a second erase.
100 } 85 scoped_ptr<PrerenderHandle> own_prerender_handle(prerender->handle);
101 86 prerender->handle = NULL;
102 scoped_ptr<PrerenderHandle> prerender_handle(id_to_handle_iter->second); 87 RemovePrerender(prerender);
103 ids_to_handle_map_.erase(id_to_handle_iter); 88
104 prerender_handle->OnCancel(); 89 if (own_prerender_handle)
105 90 own_prerender_handle->OnCancel();
106 // Because OnCancel() can remove the prerender from the map, we need to 91
107 // consider our iterator invalid. 92 StartPrerenders();
108 id_to_handle_iter = ids_to_handle_map_.find(child_and_prerender_id);
109 if (id_to_handle_iter != ids_to_handle_map_.end())
110 RemovePrerender(id_to_handle_iter);
111 } 93 }
112 94
113 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { 95 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
114 DVLOG(2) << "OnAbandonPrerender, child_id = " << child_id 96 Prerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
115 << ", prerender_id = " << prerender_id; 97 prerender_id);
116 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); 98 if (!prerender)
117 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = 99 return;
118 ids_to_handle_map_.find(child_and_prerender_id); 100
119 if (id_to_handle_iter == ids_to_handle_map_.end()) 101 if (!prerender->handle) {
120 return; 102 RemovePrerender(prerender);
121 PrerenderHandle* prerender_handle = id_to_handle_iter->second; 103 return;
122 prerender_handle->OnNavigateAway(); 104 }
105
106 prerender->handle->OnNavigateAway();
107 DCHECK(prerender->handle);
108
109 // If the prerender is not running, remove it from the list so it does not
110 // leak. If it is running, it will send a cancel event when it stops which
111 // will remove it.
112 if (!prerender->handle->IsPrerendering())
113 RemovePrerender(prerender);
123 } 114 }
124 115
125 void PrerenderLinkManager::OnChannelClosing(int child_id) { 116 void PrerenderLinkManager::OnChannelClosing(int child_id) {
126 DVLOG(2) << "OnChannelClosing, child id = " << child_id; 117 std::list<Prerender>::iterator next = prerenders_.begin();
127 const ChildAndPrerenderIdPair child_and_minimum_prerender_id( 118 while (next != prerenders_.end()) {
128 child_id, std::numeric_limits<int>::min()); 119 std::list<Prerender>::iterator it = next;
129 const ChildAndPrerenderIdPair child_and_maximum_prerender_id(
130 child_id, std::numeric_limits<int>::max());
131
132 IdPairToPrerenderHandleMap::iterator
133 it = ids_to_handle_map_.lower_bound(child_and_minimum_prerender_id);
134 IdPairToPrerenderHandleMap::iterator
135 end = ids_to_handle_map_.upper_bound(child_and_maximum_prerender_id);
136 while (it != end) {
137 IdPairToPrerenderHandleMap::iterator next = it;
138 ++next; 120 ++next;
139 121
140 size_t size_before_abandon = ids_to_handle_map_.size(); 122 if (child_id != it->launcher_child_id)
141 OnAbandonPrerender(child_id, it->first.second); 123 continue;
142 DCHECK_EQ(size_before_abandon, ids_to_handle_map_.size()); 124
143 RemovePrerender(it); 125 const size_t running_prerender_count = CountRunningPrerenders();
144 126 OnAbandonPrerender(child_id, it->prerender_id);
145 it = next; 127 DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
146 } 128 }
129 }
130
131 PrerenderLinkManager::Prerender::Prerender(int launcher_child_id,
132 int prerender_id,
133 const GURL& url,
134 const content::Referrer& referrer,
135 const gfx::Size& size,
136 int render_view_route_id,
137 TimeTicks creation_time)
138 : launcher_child_id(launcher_child_id),
139 prerender_id(prerender_id),
140 url(url),
141 referrer(referrer),
142 size(size),
143 render_view_route_id(render_view_route_id),
144 creation_time(creation_time),
145 handle(NULL) {
146 }
147
148 PrerenderLinkManager::Prerender::~Prerender() {
149 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle);
mmenke 2012/12/28 18:25:33 Think this is non-intuitive enough to be worth a c
gavinp 2012/12/28 19:53:46 Done.
147 } 150 }
148 151
149 bool PrerenderLinkManager::IsEmpty() const { 152 bool PrerenderLinkManager::IsEmpty() const {
150 return ids_to_handle_map_.empty(); 153 return prerenders_.empty();
151 } 154 }
152 155
153 void PrerenderLinkManager::RemovePrerender( 156 size_t PrerenderLinkManager::CountRunningPrerenders() const {
154 const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter) { 157 size_t retval = 0;
155 PrerenderHandle* prerender_handle = id_to_handle_iter->second; 158 for (std::list<Prerender>::const_iterator i = prerenders_.begin();
156 delete prerender_handle; 159 i != prerenders_.end(); ++i) {
157 ids_to_handle_map_.erase(id_to_handle_iter); 160 if (i->handle && i->handle->IsPrerendering())
158 } 161 ++retval;
159 162 }
160 PrerenderLinkManager::IdPairToPrerenderHandleMap::iterator 163 return retval;
161 PrerenderLinkManager::FindPrerenderHandle( 164 }
162 PrerenderHandle* prerender_handle) { 165
163 for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); 166 void PrerenderLinkManager::StartPrerenders() {
mmenke 2012/12/28 18:25:33 Some thoughts on this change (No need to address t
gavinp 2012/12/28 19:53:46 Noted.
164 it != ids_to_handle_map_.end(); ++it) { 167 if (has_shutdown_)
165 if (it->second == prerender_handle) 168 return;
166 return it; 169
167 } 170 std::set<std::pair<int, int> > launcher_and_prerender_id_set;
mmenke 2012/12/28 18:25:33 This is only used for sanity checking - are compil
gavinp 2012/12/28 19:53:46 I doubt compilers are that smart. I'm removing it.
mmenke 2012/12/28 20:15:13 optional: Actually...you could do something like:
gavinp 2012/12/28 20:54:21 Done. Also found I'd left a std::pair sitting arou
168 return ids_to_handle_map_.end(); 171
172 size_t added_prerender_count = 0;
mmenke 2012/12/28 18:25:33 nit: Think "running" or "started" would be cleare
gavinp 2012/12/28 19:53:46 total_started_prerender_count avoided confusion I
173 std::multiset<std::pair<int, int> >
174 running_launcher_and_render_view_route_set;
mmenke 2012/12/28 18:25:33 optional: You may want to use a map here instead
mmenke 2012/12/28 18:25:33 optional: May want to just pluralize this and rem
mmenke 2012/12/28 18:32:39 This is one of those time-of-day comments, btw - i
gavinp 2012/12/28 19:53:46 I liked the name change, but I can't bear to move
mmenke 2012/12/28 20:15:13 I'm content with keeping it as-is. If I had parti
175
176 // Scan the list, counting how many prerenders have handles (and so were added
177 // to the PrerenderManager). The count is done for the system as a whole, and
178 // also per launcher.
179 for (std::list<Prerender>::iterator i = prerenders_.begin();
180 i != prerenders_.end(); ++i) {
181 if (i->handle) {
182 ++added_prerender_count;
183 std::pair<int, int> launcher_and_render_view_route(
184 i->launcher_child_id, i->render_view_route_id);
185 running_launcher_and_render_view_route_set.insert(
186 launcher_and_render_view_route);
187 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
188 running_launcher_and_render_view_route_set.count(
189 launcher_and_render_view_route));
190 }
191
192 std::pair<int, int> launcher_and_prerender_id(i->launcher_child_id,
193 i->prerender_id);
194 DCHECK_EQ(0u,
195 launcher_and_prerender_id_set.count(launcher_and_prerender_id));
196 launcher_and_prerender_id_set.insert(launcher_and_prerender_id);
197 }
198 DCHECK_GE(manager_->config().max_link_concurrency, added_prerender_count);
199 DCHECK_LE(CountRunningPrerenders(), added_prerender_count);
200
201 TimeTicks now = manager_->GetCurrentTimeTicks();
202
203 // Scan the list again, starting prerenders as our counts allow.
204 std::list<Prerender>::iterator next = prerenders_.begin();
205 while (next != prerenders_.end()) {
206 std::list<Prerender>::iterator i = next;
207 ++next;
208
209 if (added_prerender_count >= manager_->config().max_link_concurrency ||
210 added_prerender_count >= prerenders_.size()) {
211 // The system is already at its prerender concurrency limit.
212 return;
213 }
214
215 if (i->handle) {
216 // This prerender has already been added to the prerender manager.
217 continue;
218 }
219
220 std::pair<int, int> launcher_and_render_view_route(
221 i->launcher_child_id, i->render_view_route_id);
222 if (manager_->config().max_link_concurrency_per_launcher <=
223 running_launcher_and_render_view_route_set.count(
224 launcher_and_render_view_route)) {
225 // This prerender's launcher is already at its limit.
226 continue;
227 }
228
229 TimeDelta prerender_age = now - i->creation_time;
230 if (prerender_age >= manager_->config().max_wait_to_launch) {
mmenke 2012/12/28 18:25:33 This should go before checking the launcher concur
gavinp 2012/12/28 19:53:46 Done.
231 // This prerender waited too long in the queue before launching.
232 prerenders_.erase(i);
233 continue;
234 }
235
236 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
237 i->launcher_child_id, i->render_view_route_id,
238 i->url, i->referrer, i->size);
239 if (!handle) {
240 // This prerender couldn't be launched, it's gone.
241 prerenders_.erase(i);
242 continue;
243 }
244
245 // We have successfully started a new prerender.
246 i->handle = handle;
247 ++added_prerender_count;
248 handle->SetObserver(this);
249 if (handle->IsPrerendering())
250 OnPrerenderStart(handle);
251
252 running_launcher_and_render_view_route_set.insert(
253 launcher_and_render_view_route);
254 }
255 }
256
257 PrerenderLinkManager::Prerender*
258 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
259 int prerender_id) {
260 for (std::list<Prerender>::iterator i = prerenders_.begin();
261 i != prerenders_.end(); ++i) {
262 if (launcher_child_id == i->launcher_child_id &&
263 prerender_id == i->prerender_id) {
264 return &(*i);
265 }
266 }
267 return NULL;
268 }
269
270 PrerenderLinkManager::Prerender*
271 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
272 DCHECK(prerender_handle);
273 for (std::list<Prerender>::iterator i = prerenders_.begin();
274 i != prerenders_.end(); ++i) {
275 if (prerender_handle == i->handle)
276 return &(*i);
277 }
278 return NULL;
279 }
280
281 void PrerenderLinkManager::RemovePrerender(Prerender* prerender) {
282 for (std::list<Prerender>::iterator i = prerenders_.begin();
283 i != prerenders_.end(); ++i) {
284 if (&(*i) == prerender) {
285 scoped_ptr<PrerenderHandle> own_handle(i->handle);
286 i->handle = NULL;
287 prerenders_.erase(i);
288 return;
289 }
290 }
291 NOTREACHED();
292 }
293
294 void PrerenderLinkManager::Shutdown() {
295 has_shutdown_ = true;
169 } 296 }
170 297
171 // In practice, this is always called from either 298 // In practice, this is always called from either
172 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending 299 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending
173 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom. 300 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom.
174 void PrerenderLinkManager::OnPrerenderStart( 301 void PrerenderLinkManager::OnPrerenderStart(
175 PrerenderHandle* prerender_handle) { 302 PrerenderHandle* prerender_handle) {
176 IdPairToPrerenderHandleMap::iterator it = 303 Prerender* prerender = FindByPrerenderHandle(prerender_handle);
177 FindPrerenderHandle(prerender_handle); 304 if (!prerender)
178 DCHECK(it != ids_to_handle_map_.end()); 305 return;
179 const int child_id = it->first.first; 306 Send(prerender->launcher_child_id,
180 const int prerender_id = it->first.second; 307 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id));
181
182 Send(child_id, new PrerenderMsg_OnPrerenderStart(prerender_id));
183 } 308 }
184 309
185 void PrerenderLinkManager::OnPrerenderAddAlias( 310 void PrerenderLinkManager::OnPrerenderAddAlias(
186 PrerenderHandle* prerender_handle, 311 PrerenderHandle* prerender_handle,
187 const GURL& alias_url) { 312 const GURL& alias_url) {
188 IdPairToPrerenderHandleMap::iterator it = 313 Prerender* prerender = FindByPrerenderHandle(prerender_handle);
189 FindPrerenderHandle(prerender_handle); 314 if (!prerender)
190 if (it == ids_to_handle_map_.end())
191 return; 315 return;
192 const int child_id = it->first.first; 316 Send(prerender->launcher_child_id,
193 const int prerender_id = it->first.second; 317 new PrerenderMsg_OnPrerenderAddAlias(prerender->prerender_id,
194 318 alias_url));
195 Send(child_id, new PrerenderMsg_OnPrerenderAddAlias(prerender_id, alias_url));
196 } 319 }
197 320
198 void PrerenderLinkManager::OnPrerenderStop( 321 void PrerenderLinkManager::OnPrerenderStop(
199 PrerenderHandle* prerender_handle) { 322 PrerenderHandle* prerender_handle) {
200 IdPairToPrerenderHandleMap::iterator it = 323 Prerender* prerender = FindByPrerenderHandle(prerender_handle);
201 FindPrerenderHandle(prerender_handle); 324 if (!prerender)
202 if (it == ids_to_handle_map_.end())
203 return; 325 return;
204 const int child_id = it->first.first;
205 const int prerender_id = it->first.second;
206 326
207 Send(child_id, new PrerenderMsg_OnPrerenderStop(prerender_id)); 327 Send(prerender->launcher_child_id,
208 RemovePrerender(it); 328 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
329 RemovePrerender(prerender);
330 StartPrerenders();
209 } 331 }
210 332
211 } // namespace prerender 333 } // namespace prerender
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698