• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/prerender/prerender_tracker.h"
6 
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/prerender/prerender_manager.h"
11 #include "chrome/browser/prerender/prerender_pending_swap_throttle.h"
12 #include "chrome/browser/prerender/prerender_resource_throttle.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/resource_context.h"
15 #include "net/base/load_flags.h"
16 
17 using content::BrowserThread;
18 
19 namespace prerender {
20 
21 namespace {
22 
DestroyPrerenderForRenderViewOnUI(const base::WeakPtr<PrerenderManager> & prerender_manager_weak_ptr,int render_process_id,int render_view_id,FinalStatus final_status)23 void DestroyPrerenderForRenderViewOnUI(
24     const base::WeakPtr<PrerenderManager>& prerender_manager_weak_ptr,
25     int render_process_id,
26     int render_view_id,
27     FinalStatus final_status) {
28   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
29   PrerenderManager* prerender_manager = prerender_manager_weak_ptr.get();
30   if (!prerender_manager)
31     return;
32 
33   prerender_manager->DestroyPrerenderForRenderView(
34       render_process_id, render_view_id, final_status);
35 }
36 
37 }  // namespace
38 
39 struct RenderViewInfo {
RenderViewInfoprerender::RenderViewInfo40   explicit RenderViewInfo(PrerenderManager* prerender_manager)
41       : final_status(FINAL_STATUS_MAX),
42         prerender_manager(prerender_manager->AsWeakPtr()) {
43   }
~RenderViewInfoprerender::RenderViewInfo44   ~RenderViewInfo() {}
45 
46   FinalStatus final_status;
47   base::WeakPtr<PrerenderManager> prerender_manager;
48 };
49 
PrerenderTracker()50 PrerenderTracker::PrerenderTracker() {
51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 }
53 
~PrerenderTracker()54 PrerenderTracker::~PrerenderTracker() {
55   DCHECK(final_status_map_.empty());
56 }
57 
TryUse(int child_id,int route_id)58 bool PrerenderTracker::TryUse(int child_id, int route_id) {
59   DCHECK(CalledOnValidThread());
60   return SetFinalStatus(child_id, route_id, FINAL_STATUS_USED, NULL);
61 }
62 
TryCancel(int child_id,int route_id,FinalStatus final_status)63 bool PrerenderTracker::TryCancel(
64     int child_id,
65     int route_id,
66     FinalStatus final_status) {
67   DCHECK_NE(FINAL_STATUS_USED, final_status);
68   DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX);
69 
70   FinalStatus actual_final_status;
71   SetFinalStatus(child_id, route_id, final_status, &actual_final_status);
72   return actual_final_status != FINAL_STATUS_USED &&
73          actual_final_status != FINAL_STATUS_MAX;
74 }
75 
TryCancelOnIOThread(int child_id,int route_id,FinalStatus final_status)76 bool PrerenderTracker::TryCancelOnIOThread(
77     int child_id,
78     int route_id,
79     FinalStatus final_status) {
80   DCHECK_NE(FINAL_STATUS_USED, final_status);
81   DCHECK_LE(0, final_status);
82   DCHECK_GT(FINAL_STATUS_MAX, final_status);
83 
84   if (!IsPrerenderingOnIOThread(child_id, route_id))
85     return false;
86   return TryCancel(child_id, route_id, final_status);
87 }
88 
GetFinalStatus(int child_id,int route_id,FinalStatus * final_status) const89 bool PrerenderTracker::GetFinalStatus(int child_id, int route_id,
90                                       FinalStatus* final_status) const {
91   ChildRouteIdPair child_route_id_pair(child_id, route_id);
92 
93   base::AutoLock lock(final_status_map_lock_);
94   FinalStatusMap::const_iterator final_status_it =
95       final_status_map_.find(child_route_id_pair);
96   if (final_status_it == final_status_map_.end())
97     return false;
98   *final_status = final_status_it->second.final_status;
99   return true;
100 }
101 
OnPrerenderStart(PrerenderContents * prerender_contents)102 void PrerenderTracker::OnPrerenderStart(
103     PrerenderContents* prerender_contents) {
104   DCHECK(CalledOnValidThread());
105   int child_id, route_id;
106   bool got_child_id = prerender_contents->GetChildId(&child_id);
107   DCHECK(got_child_id);
108   bool got_route_id = prerender_contents->GetRouteId(&route_id);
109   DCHECK(got_route_id);
110 
111   ChildRouteIdPair child_route_id_pair(child_id, route_id);
112 
113   BrowserThread::PostTask(
114       BrowserThread::IO, FROM_HERE,
115       base::Bind(&AddPrerenderOnIOThreadTask, child_route_id_pair));
116 
117   base::AutoLock lock(final_status_map_lock_);
118   // The RenderView should not already be prerendering.
119   DCHECK_EQ(0u, final_status_map_.count(child_route_id_pair));
120 
121   final_status_map_.insert(
122       std::make_pair(child_route_id_pair,
123                      RenderViewInfo(prerender_contents->prerender_manager())));
124 }
125 
OnPrerenderStop(PrerenderContents * prerender_contents)126 void PrerenderTracker::OnPrerenderStop(
127     PrerenderContents* prerender_contents) {
128   DCHECK(CalledOnValidThread());
129   int child_id, route_id;
130   bool got_child_id = prerender_contents->GetChildId(&child_id);
131   DCHECK(got_child_id);
132   bool got_route_id = prerender_contents->GetRouteId(&route_id);
133   DCHECK(got_route_id);
134 
135   ChildRouteIdPair child_route_id_pair(child_id, route_id);
136 
137   DCHECK_LT(prerender_contents->final_status(), FINAL_STATUS_MAX);
138   BrowserThread::PostTask(
139       BrowserThread::IO, FROM_HERE,
140       base::Bind(&RemovePrerenderOnIOThreadTask, child_route_id_pair,
141                  prerender_contents->final_status()));
142 
143   base::AutoLock lock(final_status_map_lock_);
144   size_t num_erased = final_status_map_.erase(child_route_id_pair);
145   DCHECK_EQ(1u, num_erased);
146 }
147 
SetFinalStatus(int child_id,int route_id,FinalStatus desired_final_status,FinalStatus * actual_final_status)148 bool PrerenderTracker::SetFinalStatus(int child_id, int route_id,
149                                       FinalStatus desired_final_status,
150                                       FinalStatus* actual_final_status) {
151   DCHECK(desired_final_status >= FINAL_STATUS_USED &&
152          desired_final_status < FINAL_STATUS_MAX);
153 
154   ChildRouteIdPair child_route_id_pair(child_id, route_id);
155 
156   base::AutoLock lock(final_status_map_lock_);
157   FinalStatusMap::iterator final_status_it =
158       final_status_map_.find(child_route_id_pair);
159   if (final_status_it == final_status_map_.end()) {
160     // The RenderView has already been either used or destroyed.
161     if (actual_final_status)
162       *actual_final_status = FINAL_STATUS_MAX;
163     return false;
164   }
165 
166   if (final_status_it->second.final_status == FINAL_STATUS_MAX) {
167     final_status_it->second.final_status = desired_final_status;
168     if (desired_final_status != FINAL_STATUS_USED) {
169       BrowserThread::PostTask(
170           BrowserThread::UI, FROM_HERE,
171           base::Bind(&DestroyPrerenderForRenderViewOnUI,
172                      final_status_it->second.prerender_manager, child_id,
173                      route_id, desired_final_status));
174     }
175 
176     if (actual_final_status)
177       *actual_final_status = desired_final_status;
178     return true;
179   }
180 
181   if (actual_final_status)
182     *actual_final_status = final_status_it->second.final_status;
183   return false;
184 }
185 
IsPrerenderingOnIOThread(int child_id,int route_id) const186 bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id,
187                                                 int route_id) const {
188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189 
190   ChildRouteIdPair child_route_id_pair(child_id, route_id);
191   return resource_throttle_io_thread_map_.count(child_route_id_pair) > 0;
192 }
193 
IsPendingSwapRequestOnIOThread(int child_id,int route_id,const GURL & url) const194 bool PrerenderTracker::IsPendingSwapRequestOnIOThread(
195     int child_id, int route_id, const GURL& url) const {
196   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
197 
198   ChildRouteIdPair child_route_id_pair(child_id, route_id);
199   PendingSwapThrottleMap::const_iterator it =
200       pending_swap_throttle_map_.find(child_route_id_pair);
201   return (it != pending_swap_throttle_map_.end() && it->second.url == url);
202 }
203 
AddResourceThrottleOnIOThread(int child_id,int route_id,const base::WeakPtr<PrerenderResourceThrottle> & throttle)204 void PrerenderTracker::AddResourceThrottleOnIOThread(
205     int child_id,
206     int route_id,
207     const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
208   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
209 
210   ChildRouteIdPair child_route_id_pair(child_id, route_id);
211   ResourceThrottleMap::iterator resource_throttle_map_it =
212       resource_throttle_io_thread_map_.find(child_route_id_pair);
213   DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
214   resource_throttle_map_it->second.push_back(throttle);
215 }
216 
AddPendingSwapThrottleOnIOThread(int child_id,int route_id,const GURL & url,const base::WeakPtr<PrerenderPendingSwapThrottle> & throttle)217 void PrerenderTracker::AddPendingSwapThrottleOnIOThread(
218     int child_id,
219     int route_id,
220     const GURL& url,
221     const base::WeakPtr<PrerenderPendingSwapThrottle>& throttle) {
222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
223 
224   ChildRouteIdPair child_route_id_pair(child_id, route_id);
225   PendingSwapThrottleMap::iterator it =
226       pending_swap_throttle_map_.find(child_route_id_pair);
227   DCHECK(it != pending_swap_throttle_map_.end());
228   if (it == pending_swap_throttle_map_.end())
229     return;
230   it->second.throttles.push_back(throttle);
231 }
232 
AddPrerenderOnIOThread(const ChildRouteIdPair & child_route_id_pair)233 void PrerenderTracker::AddPrerenderOnIOThread(
234     const ChildRouteIdPair& child_route_id_pair) {
235   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
236   DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first,
237                                    child_route_id_pair.second));
238 
239   resource_throttle_io_thread_map_.insert(
240       std::make_pair(child_route_id_pair, ResourceThrottleList()));
241 }
242 
RemovePrerenderOnIOThread(const ChildRouteIdPair & child_route_id_pair,FinalStatus final_status)243 void PrerenderTracker::RemovePrerenderOnIOThread(
244     const ChildRouteIdPair& child_route_id_pair,
245     FinalStatus final_status) {
246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
247   DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first,
248                                   child_route_id_pair.second));
249 
250   // Cancel or resume all throttled resources.
251   ResourceThrottleMap::iterator resource_throttle_map_it =
252       resource_throttle_io_thread_map_.find(child_route_id_pair);
253   DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
254   ResourceThrottleList& throttles = resource_throttle_map_it->second;
255   for (size_t i = 0; i < throttles.size(); i++) {
256     if (throttles[i]) {
257       if (final_status == FINAL_STATUS_USED) {
258         throttles[i]->Resume();
259       } else {
260         throttles[i]->Cancel();
261       }
262     }
263   }
264   resource_throttle_io_thread_map_.erase(resource_throttle_map_it);
265 }
266 
AddPrerenderPendingSwapOnIOThread(const ChildRouteIdPair & child_route_id_pair,const GURL & url)267 void PrerenderTracker::AddPrerenderPendingSwapOnIOThread(
268     const ChildRouteIdPair& child_route_id_pair,
269     const GURL& url) {
270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
271   std::pair<PendingSwapThrottleMap::iterator, bool> insert_result =
272       pending_swap_throttle_map_.insert(std::make_pair(
273           child_route_id_pair, PendingSwapThrottleData(url)));
274   DCHECK(insert_result.second);
275 }
276 
RemovePrerenderPendingSwapOnIOThread(const ChildRouteIdPair & child_route_id_pair,bool swap_successful)277 void PrerenderTracker::RemovePrerenderPendingSwapOnIOThread(
278     const ChildRouteIdPair& child_route_id_pair,
279     bool swap_successful) {
280   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
281   PendingSwapThrottleMap::iterator it =
282       pending_swap_throttle_map_.find(child_route_id_pair);
283   DCHECK(it != pending_swap_throttle_map_.end());
284   // Cancel or resume all throttled resources.
285   for (size_t i = 0; i < it->second.throttles.size(); i++) {
286     if (!it->second.throttles[i])
287       continue;
288     if (swap_successful)
289       it->second.throttles[i]->Cancel();
290     else
291       it->second.throttles[i]->Resume();
292   }
293   pending_swap_throttle_map_.erase(child_route_id_pair);
294 }
295 
AddPrerenderPendingSwap(const ChildRouteIdPair & child_route_id_pair,const GURL & url)296 void PrerenderTracker::AddPrerenderPendingSwap(
297     const ChildRouteIdPair& child_route_id_pair,
298     const GURL& url) {
299   BrowserThread::PostTask(
300       BrowserThread::IO, FROM_HERE,
301       base::Bind(&AddPrerenderPendingSwapOnIOThreadTask,
302                  child_route_id_pair, url));
303 }
304 
RemovePrerenderPendingSwap(const ChildRouteIdPair & child_route_id_pair,bool swap_successful)305 void PrerenderTracker::RemovePrerenderPendingSwap(
306     const ChildRouteIdPair& child_route_id_pair,
307     bool swap_successful) {
308   BrowserThread::PostTask(
309       BrowserThread::IO, FROM_HERE,
310       base::Bind(&RemovePrerenderPendingSwapOnIOThreadTask,
311                  child_route_id_pair, swap_successful));
312 }
313 
PendingSwapThrottleData(const GURL & swap_url)314 PrerenderTracker::PendingSwapThrottleData::PendingSwapThrottleData(
315     const GURL& swap_url)
316     : url(swap_url) {
317 }
318 
~PendingSwapThrottleData()319 PrerenderTracker::PendingSwapThrottleData::~PendingSwapThrottleData() {
320 }
321 
322 // static
GetDefault()323 PrerenderTracker* PrerenderTracker::GetDefault() {
324   return g_browser_process->prerender_tracker();
325 }
326 
327 // static
AddPrerenderOnIOThreadTask(const ChildRouteIdPair & child_route_id_pair)328 void PrerenderTracker::AddPrerenderOnIOThreadTask(
329     const ChildRouteIdPair& child_route_id_pair) {
330   GetDefault()->AddPrerenderOnIOThread(child_route_id_pair);
331 }
332 
333 // static
RemovePrerenderOnIOThreadTask(const ChildRouteIdPair & child_route_id_pair,FinalStatus final_status)334 void PrerenderTracker::RemovePrerenderOnIOThreadTask(
335     const ChildRouteIdPair& child_route_id_pair,
336     FinalStatus final_status) {
337   GetDefault()->RemovePrerenderOnIOThread(child_route_id_pair, final_status);
338 }
339 
340 // static
AddPrerenderPendingSwapOnIOThreadTask(const ChildRouteIdPair & child_route_id_pair,const GURL & url)341 void PrerenderTracker::AddPrerenderPendingSwapOnIOThreadTask(
342     const ChildRouteIdPair& child_route_id_pair,
343     const GURL& url) {
344   GetDefault()->AddPrerenderPendingSwapOnIOThread(child_route_id_pair, url);
345 }
346 
347 // static
RemovePrerenderPendingSwapOnIOThreadTask(const ChildRouteIdPair & child_route_id_pair,bool swap_successful)348 void PrerenderTracker::RemovePrerenderPendingSwapOnIOThreadTask(
349     const ChildRouteIdPair& child_route_id_pair,
350     bool swap_successful) {
351   GetDefault()->RemovePrerenderPendingSwapOnIOThread(child_route_id_pair,
352                                                      swap_successful);
353 }
354 
355 }  // namespace prerender
356