Home | History | Annotate | Download | only in prerender
      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 
     16 using content::BrowserThread;
     17 using content::RenderViewHost;
     18 
     19 namespace prerender {
     20 
     21 namespace {
     22 
     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 {
     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 
     50 PrerenderTracker::PrerenderTracker() {
     51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     52 }
     53 
     54 PrerenderTracker::~PrerenderTracker() {
     55   DCHECK(final_status_map_.empty());
     56 }
     57 
     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 
     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 
     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 
     89 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 
    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 
    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   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 
    146 bool 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 
    184 bool 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 
    192 void 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 
    201 void 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
    211 PrerenderTracker* PrerenderTracker::GetDefault() {
    212   return g_browser_process->prerender_tracker();
    213 }
    214 
    215 // static
    216 void PrerenderTracker::AddPrerenderOnIOThreadTask(
    217     const ChildRouteIdPair& child_route_id_pair) {
    218   GetDefault()->AddPrerenderOnIOThread(child_route_id_pair);
    219 }
    220 
    221 // static
    222 void PrerenderTracker::RemovePrerenderOnIOThreadTask(
    223     const ChildRouteIdPair& child_route_id_pair) {
    224   GetDefault()->RemovePrerenderOnIOThread(child_route_id_pair);
    225 }
    226 
    227 }  // namespace prerender
    228