| 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);
|
| -}
|
|
|