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
15using content::BrowserThread;
16
17namespace prerender {
18
19PrerenderTracker::PrerenderTracker() {
20  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
21}
22
23PrerenderTracker::~PrerenderTracker() {
24}
25
26bool 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
37void 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
55void 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
65void 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
82void 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
91void 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
101PrerenderTracker::PendingSwapThrottleData::PendingSwapThrottleData(
102    const GURL& swap_url)
103    : url(swap_url) {
104}
105
106PrerenderTracker::PendingSwapThrottleData::~PendingSwapThrottleData() {
107}
108
109scoped_refptr<PrerenderCookieStore>
110PrerenderTracker::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
122void 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
144void 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.get(), *url_it);
170  }
171}
172
173void 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.get() != 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