Index: chrome/browser/renderer_host/buffered_resource_handler.cc |
=================================================================== |
--- chrome/browser/renderer_host/buffered_resource_handler.cc (revision 75488) |
+++ chrome/browser/renderer_host/buffered_resource_handler.cc (working copy) |
@@ -1,491 +0,0 @@ |
-// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/renderer_host/buffered_resource_handler.h" |
- |
-#include <vector> |
- |
-#include "base/logging.h" |
-#include "base/metrics/histogram.h" |
-#include "base/string_util.h" |
-#include "chrome/browser/browser_thread.h" |
-#include "chrome/browser/renderer_host/download_throttling_resource_handler.h" |
-#include "chrome/browser/renderer_host/resource_dispatcher_host.h" |
-#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" |
-#include "chrome/browser/renderer_host/x509_user_cert_resource_handler.h" |
-#include "chrome/common/extensions/user_script.h" |
-#include "chrome/common/resource_response.h" |
-#include "chrome/common/url_constants.h" |
-#include "net/base/io_buffer.h" |
-#include "net/base/mime_sniffer.h" |
-#include "net/base/mime_util.h" |
-#include "net/base/net_errors.h" |
-#include "net/http/http_response_headers.h" |
-#include "webkit/plugins/npapi/plugin_list.h" |
- |
-namespace { |
- |
-void RecordSnifferMetrics(bool sniffing_blocked, |
- bool we_would_like_to_sniff, |
- const std::string& mime_type) { |
- static scoped_refptr<base::Histogram> nosniff_usage = |
- base::BooleanHistogram::FactoryGet( |
- "nosniff.usage", base::Histogram::kUmaTargetedHistogramFlag); |
- nosniff_usage->AddBoolean(sniffing_blocked); |
- |
- if (sniffing_blocked) { |
- static scoped_refptr<base::Histogram> nosniff_otherwise = |
- base::BooleanHistogram::FactoryGet( |
- "nosniff.otherwise", base::Histogram::kUmaTargetedHistogramFlag); |
- nosniff_otherwise->AddBoolean(we_would_like_to_sniff); |
- |
- static scoped_refptr<base::Histogram> nosniff_empty_mime_type = |
- base::BooleanHistogram::FactoryGet( |
- "nosniff.empty_mime_type", |
- base::Histogram::kUmaTargetedHistogramFlag); |
- nosniff_empty_mime_type->AddBoolean(mime_type.empty()); |
- } |
-} |
- |
-} // namespace |
- |
-BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler, |
- ResourceDispatcherHost* host, |
- net::URLRequest* request) |
- : real_handler_(handler), |
- host_(host), |
- request_(request), |
- read_buffer_size_(0), |
- bytes_read_(0), |
- sniff_content_(false), |
- should_buffer_(false), |
- wait_for_plugins_(false), |
- buffering_(false), |
- finished_(false) { |
-} |
- |
-bool BufferedResourceHandler::OnUploadProgress(int request_id, |
- uint64 position, |
- uint64 size) { |
- return real_handler_->OnUploadProgress(request_id, position, size); |
-} |
- |
-bool BufferedResourceHandler::OnRequestRedirected(int request_id, |
- const GURL& new_url, |
- ResourceResponse* response, |
- bool* defer) { |
- return real_handler_->OnRequestRedirected( |
- request_id, new_url, response, defer); |
-} |
- |
-bool BufferedResourceHandler::OnResponseStarted(int request_id, |
- ResourceResponse* response) { |
- response_ = response; |
- if (!DelayResponse()) |
- return CompleteResponseStarted(request_id, false); |
- return true; |
-} |
- |
-bool BufferedResourceHandler::OnResponseCompleted( |
- int request_id, |
- const net::URLRequestStatus& status, |
- const std::string& security_info) { |
- return real_handler_->OnResponseCompleted(request_id, status, security_info); |
-} |
- |
-void BufferedResourceHandler::OnRequestClosed() { |
- request_ = NULL; |
- real_handler_->OnRequestClosed(); |
-} |
- |
-bool BufferedResourceHandler::OnWillStart(int request_id, |
- const GURL& url, |
- bool* defer) { |
- return real_handler_->OnWillStart(request_id, url, defer); |
-} |
- |
-// We'll let the original event handler provide a buffer, and reuse it for |
-// subsequent reads until we're done buffering. |
-bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, |
- int* buf_size, int min_size) { |
- if (buffering_) { |
- DCHECK(!my_buffer_.get()); |
- my_buffer_ = new net::IOBuffer(net::kMaxBytesToSniff); |
- *buf = my_buffer_.get(); |
- *buf_size = net::kMaxBytesToSniff; |
- return true; |
- } |
- |
- if (finished_) |
- return false; |
- |
- if (!real_handler_->OnWillRead(request_id, buf, buf_size, min_size)) { |
- return false; |
- } |
- read_buffer_ = *buf; |
- read_buffer_size_ = *buf_size; |
- DCHECK_GE(read_buffer_size_, net::kMaxBytesToSniff * 2); |
- bytes_read_ = 0; |
- return true; |
-} |
- |
-bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { |
- if (sniff_content_ || should_buffer_) { |
- if (KeepBuffering(*bytes_read)) |
- return true; |
- |
- *bytes_read = bytes_read_; |
- |
- // Done buffering, send the pending ResponseStarted event. |
- if (!CompleteResponseStarted(request_id, true)) |
- return false; |
- } else if (wait_for_plugins_) { |
- return true; |
- } |
- |
- // Release the reference that we acquired at OnWillRead. |
- read_buffer_ = NULL; |
- return real_handler_->OnReadCompleted(request_id, bytes_read); |
-} |
- |
-BufferedResourceHandler::~BufferedResourceHandler() {} |
- |
-bool BufferedResourceHandler::DelayResponse() { |
- std::string mime_type; |
- request_->GetMimeType(&mime_type); |
- |
- std::string content_type_options; |
- request_->GetResponseHeaderByName("x-content-type-options", |
- &content_type_options); |
- |
- const bool sniffing_blocked = |
- LowerCaseEqualsASCII(content_type_options, "nosniff"); |
- const bool not_modified_status = |
- response_->response_head.headers && |
- response_->response_head.headers->response_code() == 304; |
- const bool we_would_like_to_sniff = not_modified_status ? |
- false : net::ShouldSniffMimeType(request_->url(), mime_type); |
- |
- RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type); |
- |
- if (!sniffing_blocked && we_would_like_to_sniff) { |
- // We're going to look at the data before deciding what the content type |
- // is. That means we need to delay sending the ResponseStarted message |
- // over the IPC channel. |
- sniff_content_ = true; |
- VLOG(1) << "To buffer: " << request_->url().spec(); |
- return true; |
- } |
- |
- if (sniffing_blocked && mime_type.empty() && !not_modified_status) { |
- // Ugg. The server told us not to sniff the content but didn't give us a |
- // mime type. What's a browser to do? Turns out, we're supposed to treat |
- // the response as "text/plain". This is the most secure option. |
- mime_type.assign("text/plain"); |
- response_->response_head.mime_type.assign(mime_type); |
- } |
- |
- if (mime_type == "application/rss+xml" || |
- mime_type == "application/atom+xml") { |
- // Sad face. The server told us that they wanted us to treat the response |
- // as RSS or Atom. Unfortunately, we don't have a built-in feed previewer |
- // like other browsers. We can't just render the content as XML because |
- // web sites let third parties inject arbitrary script into their RSS |
- // feeds. That leaves us with little choice but to practically ignore the |
- // response. In the future, when we have an RSS feed previewer, we can |
- // remove this logic. |
- mime_type.assign("text/plain"); |
- response_->response_head.mime_type.assign(mime_type); |
- } |
- |
- if (ShouldBuffer(request_->url(), mime_type)) { |
- // This is a temporary fix for the fact that webkit expects to have |
- // enough data to decode the doctype in order to select the rendering |
- // mode. |
- should_buffer_ = true; |
- return true; |
- } |
- |
- if (!not_modified_status && ShouldWaitForPlugins()) { |
- wait_for_plugins_ = true; |
- return true; |
- } |
- |
- return false; |
-} |
- |
-bool BufferedResourceHandler::ShouldBuffer(const GURL& url, |
- const std::string& mime_type) { |
- // We are willing to buffer for HTTP and HTTPS. |
- bool sniffable_scheme = url.is_empty() || |
- url.SchemeIs(chrome::kHttpScheme) || |
- url.SchemeIs(chrome::kHttpsScheme); |
- if (!sniffable_scheme) |
- return false; |
- |
- // Today, the only reason to buffer the request is to fix the doctype decoding |
- // performed by webkit: if there is not enough data it will go to quirks mode. |
- // We only expect the doctype check to apply to html documents. |
- return mime_type == "text/html"; |
-} |
- |
-bool BufferedResourceHandler::DidBufferEnough(int bytes_read) { |
- const int kRequiredLength = 256; |
- |
- return bytes_read >= kRequiredLength; |
-} |
- |
-bool BufferedResourceHandler::KeepBuffering(int bytes_read) { |
- DCHECK(read_buffer_); |
- if (my_buffer_) { |
- // We are using our own buffer to read, update the main buffer. |
- // TODO(darin): We should handle the case where read_buffer_size_ is small! |
- // See RedirectToFileResourceHandler::BufIsFull to see how this impairs |
- // downstream ResourceHandler implementations. |
- CHECK_LT(bytes_read + bytes_read_, read_buffer_size_); |
- memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read); |
- my_buffer_ = NULL; |
- } |
- bytes_read_ += bytes_read; |
- finished_ = (bytes_read == 0); |
- |
- if (sniff_content_) { |
- std::string type_hint, new_type; |
- request_->GetMimeType(&type_hint); |
- |
- if (!net::SniffMimeType(read_buffer_->data(), bytes_read_, |
- request_->url(), type_hint, &new_type)) { |
- // SniffMimeType() returns false if there is not enough data to determine |
- // the mime type. However, even if it returns false, it returns a new type |
- // that is probably better than the current one. |
- DCHECK_LT(bytes_read_, net::kMaxBytesToSniff); |
- if (!finished_) { |
- buffering_ = true; |
- return true; |
- } |
- } |
- sniff_content_ = false; |
- response_->response_head.mime_type.assign(new_type); |
- |
- // We just sniffed the mime type, maybe there is a doctype to process. |
- if (ShouldBuffer(request_->url(), new_type)) { |
- should_buffer_ = true; |
- } else if (ShouldWaitForPlugins()) { |
- wait_for_plugins_ = true; |
- } |
- } |
- |
- if (should_buffer_) { |
- if (!finished_ && !DidBufferEnough(bytes_read_)) { |
- buffering_ = true; |
- return true; |
- } |
- |
- should_buffer_ = false; |
- if (ShouldWaitForPlugins()) |
- wait_for_plugins_ = true; |
- } |
- |
- buffering_ = false; |
- |
- if (wait_for_plugins_) |
- return true; |
- |
- return false; |
-} |
- |
-bool BufferedResourceHandler::CompleteResponseStarted(int request_id, |
- bool in_complete) { |
- ResourceDispatcherHostRequestInfo* info = |
- ResourceDispatcherHost::InfoForRequest(request_); |
- std::string mime_type; |
- request_->GetMimeType(&mime_type); |
- |
- // Check if this is an X.509 certificate, if yes, let it be handled |
- // by X509UserCertResourceHandler. |
- if (mime_type == "application/x-x509-user-cert") { |
- // This is entirely similar to how DownloadThrottlingResourceHandler |
- // works except we are doing it for an X.509 client certificates. |
- |
- if (response_->response_head.headers && // Can be NULL if FTP. |
- response_->response_head.headers->response_code() / 100 != 2) { |
- // The response code indicates that this is an error page, but we are |
- // expecting an X.509 user certificate. We follow Firefox here and show |
- // our own error page instead of handling the error page as a |
- // certificate. |
- // TODO(abarth): We should abstract the response_code test, but this kind |
- // of check is scattered throughout our codebase. |
- request_->SimulateError(net::ERR_FILE_NOT_FOUND); |
- return false; |
- } |
- |
- X509UserCertResourceHandler* x509_cert_handler = |
- new X509UserCertResourceHandler(host_, request_, |
- info->child_id(), info->route_id()); |
- UseAlternateResourceHandler(request_id, x509_cert_handler); |
- } |
- |
- // Check to see if we should forward the data from this request to the |
- // download thread. |
- // TODO(paulg): Only download if the context from the renderer allows it. |
- if (info->allow_download() && ShouldDownload(NULL)) { |
- if (response_->response_head.headers && // Can be NULL if FTP. |
- response_->response_head.headers->response_code() / 100 != 2) { |
- // The response code indicates that this is an error page, but we don't |
- // know how to display the content. We follow Firefox here and show our |
- // own error page instead of triggering a download. |
- // TODO(abarth): We should abstract the response_code test, but this kind |
- // of check is scattered throughout our codebase. |
- request_->SimulateError(net::ERR_FILE_NOT_FOUND); |
- return false; |
- } |
- |
- info->set_is_download(true); |
- |
- DownloadThrottlingResourceHandler* download_handler = |
- new DownloadThrottlingResourceHandler(host_, |
- request_, |
- request_->url(), |
- info->child_id(), |
- info->route_id(), |
- request_id, |
- in_complete); |
- UseAlternateResourceHandler(request_id, download_handler); |
- } |
- return real_handler_->OnResponseStarted(request_id, response_); |
-} |
- |
-bool BufferedResourceHandler::ShouldWaitForPlugins() { |
- bool need_plugin_list; |
- if (!ShouldDownload(&need_plugin_list) || !need_plugin_list) |
- return false; |
- |
- // We don't want to keep buffering as our buffer will fill up. |
- ResourceDispatcherHostRequestInfo* info = |
- ResourceDispatcherHost::InfoForRequest(request_); |
- host_->PauseRequest(info->child_id(), info->request_id(), true); |
- |
- // Schedule plugin loading on the file thread. |
- BrowserThread::PostTask( |
- BrowserThread::FILE, FROM_HERE, |
- NewRunnableMethod(this, &BufferedResourceHandler::LoadPlugins)); |
- return true; |
-} |
- |
-// This test mirrors the decision that WebKit makes in |
-// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType. |
-bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) { |
- if (need_plugin_list) |
- *need_plugin_list = false; |
- std::string type = StringToLowerASCII(response_->response_head.mime_type); |
- std::string disposition; |
- request_->GetResponseHeaderByName("content-disposition", &disposition); |
- disposition = StringToLowerASCII(disposition); |
- |
- // First, examine content-disposition. |
- if (!disposition.empty()) { |
- bool should_download = true; |
- |
- // Some broken sites just send ... |
- // Content-Disposition: ; filename="file" |
- // ... screen those out here. |
- if (disposition[0] == ';') |
- should_download = false; |
- |
- if (disposition.compare(0, 6, "inline") == 0) |
- should_download = false; |
- |
- // Some broken sites just send ... |
- // Content-Disposition: filename="file" |
- // ... without a disposition token... Screen those out. |
- if (disposition.compare(0, 8, "filename") == 0) |
- should_download = false; |
- |
- // Also in use is Content-Disposition: name="file" |
- if (disposition.compare(0, 4, "name") == 0) |
- should_download = false; |
- |
- // We have a content-disposition of "attachment" or unknown. |
- // RFC 2183, section 2.8 says that an unknown disposition |
- // value should be treated as "attachment". |
- if (should_download) |
- return true; |
- } |
- |
- // Special-case user scripts to get downloaded instead of viewed. |
- if (UserScript::HasUserScriptFileExtension(request_->url())) |
- return true; |
- |
- // MIME type checking. |
- if (net::IsSupportedMimeType(type)) |
- return false; |
- |
- if (need_plugin_list) { |
- if (!webkit::npapi::PluginList::Singleton()->PluginsLoaded()) { |
- *need_plugin_list = true; |
- return true; |
- } |
- } else { |
- DCHECK(webkit::npapi::PluginList::Singleton()->PluginsLoaded()); |
- } |
- |
- // Finally, check the plugin list. |
- webkit::npapi::WebPluginInfo info; |
- bool allow_wildcard = false; |
- return !webkit::npapi::PluginList::Singleton()->GetPluginInfo( |
- GURL(), type, allow_wildcard, &info, NULL) || |
- !webkit::npapi::IsPluginEnabled(info); |
-} |
- |
-void BufferedResourceHandler::UseAlternateResourceHandler( |
- int request_id, |
- ResourceHandler* handler) { |
- ResourceDispatcherHostRequestInfo* info = |
- ResourceDispatcherHost::InfoForRequest(request_); |
- if (bytes_read_) { |
- // A Read has already occured and we need to copy the data into the new |
- // ResourceHandler. |
- net::IOBuffer* buf = NULL; |
- int buf_len = 0; |
- handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_); |
- CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); |
- memcpy(buf->data(), read_buffer_->data(), bytes_read_); |
- } |
- |
- // Inform the original ResourceHandler that this will be handled entirely by |
- // the new ResourceHandler. |
- real_handler_->OnResponseStarted(info->request_id(), response_); |
- net::URLRequestStatus status(net::URLRequestStatus::HANDLED_EXTERNALLY, 0); |
- real_handler_->OnResponseCompleted(info->request_id(), status, std::string()); |
- |
- // Remove the non-owning pointer to the CrossSiteResourceHandler, if any, |
- // from the extra request info because the CrossSiteResourceHandler (part of |
- // the original ResourceHandler chain) will be deleted by the next statement. |
- info->set_cross_site_handler(NULL); |
- |
- // This is handled entirely within the new ResourceHandler, so just reset the |
- // original ResourceHandler. |
- real_handler_ = handler; |
-} |
- |
-void BufferedResourceHandler::LoadPlugins() { |
- std::vector<webkit::npapi::WebPluginInfo> plugins; |
- webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins); |
- |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- NewRunnableMethod(this, &BufferedResourceHandler::OnPluginsLoaded)); |
-} |
- |
-void BufferedResourceHandler::OnPluginsLoaded() { |
- wait_for_plugins_ = false; |
- if (!request_) |
- return; |
- |
- ResourceDispatcherHostRequestInfo* info = |
- ResourceDispatcherHost::InfoForRequest(request_); |
- host_->PauseRequest(info->child_id(), info->request_id(), false); |
- if (!CompleteResponseStarted(info->request_id(), false)) |
- host_->CancelRequest(info->child_id(), info->request_id(), false); |
-} |