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