summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-28 08:04:45 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-28 08:04:45 +0000
commitc0dac327638b6e975dda0dc9d4d538f07ea161e5 (patch)
tree31830e8c9bd558ba8b1812701ced769d632255b1
parentb29bd2338b14010216c273595bb15401f2382342 (diff)
downloadchromium_src-c0dac327638b6e975dda0dc9d4d538f07ea161e5.zip
chromium_src-c0dac327638b6e975dda0dc9d4d538f07ea161e5.tar.gz
chromium_src-c0dac327638b6e975dda0dc9d4d538f07ea161e5.tar.bz2
Initial stream to file implementation.
This patch excludes one important detail. This code does not cleanup temp files yet. That will be added in a subsequent CL. R=brettw BUG=49789 TEST=third_party/ppapi/tests/test_url_loader.cc:TestStreamToFile Review URL: http://codereview.chromium.org/2806079 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53923 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--DEPS2
-rw-r--r--chrome/browser/renderer_host/buffered_resource_handler.cc3
-rw-r--r--chrome/browser/renderer_host/redirect_to_file_resource_handler.cc185
-rw-r--r--chrome/browser/renderer_host/redirect_to_file_resource_handler.h73
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc4
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--webkit/glue/plugins/pepper_url_loader.cc42
-rw-r--r--webkit/glue/plugins/pepper_url_loader.h3
-rw-r--r--webkit/glue/plugins/pepper_url_response_info.cc2
9 files changed, 306 insertions, 10 deletions
diff --git a/DEPS b/DEPS
index 1ed4769..f6c7e70 100644
--- a/DEPS
+++ b/DEPS
@@ -158,7 +158,7 @@ deps = {
Var("libvpx_revision"),
"src/third_party/ppapi":
- "http://ppapi.googlecode.com/svn/trunk@169",
+ "http://ppapi.googlecode.com/svn/trunk@174",
"src/third_party/libjingle/source":
"http://libjingle.googlecode.com/svn/branches/nextsnap@" +
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc
index 8ffa49a..843fd9a 100644
--- a/chrome/browser/renderer_host/buffered_resource_handler.cc
+++ b/chrome/browser/renderer_host/buffered_resource_handler.cc
@@ -242,6 +242,9 @@ bool BufferedResourceHandler::KeepBuffering(int bytes_read) {
DCHECK(read_buffer_);
if (my_buffer_) {
// We are using our own buffer to read, update the main buffer.
+ // TODO(darin): We should handle the case where read_buffer_size_ is small!
+ // See RedirectToFileResourceHandler::BufIsFull to see how this impairs
+ // downstream ResourceHandler implementations.
CHECK_LT(bytes_read + bytes_read_, read_buffer_size_);
memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read);
my_buffer_ = NULL;
diff --git a/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc b/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc
new file mode 100644
index 0000000..4f523b7
--- /dev/null
+++ b/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2010 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/redirect_to_file_resource_handler.h"
+
+#include "base/file_util.h"
+#include "base/platform_file.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/common/resource_response.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
+#include "net/base/net_errors.h"
+
+// TODO(darin): Use the buffer sizing algorithm from AsyncResourceHandler.
+static const int kReadBufSize = 32768;
+
+RedirectToFileResourceHandler::RedirectToFileResourceHandler(
+ ResourceHandler* next_handler,
+ int process_id,
+ ResourceDispatcherHost* host)
+ : host_(host),
+ next_handler_(next_handler),
+ process_id_(process_id),
+ request_id_(-1),
+ buf_(new net::GrowableIOBuffer()),
+ buf_write_pending_(false),
+ write_cursor_(0),
+ write_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ &RedirectToFileResourceHandler::DidWriteToFile),
+ write_callback_pending_(false) {
+}
+
+bool RedirectToFileResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool RedirectToFileResourceHandler::OnRequestRedirected(
+ int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ return next_handler_->OnRequestRedirected(request_id, new_url, response,
+ defer);
+}
+
+bool RedirectToFileResourceHandler::OnResponseStarted(
+ int request_id,
+ ResourceResponse* response) {
+ if (response->response_head.status.is_success()) {
+ // TODO(darin): Move this file creation to a background thread.
+ file_util::CreateTemporaryFile(&file_path_);
+ response->response_head.download_file_path = file_path_;
+ }
+ return next_handler_->OnResponseStarted(request_id, response);
+}
+
+bool RedirectToFileResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return next_handler_->OnWillStart(request_id, url, defer);
+}
+
+bool RedirectToFileResourceHandler::OnWillRead(int request_id,
+ net::IOBuffer** buf,
+ int* buf_size,
+ int min_size) {
+ DCHECK(min_size == -1);
+
+ if (!buf_->capacity())
+ buf_->SetCapacity(kReadBufSize);
+
+ // We should have paused this network request already if the buffer is full.
+ DCHECK(!BufIsFull());
+
+ *buf = buf_;
+ *buf_size = buf_->RemainingCapacity();
+
+ buf_write_pending_ = true;
+ return true;
+}
+
+bool RedirectToFileResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (!buf_write_pending_) {
+ // Ignore spurious OnReadCompleted! PauseRequest(true) called from within
+ // OnReadCompleted tells the ResourceDispatcherHost that we did not consume
+ // the data. PauseRequest(false) then repeats the last OnReadCompleted
+ // call. We pause the request so that we can copy our buffer to disk, so
+ // we need to consume the data now. The ResourceDispatcherHost pause
+ // mechanism does not fit our use case very well.
+ // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack!
+ return true;
+ }
+
+ buf_write_pending_ = false;
+
+ // We use the buffer's offset field to record the end of the buffer.
+
+ int new_offset = buf_->offset() + *bytes_read;
+ DCHECK(new_offset <= buf_->capacity());
+ buf_->set_offset(new_offset);
+
+ if (!file_stream_.IsOpen()) {
+ int rv = file_stream_.Open(file_path_, base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_ASYNC);
+ if (rv != net::OK)
+ return false;
+ }
+
+ if (BufIsFull())
+ host_->PauseRequest(process_id_, request_id, true);
+
+ request_id_ = request_id;
+ return WriteMore();
+}
+
+bool RedirectToFileResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status,
+ const std::string& security_info) {
+ return next_handler_->OnResponseCompleted(request_id, status, security_info);
+}
+
+void RedirectToFileResourceHandler::OnRequestClosed() {
+ next_handler_->OnRequestClosed();
+}
+
+RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
+}
+
+void RedirectToFileResourceHandler::DidWriteToFile(int result) {
+ write_callback_pending_ = false;
+
+ bool failed = false;
+ if (result > 0) {
+ write_cursor_ += result;
+ failed = !WriteMore();
+ } else {
+ failed = true;
+ }
+
+ if (failed)
+ host_->CancelRequest(process_id_, request_id_, false);
+}
+
+bool RedirectToFileResourceHandler::WriteMore() {
+ for (;;) {
+ if (write_cursor_ == buf_->offset()) {
+ // We've caught up to the network load, but it may be in the process of
+ // appending more data to the buffer.
+ if (!buf_write_pending_) {
+ if (BufIsFull())
+ host_->PauseRequest(process_id_, request_id_, false);
+ buf_->set_offset(0);
+ write_cursor_ = 0;
+ }
+ return true;
+ }
+ if (write_callback_pending_)
+ return true;
+ DCHECK(write_cursor_ < buf_->offset());
+ int rv = file_stream_.Write(buf_->StartOfBuffer() + write_cursor_,
+ buf_->offset() - write_cursor_,
+ &write_callback_);
+ if (rv == net::ERR_IO_PENDING) {
+ write_callback_pending_ = true;
+ return true;
+ }
+ if (rv < 0)
+ return false;
+ write_cursor_ += rv;
+ }
+}
+
+bool RedirectToFileResourceHandler::BufIsFull() const {
+ // This is a hack to workaround BufferedResourceHandler's inability to
+ // deal with a ResourceHandler that returns a buffer size of less than
+ // 2 * net::kMaxBytesToSniff from its OnWillRead method.
+ // TODO(darin): Fix this retardation!
+ return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
+}
diff --git a/chrome/browser/renderer_host/redirect_to_file_resource_handler.h b/chrome/browser/renderer_host/redirect_to_file_resource_handler.h
new file mode 100644
index 0000000..f9d2b57
--- /dev/null
+++ b/chrome/browser/renderer_host/redirect_to_file_resource_handler.h
@@ -0,0 +1,73 @@
+// 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_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+#include "net/base/completion_callback.h"
+#include "net/base/file_stream.h"
+
+class ResourceDispatcherHost;
+
+namespace net {
+class GrowableIOBuffer;
+}
+
+// Redirects network data to a file. This is intended to be layered in front
+// of either the AsyncResourceHandler or the SyncResourceHandler.
+class RedirectToFileResourceHandler : public ResourceHandler {
+ public:
+ RedirectToFileResourceHandler(
+ ResourceHandler* next_handler,
+ int process_id,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ bool OnResponseStarted(int request_id, ResourceResponse* response);
+ bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ bool OnReadCompleted(int request_id, int* bytes_read);
+ bool OnResponseCompleted(int request_id,
+ const URLRequestStatus& status,
+ const std::string& security_info);
+ void OnRequestClosed();
+
+ private:
+ virtual ~RedirectToFileResourceHandler();
+ void DidWriteToFile(int result);
+ bool WriteMore();
+ bool BufIsFull() const;
+
+ ResourceDispatcherHost* host_;
+ scoped_refptr<ResourceHandler> next_handler_;
+ int process_id_;
+ int request_id_;
+
+ // We allocate a single, fixed-size IO buffer (buf_) used to read from the
+ // network (buf_write_pending_ is true while the system is copying data into
+ // buf_), and then write this buffer out to disk (write_callback_pending_ is
+ // true while writing to disk). Reading from the network is suspended while
+ // the buffer is full (BufIsFull returns true). The write_cursor_ member
+ // tracks the offset into buf_ that we are writing to disk.
+
+ scoped_refptr<net::GrowableIOBuffer> buf_;
+ bool buf_write_pending_;
+ int write_cursor_;
+
+ FilePath file_path_;
+ net::FileStream file_stream_;
+ net::CompletionCallbackImpl<RedirectToFileResourceHandler> write_callback_;
+ bool write_callback_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(RedirectToFileResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc
index eb826a2..b9a11c7 100644
--- a/chrome/browser/renderer_host/resource_dispatcher_host.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc
@@ -34,6 +34,7 @@
#include "chrome/browser/renderer_host/cross_site_resource_handler.h"
#include "chrome/browser/renderer_host/download_resource_handler.h"
#include "chrome/browser/renderer_host/global_request_id.h"
+#include "chrome/browser/renderer_host/redirect_to_file_resource_handler.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/browser/renderer_host/render_view_host_notification_task.h"
@@ -401,6 +402,9 @@ void ResourceDispatcherHost::BeginRequest(
this);
}
+ if (request_data.download_to_file)
+ handler = new RedirectToFileResourceHandler(handler, child_id, this);
+
if (HandleExternalProtocol(request_id, child_id, route_id,
request_data.url, request_data.resource_type,
handler)) {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index cc0cbf5..4bb549b 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2139,6 +2139,8 @@
'browser/renderer_host/gtk_key_bindings_handler.h',
'browser/renderer_host/offline_resource_handler.cc',
'browser/renderer_host/offline_resource_handler.h',
+ 'browser/renderer_host/redirect_to_file_resource_handler.cc',
+ 'browser/renderer_host/redirect_to_file_resource_handler.h',
'browser/renderer_host/render_process_host.cc',
'browser/renderer_host/render_process_host.h',
'browser/renderer_host/render_sandbox_host_linux.h',
diff --git a/webkit/glue/plugins/pepper_url_loader.cc b/webkit/glue/plugins/pepper_url_loader.cc
index 669ce0d..26dadad 100644
--- a/webkit/glue/plugins/pepper_url_loader.cc
+++ b/webkit/glue/plugins/pepper_url_loader.cc
@@ -122,6 +122,15 @@ int32_t ReadResponseBody(PP_Resource loader_id,
return loader->ReadResponseBody(buffer, bytes_to_read, callback);
}
+int32_t FinishStreamingToFile(PP_Resource loader_id,
+ PP_CompletionCallback callback) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return PP_ERROR_BADRESOURCE;
+
+ return loader->FinishStreamingToFile(callback);
+}
+
void Close(PP_Resource loader_id) {
scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
if (!loader)
@@ -139,6 +148,7 @@ const PPB_URLLoader ppb_urlloader = {
&GetDownloadProgress,
&GetResponseInfo,
&ReadResponseBody,
+ &FinishStreamingToFile,
&Close
};
@@ -154,7 +164,7 @@ URLLoader::URLLoader(PluginInstance* instance)
total_bytes_to_be_received_(-1),
user_buffer_(NULL),
user_buffer_size_(0),
- done_(false) {
+ done_status_(PP_ERROR_WOULDBLOCK) {
}
URLLoader::~URLLoader() {
@@ -200,6 +210,8 @@ int32_t URLLoader::FollowRedirect(PP_CompletionCallback callback) {
int32_t URLLoader::ReadResponseBody(char* buffer, int32_t bytes_to_read,
PP_CompletionCallback callback) {
+ if (!response_info_ || response_info_->body())
+ return PP_ERROR_FAILED;
if (bytes_to_read <= 0 || !buffer)
return PP_ERROR_BADARGUMENT;
if (pending_callback_.func)
@@ -215,16 +227,32 @@ int32_t URLLoader::ReadResponseBody(char* buffer, int32_t bytes_to_read,
if (!buffer_.empty())
return FillUserBuffer();
- if (done_) {
+ // We may have already reached EOF.
+ if (done_status_ != PP_ERROR_WOULDBLOCK) {
user_buffer_ = NULL;
user_buffer_size_ = 0;
- return 0;
+ return done_status_;
}
pending_callback_ = callback;
return PP_ERROR_WOULDBLOCK;
}
+int32_t URLLoader::FinishStreamingToFile(PP_CompletionCallback callback) {
+ if (!response_info_ || !response_info_->body())
+ return PP_ERROR_FAILED;
+ if (pending_callback_.func)
+ return PP_ERROR_INPROGRESS;
+
+ // We may have already reached EOF.
+ if (done_status_ != PP_ERROR_WOULDBLOCK)
+ return done_status_;
+
+ // Wait for didFinishLoading / didFail.
+ pending_callback_ = callback;
+ return PP_ERROR_WOULDBLOCK;
+}
+
void URLLoader::Close() {
NOTIMPLEMENTED(); // TODO(darin): Implement me.
}
@@ -274,14 +302,14 @@ void URLLoader::didReceiveData(WebURLLoader* loader,
}
void URLLoader::didFinishLoading(WebURLLoader* loader) {
- done_ = true;
- RunCallback(PP_OK);
+ done_status_ = PP_OK;
+ RunCallback(done_status_);
}
void URLLoader::didFail(WebURLLoader* loader, const WebURLError& error) {
- done_ = true;
// TODO(darin): Provide more detailed error information.
- RunCallback(PP_ERROR_FAILED);
+ done_status_ = PP_ERROR_FAILED;
+ RunCallback(done_status_);
}
void URLLoader::RunCallback(int32_t result) {
diff --git a/webkit/glue/plugins/pepper_url_loader.h b/webkit/glue/plugins/pepper_url_loader.h
index d0d70f1..5cf3e81 100644
--- a/webkit/glue/plugins/pepper_url_loader.h
+++ b/webkit/glue/plugins/pepper_url_loader.h
@@ -38,6 +38,7 @@ class URLLoader : public Resource, public WebKit::WebURLLoaderClient {
int32_t FollowRedirect(PP_CompletionCallback callback);
int32_t ReadResponseBody(char* buffer, int32_t bytes_to_read,
PP_CompletionCallback callback);
+ int32_t FinishStreamingToFile(PP_CompletionCallback callback);
void Close();
// WebKit::WebURLLoaderClient implementation.
@@ -83,7 +84,7 @@ class URLLoader : public Resource, public WebKit::WebURLLoaderClient {
int64_t total_bytes_to_be_received_;
char* user_buffer_;
size_t user_buffer_size_;
- bool done_;
+ int32_t done_status_;
};
} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_url_response_info.cc b/webkit/glue/plugins/pepper_url_response_info.cc
index fab8cae..878fed5 100644
--- a/webkit/glue/plugins/pepper_url_response_info.cc
+++ b/webkit/glue/plugins/pepper_url_response_info.cc
@@ -110,7 +110,7 @@ bool URLResponseInfo::Initialize(const WebURLResponse& response) {
headers_ = flattener.buffer();
WebString file_path = response.downloadFilePath();
- if (!file_path.isNull())
+ if (!file_path.isEmpty())
body_ = new FileRef(module(), webkit_glue::WebStringToFilePath(file_path));
return true;
}