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.
5 #include "chrome/browser/prerender/prerender_resource_throttle.h"
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/browser_thread.h"
12 #include "content/public/browser/render_frame_host.h"
13 #include "content/public/browser/resource_controller.h"
14 #include "content/public/browser/resource_request_info.h"
15 #include "content/public/browser/web_contents.h"
16 #include "net/url_request/url_request.h"
21 static const char kFollowOnlyWhenPrerenderShown
[] =
22 "follow-only-when-prerender-shown";
24 PrerenderContents
* g_prerender_contents_for_testing
;
27 void PrerenderResourceThrottle::OverridePrerenderContentsForTesting(
28 PrerenderContents
* contents
) {
29 g_prerender_contents_for_testing
= contents
;
32 PrerenderResourceThrottle::PrerenderResourceThrottle(
33 net::URLRequest
* request
,
34 PrerenderTracker
* tracker
)
39 void PrerenderResourceThrottle::WillStartRequest(bool* defer
) {
40 const content::ResourceRequestInfo
* info
=
41 content::ResourceRequestInfo::ForRequest(request_
);
42 int child_id
= info
->GetChildID();
43 int route_id
= info
->GetRouteID();
45 // If the prerender was used since the throttle was added, leave it
47 if (!tracker_
->IsPrerenderingOnIOThread(child_id
, route_id
))
51 content::BrowserThread::PostTask(
52 content::BrowserThread::UI
,
54 base::Bind(&PrerenderResourceThrottle::WillStartRequestOnUI
,
55 AsWeakPtr(), request_
->method(), info
->GetChildID(),
56 info
->GetRenderFrameID(), request_
->url()));
59 void PrerenderResourceThrottle::WillRedirectRequest(const GURL
& new_url
,
61 const content::ResourceRequestInfo
* info
=
62 content::ResourceRequestInfo::ForRequest(request_
);
63 int child_id
= info
->GetChildID();
64 int route_id
= info
->GetRouteID();
66 // If the prerender was used since the throttle was added, leave it
68 if (!tracker_
->IsPrerenderingOnIOThread(child_id
, route_id
))
73 request_
->GetResponseHeaderByName(kFollowOnlyWhenPrerenderShown
, &header
);
75 content::BrowserThread::PostTask(
76 content::BrowserThread::UI
,
78 base::Bind(&PrerenderResourceThrottle::WillRedirectRequestOnUI
,
79 AsWeakPtr(), header
, info
->GetResourceType(), info
->IsAsync(),
80 info
->GetChildID(), info
->GetRenderFrameID(), new_url
));
83 const char* PrerenderResourceThrottle::GetNameForLogging() const {
84 return "PrerenderResourceThrottle";
87 void PrerenderResourceThrottle::Resume() {
88 controller()->Resume();
91 void PrerenderResourceThrottle::Cancel() {
92 controller()->Cancel();
95 void PrerenderResourceThrottle::WillStartRequestOnUI(
96 const base::WeakPtr
<PrerenderResourceThrottle
>& throttle
,
97 const std::string
& method
,
98 int render_process_id
,
102 PrerenderContents
* prerender_contents
=
103 PrerenderContentsFromRenderFrame(render_process_id
, render_frame_id
);
104 if (prerender_contents
) {
105 // Abort any prerenders that spawn requests that use unsupported HTTP
106 // methods or schemes.
107 if (!PrerenderManager::IsValidHttpMethod(method
)) {
108 prerender_contents
->Destroy(FINAL_STATUS_INVALID_HTTP_METHOD
);
110 } else if (!PrerenderManager::DoesSubresourceURLHaveValidScheme(url
)) {
111 prerender_contents
->Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME
);
112 ReportUnsupportedPrerenderScheme(url
);
117 content::BrowserThread::PostTask(
118 content::BrowserThread::IO
,
120 base::Bind(cancel
? &PrerenderResourceThrottle::Cancel
:
121 &PrerenderResourceThrottle::Resume
, throttle
));
124 void PrerenderResourceThrottle::WillRedirectRequestOnUI(
125 const base::WeakPtr
<PrerenderResourceThrottle
>& throttle
,
126 const std::string
& follow_only_when_prerender_shown_header
,
127 ResourceType::Type resource_type
,
129 int render_process_id
,
131 const GURL
& new_url
) {
133 PrerenderContents
* prerender_contents
=
134 PrerenderContentsFromRenderFrame(render_process_id
, render_frame_id
);
135 if (prerender_contents
) {
136 // Abort any prerenders with requests which redirect to invalid schemes.
137 if (!PrerenderManager::DoesURLHaveValidScheme(new_url
)) {
138 prerender_contents
->Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME
);
139 ReportUnsupportedPrerenderScheme(new_url
);
141 } else if (follow_only_when_prerender_shown_header
== "1" &&
142 resource_type
!= ResourceType::MAIN_FRAME
) {
143 // Only defer redirects with the Follow-Only-When-Prerender-Shown
144 // header. Do not defer redirects on main frame loads.
146 // Cancel on deferred synchronous requests. Those will
147 // indefinitely hang up a renderer process.
148 prerender_contents
->Destroy(FINAL_STATUS_BAD_DEFERRED_REDIRECT
);
151 // Defer the redirect until the prerender is used or
152 // canceled. It is possible for the UI thread to used the
153 // prerender at the same time. But then |tracker_| will resume
154 // the request soon in
155 // PrerenderTracker::RemovePrerenderOnIOThread.
156 content::BrowserThread::PostTask(
157 content::BrowserThread::IO
,
159 base::Bind(&PrerenderResourceThrottle::AddResourceThrottle
,
166 content::BrowserThread::PostTask(
167 content::BrowserThread::IO
,
169 base::Bind(cancel
? &PrerenderResourceThrottle::Cancel
:
170 &PrerenderResourceThrottle::Resume
, throttle
));
173 PrerenderContents
* PrerenderResourceThrottle::PrerenderContentsFromRenderFrame(
174 int render_process_id
, int render_frame_id
) {
175 if (g_prerender_contents_for_testing
)
176 return g_prerender_contents_for_testing
;
177 content::RenderFrameHost
* rfh
= content::RenderFrameHost::FromID(
178 render_process_id
, render_frame_id
);
179 content::WebContents
* web_contents
=
180 content::WebContents::FromRenderFrameHost(rfh
);
181 return PrerenderContents::FromWebContents(web_contents
);
184 void PrerenderResourceThrottle::AddResourceThrottle() {
185 const content::ResourceRequestInfo
* info
=
186 content::ResourceRequestInfo::ForRequest(request_
);
187 int child_id
= info
->GetChildID();
188 int route_id
= info
->GetRouteID();
189 tracker_
->AddResourceThrottleOnIOThread(child_id
, route_id
, AsWeakPtr());
192 } // namespace prerender