OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
mmenke
2016/06/09 18:09:29
Weird that git isn't picking up that this file was
clamy
2016/06/21 16:14:15
Done.
| |
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 "content/browser/loader/replacing_resource_handler.h" | |
6 | |
7 #include <utility> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/location.h" | |
12 #include "base/logging.h" | |
13 #include "base/metrics/histogram_macros.h" | |
14 #include "base/single_thread_task_runner.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/threading/thread_task_runner_handle.h" | |
17 #include "components/mime_util/mime_util.h" | |
18 #include "content/browser/download/download_resource_handler.h" | |
19 #include "content/browser/download/download_stats.h" | |
20 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
21 #include "content/browser/loader/resource_request_info_impl.h" | |
22 #include "content/browser/loader/stream_resource_handler.h" | |
23 #include "content/public/browser/content_browser_client.h" | |
24 #include "content/public/browser/download_item.h" | |
25 #include "content/public/browser/download_save_info.h" | |
26 #include "content/public/browser/download_url_parameters.h" | |
27 #include "content/public/browser/plugin_service.h" | |
28 #include "content/public/browser/resource_context.h" | |
29 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
30 #include "content/public/common/resource_response.h" | |
31 #include "content/public/common/webplugininfo.h" | |
32 #include "net/base/io_buffer.h" | |
33 #include "net/base/mime_sniffer.h" | |
mattm
2016/06/09 22:55:32
not used here?
clamy
2016/06/21 16:14:15
Done.
| |
34 #include "net/base/mime_util.h" | |
35 #include "net/http/http_content_disposition.h" | |
36 #include "net/http/http_response_headers.h" | |
37 | |
38 namespace content { | |
39 | |
40 ReplacingResourceHandler::ReplacingResourceHandler( | |
41 std::unique_ptr<ResourceHandler> next_handler, | |
42 ResourceDispatcherHostImpl* host, | |
43 PluginService* plugin_service, | |
44 net::URLRequest* request) | |
45 : LayeredResourceHandler(request, std::move(next_handler)), | |
46 state_(State::STARTING), | |
47 host_(host), | |
48 #if defined(ENABLE_PLUGINS) | |
49 plugin_service_(plugin_service), | |
50 #endif | |
51 switched_handler_(false), | |
52 must_download_(false), | |
53 must_download_is_set_(false), | |
54 bytes_read_(0), | |
55 weak_ptr_factory_(this) { | |
56 } | |
57 | |
58 ReplacingResourceHandler::~ReplacingResourceHandler() {} | |
59 | |
60 bool ReplacingResourceHandler::OnResponseStarted(ResourceResponse* response, | |
61 bool* defer) { | |
62 if (response->head.headers.get() && | |
63 response->head.headers->response_code() == 304) { | |
64 return true; | |
mmenke
2016/06/09 18:09:29
BUG: This should be passed through to the next ha
clamy
2016/06/21 16:14:15
Done.
| |
65 } | |
66 | |
67 response_ = response; | |
68 | |
69 state_ = State::WAITING_FOR_NEW_HANDLER; | |
70 | |
71 return MakeHandlerChoice(defer); | |
72 } | |
73 | |
74 bool ReplacingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, | |
75 int* buf_size, | |
76 int min_size) { | |
77 if (state_ == State::CHOICE_MADE) | |
78 return next_handler_->OnWillRead(buf, buf_size, min_size); | |
79 | |
80 DCHECK_EQ(State::STARTING, state_); | |
81 DCHECK_EQ(-1, min_size); | |
82 | |
83 if (!next_handler_->OnWillRead(buf, buf_size, min_size)) | |
84 return false; | |
85 | |
86 read_buffer_ = *buf; | |
87 return true; | |
88 } | |
89 | |
90 bool ReplacingResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { | |
91 if (state_ == State::CHOICE_MADE) | |
92 return next_handler_->OnReadCompleted(bytes_read, defer); | |
93 | |
94 DCHECK(state_ == State::WAITING_FOR_BUFFER_COPY); | |
mmenke
2016/06/09 18:09:29
DCHECK_EQ
clamy
2016/06/21 16:14:15
Done.
| |
95 bytes_read_ += bytes_read; | |
96 if (!CopyReadBufferToNextHandler()) | |
97 return false; | |
98 | |
99 return next_handler_->OnReadCompleted(bytes_read, defer); | |
100 } | |
101 | |
102 bool ReplacingResourceHandler::MakeHandlerChoice(bool* defer) { | |
103 if (!SelectNextHandler(defer)) | |
104 return false; | |
105 | |
106 if (*defer) | |
107 return true; | |
108 | |
109 if (!next_handler_->OnResponseStarted(response_.get(), defer)) | |
110 return false; | |
111 | |
112 state_ = read_buffer_ && switched_handler_ ? State::WAITING_FOR_BUFFER_COPY | |
113 : State::CHOICE_MADE; | |
114 return true; | |
115 } | |
116 | |
117 bool ReplacingResourceHandler::SelectPluginHandler(bool* defer, | |
118 bool* handled_by_plugin) { | |
119 *handled_by_plugin = false; | |
120 #if defined(ENABLE_PLUGINS) | |
121 ResourceRequestInfoImpl* info = GetRequestInfo(); | |
122 bool allow_wildcard = false; | |
123 bool stale; | |
124 WebPluginInfo plugin; | |
125 bool has_plugin = plugin_service_->GetPluginInfo( | |
126 info->GetChildID(), info->GetRenderFrameID(), info->GetContext(), | |
127 request()->url(), GURL(), response_->head.mime_type, allow_wildcard, | |
128 &stale, &plugin, NULL); | |
129 | |
130 if (stale) { | |
131 // Refresh the plugins asynchronously. | |
132 plugin_service_->GetPlugins( | |
133 base::Bind(&ReplacingResourceHandler::OnPluginsLoaded, | |
134 weak_ptr_factory_.GetWeakPtr())); | |
135 request()->LogBlockedBy("ReplacingResourceHandler"); | |
136 *defer = true; | |
137 return true; | |
138 } | |
139 | |
140 if (has_plugin && plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) { | |
141 *handled_by_plugin = true; | |
142 return true; | |
143 } | |
144 | |
145 // Attempt to intercept the request as a stream. | |
146 base::FilePath plugin_path; | |
147 if (has_plugin) | |
148 plugin_path = plugin.path; | |
149 std::string payload; | |
150 std::unique_ptr<ResourceHandler> handler(host_->MaybeInterceptAsStream( | |
151 plugin_path, request(), response_.get(), &payload)); | |
152 if (handler) { | |
153 *handled_by_plugin = true; | |
154 return UseAlternateNextHandler(std::move(handler), payload); | |
155 } | |
156 #endif | |
157 return true; | |
158 } | |
159 | |
160 bool ReplacingResourceHandler::SelectNextHandler(bool* defer) { | |
161 DCHECK(!response_->head.mime_type.empty()); | |
162 | |
163 ResourceRequestInfoImpl* info = GetRequestInfo(); | |
164 const std::string& mime_type = response_->head.mime_type; | |
165 | |
166 // https://crbug.com/568184 - Temporary hack to track servers that aren't | |
167 // setting Content-Disposition when sending x-x509-user-cert and expecting | |
168 // the browser to automatically install certificates; this is being | |
169 // deprecated and will be removed upon full <keygen> removal. | |
170 if (mime_type == "application/x-x509-user-cert") { | |
171 UMA_HISTOGRAM_BOOLEAN( | |
172 "UserCert.ContentDisposition", | |
173 response_->head.headers->HasHeader("Content-Disposition")); | |
174 } | |
175 | |
176 // Allow requests for object/embed tags to be intercepted as streams. | |
177 if (info->GetResourceType() == content::RESOURCE_TYPE_OBJECT) { | |
178 DCHECK(!info->allow_download()); | |
179 | |
180 bool handled_by_plugin; | |
181 if (!SelectPluginHandler(defer, &handled_by_plugin)) | |
182 return false; | |
183 if (handled_by_plugin || *defer) | |
184 return true; | |
185 } | |
186 | |
187 if (!info->allow_download()) | |
188 return true; | |
189 | |
190 // info->allow_download() == true implies | |
191 // info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME or | |
192 // info->GetResourceType() == RESOURCE_TYPE_SUB_FRAME. | |
193 DCHECK(info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME || | |
194 info->GetResourceType() == RESOURCE_TYPE_SUB_FRAME); | |
195 | |
196 bool must_download = MustDownload(); | |
197 if (!must_download) { | |
198 if (mime_util::IsSupportedMimeType(mime_type)) | |
199 return true; | |
200 | |
201 bool handled_by_plugin; | |
202 if (!SelectPluginHandler(defer, &handled_by_plugin)) | |
203 return false; | |
204 if (handled_by_plugin || *defer) | |
205 return true; | |
206 } | |
207 | |
208 // Install download handler | |
209 info->set_is_download(true); | |
210 std::unique_ptr<ResourceHandler> handler( | |
211 host_->CreateResourceHandlerForDownload(request(), | |
212 true, // is_content_initiated | |
213 must_download)); | |
214 return UseAlternateNextHandler(std::move(handler), std::string()); | |
215 } | |
216 | |
217 bool ReplacingResourceHandler::UseAlternateNextHandler( | |
218 std::unique_ptr<ResourceHandler> new_handler, | |
219 const std::string& payload_for_old_handler) { | |
220 if (response_->head.headers.get() && // Can be NULL if FTP. | |
221 response_->head.headers->response_code() / 100 != 2) { | |
222 // The response code indicates that this is an error page, but we don't | |
223 // know how to display the content. We follow Firefox here and show our | |
224 // own error page instead of triggering a download. | |
225 // TODO(abarth): We should abstract the response_code test, but this kind | |
226 // of check is scattered throughout our codebase. | |
227 request()->CancelWithError(net::ERR_INVALID_RESPONSE); | |
228 return false; | |
229 } | |
230 | |
231 // Inform the original ResourceHandler that this will be handled entirely by | |
232 // the new ResourceHandler. | |
233 // TODO(darin): We should probably check the return values of these. | |
234 bool defer_ignored = false; | |
235 next_handler_->OnResponseStarted(response_.get(), &defer_ignored); | |
236 // Although deferring OnResponseStarted is legal, the only downstream handler | |
237 // which does so is CrossSiteResourceHandler. Cross-site transitions should | |
238 // not trigger when switching handlers. | |
239 DCHECK(!defer_ignored); | |
240 if (payload_for_old_handler.empty()) { | |
241 net::URLRequestStatus status(net::URLRequestStatus::CANCELED, | |
242 net::ERR_ABORTED); | |
243 next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored); | |
244 DCHECK(!defer_ignored); | |
245 } else { | |
246 scoped_refptr<net::IOBuffer> buf; | |
247 int size = 0; | |
248 next_handler_->OnWillRead(&buf, &size, -1); | |
249 CHECK_GE(size, static_cast<int>(payload_for_old_handler.length())); | |
250 memcpy(buf->data(), payload_for_old_handler.c_str(), | |
251 payload_for_old_handler.length()); | |
252 next_handler_->OnReadCompleted(payload_for_old_handler.length(), | |
253 &defer_ignored); | |
254 DCHECK(!defer_ignored); | |
255 | |
256 net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); | |
257 next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored); | |
258 DCHECK(!defer_ignored); | |
259 } | |
260 | |
261 // This is handled entirely within the new ResourceHandler, so just reset the | |
262 // original ResourceHandler. | |
263 next_handler_ = std::move(new_handler); | |
264 next_handler_->SetController(controller()); | |
265 | |
266 switched_handler_ = true; | |
267 | |
268 return true; | |
269 } | |
270 | |
271 bool ReplacingResourceHandler::MustDownload() { | |
272 if (must_download_is_set_) | |
273 return must_download_; | |
274 | |
275 must_download_is_set_ = true; | |
276 | |
277 std::string disposition; | |
278 request()->GetResponseHeaderByName("content-disposition", &disposition); | |
279 if (!disposition.empty() && | |
280 net::HttpContentDisposition(disposition, std::string()).is_attachment()) { | |
281 must_download_ = true; | |
282 } else if (host_->delegate() && | |
283 host_->delegate()->ShouldForceDownloadResource( | |
284 request()->url(), response_->head.mime_type)) { | |
285 must_download_ = true; | |
286 } else { | |
287 must_download_ = false; | |
288 } | |
289 | |
290 return must_download_; | |
291 } | |
292 | |
293 void ReplacingResourceHandler::OnPluginsLoaded( | |
294 const std::vector<WebPluginInfo>& plugins) { | |
295 request()->LogUnblocked(); | |
296 bool defer = false; | |
297 if (!MakeHandlerChoice(&defer)) { | |
298 controller()->Cancel(); | |
299 } else if (!defer) { | |
300 controller()->Resume(); | |
301 } | |
302 } | |
303 | |
304 bool ReplacingResourceHandler::CopyReadBufferToNextHandler() { | |
305 DCHECK(read_buffer_.get()); | |
306 scoped_refptr<net::IOBuffer> buf; | |
307 int buf_len = 0; | |
308 if (!next_handler_->OnWillRead(&buf, &buf_len, bytes_read_)) | |
309 return false; | |
310 | |
311 CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); | |
312 memcpy(buf->data(), read_buffer_->data(), bytes_read_); | |
313 state_ = State::CHOICE_MADE; | |
314 return true; | |
315 } | |
316 | |
317 } // namespace content | |
OLD | NEW |