blob: ecc94ef104e94d7b8a037fb61e809f28e3c81132 [file] [log] [blame]
John Abd-El-Malekd2377982018-01-08 22:23:121// Copyright 2017 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/common/prerender_url_loader_throttle.h"
6
7#include "build/build_config.h"
8#include "chrome/common/prerender_util.h"
9#include "content/public/common/content_constants.h"
John Abd-El-Malekd2377982018-01-08 22:23:1210#include "net/base/load_flags.h"
11#include "net/url_request/redirect_info.h"
John Abd-El-Malek1df61792018-01-12 20:40:4512#include "services/network/public/cpp/resource_request.h"
John Abd-El-Malek46248032018-01-17 19:11:2313#include "services/network/public/cpp/resource_response.h"
John Abd-El-Malekd2377982018-01-08 22:23:1214
15namespace prerender {
16
17namespace {
18
19void CancelPrerenderForUnsupportedMethod(
20 PrerenderURLLoaderThrottle::CancelerGetterCallback callback) {
21 chrome::mojom::PrerenderCanceler* canceler = std::move(callback).Run();
22 if (canceler)
23 canceler->CancelPrerenderForUnsupportedMethod();
24}
25
26void CancelPrerenderForUnsupportedScheme(
27 PrerenderURLLoaderThrottle::CancelerGetterCallback callback,
28 const GURL& url) {
29 chrome::mojom::PrerenderCanceler* canceler = std::move(callback).Run();
30 if (canceler)
31 canceler->CancelPrerenderForUnsupportedScheme(url);
32}
33
34void CancelPrerenderForSyncDeferredRedirect(
35 PrerenderURLLoaderThrottle::CancelerGetterCallback callback) {
36 chrome::mojom::PrerenderCanceler* canceler = std::move(callback).Run();
37 if (canceler)
38 canceler->CancelPrerenderForSyncDeferredRedirect();
39}
40
41// Returns true if the response has a "no-store" cache control header.
John Abd-El-Malek46248032018-01-17 19:11:2342bool IsNoStoreResponse(const network::ResourceResponseHead& response_head) {
Alexei Svitkinef14b0332018-01-17 01:47:5143 return response_head.headers &&
44 response_head.headers->HasHeaderValue("cache-control", "no-store");
John Abd-El-Malekd2377982018-01-08 22:23:1245}
46
47} // namespace
48
49PrerenderURLLoaderThrottle::PrerenderURLLoaderThrottle(
50 PrerenderMode mode,
51 const std::string& histogram_prefix,
52 CancelerGetterCallback canceler_getter,
53 scoped_refptr<base::SequencedTaskRunner> canceler_getter_task_runner)
54 : mode_(mode),
55 histogram_prefix_(histogram_prefix),
56 canceler_getter_(std::move(canceler_getter)),
57 canceler_getter_task_runner_(canceler_getter_task_runner) {
John Abd-El-Malekd2377982018-01-08 22:23:1258}
59
60PrerenderURLLoaderThrottle::~PrerenderURLLoaderThrottle() {
61 if (destruction_closure_)
62 std::move(destruction_closure_).Run();
63}
64
65void PrerenderURLLoaderThrottle::PrerenderUsed() {
66 if (original_request_priority_)
67 delegate_->SetPriority(original_request_priority_.value());
68 if (deferred_)
69 delegate_->Resume();
70}
71
72void PrerenderURLLoaderThrottle::DetachFromCurrentSequence() {
73 // This method is only called for synchronous XHR from the main thread.
74 sync_xhr_ = true;
75}
76
77void PrerenderURLLoaderThrottle::WillStartRequest(
John Abd-El-Malek1df61792018-01-12 20:40:4578 network::ResourceRequest* request,
John Abd-El-Malekd2377982018-01-08 22:23:1279 bool* defer) {
80 resource_type_ = static_cast<content::ResourceType>(request->resource_type);
81 // Abort any prerenders that spawn requests that use unsupported HTTP
82 // methods or schemes.
83 if (!IsValidHttpMethod(mode_, request->method)) {
84 // If this is a full prerender, cancel the prerender in response to
85 // invalid requests. For prefetches, cancel invalid requests but keep the
86 // prefetch going.
87 delegate_->CancelWithError(net::ERR_ABORTED);
88 if (mode_ == FULL_PRERENDER) {
89 canceler_getter_task_runner_->PostTask(
90 FROM_HERE, base::BindOnce(CancelPrerenderForUnsupportedMethod,
tzik6d3cd7562018-02-21 12:07:2291 std::move(canceler_getter_)));
John Abd-El-Malekd2377982018-01-08 22:23:1292 return;
93 }
94 }
95
96 if (request->resource_type != content::RESOURCE_TYPE_MAIN_FRAME &&
97 !DoesSubresourceURLHaveValidScheme(request->url)) {
98 // Destroying the prerender for unsupported scheme only for non-main
99 // resource to allow chrome://crash to actually crash in the
100 // *RendererCrash tests instead of being intercepted here. The
101 // unsupported scheme for the main resource is checked in
102 // WillRedirectRequest() and PrerenderContents::CheckURL(). See
103 // http://crbug.com/673771.
104 delegate_->CancelWithError(net::ERR_ABORTED);
105 canceler_getter_task_runner_->PostTask(
106 FROM_HERE, base::BindOnce(CancelPrerenderForUnsupportedScheme,
tzik6d3cd7562018-02-21 12:07:22107 std::move(canceler_getter_), request->url));
John Abd-El-Malekd2377982018-01-08 22:23:12108 return;
109 }
110
111#if defined(OS_ANDROID)
112 if (request->resource_type == content::RESOURCE_TYPE_FAVICON) {
113 // Delay icon fetching until the contents are getting swapped in
114 // to conserve network usage in mobile devices.
115 *defer = true;
John Abd-El-Malekd2377982018-01-08 22:23:12116 return;
117 }
118#else
119 // Priorities for prerendering requests are lowered, to avoid competing with
120 // other page loads, except on Android where this is less likely to be a
121 // problem. In some cases, this may negatively impact the performance of
122 // prerendering, see https://crbug.com/652746 for details.
123 // Requests with the IGNORE_LIMITS flag set (i.e., sync XHRs)
124 // should remain at MAXIMUM_PRIORITY.
125 if (request->load_flags & net::LOAD_IGNORE_LIMITS) {
126 DCHECK_EQ(request->priority, net::MAXIMUM_PRIORITY);
127 } else if (request->priority != net::IDLE) {
128 original_request_priority_ = request->priority;
129 request->priority = net::IDLE;
130 }
131#endif // OS_ANDROID
John Abd-El-Malek6e48de12018-01-10 00:10:08132
133 if (mode_ == PREFETCH_ONLY) {
134 detached_timer_.Start(FROM_HERE,
135 base::TimeDelta::FromMilliseconds(
136 content::kDefaultDetachableCancelDelayMs),
137 this, &PrerenderURLLoaderThrottle::OnTimedOut);
138 }
John Abd-El-Malekd2377982018-01-08 22:23:12139}
140
141void PrerenderURLLoaderThrottle::WillRedirectRequest(
142 const net::RedirectInfo& redirect_info,
John Abd-El-Malek46248032018-01-17 19:11:23143 const network::ResourceResponseHead& response_head,
John Abd-El-Malekd2377982018-01-08 22:23:12144 bool* defer) {
145 redirect_count_++;
146 if (mode_ == PREFETCH_ONLY) {
147 RecordPrefetchResponseReceived(
148 histogram_prefix_, content::IsResourceTypeFrame(resource_type_),
149 true /* is_redirect */, IsNoStoreResponse(response_head));
150 }
151
152 std::string follow_only_when_prerender_shown_header;
153 response_head.headers->GetNormalizedHeader(
154 kFollowOnlyWhenPrerenderShown, &follow_only_when_prerender_shown_header);
155 // Abort any prerenders with requests which redirect to invalid schemes.
156 if (!DoesURLHaveValidScheme(redirect_info.new_url)) {
157 delegate_->CancelWithError(net::ERR_ABORTED);
158 canceler_getter_task_runner_->PostTask(
tzik6d3cd7562018-02-21 12:07:22159 FROM_HERE,
160 base::BindOnce(CancelPrerenderForUnsupportedScheme,
161 std::move(canceler_getter_), redirect_info.new_url));
John Abd-El-Malekd2377982018-01-08 22:23:12162 } else if (follow_only_when_prerender_shown_header == "1" &&
163 resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME) {
164 // Only defer redirects with the Follow-Only-When-Prerender-Shown
165 // header. Do not defer redirects on main frame loads.
166 if (sync_xhr_) {
167 // Cancel on deferred synchronous requests. Those will
168 // indefinitely hang up a renderer process.
169 canceler_getter_task_runner_->PostTask(
170 FROM_HERE, base::BindOnce(CancelPrerenderForSyncDeferredRedirect,
tzik6d3cd7562018-02-21 12:07:22171 std::move(canceler_getter_)));
John Abd-El-Malekd2377982018-01-08 22:23:12172 delegate_->CancelWithError(net::ERR_ABORTED);
173 } else {
174 // Defer the redirect until the prerender is used or canceled.
175 *defer = true;
176 deferred_ = true;
177 }
178 }
179}
180
181void PrerenderURLLoaderThrottle::WillProcessResponse(
182 const GURL& response_url,
John Abd-El-Malek46248032018-01-17 19:11:23183 const network::ResourceResponseHead& response_head,
John Abd-El-Malekd2377982018-01-08 22:23:12184 bool* defer) {
185 if (mode_ != PREFETCH_ONLY)
186 return;
187
188 bool is_main_resource = content::IsResourceTypeFrame(resource_type_);
189 RecordPrefetchResponseReceived(histogram_prefix_, is_main_resource,
190 true /* is_redirect */,
191 IsNoStoreResponse(response_head));
192 RecordPrefetchRedirectCount(histogram_prefix_, is_main_resource,
193 redirect_count_);
194}
195
196void PrerenderURLLoaderThrottle::OnTimedOut() {
197 delegate_->CancelWithError(net::ERR_ABORTED);
198}
199
200} // namespace prerender