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 "content/public/browser/browser_thread.h"
12#include "content/public/browser/render_view_host.h"
13#include "content/public/browser/resource_context.h"
14#include "net/base/load_flags.h"
15
16using content::BrowserThread;
17using content::RenderViewHost;
18
19namespace prerender {
20
21namespace {
22
23void 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
39struct RenderViewInfo {
40  explicit RenderViewInfo(PrerenderManager* prerender_manager)
41      : final_status(FINAL_STATUS_MAX),
42        prerender_manager(prerender_manager->AsWeakPtr()) {
43  }
44  ~RenderViewInfo() {}
45
46  FinalStatus final_status;
47  base::WeakPtr<PrerenderManager> prerender_manager;
48};
49
50PrerenderTracker::PrerenderTracker() {
51  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52}
53
54PrerenderTracker::~PrerenderTracker() {
55  DCHECK(final_status_map_.empty());
56}
57
58bool PrerenderTracker::TryUse(int child_id, int route_id) {
59  DCHECK(CalledOnValidThread());
60  return SetFinalStatus(child_id, route_id, FINAL_STATUS_USED, NULL);
61}
62
63bool 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
76bool 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
89bool 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
102void 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
126void 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  BrowserThread::PostTask(
138      BrowserThread::IO, FROM_HERE,
139      base::Bind(&RemovePrerenderOnIOThreadTask, child_route_id_pair));
140
141  base::AutoLock lock(final_status_map_lock_);
142  size_t num_erased = final_status_map_.erase(child_route_id_pair);
143  DCHECK_EQ(1u, num_erased);
144}
145
146bool PrerenderTracker::SetFinalStatus(int child_id, int route_id,
147                                      FinalStatus desired_final_status,
148                                      FinalStatus* actual_final_status) {
149  DCHECK(desired_final_status >= FINAL_STATUS_USED &&
150         desired_final_status < FINAL_STATUS_MAX);
151
152  ChildRouteIdPair child_route_id_pair(child_id, route_id);
153
154  base::AutoLock lock(final_status_map_lock_);
155  FinalStatusMap::iterator final_status_it =
156      final_status_map_.find(child_route_id_pair);
157  if (final_status_it == final_status_map_.end()) {
158    // The RenderView has already been either used or destroyed.
159    if (actual_final_status)
160      *actual_final_status = FINAL_STATUS_MAX;
161    return false;
162  }
163
164  if (final_status_it->second.final_status == FINAL_STATUS_MAX) {
165    final_status_it->second.final_status = desired_final_status;
166    if (desired_final_status != FINAL_STATUS_USED) {
167      BrowserThread::PostTask(
168          BrowserThread::UI, FROM_HERE,
169          base::Bind(&DestroyPrerenderForRenderViewOnUI,
170                     final_status_it->second.prerender_manager, child_id,
171                     route_id, desired_final_status));
172    }
173
174    if (actual_final_status)
175      *actual_final_status = desired_final_status;
176    return true;
177  }
178
179  if (actual_final_status)
180    *actual_final_status = final_status_it->second.final_status;
181  return false;
182}
183
184bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id,
185                                                int route_id) const {
186  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
187
188  ChildRouteIdPair child_route_id_pair(child_id, route_id);
189  return possibly_prerendering_io_thread_set_.count(child_route_id_pair) > 0;
190}
191
192void PrerenderTracker::AddPrerenderOnIOThread(
193    const ChildRouteIdPair& child_route_id_pair) {
194  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
195  DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first,
196                                   child_route_id_pair.second));
197
198  possibly_prerendering_io_thread_set_.insert(child_route_id_pair);
199}
200
201void PrerenderTracker::RemovePrerenderOnIOThread(
202    const ChildRouteIdPair& child_route_id_pair) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204  DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first,
205                                  child_route_id_pair.second));
206
207  possibly_prerendering_io_thread_set_.erase(child_route_id_pair);
208}
209
210// static
211PrerenderTracker* PrerenderTracker::GetDefault() {
212  return g_browser_process->prerender_tracker();
213}
214
215// static
216void PrerenderTracker::AddPrerenderOnIOThreadTask(
217    const ChildRouteIdPair& child_route_id_pair) {
218  GetDefault()->AddPrerenderOnIOThread(child_route_id_pair);
219}
220
221// static
222void PrerenderTracker::RemovePrerenderOnIOThreadTask(
223    const ChildRouteIdPair& child_route_id_pair) {
224  GetDefault()->RemovePrerenderOnIOThread(child_route_id_pair);
225}
226
227}  // namespace prerender
228