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/prerender/prerender_pending_swap_throttle.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/render_process_host.h"
12 #include "net/url_request/url_request_context.h"
13 #include "net/url_request/url_request_context_getter.h"
14
15 using content::BrowserThread;
16
17 namespace prerender {
18
PrerenderTracker()19 PrerenderTracker::PrerenderTracker() {
20 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
21 }
22
~PrerenderTracker()23 PrerenderTracker::~PrerenderTracker() {
24 }
25
IsPendingSwapRequestOnIOThread(int render_process_id,int render_frame_id,const GURL & url) const26 bool PrerenderTracker::IsPendingSwapRequestOnIOThread(
27 int render_process_id, int render_frame_id, const GURL& url) const {
28 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
29
30 ChildRouteIdPair render_frame_route_id_pair(
31 render_process_id, render_frame_id);
32 PendingSwapThrottleMap::const_iterator it =
33 pending_swap_throttle_map_.find(render_frame_route_id_pair);
34 return (it != pending_swap_throttle_map_.end() && it->second.url == url);
35 }
36
AddPendingSwapThrottleOnIOThread(int render_process_id,int render_frame_id,const GURL & url,const base::WeakPtr<PrerenderPendingSwapThrottle> & throttle)37 void PrerenderTracker::AddPendingSwapThrottleOnIOThread(
38 int render_process_id,
39 int render_frame_id,
40 const GURL& url,
41 const base::WeakPtr<PrerenderPendingSwapThrottle>& throttle) {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
43
44 ChildRouteIdPair render_frame_route_id_pair(
45 render_process_id, render_frame_id);
46 PendingSwapThrottleMap::iterator it =
47 pending_swap_throttle_map_.find(render_frame_route_id_pair);
48 DCHECK(it != pending_swap_throttle_map_.end());
49 if (it == pending_swap_throttle_map_.end())
50 return;
51 CHECK(!it->second.throttle);
52 it->second.throttle = throttle;
53 }
54
AddPrerenderPendingSwapOnIOThread(const ChildRouteIdPair & render_frame_route_id_pair,const GURL & url)55 void PrerenderTracker::AddPrerenderPendingSwapOnIOThread(
56 const ChildRouteIdPair& render_frame_route_id_pair,
57 const GURL& url) {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
59 std::pair<PendingSwapThrottleMap::iterator, bool> insert_result =
60 pending_swap_throttle_map_.insert(std::make_pair(
61 render_frame_route_id_pair, PendingSwapThrottleData(url)));
62 DCHECK(insert_result.second);
63 }
64
RemovePrerenderPendingSwapOnIOThread(const ChildRouteIdPair & render_frame_route_id_pair,bool swap_successful)65 void PrerenderTracker::RemovePrerenderPendingSwapOnIOThread(
66 const ChildRouteIdPair& render_frame_route_id_pair,
67 bool swap_successful) {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
69 PendingSwapThrottleMap::iterator it =
70 pending_swap_throttle_map_.find(render_frame_route_id_pair);
71 DCHECK(it != pending_swap_throttle_map_.end());
72 // Cancel or resume all throttled resources.
73 if (it->second.throttle) {
74 if (swap_successful)
75 it->second.throttle->Cancel();
76 else
77 it->second.throttle->Resume();
78 }
79 pending_swap_throttle_map_.erase(render_frame_route_id_pair);
80 }
81
AddPrerenderPendingSwap(const ChildRouteIdPair & render_frame_route_id_pair,const GURL & url)82 void PrerenderTracker::AddPrerenderPendingSwap(
83 const ChildRouteIdPair& render_frame_route_id_pair,
84 const GURL& url) {
85 BrowserThread::PostTask(
86 BrowserThread::IO, FROM_HERE,
87 base::Bind(&PrerenderTracker::AddPrerenderPendingSwapOnIOThread,
88 base::Unretained(this), render_frame_route_id_pair, url));
89 }
90
RemovePrerenderPendingSwap(const ChildRouteIdPair & render_frame_route_id_pair,bool swap_successful)91 void PrerenderTracker::RemovePrerenderPendingSwap(
92 const ChildRouteIdPair& render_frame_route_id_pair,
93 bool swap_successful) {
94 BrowserThread::PostTask(
95 BrowserThread::IO, FROM_HERE,
96 base::Bind(&PrerenderTracker::RemovePrerenderPendingSwapOnIOThread,
97 base::Unretained(this), render_frame_route_id_pair,
98 swap_successful));
99 }
100
PendingSwapThrottleData(const GURL & swap_url)101 PrerenderTracker::PendingSwapThrottleData::PendingSwapThrottleData(
102 const GURL& swap_url)
103 : url(swap_url) {
104 }
105
~PendingSwapThrottleData()106 PrerenderTracker::PendingSwapThrottleData::~PendingSwapThrottleData() {
107 }
108
109 scoped_refptr<PrerenderCookieStore>
GetPrerenderCookieStoreForRenderProcess(int process_id)110 PrerenderTracker::GetPrerenderCookieStoreForRenderProcess(
111 int process_id) {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
113 PrerenderCookieStoreMap::const_iterator it =
114 prerender_cookie_store_map_.find(process_id);
115
116 if (it == prerender_cookie_store_map_.end())
117 return NULL;
118
119 return it->second;
120 }
121
OnCookieChangedForURL(int process_id,net::CookieMonster * cookie_monster,const GURL & url)122 void PrerenderTracker::OnCookieChangedForURL(
123 int process_id,
124 net::CookieMonster* cookie_monster,
125 const GURL& url) {
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
127
128 // We only care about cookie changes by non-prerender tabs, since only those
129 // get applied to the underlying cookie store. Therefore, if a cookie change
130 // originated from a prerender, there is nothing to do.
131 if (ContainsKey(prerender_cookie_store_map_, process_id))
132 return;
133
134 // Since the cookie change did not come from a prerender, broadcast it too
135 // all prerenders so that they can be cancelled if there is a conflict.
136 for (PrerenderCookieStoreMap::iterator it =
137 prerender_cookie_store_map_.begin();
138 it != prerender_cookie_store_map_.end();
139 ++it) {
140 it->second->OnCookieChangedForURL(cookie_monster, url);
141 }
142 }
143
RemovePrerenderCookieStoreOnIOThread(int process_id,bool was_swapped)144 void PrerenderTracker::RemovePrerenderCookieStoreOnIOThread(int process_id,
145 bool was_swapped) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147
148 PrerenderCookieStoreMap::iterator it =
149 prerender_cookie_store_map_.find(process_id);
150
151 if (it == prerender_cookie_store_map_.end())
152 return;
153
154 std::vector<GURL> cookie_change_urls;
155 if (was_swapped)
156 it->second->ApplyChanges(&cookie_change_urls);
157
158 scoped_refptr<net::CookieMonster> cookie_monster(
159 it->second->default_cookie_monster());
160
161 prerender_cookie_store_map_.erase(it);
162
163 // For each cookie updated by ApplyChanges, we need to call
164 // OnCookieChangedForURL so that any potentially conflicting prerenders
165 // will be aborted.
166 for (std::vector<GURL>::const_iterator url_it = cookie_change_urls.begin();
167 url_it != cookie_change_urls.end();
168 ++url_it) {
169 OnCookieChangedForURL(process_id, cookie_monster, *url_it);
170 }
171 }
172
AddPrerenderCookieStoreOnIOThread(int process_id,scoped_refptr<net::URLRequestContextGetter> request_context,const base::Closure & cookie_conflict_cb)173 void PrerenderTracker::AddPrerenderCookieStoreOnIOThread(
174 int process_id,
175 scoped_refptr<net::URLRequestContextGetter> request_context,
176 const base::Closure& cookie_conflict_cb) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178 DCHECK(request_context != NULL);
179 net::CookieMonster* cookie_monster =
180 request_context->GetURLRequestContext()->cookie_store()->
181 GetCookieMonster();
182 DCHECK(cookie_monster != NULL);
183 bool exists = (prerender_cookie_store_map_.find(process_id) !=
184 prerender_cookie_store_map_.end());
185 DCHECK(!exists);
186 if (exists)
187 return;
188 prerender_cookie_store_map_[process_id] =
189 new PrerenderCookieStore(make_scoped_refptr(cookie_monster),
190 cookie_conflict_cb);
191 }
192
193 } // namespace prerender
194