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
PrerenderResourceThrottle(net::URLRequest * request,PrerenderTracker * tracker)20 PrerenderResourceThrottle::PrerenderResourceThrottle(
21 net::URLRequest* request,
22 PrerenderTracker* tracker)
23 : request_(request),
24 tracker_(tracker),
25 throttled_(false) {
26 }
27
WillStartRequest(bool * defer)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
WillRedirectRequest(const GURL & new_url,bool * defer)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
GetNameForLogging() const115 const char* PrerenderResourceThrottle::GetNameForLogging() const {
116 return "PrerenderResourceThrottle";
117 }
118
Resume()119 void PrerenderResourceThrottle::Resume() {
120 DCHECK(throttled_);
121
122 throttled_ = false;
123 controller()->Resume();
124 }
125
Cancel()126 void PrerenderResourceThrottle::Cancel() {
127 DCHECK(throttled_);
128
129 throttled_ = false;
130 controller()->Cancel();
131 }
132
133 } // namespace prerender
134