Index: trunk/src/content/browser/loader/redirect_to_file_resource_handler.cc |
=================================================================== |
--- trunk/src/content/browser/loader/redirect_to_file_resource_handler.cc (revision 256703) |
+++ trunk/src/content/browser/loader/redirect_to_file_resource_handler.cc (working copy) |
@@ -5,12 +5,14 @@ |
#include "content/browser/loader/redirect_to_file_resource_handler.h" |
#include "base/bind.h" |
+#include "base/files/file_util_proxy.h" |
#include "base/logging.h" |
+#include "base/message_loop/message_loop_proxy.h" |
#include "base/platform_file.h" |
#include "base/threading/thread_restrictions.h" |
+#include "content/browser/loader/resource_dispatcher_host_impl.h" |
#include "content/browser/loader/resource_request_info_impl.h" |
-#include "content/browser/loader/temporary_file_stream.h" |
-#include "content/public/browser/resource_controller.h" |
+#include "content/public/browser/browser_thread.h" |
#include "content/public/common/resource_response.h" |
#include "net/base/file_stream.h" |
#include "net/base/io_buffer.h" |
@@ -51,118 +53,33 @@ |
static const int kInitialReadBufSize = 32768; |
static const int kMaxReadBufSize = 524288; |
-// A separate IO thread object to manage the lifetime of the net::FileStream and |
-// the ShareableFileReference. When the handler is destroyed, it asynchronously |
-// closes net::FileStream after all pending writes complete. Only after the |
-// stream is closed is the ShareableFileReference released, to ensure the |
-// temporary is not deleted before it is closed. |
-class RedirectToFileResourceHandler::Writer { |
- public: |
- Writer(RedirectToFileResourceHandler* handler, |
- scoped_ptr<net::FileStream> file_stream, |
- ShareableFileReference* deletable_file) |
- : handler_(handler), |
- file_stream_(file_stream.Pass()), |
- is_writing_(false), |
- deletable_file_(deletable_file) { |
- DCHECK(!deletable_file_->path().empty()); |
- } |
- |
- bool is_writing() const { return is_writing_; } |
- const base::FilePath& path() const { return deletable_file_->path(); } |
- |
- int Write(net::IOBuffer* buf, int buf_len) { |
- DCHECK(!is_writing_); |
- DCHECK(handler_); |
- int result = file_stream_->Write( |
- buf, buf_len, |
- base::Bind(&Writer::DidWriteToFile, base::Unretained(this))); |
- if (result == net::ERR_IO_PENDING) |
- is_writing_ = true; |
- return result; |
- } |
- |
- void Close() { |
- handler_ = NULL; |
- if (!is_writing_) |
- CloseAndDelete(); |
- } |
- |
- private: |
- // Only DidClose can delete this. |
- ~Writer() { |
- } |
- |
- void DidWriteToFile(int result) { |
- DCHECK(is_writing_); |
- is_writing_ = false; |
- if (handler_) { |
- handler_->DidWriteToFile(result); |
- } else { |
- CloseAndDelete(); |
- } |
- } |
- |
- void CloseAndDelete() { |
- DCHECK(!is_writing_); |
- int result = file_stream_->Close(base::Bind(&Writer::DidClose, |
- base::Unretained(this))); |
- if (result != net::ERR_IO_PENDING) |
- DidClose(result); |
- } |
- |
- void DidClose(int result) { |
- delete this; |
- } |
- |
- RedirectToFileResourceHandler* handler_; |
- |
- scoped_ptr<net::FileStream> file_stream_; |
- bool is_writing_; |
- |
- // We create a ShareableFileReference that's deletable for the temp file |
- // created as a result of the download. |
- scoped_refptr<webkit_blob::ShareableFileReference> deletable_file_; |
- |
- DISALLOW_COPY_AND_ASSIGN(Writer); |
-}; |
- |
RedirectToFileResourceHandler::RedirectToFileResourceHandler( |
scoped_ptr<ResourceHandler> next_handler, |
- net::URLRequest* request) |
+ net::URLRequest* request, |
+ ResourceDispatcherHostImpl* host) |
: LayeredResourceHandler(request, next_handler.Pass()), |
+ weak_factory_(this), |
+ host_(host), |
buf_(new net::GrowableIOBuffer()), |
buf_write_pending_(false), |
write_cursor_(0), |
- writer_(NULL), |
+ write_callback_pending_(false), |
next_buffer_size_(kInitialReadBufSize), |
did_defer_(false), |
- completed_during_write_(false), |
- weak_factory_(this) { |
+ completed_during_write_(false) { |
} |
RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { |
- // Orphan the writer to asynchronously close and release the temporary file. |
- if (writer_) { |
- writer_->Close(); |
- writer_ = NULL; |
- } |
} |
-void RedirectToFileResourceHandler:: |
- SetCreateTemporaryFileStreamFunctionForTesting( |
- const CreateTemporaryFileStreamFunction& create_temporary_file_stream) { |
- create_temporary_file_stream_ = create_temporary_file_stream; |
-} |
- |
bool RedirectToFileResourceHandler::OnResponseStarted( |
int request_id, |
ResourceResponse* response, |
bool* defer) { |
if (response->head.error_code == net::OK || |
response->head.error_code == net::ERR_IO_PENDING) { |
- DCHECK(writer_); |
- response->head.download_file_path = writer_->path(); |
+ DCHECK(deletable_file_.get() && !deletable_file_->path().empty()); |
+ response->head.download_file_path = deletable_file_->path(); |
} |
return next_handler_->OnResponseStarted(request_id, response, defer); |
} |
@@ -170,23 +87,19 @@ |
bool RedirectToFileResourceHandler::OnWillStart(int request_id, |
const GURL& url, |
bool* defer) { |
- DCHECK(!writer_); |
- |
- // Defer starting the request until we have created the temporary file. |
- // TODO(darin): This is sub-optimal. We should not delay starting the |
- // network request like this. |
- will_start_url_ = url; |
- did_defer_ = *defer = true; |
- if (create_temporary_file_stream_.is_null()) { |
- CreateTemporaryFileStream( |
+ if (!deletable_file_.get()) { |
+ // Defer starting the request until we have created the temporary file. |
+ // TODO(darin): This is sub-optimal. We should not delay starting the |
+ // network request like this. |
+ did_defer_ = *defer = true; |
+ base::FileUtilProxy::CreateTemporary( |
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get(), |
+ base::PLATFORM_FILE_ASYNC, |
base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, |
weak_factory_.GetWeakPtr())); |
- } else { |
- create_temporary_file_stream_.Run( |
- base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, |
- weak_factory_.GetWeakPtr())); |
+ return true; |
} |
- return true; |
+ return next_handler_->OnWillStart(request_id, url, defer); |
} |
bool RedirectToFileResourceHandler::OnWillRead( |
@@ -238,7 +151,7 @@ |
const net::URLRequestStatus& status, |
const std::string& security_info, |
bool* defer) { |
- if (writer_ && writer_->is_writing()) { |
+ if (write_callback_pending_) { |
completed_during_write_ = true; |
completed_status_ = status; |
completed_security_info_ = security_info; |
@@ -250,31 +163,25 @@ |
} |
void RedirectToFileResourceHandler::DidCreateTemporaryFile( |
- base::File::Error error_code, |
- scoped_ptr<net::FileStream> file_stream, |
- ShareableFileReference* deletable_file) { |
- DCHECK(!writer_); |
- if (error_code != base::File::FILE_OK) { |
- controller()->CancelWithError(net::FileErrorToNetError(error_code)); |
- return; |
- } |
- |
- writer_ = new Writer(this, file_stream.Pass(), deletable_file); |
- |
- // Resume the request. |
- DCHECK(did_defer_); |
- bool defer = false; |
- if (!next_handler_->OnWillStart(GetRequestID(), will_start_url_, &defer)) { |
- controller()->Cancel(); |
- } else if (!defer) { |
- ResumeIfDeferred(); |
- } else { |
- did_defer_ = false; |
- } |
- will_start_url_ = GURL(); |
+ base::File::Error /*error_code*/, |
+ base::PassPlatformFile file_handle, |
+ const base::FilePath& file_path) { |
+ deletable_file_ = ShareableFileReference::GetOrCreate( |
+ file_path, |
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE, |
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get()); |
+ file_stream_.reset( |
+ new net::FileStream(file_handle.ReleaseValue(), |
+ base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, |
+ NULL)); |
+ const ResourceRequestInfo* info = GetRequestInfo(); |
+ host_->RegisterDownloadedTempFile( |
+ info->GetChildID(), info->GetRequestID(), deletable_file_.get()); |
+ ResumeIfDeferred(); |
} |
void RedirectToFileResourceHandler::DidWriteToFile(int result) { |
+ write_callback_pending_ = false; |
int request_id = GetRequestID(); |
bool failed = false; |
@@ -287,38 +194,20 @@ |
} |
if (failed) { |
- DCHECK(!writer_->is_writing()); |
- // TODO(davidben): Recover the error code from WriteMore or |result|, as |
- // appropriate. |
- if (completed_during_write_ && completed_status_.is_success()) { |
- // If the request successfully completed mid-write, but the write failed, |
- // convert the status to a failure for downstream. |
- completed_status_.set_status(net::URLRequestStatus::CANCELED); |
- completed_status_.set_error(net::ERR_FAILED); |
- } |
- if (!completed_during_write_) |
- controller()->CancelWithError(net::ERR_FAILED); |
- } |
- |
- if (completed_during_write_ && !writer_->is_writing()) { |
- // Resume shutdown now that all data has been written to disk. Note that |
- // this should run even in the |failed| case above, otherwise a failed write |
- // leaves the handler stuck. |
+ ResumeIfDeferred(); |
+ } else if (completed_during_write_) { |
bool defer = false; |
next_handler_->OnResponseCompleted(request_id, |
completed_status_, |
completed_security_info_, |
&defer); |
- if (!defer) { |
+ if (!defer) |
ResumeIfDeferred(); |
- } else { |
- did_defer_ = false; |
- } |
} |
} |
bool RedirectToFileResourceHandler::WriteMore() { |
- DCHECK(writer_); |
+ DCHECK(file_stream_.get()); |
for (;;) { |
if (write_cursor_ == buf_->offset()) { |
// We've caught up to the network load, but it may be in the process of |
@@ -331,7 +220,7 @@ |
} |
return true; |
} |
- if (writer_->is_writing()) |
+ if (write_callback_pending_) |
return true; |
DCHECK(write_cursor_ < buf_->offset()); |
@@ -353,9 +242,15 @@ |
buf_.get(), buf_->StartOfBuffer() + write_cursor_); |
int write_len = buf_->offset() - write_cursor_; |
- int rv = writer_->Write(wrapped.get(), write_len); |
- if (rv == net::ERR_IO_PENDING) |
+ int rv = file_stream_->Write( |
+ wrapped.get(), |
+ write_len, |
+ base::Bind(&RedirectToFileResourceHandler::DidWriteToFile, |
+ base::Unretained(this))); |
+ if (rv == net::ERR_IO_PENDING) { |
+ write_callback_pending_ = true; |
return true; |
+ } |
if (rv <= 0) |
return false; |
next_handler_->OnDataDownloaded(GetRequestID(), rv); |