OLD | NEW |
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<LinkPrerender>::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 DCHECK_EQ(static_cast<LinkPrerender*>(NULL), |
66 << ", prerender_id = " << prerender_id | 71 FindByLauncherChildIdAndPrerenderId(launcher_child_id, |
67 << ", url = " << url.spec(); | 72 prerender_id)); |
68 DVLOG(3) << "... referrer url = " << referrer.url.spec() | 73 LinkPrerender |
69 << ", size = (" << size.width() << ", " << size.height() << ")" | 74 prerender(launcher_child_id, prerender_id, url, referrer, size, |
70 << ", render_view_route_id = " << render_view_route_id; | 75 render_view_route_id, manager_->GetCurrentTimeTicks()); |
71 | 76 prerenders_.push_back(prerender); |
72 | 77 StartPrerenders(); |
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 } | 78 } |
90 | 79 |
91 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { | 80 void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { |
92 DVLOG(2) << "OnCancelPrerender, child_id = " << child_id | 81 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id, |
93 << ", prerender_id = " << prerender_id; | 82 prerender_id); |
94 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); | 83 if (!prerender) |
95 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = | 84 return; |
96 ids_to_handle_map_.find(child_and_prerender_id); | 85 |
97 if (id_to_handle_iter == ids_to_handle_map_.end()) { | 86 // Remove the handle from the PrerenderLinkManager before we cancel this |
98 DVLOG(5) << "... canceling a prerender that doesn't exist."; | 87 // prerender, to avoid reentering the PrerenderLinkManager, sending events to |
99 return; | 88 // the underlying prerender and making a second erase. |
100 } | 89 scoped_ptr<PrerenderHandle> own_prerender_handle(prerender->handle); |
101 | 90 prerender->handle = NULL; |
102 scoped_ptr<PrerenderHandle> prerender_handle(id_to_handle_iter->second); | 91 RemovePrerender(prerender); |
103 ids_to_handle_map_.erase(id_to_handle_iter); | 92 |
104 prerender_handle->OnCancel(); | 93 if (own_prerender_handle) |
105 | 94 own_prerender_handle->OnCancel(); |
106 // Because OnCancel() can remove the prerender from the map, we need to | 95 |
107 // consider our iterator invalid. | 96 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 } | 97 } |
112 | 98 |
113 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { | 99 void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { |
114 DVLOG(2) << "OnAbandonPrerender, child_id = " << child_id | 100 LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id, |
115 << ", prerender_id = " << prerender_id; | 101 prerender_id); |
116 const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); | 102 if (!prerender) |
117 IdPairToPrerenderHandleMap::iterator id_to_handle_iter = | 103 return; |
118 ids_to_handle_map_.find(child_and_prerender_id); | 104 |
119 if (id_to_handle_iter == ids_to_handle_map_.end()) | 105 if (!prerender->handle) { |
120 return; | 106 RemovePrerender(prerender); |
121 PrerenderHandle* prerender_handle = id_to_handle_iter->second; | 107 return; |
122 prerender_handle->OnNavigateAway(); | 108 } |
| 109 |
| 110 prerender->handle->OnNavigateAway(); |
| 111 DCHECK(prerender->handle); |
| 112 |
| 113 // If the prerender is not running, remove it from the list so it does not |
| 114 // leak. If it is running, it will send a cancel event when it stops which |
| 115 // will remove it. |
| 116 if (!prerender->handle->IsPrerendering()) |
| 117 RemovePrerender(prerender); |
123 } | 118 } |
124 | 119 |
125 void PrerenderLinkManager::OnChannelClosing(int child_id) { | 120 void PrerenderLinkManager::OnChannelClosing(int child_id) { |
126 DVLOG(2) << "OnChannelClosing, child id = " << child_id; | 121 std::list<LinkPrerender>::iterator next = prerenders_.begin(); |
127 const ChildAndPrerenderIdPair child_and_minimum_prerender_id( | 122 while (next != prerenders_.end()) { |
128 child_id, std::numeric_limits<int>::min()); | 123 std::list<LinkPrerender>::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; | 124 ++next; |
139 | 125 |
140 size_t size_before_abandon = ids_to_handle_map_.size(); | 126 if (child_id != it->launcher_child_id) |
141 OnAbandonPrerender(child_id, it->first.second); | 127 continue; |
142 DCHECK_EQ(size_before_abandon, ids_to_handle_map_.size()); | 128 |
143 RemovePrerender(it); | 129 const size_t running_prerender_count = CountRunningPrerenders(); |
144 | 130 OnAbandonPrerender(child_id, it->prerender_id); |
145 it = next; | 131 DCHECK_EQ(running_prerender_count, CountRunningPrerenders()); |
146 } | 132 } |
| 133 } |
| 134 |
| 135 PrerenderLinkManager::LinkPrerender::LinkPrerender( |
| 136 int launcher_child_id, |
| 137 int prerender_id, |
| 138 const GURL& url, |
| 139 const content::Referrer& referrer, |
| 140 const gfx::Size& size, |
| 141 int render_view_route_id, |
| 142 TimeTicks creation_time) : launcher_child_id(launcher_child_id), |
| 143 prerender_id(prerender_id), |
| 144 url(url), |
| 145 referrer(referrer), |
| 146 size(size), |
| 147 render_view_route_id(render_view_route_id), |
| 148 creation_time(creation_time), |
| 149 handle(NULL) { |
| 150 } |
| 151 |
| 152 PrerenderLinkManager::LinkPrerender::~LinkPrerender() { |
| 153 DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle) |
| 154 << "The PrerenderHandle should be destroyed before its Prerender."; |
147 } | 155 } |
148 | 156 |
149 bool PrerenderLinkManager::IsEmpty() const { | 157 bool PrerenderLinkManager::IsEmpty() const { |
150 return ids_to_handle_map_.empty(); | 158 return prerenders_.empty(); |
151 } | 159 } |
152 | 160 |
153 void PrerenderLinkManager::RemovePrerender( | 161 size_t PrerenderLinkManager::CountRunningPrerenders() const { |
154 const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter) { | 162 size_t retval = 0; |
155 PrerenderHandle* prerender_handle = id_to_handle_iter->second; | 163 for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin(); |
156 delete prerender_handle; | 164 i != prerenders_.end(); ++i) { |
157 ids_to_handle_map_.erase(id_to_handle_iter); | 165 if (i->handle && i->handle->IsPrerendering()) |
158 } | 166 ++retval; |
159 | 167 } |
160 PrerenderLinkManager::IdPairToPrerenderHandleMap::iterator | 168 return retval; |
161 PrerenderLinkManager::FindPrerenderHandle( | 169 } |
162 PrerenderHandle* prerender_handle) { | 170 |
163 for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); | 171 void PrerenderLinkManager::StartPrerenders() { |
164 it != ids_to_handle_map_.end(); ++it) { | 172 if (has_shutdown_) |
165 if (it->second == prerender_handle) | 173 return; |
166 return it; | 174 |
167 } | 175 size_t total_started_prerender_count = 0; |
168 return ids_to_handle_map_.end(); | 176 std::multiset<std::pair<int, int> > |
| 177 running_launcher_and_render_view_routes; |
| 178 |
| 179 // Scan the list, counting how many prerenders have handles (and so were added |
| 180 // to the PrerenderManager). The count is done for the system as a whole, and |
| 181 // also per launcher. |
| 182 for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| 183 i != prerenders_.end(); ++i) { |
| 184 if (i->handle) { |
| 185 ++total_started_prerender_count; |
| 186 std::pair<int, int> launcher_and_render_view_route( |
| 187 i->launcher_child_id, i->render_view_route_id); |
| 188 running_launcher_and_render_view_routes.insert( |
| 189 launcher_and_render_view_route); |
| 190 DCHECK_GE(manager_->config().max_link_concurrency_per_launcher, |
| 191 running_launcher_and_render_view_routes.count( |
| 192 launcher_and_render_view_route)); |
| 193 } |
| 194 |
| 195 DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id, |
| 196 i->prerender_id)); |
| 197 } |
| 198 DCHECK_GE(manager_->config().max_link_concurrency, |
| 199 total_started_prerender_count); |
| 200 DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count); |
| 201 |
| 202 TimeTicks now = manager_->GetCurrentTimeTicks(); |
| 203 |
| 204 // Scan the list again, starting prerenders as our counts allow. |
| 205 std::list<LinkPrerender>::iterator next = prerenders_.begin(); |
| 206 while (next != prerenders_.end()) { |
| 207 std::list<LinkPrerender>::iterator i = next; |
| 208 ++next; |
| 209 |
| 210 if (total_started_prerender_count >= |
| 211 manager_->config().max_link_concurrency || |
| 212 total_started_prerender_count >= prerenders_.size()) { |
| 213 // The system is already at its prerender concurrency limit. |
| 214 return; |
| 215 } |
| 216 |
| 217 if (i->handle) { |
| 218 // This prerender has already been added to the prerender manager. |
| 219 continue; |
| 220 } |
| 221 |
| 222 TimeDelta prerender_age = now - i->creation_time; |
| 223 if (prerender_age >= manager_->config().max_wait_to_launch) { |
| 224 // This prerender waited too long in the queue before launching. |
| 225 prerenders_.erase(i); |
| 226 continue; |
| 227 } |
| 228 |
| 229 std::pair<int, int> launcher_and_render_view_route( |
| 230 i->launcher_child_id, i->render_view_route_id); |
| 231 if (manager_->config().max_link_concurrency_per_launcher <= |
| 232 running_launcher_and_render_view_routes.count( |
| 233 launcher_and_render_view_route)) { |
| 234 // This prerender's launcher is already at its limit. |
| 235 continue; |
| 236 } |
| 237 |
| 238 PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender( |
| 239 i->launcher_child_id, i->render_view_route_id, |
| 240 i->url, i->referrer, i->size); |
| 241 if (!handle) { |
| 242 // This prerender couldn't be launched, it's gone. |
| 243 prerenders_.erase(i); |
| 244 continue; |
| 245 } |
| 246 |
| 247 // We have successfully started a new prerender. |
| 248 i->handle = handle; |
| 249 ++total_started_prerender_count; |
| 250 handle->SetObserver(this); |
| 251 if (handle->IsPrerendering()) |
| 252 OnPrerenderStart(handle); |
| 253 |
| 254 running_launcher_and_render_view_routes.insert( |
| 255 launcher_and_render_view_route); |
| 256 } |
| 257 } |
| 258 |
| 259 PrerenderLinkManager::LinkPrerender* |
| 260 PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id, |
| 261 int prerender_id) { |
| 262 for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| 263 i != prerenders_.end(); ++i) { |
| 264 if (launcher_child_id == i->launcher_child_id && |
| 265 prerender_id == i->prerender_id) { |
| 266 return &(*i); |
| 267 } |
| 268 } |
| 269 return NULL; |
| 270 } |
| 271 |
| 272 PrerenderLinkManager::LinkPrerender* |
| 273 PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) { |
| 274 DCHECK(prerender_handle); |
| 275 for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| 276 i != prerenders_.end(); ++i) { |
| 277 if (prerender_handle == i->handle) |
| 278 return &(*i); |
| 279 } |
| 280 return NULL; |
| 281 } |
| 282 |
| 283 void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) { |
| 284 for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| 285 i != prerenders_.end(); ++i) { |
| 286 if (&(*i) == prerender) { |
| 287 scoped_ptr<PrerenderHandle> own_handle(i->handle); |
| 288 i->handle = NULL; |
| 289 prerenders_.erase(i); |
| 290 return; |
| 291 } |
| 292 } |
| 293 NOTREACHED(); |
| 294 } |
| 295 |
| 296 void PrerenderLinkManager::Shutdown() { |
| 297 has_shutdown_ = true; |
169 } | 298 } |
170 | 299 |
171 // In practice, this is always called from either | 300 // In practice, this is always called from either |
172 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending | 301 // PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending |
173 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom. | 302 // prerender case, from PrerenderHandle::AdoptPrerenderDataFrom. |
174 void PrerenderLinkManager::OnPrerenderStart( | 303 void PrerenderLinkManager::OnPrerenderStart( |
175 PrerenderHandle* prerender_handle) { | 304 PrerenderHandle* prerender_handle) { |
176 IdPairToPrerenderHandleMap::iterator it = | 305 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle); |
177 FindPrerenderHandle(prerender_handle); | 306 if (!prerender) |
178 DCHECK(it != ids_to_handle_map_.end()); | 307 return; |
179 const int child_id = it->first.first; | 308 Send(prerender->launcher_child_id, |
180 const int prerender_id = it->first.second; | 309 new PrerenderMsg_OnPrerenderStart(prerender->prerender_id)); |
181 | |
182 Send(child_id, new PrerenderMsg_OnPrerenderStart(prerender_id)); | |
183 } | 310 } |
184 | 311 |
185 void PrerenderLinkManager::OnPrerenderAddAlias( | 312 void PrerenderLinkManager::OnPrerenderAddAlias( |
186 PrerenderHandle* prerender_handle, | 313 PrerenderHandle* prerender_handle, |
187 const GURL& alias_url) { | 314 const GURL& alias_url) { |
188 IdPairToPrerenderHandleMap::iterator it = | 315 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle); |
189 FindPrerenderHandle(prerender_handle); | 316 if (!prerender) |
190 if (it == ids_to_handle_map_.end()) | |
191 return; | 317 return; |
192 const int child_id = it->first.first; | 318 Send(prerender->launcher_child_id, |
193 const int prerender_id = it->first.second; | 319 new PrerenderMsg_OnPrerenderAddAlias(prerender->prerender_id, |
194 | 320 alias_url)); |
195 Send(child_id, new PrerenderMsg_OnPrerenderAddAlias(prerender_id, alias_url)); | |
196 } | 321 } |
197 | 322 |
198 void PrerenderLinkManager::OnPrerenderStop( | 323 void PrerenderLinkManager::OnPrerenderStop( |
199 PrerenderHandle* prerender_handle) { | 324 PrerenderHandle* prerender_handle) { |
200 IdPairToPrerenderHandleMap::iterator it = | 325 LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle); |
201 FindPrerenderHandle(prerender_handle); | 326 if (!prerender) |
202 if (it == ids_to_handle_map_.end()) | |
203 return; | 327 return; |
204 const int child_id = it->first.first; | |
205 const int prerender_id = it->first.second; | |
206 | 328 |
207 Send(child_id, new PrerenderMsg_OnPrerenderStop(prerender_id)); | 329 Send(prerender->launcher_child_id, |
208 RemovePrerender(it); | 330 new PrerenderMsg_OnPrerenderStop(prerender->prerender_id)); |
| 331 RemovePrerender(prerender); |
| 332 StartPrerenders(); |
209 } | 333 } |
210 | 334 |
211 } // namespace prerender | 335 } // namespace prerender |
OLD | NEW |