summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-20 01:18:59 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-20 01:18:59 +0000
commitfe54944f6080ed9f48619899ad342924754b19a9 (patch)
tree7959716d0543c6699cd254af29ba963b48fecd69 /chrome
parent6995f1dfc7d50b1296221b3875d79d31a978eb6a (diff)
downloadchromium_src-fe54944f6080ed9f48619899ad342924754b19a9.zip
chromium_src-fe54944f6080ed9f48619899ad342924754b19a9.tar.gz
chromium_src-fe54944f6080ed9f48619899ad342924754b19a9.tar.bz2
This CL moves the different resource handlers from resource_dispatcher_host.cc into their own files, into a new renderer_host directory.
Their is no functionality change in this CL. The motivation for doing that was: - resource_dispatcher_host.cc has become very long and more than 1/3 of the code is for the resource handler. - having the resource handler in their own file allows having unit tests for them. BUG=none TEST=run all unit tests and UI tests. Review URL: http://codereview.chromium.org/14487 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7336 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/browser.scons12
-rw-r--r--chrome/browser/browser.vcproj106
-rw-r--r--chrome/browser/browser_process.h2
-rw-r--r--chrome/browser/browser_process_impl.cc2
-rw-r--r--chrome/browser/download/download_file.cc2
-rw-r--r--chrome/browser/download/download_manager.cc2
-rw-r--r--chrome/browser/download/save_file_manager.cc2
-rw-r--r--chrome/browser/download/save_package.cc2
-rw-r--r--chrome/browser/login_prompt.cc2
-rw-r--r--chrome/browser/plugin_process_host.cc2
-rw-r--r--chrome/browser/render_widget_helper.cc2
-rw-r--r--chrome/browser/renderer_host/async_resource_handler.cc109
-rw-r--r--chrome/browser/renderer_host/async_resource_handler.h50
-rw-r--r--chrome/browser/renderer_host/buffered_resource_handler.cc231
-rw-r--r--chrome/browser/renderer_host/buffered_resource_handler.h62
-rw-r--r--chrome/browser/renderer_host/cross_site_resource_handler.cc218
-rw-r--r--chrome/browser/renderer_host/cross_site_resource_handler.h57
-rw-r--r--chrome/browser/renderer_host/download_resource_handler.cc169
-rw-r--r--chrome/browser/renderer_host/download_resource_handler.h75
-rw-r--r--chrome/browser/renderer_host/download_throttling_resource_handler.cc145
-rw-r--r--chrome/browser/renderer_host/download_throttling_resource_handler.h87
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc1217
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.h (renamed from chrome/browser/resource_dispatcher_host.h)105
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc (renamed from chrome/browser/resource_dispatcher_host_uitest.cc)4
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc (renamed from chrome/browser/resource_dispatcher_host_unittest.cc)2
-rw-r--r--chrome/browser/renderer_host/resource_handler.h67
-rw-r--r--chrome/browser/renderer_host/safe_browsing_resource_handler.cc179
-rw-r--r--chrome/browser/renderer_host/safe_browsing_resource_handler.h63
-rw-r--r--chrome/browser/renderer_host/save_file_resource_handler.cc85
-rw-r--r--chrome/browser/renderer_host/save_file_resource_handler.h64
-rw-r--r--chrome/browser/renderer_host/sync_resource_handler.cc54
-rw-r--r--chrome/browser/renderer_host/sync_resource_handler.h34
-rw-r--r--chrome/browser/resource_dispatcher_host.cc2524
-rw-r--r--chrome/browser/resource_message_filter.h2
-rw-r--r--chrome/browser/resource_request_details.h2
-rw-r--r--chrome/browser/ssl_manager.h2
-rw-r--r--chrome/browser/tab_util.cc2
-rw-r--r--chrome/browser/views/options/advanced_contents_view.cc1
-rw-r--r--chrome/test/ui/ui_tests.vcproj2
-rw-r--r--chrome/test/unit/unittests.vcproj20
40 files changed, 3135 insertions, 2633 deletions
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons
index 8b6105b..abfb6ff 100644
--- a/chrome/browser/browser.scons
+++ b/chrome/browser/browser.scons
@@ -244,8 +244,16 @@ if env.Bit('windows'):
'render_view_host_manager.cc',
'render_widget_helper.cc',
'render_widget_host.cc',
- 'repost_Form_warning_dialog.cc',
- 'resource_dispatcher_host.cc',
+ 'renderer_host/async_resource_handler.cc',
+ 'renderer_host/buffered_resource_handler.cc',
+ 'renderer_host/cross_site_resource_handler.cc',
+ 'renderer_host/download_resource_handler.cc',
+ 'renderer_host/download_throttling_resource_handler.cc',
+ 'renderer_host/resource_dispatcher_host.cc',
+ 'renderer_host/safe_browsing_resource_handler.cc',
+ 'renderer_host/save_file_resource_handler.cc',
+ 'renderer_host/sync_resource_handler.cc',
+ 'repost_form_warning_dialog.cc',
'resource_message_filter.cc',
'rlz/rlz.cc',
'safe_browsing/protocol_manager.cc',
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 387f8ee..61ad12a 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -554,14 +554,6 @@
>
</File>
<File
- RelativePath=".\resource_dispatcher_host.cc"
- >
- </File>
- <File
- RelativePath=".\resource_dispatcher_host.h"
- >
- </File>
- <File
RelativePath=".\resource_message_filter.cc"
>
</File>
@@ -2254,6 +2246,104 @@
>
</File>
</Filter>
+ <Filter
+ Name="Renderer Host"
+ >
+ <File
+ RelativePath=".\renderer_host\async_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\async_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\buffered_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\buffered_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\cross_site_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\cross_site_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\download_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\download_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\download_throttling_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\download_throttling_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\resource_dispatcher_host.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\renderer_host\resource_dispatcher_host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\safe_browsing_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\safe_browsing_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\save_file_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\save_file_resource_handler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\sync_resource_handler.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\renderer_host\sync_resource_handler.h"
+ >
+ </File>
+ </Filter>
<File
RelativePath=".\browser_trial.cc"
>
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 221f491..18ce8be 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -16,7 +16,7 @@
#include "base/basictypes.h"
#include "base/message_loop.h"
#if defined(OS_WIN)
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#endif // defined(OS_WIN)
class AutomationProviderList;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 3d3e8a5..904de03 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -19,7 +19,7 @@
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/debugger/debugger_wrapper.h"
#include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc
index 2a5e612..b06be04 100644
--- a/chrome/browser/download/download_file.cc
+++ b/chrome/browser/download/download_file.cc
@@ -15,7 +15,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/profile.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/tab_util.h"
#include "chrome/browser/web_contents.h"
#include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc
index adf62d3..b4bdea9 100644
--- a/chrome/browser/download/download_manager.cc
+++ b/chrome/browser/download/download_manager.cc
@@ -24,7 +24,7 @@
#include "chrome/browser/profile.h"
#include "chrome/browser/render_process_host.h"
#include "chrome/browser/render_view_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/tab_util.h"
#include "chrome/browser/web_contents.h"
#include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/download/save_file_manager.cc b/chrome/browser/download/save_file_manager.cc
index 631b55d..41607ea 100644
--- a/chrome/browser/download/save_file_manager.cc
+++ b/chrome/browser/download/save_file_manager.cc
@@ -15,7 +15,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/save_file.h"
#include "chrome/browser/download/save_package.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_util.h"
#include "chrome/browser/web_contents.h"
diff --git a/chrome/browser/download/save_package.cc b/chrome/browser/download/save_package.cc
index 9eaf7fc..d01d2d7 100644
--- a/chrome/browser/download/save_package.cc
+++ b/chrome/browser/download/save_package.cc
@@ -21,7 +21,7 @@
#include "chrome/browser/render_process_host.h"
#include "chrome/browser/render_view_host.h"
#include "chrome/browser/render_view_host_delegate.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/tab_util.h"
#include "chrome/browser/web_contents.h"
#include "chrome/browser/views/download_shelf_view.h"
diff --git a/chrome/browser/login_prompt.cc b/chrome/browser/login_prompt.cc
index db9d775..24fd24f 100644
--- a/chrome/browser/login_prompt.cc
+++ b/chrome/browser/login_prompt.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/password_manager.h"
#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/web_contents.h"
#include "chrome/browser/tab_util.h"
#include "chrome/browser/views/login_view.h"
diff --git a/chrome/browser/plugin_process_host.cc b/chrome/browser/plugin_process_host.cc
index 323fa93..4dcb107 100644
--- a/chrome/browser/plugin_process_host.cc
+++ b/chrome/browser/plugin_process_host.cc
@@ -21,7 +21,7 @@
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/sandbox_policy.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_plugin_lib.h"
diff --git a/chrome/browser/render_widget_helper.cc b/chrome/browser/render_widget_helper.cc
index 4b87080..0d76141 100644
--- a/chrome/browser/render_widget_helper.cc
+++ b/chrome/browser/render_widget_helper.cc
@@ -7,7 +7,7 @@
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
using base::TimeDelta;
using base::TimeTicks;
diff --git a/chrome/browser/renderer_host/async_resource_handler.cc b/chrome/browser/renderer_host/async_resource_handler.cc
new file mode 100644
index 0000000..2708a22
--- /dev/null
+++ b/chrome/browser/renderer_host/async_resource_handler.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2006-2008 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/async_resource_handler.h"
+
+base::SharedMemory* AsyncResourceHandler::spare_read_buffer_;
+
+AsyncResourceHandler::AsyncResourceHandler(
+ ResourceDispatcherHost::Receiver* receiver,
+ int render_process_host_id,
+ int routing_id,
+ HANDLE render_process,
+ const GURL& url,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : receiver_(receiver),
+ render_process_host_id_(render_process_host_id),
+ routing_id_(routing_id),
+ render_process_(render_process),
+ rdh_(resource_dispatcher_host) {
+}
+
+bool AsyncResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return receiver_->Send(new ViewMsg_Resource_UploadProgress(routing_id_,
+ request_id,
+ position, size));
+}
+
+bool AsyncResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url) {
+ return receiver_->Send(new ViewMsg_Resource_ReceivedRedirect(routing_id_,
+ request_id,
+ new_url));
+}
+
+bool AsyncResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ receiver_->Send(new ViewMsg_Resource_ReceivedResponse(
+ routing_id_, request_id, response->response_head));
+ return true;
+}
+
+bool AsyncResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size,
+ int min_size) {
+ DCHECK(min_size == -1);
+ static const int kReadBufSize = 32768;
+ if (spare_read_buffer_) {
+ read_buffer_.reset(spare_read_buffer_);
+ spare_read_buffer_ = NULL;
+ } else {
+ read_buffer_.reset(new base::SharedMemory);
+ if (!read_buffer_->Create(std::wstring(), false, false, kReadBufSize))
+ return false;
+ if (!read_buffer_->Map(kReadBufSize))
+ return false;
+ }
+ *buf = static_cast<char*>(read_buffer_->memory());
+ *buf_size = kReadBufSize;
+ return true;
+}
+
+bool AsyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+ DCHECK(read_buffer_.get());
+
+ if (!rdh_->WillSendData(render_process_host_id_, request_id)) {
+ // We should not send this data now, we have too many pending requests.
+ return true;
+ }
+
+ base::SharedMemoryHandle handle;
+ if (!read_buffer_->GiveToProcess(render_process_, &handle)) {
+ // We wrongfully incremented the pending data count. Fake an ACK message
+ // to fix this. We can't move this call above the WillSendData because
+ // it's killing our read_buffer_, and we don't want that when we pause
+ // the request.
+ rdh_->OnDataReceivedACK(render_process_host_id_, request_id);
+ return false;
+ }
+
+ receiver_->Send(new ViewMsg_Resource_DataReceived(
+ routing_id_, request_id, handle, *bytes_read));
+
+ return true;
+}
+
+bool AsyncResourceHandler::OnResponseCompleted(int request_id,
+ const URLRequestStatus& status) {
+ receiver_->Send(new ViewMsg_Resource_RequestComplete(routing_id_,
+ request_id, status));
+
+ // If we still have a read buffer, then see about caching it for later...
+ if (spare_read_buffer_) {
+ read_buffer_.reset();
+ } else if (read_buffer_.get() && read_buffer_->memory()) {
+ spare_read_buffer_ = read_buffer_.release();
+ }
+ return true;
+}
+
+// static
+void AsyncResourceHandler::GlobalCleanup() {
+ delete spare_read_buffer_;
+ spare_read_buffer_ = NULL;
+}
diff --git a/chrome/browser/renderer_host/async_resource_handler.h b/chrome/browser/renderer_host/async_resource_handler.h
new file mode 100644
index 0000000..5d24b75
--- /dev/null
+++ b/chrome/browser/renderer_host/async_resource_handler.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+class base::SharedMemory;
+
+// Used to complete an asynchronous resource request in response to resource
+// load events from the resource dispatcher host.
+class AsyncResourceHandler : public ResourceHandler {
+ public:
+ AsyncResourceHandler(ResourceDispatcherHost::Receiver* receiver,
+ int render_process_host_id,
+ int routing_id,
+ HANDLE render_process,
+ const GURL& url,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ bool OnRequestRedirected(int request_id, const GURL& new_url);
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ static void GlobalCleanup();
+
+ private:
+ // When reading, we don't know if we are going to get EOF (0 bytes read), so
+ // we typically have a buffer that we allocated but did not use. We keep
+ // this buffer around for the next read as a small optimization.
+ static base::SharedMemory* spare_read_buffer_;
+
+ scoped_ptr<base::SharedMemory> read_buffer_;
+ ResourceDispatcherHost::Receiver* receiver_;
+ int render_process_host_id_;
+ int routing_id_;
+ HANDLE render_process_;
+ ResourceDispatcherHost* rdh_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc
new file mode 100644
index 0000000..ae9fa35
--- /dev/null
+++ b/chrome/browser/renderer_host/buffered_resource_handler.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2006-2008 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 "net/base/mime_sniffer.h"
+#include "chrome/browser/renderer_host/download_throttling_resource_handler.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+
+BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler,
+ ResourceDispatcherHost* host,
+ URLRequest* request)
+ : real_handler_(handler),
+ host_(host),
+ request_(request),
+ bytes_read_(0),
+ sniff_content_(false),
+ should_buffer_(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) {
+ return real_handler_->OnRequestRedirected(request_id, new_url);
+}
+
+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 URLRequestStatus& status) {
+ return real_handler_->OnResponseCompleted(request_id, status);
+}
+
+// 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,
+ char** buf, int* buf_size,
+ int min_size) {
+ if (buffering_) {
+ *buf = read_buffer_ + bytes_read_;
+ *buf_size = read_buffer_size_ - bytes_read_;
+ DCHECK(*buf_size > 0);
+ return true;
+ }
+
+ if (finished_)
+ return false;
+
+ bool ret = real_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+ read_buffer_ = *buf;
+ read_buffer_size_ = *buf_size;
+ bytes_read_ = 0;
+ return ret;
+}
+
+bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request_);
+
+ if (sniff_content_ || should_buffer_) {
+ if (KeepBuffering(*bytes_read))
+ return true;
+
+ LOG(INFO) << "Finished buffering " << request_->url().spec();
+ sniff_content_ = should_buffer_ = false;
+ *bytes_read = bytes_read_;
+
+ // Done buffering, send the pending ResponseStarted event.
+ if (!CompleteResponseStarted(request_id, true))
+ return false;
+ }
+
+ return real_handler_->OnReadCompleted(request_id, bytes_read);
+}
+
+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);
+ if (content_type_options != "nosniff" &&
+ net::ShouldSniffMimeType(request_->url(), mime_type)) {
+ // 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;
+ LOG(INFO) << "To buffer: " << request_->url().spec();
+ return true;
+ }
+
+ 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;
+ LOG(INFO) << "To buffer: " << request_->url().spec();
+ 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("http") ||
+ url.SchemeIs("https");
+ 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::KeepBuffering(int bytes_read) {
+ DCHECK(read_buffer_);
+ 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_, 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(bytes_read_ < 512 /*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;
+ }
+
+ if (!finished_ && should_buffer_) {
+ if (!DidBufferEnough(bytes_read_)) {
+ buffering_ = true;
+ return true;
+ }
+ }
+ buffering_ = false;
+ return false;
+}
+
+bool BufferedResourceHandler::CompleteResponseStarted(int request_id,
+ bool in_complete) {
+ // 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.
+ std::string content_disposition;
+ request_->GetResponseHeaderByName("content-disposition",
+ &content_disposition);
+
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request_);
+
+ if (info->allow_download &&
+ host_->ShouldDownload(response_->response_head.mime_type,
+ content_disposition)) {
+ 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_->CancelWithError(net::ERR_FILE_NOT_FOUND);
+ return false;
+ }
+
+ info->is_download = true;
+
+ scoped_refptr<DownloadThrottlingResourceHandler> download_handler =
+ new DownloadThrottlingResourceHandler(host_,
+ request_,
+ request_->url().spec(),
+ info->render_process_host_id,
+ info->render_view_id,
+ request_id,
+ in_complete);
+ if (bytes_read_) {
+ // a Read has already occurred and we need to copy the data into the
+ // EventHandler.
+ char *buf = NULL;
+ int buf_len = 0;
+ download_handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_);
+ CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
+ memcpy(buf, read_buffer_, bytes_read_);
+ }
+ // Update the renderer with the response headers which will cause it to
+ // cancel the request.
+ // TODO(paulg): Send the renderer a response that indicates that the request
+ // will be handled by an external source (the browser).
+ real_handler_->OnResponseStarted(info->request_id, response_);
+ real_handler_ = download_handler;
+ }
+ return real_handler_->OnResponseStarted(request_id, response_);
+}
+
+bool BufferedResourceHandler::DidBufferEnough(int bytes_read) {
+ const int kRequiredLength = 256;
+
+ return bytes_read >= kRequiredLength;
+}
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.h b/chrome/browser/renderer_host/buffered_resource_handler.h
new file mode 100644
index 0000000..0b832a9
--- /dev/null
+++ b/chrome/browser/renderer_host/buffered_resource_handler.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
+
+#include <string>
+
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+class ResourceDispatcherHost;
+
+// Used to buffer a request until enough data has been received.
+class BufferedResourceHandler : public ResourceHandler {
+ public:
+ BufferedResourceHandler(ResourceHandler* handler,
+ ResourceDispatcherHost* host,
+ URLRequest* request);
+
+ // ResourceHandler implementation:
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ bool OnRequestRedirected(int request_id, const GURL& new_url);
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ private:
+ // Returns true if we should delay OnResponseStarted forwarding.
+ bool DelayResponse();
+
+ // Returns true if there will be a need to parse the DocType of the document
+ // to determine the right way to handle it.
+ bool ShouldBuffer(const GURL& url, const std::string& mime_type);
+
+ // Returns true if there is enough information to process the DocType.
+ bool DidBufferEnough(int bytes_read);
+
+ // Returns true if we have to keep buffering data.
+ bool KeepBuffering(int bytes_read);
+
+ // Sends a pending OnResponseStarted notification. |in_complete| is true if
+ // this is invoked from |OnResponseCompleted|.
+ bool CompleteResponseStarted(int request_id, bool in_complete);
+
+ scoped_refptr<ResourceHandler> real_handler_;
+ scoped_refptr<ResourceResponse> response_;
+ ResourceDispatcherHost* host_;
+ URLRequest* request_;
+ char* read_buffer_;
+ int read_buffer_size_;
+ int bytes_read_;
+ bool sniff_content_;
+ bool should_buffer_;
+ bool buffering_;
+ bool finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.cc b/chrome/browser/renderer_host/cross_site_resource_handler.cc
new file mode 100644
index 0000000..aaf579b
--- /dev/null
+++ b/chrome/browser/renderer_host/cross_site_resource_handler.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2006-2008 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/cross_site_resource_handler.h"
+
+#include "chrome/browser/render_view_host.h"
+
+namespace {
+// Task to notify the WebContents that a cross-site response has begun, so that
+// WebContents can tell the old page to run its onunload handler.
+class CrossSiteNotifyTabTask : public Task {
+ public:
+ CrossSiteNotifyTabTask(int render_process_host_id,
+ int render_view_id,
+ int request_id)
+ : render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ request_id_(request_id) {}
+
+ void Run() {
+ RenderViewHost* view =
+ RenderViewHost::FromID(render_process_host_id_, render_view_id_);
+ if (view) {
+ view->OnCrossSiteResponse(render_process_host_id_, request_id_);
+ } else {
+ // The view couldn't be found.
+ // TODO(creis): Should notify the IO thread to proceed anyway, using
+ // ResourceDispatcherHost::OnClosePageACK.
+ }
+ }
+
+ private:
+ int render_process_host_id_;
+ int render_view_id_;
+ int request_id_;
+};
+}
+
+CrossSiteResourceHandler::CrossSiteResourceHandler(
+ ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : next_handler_(handler),
+ render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ has_started_response_(false),
+ in_cross_site_transition_(false),
+ request_id_(-1),
+ completed_during_transition_(false),
+ completed_status_(),
+ response_(NULL),
+ rdh_(resource_dispatcher_host) {}
+
+bool CrossSiteResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url) {
+ // We should not have started the transition before being redirected.
+ DCHECK(!in_cross_site_transition_);
+ return next_handler_->OnRequestRedirected(request_id, new_url);
+}
+
+bool CrossSiteResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ // At this point, we know that the response is safe to send back to the
+ // renderer: it is not a download, and it has passed the SSL and safe
+ // browsing checks.
+ // We should not have already started the transition before now.
+ DCHECK(!in_cross_site_transition_);
+ has_started_response_ = true;
+
+ // Look up the request and associated info.
+ ResourceDispatcherHost::GlobalRequestID global_id(render_process_host_id_,
+ request_id);
+ URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Request wasn't found";
+ return false;
+ }
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+
+ // If this is a download, just pass the response through without doing a
+ // cross-site check. The renderer will see it is a download and abort the
+ // request.
+ if (info->is_download) {
+ return next_handler_->OnResponseStarted(request_id, response);
+ }
+
+ // Tell the renderer to run the onunload event handler, and wait for the
+ // reply.
+ StartCrossSiteTransition(request_id, response, global_id);
+ return true;
+}
+
+bool CrossSiteResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size,
+ int min_size) {
+ return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+}
+
+bool CrossSiteResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (!in_cross_site_transition_) {
+ return next_handler_->OnReadCompleted(request_id, bytes_read);
+ }
+ return true;
+}
+
+bool CrossSiteResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status) {
+ if (!in_cross_site_transition_) {
+ if (has_started_response_) {
+ // We've already completed the transition, so just pass it through.
+ return next_handler_->OnResponseCompleted(request_id, status);
+ } else {
+ // Some types of failures will call OnResponseCompleted without calling
+ // CrossSiteResourceHandler::OnResponseStarted. We should wait now for
+ // the cross-site transition. Also continue with the logic below to
+ // remember that we completed during the cross-site transition.
+ ResourceDispatcherHost::GlobalRequestID global_id(render_process_host_id_,
+ request_id);
+ StartCrossSiteTransition(request_id, NULL, global_id);
+ }
+ }
+
+ // We have to buffer the call until after the transition completes.
+ completed_during_transition_ = true;
+ completed_status_ = status;
+
+ // Return false to tell RDH not to notify the world or clean up the
+ // pending request. We will do so in ResumeResponse.
+ return false;
+}
+
+// We can now send the response to the new renderer, which will cause
+// WebContents to swap in the new renderer and destroy the old one.
+void CrossSiteResourceHandler::ResumeResponse() {
+ DCHECK(request_id_ != -1);
+ DCHECK(in_cross_site_transition_);
+ in_cross_site_transition_ = false;
+
+ // Find the request for this response.
+ ResourceDispatcherHost::GlobalRequestID global_id(render_process_host_id_,
+ request_id_);
+ URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Resuming a request that wasn't found";
+ return;
+ }
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+
+ if (has_started_response_) {
+ // Send OnResponseStarted to the new renderer.
+ DCHECK(response_);
+ next_handler_->OnResponseStarted(request_id_, response_);
+
+ // Unpause the request to resume reading. Any further reads will be
+ // directed toward the new renderer.
+ rdh_->PauseRequest(render_process_host_id_, request_id_, false);
+ }
+
+ // Remove ourselves from the ExtraRequestInfo.
+ info->cross_site_handler = NULL;
+
+ // If the response completed during the transition, notify the next
+ // event handler.
+ if (completed_during_transition_) {
+ next_handler_->OnResponseCompleted(request_id_, completed_status_);
+
+ // Since we didn't notify the world or clean up the pending request in
+ // RDH::OnResponseCompleted during the transition, we should do it now.
+ rdh_->NotifyResponseCompleted(request, render_process_host_id_);
+ rdh_->RemovePendingRequest(render_process_host_id_, request_id_);
+ }
+}
+
+// Prepare to render the cross-site response in a new RenderViewHost, by
+// telling the old RenderViewHost to run its onunload handler.
+void CrossSiteResourceHandler::StartCrossSiteTransition(
+ int request_id,
+ ResourceResponse* response,
+ ResourceDispatcherHost::GlobalRequestID global_id) {
+ in_cross_site_transition_ = true;
+ request_id_ = request_id;
+ response_ = response;
+
+ // Store this handler on the ExtraRequestInfo, so that RDH can call our
+ // ResumeResponse method when the close ACK is received.
+ URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Cross site response for a request that wasn't found";
+ return;
+ }
+ ResourceDispatcherHost::ExtraRequestInfo* info =
+ ResourceDispatcherHost::ExtraInfoForRequest(request);
+ info->cross_site_handler = this;
+
+ if (has_started_response_) {
+ // Pause the request until the old renderer is finished and the new
+ // renderer is ready.
+ rdh_->PauseRequest(render_process_host_id_, request_id, true);
+ }
+ // If our OnResponseStarted wasn't called, then we're being called by
+ // OnResponseCompleted after a failure. We don't need to pause, because
+ // there will be no reads.
+
+ // Tell the tab responsible for this request that a cross-site response is
+ // starting, so that it can tell its old renderer to run its onunload
+ // handler now. We will wait to hear the corresponding ClosePage_ACK.
+ CrossSiteNotifyTabTask* task =
+ new CrossSiteNotifyTabTask(render_process_host_id_,
+ render_view_id_,
+ request_id);
+ rdh_->ui_loop()->PostTask(FROM_HERE, task);
+}
diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.h b/chrome/browser/renderer_host/cross_site_resource_handler.h
new file mode 100644
index 0000000..7801352
--- /dev/null
+++ b/chrome/browser/renderer_host/cross_site_resource_handler.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+// Ensures that cross-site responses are delayed until the onunload handler of
+// the previous page is allowed to run. This handler wraps an
+// AsyncEventHandler, and it sits inside SafeBrowsing and Buffered event
+// handlers. This is important, so that it can intercept OnResponseStarted
+// after we determine that a response is safe and not a download.
+class CrossSiteResourceHandler : public ResourceHandler {
+ public:
+ CrossSiteResourceHandler(ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ bool OnRequestRedirected(int request_id, const GURL& new_url);
+ bool OnResponseStarted(int request_id,
+ ResourceResponse* response);
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ // We can now send the response to the new renderer, which will cause
+ // WebContents to swap in the new renderer and destroy the old one.
+ void ResumeResponse();
+
+ private:
+ // Prepare to render the cross-site response in a new RenderViewHost, by
+ // telling the old RenderViewHost to run its onunload handler.
+ void StartCrossSiteTransition(
+ int request_id,
+ ResourceResponse* response,
+ ResourceDispatcherHost::GlobalRequestID global_id);
+
+ scoped_refptr<ResourceHandler> next_handler_;
+ int render_process_host_id_;
+ int render_view_id_;
+ bool has_started_response_;
+ bool in_cross_site_transition_;
+ int request_id_;
+ bool completed_during_transition_;
+ URLRequestStatus completed_status_;
+ ResourceResponse* response_;
+ ResourceDispatcherHost* rdh_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/download_resource_handler.cc b/chrome/browser/renderer_host/download_resource_handler.cc
new file mode 100644
index 0000000..13e7457
--- /dev/null
+++ b/chrome/browser/renderer_host/download_resource_handler.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2006-2008 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/download_resource_handler.h"
+
+#include "chrome/browser/download/download_file.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+
+DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh,
+ int render_process_host_id,
+ int render_view_id,
+ int request_id,
+ const std::string& url,
+ DownloadFileManager* manager,
+ URLRequest* request,
+ bool save_as)
+ : download_id_(-1),
+ global_id_(ResourceDispatcherHost::GlobalRequestID(render_process_host_id,
+ request_id)),
+ render_view_id_(render_view_id),
+ read_buffer_(NULL),
+ url_(UTF8ToWide(url)),
+ content_length_(0),
+ download_manager_(manager),
+ request_(request),
+ save_as_(save_as),
+ buffer_(new DownloadBuffer),
+ rdh_(rdh),
+ is_paused_(false) {
+}
+
+// Not needed, as this event handler ought to be the final resource.
+bool DownloadResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& url) {
+ url_ = UTF8ToWide(url.spec());
+ return true;
+}
+
+// Send the download creation information to the download thread.
+bool DownloadResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ std::string content_disposition;
+ request_->GetResponseHeaderByName("content-disposition",
+ &content_disposition);
+ set_content_disposition(content_disposition);
+ set_content_length(response->response_head.content_length);
+
+ download_id_ = download_manager_->GetNextId();
+ // |download_manager_| consumes (deletes):
+ DownloadCreateInfo* info = new DownloadCreateInfo;
+ info->url = url_;
+ info->start_time = base::Time::Now();
+ info->received_bytes = 0;
+ info->total_bytes = content_length_;
+ info->state = DownloadItem::IN_PROGRESS;
+ info->download_id = download_id_;
+ info->render_process_id = global_id_.render_process_host_id;
+ info->render_view_id = render_view_id_;
+ info->request_id = global_id_.request_id;
+ info->content_disposition = content_disposition_;
+ info->mime_type = response->response_head.mime_type;
+ info->save_as = save_as_;
+ info->is_dangerous = false;
+ download_manager_->file_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(download_manager_,
+ &DownloadFileManager::StartDownload,
+ info));
+ return true;
+}
+
+// Create a new buffer, which will be handed to the download thread for file
+// writing and deletion.
+bool DownloadResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size,
+ int min_size) {
+ DCHECK(buf && buf_size);
+ if (!read_buffer_) {
+ *buf_size = min_size < 0 ? kReadBufSize : min_size;
+ read_buffer_ = new char[*buf_size];
+ }
+ *buf = read_buffer_;
+ return true;
+}
+
+// Pass the buffer to the download file writer.
+bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+ DCHECK(read_buffer_);
+ AutoLock auto_lock(buffer_->lock);
+ bool need_update = buffer_->contents.empty();
+ buffer_->contents.push_back(std::make_pair(read_buffer_, *bytes_read));
+ if (need_update) {
+ download_manager_->file_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(download_manager_,
+ &DownloadFileManager::UpdateDownload,
+ download_id_,
+ buffer_));
+ }
+ read_buffer_ = NULL;
+
+ // We schedule a pause outside of the read loop if there is too much file
+ // writing work to do.
+ if (buffer_->contents.size() > kLoadsToWrite)
+ StartPauseTimer();
+
+ return true;
+}
+
+bool DownloadResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status) {
+ download_manager_->file_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(download_manager_,
+ &DownloadFileManager::DownloadFinished,
+ download_id_,
+ buffer_));
+ delete [] read_buffer_;
+
+ // 'buffer_' is deleted by the DownloadFileManager.
+ buffer_ = NULL;
+ return true;
+}
+
+// If the content-length header is not present (or contains something other
+// than numbers), the incoming content_length is -1 (unknown size).
+// Set the content length to 0 to indicate unknown size to DownloadManager.
+void DownloadResourceHandler::set_content_length(const int64& content_length) {
+ content_length_ = 0;
+ if (content_length > 0)
+ content_length_ = content_length;
+}
+
+void DownloadResourceHandler::set_content_disposition(
+ const std::string& content_disposition) {
+ content_disposition_ = content_disposition;
+}
+
+void DownloadResourceHandler::CheckWriteProgress() {
+ if (!buffer_)
+ return; // The download completed while we were waiting to run.
+
+ size_t contents_size;
+ {
+ AutoLock lock(buffer_->lock);
+ contents_size = buffer_->contents.size();
+ }
+
+ bool should_pause = contents_size > kLoadsToWrite;
+
+ // We'll come back later and see if it's okay to unpause the request.
+ if (should_pause)
+ StartPauseTimer();
+
+ if (is_paused_ != should_pause) {
+ rdh_->PauseRequest(global_id_.render_process_host_id,
+ global_id_.request_id,
+ should_pause);
+ is_paused_ = should_pause;
+ }
+}
+
+void DownloadResourceHandler::StartPauseTimer() {
+ if (!pause_timer_.IsRunning())
+ pause_timer_.Start(base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this,
+ &DownloadResourceHandler::CheckWriteProgress);
+}
diff --git a/chrome/browser/renderer_host/download_resource_handler.h b/chrome/browser/renderer_host/download_resource_handler.h
new file mode 100644
index 0000000..52f6c94
--- /dev/null
+++ b/chrome/browser/renderer_host/download_resource_handler.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_RESOURCE_HANDLER_H_
+
+#include <string>
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+struct DownloadBuffer;
+
+// Forwards data to the download thread.
+class DownloadResourceHandler : public ResourceHandler {
+ public:
+ DownloadResourceHandler(ResourceDispatcherHost* rdh,
+ int render_process_host_id,
+ int render_view_id,
+ int request_id,
+ const std::string& url,
+ DownloadFileManager* manager,
+ URLRequest* request,
+ bool save_as);
+
+ // Not needed, as this event handler ought to be the final resource.
+ bool OnRequestRedirected(int request_id, const GURL& url);
+
+ // Send the download creation information to the download thread.
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+
+ // Create a new buffer, which will be handed to the download thread for file
+ // writing and deletion.
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+
+ bool OnReadCompleted(int request_id, int* bytes_read);
+
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ // If the content-length header is not present (or contains something other
+ // than numbers), the incoming content_length is -1 (unknown size).
+ // Set the content length to 0 to indicate unknown size to DownloadManager.
+ void set_content_length(const int64& content_length);
+
+ void set_content_disposition(const std::string& content_disposition);
+
+ void CheckWriteProgress();
+
+ private:
+ void StartPauseTimer();
+
+ int download_id_;
+ ResourceDispatcherHost::GlobalRequestID global_id_;
+ int render_view_id_;
+ char* read_buffer_;
+ std::string content_disposition_;
+ std::wstring url_;
+ int64 content_length_;
+ DownloadFileManager* download_manager_;
+ URLRequest* request_;
+ bool save_as_; // Request was initiated via "Save As" by the user.
+ DownloadBuffer* buffer_;
+ ResourceDispatcherHost* rdh_;
+ bool is_paused_;
+ base::OneShotTimer<DownloadResourceHandler> pause_timer_;
+
+ static const int kReadBufSize = 32768; // bytes
+ static const int kLoadsToWrite = 100; // number of data buffers queued
+ static const int kThrottleTimeMs = 200; // milliseconds
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.cc b/chrome/browser/renderer_host/download_throttling_resource_handler.cc
new file mode 100644
index 0000000..07b8164
--- /dev/null
+++ b/chrome/browser/renderer_host/download_throttling_resource_handler.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2006-2008 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/download_throttling_resource_handler.h"
+
+#include "chrome/browser/renderer_host/download_resource_handler.h"
+
+DownloadThrottlingResourceHandler::DownloadThrottlingResourceHandler(
+ ResourceDispatcherHost* host,
+ URLRequest* request,
+ const std::string& url,
+ int render_process_host_id,
+ int render_view_id,
+ int request_id,
+ bool in_complete)
+ : host_(host),
+ request_(request),
+ url_(url),
+ render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ request_id_(request_id),
+ tmp_buffer_length_(0),
+ ignore_on_read_complete_(in_complete) {
+ // Pause the request.
+ host_->PauseRequest(render_process_host_id_, request_id_, true);
+ host_->download_request_manager()->CanDownloadOnIOThread(
+ render_process_host_id_, render_view_id, this);
+ }
+
+DownloadThrottlingResourceHandler::~DownloadThrottlingResourceHandler() {
+}
+
+bool DownloadThrottlingResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ if (download_handler_.get())
+ return download_handler_->OnUploadProgress(request_id, position, size);
+ return true;
+}
+
+bool DownloadThrottlingResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& url) {
+ if (download_handler_.get())
+ return download_handler_->OnRequestRedirected(request_id, url);
+ url_ = url.spec();
+ return true;
+}
+
+bool DownloadThrottlingResourceHandler::OnResponseStarted(
+ int request_id,
+ ResourceResponse* response) {
+ if (download_handler_.get())
+ return download_handler_->OnResponseStarted(request_id, response);
+ response_ = response;
+ return true;
+}
+
+bool DownloadThrottlingResourceHandler::OnWillRead(int request_id,
+ char** buf,
+ int* buf_size,
+ int min_size) {
+ if (download_handler_.get())
+ return download_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+
+ // We should only have this invoked once, as such we only deal with one
+ // tmp buffer.
+ DCHECK(!tmp_buffer_.get());
+ if (min_size < 0)
+ min_size = 1024;
+ tmp_buffer_.reset(new char[min_size]);
+ *buf = tmp_buffer_.get();
+ *buf_size = min_size;
+ return true;
+}
+
+bool DownloadThrottlingResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (ignore_on_read_complete_) {
+ // See comments above definition for details on this.
+ ignore_on_read_complete_ = false;
+ return true;
+ }
+ if (!*bytes_read)
+ return true;
+
+ if (tmp_buffer_.get()) {
+ DCHECK(!tmp_buffer_length_);
+ tmp_buffer_length_ = *bytes_read;
+ if (download_handler_.get())
+ CopyTmpBufferToDownloadHandler();
+ return true;
+ }
+ if (download_handler_.get())
+ return download_handler_->OnReadCompleted(request_id, bytes_read);
+ return true;
+}
+
+bool DownloadThrottlingResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status) {
+ if (download_handler_.get())
+ return download_handler_->OnResponseCompleted(request_id, status);
+ NOTREACHED();
+ return true;
+}
+
+void DownloadThrottlingResourceHandler::CancelDownload() {
+ host_->CancelRequest(render_process_host_id_, request_id_, false);
+}
+
+void DownloadThrottlingResourceHandler::ContinueDownload() {
+ DCHECK(!download_handler_.get());
+ download_handler_ =
+ new DownloadResourceHandler(host_,
+ render_process_host_id_,
+ render_view_id_,
+ request_id_,
+ url_,
+ host_->download_file_manager(),
+ request_,
+ false);
+ if (response_.get())
+ download_handler_->OnResponseStarted(request_id_, response_.get());
+
+ if (tmp_buffer_length_)
+ CopyTmpBufferToDownloadHandler();
+
+ // And let the request continue.
+ host_->PauseRequest(render_process_host_id_, request_id_, false);
+}
+
+void DownloadThrottlingResourceHandler::CopyTmpBufferToDownloadHandler() {
+ // Copy over the tmp buffer.
+ char* buffer;
+ int buf_size;
+ if (download_handler_->OnWillRead(request_id_, &buffer, &buf_size,
+ tmp_buffer_length_)) {
+ CHECK(buf_size >= tmp_buffer_length_);
+ memcpy(buffer, tmp_buffer_.get(), tmp_buffer_length_);
+ download_handler_->OnReadCompleted(request_id_, &tmp_buffer_length_);
+ }
+ tmp_buffer_length_ = 0;
+ tmp_buffer_.reset();
+}
diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.h b/chrome/browser/renderer_host/download_throttling_resource_handler.h
new file mode 100644
index 0000000..177f4fd
--- /dev/null
+++ b/chrome/browser/renderer_host/download_throttling_resource_handler.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_THROTTLING_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_THROTTLING_RESOURCE_HANDLER_H_
+
+#include <string>
+
+#include "chrome/browser/renderer_host/resource_handler.h"
+#include "chrome/browser/download/download_request_manager.h"
+
+class DownloadResourceHandler;
+class ResourceDispatcherHost;
+
+// DownloadThrottlingResourceHandler is used to determine if a download should
+// be allowed. When a DownloadThrottlingResourceHandler is created it pauses the
+// download and asks the DownloadRequestManager if the download should be
+// allowed. The DownloadRequestManager notifies us asynchronously as to whether
+// the download is allowed or not. If the download is allowed the request is
+// resumed, a DownloadResourceHandler is created and all EventHandler methods
+// are delegated to it. If the download is not allowed the request is canceled.
+
+class DownloadThrottlingResourceHandler
+ : public ResourceHandler,
+ public DownloadRequestManager::Callback {
+ public:
+ DownloadThrottlingResourceHandler(ResourceDispatcherHost* host,
+ URLRequest* request,
+ const std::string& url,
+ int render_process_host_id,
+ int render_view_id,
+ int request_id,
+ bool in_complete);
+ virtual ~DownloadThrottlingResourceHandler();
+
+ // ResourceHanlder implementation:
+ virtual bool OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& url);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillRead(int request_id,
+ char** buf,
+ int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const URLRequestStatus& status);
+
+ // DownloadRequestManager::Callback implementation:
+ void CancelDownload();
+ void ContinueDownload();
+
+ private:
+ void CopyTmpBufferToDownloadHandler();
+
+ ResourceDispatcherHost* host_;
+ URLRequest* request_;
+ std::string url_;
+ int render_process_host_id_;
+ int render_view_id_;
+ int request_id_;
+
+ // Handles the actual download. This is only created if the download is
+ // allowed to continue.
+ scoped_refptr<DownloadResourceHandler> download_handler_;
+
+ // Response supplied to OnResponseStarted. Only non-null if OnResponseStarted
+ // is invoked.
+ scoped_refptr<ResourceResponse> response_;
+
+ // If we're created by way of BufferedEventHandler we'll get one request for
+ // a buffer. This is that buffer.
+ scoped_array<char> tmp_buffer_;
+ int tmp_buffer_length_;
+
+ // If true the next call to OnReadCompleted is ignored. This is used if we're
+ // paused during a call to OnReadCompleted. Pausing during OnReadCompleted
+ // results in two calls to OnReadCompleted for the same data. This make sure
+ // we ignore one of them.
+ bool ignore_on_read_complete_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadThrottlingResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_DOWNLOAD_THROTTLING_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc
new file mode 100644
index 0000000..f746429
--- /dev/null
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc
@@ -0,0 +1,1217 @@
+// Copyright (c) 2006-2008 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.
+
+// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
+
+#include <vector>
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/cross_site_request_manager.h"
+#include "chrome/browser/download/download_file.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/download/download_request_manager.h"
+#include "chrome/browser/download/save_file_manager.h"
+#include "chrome/browser/external_protocol_handler.h"
+#include "chrome/browser/login_prompt.h"
+#include "chrome/browser/plugin_service.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/render_view_host_delegate.h"
+#include "chrome/browser/renderer_host/async_resource_handler.h"
+#include "chrome/browser/renderer_host/buffered_resource_handler.h"
+#include "chrome/browser/renderer_host/cross_site_resource_handler.h"
+#include "chrome/browser/renderer_host/download_resource_handler.h"
+#include "chrome/browser/renderer_host/safe_browsing_resource_handler.h"
+#include "chrome/browser/renderer_host/save_file_resource_handler.h"
+#include "chrome/browser/renderer_host/sync_resource_handler.h"
+#include "chrome/browser/renderer_security_policy.h"
+#include "chrome/browser/resource_request_details.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/tab_util.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_types.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/stl_util-inl.h"
+#include "net/base/auth.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/load_flags.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+
+// Uncomment to enable logging of request traffic.
+//#define LOG_RESOURCE_DISPATCHER_REQUESTS
+
+#ifdef LOG_RESOURCE_DISPATCHER_REQUESTS
+# define RESOURCE_LOG(stuff) LOG(INFO) << stuff
+#else
+# define RESOURCE_LOG(stuff)
+#endif
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+// ----------------------------------------------------------------------------
+
+// The interval for calls to ResourceDispatcherHost::UpdateLoadStates
+static const int kUpdateLoadStatesIntervalMsec = 100;
+
+// Maximum number of pending data messages sent to the renderer at any
+// given time for a given request.
+static const int kMaxPendingDataMessages = 20;
+
+// A ShutdownTask proxies a shutdown task from the UI thread to the IO thread.
+// It should be constructed on the UI thread and run in the IO thread.
+class ResourceDispatcherHost::ShutdownTask : public Task {
+ public:
+ explicit ShutdownTask(ResourceDispatcherHost* resource_dispatcher_host)
+ : rdh_(resource_dispatcher_host) { }
+
+ void Run() {
+ rdh_->OnShutdown();
+ }
+
+ private:
+ ResourceDispatcherHost* rdh_;
+};
+
+namespace {
+
+// Consults the RendererSecurity policy to determine whether the
+// ResourceDispatcherHost should service this request. A request might be
+// disallowed if the renderer is not authorized to restrive the request URL or
+// if the renderer is attempting to upload an unauthorized file.
+bool ShouldServiceRequest(int render_process_host_id,
+ const ViewHostMsg_Resource_Request& request_data) {
+ // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed.
+ if (render_process_host_id == -1)
+ return true;
+
+ RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance();
+
+ // Check if the renderer is permitted to request the requested URL.
+ if (!policy->CanRequestURL(render_process_host_id, request_data.url)) {
+ LOG(INFO) << "Denied unauthorized request for " <<
+ request_data.url.possibly_invalid_spec();
+ return false;
+ }
+
+ // Check if the renderer is permitted to upload the requested files.
+ const std::vector<net::UploadData::Element>& uploads =
+ request_data.upload_content;
+ std::vector<net::UploadData::Element>::const_iterator iter;
+ for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
+ if (iter->type() == net::UploadData::TYPE_FILE &&
+ !policy->CanUploadFile(render_process_host_id, iter->file_path())) {
+ NOTREACHED() << "Denied unauthorized upload of " << iter->file_path();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop)
+ : ui_loop_(MessageLoop::current()),
+ io_loop_(io_loop),
+ download_file_manager_(new DownloadFileManager(ui_loop_, this)),
+ download_request_manager_(new DownloadRequestManager(io_loop, ui_loop_)),
+ save_file_manager_(new SaveFileManager(ui_loop_, io_loop, this)),
+ safe_browsing_(new SafeBrowsingService),
+ request_id_(-1),
+ plugin_service_(PluginService::GetInstance()),
+ method_runner_(this),
+ is_shutdown_(false) {
+}
+
+ResourceDispatcherHost::~ResourceDispatcherHost() {
+ AsyncResourceHandler::GlobalCleanup();
+ STLDeleteValues(&pending_requests_);
+}
+
+void ResourceDispatcherHost::Initialize() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ download_file_manager_->Initialize();
+ safe_browsing_->Initialize(io_loop_);
+}
+
+void ResourceDispatcherHost::Shutdown() {
+ DCHECK(MessageLoop::current() == ui_loop_);
+ io_loop_->PostTask(FROM_HERE, new ShutdownTask(this));
+}
+
+void ResourceDispatcherHost::OnShutdown() {
+ DCHECK(MessageLoop::current() == io_loop_);
+ is_shutdown_ = true;
+ STLDeleteValues(&pending_requests_);
+ // Make sure we shutdown the timer now, otherwise by the time our destructor
+ // runs if the timer is still running the Task is deleted twice (once by
+ // the MessageLoop and the second time by RepeatingTimer).
+ update_load_states_timer_.Stop();
+}
+
+bool ResourceDispatcherHost::HandleExternalProtocol(int request_id,
+ int render_process_host_id,
+ int tab_contents_id,
+ const GURL& url,
+ ResourceType::Type type,
+ ResourceHandler* handler) {
+ if (!ResourceType::IsFrame(type) || URLRequest::IsHandledURL(url))
+ return false;
+
+ ui_loop_->PostTask(FROM_HERE, NewRunnableFunction(
+ &ExternalProtocolHandler::LaunchUrl, url, render_process_host_id,
+ tab_contents_id));
+
+ handler->OnResponseCompleted(request_id, URLRequestStatus(
+ URLRequestStatus::FAILED,
+ net::ERR_ABORTED));
+ return true;
+}
+
+void ResourceDispatcherHost::BeginRequest(
+ Receiver* receiver,
+ HANDLE render_process_handle,
+ int render_process_host_id,
+ int render_view_id,
+ int request_id,
+ const ViewHostMsg_Resource_Request& request_data,
+ URLRequestContext* request_context,
+ IPC::Message* sync_result) {
+ if (is_shutdown_ ||
+ !ShouldServiceRequest(render_process_host_id, request_data)) {
+ // Tell the renderer that this request was disallowed.
+ receiver->Send(new ViewMsg_Resource_RequestComplete(
+ render_view_id,
+ request_id,
+ URLRequestStatus(URLRequestStatus::FAILED, net::ERR_ABORTED)));
+ return;
+ }
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ // TODO(mpcomplete): This takes 200 ms! Investigate parallelizing this by
+ // starting the load earlier in a BG thread.
+ plugin_service_->LoadChromePlugins(this);
+
+ // Construct the event handler.
+ scoped_refptr<ResourceHandler> handler;
+ if (sync_result) {
+ handler = new SyncResourceHandler(receiver, request_data.url, sync_result);
+ } else {
+ handler = new AsyncResourceHandler(receiver,
+ render_process_host_id,
+ render_view_id,
+ render_process_handle,
+ request_data.url,
+ this);
+ }
+
+ if (HandleExternalProtocol(request_id, render_process_host_id, render_view_id,
+ request_data.url, request_data.resource_type,
+ handler)) {
+ return;
+ }
+
+ // Construct the request.
+ URLRequest* request = new URLRequest(request_data.url, this);
+ request->set_method(request_data.method);
+ request->set_policy_url(request_data.policy_url);
+ request->set_referrer(request_data.referrer.spec());
+ request->SetExtraRequestHeaders(request_data.headers);
+ request->set_load_flags(request_data.load_flags);
+ request->set_context(request_context);
+ request->set_origin_pid(request_data.origin_pid);
+
+ // Set upload data.
+ uint64 upload_size = 0;
+ if (!request_data.upload_content.empty()) {
+ scoped_refptr<net::UploadData> upload = new net::UploadData();
+ upload->set_elements(request_data.upload_content); // Deep copy.
+ request->set_upload(upload);
+ upload_size = upload->GetContentLength();
+ }
+
+ // Install a CrossSiteResourceHandler if this request is coming from a
+ // RenderViewHost with a pending cross-site request. We only check this for
+ // MAIN_FRAME requests.
+ // TODO(mpcomplete): remove "render_process_host_id != -1"
+ // when http://b/viewIssue?id=1080959 is fixed.
+ if (request_data.resource_type == ResourceType::MAIN_FRAME &&
+ render_process_host_id != -1 &&
+ Singleton<CrossSiteRequestManager>::get()->
+ HasPendingCrossSiteRequest(render_process_host_id, render_view_id)) {
+ // Wrap the event handler to be sure the current page's onunload handler
+ // has a chance to run before we render the new page.
+ handler = new CrossSiteResourceHandler(handler,
+ render_process_host_id,
+ render_view_id,
+ this);
+ }
+
+ if (safe_browsing_->enabled() &&
+ safe_browsing_->CanCheckUrl(request_data.url)) {
+ handler = new SafeBrowsingResourceHandler(handler,
+ render_process_host_id,
+ render_view_id,
+ request_data.url,
+ request_data.resource_type,
+ safe_browsing_,
+ this);
+ }
+
+ // Insert a buffered event handler before the actual one.
+ handler = new BufferedResourceHandler(handler, this, request);
+
+ // Make extra info and read footer (contains request ID).
+ ExtraRequestInfo* extra_info =
+ new ExtraRequestInfo(handler,
+ request_id,
+ render_process_host_id,
+ render_view_id,
+ request_data.mixed_content,
+ request_data.resource_type,
+ upload_size);
+ extra_info->allow_download =
+ ResourceType::IsFrame(request_data.resource_type);
+ request->set_user_data(extra_info); // takes pointer ownership
+
+ BeginRequestInternal(request, request_data.mixed_content);
+}
+
+// We are explicitly forcing the download of 'url'.
+void ResourceDispatcherHost::BeginDownload(const GURL& url,
+ const GURL& referrer,
+ int render_process_host_id,
+ int render_view_id,
+ URLRequestContext* request_context) {
+ if (is_shutdown_)
+ return;
+
+ // Check if the renderer is permitted to request the requested URL.
+ //
+ // TODO(mpcomplete): remove "render_process_host_id != -1"
+ // when http://b/viewIssue?id=1080959 is fixed.
+ if (render_process_host_id != -1 &&
+ !RendererSecurityPolicy::GetInstance()->
+ CanRequestURL(render_process_host_id, url)) {
+ LOG(INFO) << "Denied unauthorized download request for " <<
+ url.possibly_invalid_spec();
+ return;
+ }
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ plugin_service_->LoadChromePlugins(this);
+ URLRequest* request = new URLRequest(url, this);
+
+ request_id_--;
+
+ scoped_refptr<ResourceHandler> handler =
+ new DownloadResourceHandler(this,
+ render_process_host_id,
+ render_view_id,
+ request_id_,
+ url.spec(),
+ download_file_manager_.get(),
+ request,
+ true);
+
+
+ if (safe_browsing_->enabled() && safe_browsing_->CanCheckUrl(url)) {
+ handler = new SafeBrowsingResourceHandler(handler,
+ render_process_host_id,
+ render_view_id,
+ url,
+ ResourceType::MAIN_FRAME,
+ safe_browsing_,
+ this);
+ }
+
+ bool known_proto = URLRequest::IsHandledURL(url);
+ if (!known_proto) {
+ CHECK(false);
+ }
+
+ request->set_method("GET");
+ request->set_referrer(referrer.spec());
+ request->set_context(request_context);
+
+ ExtraRequestInfo* extra_info =
+ new ExtraRequestInfo(handler,
+ request_id_,
+ render_process_host_id,
+ render_view_id,
+ false, // Downloads are not considered mixed-content
+ ResourceType::SUB_RESOURCE,
+ 0 /* upload_size */ );
+ extra_info->allow_download = true;
+ extra_info->is_download = true;
+ request->set_user_data(extra_info); // Takes pointer ownership.
+
+ BeginRequestInternal(request, false);
+}
+
+// This function is only used for saving feature.
+void ResourceDispatcherHost::BeginSaveFile(const GURL& url,
+ const GURL& referrer,
+ int render_process_host_id,
+ int render_view_id,
+ URLRequestContext* request_context) {
+ if (is_shutdown_)
+ return;
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ plugin_service_->LoadChromePlugins(this);
+
+ scoped_refptr<ResourceHandler> handler =
+ new SaveFileResourceHandler(render_process_host_id,
+ render_view_id,
+ url.spec(),
+ save_file_manager_.get());
+ request_id_--;
+
+ bool known_proto = URLRequest::IsHandledURL(url);
+ if (!known_proto) {
+ // Since any URLs which have non-standard scheme have been filtered
+ // by save manager(see GURL::SchemeIsStandard). This situation
+ // should not happen.
+ NOTREACHED();
+ return;
+ }
+
+ URLRequest* request = new URLRequest(url, this);
+ request->set_method("GET");
+ request->set_referrer(referrer.spec());
+ // So far, for saving page, we need fetch content from cache, in the
+ // future, maybe we can use a configuration to configure this behavior.
+ request->set_load_flags(net::LOAD_ONLY_FROM_CACHE);
+ request->set_context(request_context);
+
+ ExtraRequestInfo* extra_info =
+ new ExtraRequestInfo(handler,
+ request_id_,
+ render_process_host_id,
+ render_view_id,
+ false,
+ ResourceType::SUB_RESOURCE,
+ 0 /* upload_size */);
+ // Just saving some resources we need, disallow downloading.
+ extra_info->allow_download = false;
+ extra_info->is_download = false;
+ request->set_user_data(extra_info); // Takes pointer ownership.
+
+ BeginRequestInternal(request, false);
+}
+
+void ResourceDispatcherHost::CancelRequest(int render_process_host_id,
+ int request_id,
+ bool from_renderer) {
+ CancelRequest(render_process_host_id, request_id, from_renderer, true);
+}
+
+void ResourceDispatcherHost::CancelRequest(int render_process_host_id,
+ int request_id,
+ bool from_renderer,
+ bool allow_delete) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(render_process_host_id, request_id));
+ if (i == pending_requests_.end()) {
+ // We probably want to remove this warning eventually, but I wanted to be
+ // able to notice when this happens during initial development since it
+ // should be rare and may indicate a bug.
+ DLOG(WARNING) << "Canceling a request that wasn't found";
+ return;
+ }
+
+ // WebKit will send us a cancel for downloads since it no longer handles them.
+ // In this case, ignore the cancel since we handle downloads in the browser.
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+ if (!from_renderer || !info->is_download) {
+ if (info->login_handler) {
+ info->login_handler->OnRequestCancelled();
+ info->login_handler = NULL;
+ }
+ if (!i->second->is_pending() && allow_delete) {
+ // No io is pending, canceling the request won't notify us of anything,
+ // so we explicitly remove it.
+ // TODO: removing the request in this manner means we're not notifying
+ // anyone. We need make sure the event handlers and others are notified
+ // so that everything is cleaned up properly.
+ RemovePendingRequest(info->render_process_host_id, info->request_id);
+ } else {
+ i->second->Cancel();
+ }
+ }
+
+ // Do not remove from the pending requests, as the request will still
+ // call AllDataReceived, and may even have more data before it does
+ // that.
+}
+
+void ResourceDispatcherHost::OnDataReceivedACK(int render_process_host_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(render_process_host_id, request_id));
+ if (i == pending_requests_.end())
+ return;
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+
+ // Decrement the number of pending data messages.
+ info->pending_data_count--;
+
+ // If the pending data count was higher than the max, resume the request.
+ if (info->pending_data_count == kMaxPendingDataMessages) {
+ // Decrement the pending data count one more time because we also
+ // incremented it before pausing the request.
+ info->pending_data_count--;
+
+ // Resume the request.
+ PauseRequest(render_process_host_id, request_id, false);
+ }
+}
+
+void ResourceDispatcherHost::OnUploadProgressACK(int render_process_host_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(render_process_host_id, request_id));
+ if (i == pending_requests_.end())
+ return;
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+ info->waiting_for_upload_progress_ack = false;
+}
+
+bool ResourceDispatcherHost::WillSendData(int render_process_host_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(render_process_host_id, request_id));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << L"WillSendData for invalid request";
+ return false;
+ }
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+
+ info->pending_data_count++;
+ if (info->pending_data_count > kMaxPendingDataMessages) {
+ // We reached the max number of data messages that can be sent to
+ // the renderer for a given request. Pause the request and wait for
+ // the renderer to start processing them before resuming it.
+ PauseRequest(render_process_host_id, request_id, true);
+ return false;
+ }
+
+ return true;
+}
+
+void ResourceDispatcherHost::PauseRequest(int render_process_host_id,
+ int request_id,
+ bool pause) {
+ GlobalRequestID global_id(render_process_host_id, request_id);
+ PendingRequestList::iterator i = pending_requests_.find(global_id);
+ if (i == pending_requests_.end()) {
+ DLOG(WARNING) << "Pausing a request that wasn't found";
+ return;
+ }
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+
+ int pause_count = info->pause_count + (pause ? 1 : -1);
+ if (pause_count < 0) {
+ NOTREACHED(); // Unbalanced call to pause.
+ return;
+ }
+ info->pause_count = pause_count;
+
+ RESOURCE_LOG("To pause (" << pause << "): " << i->second->url().spec());
+
+ // If we're resuming, kick the request to start reading again. Run the read
+ // asynchronously to avoid recursion problems.
+ if (info->pause_count == 0) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_runner_.NewRunnableMethod(
+ &ResourceDispatcherHost::ResumeRequest, global_id));
+ }
+}
+
+void ResourceDispatcherHost::OnClosePageACK(int render_process_host_id,
+ int request_id) {
+ GlobalRequestID global_id(render_process_host_id, request_id);
+ PendingRequestList::iterator i = pending_requests_.find(global_id);
+ if (i == pending_requests_.end()) {
+ // If there are no matching pending requests, then this is not a
+ // cross-site navigation and we are just closing the tab/browser.
+ ui_loop_->PostTask(FROM_HERE, NewRunnableFunction(
+ &RenderViewHost::ClosePageIgnoringUnloadEvents,
+ render_process_host_id,
+ request_id));
+ return;
+ }
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+ if (info->cross_site_handler) {
+ info->cross_site_handler->ResumeResponse();
+ }
+}
+
+// The object died, so cancel and detach all requests associated with it except
+// for downloads, which belong to the browser process even if initiated via a
+// renderer.
+void ResourceDispatcherHost::CancelRequestsForProcess(
+ int render_process_host_id) {
+ CancelRequestsForRenderView(render_process_host_id, -1 /* cancel all */);
+}
+
+void ResourceDispatcherHost::CancelRequestsForRenderView(
+ int render_process_host_id,
+ int render_view_id) {
+ // Since pending_requests_ is a map, we first build up a list of all of the
+ // matching requests to be cancelled, and then we cancel them. Since there
+ // may be more than one request to cancel, we cannot simply hold onto the map
+ // iterators found in the first loop.
+
+ // Find the global ID of all matching elements.
+ std::vector<GlobalRequestID> matching_requests;
+ for (PendingRequestList::const_iterator i = pending_requests_.begin();
+ i != pending_requests_.end(); ++i) {
+ if (i->first.render_process_host_id == render_process_host_id) {
+ ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
+ if (!info->is_download && (render_view_id == -1 ||
+ render_view_id == info->render_view_id)) {
+ matching_requests.push_back(
+ GlobalRequestID(render_process_host_id, i->first.request_id));
+ }
+ }
+ }
+
+ // Remove matches.
+ for (size_t i = 0; i < matching_requests.size(); ++i) {
+ PendingRequestList::iterator iter =
+ pending_requests_.find(matching_requests[i]);
+ // Although every matching request was in pending_requests_ when we built
+ // matching_requests, it is normal for a matching request to be not found
+ // in pending_requests_ after we have removed some matching requests from
+ // pending_requests_. For example, deleting a URLRequest that has
+ // exclusive (write) access to an HTTP cache entry may unblock another
+ // URLRequest that needs exclusive access to the same cache entry, and
+ // that URLRequest may complete and remove itself from pending_requests_.
+ // So we need to check that iter is not equal to pending_requests_.end().
+ if (iter != pending_requests_.end())
+ RemovePendingRequest(iter);
+ }
+}
+
+// Cancels the request and removes it from the list.
+void ResourceDispatcherHost::RemovePendingRequest(int render_process_host_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(render_process_host_id, request_id));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Trying to remove a request that's not here";
+ return;
+ }
+ RemovePendingRequest(i);
+}
+
+void ResourceDispatcherHost::RemovePendingRequest(
+ const PendingRequestList::iterator& iter) {
+ // Notify the login handler that this request object is going away.
+ ExtraRequestInfo* info = ExtraInfoForRequest(iter->second);
+ if (info && info->login_handler)
+ info->login_handler->OnRequestCancelled();
+
+ delete iter->second;
+ pending_requests_.erase(iter);
+
+ // If we have no more pending requests, then stop the load state monitor
+ if (pending_requests_.empty())
+ update_load_states_timer_.Stop();
+}
+
+// URLRequest::Delegate -------------------------------------------------------
+
+void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request,
+ const GURL& new_url) {
+ RESOURCE_LOG("OnReceivedRedirect: " << request->url().spec());
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+
+ DCHECK(request->status().is_success());
+
+ // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed.
+ if (info->render_process_host_id != -1 &&
+ !RendererSecurityPolicy::GetInstance()->
+ CanRequestURL(info->render_process_host_id, new_url)) {
+ LOG(INFO) << "Denied unauthorized request for " <<
+ new_url.possibly_invalid_spec();
+
+ // Tell the renderer that this request was disallowed.
+ CancelRequest(info->render_process_host_id, info->request_id, false);
+ return;
+ }
+
+ NofityReceivedRedirect(request, info->render_process_host_id, new_url);
+
+ if (HandleExternalProtocol(info->request_id, info->render_process_host_id,
+ info->render_view_id, new_url,
+ info->resource_type, info->resource_handler)) {
+ // The request is complete so we can remove it.
+ RemovePendingRequest(info->render_process_host_id, info->request_id);
+ return;
+ }
+
+ if (!info->resource_handler->OnRequestRedirected(info->request_id, new_url))
+ CancelRequest(info->render_process_host_id, info->request_id, false);
+}
+
+void ResourceDispatcherHost::OnAuthRequired(
+ URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ // Create a login dialog on the UI thread to get authentication data,
+ // or pull from cache and continue on the IO thread.
+ // TODO(mpcomplete): We should block the parent tab while waiting for
+ // authentication.
+ // That would also solve the problem of the URLRequest being cancelled
+ // before we receive authentication.
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ DCHECK(!info->login_handler) <<
+ "OnAuthRequired called with login_handler pending";
+ info->login_handler = CreateLoginPrompt(auth_info, request, ui_loop_);
+}
+
+void ResourceDispatcherHost::OnSSLCertificateError(
+ URLRequest* request,
+ int cert_error,
+ net::X509Certificate* cert) {
+ DCHECK(request);
+ SSLManager::OnSSLCertificateError(this, request, cert_error, cert, ui_loop_);
+}
+
+void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) {
+ RESOURCE_LOG("OnResponseStarted: " << request->url().spec());
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ if (PauseRequestIfNeeded(info)) {
+ RESOURCE_LOG("OnResponseStarted pausing: " << request->url().spec());
+ return;
+ }
+
+ if (request->status().is_success()) {
+ // We want to send a final upload progress message prior to sending
+ // the response complete message even if we're waiting for an ack to
+ // to a previous upload progress message.
+ info->waiting_for_upload_progress_ack = false;
+ MaybeUpdateUploadProgress(info, request);
+
+ if (!CompleteResponseStarted(request)) {
+ CancelRequest(info->render_process_host_id, info->request_id, false);
+ } else {
+ // Start reading.
+ int bytes_read = 0;
+ if (Read(request, &bytes_read)) {
+ OnReadCompleted(request, bytes_read);
+ } else if (!request->status().is_io_pending()) {
+ DCHECK(!info->is_paused);
+ // If the error is not an IO pending, then we're done reading.
+ OnResponseCompleted(request);
+ }
+ }
+ } else {
+ OnResponseCompleted(request);
+ }
+}
+
+bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) {
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+
+ response->response_head.status = request->status();
+ response->response_head.request_time = request->request_time();
+ response->response_head.response_time = request->response_time();
+ response->response_head.headers = request->response_headers();
+ request->GetCharset(&response->response_head.charset);
+ response->response_head.filter_policy = info->filter_policy;
+ response->response_head.content_length = request->GetExpectedContentSize();
+ request->GetMimeType(&response->response_head.mime_type);
+
+ if (request->ssl_info().cert) {
+ int cert_id =
+ CertStore::GetSharedInstance()->StoreCert(
+ request->ssl_info().cert,
+ info->render_process_host_id);
+ int cert_status = request->ssl_info().cert_status;
+ // EV certificate verification could be expensive. We don't want to spend
+ // time performing EV certificate verification on all resources because
+ // EV status is irrelevant to sub-frames and sub-resources. So we call
+ // IsEV here rather than in the network layer because the network layer
+ // doesn't know the resource type.
+ if (info->resource_type == ResourceType::MAIN_FRAME &&
+ request->ssl_info().cert->IsEV(cert_status))
+ cert_status |= net::CERT_STATUS_IS_EV;
+
+ response->response_head.security_info =
+ SSLManager::SerializeSecurityInfo(cert_id,
+ cert_status,
+ request->ssl_info().security_bits);
+ } else {
+ // We should not have any SSL state.
+ DCHECK(!request->ssl_info().cert_status &&
+ (request->ssl_info().security_bits == -1 ||
+ request->ssl_info().security_bits == 0));
+ }
+
+ NotifyResponseStarted(request, info->render_process_host_id);
+ return info->resource_handler->OnResponseStarted(info->request_id,
+ response.get());
+}
+
+void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request,
+ bool mixed_content) {
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ GlobalRequestID global_id(info->render_process_host_id, info->request_id);
+ pending_requests_[global_id] = request;
+ if (mixed_content) {
+ // We don't start the request in that case. The SSLManager will potentially
+ // change the request (potentially to indicate its content should be
+ // filtered) and start it itself.
+ SSLManager::OnMixedContentRequest(this, request, ui_loop_);
+ return;
+ }
+ request->Start();
+
+ // Make sure we have the load state monitor running
+ if (!update_load_states_timer_.IsRunning()) {
+ update_load_states_timer_.Start(
+ TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec),
+ this, &ResourceDispatcherHost::UpdateLoadStates);
+ }
+}
+
+// This test mirrors the decision that WebKit makes in
+// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType.
+// static.
+bool ResourceDispatcherHost::ShouldDownload(
+ const std::string& mime_type, const std::string& content_disposition) {
+ std::string type = StringToLowerASCII(mime_type);
+ std::string disposition = StringToLowerASCII(content_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;
+ }
+
+ // MIME type checking.
+ if (net::IsSupportedMimeType(type))
+ return false;
+
+ // Finally, check the plugin service.
+ bool allow_wildcard = false;
+ return !plugin_service_->HavePluginFor(type, allow_wildcard);
+}
+
+bool ResourceDispatcherHost::PauseRequestIfNeeded(ExtraRequestInfo* info) {
+ if (info->pause_count > 0)
+ info->is_paused = true;
+
+ return info->is_paused;
+}
+
+void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(request_id);
+ if (i == pending_requests_.end()) // The request may have been destroyed
+ return;
+
+ URLRequest* request = i->second;
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ if (!info->is_paused)
+ return;
+
+ RESOURCE_LOG("Resuming: " << i->second->url().spec());
+
+ info->is_paused = false;
+
+ if (info->has_started_reading)
+ OnReadCompleted(i->second, info->paused_read_bytes);
+ else
+ OnResponseStarted(i->second);
+}
+
+bool ResourceDispatcherHost::Read(URLRequest* request, int* bytes_read) {
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ DCHECK(!info->is_paused);
+
+ char* buf;
+ int buf_size;
+ if (!info->resource_handler->OnWillRead(info->request_id,
+ &buf, &buf_size, -1)) {
+ return false;
+ }
+
+ DCHECK(buf);
+ DCHECK(buf_size > 0);
+
+ info->has_started_reading = true;
+ return request->Read(buf, buf_size, bytes_read);
+}
+
+void ResourceDispatcherHost::OnReadCompleted(URLRequest* request,
+ int bytes_read) {
+ DCHECK(request);
+ RESOURCE_LOG("OnReadCompleted: " << request->url().spec());
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+ if (PauseRequestIfNeeded(info)) {
+ info->paused_read_bytes = bytes_read;
+ RESOURCE_LOG("OnReadCompleted pausing: " << request->url().spec());
+ return;
+ }
+
+ if (request->status().is_success() && CompleteRead(request, &bytes_read)) {
+ // The request can be paused if we realize that the renderer is not
+ // servicing messages fast enough.
+ if (info->pause_count == 0 &&
+ Read(request, &bytes_read) &&
+ request->status().is_success()) {
+ if (bytes_read == 0) {
+ CompleteRead(request, &bytes_read);
+ } else {
+ // Force the next CompleteRead / Read pair to run as a separate task.
+ // This avoids a fast, large network request from monopolizing the IO
+ // thread and starving other IO operations from running.
+ info->paused_read_bytes = bytes_read;
+ info->is_paused = true;
+ GlobalRequestID id(info->render_process_host_id, info->request_id);
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_runner_.NewRunnableMethod(
+ &ResourceDispatcherHost::ResumeRequest, id));
+ return;
+ }
+ }
+ }
+
+ if (PauseRequestIfNeeded(info)) {
+ info->paused_read_bytes = bytes_read;
+ RESOURCE_LOG("OnReadCompleted (CompleteRead) pausing: " <<
+ request->url().spec());
+ return;
+ }
+
+ // If the status is not IO pending then we've either finished (success) or we
+ // had an error. Either way, we're done!
+ if (!request->status().is_io_pending())
+ OnResponseCompleted(request);
+}
+
+bool ResourceDispatcherHost::CompleteRead(URLRequest* request,
+ int* bytes_read) {
+ if (!request->status().is_success()) {
+ NOTREACHED();
+ return false;
+ }
+
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+
+ if (!info->resource_handler->OnReadCompleted(info->request_id, bytes_read)) {
+ // Pass in false as the last arg to indicate we don't want |request|
+ // deleted. We do this as callers of us assume |request| is valid after we
+ // return.
+ CancelRequest(info->render_process_host_id, info->request_id, false, false);
+ return false;
+ }
+
+ return *bytes_read != 0;
+}
+
+void ResourceDispatcherHost::OnResponseCompleted(URLRequest* request) {
+ RESOURCE_LOG("OnResponseCompleted: " << request->url().spec());
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+
+ if (info->resource_handler->OnResponseCompleted(info->request_id,
+ request->status())) {
+ NotifyResponseCompleted(request, info->render_process_host_id);
+
+ // The request is complete so we can remove it.
+ RemovePendingRequest(info->render_process_host_id, info->request_id);
+ }
+ // If the handler's OnResponseCompleted returns false, we are deferring the
+ // call until later. We will notify the world and clean up when we resume.
+}
+
+void ResourceDispatcherHost::AddObserver(Observer* obs) {
+ observer_list_.AddObserver(obs);
+}
+
+void ResourceDispatcherHost::RemoveObserver(Observer* obs) {
+ observer_list_.RemoveObserver(obs);
+}
+
+URLRequest* ResourceDispatcherHost::GetURLRequest(
+ GlobalRequestID request_id) const {
+ // This should be running in the IO loop. io_loop_ can be NULL during the
+ // unit_tests.
+ DCHECK(MessageLoop::current() == io_loop_ && io_loop_);
+
+ PendingRequestList::const_iterator i = pending_requests_.find(request_id);
+ if (i == pending_requests_.end())
+ return NULL;
+
+ return i->second;
+}
+
+// A NotificationTask proxies a resource dispatcher notification from the IO
+// thread to the UI thread. It should be constructed on the IO thread and run
+// in the UI thread. Takes ownership of |details|.
+class NotificationTask : public Task {
+ public:
+ NotificationTask(NotificationType type,
+ URLRequest* request,
+ ResourceRequestDetails* details)
+ : type_(type),
+ details_(details) {
+ if (!tab_util::GetTabContentsID(request,
+ &render_process_host_id_,
+ &tab_contents_id_))
+ NOTREACHED();
+ }
+
+ void Run() {
+ // Find the tab associated with this request.
+ TabContents* tab_contents =
+ tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_);
+
+ if (tab_contents) {
+ // Issue the notification.
+ NotificationService::current()->
+ Notify(type_,
+ Source<NavigationController>(tab_contents->controller()),
+ Details<ResourceRequestDetails>(details_.get()));
+ }
+ }
+
+ private:
+ // These IDs let us find the correct tab on the UI thread.
+ int render_process_host_id_;
+ int tab_contents_id_;
+
+ // The type and details of the notification.
+ NotificationType type_;
+ scoped_ptr<ResourceRequestDetails> details_;
+};
+
+static int GetCertID(URLRequest* request, int render_process_host_id) {
+ if (request->ssl_info().cert) {
+ return CertStore::GetSharedInstance()->StoreCert(request->ssl_info().cert,
+ render_process_host_id);
+ }
+ // If there is no SSL info attached to this request, we must either be a non
+ // secure request, or the request has been canceled or failed (before the SSL
+ // info was populated), or the response is an error (we have seen 403, 404,
+ // and 501) made up by the proxy.
+ DCHECK(!request->url().SchemeIsSecure() ||
+ (request->status().status() == URLRequestStatus::CANCELED) ||
+ (request->status().status() == URLRequestStatus::FAILED) ||
+ ((request->response_headers()->response_code() >= 400) &&
+ (request->response_headers()->response_code() <= 599)));
+ return 0;
+}
+
+void ResourceDispatcherHost::NotifyResponseStarted(URLRequest* request,
+ int render_process_host_id) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnRequestStarted(this, request));
+
+ // Notify the observers on the UI thread.
+ ui_loop_->PostTask(FROM_HERE,
+ new NotificationTask(NOTIFY_RESOURCE_RESPONSE_STARTED, request,
+ new ResourceRequestDetails(request,
+ GetCertID(request, render_process_host_id))));
+}
+
+void ResourceDispatcherHost::NotifyResponseCompleted(
+ URLRequest* request,
+ int render_process_host_id) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnResponseCompleted(this, request));
+
+ // Notify the observers on the UI thread.
+ ui_loop_->PostTask(FROM_HERE,
+ new NotificationTask(NOTIFY_RESOURCE_RESPONSE_COMPLETED, request,
+ new ResourceRequestDetails(request,
+ GetCertID(request, render_process_host_id))));
+}
+
+void ResourceDispatcherHost::NofityReceivedRedirect(URLRequest* request,
+ int render_process_host_id,
+ const GURL& new_url) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnReceivedRedirect(this, request, new_url));
+
+ int cert_id = GetCertID(request, render_process_host_id);
+
+ // Notify the observers on the UI thread.
+ ui_loop_->PostTask(FROM_HERE,
+ new NotificationTask(NOTIFY_RESOURCE_RECEIVED_REDIRECT, request,
+ new ResourceRedirectDetails(request,
+ cert_id,
+ new_url)));
+}
+
+namespace {
+
+// This function attempts to return the "more interesting" load state of |a|
+// and |b|. We don't have temporal information about these load states
+// (meaning we don't know when we transitioned into these states), so we just
+// rank them according to how "interesting" the states are.
+//
+// We take advantage of the fact that the load states are an enumeration listed
+// in the order in which they occur during the lifetime of a request, so we can
+// regard states with larger numeric values as being further along toward
+// completion. We regard those states as more interesting to report since they
+// represent progress.
+//
+// For example, by this measure "tranferring data" is a more interesting state
+// than "resolving host" because when we are transferring data we are actually
+// doing something that corresponds to changes that the user might observe,
+// whereas waiting for a host name to resolve implies being stuck.
+//
+net::LoadState MoreInterestingLoadState(net::LoadState a, net::LoadState b) {
+ return (a < b) ? b : a;
+}
+
+// Carries information about a load state change.
+struct LoadInfo {
+ GURL url;
+ net::LoadState load_state;
+};
+
+// Map from ProcessID+ViewID pair to LoadState
+typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap;
+
+// Used to marshall calls to LoadStateChanged from the IO to UI threads. We do
+// them all as a single task to avoid spamming the UI thread.
+class LoadInfoUpdateTask : public Task {
+ public:
+ virtual void Run() {
+ LoadInfoMap::const_iterator i;
+ for (i = info_map.begin(); i != info_map.end(); ++i) {
+ RenderViewHost* view =
+ RenderViewHost::FromID(i->first.first, i->first.second);
+ if (view) // The view could be gone at this point.
+ view->LoadStateChanged(i->second.url, i->second.load_state);
+ }
+ }
+ LoadInfoMap info_map;
+};
+
+} // namespace
+
+void ResourceDispatcherHost::UpdateLoadStates() {
+ // Populate this map with load state changes, and then send them on to the UI
+ // thread where they can be passed along to the respective RVHs.
+ LoadInfoMap info_map;
+
+ PendingRequestList::const_iterator i;
+ for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) {
+ URLRequest* request = i->second;
+ net::LoadState load_state = request->GetLoadState();
+ ExtraRequestInfo* info = ExtraInfoForRequest(request);
+
+ // We also poll for upload progress on this timer and send upload
+ // progress ipc messages to the plugin process.
+ MaybeUpdateUploadProgress(info, request);
+
+ if (info->last_load_state != load_state) {
+ info->last_load_state = load_state;
+
+ std::pair<int, int> key(info->render_process_host_id,
+ info->render_view_id);
+ net::LoadState to_insert;
+ LoadInfoMap::iterator existing = info_map.find(key);
+ if (existing == info_map.end()) {
+ to_insert = load_state;
+ } else {
+ to_insert =
+ MoreInterestingLoadState(existing->second.load_state, load_state);
+ if (to_insert == existing->second.load_state)
+ continue;
+ }
+ LoadInfo& load_info = info_map[key];
+ load_info.url = request->url();
+ load_info.load_state = to_insert;
+ }
+ }
+
+ if (info_map.empty())
+ return;
+
+ LoadInfoUpdateTask* task = new LoadInfoUpdateTask;
+ task->info_map.swap(info_map);
+ ui_loop_->PostTask(FROM_HERE, task);
+}
+
+void ResourceDispatcherHost::MaybeUpdateUploadProgress(ExtraRequestInfo *info,
+ URLRequest *request) {
+ if (!info->upload_size || info->waiting_for_upload_progress_ack ||
+ !(request->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS))
+ return;
+
+ uint64 size = info->upload_size;
+ uint64 position = request->GetUploadProgress();
+ if (position == info->last_upload_position)
+ return; // no progress made since last time
+
+ const uint64 kHalfPercentIncrements = 200;
+ const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000);
+
+ uint64 amt_since_last = position - info->last_upload_position;
+ TimeDelta time_since_last = TimeTicks::Now() - info->last_upload_ticks;
+
+ bool is_finished = (size == position);
+ bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements));
+ bool too_much_time_passed = time_since_last > kOneSecond;
+
+ if (is_finished || enough_new_progress || too_much_time_passed) {
+ info->resource_handler->OnUploadProgress(info->request_id, position, size);
+ info->waiting_for_upload_progress_ack = true;
+ info->last_upload_ticks = TimeTicks::Now();
+ info->last_upload_position = position;
+ }
+}
diff --git a/chrome/browser/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h
index fd6b5d2..f0b7f94 100644
--- a/chrome/browser/resource_dispatcher_host.h
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.h
@@ -9,8 +9,8 @@
//
// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
-#ifndef CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__
-#define CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__
+#ifndef CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
+#define CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
#include <map>
#include <string>
@@ -18,14 +18,14 @@
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/ref_counted.h"
-#include "base/shared_memory.h"
-#include "base/task.h"
#include "base/timer.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
#include "chrome/common/filter_policy.h"
#include "chrome/common/ipc_message.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/resource_type.h"
+class CrossSiteResourceHandler;
class DownloadFileManager;
class DownloadRequestManager;
class LoginHandler;
@@ -33,56 +33,11 @@ class MessageLoop;
class PluginService;
class SafeBrowsingService;
class SaveFileManager;
-class TabContents;
class URLRequestContext;
struct ViewHostMsg_Resource_Request;
-struct ViewMsg_Resource_ResponseHead;
class ResourceDispatcherHost : public URLRequest::Delegate {
public:
- // Simple wrapper that refcounts ViewMsg_Resource_ResponseHead.
- struct Response;
-
- // The resource dispatcher host uses this interface to push load events to the
- // renderer, allowing for differences in the types of IPC messages generated.
- // See the implementations of this interface defined below.
- class EventHandler : public base::RefCounted<EventHandler> {
- public:
- virtual ~EventHandler() {}
-
- // Called as upload progress is made.
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
- return true;
- }
-
- // The request was redirected to a new URL.
- virtual bool OnRequestRedirected(int request_id, const GURL& url) = 0;
-
- // Response headers and meta data are available.
- virtual bool OnResponseStarted(int request_id, Response* response) = 0;
-
- // Data will be read for the response. Upon success, this method places the
- // size and address of the buffer where the data is to be written in its
- // out-params. This call will be followed by either OnReadCompleted or
- // OnResponseCompleted, at which point the buffer may be recycled.
- virtual bool OnWillRead(int request_id,
- char** buf,
- int* buf_size,
- int min_size) = 0;
-
- // Data (*bytes_read bytes) was written into the buffer provided by
- // OnWillRead. A return value of false cancels the request, true continues
- // reading data.
- virtual bool OnReadCompleted(int request_id, int* bytes_read) = 0;
-
- // The response is complete. The final response status is given.
- // Returns false if the handler is deferring the call to a later time.
- virtual bool OnResponseCompleted(int request_id,
- const URLRequestStatus& status) = 0;
- };
-
// Implemented by the client of ResourceDispatcherHost to receive messages in
// response to a resource load. The messages are intended to be forwarded to
// the ResourceDispatcher in the renderer process via an IPC channel that the
@@ -97,22 +52,18 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
// renderer crashes and the channel dies).
typedef IPC::Message::Sender Receiver;
- // Forward declaration of CrossSiteEventHandler, so that it can be referenced
- // in ExtraRequestInfo.
- class CrossSiteEventHandler;
-
// Holds the data we would like to associate with each request
class ExtraRequestInfo : public URLRequest::UserData {
friend class ResourceDispatcherHost;
public:
- ExtraRequestInfo(EventHandler* handler,
+ ExtraRequestInfo(ResourceHandler* handler,
int request_id,
int render_process_host_id,
int render_view_id,
bool mixed_content,
ResourceType::Type resource_type,
uint64 upload_size)
- : event_handler(handler),
+ : resource_handler(handler),
cross_site_handler(NULL),
login_handler(NULL),
request_id(request_id),
@@ -133,13 +84,13 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
paused_read_bytes(0) {
}
- // Top-level EventHandler servicing this request.
- scoped_refptr<EventHandler> event_handler;
+ // Top-level ResourceHandler servicing this request.
+ scoped_refptr<ResourceHandler> resource_handler;
- // CrossSiteEventHandler for this request, if it is a cross-site request.
- // (NULL otherwise.) This handler is part of the chain of EventHandlers
- // pointed to by event_handler.
- CrossSiteEventHandler* cross_site_handler;
+ // CrossSiteResourceHandler for this request, if it is a cross-site request.
+ // (NULL otherwise.) This handler is part of the chain of ResourceHandlers
+ // pointed to by resource_handler.
+ CrossSiteResourceHandler* cross_site_handler;
LoginHandler* login_handler;
@@ -361,15 +312,18 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
// Retrieves a URLRequest. Must be called from the IO thread.
URLRequest* GetURLRequest(GlobalRequestID request_id) const;
+ // A test to determining whether a given request should be forwarded to the
+ // download thread.
+ bool ShouldDownload(const std::string& mime_type,
+ const std::string& content_disposition);
+
+ // Notify our observers that a request has been cancelled.
+ void NotifyResponseCompleted(URLRequest* request, int render_process_host_id);
+
+ void RemovePendingRequest(int render_process_host_id, int request_id);
+
private:
- class AsyncEventHandler;
- class BufferedEventHandler;
- class CrossSiteNotifyTabTask;
- class DownloadEventHandler;
- class DownloadThrottlingEventHandler;
- class SaveFileEventHandler;
class ShutdownTask;
- class SyncEventHandler;
friend class ShutdownTask;
@@ -416,12 +370,6 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
// out we have a lot of things here.
typedef std::map<GlobalRequestID,URLRequest*> PendingRequestList;
- // A test to determining whether a given request should be forwarded to the
- // download thread.
- bool ShouldDownload(const std::string& mime_type,
- const std::string& content_disposition);
-
- void RemovePendingRequest(int render_process_host_id, int request_id);
// Deletes the pending request identified by the iterator passed in.
// This function will invalidate the iterator passed in. Callers should
// not rely on this iterator being valid on return.
@@ -430,9 +378,6 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
// Notify our observers that we started receiving a response for a request.
void NotifyResponseStarted(URLRequest* request, int render_process_host_id);
- // Notify our observers that a request has been cancelled.
- void NotifyResponseCompleted(URLRequest* request, int render_process_host_id);
-
// Notify our observers that a request has been redirected.
void NofityReceivedRedirect(URLRequest* request,
int render_process_host_id,
@@ -445,7 +390,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
int tab_contents_id,
const GURL& url,
ResourceType::Type resource_type,
- EventHandler* handler);
+ ResourceHandler* handler);
void UpdateLoadStates();
@@ -495,7 +440,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
// True if the resource dispatcher host has been shut down.
bool is_shutdown_;
- DISALLOW_EVIL_CONSTRUCTORS(ResourceDispatcherHost);
+ DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost);
};
-#endif // CHROME_BROWSER_RESOURCE_DISPATCHER_HOST_H__
+#endif // CHROME_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
diff --git a/chrome/browser/resource_dispatcher_host_uitest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc
index 82735dc..6d276ee 100644
--- a/chrome/browser/resource_dispatcher_host_uitest.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc
@@ -127,7 +127,7 @@ TEST_F(ResourceDispatcherTest, SyncXMLHttpRequestDuringUnload) {
// (the bug would make this step hang the renderer).
bool timed_out = false;
tab->NavigateToURLWithTimeout(server.TestServerPageW(L"files/title2.html"),
- action_max_timeout_ms(),
+ kWaitForActionMaxMsec,
&timed_out);
EXPECT_FALSE(timed_out);
@@ -263,7 +263,7 @@ TEST_F(ResourceDispatcherTest, CrossSiteNavigationErrorPage) {
std::wstring redirect_url = L"javascript:window.location='" +
ASCIIToWide(test_url.possibly_invalid_spec()) + L"'";
tab->NavigateToURLAsync(GURL(redirect_url));
- Sleep(action_timeout_ms()); // Wait for JavaScript redirect to happen.
+ Sleep(kWaitForActionMsec); // Wait for JavaScript redirect to happen.
EXPECT_TRUE(tab->GetTabTitle(&tab_title));
EXPECT_EQ(L"Title Of Awesomeness", tab_title);
}
diff --git a/chrome/browser/resource_dispatcher_host_unittest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc
index 7002b41..8ae6e1a 100644
--- a/chrome/browser/resource_dispatcher_host_unittest.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host_unittest.cc
@@ -6,7 +6,7 @@
#include "base/message_loop.h"
#include "chrome/browser/renderer_security_policy.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/common/render_messages.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
diff --git a/chrome/browser/renderer_host/resource_handler.h b/chrome/browser/renderer_host/resource_handler.h
new file mode 100644
index 0000000..a6955c7
--- /dev/null
+++ b/chrome/browser/renderer_host/resource_handler.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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.
+
+// This is the browser side of the resource dispatcher, it receives requests
+// from the RenderProcessHosts, and dispatches them to URLRequests. It then
+// fowards the messages from the URLRequests back to the correct process for
+// handling.
+//
+// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "chrome/common/render_messages.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request.h"
+
+// Simple wrapper that refcounts ViewMsg_Resource_ResponseHead.
+struct ResourceResponse : public base::RefCounted<ResourceResponse> {
+ ViewMsg_Resource_ResponseHead response_head;
+};
+
+// The resource dispatcher host uses this interface to push load events to the
+// renderer, allowing for differences in the types of IPC messages generated.
+// See the implementations of this interface defined below.
+class ResourceHandler : public base::RefCounted<ResourceHandler> {
+ public:
+ virtual ~ResourceHandler() {}
+
+ // Called as upload progress is made.
+ virtual bool OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return true;
+ }
+
+ // The request was redirected to a new URL.
+ virtual bool OnRequestRedirected(int request_id, const GURL& url) = 0;
+
+ // Response headers and meta data are available.
+ virtual bool OnResponseStarted(int request_id,
+ ResourceResponse* response) = 0;
+
+ // Data will be read for the response. Upon success, this method places the
+ // size and address of the buffer where the data is to be written in its
+ // out-params. This call will be followed by either OnReadCompleted or
+ // OnResponseCompleted, at which point the buffer may be recycled.
+ virtual bool OnWillRead(int request_id,
+ char** buf,
+ int* buf_size,
+ int min_size) = 0;
+
+ // Data (*bytes_read bytes) was written into the buffer provided by
+ // OnWillRead. A return value of false cancels the request, true continues
+ // reading data.
+ virtual bool OnReadCompleted(int request_id, int* bytes_read) = 0;
+
+ // The response is complete. The final response status is given.
+ // Returns false if the handler is deferring the call to a later time.
+ virtual bool OnResponseCompleted(int request_id,
+ const URLRequestStatus& status) = 0;
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/safe_browsing_resource_handler.cc b/chrome/browser/renderer_host/safe_browsing_resource_handler.cc
new file mode 100644
index 0000000..d9e70a4
--- /dev/null
+++ b/chrome/browser/renderer_host/safe_browsing_resource_handler.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2006-2008 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/safe_browsing_resource_handler.h"
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+
+// Maximum time to wait for a gethash response from the Safe Browsing servers.
+static const int kMaxGetHashMs = 1000;
+
+SafeBrowsingResourceHandler::SafeBrowsingResourceHandler(
+ ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ const GURL& url,
+ ResourceType::Type resource_type,
+ SafeBrowsingService* safe_browsing,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : next_handler_(handler),
+ render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ paused_request_id_(-1),
+ safe_browsing_(safe_browsing),
+ in_safe_browsing_check_(false),
+ displaying_blocking_page_(false),
+ rdh_(resource_dispatcher_host),
+ queued_error_request_id_(-1),
+ resource_type_(resource_type) {
+ if (safe_browsing_->CheckUrl(url, this)) {
+ safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
+ safe_browsing_->LogPauseDelay(base::TimeDelta()); // No delay.
+ } else {
+ AddRef();
+ in_safe_browsing_check_ = true;
+ // Can't pause now because it's too early, so we'll do it in OnWillRead.
+ }
+}
+
+bool SafeBrowsingResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool SafeBrowsingResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url) {
+ if (in_safe_browsing_check_) {
+ Release();
+ in_safe_browsing_check_ = false;
+ safe_browsing_->CancelCheck(this);
+ }
+
+ if (safe_browsing_->CheckUrl(new_url, this)) {
+ safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
+ safe_browsing_->LogPauseDelay(base::TimeDelta()); // No delay.
+ } else {
+ AddRef();
+ in_safe_browsing_check_ = true;
+ // Can't pause now because it's too early, so we'll do it in OnWillRead.
+ }
+
+ return next_handler_->OnRequestRedirected(request_id, new_url);
+}
+
+bool SafeBrowsingResourceHandler::OnResponseStarted(
+ int request_id, ResourceResponse* response) {
+ return next_handler_->OnResponseStarted(request_id, response);
+}
+
+void SafeBrowsingResourceHandler::OnGetHashTimeout() {
+ if (!in_safe_browsing_check_)
+ return;
+
+ safe_browsing_->CancelCheck(this);
+ OnUrlCheckResult(GURL::EmptyGURL(), SafeBrowsingService::URL_SAFE);
+}
+
+bool SafeBrowsingResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size,
+ int min_size) {
+ if (in_safe_browsing_check_ && pause_time_.is_null()) {
+ pause_time_ = base::Time::Now();
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SafeBrowsingResourceHandler::OnGetHashTimeout),
+ kMaxGetHashMs);
+ }
+
+ if (in_safe_browsing_check_ || displaying_blocking_page_) {
+ rdh_->PauseRequest(render_process_host_id_, request_id, true);
+ paused_request_id_ = request_id;
+ }
+
+ return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+}
+
+bool SafeBrowsingResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ return next_handler_->OnReadCompleted(request_id, bytes_read);
+}
+
+bool SafeBrowsingResourceHandler::OnResponseCompleted(
+ int request_id, const URLRequestStatus& status) {
+ if ((in_safe_browsing_check_ ||
+ safe_browsing_result_ != SafeBrowsingService::URL_SAFE) &&
+ status.status() == URLRequestStatus::FAILED &&
+ status.os_error() == net::ERR_NAME_NOT_RESOLVED) {
+ // Got a DNS error while the safebrowsing check is in progress or we
+ // already know that the site is unsafe. Don't show the the dns error
+ // page.
+ queued_error_.reset(new URLRequestStatus(status));
+ queued_error_request_id_ = request_id;
+ return true;
+ }
+
+ return next_handler_->OnResponseCompleted(request_id, status);
+}
+
+// SafeBrowsingService::Client implementation, called on the IO thread once
+// the URL has been classified.
+void SafeBrowsingResourceHandler::OnUrlCheckResult(
+ const GURL& url, SafeBrowsingService::UrlCheckResult result) {
+ DCHECK(in_safe_browsing_check_);
+ DCHECK(!displaying_blocking_page_);
+
+ safe_browsing_result_ = result;
+ in_safe_browsing_check_ = false;
+
+ if (result == SafeBrowsingService::URL_SAFE) {
+ if (paused_request_id_ != -1) {
+ rdh_->PauseRequest(render_process_host_id_, paused_request_id_, false);
+ paused_request_id_ = -1;
+ }
+
+ base::TimeDelta pause_delta;
+ if (!pause_time_.is_null())
+ pause_delta = base::Time::Now() - pause_time_;
+ safe_browsing_->LogPauseDelay(pause_delta);
+
+ if (queued_error_.get()) {
+ next_handler_->OnResponseCompleted(
+ queued_error_request_id_, *queued_error_.get());
+ queued_error_.reset();
+ }
+
+ Release();
+ } else {
+ displaying_blocking_page_ = true;
+ safe_browsing_->DisplayBlockingPage(
+ url, resource_type_, result, this, rdh_->ui_loop(),
+ render_process_host_id_, render_view_id_);
+ }
+}
+
+// SafeBrowsingService::Client implementation, called on the IO thread when
+// the user has decided to proceed with the current request, or go back.
+void SafeBrowsingResourceHandler::OnBlockingPageComplete(bool proceed) {
+ DCHECK(displaying_blocking_page_);
+ displaying_blocking_page_ = false;
+
+ if (proceed) {
+ safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
+ if (paused_request_id_ != -1) {
+ rdh_->PauseRequest(render_process_host_id_, paused_request_id_, false);
+ paused_request_id_ = -1;
+ }
+
+ if (queued_error_.get()) {
+ next_handler_->OnResponseCompleted(
+ queued_error_request_id_, *queued_error_.get());
+ queued_error_.reset();
+ }
+ } else {
+ rdh_->CancelRequest(render_process_host_id_, paused_request_id_, false);
+ }
+
+ Release();
+}
diff --git a/chrome/browser/renderer_host/safe_browsing_resource_handler.h b/chrome/browser/renderer_host/safe_browsing_resource_handler.h
new file mode 100644
index 0000000..244df8b
--- /dev/null
+++ b/chrome/browser/renderer_host/safe_browsing_resource_handler.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_SAFE_BROWSING_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_SAFE_BROWSING_RESOURCE_HANDLER_H_
+
+#include "base/time.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+
+class ResourceDispatcherHost;
+
+// Checks that a url is safe.
+class SafeBrowsingResourceHandler : public ResourceHandler,
+ public SafeBrowsingService::Client {
+ public:
+ SafeBrowsingResourceHandler(ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ const GURL& url,
+ ResourceType::Type resource_type,
+ SafeBrowsingService* safe_browsing,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ bool OnRequestRedirected(int request_id, const GURL& new_url);
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+ void OnGetHashTimeout();
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ // SafeBrowsingService::Client implementation, called on the IO thread once
+ // the URL has been classified.
+ void OnUrlCheckResult(const GURL& url,
+ SafeBrowsingService::UrlCheckResult result);
+
+ // SafeBrowsingService::Client implementation, called on the IO thread when
+ // the user has decided to proceed with the current request, or go back.
+ void OnBlockingPageComplete(bool proceed);
+
+ private:
+ scoped_refptr<ResourceHandler> next_handler_;
+ int render_process_host_id_;
+ int render_view_id_;
+ int paused_request_id_; // -1 if not paused
+ bool in_safe_browsing_check_;
+ bool displaying_blocking_page_;
+ SafeBrowsingService::UrlCheckResult safe_browsing_result_;
+ scoped_refptr<SafeBrowsingService> safe_browsing_;
+ scoped_ptr<URLRequestStatus> queued_error_;
+ int queued_error_request_id_;
+ ResourceDispatcherHost* rdh_;
+ base::Time pause_time_;
+ ResourceType::Type resource_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingResourceHandler);
+};
+
+
+#endif // CHROME_BROWSER_RENDERER_HOST_SAFE_BROWSING_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/save_file_resource_handler.cc b/chrome/browser/renderer_host/save_file_resource_handler.cc
new file mode 100644
index 0000000..420b28a
--- /dev/null
+++ b/chrome/browser/renderer_host/save_file_resource_handler.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2006-2008 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/save_file_resource_handler.h"
+
+#include "chrome/browser/download/save_file_manager.h"
+
+SaveFileResourceHandler::SaveFileResourceHandler(int render_process_host_id,
+ int render_view_id,
+ const std::string& url,
+ SaveFileManager* manager)
+ : save_id_(-1),
+ render_process_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ read_buffer_(NULL),
+ url_(UTF8ToWide(url)),
+ content_length_(0),
+ save_manager_(manager) {
+}
+
+bool SaveFileResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& url) {
+ final_url_ = UTF8ToWide(url.spec());
+ return true;
+}
+
+bool SaveFileResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ save_id_ = save_manager_->GetNextId();
+ // |save_manager_| consumes (deletes):
+ SaveFileCreateInfo* info = new SaveFileCreateInfo;
+ info->url = url_;
+ info->final_url = final_url_;
+ info->total_bytes = content_length_;
+ info->save_id = save_id_;
+ info->render_process_id = render_process_id_;
+ info->render_view_id = render_view_id_;
+ info->request_id = request_id;
+ info->content_disposition = content_disposition_;
+ info->save_source = SaveFileCreateInfo::SAVE_FILE_FROM_NET;
+ save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
+ NewRunnableMethod(save_manager_,
+ &SaveFileManager::StartSave,
+ info));
+ return true;
+}
+
+bool SaveFileResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size,
+ int min_size) {
+ DCHECK(buf && buf_size);
+ if (!read_buffer_) {
+ *buf_size = min_size < 0 ? kReadBufSize : min_size;
+ read_buffer_ = new char[*buf_size];
+ }
+ *buf = read_buffer_;
+ return true;
+}
+
+bool SaveFileResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ DCHECK(read_buffer_);
+ save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
+ NewRunnableMethod(save_manager_,
+ &SaveFileManager::UpdateSaveProgress,
+ save_id_,
+ read_buffer_,
+ *bytes_read));
+ read_buffer_ = NULL;
+ return true;
+}
+
+bool SaveFileResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status) {
+ save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
+ NewRunnableMethod(save_manager_,
+ &SaveFileManager::SaveFinished,
+ save_id_,
+ url_,
+ render_process_id_,
+ status.is_success() && !status.is_io_pending()));
+ delete [] read_buffer_;
+ return true;
+}
diff --git a/chrome/browser/renderer_host/save_file_resource_handler.h b/chrome/browser/renderer_host/save_file_resource_handler.h
new file mode 100644
index 0000000..87f06ae
--- /dev/null
+++ b/chrome/browser/renderer_host/save_file_resource_handler.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_SAVE_FILE_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_SAVE_FILE_RESOURCE_HANDLER_H_
+
+#include <string>
+
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+class SaveFileManager;
+
+// Forwards data to the save thread.
+class SaveFileResourceHandler : public ResourceHandler {
+ public:
+ SaveFileResourceHandler(int render_process_host_id,
+ int render_view_id,
+ const std::string& url,
+ SaveFileManager* manager);
+
+ // Saves the redirected URL to final_url_, we need to use the original
+ // URL to match original request.
+ bool OnRequestRedirected(int request_id, const GURL& url);
+
+ // Sends the download creation information to the download thread.
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+
+ // Creates a new buffer, which will be handed to the download thread for file
+ // writing and deletion.
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+
+ // Passes the buffer to the download file writer.
+ bool OnReadCompleted(int request_id, int* bytes_read);
+
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ // If the content-length header is not present (or contains something other
+ // than numbers), StringToInt64 returns 0, which indicates 'unknown size' and
+ // is handled correctly by the SaveManager.
+ void set_content_length(const std::string& content_length) {
+ content_length_ = StringToInt64(content_length);
+ }
+
+ void set_content_disposition(const std::string& content_disposition) {
+ content_disposition_ = content_disposition;
+ }
+
+ private:
+ int save_id_;
+ int render_process_id_;
+ int render_view_id_;
+ char* read_buffer_;
+ std::string content_disposition_;
+ std::wstring url_;
+ std::wstring final_url_;
+ int64 content_length_;
+ SaveFileManager* save_manager_;
+
+ static const int kReadBufSize = 32768; // bytes
+
+ DISALLOW_COPY_AND_ASSIGN(SaveFileResourceHandler);
+};
+#endif // CHROME_BROWSER_RENDERER_HOST_SAVE_FILE_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/sync_resource_handler.cc b/chrome/browser/renderer_host/sync_resource_handler.cc
new file mode 100644
index 0000000..aace1c1
--- /dev/null
+++ b/chrome/browser/renderer_host/sync_resource_handler.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2006-2008 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/sync_resource_handler.h"
+
+SyncResourceHandler::SyncResourceHandler(
+ ResourceDispatcherHost::Receiver* receiver,
+ const GURL& url,
+ IPC::Message* result_message)
+ : receiver_(receiver),
+ result_message_(result_message) {
+ result_.final_url = url;
+ result_.filter_policy = FilterPolicy::DONT_FILTER;
+}
+
+bool SyncResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url) {
+ result_.final_url = new_url;
+ return true;
+}
+
+bool SyncResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ // We don't care about copying the status here.
+ result_.headers = response->response_head.headers;
+ result_.mime_type = response->response_head.mime_type;
+ result_.charset = response->response_head.charset;
+ return true;
+}
+
+bool SyncResourceHandler::OnWillRead(int request_id,
+ char** buf, int* buf_size, int min_size) {
+ DCHECK(min_size == -1);
+ *buf = read_buffer_;
+ *buf_size = kReadBufSize;
+ return true;
+}
+
+bool SyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+ result_.data.append(read_buffer_, *bytes_read);
+ return true;
+}
+
+bool SyncResourceHandler::OnResponseCompleted(int request_id,
+ const URLRequestStatus& status) {
+ result_.status = status;
+
+ ViewHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
+ receiver_->Send(result_message_);
+ return true;
+}
diff --git a/chrome/browser/renderer_host/sync_resource_handler.h b/chrome/browser/renderer_host/sync_resource_handler.h
new file mode 100644
index 0000000..81a6078
--- /dev/null
+++ b/chrome/browser/renderer_host/sync_resource_handler.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
+
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+// Used to complete a synchronous resource request in response to resource load
+// events from the resource dispatcher host.
+class SyncResourceHandler : public ResourceHandler {
+ public:
+ SyncResourceHandler(ResourceDispatcherHost::Receiver* receiver,
+ const GURL& url,
+ IPC::Message* result_message);
+
+ bool OnRequestRedirected(int request_id, const GURL& new_url);
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+ bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id, const URLRequestStatus& status);
+
+ private:
+ enum { kReadBufSize = 3840 };
+ char read_buffer_[kReadBufSize];
+
+ ViewHostMsg_SyncLoad_Result result_;
+ ResourceDispatcherHost::Receiver* receiver_;
+ IPC::Message* result_message_;
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/resource_dispatcher_host.cc b/chrome/browser/resource_dispatcher_host.cc
deleted file mode 100644
index 042f178..0000000
--- a/chrome/browser/resource_dispatcher_host.cc
+++ /dev/null
@@ -1,2524 +0,0 @@
-// Copyright (c) 2006-2008 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.
-
-// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
-
-#include <vector>
-
-#include "chrome/browser/resource_dispatcher_host.h"
-
-#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/time.h"
-#include "chrome/browser/cert_store.h"
-#include "chrome/browser/cross_site_request_manager.h"
-#include "chrome/browser/download/download_file.h"
-#include "chrome/browser/download/download_manager.h"
-#include "chrome/browser/download/download_request_manager.h"
-#include "chrome/browser/download/save_file_manager.h"
-#include "chrome/browser/external_protocol_handler.h"
-#include "chrome/browser/login_prompt.h"
-#include "chrome/browser/plugin_service.h"
-#include "chrome/browser/renderer_security_policy.h"
-#include "chrome/browser/render_view_host.h"
-#include "chrome/browser/render_view_host_delegate.h"
-#include "chrome/browser/resource_request_details.h"
-#include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/browser/tab_util.h"
-#include "chrome/browser/web_contents.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_types.h"
-#include "chrome/common/render_messages.h"
-#include "chrome/common/stl_util-inl.h"
-#include "net/base/auth.h"
-#include "net/base/cert_status_flags.h"
-#include "net/base/load_flags.h"
-#include "net/base/mime_sniffer.h"
-#include "net/base/mime_util.h"
-#include "net/base/net_errors.h"
-#include "net/url_request/url_request.h"
-
-// Uncomment to enable logging of request traffic.
-//#define LOG_RESOURCE_DISPATCHER_REQUESTS
-
-#ifdef LOG_RESOURCE_DISPATCHER_REQUESTS
-# define RESOURCE_LOG(stuff) LOG(INFO) << stuff
-#else
-# define RESOURCE_LOG(stuff)
-#endif
-
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-
-// ----------------------------------------------------------------------------
-
-// The interval for calls to ResourceDispatcherHost::UpdateLoadStates
-static const int kUpdateLoadStatesIntervalMsec = 100;
-
-// Maximum number of pending data messages sent to the renderer at any
-// given time for a given request.
-static const int kMaxPendingDataMessages = 20;
-
-// Maximum time to wait for a gethash response from the Safe Browsing servers.
-static const int kMaxGetHashMs = 1000;
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::Response
-
-struct ResourceDispatcherHost::Response : public base::RefCounted<Response> {
- ViewMsg_Resource_ResponseHead response_head;
-};
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::AsyncEventHandler
-
-// Used to complete an asynchronous resource request in response to resource
-// load events from the resource dispatcher host.
-class ResourceDispatcherHost::AsyncEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- AsyncEventHandler(ResourceDispatcherHost::Receiver* receiver,
- int render_process_host_id,
- int routing_id,
- HANDLE render_process,
- const GURL& url,
- ResourceDispatcherHost* resource_dispatcher_host)
- : receiver_(receiver),
- render_process_host_id_(render_process_host_id),
- routing_id_(routing_id),
- render_process_(render_process),
- rdh_(resource_dispatcher_host) { }
-
- static void GlobalCleanup() {
- delete spare_read_buffer_;
- spare_read_buffer_ = NULL;
- }
-
- bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
- return receiver_->Send(new ViewMsg_Resource_UploadProgress(
- routing_id_, request_id, position, size));
- }
-
- bool OnRequestRedirected(int request_id, const GURL& new_url) {
- return receiver_->Send(new ViewMsg_Resource_ReceivedRedirect(
- routing_id_, request_id, new_url));
- }
-
- bool OnResponseStarted(int request_id, Response* response) {
- receiver_->Send(new ViewMsg_Resource_ReceivedResponse(
- routing_id_, request_id, response->response_head));
- return true;
- }
-
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- DCHECK(min_size == -1);
- static const int kReadBufSize = 32768;
- if (spare_read_buffer_) {
- read_buffer_.reset(spare_read_buffer_);
- spare_read_buffer_ = NULL;
- } else {
- read_buffer_.reset(new base::SharedMemory);
- if (!read_buffer_->Create(std::wstring(), false, false, kReadBufSize))
- return false;
- if (!read_buffer_->Map(kReadBufSize))
- return false;
- }
- *buf = static_cast<char*>(read_buffer_->memory());
- *buf_size = kReadBufSize;
- return true;
- }
-
- bool OnReadCompleted(int request_id, int* bytes_read) {
- if (!*bytes_read)
- return true;
- DCHECK(read_buffer_.get());
-
- if (!rdh_->WillSendData(render_process_host_id_, request_id)) {
- // We should not send this data now, we have too many pending requests.
- return true;
- }
-
- base::SharedMemoryHandle handle;
- if (!read_buffer_->GiveToProcess(render_process_, &handle)) {
- // We wrongfully incremented the pending data count. Fake an ACK message
- // to fix this. We can't move this call above the WillSendData because
- // it's killing our read_buffer_, and we don't want that when we pause
- // the request.
- rdh_->OnDataReceivedACK(render_process_host_id_, request_id);
- return false;
- }
-
- receiver_->Send(new ViewMsg_Resource_DataReceived(
- routing_id_, request_id, handle, *bytes_read));
-
- return true;
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- receiver_->Send(new ViewMsg_Resource_RequestComplete(
- routing_id_, request_id, status));
-
- // If we still have a read buffer, then see about caching it for later...
- if (spare_read_buffer_) {
- read_buffer_.reset();
- } else if (read_buffer_.get() && read_buffer_->memory()) {
- spare_read_buffer_ = read_buffer_.release();
- }
- return true;
- }
-
- private:
- // When reading, we don't know if we are going to get EOF (0 bytes read), so
- // we typically have a buffer that we allocated but did not use. We keep
- // this buffer around for the next read as a small optimization.
- static base::SharedMemory* spare_read_buffer_;
-
- scoped_ptr<base::SharedMemory> read_buffer_;
- ResourceDispatcherHost::Receiver* receiver_;
- int render_process_host_id_;
- int routing_id_;
- HANDLE render_process_;
- ResourceDispatcherHost* rdh_;
-};
-
-base::SharedMemory*
- ResourceDispatcherHost::AsyncEventHandler::spare_read_buffer_;
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::SyncEventHandler
-
-// Used to complete a synchronous resource request in response to resource load
-// events from the resource dispatcher host.
-class ResourceDispatcherHost::SyncEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- SyncEventHandler(ResourceDispatcherHost::Receiver* receiver,
- const GURL& url,
- IPC::Message* result_message)
- : receiver_(receiver),
- result_message_(result_message) {
- result_.final_url = url;
- result_.filter_policy = FilterPolicy::DONT_FILTER;
- }
-
- bool OnRequestRedirected(int request_id, const GURL& new_url) {
- result_.final_url = new_url;
- return true;
- }
-
- bool OnResponseStarted(int request_id, Response* response) {
- // We don't care about copying the status here.
- result_.headers = response->response_head.headers;
- result_.mime_type = response->response_head.mime_type;
- result_.charset = response->response_head.charset;
- return true;
- }
-
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- DCHECK(min_size == -1);
- *buf = read_buffer_;
- *buf_size = kReadBufSize;
- return true;
- }
-
- bool OnReadCompleted(int request_id, int* bytes_read) {
- if (!*bytes_read)
- return true;
- result_.data.append(read_buffer_, *bytes_read);
- return true;
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- result_.status = status;
-
- ViewHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
- receiver_->Send(result_message_);
- return true;
- }
-
- private:
- enum { kReadBufSize = 3840 };
- char read_buffer_[kReadBufSize];
-
- ViewHostMsg_SyncLoad_Result result_;
- ResourceDispatcherHost::Receiver* receiver_;
- IPC::Message* result_message_;
-};
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::DownloadEventHandler
-// Forwards data to the download thread.
-
-class ResourceDispatcherHost::DownloadEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- DownloadEventHandler(ResourceDispatcherHost* rdh,
- int render_process_host_id,
- int render_view_id,
- int request_id,
- const std::string& url,
- DownloadFileManager* manager,
- URLRequest* request,
- bool save_as)
- : download_id_(-1),
- global_id_(render_process_host_id, request_id),
- render_view_id_(render_view_id),
- read_buffer_(NULL),
- url_(UTF8ToWide(url)),
- content_length_(0),
- download_manager_(manager),
- request_(request),
- save_as_(save_as),
- buffer_(new DownloadBuffer),
- rdh_(rdh),
- is_paused_(false) {
- }
-
- // Not needed, as this event handler ought to be the final resource.
- bool OnRequestRedirected(int request_id, const GURL& url) {
- url_ = UTF8ToWide(url.spec());
- return true;
- }
-
- // Send the download creation information to the download thread.
- bool OnResponseStarted(int request_id, Response* response) {
- std::string content_disposition;
- request_->GetResponseHeaderByName("content-disposition",
- &content_disposition);
- set_content_disposition(content_disposition);
- set_content_length(response->response_head.content_length);
-
- download_id_ = download_manager_->GetNextId();
- // |download_manager_| consumes (deletes):
- DownloadCreateInfo* info = new DownloadCreateInfo;
- info->url = url_;
- info->start_time = Time::Now();
- info->received_bytes = 0;
- info->total_bytes = content_length_;
- info->state = DownloadItem::IN_PROGRESS;
- info->download_id = download_id_;
- info->render_process_id = global_id_.render_process_host_id;
- info->render_view_id = render_view_id_;
- info->request_id = global_id_.request_id;
- info->content_disposition = content_disposition_;
- info->mime_type = response->response_head.mime_type;
- info->save_as = save_as_;
- info->is_dangerous = false;
- download_manager_->file_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(download_manager_,
- &DownloadFileManager::StartDownload,
- info));
- return true;
- }
-
- // Create a new buffer, which will be handed to the download thread for file
- // writing and deletion.
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- DCHECK(buf && buf_size);
- if (!read_buffer_) {
- *buf_size = min_size < 0 ? kReadBufSize : min_size;
- read_buffer_ = new char[*buf_size];
- }
- *buf = read_buffer_;
- return true;
- }
-
- // Pass the buffer to the download file writer.
- bool OnReadCompleted(int request_id, int* bytes_read) {
- if (!*bytes_read)
- return true;
- DCHECK(read_buffer_);
- AutoLock auto_lock(buffer_->lock);
- bool need_update = buffer_->contents.empty();
- buffer_->contents.push_back(std::make_pair(read_buffer_, *bytes_read));
- if (need_update) {
- download_manager_->file_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(download_manager_,
- &DownloadFileManager::UpdateDownload,
- download_id_,
- buffer_));
- }
- read_buffer_ = NULL;
-
- // We schedule a pause outside of the read loop if there is too much file
- // writing work to do.
- if (buffer_->contents.size() > kLoadsToWrite)
- StartPauseTimer();
-
- return true;
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- download_manager_->file_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(download_manager_,
- &DownloadFileManager::DownloadFinished,
- download_id_,
- buffer_));
- delete [] read_buffer_;
-
- // 'buffer_' is deleted by the DownloadFileManager.
- buffer_ = NULL;
- return true;
- }
-
- // If the content-length header is not present (or contains something other
- // than numbers), the incoming content_length is -1 (unknown size).
- // Set the content length to 0 to indicate unknown size to DownloadManager.
- void set_content_length(const int64& content_length) {
- content_length_ = 0;
- if (content_length > 0)
- content_length_ = content_length;
- }
-
- void set_content_disposition(const std::string& content_disposition) {
- content_disposition_ = content_disposition;
- }
-
- void CheckWriteProgress() {
- if (!buffer_)
- return; // The download completed while we were waiting to run.
-
- size_t contents_size;
- {
- AutoLock lock(buffer_->lock);
- contents_size = buffer_->contents.size();
- }
-
- bool should_pause = contents_size > kLoadsToWrite;
-
- // We'll come back later and see if it's okay to unpause the request.
- if (should_pause)
- StartPauseTimer();
-
- if (is_paused_ != should_pause) {
- rdh_->PauseRequest(global_id_.render_process_host_id,
- global_id_.request_id,
- should_pause);
- is_paused_ = should_pause;
- }
- }
-
- private:
- void StartPauseTimer() {
- if (!pause_timer_.IsRunning())
- pause_timer_.Start(TimeDelta::FromMilliseconds(kThrottleTimeMs), this,
- &DownloadEventHandler::CheckWriteProgress);
- }
-
- int download_id_;
- ResourceDispatcherHost::GlobalRequestID global_id_;
- int render_view_id_;
- char* read_buffer_;
- std::string content_disposition_;
- std::wstring url_;
- int64 content_length_;
- DownloadFileManager* download_manager_;
- URLRequest* request_;
- bool save_as_; // Request was initiated via "Save As" by the user.
- DownloadBuffer* buffer_;
- ResourceDispatcherHost* rdh_;
- bool is_paused_;
- base::OneShotTimer<DownloadEventHandler> pause_timer_;
-
- static const int kReadBufSize = 32768; // bytes
- static const int kLoadsToWrite = 100; // number of data buffers queued
- static const int kThrottleTimeMs = 200; // milliseconds
-
- DISALLOW_EVIL_CONSTRUCTORS(DownloadEventHandler);
-};
-
-// DownloadThrottlingEventHandler----------------------------------------------
-
-// DownloadThrottlingEventHandler is used to determine if a download should be
-// allowed. When a DownloadThrottlingEventHandler is created it pauses the
-// download and asks the DownloadRequestManager if the download should be
-// allowed. The DownloadRequestManager notifies us asynchronously as to whether
-// the download is allowed or not. If the download is allowed the request is
-// resumed, a DownloadEventHandler is created and all EventHandler methods are
-// delegated to it. If the download is not allowed the request is canceled.
-
-class ResourceDispatcherHost::DownloadThrottlingEventHandler :
- public ResourceDispatcherHost::EventHandler,
- public DownloadRequestManager::Callback {
- public:
- DownloadThrottlingEventHandler(ResourceDispatcherHost* host,
- URLRequest* request,
- const std::string& url,
- int render_process_host_id,
- int render_view_id,
- int request_id,
- bool in_complete)
- : host_(host),
- request_(request),
- url_(url),
- render_process_host_id_(render_process_host_id),
- render_view_id_(render_view_id),
- request_id_(request_id),
- tmp_buffer_length_(0),
- ignore_on_read_complete_(in_complete) {
- // Pause the request.
- host_->PauseRequest(render_process_host_id_, request_id_, true);
- host_->download_request_manager()->CanDownloadOnIOThread(
- render_process_host_id_, render_view_id, this);
- }
-
- virtual ~DownloadThrottlingEventHandler() {}
-
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
- if (download_handler_.get())
- return download_handler_->OnUploadProgress(request_id, position, size);
- return true;
- }
-
- virtual bool OnRequestRedirected(int request_id, const GURL& url) {
- if (download_handler_.get())
- return download_handler_->OnRequestRedirected(request_id, url);
- url_ = url.spec();
- return true;
- }
-
- virtual bool OnResponseStarted(int request_id, Response* response) {
- if (download_handler_.get())
- return download_handler_->OnResponseStarted(request_id, response);
- response_ = response;
- return true;
- }
-
- virtual bool OnWillRead(int request_id,
- char** buf,
- int* buf_size,
- int min_size) {
- if (download_handler_.get())
- return download_handler_->OnWillRead(request_id, buf, buf_size, min_size);
-
- // We should only have this invoked once, as such we only deal with one
- // tmp buffer.
- DCHECK(!tmp_buffer_.get());
- if (min_size < 0)
- min_size = 1024;
- tmp_buffer_.reset(new char[min_size]);
- *buf = tmp_buffer_.get();
- *buf_size = min_size;
- return true;
- }
-
- virtual bool OnReadCompleted(int request_id, int* bytes_read) {
- if (ignore_on_read_complete_) {
- // See comments above definition for details on this.
- ignore_on_read_complete_ = false;
- return true;
- }
- if (!*bytes_read)
- return true;
-
- if (tmp_buffer_.get()) {
- DCHECK(!tmp_buffer_length_);
- tmp_buffer_length_ = *bytes_read;
- if (download_handler_.get())
- CopyTmpBufferToDownloadHandler();
- return true;
- }
- if (download_handler_.get())
- return download_handler_->OnReadCompleted(request_id, bytes_read);
- return true;
- }
-
- virtual bool OnResponseCompleted(int request_id,
- const URLRequestStatus& status) {
- if (download_handler_.get())
- return download_handler_->OnResponseCompleted(request_id, status);
- NOTREACHED();
- return true;
- }
-
- void CancelDownload() {
- host_->CancelRequest(render_process_host_id_, request_id_, false);
- }
-
- void ContinueDownload() {
- DCHECK(!download_handler_.get());
- download_handler_ =
- new DownloadEventHandler(host_,
- render_process_host_id_,
- render_view_id_,
- request_id_,
- url_,
- host_->download_file_manager(),
- request_,
- false);
- if (response_.get())
- download_handler_->OnResponseStarted(request_id_, response_.get());
-
- if (tmp_buffer_length_)
- CopyTmpBufferToDownloadHandler();
-
- // And let the request continue.
- host_->PauseRequest(render_process_host_id_, request_id_, false);
- }
-
- private:
- void CopyTmpBufferToDownloadHandler() {
- // Copy over the tmp buffer.
- char* buffer;
- int buf_size;
- if (download_handler_->OnWillRead(request_id_, &buffer, &buf_size,
- tmp_buffer_length_)) {
- CHECK(buf_size >= tmp_buffer_length_);
- memcpy(buffer, tmp_buffer_.get(), tmp_buffer_length_);
- download_handler_->OnReadCompleted(request_id_, &tmp_buffer_length_);
- }
- tmp_buffer_length_ = 0;
- tmp_buffer_.reset();
- }
-
- ResourceDispatcherHost* host_;
- URLRequest* request_;
- std::string url_;
- int render_process_host_id_;
- int render_view_id_;
- int request_id_;
-
- // Handles the actual download. This is only created if the download is
- // allowed to continue.
- scoped_refptr<DownloadEventHandler> download_handler_;
-
- // Response supplied to OnResponseStarted. Only non-null if OnResponseStarted
- // is invoked.
- scoped_refptr<Response> response_;
-
- // If we're created by way of BufferedEventHandler we'll get one request for
- // a buffer. This is that buffer.
- scoped_array<char> tmp_buffer_;
- int tmp_buffer_length_;
-
- // If true the next call to OnReadCompleted is ignored. This is used if we're
- // paused during a call to OnReadCompleted. Pausing during OnReadCompleted
- // results in two calls to OnReadCompleted for the same data. This make sure
- // we ignore one of them.
- bool ignore_on_read_complete_;
-
- DISALLOW_COPY_AND_ASSIGN(DownloadThrottlingEventHandler);
-};
-
-
-// ----------------------------------------------------------------------------
-
-// Checks that a url is safe.
-class SafeBrowsingEventHandler
- : public ResourceDispatcherHost::EventHandler,
- public SafeBrowsingService::Client {
- public:
- SafeBrowsingEventHandler(ResourceDispatcherHost::EventHandler* handler,
- int render_process_host_id,
- int render_view_id,
- const GURL& url,
- ResourceType::Type resource_type,
- SafeBrowsingService* safe_browsing,
- ResourceDispatcherHost* resource_dispatcher_host)
- : next_handler_(handler),
- render_process_host_id_(render_process_host_id),
- render_view_id_(render_view_id),
- paused_request_id_(-1),
- safe_browsing_(safe_browsing),
- in_safe_browsing_check_(false),
- displaying_blocking_page_(false),
- rdh_(resource_dispatcher_host),
- queued_error_request_id_(-1),
- resource_type_(resource_type) {
- if (safe_browsing_->CheckUrl(url, this)) {
- safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
- safe_browsing_->LogPauseDelay(TimeDelta()); // No delay.
- } else {
- AddRef();
- in_safe_browsing_check_ = true;
- // Can't pause now because it's too early, so we'll do it in OnWillRead.
- }
- }
-
- bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
- return next_handler_->OnUploadProgress(request_id, position, size);
- }
-
- bool OnRequestRedirected(int request_id, const GURL& new_url) {
- if (in_safe_browsing_check_) {
- Release();
- in_safe_browsing_check_ = false;
- safe_browsing_->CancelCheck(this);
- }
-
- if (safe_browsing_->CheckUrl(new_url, this)) {
- safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
- safe_browsing_->LogPauseDelay(TimeDelta()); // No delay.
- } else {
- AddRef();
- in_safe_browsing_check_ = true;
- // Can't pause now because it's too early, so we'll do it in OnWillRead.
- }
-
- return next_handler_->OnRequestRedirected(request_id, new_url);
- }
-
- bool OnResponseStarted(int request_id,
- ResourceDispatcherHost::Response* response) {
- return next_handler_->OnResponseStarted(request_id, response);
- }
-
- void OnGetHashTimeout() {
- if (!in_safe_browsing_check_)
- return;
-
- safe_browsing_->CancelCheck(this);
- OnUrlCheckResult(GURL::EmptyGURL(), SafeBrowsingService::URL_SAFE);
- }
-
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- if (in_safe_browsing_check_ && pause_time_.is_null()) {
- pause_time_ = Time::Now();
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- NewRunnableMethod(this, &SafeBrowsingEventHandler::OnGetHashTimeout),
- kMaxGetHashMs);
- }
-
- if (in_safe_browsing_check_ || displaying_blocking_page_) {
- rdh_->PauseRequest(render_process_host_id_, request_id, true);
- paused_request_id_ = request_id;
- }
-
- return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
- }
-
- bool OnReadCompleted(int request_id, int* bytes_read) {
- return next_handler_->OnReadCompleted(request_id, bytes_read);
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- if ((in_safe_browsing_check_ ||
- safe_browsing_result_ != SafeBrowsingService::URL_SAFE) &&
- status.status() == URLRequestStatus::FAILED &&
- status.os_error() == net::ERR_NAME_NOT_RESOLVED) {
- // Got a DNS error while the safebrowsing check is in progress or we
- // already know that the site is unsafe. Don't show the the dns error
- // page.
- queued_error_.reset(new URLRequestStatus(status));
- queued_error_request_id_ = request_id;
- return true;
- }
-
- return next_handler_->OnResponseCompleted(request_id, status);
- }
-
- // SafeBrowsingService::Client implementation, called on the IO thread once
- // the URL has been classified.
- void OnUrlCheckResult(const GURL& url,
- SafeBrowsingService::UrlCheckResult result) {
- DCHECK(in_safe_browsing_check_);
- DCHECK(!displaying_blocking_page_);
-
- safe_browsing_result_ = result;
- in_safe_browsing_check_ = false;
-
- if (result == SafeBrowsingService::URL_SAFE) {
- if (paused_request_id_ != -1) {
- rdh_->PauseRequest(render_process_host_id_, paused_request_id_, false);
- paused_request_id_ = -1;
- }
-
- TimeDelta pause_delta;
- if (!pause_time_.is_null())
- pause_delta = Time::Now() - pause_time_;
- safe_browsing_->LogPauseDelay(pause_delta);
-
- if (queued_error_.get()) {
- next_handler_->OnResponseCompleted(
- queued_error_request_id_, *queued_error_.get());
- queued_error_.reset();
- }
-
- Release();
- } else {
- displaying_blocking_page_ = true;
- safe_browsing_->DisplayBlockingPage(
- url, resource_type_, result, this, rdh_->ui_loop(),
- render_process_host_id_, render_view_id_);
- }
- }
-
- // SafeBrowsingService::Client implementation, called on the IO thread when
- // the user has decided to proceed with the current request, or go back.
- void OnBlockingPageComplete(bool proceed) {
- DCHECK(displaying_blocking_page_);
- displaying_blocking_page_ = false;
-
- if (proceed) {
- safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
- if (paused_request_id_ != -1) {
- rdh_->PauseRequest(render_process_host_id_, paused_request_id_, false);
- paused_request_id_ = -1;
- }
-
- if (queued_error_.get()) {
- next_handler_->OnResponseCompleted(
- queued_error_request_id_, *queued_error_.get());
- queued_error_.reset();
- }
- } else {
- rdh_->CancelRequest(render_process_host_id_, paused_request_id_, false);
- }
-
- Release();
- }
-
- private:
- scoped_refptr<ResourceDispatcherHost::EventHandler> next_handler_;
- int render_process_host_id_;
- int render_view_id_;
- int paused_request_id_; // -1 if not paused
- bool in_safe_browsing_check_;
- bool displaying_blocking_page_;
- SafeBrowsingService::UrlCheckResult safe_browsing_result_;
- scoped_refptr<SafeBrowsingService> safe_browsing_;
- scoped_ptr<URLRequestStatus> queued_error_;
- int queued_error_request_id_;
- ResourceDispatcherHost* rdh_;
- Time pause_time_;
- ResourceType::Type resource_type_;
-};
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::CrossSiteEventHandler
-
-// Task to notify the WebContents that a cross-site response has begun, so that
-// WebContents can tell the old page to run its onunload handler.
-class ResourceDispatcherHost::CrossSiteNotifyTabTask : public Task {
- public:
- CrossSiteNotifyTabTask(int render_process_host_id,
- int render_view_id,
- int request_id)
- : render_process_host_id_(render_process_host_id),
- render_view_id_(render_view_id),
- request_id_(request_id) {}
-
- void Run() {
- RenderViewHost* view =
- RenderViewHost::FromID(render_process_host_id_, render_view_id_);
- if (view) {
- view->OnCrossSiteResponse(render_process_host_id_, request_id_);
- } else {
- // The view couldn't be found.
- // TODO(creis): Should notify the IO thread to proceed anyway, using
- // ResourceDispatcherHost::OnClosePageACK.
- }
- }
-
- private:
- int render_process_host_id_;
- int render_view_id_;
- int request_id_;
-};
-
-// Ensures that cross-site responses are delayed until the onunload handler of
-// the previous page is allowed to run. This handler wraps an
-// AsyncEventHandler, and it sits inside SafeBrowsing and Buffered event
-// handlers. This is important, so that it can intercept OnResponseStarted
-// after we determine that a response is safe and not a download.
-class ResourceDispatcherHost::CrossSiteEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- CrossSiteEventHandler(ResourceDispatcherHost::EventHandler* handler,
- int render_process_host_id,
- int render_view_id,
- ResourceDispatcherHost* resource_dispatcher_host)
- : next_handler_(handler),
- render_process_host_id_(render_process_host_id),
- render_view_id_(render_view_id),
- has_started_response_(false),
- in_cross_site_transition_(false),
- request_id_(-1),
- completed_during_transition_(false),
- completed_status_(),
- response_(NULL),
- rdh_(resource_dispatcher_host) {}
-
- bool OnRequestRedirected(int request_id, const GURL& new_url) {
- // We should not have started the transition before being redirected.
- DCHECK(!in_cross_site_transition_);
- return next_handler_->OnRequestRedirected(request_id, new_url);
- }
-
- bool OnResponseStarted(int request_id,
- ResourceDispatcherHost::Response* response) {
- // At this point, we know that the response is safe to send back to the
- // renderer: it is not a download, and it has passed the SSL and safe
- // browsing checks.
- // We should not have already started the transition before now.
- DCHECK(!in_cross_site_transition_);
- has_started_response_ = true;
-
- // Look up the request and associated info.
- GlobalRequestID global_id(render_process_host_id_, request_id);
- URLRequest* request = rdh_->GetURLRequest(global_id);
- if (!request) {
- DLOG(WARNING) << "Request wasn't found";
- return false;
- }
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- // If this is a download, just pass the response through without doing a
- // cross-site check. The renderer will see it is a download and abort the
- // request.
- if (info->is_download) {
- return next_handler_->OnResponseStarted(request_id, response);
- }
-
- // Tell the renderer to run the onunload event handler, and wait for the
- // reply.
- StartCrossSiteTransition(request_id, response, global_id);
- return true;
- }
-
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
- }
-
- bool OnReadCompleted(int request_id, int* bytes_read) {
- if (!in_cross_site_transition_) {
- return next_handler_->OnReadCompleted(request_id, bytes_read);
- }
- return true;
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- if (!in_cross_site_transition_) {
- if (has_started_response_) {
- // We've already completed the transition, so just pass it through.
- return next_handler_->OnResponseCompleted(request_id, status);
- } else {
- // Some types of failures will call OnResponseCompleted without calling
- // CrossSiteEventHandler::OnResponseStarted. We should wait now for
- // the cross-site transition. Also continue with the logic below to
- // remember that we completed during the cross-site transition.
- GlobalRequestID global_id(render_process_host_id_, request_id);
- StartCrossSiteTransition(request_id, NULL, global_id);
- }
- }
-
- // We have to buffer the call until after the transition completes.
- completed_during_transition_ = true;
- completed_status_ = status;
-
- // Return false to tell RDH not to notify the world or clean up the
- // pending request. We will do so in ResumeResponse.
- return false;
- }
-
- // We can now send the response to the new renderer, which will cause
- // WebContents to swap in the new renderer and destroy the old one.
- void ResumeResponse() {
- DCHECK(request_id_ != -1);
- DCHECK(in_cross_site_transition_);
- in_cross_site_transition_ = false;
-
- // Find the request for this response.
- GlobalRequestID global_id(render_process_host_id_, request_id_);
- URLRequest* request = rdh_->GetURLRequest(global_id);
- if (!request) {
- DLOG(WARNING) << "Resuming a request that wasn't found";
- return;
- }
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- if (has_started_response_) {
- // Send OnResponseStarted to the new renderer.
- DCHECK(response_);
- next_handler_->OnResponseStarted(request_id_, response_);
-
- // Unpause the request to resume reading. Any further reads will be
- // directed toward the new renderer.
- rdh_->PauseRequest(render_process_host_id_, request_id_, false);
- }
-
- // Remove ourselves from the ExtraRequestInfo.
- info->cross_site_handler = NULL;
-
- // If the response completed during the transition, notify the next
- // event handler.
- if (completed_during_transition_) {
- next_handler_->OnResponseCompleted(request_id_, completed_status_);
-
- // Since we didn't notify the world or clean up the pending request in
- // RDH::OnResponseCompleted during the transition, we should do it now.
- rdh_->NotifyResponseCompleted(request, render_process_host_id_);
- rdh_->RemovePendingRequest(render_process_host_id_, request_id_);
- }
- }
-
- private:
- // Prepare to render the cross-site response in a new RenderViewHost, by
- // telling the old RenderViewHost to run its onunload handler.
- void StartCrossSiteTransition(int request_id,
- ResourceDispatcherHost::Response* response,
- GlobalRequestID global_id) {
- in_cross_site_transition_ = true;
- request_id_ = request_id;
- response_ = response;
-
- // Store this handler on the ExtraRequestInfo, so that RDH can call our
- // ResumeResponse method when the close ACK is received.
- URLRequest* request = rdh_->GetURLRequest(global_id);
- if (!request) {
- DLOG(WARNING) << "Cross site response for a request that wasn't found";
- return;
- }
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- info->cross_site_handler = this;
-
- if (has_started_response_) {
- // Pause the request until the old renderer is finished and the new
- // renderer is ready.
- rdh_->PauseRequest(render_process_host_id_, request_id, true);
- }
- // If our OnResponseStarted wasn't called, then we're being called by
- // OnResponseCompleted after a failure. We don't need to pause, because
- // there will be no reads.
-
- // Tell the tab responsible for this request that a cross-site response is
- // starting, so that it can tell its old renderer to run its onunload
- // handler now. We will wait to hear the corresponding ClosePage_ACK.
- ResourceDispatcherHost::CrossSiteNotifyTabTask* task =
- new CrossSiteNotifyTabTask(render_process_host_id_,
- render_view_id_,
- request_id);
- rdh_->ui_loop()->PostTask(FROM_HERE, task);
- }
-
- scoped_refptr<ResourceDispatcherHost::EventHandler> next_handler_;
- int render_process_host_id_;
- int render_view_id_;
- bool has_started_response_;
- bool in_cross_site_transition_;
- int request_id_;
- bool completed_during_transition_;
- URLRequestStatus completed_status_;
- ResourceDispatcherHost::Response* response_;
- ResourceDispatcherHost* rdh_;
-};
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::BufferedEventHandler
-
-// Used to buffer a request until enough data has been received.
-class ResourceDispatcherHost::BufferedEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- BufferedEventHandler(ResourceDispatcherHost::EventHandler* handler,
- ResourceDispatcherHost* host, URLRequest* request)
- : real_handler_(handler),
- host_(host),
- request_(request),
- bytes_read_(0),
- sniff_content_(false),
- should_buffer_(false),
- buffering_(false),
- finished_(false) {}
-
- bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
- return real_handler_->OnUploadProgress(request_id, position, size);
- }
-
- bool OnRequestRedirected(int request_id, const GURL& new_url) {
- return real_handler_->OnRequestRedirected(request_id, new_url);
- }
-
- bool OnResponseStarted(int request_id, Response* response) {
- response_ = response;
- if (!DelayResponse())
- return CompleteResponseStarted(request_id, false);
- return true;
- }
-
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size);
- bool OnReadCompleted(int request_id, int* bytes_read);
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- return real_handler_->OnResponseCompleted(request_id, status);
- }
-
- private:
- // Returns true if we should delay OnResponseStarted forwarding.
- bool DelayResponse();
-
- // Returns true if there will be a need to parse the DocType of the document
- // to determine the right way to handle it.
- bool ShouldBuffer(const GURL& url, const std::string& mime_type);
-
- // Returns true if there is enough information to process the DocType.
- bool DidBufferEnough(int bytes_read) {
- const int kRequiredLength = 256;
-
- return bytes_read >= kRequiredLength;
- }
-
- // Returns true if we have to keep buffering data.
- bool KeepBuffering(int bytes_read);
-
- // Sends a pending OnResponseStarted notification. |in_complete| is true if
- // this is invoked from |OnResponseCompleted|.
- bool CompleteResponseStarted(int request_id, bool in_complete);
-
- scoped_refptr<ResourceDispatcherHost::EventHandler> real_handler_;
- scoped_refptr<Response> response_;
- ResourceDispatcherHost* host_;
- URLRequest* request_;
- char* read_buffer_;
- int read_buffer_size_;
- int bytes_read_;
- bool sniff_content_;
- bool should_buffer_;
- bool buffering_;
- bool finished_;
-
- DISALLOW_EVIL_CONSTRUCTORS(BufferedEventHandler);
-};
-
-// We'll let the original event handler provide a buffer, and reuse it for
-// subsequent reads until we're done buffering.
-bool ResourceDispatcherHost::BufferedEventHandler::OnWillRead(
- int request_id, char** buf, int* buf_size, int min_size) {
- if (buffering_) {
- *buf = read_buffer_ + bytes_read_;
- *buf_size = read_buffer_size_ - bytes_read_;
- DCHECK(*buf_size > 0);
- return true;
- }
-
- if (finished_)
- return false;
-
- bool ret = real_handler_->OnWillRead(request_id, buf, buf_size, min_size);
- read_buffer_ = *buf;
- read_buffer_size_ = *buf_size;
- bytes_read_ = 0;
- return ret;
-}
-
-bool ResourceDispatcherHost::BufferedEventHandler::OnReadCompleted(
- int request_id, int* bytes_read) {
- ResourceDispatcherHost::ExtraRequestInfo* info =
- ResourceDispatcherHost::ExtraInfoForRequest(request_);
-
- if (sniff_content_ || should_buffer_) {
- if (KeepBuffering(*bytes_read))
- return true;
-
- LOG(INFO) << "Finished buffering " << request_->url().spec();
- sniff_content_ = should_buffer_ = false;
- *bytes_read = bytes_read_;
-
- // Done buffering, send the pending ResponseStarted event.
- if (!CompleteResponseStarted(request_id, true))
- return false;
- }
-
- return real_handler_->OnReadCompleted(request_id, bytes_read);
-}
-
-bool ResourceDispatcherHost::BufferedEventHandler::DelayResponse() {
- std::string mime_type;
- request_->GetMimeType(&mime_type);
-
- std::string content_type_options;
- request_->GetResponseHeaderByName("x-content-type-options",
- &content_type_options);
- if (content_type_options != "nosniff" &&
- net::ShouldSniffMimeType(request_->url(), mime_type)) {
- // 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;
- LOG(INFO) << "To buffer: " << request_->url().spec();
- return true;
- }
-
- 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;
- LOG(INFO) << "To buffer: " << request_->url().spec();
- return true;
- }
- return false;
-}
-
-bool ResourceDispatcherHost::BufferedEventHandler::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("http") ||
- url.SchemeIs("https");
- 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 ResourceDispatcherHost::BufferedEventHandler::KeepBuffering(
- int bytes_read) {
- DCHECK(read_buffer_);
- 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_, 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(bytes_read_ < 512 /*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;
- }
-
- if (!finished_ && should_buffer_) {
- if (!DidBufferEnough(bytes_read_)) {
- buffering_ = true;
- return true;
- }
- }
- buffering_ = false;
- return false;
-}
-
-bool ResourceDispatcherHost::BufferedEventHandler::CompleteResponseStarted(
- int request_id,
- bool in_complete) {
- // 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.
- std::string content_disposition;
- request_->GetResponseHeaderByName("content-disposition",
- &content_disposition);
-
- ResourceDispatcherHost::ExtraRequestInfo* info =
- ResourceDispatcherHost::ExtraInfoForRequest(request_);
-
- if (info->allow_download &&
- host_->ShouldDownload(response_->response_head.mime_type,
- content_disposition)) {
- 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_->CancelWithError(net::ERR_FILE_NOT_FOUND);
- return false;
- }
-
- info->is_download = true;
-
- scoped_refptr<DownloadThrottlingEventHandler> download_handler =
- new DownloadThrottlingEventHandler(host_,
- request_,
- request_->url().spec(),
- info->render_process_host_id,
- info->render_view_id,
- request_id,
- in_complete);
- if (bytes_read_) {
- // a Read has already occurred and we need to copy the data into the
- // EventHandler.
- char *buf = NULL;
- int buf_len = 0;
- download_handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_);
- CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
- memcpy(buf, read_buffer_, bytes_read_);
- }
- // Update the renderer with the response headers which will cause it to
- // cancel the request.
- // TODO(paulg): Send the renderer a response that indicates that the request
- // will be handled by an external source (the browser).
- real_handler_->OnResponseStarted(info->request_id, response_);
- real_handler_ = download_handler;
- }
- return real_handler_->OnResponseStarted(request_id, response_);
-}
-
-namespace {
-
-// Consults the RendererSecurity policy to determine whether the
-// ResourceDispatcherHost should service this request. A request might be
-// disallowed if the renderer is not authorized to restrive the request URL or
-// if the renderer is attempting to upload an unauthorized file.
-bool ShouldServiceRequest(int render_process_host_id,
- const ViewHostMsg_Resource_Request& request_data) {
- // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed.
- if (render_process_host_id == -1)
- return true;
-
- RendererSecurityPolicy* policy = RendererSecurityPolicy::GetInstance();
-
- // Check if the renderer is permitted to request the requested URL.
- if (!policy->CanRequestURL(render_process_host_id, request_data.url)) {
- LOG(INFO) << "Denied unauthorized request for " <<
- request_data.url.possibly_invalid_spec();
- return false;
- }
-
- // Check if the renderer is permitted to upload the requested files.
- const std::vector<net::UploadData::Element>& uploads =
- request_data.upload_content;
- for (std::vector<net::UploadData::Element>::const_iterator iter(uploads.begin());
- iter != uploads.end(); ++iter) {
- if (iter->type() == net::UploadData::TYPE_FILE &&
- !policy->CanUploadFile(render_process_host_id, iter->file_path())) {
- NOTREACHED() << "Denied unauthorized upload of " << iter->file_path();
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost::SaveFileEventHandler
-// Forwards data to the save thread.
-class ResourceDispatcherHost::SaveFileEventHandler
- : public ResourceDispatcherHost::EventHandler {
- public:
- SaveFileEventHandler(int render_process_host_id,
- int render_view_id,
- const std::string& url,
- SaveFileManager* manager)
- : save_id_(-1),
- render_process_id_(render_process_host_id),
- render_view_id_(render_view_id),
- read_buffer_(NULL),
- url_(UTF8ToWide(url)),
- content_length_(0),
- save_manager_(manager) {
- }
-
- // Save the redirected URL to final_url_, we need to use the original
- // URL to match original request.
- bool OnRequestRedirected(int request_id, const GURL& url) {
- final_url_ = UTF8ToWide(url.spec());
- return true;
- }
-
- // Send the download creation information to the download thread.
- bool OnResponseStarted(int request_id, Response* response) {
- save_id_ = save_manager_->GetNextId();
- // |save_manager_| consumes (deletes):
- SaveFileCreateInfo* info = new SaveFileCreateInfo;
- info->url = url_;
- info->final_url = final_url_;
- info->total_bytes = content_length_;
- info->save_id = save_id_;
- info->render_process_id = render_process_id_;
- info->render_view_id = render_view_id_;
- info->request_id = request_id;
- info->content_disposition = content_disposition_;
- info->save_source = SaveFileCreateInfo::SAVE_FILE_FROM_NET;
- save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
- NewRunnableMethod(save_manager_,
- &SaveFileManager::StartSave,
- info));
- return true;
- }
-
- // Create a new buffer, which will be handed to the download thread for file
- // writing and deletion.
- bool OnWillRead(int request_id, char** buf, int* buf_size, int min_size) {
- DCHECK(buf && buf_size);
- if (!read_buffer_) {
- *buf_size = min_size < 0 ? kReadBufSize : min_size;
- read_buffer_ = new char[*buf_size];
- }
- *buf = read_buffer_;
- return true;
- }
-
- // Pass the buffer to the download file writer.
- bool OnReadCompleted(int request_id, int* bytes_read) {
- DCHECK(read_buffer_);
- save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
- NewRunnableMethod(save_manager_,
- &SaveFileManager::UpdateSaveProgress,
- save_id_,
- read_buffer_,
- *bytes_read));
- read_buffer_ = NULL;
- return true;
- }
-
- bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
- save_manager_->GetSaveLoop()->PostTask(FROM_HERE,
- NewRunnableMethod(save_manager_,
- &SaveFileManager::SaveFinished,
- save_id_,
- url_,
- render_process_id_,
- status.is_success() && !status.is_io_pending()));
- delete [] read_buffer_;
- return true;
- }
-
- // If the content-length header is not present (or contains something other
- // than numbers), StringToInt64 returns 0, which indicates 'unknown size' and
- // is handled correctly by the SaveManager.
- void set_content_length(const std::string& content_length) {
- content_length_ = StringToInt64(content_length);
- }
-
- void set_content_disposition(const std::string& content_disposition) {
- content_disposition_ = content_disposition;
- }
-
- private:
- int save_id_;
- int render_process_id_;
- int render_view_id_;
- char* read_buffer_;
- std::string content_disposition_;
- std::wstring url_;
- std::wstring final_url_;
- int64 content_length_;
- SaveFileManager* save_manager_;
-
- static const int kReadBufSize = 32768; // bytes
-
- DISALLOW_EVIL_CONSTRUCTORS(SaveFileEventHandler);
-};
-
-// ----------------------------------------------------------------------------
-// ResourceDispatcherHost
-
-ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop)
- : ui_loop_(MessageLoop::current()),
- io_loop_(io_loop),
- download_file_manager_(new DownloadFileManager(ui_loop_, this)),
- download_request_manager_(new DownloadRequestManager(io_loop, ui_loop_)),
- save_file_manager_(new SaveFileManager(ui_loop_, io_loop, this)),
- safe_browsing_(new SafeBrowsingService),
- request_id_(-1),
- plugin_service_(PluginService::GetInstance()),
- method_runner_(this),
- is_shutdown_(false) {
-}
-
-ResourceDispatcherHost::~ResourceDispatcherHost() {
- AsyncEventHandler::GlobalCleanup();
- STLDeleteValues(&pending_requests_);
-}
-
-void ResourceDispatcherHost::Initialize() {
- DCHECK(MessageLoop::current() == ui_loop_);
- download_file_manager_->Initialize();
- safe_browsing_->Initialize(io_loop_);
-}
-
-// A ShutdownTask proxies a shutdown task from the UI thread to the IO thread.
-// It should be constructed on the UI thread and run in the IO thread.
-class ResourceDispatcherHost::ShutdownTask : public Task {
- public:
- explicit ShutdownTask(ResourceDispatcherHost* resource_dispatcher_host)
- : rdh_(resource_dispatcher_host) { }
-
- void Run() {
- rdh_->OnShutdown();
- }
-
- private:
- ResourceDispatcherHost* rdh_;
-};
-
-void ResourceDispatcherHost::Shutdown() {
- DCHECK(MessageLoop::current() == ui_loop_);
- io_loop_->PostTask(FROM_HERE, new ShutdownTask(this));
-}
-
-void ResourceDispatcherHost::OnShutdown() {
- DCHECK(MessageLoop::current() == io_loop_);
- is_shutdown_ = true;
- STLDeleteValues(&pending_requests_);
- // Make sure we shutdown the timer now, otherwise by the time our destructor
- // runs if the timer is still running the Task is deleted twice (once by
- // the MessageLoop and the second time by RepeatingTimer).
- update_load_states_timer_.Stop();
-}
-
-bool ResourceDispatcherHost::HandleExternalProtocol(int request_id,
- int render_process_host_id,
- int tab_contents_id,
- const GURL& url,
- ResourceType::Type type,
- EventHandler* handler) {
- if (!ResourceType::IsFrame(type) || URLRequest::IsHandledURL(url))
- return false;
-
- ui_loop_->PostTask(FROM_HERE, NewRunnableFunction(
- &ExternalProtocolHandler::LaunchUrl, url, render_process_host_id,
- tab_contents_id));
-
- handler->OnResponseCompleted(request_id, URLRequestStatus(
- URLRequestStatus::FAILED,
- net::ERR_ABORTED));
- return true;
-}
-
-void ResourceDispatcherHost::BeginRequest(
- Receiver* receiver,
- HANDLE render_process_handle,
- int render_process_host_id,
- int render_view_id,
- int request_id,
- const ViewHostMsg_Resource_Request& request_data,
- URLRequestContext* request_context,
- IPC::Message* sync_result) {
- if (is_shutdown_ ||
- !ShouldServiceRequest(render_process_host_id, request_data)) {
- // Tell the renderer that this request was disallowed.
- receiver->Send(new ViewMsg_Resource_RequestComplete(
- render_view_id,
- request_id,
- URLRequestStatus(URLRequestStatus::FAILED, net::ERR_ABORTED)));
- return;
- }
-
- // Ensure the Chrome plugins are loaded, as they may intercept network
- // requests. Does nothing if they are already loaded.
- // TODO(mpcomplete): This takes 200 ms! Investigate parallelizing this by
- // starting the load earlier in a BG thread.
- plugin_service_->LoadChromePlugins(this);
-
- // Construct the event handler.
- scoped_refptr<EventHandler> handler;
- if (sync_result) {
- handler = new SyncEventHandler(receiver, request_data.url, sync_result);
- } else {
- handler = new AsyncEventHandler(receiver,
- render_process_host_id,
- render_view_id,
- render_process_handle,
- request_data.url,
- this);
- }
-
- if (HandleExternalProtocol(request_id, render_process_host_id, render_view_id,
- request_data.url, request_data.resource_type,
- handler)) {
- return;
- }
-
- // Construct the request.
- URLRequest* request = new URLRequest(request_data.url, this);
- request->set_method(request_data.method);
- request->set_policy_url(request_data.policy_url);
- request->set_referrer(request_data.referrer.spec());
- request->SetExtraRequestHeaders(request_data.headers);
- request->set_load_flags(request_data.load_flags);
- request->set_context(request_context);
- request->set_origin_pid(request_data.origin_pid);
-
- // Set upload data.
- uint64 upload_size = 0;
- if (!request_data.upload_content.empty()) {
- scoped_refptr<net::UploadData> upload = new net::UploadData();
- upload->set_elements(request_data.upload_content); // Deep copy.
- request->set_upload(upload);
- upload_size = upload->GetContentLength();
- }
-
- // Install a CrossSiteEventHandler if this request is coming from a
- // RenderViewHost with a pending cross-site request. We only check this for
- // MAIN_FRAME requests.
- // TODO(mpcomplete): remove "render_process_host_id != -1"
- // when http://b/viewIssue?id=1080959 is fixed.
- if (request_data.resource_type == ResourceType::MAIN_FRAME &&
- render_process_host_id != -1 &&
- Singleton<CrossSiteRequestManager>::get()->
- HasPendingCrossSiteRequest(render_process_host_id, render_view_id)) {
- // Wrap the event handler to be sure the current page's onunload handler
- // has a chance to run before we render the new page.
- handler = new CrossSiteEventHandler(handler,
- render_process_host_id,
- render_view_id,
- this);
- }
-
- if (safe_browsing_->enabled() &&
- safe_browsing_->CanCheckUrl(request_data.url)) {
- handler = new SafeBrowsingEventHandler(handler,
- render_process_host_id,
- render_view_id,
- request_data.url,
- request_data.resource_type,
- safe_browsing_,
- this);
- }
-
- // Insert a buffered event handler before the actual one.
- handler = new BufferedEventHandler(handler, this, request);
-
- // Make extra info and read footer (contains request ID).
- ExtraRequestInfo* extra_info =
- new ExtraRequestInfo(handler,
- request_id,
- render_process_host_id,
- render_view_id,
- request_data.mixed_content,
- request_data.resource_type,
- upload_size);
- extra_info->allow_download =
- ResourceType::IsFrame(request_data.resource_type);
- request->set_user_data(extra_info); // takes pointer ownership
-
- BeginRequestInternal(request, request_data.mixed_content);
-}
-
-// We are explicitly forcing the download of 'url'.
-void ResourceDispatcherHost::BeginDownload(const GURL& url,
- const GURL& referrer,
- int render_process_host_id,
- int render_view_id,
- URLRequestContext* request_context) {
- if (is_shutdown_)
- return;
-
- if (!URLRequest::IsHandledURL(url))
- return;
-
- // Check if the renderer is permitted to request the requested URL.
- //
- // TODO(mpcomplete): remove "render_process_host_id != -1"
- // when http://b/viewIssue?id=1080959 is fixed.
- if (render_process_host_id != -1 &&
- !RendererSecurityPolicy::GetInstance()->
- CanRequestURL(render_process_host_id, url)) {
- LOG(INFO) << "Denied unauthorized download request for " <<
- url.possibly_invalid_spec();
- return;
- }
-
- // Ensure the Chrome plugins are loaded, as they may intercept network
- // requests. Does nothing if they are already loaded.
- plugin_service_->LoadChromePlugins(this);
- URLRequest* request = new URLRequest(url, this);
-
- request_id_--;
-
- scoped_refptr<EventHandler> handler =
- new DownloadEventHandler(this,
- render_process_host_id,
- render_view_id,
- request_id_,
- url.spec(),
- download_file_manager_.get(),
- request,
- true);
-
-
- if (safe_browsing_->enabled() && safe_browsing_->CanCheckUrl(url)) {
- handler = new SafeBrowsingEventHandler(handler,
- render_process_host_id,
- render_view_id,
- url,
- ResourceType::MAIN_FRAME,
- safe_browsing_,
- this);
- }
-
- request->set_method("GET");
- request->set_referrer(referrer.spec());
- request->set_context(request_context);
-
- ExtraRequestInfo* extra_info =
- new ExtraRequestInfo(handler,
- request_id_,
- render_process_host_id,
- render_view_id,
- false, // Downloads are not considered mixed-content
- ResourceType::SUB_RESOURCE,
- 0 /* upload_size */ );
- extra_info->allow_download = true;
- extra_info->is_download = true;
- request->set_user_data(extra_info); // Takes pointer ownership.
-
- BeginRequestInternal(request, false);
-}
-
-// This function is only used for saving feature.
-void ResourceDispatcherHost::BeginSaveFile(const GURL& url,
- const GURL& referrer,
- int render_process_host_id,
- int render_view_id,
- URLRequestContext* request_context) {
- if (is_shutdown_)
- return;
-
- // Ensure the Chrome plugins are loaded, as they may intercept network
- // requests. Does nothing if they are already loaded.
- plugin_service_->LoadChromePlugins(this);
-
- scoped_refptr<EventHandler> handler =
- new SaveFileEventHandler(render_process_host_id,
- render_view_id,
- url.spec(),
- save_file_manager_.get());
- request_id_--;
-
- bool known_proto = URLRequest::IsHandledURL(url);
- if (!known_proto) {
- // Since any URLs which have non-standard scheme have been filtered
- // by save manager(see GURL::SchemeIsStandard). This situation
- // should not happen.
- NOTREACHED();
- return;
- }
-
- URLRequest* request = new URLRequest(url, this);
- request->set_method("GET");
- request->set_referrer(referrer.spec());
- // So far, for saving page, we need fetch content from cache, in the
- // future, maybe we can use a configuration to configure this behavior.
- request->set_load_flags(net::LOAD_ONLY_FROM_CACHE);
- request->set_context(request_context);
-
- ExtraRequestInfo* extra_info =
- new ExtraRequestInfo(handler,
- request_id_,
- render_process_host_id,
- render_view_id,
- false,
- ResourceType::SUB_RESOURCE,
- 0 /* upload_size */);
- // Just saving some resources we need, disallow downloading.
- extra_info->allow_download = false;
- extra_info->is_download = false;
- request->set_user_data(extra_info); // Takes pointer ownership.
-
- BeginRequestInternal(request, false);
-}
-
-void ResourceDispatcherHost::CancelRequest(int render_process_host_id,
- int request_id,
- bool from_renderer) {
- CancelRequest(render_process_host_id, request_id, from_renderer, true);
-}
-
-void ResourceDispatcherHost::CancelRequest(int render_process_host_id,
- int request_id,
- bool from_renderer,
- bool allow_delete) {
- PendingRequestList::iterator i = pending_requests_.find(
- GlobalRequestID(render_process_host_id, request_id));
- if (i == pending_requests_.end()) {
- // We probably want to remove this warning eventually, but I wanted to be
- // able to notice when this happens during initial development since it
- // should be rare and may indicate a bug.
- DLOG(WARNING) << "Canceling a request that wasn't found";
- return;
- }
-
- // WebKit will send us a cancel for downloads since it no longer handles them.
- // In this case, ignore the cancel since we handle downloads in the browser.
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
- if (!from_renderer || !info->is_download) {
- if (info->login_handler) {
- info->login_handler->OnRequestCancelled();
- info->login_handler = NULL;
- }
- if (!i->second->is_pending() && allow_delete) {
- // No io is pending, canceling the request won't notify us of anything,
- // so we explicitly remove it.
- // TODO: removing the request in this manner means we're not notifying
- // anyone. We need make sure the event handlers and others are notified
- // so that everything is cleaned up properly.
- RemovePendingRequest(info->render_process_host_id, info->request_id);
- } else {
- i->second->Cancel();
- }
- }
-
- // Do not remove from the pending requests, as the request will still
- // call AllDataReceived, and may even have more data before it does
- // that.
-}
-
-void ResourceDispatcherHost::OnDataReceivedACK(int render_process_host_id,
- int request_id) {
- PendingRequestList::iterator i = pending_requests_.find(
- GlobalRequestID(render_process_host_id, request_id));
- if (i == pending_requests_.end())
- return;
-
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
-
- // Decrement the number of pending data messages.
- info->pending_data_count--;
-
- // If the pending data count was higher than the max, resume the request.
- if (info->pending_data_count == kMaxPendingDataMessages) {
- // Decrement the pending data count one more time because we also
- // incremented it before pausing the request.
- info->pending_data_count--;
-
- // Resume the request.
- PauseRequest(render_process_host_id, request_id, false);
- }
-}
-
-void ResourceDispatcherHost::OnUploadProgressACK(int render_process_host_id,
- int request_id) {
- PendingRequestList::iterator i = pending_requests_.find(
- GlobalRequestID(render_process_host_id, request_id));
- if (i == pending_requests_.end())
- return;
-
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
- info->waiting_for_upload_progress_ack = false;
-}
-
-bool ResourceDispatcherHost::WillSendData(int render_process_host_id,
- int request_id) {
- PendingRequestList::iterator i = pending_requests_.find(
- GlobalRequestID(render_process_host_id, request_id));
- if (i == pending_requests_.end()) {
- NOTREACHED() << L"WillSendData for invalid request";
- return false;
- }
-
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
-
- info->pending_data_count++;
- if (info->pending_data_count > kMaxPendingDataMessages) {
- // We reached the max number of data messages that can be sent to
- // the renderer for a given request. Pause the request and wait for
- // the renderer to start processing them before resuming it.
- PauseRequest(render_process_host_id, request_id, true);
- return false;
- }
-
- return true;
-}
-
-void ResourceDispatcherHost::PauseRequest(int render_process_host_id,
- int request_id,
- bool pause) {
- GlobalRequestID global_id(render_process_host_id, request_id);
- PendingRequestList::iterator i = pending_requests_.find(global_id);
- if (i == pending_requests_.end()) {
- DLOG(WARNING) << "Pausing a request that wasn't found";
- return;
- }
-
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
-
- int pause_count = info->pause_count + (pause ? 1 : -1);
- if (pause_count < 0) {
- NOTREACHED(); // Unbalanced call to pause.
- return;
- }
- info->pause_count = pause_count;
-
- RESOURCE_LOG("To pause (" << pause << "): " << i->second->url().spec());
-
- // If we're resuming, kick the request to start reading again. Run the read
- // asynchronously to avoid recursion problems.
- if (info->pause_count == 0) {
- MessageLoop::current()->PostTask(FROM_HERE,
- method_runner_.NewRunnableMethod(
- &ResourceDispatcherHost::ResumeRequest, global_id));
- }
-}
-
-void ResourceDispatcherHost::OnClosePageACK(int render_process_host_id,
- int request_id) {
- GlobalRequestID global_id(render_process_host_id, request_id);
- PendingRequestList::iterator i = pending_requests_.find(global_id);
- if (i == pending_requests_.end()) {
- // If there are no matching pending requests, then this is not a
- // cross-site navigation and we are just closing the tab/browser.
- ui_loop_->PostTask(FROM_HERE, NewRunnableFunction(
- &RenderViewHost::ClosePageIgnoringUnloadEvents,
- render_process_host_id,
- request_id));
- return;
- }
-
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
- if (info->cross_site_handler) {
- info->cross_site_handler->ResumeResponse();
- }
-}
-
-// The object died, so cancel and detach all requests associated with it except
-// for downloads, which belong to the browser process even if initiated via a
-// renderer.
-void ResourceDispatcherHost::CancelRequestsForProcess(
- int render_process_host_id) {
- CancelRequestsForRenderView(render_process_host_id, -1 /* cancel all */);
-}
-
-void ResourceDispatcherHost::CancelRequestsForRenderView(
- int render_process_host_id,
- int render_view_id) {
- // Since pending_requests_ is a map, we first build up a list of all of the
- // matching requests to be cancelled, and then we cancel them. Since there
- // may be more than one request to cancel, we cannot simply hold onto the map
- // iterators found in the first loop.
-
- // Find the global ID of all matching elements.
- std::vector<GlobalRequestID> matching_requests;
- for (PendingRequestList::const_iterator i = pending_requests_.begin();
- i != pending_requests_.end(); ++i) {
- if (i->first.render_process_host_id == render_process_host_id) {
- ExtraRequestInfo* info = ExtraInfoForRequest(i->second);
- if (!info->is_download && (render_view_id == -1 ||
- render_view_id == info->render_view_id)) {
- matching_requests.push_back(i->first);
- }
- }
- }
-
- // Remove matches.
- for (size_t i = 0; i < matching_requests.size(); ++i) {
- PendingRequestList::iterator iter =
- pending_requests_.find(matching_requests[i]);
- // Although every matching request was in pending_requests_ when we built
- // matching_requests, it is normal for a matching request to be not found
- // in pending_requests_ after we have removed some matching requests from
- // pending_requests_. For example, deleting a URLRequest that has
- // exclusive (write) access to an HTTP cache entry may unblock another
- // URLRequest that needs exclusive access to the same cache entry, and
- // that URLRequest may complete and remove itself from pending_requests_.
- // So we need to check that iter is not equal to pending_requests_.end().
- if (iter != pending_requests_.end())
- RemovePendingRequest(iter);
- }
-}
-
-// Cancels the request and removes it from the list.
-void ResourceDispatcherHost::RemovePendingRequest(int render_process_host_id,
- int request_id) {
- PendingRequestList::iterator i = pending_requests_.find(
- GlobalRequestID(render_process_host_id, request_id));
- if (i == pending_requests_.end()) {
- NOTREACHED() << "Trying to remove a request that's not here";
- return;
- }
- RemovePendingRequest(i);
-}
-
-void ResourceDispatcherHost::RemovePendingRequest(
- const PendingRequestList::iterator& iter) {
- // Notify the login handler that this request object is going away.
- ExtraRequestInfo* info = ExtraInfoForRequest(iter->second);
- if (info && info->login_handler)
- info->login_handler->OnRequestCancelled();
-
- delete iter->second;
- pending_requests_.erase(iter);
-
- // If we have no more pending requests, then stop the load state monitor
- if (pending_requests_.empty())
- update_load_states_timer_.Stop();
-}
-
-// URLRequest::Delegate -------------------------------------------------------
-
-void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request,
- const GURL& new_url) {
- RESOURCE_LOG("OnReceivedRedirect: " << request->url().spec());
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- DCHECK(request->status().is_success());
-
- // TODO(mpcomplete): remove this when http://b/viewIssue?id=1080959 is fixed.
- if (info->render_process_host_id != -1 &&
- !RendererSecurityPolicy::GetInstance()->
- CanRequestURL(info->render_process_host_id, new_url)) {
- LOG(INFO) << "Denied unauthorized request for " <<
- new_url.possibly_invalid_spec();
-
- // Tell the renderer that this request was disallowed.
- CancelRequest(info->render_process_host_id, info->request_id, false);
- return;
- }
-
- NofityReceivedRedirect(request, info->render_process_host_id, new_url);
-
- if (HandleExternalProtocol(info->request_id, info->render_process_host_id,
- info->render_view_id, new_url,
- info->resource_type, info->event_handler)) {
- // The request is complete so we can remove it.
- RemovePendingRequest(info->render_process_host_id, info->request_id);
- return;
- }
-
- if (!info->event_handler->OnRequestRedirected(info->request_id, new_url))
- CancelRequest(info->render_process_host_id, info->request_id, false);
-}
-
-void ResourceDispatcherHost::OnAuthRequired(
- URLRequest* request,
- net::AuthChallengeInfo* auth_info) {
- // Create a login dialog on the UI thread to get authentication data,
- // or pull from cache and continue on the IO thread.
- // TODO(mpcomplete): We should block the parent tab while waiting for
- // authentication.
- // That would also solve the problem of the URLRequest being cancelled
- // before we receive authentication.
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- DCHECK(!info->login_handler) <<
- "OnAuthRequired called with login_handler pending";
- info->login_handler = CreateLoginPrompt(auth_info, request, ui_loop_);
-}
-
-void ResourceDispatcherHost::OnSSLCertificateError(
- URLRequest* request,
- int cert_error,
- net::X509Certificate* cert) {
- DCHECK(request);
- SSLManager::OnSSLCertificateError(this, request, cert_error, cert, ui_loop_);
-}
-
-void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) {
- RESOURCE_LOG("OnResponseStarted: " << request->url().spec());
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- if (PauseRequestIfNeeded(info)) {
- RESOURCE_LOG("OnResponseStarted pausing: " << request->url().spec());
- return;
- }
-
- if (request->status().is_success()) {
- // We want to send a final upload progress message prior to sending
- // the response complete message even if we're waiting for an ack to
- // to a previous upload progress message.
- info->waiting_for_upload_progress_ack = false;
- MaybeUpdateUploadProgress(info, request);
-
- if (!CompleteResponseStarted(request)) {
- CancelRequest(info->render_process_host_id, info->request_id, false);
- } else {
- // Start reading.
- int bytes_read = 0;
- if (Read(request, &bytes_read)) {
- OnReadCompleted(request, bytes_read);
- } else if (!request->status().is_io_pending()) {
- DCHECK(!info->is_paused);
- // If the error is not an IO pending, then we're done reading.
- OnResponseCompleted(request);
- }
- }
- } else {
- OnResponseCompleted(request);
- }
-}
-
-bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) {
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- scoped_refptr<Response> response(new Response);
-
- response->response_head.status = request->status();
- response->response_head.request_time = request->request_time();
- response->response_head.response_time = request->response_time();
- response->response_head.headers = request->response_headers();
- request->GetCharset(&response->response_head.charset);
- response->response_head.filter_policy = info->filter_policy;
- response->response_head.content_length = request->GetExpectedContentSize();
- request->GetMimeType(&response->response_head.mime_type);
-
- if (request->ssl_info().cert) {
- int cert_id =
- CertStore::GetSharedInstance()->StoreCert(
- request->ssl_info().cert,
- info->render_process_host_id);
- int cert_status = request->ssl_info().cert_status;
- // EV certificate verification could be expensive. We don't want to spend
- // time performing EV certificate verification on all resources because
- // EV status is irrelevant to sub-frames and sub-resources. So we call
- // IsEV here rather than in the network layer because the network layer
- // doesn't know the resource type.
- if (info->resource_type == ResourceType::MAIN_FRAME &&
- request->ssl_info().cert->IsEV(cert_status))
- cert_status |= net::CERT_STATUS_IS_EV;
-
- response->response_head.security_info =
- SSLManager::SerializeSecurityInfo(cert_id,
- cert_status,
- request->ssl_info().security_bits);
- } else {
- // We should not have any SSL state.
- DCHECK(!request->ssl_info().cert_status &&
- (request->ssl_info().security_bits == -1 ||
- request->ssl_info().security_bits == 0));
- }
-
- NotifyResponseStarted(request, info->render_process_host_id);
- return info->event_handler->OnResponseStarted(info->request_id,
- response.get());
-}
-
-void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request,
- bool mixed_content) {
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- GlobalRequestID global_id(info->render_process_host_id, info->request_id);
- pending_requests_[global_id] = request;
- if (mixed_content) {
- // We don't start the request in that case. The SSLManager will potentially
- // change the request (potentially to indicate its content should be
- // filtered) and start it itself.
- SSLManager::OnMixedContentRequest(this, request, ui_loop_);
- return;
- }
- request->Start();
-
- // Make sure we have the load state monitor running
- if (!update_load_states_timer_.IsRunning()) {
- update_load_states_timer_.Start(
- TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec),
- this, &ResourceDispatcherHost::UpdateLoadStates);
- }
-}
-
-// This test mirrors the decision that WebKit makes in
-// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType.
-// static.
-bool ResourceDispatcherHost::ShouldDownload(
- const std::string& mime_type, const std::string& content_disposition) {
- std::string type = StringToLowerASCII(mime_type);
- std::string disposition = StringToLowerASCII(content_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;
- }
-
- // MIME type checking.
- if (net::IsSupportedMimeType(type))
- return false;
-
- // Finally, check the plugin service.
- bool allow_wildcard = false;
- return !plugin_service_->HavePluginFor(type, allow_wildcard);
-}
-
-bool ResourceDispatcherHost::PauseRequestIfNeeded(ExtraRequestInfo* info) {
- if (info->pause_count > 0)
- info->is_paused = true;
-
- return info->is_paused;
-}
-
-void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) {
- PendingRequestList::iterator i = pending_requests_.find(request_id);
- if (i == pending_requests_.end()) // The request may have been destroyed
- return;
-
- URLRequest* request = i->second;
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- if (!info->is_paused)
- return;
-
- RESOURCE_LOG("Resuming: " << i->second->url().spec());
-
- info->is_paused = false;
-
- if (info->has_started_reading)
- OnReadCompleted(i->second, info->paused_read_bytes);
- else
- OnResponseStarted(i->second);
-}
-
-bool ResourceDispatcherHost::Read(URLRequest* request, int* bytes_read) {
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- DCHECK(!info->is_paused);
-
- char* buf;
- int buf_size;
- if (!info->event_handler->OnWillRead(info->request_id, &buf, &buf_size, -1))
- return false;
-
- DCHECK(buf);
- DCHECK(buf_size > 0);
-
- info->has_started_reading = true;
- return request->Read(buf, buf_size, bytes_read);
-}
-
-void ResourceDispatcherHost::OnReadCompleted(URLRequest* request,
- int bytes_read) {
- DCHECK(request);
- RESOURCE_LOG("OnReadCompleted: " << request->url().spec());
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
- if (PauseRequestIfNeeded(info)) {
- info->paused_read_bytes = bytes_read;
- RESOURCE_LOG("OnReadCompleted pausing: " << request->url().spec());
- return;
- }
-
- if (request->status().is_success() && CompleteRead(request, &bytes_read)) {
- // The request can be paused if we realize that the renderer is not
- // servicing messages fast enough.
- if (info->pause_count == 0 &&
- Read(request, &bytes_read) &&
- request->status().is_success()) {
- if (bytes_read == 0) {
- CompleteRead(request, &bytes_read);
- } else {
- // Force the next CompleteRead / Read pair to run as a separate task.
- // This avoids a fast, large network request from monopolizing the IO
- // thread and starving other IO operations from running.
- info->paused_read_bytes = bytes_read;
- info->is_paused = true;
- GlobalRequestID id(info->render_process_host_id, info->request_id);
- MessageLoop::current()->PostTask(
- FROM_HERE,
- method_runner_.NewRunnableMethod(
- &ResourceDispatcherHost::ResumeRequest, id));
- return;
- }
- }
- }
-
- if (PauseRequestIfNeeded(info)) {
- info->paused_read_bytes = bytes_read;
- RESOURCE_LOG("OnReadCompleted (CompleteRead) pausing: " <<
- request->url().spec());
- return;
- }
-
- // If the status is not IO pending then we've either finished (success) or we
- // had an error. Either way, we're done!
- if (!request->status().is_io_pending())
- OnResponseCompleted(request);
-}
-
-bool ResourceDispatcherHost::CompleteRead(URLRequest* request,
- int* bytes_read) {
- if (!request->status().is_success()) {
- NOTREACHED();
- return false;
- }
-
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- if (!info->event_handler->OnReadCompleted(info->request_id, bytes_read)) {
- // Pass in false as the last arg to indicate we don't want |request|
- // deleted. We do this as callers of us assume |request| is valid after we
- // return.
- CancelRequest(info->render_process_host_id, info->request_id, false, false);
- return false;
- }
-
- return *bytes_read != 0;
-}
-
-void ResourceDispatcherHost::OnResponseCompleted(URLRequest* request) {
- RESOURCE_LOG("OnResponseCompleted: " << request->url().spec());
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- if (info->event_handler->OnResponseCompleted(info->request_id,
- request->status())) {
- NotifyResponseCompleted(request, info->render_process_host_id);
-
- // The request is complete so we can remove it.
- RemovePendingRequest(info->render_process_host_id, info->request_id);
- }
- // If the handler's OnResponseCompleted returns false, we are deferring the
- // call until later. We will notify the world and clean up when we resume.
-}
-
-void ResourceDispatcherHost::AddObserver(Observer* obs) {
- observer_list_.AddObserver(obs);
-}
-
-void ResourceDispatcherHost::RemoveObserver(Observer* obs) {
- observer_list_.RemoveObserver(obs);
-}
-
-URLRequest* ResourceDispatcherHost::GetURLRequest(
- GlobalRequestID request_id) const {
- // This should be running in the IO loop. io_loop_ can be NULL during the
- // unit_tests.
- DCHECK(MessageLoop::current() == io_loop_ && io_loop_);
-
- PendingRequestList::const_iterator i = pending_requests_.find(request_id);
- if (i == pending_requests_.end())
- return NULL;
-
- return i->second;
-}
-
-// A NotificationTask proxies a resource dispatcher notification from the IO
-// thread to the UI thread. It should be constructed on the IO thread and run
-// in the UI thread. Takes ownership of |details|.
-class NotificationTask : public Task {
- public:
- NotificationTask(NotificationType type,
- URLRequest* request,
- ResourceRequestDetails* details)
- : type_(type),
- details_(details) {
- if (!tab_util::GetTabContentsID(request,
- &render_process_host_id_,
- &tab_contents_id_))
- NOTREACHED();
- }
-
- void Run() {
- // Find the tab associated with this request.
- WebContents* web_contents =
- tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_);
-
- if (web_contents) {
- // Issue the notification.
- NotificationService::current()->
- Notify(type_,
- Source<NavigationController>(web_contents->controller()),
- Details<ResourceRequestDetails>(details_.get()));
- }
- }
-
- private:
- // These IDs let us find the correct tab on the UI thread.
- int render_process_host_id_;
- int tab_contents_id_;
-
- // The type and details of the notification.
- NotificationType type_;
- scoped_ptr<ResourceRequestDetails> details_;
-};
-
-static int GetCertID(URLRequest* request, int render_process_host_id) {
- if (request->ssl_info().cert) {
- return CertStore::GetSharedInstance()->StoreCert(request->ssl_info().cert,
- render_process_host_id);
- }
- // If there is no SSL info attached to this request, we must either be a non
- // secure request, or the request has been canceled or failed (before the SSL
- // info was populated), or the response is an error (we have seen 403, 404,
- // and 501) made up by the proxy.
- DCHECK(!request->url().SchemeIsSecure() ||
- (request->status().status() == URLRequestStatus::CANCELED) ||
- (request->status().status() == URLRequestStatus::FAILED) ||
- ((request->response_headers()->response_code() >= 400) &&
- (request->response_headers()->response_code() <= 599)));
- return 0;
-}
-
-void ResourceDispatcherHost::NotifyResponseStarted(URLRequest* request,
- int render_process_host_id) {
- // Notify the observers on the IO thread.
- FOR_EACH_OBSERVER(Observer, observer_list_, OnRequestStarted(this, request));
-
- // Notify the observers on the UI thread.
- ui_loop_->PostTask(FROM_HERE,
- new NotificationTask(NOTIFY_RESOURCE_RESPONSE_STARTED, request,
- new ResourceRequestDetails(request,
- GetCertID(request, render_process_host_id))));
-}
-
-void ResourceDispatcherHost::NotifyResponseCompleted(
- URLRequest* request,
- int render_process_host_id) {
- // Notify the observers on the IO thread.
- FOR_EACH_OBSERVER(Observer, observer_list_,
- OnResponseCompleted(this, request));
-
- // Notify the observers on the UI thread.
- ui_loop_->PostTask(FROM_HERE,
- new NotificationTask(NOTIFY_RESOURCE_RESPONSE_COMPLETED, request,
- new ResourceRequestDetails(request,
- GetCertID(request, render_process_host_id))));
-}
-
-void ResourceDispatcherHost::NofityReceivedRedirect(URLRequest* request,
- int render_process_host_id,
- const GURL& new_url) {
- // Notify the observers on the IO thread.
- FOR_EACH_OBSERVER(Observer, observer_list_,
- OnReceivedRedirect(this, request, new_url));
-
- int cert_id = GetCertID(request, render_process_host_id);
-
- // Notify the observers on the UI thread.
- ui_loop_->PostTask(FROM_HERE,
- new NotificationTask(NOTIFY_RESOURCE_RECEIVED_REDIRECT, request,
- new ResourceRedirectDetails(request,
- cert_id,
- new_url)));
-}
-
-namespace {
-
-// This function attempts to return the "more interesting" load state of |a|
-// and |b|. We don't have temporal information about these load states
-// (meaning we don't know when we transitioned into these states), so we just
-// rank them according to how "interesting" the states are.
-//
-// We take advantage of the fact that the load states are an enumeration listed
-// in the order in which they occur during the lifetime of a request, so we can
-// regard states with larger numeric values as being further along toward
-// completion. We regard those states as more interesting to report since they
-// represent progress.
-//
-// For example, by this measure "tranferring data" is a more interesting state
-// than "resolving host" because when we are transferring data we are actually
-// doing something that corresponds to changes that the user might observe,
-// whereas waiting for a host name to resolve implies being stuck.
-//
-net::LoadState MoreInterestingLoadState(net::LoadState a, net::LoadState b) {
- return (a < b) ? b : a;
-}
-
-// Carries information about a load state change.
-struct LoadInfo {
- GURL url;
- net::LoadState load_state;
-};
-
-// Map from ProcessID+ViewID pair to LoadState
-typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap;
-
-// Used to marshall calls to LoadStateChanged from the IO to UI threads. We do
-// them all as a single task to avoid spamming the UI thread.
-class LoadInfoUpdateTask : public Task {
- public:
- virtual void Run() {
- LoadInfoMap::const_iterator i;
- for (i = info_map.begin(); i != info_map.end(); ++i) {
- RenderViewHost* view =
- RenderViewHost::FromID(i->first.first, i->first.second);
- if (view) // The view could be gone at this point.
- view->LoadStateChanged(i->second.url, i->second.load_state);
- }
- }
- LoadInfoMap info_map;
-};
-
-} // namespace
-
-void ResourceDispatcherHost::UpdateLoadStates() {
- // Populate this map with load state changes, and then send them on to the UI
- // thread where they can be passed along to the respective RVHs.
- LoadInfoMap info_map;
-
- PendingRequestList::const_iterator i;
- for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) {
- URLRequest* request = i->second;
- net::LoadState load_state = request->GetLoadState();
- ExtraRequestInfo* info = ExtraInfoForRequest(request);
-
- // We also poll for upload progress on this timer and send upload
- // progress ipc messages to the plugin process.
- MaybeUpdateUploadProgress(info, request);
-
- if (info->last_load_state != load_state) {
- info->last_load_state = load_state;
-
- std::pair<int, int> key(info->render_process_host_id,
- info->render_view_id);
- net::LoadState to_insert;
- LoadInfoMap::iterator existing = info_map.find(key);
- if (existing == info_map.end()) {
- to_insert = load_state;
- } else {
- to_insert =
- MoreInterestingLoadState(existing->second.load_state, load_state);
- if (to_insert == existing->second.load_state)
- continue;
- }
- LoadInfo& load_info = info_map[key];
- load_info.url = request->url();
- load_info.load_state = to_insert;
- }
- }
-
- if (info_map.empty())
- return;
-
- LoadInfoUpdateTask* task = new LoadInfoUpdateTask;
- task->info_map.swap(info_map);
- ui_loop_->PostTask(FROM_HERE, task);
-}
-
-void ResourceDispatcherHost::MaybeUpdateUploadProgress(ExtraRequestInfo *info,
- URLRequest *request) {
- if (!info->upload_size || info->waiting_for_upload_progress_ack ||
- !(request->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS))
- return;
-
- uint64 size = info->upload_size;
- uint64 position = request->GetUploadProgress();
- if (position == info->last_upload_position)
- return; // no progress made since last time
-
- const uint64 kHalfPercentIncrements = 200;
- const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000);
-
- uint64 amt_since_last = position - info->last_upload_position;
- TimeDelta time_since_last = TimeTicks::Now() - info->last_upload_ticks;
-
- bool is_finished = (size == position);
- bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements));
- bool too_much_time_passed = time_since_last > kOneSecond;
-
- if (is_finished || enough_new_progress || too_much_time_passed) {
- info->event_handler->OnUploadProgress(info->request_id, position, size);
- info->waiting_for_upload_progress_ack = true;
- info->last_upload_ticks = TimeTicks::Now();
- info->last_upload_position = position;
- }
-}
diff --git a/chrome/browser/resource_message_filter.h b/chrome/browser/resource_message_filter.h
index 0322953..44f0fd2 100644
--- a/chrome/browser/resource_message_filter.h
+++ b/chrome/browser/resource_message_filter.h
@@ -9,7 +9,7 @@
#include "base/gfx/rect.h"
#include "base/gfx/native_widget_types.h"
#include "base/ref_counted.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/common/ipc_channel_proxy.h"
#include "chrome/common/notification_service.h"
#include "webkit/glue/cache_manager.h"
diff --git a/chrome/browser/resource_request_details.h b/chrome/browser/resource_request_details.h
index 39e8eaa..e4a4d9d 100644
--- a/chrome/browser/resource_request_details.h
+++ b/chrome/browser/resource_request_details.h
@@ -13,7 +13,7 @@
#include "base/basictypes.h"
#include "chrome/browser/cert_store.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_status.h"
diff --git a/chrome/browser/ssl_manager.h b/chrome/browser/ssl_manager.h
index 43b71c2..2f08a93 100644
--- a/chrome/browser/ssl_manager.h
+++ b/chrome/browser/ssl_manager.h
@@ -12,8 +12,8 @@
#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "chrome/browser/provisional_load_details.h"
-#include "chrome/browser/resource_dispatcher_host.h"
#include "chrome/browser/security_style.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"
diff --git a/chrome/browser/tab_util.cc b/chrome/browser/tab_util.cc
index 863b38d4..ecb7c84 100644
--- a/chrome/browser/tab_util.cc
+++ b/chrome/browser/tab_util.cc
@@ -6,7 +6,7 @@
#include "chrome/browser/render_view_host.h"
#include "chrome/browser/render_process_host.h"
-#include "chrome/browser/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/web_contents.h"
#include "net/url_request/url_request.h"
diff --git a/chrome/browser/views/options/advanced_contents_view.cc b/chrome/browser/views/options/advanced_contents_view.cc
index c8f7d46..e63526b 100644
--- a/chrome/browser/views/options/advanced_contents_view.cc
+++ b/chrome/browser/views/options/advanced_contents_view.cc
@@ -23,7 +23,6 @@
#include "chrome/browser/gears_integration.h"
#include "chrome/browser/metrics_service.h"
#include "chrome/browser/net/dns_global.h"
-#include "chrome/browser/resource_dispatcher_host.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/views/options/cookies_view.h"
#include "chrome/browser/views/options/language_combobox_model.h"
diff --git a/chrome/test/ui/ui_tests.vcproj b/chrome/test/ui/ui_tests.vcproj
index 5fda197..9d5bdb1 100644
--- a/chrome/test/ui/ui_tests.vcproj
+++ b/chrome/test/ui/ui_tests.vcproj
@@ -362,7 +362,7 @@
Name="TestResourceDispatcherHost"
>
<File
- RelativePath="..\..\browser\resource_dispatcher_host_uitest.cc"
+ RelativePath="..\..\browser\renderer_host\resource_dispatcher_host_uitest.cc"
>
</File>
</Filter>
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index b7c7da1..449e822 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -290,8 +290,26 @@
Name="TestResourceDispatcher"
>
<File
- RelativePath="..\..\browser\resource_dispatcher_host_unittest.cc"
+ RelativePath="..\..\browser\renderer_host\resource_dispatcher_host_unittest.cc"
>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath="..\..\common\resource_dispatcher_unittest.cc"