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 "chrome/browser/prerender/prerender_pending_swap_throttle.h"
     12 #include "chrome/browser/prerender/prerender_resource_throttle.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "content/public/browser/resource_context.h"
     15 #include "net/base/load_flags.h"
     16 
     17 using content::BrowserThread;
     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   DCHECK_LT(prerender_contents->final_status(), FINAL_STATUS_MAX);
    138   BrowserThread::PostTask(
    139       BrowserThread::IO, FROM_HERE,
    140       base::Bind(&RemovePrerenderOnIOThreadTask, child_route_id_pair,
    141                  prerender_contents->final_status()));
    142 
    143   base::AutoLock lock(final_status_map_lock_);
    144   size_t num_erased = final_status_map_.erase(child_route_id_pair);
    145   DCHECK_EQ(1u, num_erased);
    146 }
    147 
    148 bool PrerenderTracker::SetFinalStatus(int child_id, int route_id,
    149                                       FinalStatus desired_final_status,
    150                                       FinalStatus* actual_final_status) {
    151   DCHECK(desired_final_status >= FINAL_STATUS_USED &&
    152          desired_final_status < FINAL_STATUS_MAX);
    153 
    154   ChildRouteIdPair child_route_id_pair(child_id, route_id);
    155 
    156   base::AutoLock lock(final_status_map_lock_);
    157   FinalStatusMap::iterator final_status_it =
    158       final_status_map_.find(child_route_id_pair);
    159   if (final_status_it == final_status_map_.end()) {
    160     // The RenderView has already been either used or destroyed.
    161     if (actual_final_status)
    162       *actual_final_status = FINAL_STATUS_MAX;
    163     return false;
    164   }
    165 
    166   if (final_status_it->second.final_status == FINAL_STATUS_MAX) {
    167     final_status_it->second.final_status = desired_final_status;
    168     if (desired_final_status != FINAL_STATUS_USED) {
    169       BrowserThread::PostTask(
    170           BrowserThread::UI, FROM_HERE,
    171           base::Bind(&DestroyPrerenderForRenderViewOnUI,
    172                      final_status_it->second.prerender_manager, child_id,
    173                      route_id, desired_final_status));
    174     }
    175 
    176     if (actual_final_status)
    177       *actual_final_status = desired_final_status;
    178     return true;
    179   }
    180 
    181   if (actual_final_status)
    182     *actual_final_status = final_status_it->second.final_status;
    183   return false;
    184 }
    185 
    186 bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id,
    187                                                 int route_id) const {
    188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    189 
    190   ChildRouteIdPair child_route_id_pair(child_id, route_id);
    191   return resource_throttle_io_thread_map_.count(child_route_id_pair) > 0;
    192 }
    193 
    194 bool PrerenderTracker::IsPendingSwapRequestOnIOThread(
    195     int child_id, int route_id, const GURL& url) const {
    196   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    197 
    198   ChildRouteIdPair child_route_id_pair(child_id, route_id);
    199   PendingSwapThrottleMap::const_iterator it =
    200       pending_swap_throttle_map_.find(child_route_id_pair);
    201   return (it != pending_swap_throttle_map_.end() && it->second.url == url);
    202 }
    203 
    204 void PrerenderTracker::AddResourceThrottleOnIOThread(
    205     int child_id,
    206     int route_id,
    207     const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
    208   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    209 
    210   ChildRouteIdPair child_route_id_pair(child_id, route_id);
    211   ResourceThrottleMap::iterator resource_throttle_map_it =
    212       resource_throttle_io_thread_map_.find(child_route_id_pair);
    213   DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
    214   resource_throttle_map_it->second.push_back(throttle);
    215 }
    216 
    217 void PrerenderTracker::AddPendingSwapThrottleOnIOThread(
    218     int child_id,
    219     int route_id,
    220     const GURL& url,
    221     const base::WeakPtr<PrerenderPendingSwapThrottle>& throttle) {
    222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    223 
    224   ChildRouteIdPair child_route_id_pair(child_id, route_id);
    225   PendingSwapThrottleMap::iterator it =
    226       pending_swap_throttle_map_.find(child_route_id_pair);
    227   DCHECK(it != pending_swap_throttle_map_.end());
    228   if (it == pending_swap_throttle_map_.end())
    229     return;
    230   it->second.throttles.push_back(throttle);
    231 }
    232 
    233 void PrerenderTracker::AddPrerenderOnIOThread(
    234     const ChildRouteIdPair& child_route_id_pair) {
    235   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    236   DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first,
    237                                    child_route_id_pair.second));
    238 
    239   resource_throttle_io_thread_map_.insert(
    240       std::make_pair(child_route_id_pair, ResourceThrottleList()));
    241 }
    242 
    243 void PrerenderTracker::RemovePrerenderOnIOThread(
    244     const ChildRouteIdPair& child_route_id_pair,
    245     FinalStatus final_status) {
    246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    247   DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first,
    248                                   child_route_id_pair.second));
    249 
    250   // Cancel or resume all throttled resources.
    251   ResourceThrottleMap::iterator resource_throttle_map_it =
    252       resource_throttle_io_thread_map_.find(child_route_id_pair);
    253   DCHECK(resource_throttle_map_it != resource_throttle_io_thread_map_.end());
    254   ResourceThrottleList& throttles = resource_throttle_map_it->second;
    255   for (size_t i = 0; i < throttles.size(); i++) {
    256     if (throttles[i]) {
    257       if (final_status == FINAL_STATUS_USED) {
    258         throttles[i]->Resume();
    259       } else {
    260         throttles[i]->Cancel();
    261       }
    262     }
    263   }
    264   resource_throttle_io_thread_map_.erase(resource_throttle_map_it);
    265 }
    266 
    267 void PrerenderTracker::AddPrerenderPendingSwapOnIOThread(
    268     const ChildRouteIdPair& child_route_id_pair,
    269     const GURL& url) {
    270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    271   std::pair<PendingSwapThrottleMap::iterator, bool> insert_result =
    272       pending_swap_throttle_map_.insert(std::make_pair(
    273           child_route_id_pair, PendingSwapThrottleData(url)));
    274   DCHECK(insert_result.second);
    275 }
    276 
    277 void PrerenderTracker::RemovePrerenderPendingSwapOnIOThread(
    278     const ChildRouteIdPair& child_route_id_pair,
    279     bool swap_successful) {
    280   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    281   PendingSwapThrottleMap::iterator it =
    282       pending_swap_throttle_map_.find(child_route_id_pair);
    283   DCHECK(it != pending_swap_throttle_map_.end());
    284   // Cancel or resume all throttled resources.
    285   for (size_t i = 0; i < it->second.throttles.size(); i++) {
    286     if (!it->second.throttles[i])
    287       continue;
    288     if (swap_successful)
    289       it->second.throttles[i]->Cancel();
    290     else
    291       it->second.throttles[i]->Resume();
    292   }
    293   pending_swap_throttle_map_.erase(child_route_id_pair);
    294 }
    295 
    296 void PrerenderTracker::AddPrerenderPendingSwap(
    297     const ChildRouteIdPair& child_route_id_pair,
    298     const GURL& url) {
    299   BrowserThread::PostTask(
    300       BrowserThread::IO, FROM_HERE,
    301       base::Bind(&AddPrerenderPendingSwapOnIOThreadTask,
    302                  child_route_id_pair, url));
    303 }
    304 
    305 void PrerenderTracker::RemovePrerenderPendingSwap(
    306     const ChildRouteIdPair& child_route_id_pair,
    307     bool swap_successful) {
    308   BrowserThread::PostTask(
    309       BrowserThread::IO, FROM_HERE,
    310       base::Bind(&RemovePrerenderPendingSwapOnIOThreadTask,
    311                  child_route_id_pair, swap_successful));
    312 }
    313 
    314 PrerenderTracker::PendingSwapThrottleData::PendingSwapThrottleData(
    315     const GURL& swap_url)
    316     : url(swap_url) {
    317 }
    318 
    319 PrerenderTracker::PendingSwapThrottleData::~PendingSwapThrottleData() {
    320 }
    321 
    322 // static
    323 PrerenderTracker* PrerenderTracker::GetDefault() {
    324   return g_browser_process->prerender_tracker();
    325 }
    326 
    327 // static
    328 void PrerenderTracker::AddPrerenderOnIOThreadTask(
    329     const ChildRouteIdPair& child_route_id_pair) {
    330   GetDefault()->AddPrerenderOnIOThread(child_route_id_pair);
    331 }
    332 
    333 // static
    334 void PrerenderTracker::RemovePrerenderOnIOThreadTask(
    335     const ChildRouteIdPair& child_route_id_pair,
    336     FinalStatus final_status) {
    337   GetDefault()->RemovePrerenderOnIOThread(child_route_id_pair, final_status);
    338 }
    339 
    340 // static
    341 void PrerenderTracker::AddPrerenderPendingSwapOnIOThreadTask(
    342     const ChildRouteIdPair& child_route_id_pair,
    343     const GURL& url) {
    344   GetDefault()->AddPrerenderPendingSwapOnIOThread(child_route_id_pair, url);
    345 }
    346 
    347 // static
    348 void PrerenderTracker::RemovePrerenderPendingSwapOnIOThreadTask(
    349     const ChildRouteIdPair& child_route_id_pair,
    350     bool swap_successful) {
    351   GetDefault()->RemovePrerenderPendingSwapOnIOThread(child_route_id_pair,
    352                                                      swap_successful);
    353 }
    354 
    355 }  // namespace prerender
    356