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_tracker.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_resource_throttle.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/render_view_host.h"
14 #include "content/public/browser/resource_context.h"
15 #include "net/base/load_flags.h"
17 using content::BrowserThread;
18 using content::RenderViewHost;
24 void DestroyPrerenderForRenderViewOnUI(
25 const base::WeakPtr<PrerenderManager>& prerender_manager_weak_ptr,
26 int render_process_id,
28 FinalStatus final_status) {
29 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
30 PrerenderManager* prerender_manager = prerender_manager_weak_ptr.get();
31 if (!prerender_manager)
34 prerender_manager->DestroyPrerenderForRenderView(
35 render_process_id, render_view_id, final_status);
40 struct RenderViewInfo {
41 explicit RenderViewInfo(PrerenderManager* prerender_manager)
42 : final_status(FINAL_STATUS_MAX),
43 prerender_manager(prerender_manager->AsWeakPtr()) {
47 FinalStatus final_status;
48 base::WeakPtr<PrerenderManager> prerender_manager;
51 PrerenderTracker::PrerenderTracker() {
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55 PrerenderTracker::~PrerenderTracker() {
56 DCHECK(final_status_map_.empty());
59 bool PrerenderTracker::TryUse(int child_id, int route_id) {
60 DCHECK(CalledOnValidThread());
61 return SetFinalStatus(child_id, route_id, FINAL_STATUS_USED, NULL);
64 bool PrerenderTracker::TryCancel(
67 FinalStatus final_status) {
68 DCHECK_NE(FINAL_STATUS_USED, final_status);
69 DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX);
71 FinalStatus actual_final_status;
72 SetFinalStatus(child_id, route_id, final_status, &actual_final_status);
73 return actual_final_status != FINAL_STATUS_USED &&
74 actual_final_status != FINAL_STATUS_MAX;
77 bool PrerenderTracker::TryCancelOnIOThread(
80 FinalStatus final_status) {
81 DCHECK_NE(FINAL_STATUS_USED, final_status);
82 DCHECK_LE(0, final_status);
83 DCHECK_GT(FINAL_STATUS_MAX, final_status);
85 if (!IsPrerenderingOnIOThread(child_id, route_id))
87 return TryCancel(child_id, route_id, final_status);
90 bool PrerenderTracker::GetFinalStatus(int child_id, int route_id,
91 FinalStatus* final_status) const {
92 ChildRouteIdPair child_route_id_pair(child_id, route_id);
94 base::AutoLock lock(final_status_map_lock_);
95 FinalStatusMap::const_iterator final_status_it =
96 final_status_map_.find(child_route_id_pair);
97 if (final_status_it == final_status_map_.end())
99 *final_status = final_status_it->second.final_status;
103 void PrerenderTracker::OnPrerenderStart(
104 PrerenderContents* prerender_contents) {
105 DCHECK(CalledOnValidThread());
106 int child_id, route_id;
107 bool got_child_id = prerender_contents->GetChildId(&child_id);
108 DCHECK(got_child_id);
109 bool got_route_id = prerender_contents->GetRouteId(&route_id);
110 DCHECK(got_route_id);
112 ChildRouteIdPair child_route_id_pair(child_id, route_id);
114 BrowserThread::PostTask(
115 BrowserThread::IO, FROM_HERE,
116 base::Bind(&AddPrerenderOnIOThreadTask, child_route_id_pair));
118 base::AutoLock lock(final_status_map_lock_);
119 // The RenderView should not already be prerendering.
120 DCHECK_EQ(0u, final_status_map_.count(child_route_id_pair));
122 final_status_map_.insert(
123 std::make_pair(child_route_id_pair,
124 RenderViewInfo(prerender_contents->prerender_manager())));
127 void PrerenderTracker::OnPrerenderStop(
128 PrerenderContents* prerender_contents) {
129 DCHECK(CalledOnValidThread());
130 int child_id, route_id;
131 bool got_child_id = prerender_contents->GetChildId(&child_id);
132 DCHECK(got_child_id);
133 bool got_route_id = prerender_contents->GetRouteId(&route_id);
134 DCHECK(got_route_id);
136 ChildRouteIdPair child_route_id_pair(child_id, route_id);
138 DCHECK_LT(prerender_contents->final_status(), FINAL_STATUS_MAX);
139 BrowserThread::PostTask(
140 BrowserThread::IO, FROM_HERE,
141 base::Bind(&RemovePrerenderOnIOThreadTask, child_route_id_pair,
142 prerender_contents->final_status()));
144 base::AutoLock lock(final_status_map_lock_);
145 size_t num_erased = final_status_map_.erase(child_route_id_pair);
146 DCHECK_EQ(1u, num_erased);
149 bool PrerenderTracker::SetFinalStatus(int child_id, int route_id,
150 FinalStatus desired_final_status,
151 FinalStatus* actual_final_status) {
152 DCHECK(desired_final_status >= FINAL_STATUS_USED &&
153 desired_final_status < FINAL_STATUS_MAX);
155 ChildRouteIdPair child_route_id_pair(child_id, route_id);
157 base::AutoLock lock(final_status_map_lock_);
158 FinalStatusMap::iterator final_status_it =
159 final_status_map_.find(child_route_id_pair);
160 if (final_status_it == final_status_map_.end()) {
161 // The RenderView has already been either used or destroyed.
162 if (actual_final_status)
163 *actual_final_status = FINAL_STATUS_MAX;
167 if (final_status_it->second.final_status == FINAL_STATUS_MAX) {
168 final_status_it->second.final_status = desired_final_status;
169 if (desired_final_status != FINAL_STATUS_USED) {
170 BrowserThread::PostTask(
171 BrowserThread::UI, FROM_HERE,
172 base::Bind(&DestroyPrerenderForRenderViewOnUI,
173 final_status_it->second.prerender_manager, child_id,
174 route_id, desired_final_status));
177 if (actual_final_status)
178 *actual_final_status = desired_final_status;
182 if (actual_final_status)
183 *actual_final_status = final_status_it->second.final_status;
187 bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id,
188 int route_id) const {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
191 ChildRouteIdPair child_route_id_pair(child_id, route_id);
192 return resource_throttle_io_thread_map_.count(child_route_id_pair) > 0;
195 void PrerenderTracker::AddResourceThrottleOnIOThread(
198 const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201 ChildRouteIdPair child_route_id_pair(child_id, route_id);
202 ResourceThrottleMap::iterator resource_throttle_map_it =
203 resource_throttle_io_thread_map_.find(child_route_id_pair);
204 DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
205 resource_throttle_map_it->second.push_back(throttle);
208 void PrerenderTracker::AddPrerenderOnIOThread(
209 const ChildRouteIdPair& child_route_id_pair) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211 DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first,
212 child_route_id_pair.second));
214 resource_throttle_io_thread_map_.insert(
215 std::make_pair(child_route_id_pair, ResourceThrottleList()));
218 void PrerenderTracker::RemovePrerenderOnIOThread(
219 const ChildRouteIdPair& child_route_id_pair,
220 FinalStatus final_status) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
222 DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first,
223 child_route_id_pair.second));
225 // Cancel or resume all throttled resources.
226 ResourceThrottleMap::iterator resource_throttle_map_it =
227 resource_throttle_io_thread_map_.find(child_route_id_pair);
228 DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
229 ResourceThrottleList& throttles = resource_throttle_map_it->second;
230 for (size_t i = 0; i < throttles.size(); i++) {
232 if (final_status == FINAL_STATUS_USED) {
233 throttles[i]->Resume();
235 throttles[i]->Cancel();
239 resource_throttle_io_thread_map_.erase(resource_throttle_map_it);
243 PrerenderTracker* PrerenderTracker::GetDefault() {
244 return g_browser_process->prerender_tracker();
248 void PrerenderTracker::AddPrerenderOnIOThreadTask(
249 const ChildRouteIdPair& child_route_id_pair) {
250 GetDefault()->AddPrerenderOnIOThread(child_route_id_pair);
254 void PrerenderTracker::RemovePrerenderOnIOThreadTask(
255 const ChildRouteIdPair& child_route_id_pair,
256 FinalStatus final_status) {
257 GetDefault()->RemovePrerenderOnIOThread(child_route_id_pair, final_status);
260 } // namespace prerender