| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "chrome/browser/renderer_host/buffered_resource_handler.h" | 5 #include "chrome/browser/renderer_host/buffered_resource_handler.h" |
| 6 | 6 |
| 7 #include "base/histogram.h" | 7 #include "base/histogram.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "net/base/mime_sniffer.h" | 10 #include "net/base/mime_sniffer.h" |
| 11 #include "net/base/net_errors.h" | 11 #include "net/base/net_errors.h" |
| 12 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/renderer_host/download_throttling_resource_handler.h" | 13 #include "chrome/browser/renderer_host/download_throttling_resource_handler.h" |
| 13 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | 14 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" |
| 14 #include "chrome/common/url_constants.h" | 15 #include "chrome/common/url_constants.h" |
| 15 #include "net/base/mime_sniffer.h" | 16 #include "net/base/mime_sniffer.h" |
| 17 #include "net/base/mime_util.h" |
| 16 #include "net/base/io_buffer.h" | 18 #include "net/base/io_buffer.h" |
| 17 #include "net/http/http_response_headers.h" | 19 #include "net/http/http_response_headers.h" |
| 20 #include "webkit/glue/plugins/plugin_list.h" |
| 18 | 21 |
| 19 namespace { | 22 namespace { |
| 20 | 23 |
| 21 const int kMaxBytesToSniff = 512; | 24 const int kMaxBytesToSniff = 512; |
| 22 | 25 |
| 23 void RecordSnifferMetrics(bool sniffing_blocked, | 26 void RecordSnifferMetrics(bool sniffing_blocked, |
| 24 bool we_would_like_to_sniff, | 27 bool we_would_like_to_sniff, |
| 25 const std::string& mime_type) { | 28 const std::string& mime_type) { |
| 26 static BooleanHistogram nosniff_usage("nosniff.usage"); | 29 static BooleanHistogram nosniff_usage("nosniff.usage"); |
| 27 nosniff_usage.SetFlags(kUmaTargetedHistogramFlag); | 30 nosniff_usage.SetFlags(kUmaTargetedHistogramFlag); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 42 | 45 |
| 43 BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler, | 46 BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler, |
| 44 ResourceDispatcherHost* host, | 47 ResourceDispatcherHost* host, |
| 45 URLRequest* request) | 48 URLRequest* request) |
| 46 : real_handler_(handler), | 49 : real_handler_(handler), |
| 47 host_(host), | 50 host_(host), |
| 48 request_(request), | 51 request_(request), |
| 49 bytes_read_(0), | 52 bytes_read_(0), |
| 50 sniff_content_(false), | 53 sniff_content_(false), |
| 51 should_buffer_(false), | 54 should_buffer_(false), |
| 55 wait_for_plugins_(false), |
| 52 buffering_(false), | 56 buffering_(false), |
| 53 finished_(false) { | 57 finished_(false) { |
| 54 } | 58 } |
| 55 | 59 |
| 56 bool BufferedResourceHandler::OnUploadProgress(int request_id, | 60 bool BufferedResourceHandler::OnUploadProgress(int request_id, |
| 57 uint64 position, | 61 uint64 position, |
| 58 uint64 size) { | 62 uint64 size) { |
| 59 return real_handler_->OnUploadProgress(request_id, position, size); | 63 return real_handler_->OnUploadProgress(request_id, position, size); |
| 60 } | 64 } |
| 61 | 65 |
| 62 bool BufferedResourceHandler::OnRequestRedirected(int request_id, | 66 bool BufferedResourceHandler::OnRequestRedirected(int request_id, |
| 63 const GURL& new_url, | 67 const GURL& new_url, |
| 64 ResourceResponse* response, | 68 ResourceResponse* response, |
| 65 bool* defer) { | 69 bool* defer) { |
| 66 return real_handler_->OnRequestRedirected( | 70 return real_handler_->OnRequestRedirected( |
| 67 request_id, new_url, response, defer); | 71 request_id, new_url, response, defer); |
| 68 } | 72 } |
| 69 | 73 |
| 70 bool BufferedResourceHandler::OnResponseStarted(int request_id, | 74 bool BufferedResourceHandler::OnResponseStarted(int request_id, |
| 71 ResourceResponse* response) { | 75 ResourceResponse* response) { |
| 72 response_ = response; | 76 response_ = response; |
| 73 if (!DelayResponse()) | 77 if (!DelayResponse()) |
| 74 return CompleteResponseStarted(request_id, false); | 78 return CompleteResponseStarted(request_id, false); |
| 75 return true; | 79 return true; |
| 76 } | 80 } |
| 77 | 81 |
| 78 | |
| 79 bool BufferedResourceHandler::OnResponseCompleted( | 82 bool BufferedResourceHandler::OnResponseCompleted( |
| 80 int request_id, | 83 int request_id, |
| 81 const URLRequestStatus& status, | 84 const URLRequestStatus& status, |
| 82 const std::string& security_info) { | 85 const std::string& security_info) { |
| 83 return real_handler_->OnResponseCompleted(request_id, status, security_info); | 86 return real_handler_->OnResponseCompleted(request_id, status, security_info); |
| 84 } | 87 } |
| 85 | 88 |
| 89 void BufferedResourceHandler::OnRequestClosed() { |
| 90 request_ = NULL; |
| 91 real_handler_->OnRequestClosed(); |
| 92 } |
| 93 |
| 86 // We'll let the original event handler provide a buffer, and reuse it for | 94 // We'll let the original event handler provide a buffer, and reuse it for |
| 87 // subsequent reads until we're done buffering. | 95 // subsequent reads until we're done buffering. |
| 88 bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, | 96 bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, |
| 89 int* buf_size, int min_size) { | 97 int* buf_size, int min_size) { |
| 90 if (buffering_) { | 98 if (buffering_) { |
| 91 DCHECK(!my_buffer_.get()); | 99 DCHECK(!my_buffer_.get()); |
| 92 my_buffer_ = new net::IOBuffer(kMaxBytesToSniff); | 100 my_buffer_ = new net::IOBuffer(kMaxBytesToSniff); |
| 93 *buf = my_buffer_.get(); | 101 *buf = my_buffer_.get(); |
| 94 *buf_size = kMaxBytesToSniff; | 102 *buf_size = kMaxBytesToSniff; |
| 95 // TODO(willchan): Remove after debugging bug 16371. | 103 // TODO(willchan): Remove after debugging bug 16371. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 111 bytes_read_ = 0; | 119 bytes_read_ = 0; |
| 112 return true; | 120 return true; |
| 113 } | 121 } |
| 114 | 122 |
| 115 bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { | 123 bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { |
| 116 if (sniff_content_ || should_buffer_) { | 124 if (sniff_content_ || should_buffer_) { |
| 117 if (KeepBuffering(*bytes_read)) | 125 if (KeepBuffering(*bytes_read)) |
| 118 return true; | 126 return true; |
| 119 | 127 |
| 120 LOG(INFO) << "Finished buffering " << request_->url().spec(); | 128 LOG(INFO) << "Finished buffering " << request_->url().spec(); |
| 121 sniff_content_ = should_buffer_ = false; | |
| 122 *bytes_read = bytes_read_; | 129 *bytes_read = bytes_read_; |
| 123 | 130 |
| 124 // Done buffering, send the pending ResponseStarted event. | 131 // Done buffering, send the pending ResponseStarted event. |
| 125 if (!CompleteResponseStarted(request_id, true)) | 132 if (!CompleteResponseStarted(request_id, true)) |
| 126 return false; | 133 return false; |
| 134 } else if (wait_for_plugins_) { |
| 135 return true; |
| 127 } | 136 } |
| 128 | 137 |
| 129 // Release the reference that we acquired at OnWillRead. | 138 // Release the reference that we acquired at OnWillRead. |
| 130 read_buffer_ = NULL; | 139 read_buffer_ = NULL; |
| 131 return real_handler_->OnReadCompleted(request_id, bytes_read); | 140 return real_handler_->OnReadCompleted(request_id, bytes_read); |
| 132 } | 141 } |
| 133 | 142 |
| 134 bool BufferedResourceHandler::DelayResponse() { | 143 bool BufferedResourceHandler::DelayResponse() { |
| 135 std::string mime_type; | 144 std::string mime_type; |
| 136 request_->GetMimeType(&mime_type); | 145 request_->GetMimeType(&mime_type); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 164 } | 173 } |
| 165 | 174 |
| 166 if (ShouldBuffer(request_->url(), mime_type)) { | 175 if (ShouldBuffer(request_->url(), mime_type)) { |
| 167 // This is a temporary fix for the fact that webkit expects to have | 176 // This is a temporary fix for the fact that webkit expects to have |
| 168 // enough data to decode the doctype in order to select the rendering | 177 // enough data to decode the doctype in order to select the rendering |
| 169 // mode. | 178 // mode. |
| 170 should_buffer_ = true; | 179 should_buffer_ = true; |
| 171 LOG(INFO) << "To buffer: " << request_->url().spec(); | 180 LOG(INFO) << "To buffer: " << request_->url().spec(); |
| 172 return true; | 181 return true; |
| 173 } | 182 } |
| 183 |
| 184 if (ShouldWaitForPlugins()) { |
| 185 wait_for_plugins_ = true; |
| 186 return true; |
| 187 } |
| 188 |
| 174 return false; | 189 return false; |
| 175 } | 190 } |
| 176 | 191 |
| 177 bool BufferedResourceHandler::ShouldBuffer(const GURL& url, | 192 bool BufferedResourceHandler::ShouldBuffer(const GURL& url, |
| 178 const std::string& mime_type) { | 193 const std::string& mime_type) { |
| 179 // We are willing to buffer for HTTP and HTTPS. | 194 // We are willing to buffer for HTTP and HTTPS. |
| 180 bool sniffable_scheme = url.is_empty() || | 195 bool sniffable_scheme = url.is_empty() || |
| 181 url.SchemeIs(chrome::kHttpScheme) || | 196 url.SchemeIs(chrome::kHttpScheme) || |
| 182 url.SchemeIs(chrome::kHttpsScheme); | 197 url.SchemeIs(chrome::kHttpsScheme); |
| 183 if (!sniffable_scheme) | 198 if (!sniffable_scheme) |
| 184 return false; | 199 return false; |
| 185 | 200 |
| 186 // Today, the only reason to buffer the request is to fix the doctype decoding | 201 // Today, the only reason to buffer the request is to fix the doctype decoding |
| 187 // performed by webkit: if there is not enough data it will go to quirks mode. | 202 // performed by webkit: if there is not enough data it will go to quirks mode. |
| 188 // We only expect the doctype check to apply to html documents. | 203 // We only expect the doctype check to apply to html documents. |
| 189 return mime_type == "text/html"; | 204 return mime_type == "text/html"; |
| 190 } | 205 } |
| 191 | 206 |
| 207 bool BufferedResourceHandler::DidBufferEnough(int bytes_read) { |
| 208 const int kRequiredLength = 256; |
| 209 |
| 210 return bytes_read >= kRequiredLength; |
| 211 } |
| 212 |
| 192 bool BufferedResourceHandler::KeepBuffering(int bytes_read) { | 213 bool BufferedResourceHandler::KeepBuffering(int bytes_read) { |
| 193 DCHECK(read_buffer_); | 214 DCHECK(read_buffer_); |
| 194 if (my_buffer_) { | 215 if (my_buffer_) { |
| 195 // We are using our own buffer to read, update the main buffer. | 216 // We are using our own buffer to read, update the main buffer. |
| 196 CHECK(bytes_read + bytes_read_ < read_buffer_size_); | 217 CHECK(bytes_read + bytes_read_ < read_buffer_size_); |
| 197 memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read); | 218 memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read); |
| 198 my_buffer_ = NULL; | 219 my_buffer_ = NULL; |
| 199 } | 220 } |
| 200 bytes_read_ += bytes_read; | 221 bytes_read_ += bytes_read; |
| 201 finished_ = (bytes_read == 0); | 222 finished_ = (bytes_read == 0); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 212 DCHECK(bytes_read_ < kMaxBytesToSniff); | 233 DCHECK(bytes_read_ < kMaxBytesToSniff); |
| 213 if (!finished_) { | 234 if (!finished_) { |
| 214 buffering_ = true; | 235 buffering_ = true; |
| 215 return true; | 236 return true; |
| 216 } | 237 } |
| 217 } | 238 } |
| 218 sniff_content_ = false; | 239 sniff_content_ = false; |
| 219 response_->response_head.mime_type.assign(new_type); | 240 response_->response_head.mime_type.assign(new_type); |
| 220 | 241 |
| 221 // We just sniffed the mime type, maybe there is a doctype to process. | 242 // We just sniffed the mime type, maybe there is a doctype to process. |
| 222 if (ShouldBuffer(request_->url(), new_type)) | 243 if (ShouldBuffer(request_->url(), new_type)) { |
| 223 should_buffer_ = true; | 244 should_buffer_ = true; |
| 245 } else if (ShouldWaitForPlugins()) { |
| 246 wait_for_plugins_ = true; |
| 247 } |
| 224 } | 248 } |
| 225 | 249 |
| 226 if (!finished_ && should_buffer_) { | 250 if (should_buffer_) { |
| 227 if (!DidBufferEnough(bytes_read_)) { | 251 if (!finished_ && !DidBufferEnough(bytes_read_)) { |
| 228 buffering_ = true; | 252 buffering_ = true; |
| 229 return true; | 253 return true; |
| 230 } | 254 } |
| 255 |
| 256 should_buffer_ = false; |
| 257 if (ShouldWaitForPlugins()) |
| 258 wait_for_plugins_ = true; |
| 231 } | 259 } |
| 260 |
| 232 buffering_ = false; | 261 buffering_ = false; |
| 262 |
| 263 if (wait_for_plugins_) |
| 264 return true; |
| 265 |
| 233 return false; | 266 return false; |
| 234 } | 267 } |
| 235 | 268 |
| 236 bool BufferedResourceHandler::CompleteResponseStarted(int request_id, | 269 bool BufferedResourceHandler::CompleteResponseStarted(int request_id, |
| 237 bool in_complete) { | 270 bool in_complete) { |
| 238 // Check to see if we should forward the data from this request to the | 271 // Check to see if we should forward the data from this request to the |
| 239 // download thread. | 272 // download thread. |
| 240 // TODO(paulg): Only download if the context from the renderer allows it. | 273 // TODO(paulg): Only download if the context from the renderer allows it. |
| 241 std::string content_disposition; | |
| 242 request_->GetResponseHeaderByName("content-disposition", | |
| 243 &content_disposition); | |
| 244 | |
| 245 ResourceDispatcherHost::ExtraRequestInfo* info = | 274 ResourceDispatcherHost::ExtraRequestInfo* info = |
| 246 ResourceDispatcherHost::ExtraInfoForRequest(request_); | 275 ResourceDispatcherHost::ExtraInfoForRequest(request_); |
| 247 | 276 |
| 248 if (info->allow_download && | 277 if (info->allow_download && ShouldDownload(NULL)) { |
| 249 host_->ShouldDownload(response_->response_head.mime_type, | |
| 250 content_disposition)) { | |
| 251 if (response_->response_head.headers && // Can be NULL if FTP. | 278 if (response_->response_head.headers && // Can be NULL if FTP. |
| 252 response_->response_head.headers->response_code() / 100 != 2) { | 279 response_->response_head.headers->response_code() / 100 != 2) { |
| 253 // The response code indicates that this is an error page, but we don't | 280 // The response code indicates that this is an error page, but we don't |
| 254 // know how to display the content. We follow Firefox here and show our | 281 // know how to display the content. We follow Firefox here and show our |
| 255 // own error page instead of triggering a download. | 282 // own error page instead of triggering a download. |
| 256 // TODO(abarth): We should abstract the response_code test, but this kind | 283 // TODO(abarth): We should abstract the response_code test, but this kind |
| 257 // of check is scattered throughout our codebase. | 284 // of check is scattered throughout our codebase. |
| 258 request_->SimulateError(net::ERR_FILE_NOT_FOUND); | 285 request_->SimulateError(net::ERR_FILE_NOT_FOUND); |
| 259 return false; | 286 return false; |
| 260 } | 287 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 286 real_handler_->OnResponseCompleted(info->request_id, status, | 313 real_handler_->OnResponseCompleted(info->request_id, status, |
| 287 std::string()); | 314 std::string()); |
| 288 | 315 |
| 289 // Ditch the old async handler that talks to the renderer for the new | 316 // Ditch the old async handler that talks to the renderer for the new |
| 290 // download handler that talks to the DownloadManager. | 317 // download handler that talks to the DownloadManager. |
| 291 real_handler_ = download_handler; | 318 real_handler_ = download_handler; |
| 292 } | 319 } |
| 293 return real_handler_->OnResponseStarted(request_id, response_); | 320 return real_handler_->OnResponseStarted(request_id, response_); |
| 294 } | 321 } |
| 295 | 322 |
| 296 bool BufferedResourceHandler::DidBufferEnough(int bytes_read) { | 323 bool BufferedResourceHandler::ShouldWaitForPlugins() { |
| 297 const int kRequiredLength = 256; | 324 bool need_plugin_list; |
| 325 if (!ShouldDownload(&need_plugin_list) || !need_plugin_list) |
| 326 return false; |
| 298 | 327 |
| 299 return bytes_read >= kRequiredLength; | 328 // We don't want to keep buffering as our buffer will fill up. |
| 329 ResourceDispatcherHost::ExtraRequestInfo* info = |
| 330 ResourceDispatcherHost::ExtraInfoForRequest(request_); |
| 331 host_->PauseRequest(info->process_id, info->request_id, true); |
| 332 |
| 333 // Schedule plugin loading on the file thread. |
| 334 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, |
| 335 NewRunnableMethod(this, &BufferedResourceHandler::LoadPlugins)); |
| 336 return true; |
| 300 } | 337 } |
| 338 |
| 339 // This test mirrors the decision that WebKit makes in |
| 340 // WebFrameLoaderClient::dispatchDecidePolicyForMIMEType. |
| 341 bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) { |
| 342 if (need_plugin_list) |
| 343 *need_plugin_list = false; |
| 344 std::string type = StringToLowerASCII(response_->response_head.mime_type); |
| 345 std::string disposition; |
| 346 request_->GetResponseHeaderByName("content-disposition", &disposition); |
| 347 disposition = StringToLowerASCII(disposition); |
| 348 |
| 349 // First, examine content-disposition. |
| 350 if (!disposition.empty()) { |
| 351 bool should_download = true; |
| 352 |
| 353 // Some broken sites just send ... |
| 354 // Content-Disposition: ; filename="file" |
| 355 // ... screen those out here. |
| 356 if (disposition[0] == ';') |
| 357 should_download = false; |
| 358 |
| 359 if (disposition.compare(0, 6, "inline") == 0) |
| 360 should_download = false; |
| 361 |
| 362 // Some broken sites just send ... |
| 363 // Content-Disposition: filename="file" |
| 364 // ... without a disposition token... Screen those out. |
| 365 if (disposition.compare(0, 8, "filename") == 0) |
| 366 should_download = false; |
| 367 |
| 368 // Also in use is Content-Disposition: name="file" |
| 369 if (disposition.compare(0, 4, "name") == 0) |
| 370 should_download = false; |
| 371 |
| 372 // We have a content-disposition of "attachment" or unknown. |
| 373 // RFC 2183, section 2.8 says that an unknown disposition |
| 374 // value should be treated as "attachment". |
| 375 if (should_download) |
| 376 return true; |
| 377 } |
| 378 |
| 379 // MIME type checking. |
| 380 if (net::IsSupportedMimeType(type)) |
| 381 return false; |
| 382 |
| 383 if (need_plugin_list) { |
| 384 if (!NPAPI::PluginList::Singleton()->PluginsLoaded()) { |
| 385 *need_plugin_list = true; |
| 386 return true; |
| 387 } |
| 388 } else { |
| 389 DCHECK(NPAPI::PluginList::Singleton()->PluginsLoaded()); |
| 390 } |
| 391 |
| 392 // Finally, check the plugin list. |
| 393 WebPluginInfo info; |
| 394 bool allow_wildcard = false; |
| 395 return !NPAPI::PluginList::Singleton()->GetPluginInfo( |
| 396 GURL(), type, "", allow_wildcard, &info, NULL); |
| 397 } |
| 398 |
| 399 void BufferedResourceHandler::LoadPlugins() { |
| 400 std::vector<WebPluginInfo> plugins; |
| 401 NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins); |
| 402 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, |
| 403 NewRunnableMethod(this, &BufferedResourceHandler::OnPluginsLoaded)); |
| 404 } |
| 405 |
| 406 void BufferedResourceHandler::OnPluginsLoaded() { |
| 407 wait_for_plugins_ = false; |
| 408 if (!request_) |
| 409 return; |
| 410 |
| 411 ResourceDispatcherHost::ExtraRequestInfo* info = |
| 412 ResourceDispatcherHost::ExtraInfoForRequest(request_); |
| 413 host_->PauseRequest(info->process_id, info->request_id, false); |
| 414 if (!CompleteResponseStarted(info->request_id, false)) |
| 415 host_->CancelRequest(info->process_id, info->request_id, false); |
| 416 } |
| OLD | NEW |