| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/buffered_resource_handler.h" | 5 #include "content/browser/renderer_host/buffered_resource_handler.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
| 12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 13 #include "content/browser/download/download_id_factory.h" | 13 #include "content/browser/download/download_id_factory.h" |
| 14 #include "content/browser/download/download_resource_handler.h" | 14 #include "content/browser/download/download_resource_handler.h" |
| 15 #include "content/browser/download/download_types.h" | 15 #include "content/browser/download/download_types.h" |
| 16 #include "content/browser/plugin_service.h" | 16 #include "content/browser/plugin_service.h" |
| 17 #include "content/browser/renderer_host/resource_dispatcher_host.h" | 17 #include "content/browser/renderer_host/resource_dispatcher_host.h" |
| 18 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" | 18 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" |
| 19 #include "content/browser/renderer_host/x509_user_cert_resource_handler.h" | 19 #include "content/browser/renderer_host/x509_user_cert_resource_handler.h" |
| 20 #include "content/browser/resource_context.h" | 20 #include "content/browser/resource_context.h" |
| 21 #include "content/common/resource_response.h" | |
| 22 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
| 23 #include "content/public/browser/content_browser_client.h" | 22 #include "content/public/browser/content_browser_client.h" |
| 24 #include "content/public/browser/resource_dispatcher_host_delegate.h" | 23 #include "content/public/browser/resource_dispatcher_host_delegate.h" |
| 24 #include "content/public/common/resource_response.h" |
| 25 #include "net/base/io_buffer.h" | 25 #include "net/base/io_buffer.h" |
| 26 #include "net/base/mime_sniffer.h" | 26 #include "net/base/mime_sniffer.h" |
| 27 #include "net/base/mime_util.h" | 27 #include "net/base/mime_util.h" |
| 28 #include "net/base/net_errors.h" | 28 #include "net/base/net_errors.h" |
| 29 #include "net/http/http_response_headers.h" | 29 #include "net/http/http_response_headers.h" |
| 30 #include "webkit/plugins/webplugininfo.h" | 30 #include "webkit/plugins/webplugininfo.h" |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 void RecordSnifferMetrics(bool sniffing_blocked, | 34 void RecordSnifferMetrics(bool sniffing_blocked, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 buffering_(false), | 71 buffering_(false), |
| 72 finished_(false) { | 72 finished_(false) { |
| 73 } | 73 } |
| 74 | 74 |
| 75 bool BufferedResourceHandler::OnUploadProgress(int request_id, | 75 bool BufferedResourceHandler::OnUploadProgress(int request_id, |
| 76 uint64 position, | 76 uint64 position, |
| 77 uint64 size) { | 77 uint64 size) { |
| 78 return real_handler_->OnUploadProgress(request_id, position, size); | 78 return real_handler_->OnUploadProgress(request_id, position, size); |
| 79 } | 79 } |
| 80 | 80 |
| 81 bool BufferedResourceHandler::OnRequestRedirected(int request_id, | 81 bool BufferedResourceHandler::OnRequestRedirected( |
| 82 const GURL& new_url, | 82 int request_id, |
| 83 ResourceResponse* response, | 83 const GURL& new_url, |
| 84 bool* defer) { | 84 content::ResourceResponse* response, |
| 85 bool* defer) { |
| 85 return real_handler_->OnRequestRedirected( | 86 return real_handler_->OnRequestRedirected( |
| 86 request_id, new_url, response, defer); | 87 request_id, new_url, response, defer); |
| 87 } | 88 } |
| 88 | 89 |
| 89 bool BufferedResourceHandler::OnResponseStarted(int request_id, | 90 bool BufferedResourceHandler::OnResponseStarted( |
| 90 ResourceResponse* response) { | 91 int request_id, |
| 92 content::ResourceResponse* response) { |
| 91 response_ = response; | 93 response_ = response; |
| 92 if (!DelayResponse()) | 94 if (!DelayResponse()) |
| 93 return CompleteResponseStarted(request_id, false); | 95 return CompleteResponseStarted(request_id, false); |
| 94 return true; | 96 return true; |
| 95 } | 97 } |
| 96 | 98 |
| 97 bool BufferedResourceHandler::OnResponseCompleted( | 99 bool BufferedResourceHandler::OnResponseCompleted( |
| 98 int request_id, | 100 int request_id, |
| 99 const net::URLRequestStatus& status, | 101 const net::URLRequestStatus& status, |
| 100 const std::string& security_info) { | 102 const std::string& security_info) { |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 std::string mime_type; | 170 std::string mime_type; |
| 169 request_->GetMimeType(&mime_type); | 171 request_->GetMimeType(&mime_type); |
| 170 | 172 |
| 171 std::string content_type_options; | 173 std::string content_type_options; |
| 172 request_->GetResponseHeaderByName("x-content-type-options", | 174 request_->GetResponseHeaderByName("x-content-type-options", |
| 173 &content_type_options); | 175 &content_type_options); |
| 174 | 176 |
| 175 const bool sniffing_blocked = | 177 const bool sniffing_blocked = |
| 176 LowerCaseEqualsASCII(content_type_options, "nosniff"); | 178 LowerCaseEqualsASCII(content_type_options, "nosniff"); |
| 177 const bool not_modified_status = | 179 const bool not_modified_status = |
| 178 response_->response_head.headers && | 180 response_->headers && response_->headers->response_code() == 304; |
| 179 response_->response_head.headers->response_code() == 304; | |
| 180 const bool we_would_like_to_sniff = not_modified_status ? | 181 const bool we_would_like_to_sniff = not_modified_status ? |
| 181 false : net::ShouldSniffMimeType(request_->url(), mime_type); | 182 false : net::ShouldSniffMimeType(request_->url(), mime_type); |
| 182 | 183 |
| 183 RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type); | 184 RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type); |
| 184 | 185 |
| 185 if (!sniffing_blocked && we_would_like_to_sniff) { | 186 if (!sniffing_blocked && we_would_like_to_sniff) { |
| 186 // We're going to look at the data before deciding what the content type | 187 // We're going to look at the data before deciding what the content type |
| 187 // is. That means we need to delay sending the ResponseStarted message | 188 // is. That means we need to delay sending the ResponseStarted message |
| 188 // over the IPC channel. | 189 // over the IPC channel. |
| 189 sniff_content_ = true; | 190 sniff_content_ = true; |
| 190 VLOG(1) << "To buffer: " << request_->url().spec(); | 191 VLOG(1) << "To buffer: " << request_->url().spec(); |
| 191 return true; | 192 return true; |
| 192 } | 193 } |
| 193 | 194 |
| 194 if (sniffing_blocked && mime_type.empty() && !not_modified_status) { | 195 if (sniffing_blocked && mime_type.empty() && !not_modified_status) { |
| 195 // Ugg. The server told us not to sniff the content but didn't give us a | 196 // Ugg. The server told us not to sniff the content but didn't give us a |
| 196 // mime type. What's a browser to do? Turns out, we're supposed to treat | 197 // mime type. What's a browser to do? Turns out, we're supposed to treat |
| 197 // the response as "text/plain". This is the most secure option. | 198 // the response as "text/plain". This is the most secure option. |
| 198 mime_type.assign("text/plain"); | 199 mime_type.assign("text/plain"); |
| 199 response_->response_head.mime_type.assign(mime_type); | 200 response_->mime_type.assign(mime_type); |
| 200 } | 201 } |
| 201 | 202 |
| 202 if (mime_type == "application/rss+xml" || | 203 if (mime_type == "application/rss+xml" || |
| 203 mime_type == "application/atom+xml") { | 204 mime_type == "application/atom+xml") { |
| 204 // Sad face. The server told us that they wanted us to treat the response | 205 // Sad face. The server told us that they wanted us to treat the response |
| 205 // as RSS or Atom. Unfortunately, we don't have a built-in feed previewer | 206 // as RSS or Atom. Unfortunately, we don't have a built-in feed previewer |
| 206 // like other browsers. We can't just render the content as XML because | 207 // like other browsers. We can't just render the content as XML because |
| 207 // web sites let third parties inject arbitrary script into their RSS | 208 // web sites let third parties inject arbitrary script into their RSS |
| 208 // feeds. That leaves us with little choice but to practically ignore the | 209 // feeds. That leaves us with little choice but to practically ignore the |
| 209 // response. In the future, when we have an RSS feed previewer, we can | 210 // response. In the future, when we have an RSS feed previewer, we can |
| 210 // remove this logic. | 211 // remove this logic. |
| 211 mime_type.assign("text/plain"); | 212 mime_type.assign("text/plain"); |
| 212 response_->response_head.mime_type.assign(mime_type); | 213 response_->mime_type.assign(mime_type); |
| 213 } | 214 } |
| 214 | 215 |
| 215 if (!not_modified_status && ShouldWaitForPlugins()) { | 216 if (!not_modified_status && ShouldWaitForPlugins()) { |
| 216 wait_for_plugins_ = true; | 217 wait_for_plugins_ = true; |
| 217 return true; | 218 return true; |
| 218 } | 219 } |
| 219 | 220 |
| 220 return false; | 221 return false; |
| 221 } | 222 } |
| 222 | 223 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 249 // SniffMimeType() returns false if there is not enough data to determine | 250 // SniffMimeType() returns false if there is not enough data to determine |
| 250 // the mime type. However, even if it returns false, it returns a new type | 251 // the mime type. However, even if it returns false, it returns a new type |
| 251 // that is probably better than the current one. | 252 // that is probably better than the current one. |
| 252 DCHECK_LT(bytes_read_, net::kMaxBytesToSniff); | 253 DCHECK_LT(bytes_read_, net::kMaxBytesToSniff); |
| 253 if (!finished_) { | 254 if (!finished_) { |
| 254 buffering_ = true; | 255 buffering_ = true; |
| 255 return true; | 256 return true; |
| 256 } | 257 } |
| 257 } | 258 } |
| 258 sniff_content_ = false; | 259 sniff_content_ = false; |
| 259 response_->response_head.mime_type.assign(new_type); | 260 response_->mime_type.assign(new_type); |
| 260 | 261 |
| 261 // We just sniffed the mime type, maybe there is a doctype to process. | 262 // We just sniffed the mime type, maybe there is a doctype to process. |
| 262 if (ShouldWaitForPlugins()) | 263 if (ShouldWaitForPlugins()) |
| 263 wait_for_plugins_ = true; | 264 wait_for_plugins_ = true; |
| 264 } | 265 } |
| 265 | 266 |
| 266 buffering_ = false; | 267 buffering_ = false; |
| 267 | 268 |
| 268 if (wait_for_plugins_) | 269 if (wait_for_plugins_) |
| 269 return true; | 270 return true; |
| 270 | 271 |
| 271 return false; | 272 return false; |
| 272 } | 273 } |
| 273 | 274 |
| 274 bool BufferedResourceHandler::CompleteResponseStarted(int request_id, | 275 bool BufferedResourceHandler::CompleteResponseStarted(int request_id, |
| 275 bool in_complete) { | 276 bool in_complete) { |
| 276 ResourceDispatcherHostRequestInfo* info = | 277 ResourceDispatcherHostRequestInfo* info = |
| 277 ResourceDispatcherHost::InfoForRequest(request_); | 278 ResourceDispatcherHost::InfoForRequest(request_); |
| 278 std::string mime_type; | 279 std::string mime_type; |
| 279 request_->GetMimeType(&mime_type); | 280 request_->GetMimeType(&mime_type); |
| 280 | 281 |
| 281 // Check if this is an X.509 certificate, if yes, let it be handled | 282 // Check if this is an X.509 certificate, if yes, let it be handled |
| 282 // by X509UserCertResourceHandler. | 283 // by X509UserCertResourceHandler. |
| 283 if (mime_type == "application/x-x509-user-cert") { | 284 if (mime_type == "application/x-x509-user-cert") { |
| 284 // This is entirely similar to how DownloadThrottlingResourceHandler | 285 // This is entirely similar to how DownloadThrottlingResourceHandler |
| 285 // works except we are doing it for an X.509 client certificates. | 286 // works except we are doing it for an X.509 client certificates. |
| 286 | 287 |
| 287 if (response_->response_head.headers && // Can be NULL if FTP. | 288 if (response_->headers && // Can be NULL if FTP. |
| 288 response_->response_head.headers->response_code() / 100 != 2) { | 289 response_->headers->response_code() / 100 != 2) { |
| 289 // The response code indicates that this is an error page, but we are | 290 // The response code indicates that this is an error page, but we are |
| 290 // expecting an X.509 user certificate. We follow Firefox here and show | 291 // expecting an X.509 user certificate. We follow Firefox here and show |
| 291 // our own error page instead of handling the error page as a | 292 // our own error page instead of handling the error page as a |
| 292 // certificate. | 293 // certificate. |
| 293 // TODO(abarth): We should abstract the response_code test, but this kind | 294 // TODO(abarth): We should abstract the response_code test, but this kind |
| 294 // of check is scattered throughout our codebase. | 295 // of check is scattered throughout our codebase. |
| 295 request_->SimulateError(net::ERR_FILE_NOT_FOUND); | 296 request_->SimulateError(net::ERR_FILE_NOT_FOUND); |
| 296 return false; | 297 return false; |
| 297 } | 298 } |
| 298 | 299 |
| 299 X509UserCertResourceHandler* x509_cert_handler = | 300 X509UserCertResourceHandler* x509_cert_handler = |
| 300 new X509UserCertResourceHandler(host_, request_, | 301 new X509UserCertResourceHandler(host_, request_, |
| 301 info->child_id(), info->route_id()); | 302 info->child_id(), info->route_id()); |
| 302 UseAlternateResourceHandler(request_id, x509_cert_handler); | 303 UseAlternateResourceHandler(request_id, x509_cert_handler); |
| 303 } | 304 } |
| 304 | 305 |
| 305 // Check to see if we should forward the data from this request to the | 306 // Check to see if we should forward the data from this request to the |
| 306 // download thread. | 307 // download thread. |
| 307 // TODO(paulg): Only download if the context from the renderer allows it. | 308 // TODO(paulg): Only download if the context from the renderer allows it. |
| 308 if (info->allow_download() && ShouldDownload(NULL)) { | 309 if (info->allow_download() && ShouldDownload(NULL)) { |
| 309 if (response_->response_head.headers && // Can be NULL if FTP. | 310 if (response_->headers && // Can be NULL if FTP. |
| 310 response_->response_head.headers->response_code() / 100 != 2) { | 311 response_->headers->response_code() / 100 != 2) { |
| 311 // The response code indicates that this is an error page, but we don't | 312 // The response code indicates that this is an error page, but we don't |
| 312 // know how to display the content. We follow Firefox here and show our | 313 // know how to display the content. We follow Firefox here and show our |
| 313 // own error page instead of triggering a download. | 314 // own error page instead of triggering a download. |
| 314 // TODO(abarth): We should abstract the response_code test, but this kind | 315 // TODO(abarth): We should abstract the response_code test, but this kind |
| 315 // of check is scattered throughout our codebase. | 316 // of check is scattered throughout our codebase. |
| 316 request_->SimulateError(net::ERR_FILE_NOT_FOUND); | 317 request_->SimulateError(net::ERR_FILE_NOT_FOUND); |
| 317 return false; | 318 return false; |
| 318 } | 319 } |
| 319 | 320 |
| 320 info->set_is_download(true); | 321 info->set_is_download(true); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 359 PluginService::GetInstance()->GetPlugins( | 360 PluginService::GetInstance()->GetPlugins( |
| 360 base::Bind(&BufferedResourceHandler::OnPluginsLoaded, this)); | 361 base::Bind(&BufferedResourceHandler::OnPluginsLoaded, this)); |
| 361 return true; | 362 return true; |
| 362 } | 363 } |
| 363 | 364 |
| 364 // This test mirrors the decision that WebKit makes in | 365 // This test mirrors the decision that WebKit makes in |
| 365 // WebFrameLoaderClient::dispatchDecidePolicyForMIMEType. | 366 // WebFrameLoaderClient::dispatchDecidePolicyForMIMEType. |
| 366 bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) { | 367 bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) { |
| 367 if (need_plugin_list) | 368 if (need_plugin_list) |
| 368 *need_plugin_list = false; | 369 *need_plugin_list = false; |
| 369 std::string type = StringToLowerASCII(response_->response_head.mime_type); | 370 std::string type = StringToLowerASCII(response_->mime_type); |
| 370 std::string disposition; | 371 std::string disposition; |
| 371 request_->GetResponseHeaderByName("content-disposition", &disposition); | 372 request_->GetResponseHeaderByName("content-disposition", &disposition); |
| 372 disposition = StringToLowerASCII(disposition); | 373 disposition = StringToLowerASCII(disposition); |
| 373 | 374 |
| 374 // First, examine content-disposition. | 375 // First, examine content-disposition. |
| 375 if (!disposition.empty()) { | 376 if (!disposition.empty()) { |
| 376 bool should_download = true; | 377 bool should_download = true; |
| 377 | 378 |
| 378 // Some broken sites just send ... | 379 // Some broken sites just send ... |
| 379 // Content-Disposition: ; filename="file" | 380 // Content-Disposition: ; filename="file" |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 wait_for_plugins_ = false; | 469 wait_for_plugins_ = false; |
| 469 if (!request_) | 470 if (!request_) |
| 470 return; | 471 return; |
| 471 | 472 |
| 472 ResourceDispatcherHostRequestInfo* info = | 473 ResourceDispatcherHostRequestInfo* info = |
| 473 ResourceDispatcherHost::InfoForRequest(request_); | 474 ResourceDispatcherHost::InfoForRequest(request_); |
| 474 host_->PauseRequest(info->child_id(), info->request_id(), false); | 475 host_->PauseRequest(info->child_id(), info->request_id(), false); |
| 475 if (!CompleteResponseStarted(info->request_id(), false)) | 476 if (!CompleteResponseStarted(info->request_id(), false)) |
| 476 host_->CancelRequest(info->child_id(), info->request_id(), false); | 477 host_->CancelRequest(info->child_id(), info->request_id(), false); |
| 477 } | 478 } |
| OLD | NEW |