summaryrefslogtreecommitdiff
path: root/chromium/content/browser/loader/async_resource_handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/loader/async_resource_handler.cc')
-rw-r--r--chromium/content/browser/loader/async_resource_handler.cc365
1 files changed, 365 insertions, 0 deletions
diff --git a/chromium/content/browser/loader/async_resource_handler.cc b/chromium/content/browser/loader/async_resource_handler.cc
new file mode 100644
index 00000000000..02e5552b901
--- /dev/null
+++ b/chromium/content/browser/loader/async_resource_handler.cc
@@ -0,0 +1,365 @@
+// Copyright (c) 2012 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 "content/browser/loader/async_resource_handler.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/containers/hash_tables.h"
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/browser/devtools/devtools_netlog_observer.h"
+#include "content/browser/host_zoom_map_impl.h"
+#include "content/browser/loader/resource_buffer.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/resource_message_filter.h"
+#include "content/browser/loader/resource_request_info_impl.h"
+#include "content/browser/resource_context_impl.h"
+#include "content/common/resource_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "content/public/common/resource_response.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+
+using base::TimeTicks;
+
+namespace content {
+namespace {
+
+static int kBufferSize = 1024 * 512;
+static int kMinAllocationSize = 1024 * 4;
+static int kMaxAllocationSize = 1024 * 32;
+
+void GetNumericArg(const std::string& name, int* result) {
+ const std::string& value =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
+ if (!value.empty())
+ base::StringToInt(value, result);
+}
+
+void InitializeResourceBufferConstants() {
+ static bool did_init = false;
+ if (did_init)
+ return;
+ did_init = true;
+
+ GetNumericArg("resource-buffer-size", &kBufferSize);
+ GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
+ GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
+}
+
+int CalcUsedPercentage(int bytes_read, int buffer_size) {
+ double ratio = static_cast<double>(bytes_read) / buffer_size;
+ return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer.
+}
+
+} // namespace
+
+class DependentIOBuffer : public net::WrappedIOBuffer {
+ public:
+ DependentIOBuffer(ResourceBuffer* backing, char* memory)
+ : net::WrappedIOBuffer(memory),
+ backing_(backing) {
+ }
+ private:
+ virtual ~DependentIOBuffer() {}
+ scoped_refptr<ResourceBuffer> backing_;
+};
+
+AsyncResourceHandler::AsyncResourceHandler(
+ ResourceMessageFilter* filter,
+ int routing_id,
+ net::URLRequest* request,
+ ResourceDispatcherHostImpl* rdh)
+ : ResourceMessageDelegate(request),
+ filter_(filter),
+ routing_id_(routing_id),
+ request_(request),
+ rdh_(rdh),
+ pending_data_count_(0),
+ allocation_size_(0),
+ did_defer_(false),
+ has_checked_for_sufficient_resources_(false),
+ sent_received_response_msg_(false),
+ sent_first_data_msg_(false) {
+ InitializeResourceBufferConstants();
+}
+
+AsyncResourceHandler::~AsyncResourceHandler() {
+ if (has_checked_for_sufficient_resources_)
+ rdh_->FinishedWithResourcesForRequest(request_);
+}
+
+bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(AsyncResourceHandler, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
+ IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void AsyncResourceHandler::OnFollowRedirect(
+ int request_id,
+ bool has_new_first_party_for_cookies,
+ const GURL& new_first_party_for_cookies) {
+ if (!request_->status().is_success()) {
+ DVLOG(1) << "OnFollowRedirect for invalid request";
+ return;
+ }
+
+ if (has_new_first_party_for_cookies)
+ request_->set_first_party_for_cookies(new_first_party_for_cookies);
+
+ ResumeIfDeferred();
+}
+
+void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
+ if (pending_data_count_) {
+ --pending_data_count_;
+
+ buffer_->RecycleLeastRecentlyAllocated();
+ if (buffer_->CanAllocate())
+ ResumeIfDeferred();
+ }
+}
+
+bool AsyncResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id,
+ position, size));
+}
+
+bool AsyncResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ *defer = did_defer_ = true;
+
+ if (rdh_->delegate()) {
+ rdh_->delegate()->OnRequestRedirected(new_url, request_,
+ filter_->resource_context(),
+ response);
+ }
+
+ DevToolsNetLogObserver::PopulateResponseInfo(request_, response);
+ response->head.request_start = request_->creation_time();
+ response->head.response_start = TimeTicks::Now();
+ return filter_->Send(new ResourceMsg_ReceivedRedirect(
+ routing_id_, request_id, new_url, response->head));
+}
+
+bool AsyncResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response,
+ bool* defer) {
+ // For changes to the main frame, inform the renderer of the new URL's
+ // per-host settings before the request actually commits. This way the
+ // renderer will be able to set these precisely at the time the
+ // request commits, avoiding the possibility of e.g. zooming the old content
+ // or of having to layout the new content twice.
+
+ ResourceContext* resource_context = filter_->resource_context();
+ if (rdh_->delegate()) {
+ rdh_->delegate()->OnResponseStarted(
+ request_, resource_context, response, filter_.get());
+ }
+
+ DevToolsNetLogObserver::PopulateResponseInfo(request_, response);
+
+ HostZoomMap* host_zoom_map =
+ GetHostZoomMapForResourceContext(resource_context);
+
+ const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
+ if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
+ const GURL& request_url = request_->url();
+ filter_->Send(new ViewMsg_SetZoomLevelForLoadingURL(
+ info->GetRouteID(),
+ request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
+ request_url.scheme(),
+ net::GetHostOrSpecFromURL(request_url))));
+ }
+
+ response->head.request_start = request_->creation_time();
+ response->head.response_start = TimeTicks::Now();
+ filter_->Send(new ResourceMsg_ReceivedResponse(
+ routing_id_, request_id, response->head));
+ sent_received_response_msg_ = true;
+
+ if (request_->response_info().metadata.get()) {
+ std::vector<char> copy(request_->response_info().metadata->data(),
+ request_->response_info().metadata->data() +
+ request_->response_info().metadata->size());
+ filter_->Send(new ResourceMsg_ReceivedCachedMetadata(
+ routing_id_, request_id, copy));
+ }
+
+ return true;
+}
+
+bool AsyncResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return true;
+}
+
+bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ DCHECK_EQ(-1, min_size);
+
+ if (!EnsureResourceBufferIsInitialized())
+ return false;
+
+ DCHECK(buffer_->CanAllocate());
+ char* memory = buffer_->Allocate(&allocation_size_);
+ CHECK(memory);
+
+ *buf = new DependentIOBuffer(buffer_.get(), memory);
+ *buf_size = allocation_size_;
+
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
+ *buf_size, 0, kMaxAllocationSize, 100);
+ return true;
+}
+
+bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
+ bool* defer) {
+ if (!bytes_read)
+ return true;
+
+ buffer_->ShrinkLastAllocation(bytes_read);
+
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.AsyncResourceHandler_SharedIOBuffer_Used",
+ bytes_read, 0, kMaxAllocationSize, 100);
+ UMA_HISTOGRAM_PERCENTAGE(
+ "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
+ CalcUsedPercentage(bytes_read, allocation_size_));
+
+ if (!sent_first_data_msg_) {
+ base::SharedMemoryHandle handle;
+ int size;
+ if (!buffer_->ShareToProcess(filter_->PeerHandle(), &handle, &size))
+ return false;
+ filter_->Send(
+ new ResourceMsg_SetDataBuffer(routing_id_, request_id, handle, size,
+ filter_->peer_pid()));
+ sent_first_data_msg_ = true;
+ }
+
+ int data_offset = buffer_->GetLastAllocationOffset();
+ int encoded_data_length =
+ DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_);
+
+ filter_->Send(
+ new ResourceMsg_DataReceived(routing_id_, request_id, data_offset,
+ bytes_read, encoded_data_length));
+ ++pending_data_count_;
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.AsyncResourceHandler_PendingDataCount",
+ pending_data_count_, 0, 100, 100);
+
+ if (!buffer_->CanAllocate()) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
+ pending_data_count_, 0, 100, 100);
+ *defer = did_defer_ = true;
+ }
+
+ return true;
+}
+
+void AsyncResourceHandler::OnDataDownloaded(
+ int request_id, int bytes_downloaded) {
+ filter_->Send(new ResourceMsg_DataDownloaded(
+ routing_id_, request_id, bytes_downloaded));
+}
+
+bool AsyncResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ // If we crash here, figure out what URL the renderer was requesting.
+ // http://crbug.com/107692
+ char url_buf[128];
+ base::strlcpy(url_buf, request_->url().spec().c_str(), arraysize(url_buf));
+ base::debug::Alias(url_buf);
+
+ // TODO(gavinp): Remove this CHECK when we figure out the cause of
+ // http://crbug.com/124680 . This check mirrors closely check in
+ // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
+ // ResourceHandleInternal which asserts on its state and crashes. By crashing
+ // when the message is sent, we should get better crash reports.
+ CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
+ sent_received_response_msg_);
+
+ TimeTicks completion_time = TimeTicks::Now();
+
+ int error_code = status.error();
+ bool was_ignored_by_handler =
+ ResourceRequestInfoImpl::ForRequest(request_)->WasIgnoredByHandler();
+
+ DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
+ // If this check fails, then we're in an inconsistent state because all
+ // requests ignored by the handler should be canceled (which should result in
+ // the ERR_ABORTED error code).
+ DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
+
+ // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
+ // with a status() != SUCCESS and an error_code() == net::OK.
+ if (status.status() == net::URLRequestStatus::CANCELED &&
+ error_code == net::OK) {
+ error_code = net::ERR_ABORTED;
+ } else if (status.status() == net::URLRequestStatus::FAILED &&
+ error_code == net::OK) {
+ error_code = net::ERR_FAILED;
+ }
+
+ filter_->Send(new ResourceMsg_RequestComplete(routing_id_,
+ request_id,
+ error_code,
+ was_ignored_by_handler,
+ security_info,
+ completion_time));
+ return true;
+}
+
+bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
+ if (buffer_.get() && buffer_->IsInitialized())
+ return true;
+
+ if (!has_checked_for_sufficient_resources_) {
+ has_checked_for_sufficient_resources_ = true;
+ if (!rdh_->HasSufficientResourcesForRequest(request_)) {
+ controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
+ return false;
+ }
+ }
+
+ buffer_ = new ResourceBuffer();
+ return buffer_->Initialize(kBufferSize,
+ kMinAllocationSize,
+ kMaxAllocationSize);
+}
+
+void AsyncResourceHandler::ResumeIfDeferred() {
+ if (did_defer_) {
+ did_defer_ = false;
+ controller()->Resume();
+ }
+}
+
+} // namespace content