Home | History | Annotate | Download | only in prerender
      1 // Copyright 2013 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_resource_throttle.h"
      6 
      7 #include "chrome/browser/prerender/prerender_final_status.h"
      8 #include "chrome/browser/prerender/prerender_manager.h"
      9 #include "chrome/browser/prerender/prerender_tracker.h"
     10 #include "chrome/browser/prerender/prerender_util.h"
     11 #include "content/public/browser/resource_controller.h"
     12 #include "content/public/browser/resource_request_info.h"
     13 #include "net/url_request/url_request.h"
     14 
     15 namespace prerender {
     16 
     17 static const char kFollowOnlyWhenPrerenderShown[] =
     18     "follow-only-when-prerender-shown";
     19 
     20 PrerenderResourceThrottle::PrerenderResourceThrottle(
     21     net::URLRequest* request,
     22     PrerenderTracker* tracker)
     23     : request_(request),
     24       tracker_(tracker),
     25       throttled_(false) {
     26 }
     27 
     28 void PrerenderResourceThrottle::WillStartRequest(bool* defer) {
     29   const content::ResourceRequestInfo* info =
     30       content::ResourceRequestInfo::ForRequest(request_);
     31   int child_id = info->GetChildID();
     32   int route_id = info->GetRouteID();
     33 
     34   // If the prerender was used since the throttle was added, leave it
     35   // alone.
     36   if (!tracker_->IsPrerenderingOnIOThread(child_id, route_id))
     37     return;
     38 
     39   // Abort any prerenders that spawn requests that use unsupported HTTP methods
     40   // or schemes.
     41   if (!prerender::PrerenderManager::IsValidHttpMethod(request_->method()) &&
     42       tracker_->TryCancelOnIOThread(
     43           child_id, route_id, prerender::FINAL_STATUS_INVALID_HTTP_METHOD)) {
     44     controller()->Cancel();
     45     return;
     46   }
     47   if (!prerender::PrerenderManager::DoesSubresourceURLHaveValidScheme(
     48           request_->url()) &&
     49       tracker_->TryCancelOnIOThread(
     50           child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME)) {
     51     ReportUnsupportedPrerenderScheme(request_->url());
     52     controller()->Cancel();
     53     return;
     54   }
     55 }
     56 
     57 void PrerenderResourceThrottle::WillRedirectRequest(const GURL& new_url,
     58                                                     bool* defer) {
     59   DCHECK(!throttled_);
     60 
     61   const content::ResourceRequestInfo* info =
     62       content::ResourceRequestInfo::ForRequest(request_);
     63   int child_id = info->GetChildID();
     64   int route_id = info->GetRouteID();
     65 
     66   // If the prerender was used since the throttle was added, leave it
     67   // alone.
     68   if (!tracker_->IsPrerenderingOnIOThread(child_id, route_id))
     69     return;
     70 
     71   // Abort any prerenders with requests which redirect to invalid schemes.
     72   if (!prerender::PrerenderManager::DoesURLHaveValidScheme(new_url) &&
     73       tracker_->TryCancel(
     74           child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME)) {
     75     ReportUnsupportedPrerenderScheme(new_url);
     76     controller()->Cancel();
     77     return;
     78   }
     79 
     80   // Only defer redirects with the Follow-Only-When-Prerender-Shown
     81   // header.
     82   std::string header;
     83   request_->GetResponseHeaderByName(kFollowOnlyWhenPrerenderShown, &header);
     84   if (header != "1")
     85     return;
     86 
     87   // Do not defer redirects on main frame loads.
     88   if (info->GetResourceType() == ResourceType::MAIN_FRAME)
     89     return;
     90 
     91   if (!info->IsAsync()) {
     92     // Cancel on deferred synchronous requests. Those will
     93     // indefinitely hang up a renderer process.
     94     //
     95     // If the TryCancelOnIOThread fails, the UI thread won a race to
     96     // use the prerender, so let the request through.
     97     if (tracker_->TryCancelOnIOThread(child_id, route_id,
     98                                       FINAL_STATUS_BAD_DEFERRED_REDIRECT)) {
     99       controller()->Cancel();
    100     }
    101     return;
    102   }
    103 
    104   // Defer the redirect until the prerender is used or
    105   // canceled. It is possible for the UI thread to used the
    106   // prerender at the same time. But then |tracker_| will resume
    107   // the request soon in
    108   // PrerenderTracker::RemovePrerenderOnIOThread.
    109   *defer = true;
    110   throttled_ = true;
    111   tracker_->AddResourceThrottleOnIOThread(child_id, route_id,
    112                                           this->AsWeakPtr());
    113 }
    114 
    115 const char* PrerenderResourceThrottle::GetNameForLogging() const {
    116   return "PrerenderResourceThrottle";
    117 }
    118 
    119 void PrerenderResourceThrottle::Resume() {
    120   DCHECK(throttled_);
    121 
    122   throttled_ = false;
    123   controller()->Resume();
    124 }
    125 
    126 void PrerenderResourceThrottle::Cancel() {
    127   DCHECK(throttled_);
    128 
    129   throttled_ = false;
    130   controller()->Cancel();
    131 }
    132 
    133 }  // namespace prerender
    134