diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 08:04:45 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 08:04:45 +0000 |
commit | c0dac327638b6e975dda0dc9d4d538f07ea161e5 (patch) | |
tree | 31830e8c9bd558ba8b1812701ced769d632255b1 | |
parent | b29bd2338b14010216c273595bb15401f2382342 (diff) | |
download | chromium_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-- | DEPS | 2 | ||||
-rw-r--r-- | chrome/browser/renderer_host/buffered_resource_handler.cc | 3 | ||||
-rw-r--r-- | chrome/browser/renderer_host/redirect_to_file_resource_handler.cc | 185 | ||||
-rw-r--r-- | chrome/browser/renderer_host/redirect_to_file_resource_handler.h | 73 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 4 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | webkit/glue/plugins/pepper_url_loader.cc | 42 | ||||
-rw-r--r-- | webkit/glue/plugins/pepper_url_loader.h | 3 | ||||
-rw-r--r-- | webkit/glue/plugins/pepper_url_response_info.cc | 2 |
9 files changed, 306 insertions, 10 deletions
@@ -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; } |