diff options
author | ericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-07 19:55:00 +0000 |
---|---|---|
committer | ericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-07 19:55:00 +0000 |
commit | 368ddad78d3f2eac1ea7a92cfb9716ecd0166279 (patch) | |
tree | 12991a1cf9204760a254dfacd954158035e4d52b /webkit/fileapi | |
parent | c1973bdf0c94687f96155af8249358a4b585e381 (diff) | |
download | chromium_src-368ddad78d3f2eac1ea7a92cfb9716ecd0166279.zip chromium_src-368ddad78d3f2eac1ea7a92cfb9716ecd0166279.tar.gz chromium_src-368ddad78d3f2eac1ea7a92cfb9716ecd0166279.tar.bz2 |
Add the actual writing-files part of FileWriter.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/3476002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61833 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/fileapi')
-rw-r--r-- | webkit/fileapi/file_system_operation.cc | 60 | ||||
-rw-r--r-- | webkit/fileapi/file_system_operation.h | 19 | ||||
-rw-r--r-- | webkit/fileapi/file_writer_delegate.cc | 164 | ||||
-rw-r--r-- | webkit/fileapi/file_writer_delegate.h | 71 | ||||
-rw-r--r-- | webkit/fileapi/webkit_fileapi.gypi | 2 |
5 files changed, 305 insertions, 11 deletions
diff --git a/webkit/fileapi/file_system_operation.cc b/webkit/fileapi/file_system_operation.cc index 29336a8..29690b9 100644 --- a/webkit/fileapi/file_system_operation.cc +++ b/webkit/fileapi/file_system_operation.cc @@ -5,8 +5,9 @@ #include "webkit/fileapi/file_system_operation.h" #include "base/time.h" -#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_context.h" #include "webkit/fileapi/file_system_callback_dispatcher.h" +#include "webkit/fileapi/file_writer_delegate.h" namespace fileapi { @@ -24,6 +25,8 @@ FileSystemOperation::FileSystemOperation( } FileSystemOperation::~FileSystemOperation() { + if (file_writer_delegate_.get()) + base::FileUtilProxy::Close(proxy_, file_writer_delegate_->file(), NULL); } void FileSystemOperation::CreateFile(const FilePath& path, @@ -130,6 +133,7 @@ void FileSystemOperation::Remove(const FilePath& path, bool recursive) { } void FileSystemOperation::Write( + scoped_refptr<URLRequestContext> url_request_context, const FilePath& path, const GURL& blob_url, int64 offset) { @@ -137,7 +141,28 @@ void FileSystemOperation::Write( DCHECK(kOperationNone == pending_operation_); pending_operation_ = kOperationWrite; #endif - NOTREACHED(); + DCHECK(blob_url.is_valid()); + file_writer_delegate_.reset(new FileWriterDelegate(this, offset)); + blob_request_.reset(new URLRequest(blob_url, file_writer_delegate_.get())); + blob_request_->set_context(url_request_context); + base::FileUtilProxy::CreateOrOpen( + proxy_, + path, + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC, + callback_factory_.NewCallback( + &FileSystemOperation::OnFileOpenedForWrite)); +} + +void FileSystemOperation::OnFileOpenedForWrite( + base::PlatformFileError rv, + base::PassPlatformFile file, + bool created) { + if (base::PLATFORM_FILE_OK != rv) { + dispatcher_->DidFail(rv); + return; + } + file_writer_delegate_->Start(file.ReleaseValue(), blob_request_.get()); } void FileSystemOperation::Truncate(const FilePath& path, int64 length) { @@ -166,15 +191,32 @@ void FileSystemOperation::TouchFile(const FilePath& path, // We can only get here on a write or truncate that's not yet completed. // We don't support cancelling any other operation at this time. void FileSystemOperation::Cancel(FileSystemOperation* cancel_operation) { + if (file_writer_delegate_.get()) { #ifndef NDEBUG - DCHECK(kOperationTruncate == pending_operation_); - // FIXME(ericu): Cancelling for writes coming soon. + DCHECK(kOperationWrite == pending_operation_); #endif - // We're cancelling a truncate operation, but we can't actually stop it - // since it's been proxied to another thread. We need to save the - // cancel_operation so that when the truncate returns, it can see that it's - // been cancelled, report it, and report that the cancel has succeeded. - cancel_operation_ = cancel_operation; + // Writes are done without proxying through FileUtilProxy after the initial + // opening of the PlatformFile. All state changes are done on this thread, + // so we're guaranteed to be able to shut down atomically. We do need to + // check that the file has been opened [which means the blob_request_ has + // been created], so we know how much we need to do. + if (blob_request_.get()) + // This halts any calls to file_writer_delegate_ from blob_request_. + blob_request_->Cancel(); + + // This deletes us, and by proxy deletes file_writer_delegate_ if any. + dispatcher_->DidFail(base::PLATFORM_FILE_ERROR_ABORT); + cancel_operation->dispatcher_->DidSucceed(); + } else { +#ifndef NDEBUG + DCHECK(kOperationTruncate == pending_operation_); +#endif + // We're cancelling a truncate operation, but we can't actually stop it + // since it's been proxied to another thread. We need to save the + // cancel_operation so that when the truncate returns, it can see that it's + // been cancelled, report it, and report that the cancel has succeeded. + cancel_operation_ = cancel_operation; + } } void FileSystemOperation::DidCreateFileExclusive( diff --git a/webkit/fileapi/file_system_operation.h b/webkit/fileapi/file_system_operation.h index ba65b7d..bfa4141 100644 --- a/webkit/fileapi/file_system_operation.h +++ b/webkit/fileapi/file_system_operation.h @@ -14,16 +14,20 @@ #include "base/ref_counted.h" #include "base/scoped_callback_factory.h" #include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" namespace base { class Time; } class GURL; +class URLRequest; +class URLRequestContext; namespace fileapi { class FileSystemCallbackDispatcher; +class FileWriterDelegate; // This class is designed to serve one-time file system operation per instance. // Only one method(CreateFile, CreateDirectory, Copy, Move, DirectoryExists, @@ -58,7 +62,9 @@ class FileSystemOperation { void Remove(const FilePath& path, bool recursive); - void Write(const FilePath& path, const GURL& blob_url, int64 offset); + void Write( + scoped_refptr<URLRequestContext> url_request_context, + const FilePath& path, const GURL& blob_url, int64 offset); void Truncate(const FilePath& path, int64 length); @@ -107,10 +113,19 @@ class FileSystemOperation { void DidTouchFile(base::PlatformFileError rv); - scoped_ptr<FileSystemCallbackDispatcher> dispatcher_; + // Helper for Write(). + void OnFileOpenedForWrite( + base::PlatformFileError rv, + base::PassPlatformFile file, + bool created); + scoped_ptr<FileSystemCallbackDispatcher> dispatcher_; base::ScopedCallbackFactory<FileSystemOperation> callback_factory_; + // These are all used only by Write(). + friend class FileWriterDelegate; + scoped_ptr<FileWriterDelegate> file_writer_delegate_; + scoped_ptr<URLRequest> blob_request_; FileSystemOperation* cancel_operation_; #ifndef NDEBUG diff --git a/webkit/fileapi/file_writer_delegate.cc b/webkit/fileapi/file_writer_delegate.cc new file mode 100644 index 0000000..d84d22f32 --- /dev/null +++ b/webkit/fileapi/file_writer_delegate.cc @@ -0,0 +1,164 @@ +// 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 "webkit/fileapi/file_writer_delegate.h" + +#include "base/message_loop.h" +#include "net/base/net_errors.h" +#include "webkit/fileapi/file_system_operation.h" + +namespace fileapi { + +static const int kReadBufSize = 32768; + +FileWriterDelegate::FileWriterDelegate( + FileSystemOperation* file_system_operation, + int64 offset) + : file_system_operation_(file_system_operation), + file_(base::kInvalidPlatformFileValue), + offset_(offset), + bytes_read_backlog_(0), + bytes_written_(0), + bytes_read_(0), + io_buffer_(new net::IOBufferWithSize(kReadBufSize)), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +FileWriterDelegate::~FileWriterDelegate() { +} + +void FileWriterDelegate::Start(base::PlatformFile file, URLRequest* request) { + file_ = file; + request_ = request; + file_stream_.reset( + new net::FileStream( + file, + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC)); + request_->Start(); +} + +void FileWriterDelegate::OnReceivedRedirect( + URLRequest* request, const GURL& new_url, bool* defer_redirect) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnAuthRequired( + URLRequest* request, net::AuthChallengeInfo* auth_info) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnCertificateRequested( + URLRequest* request, net::SSLCertRequestInfo* cert_request_info) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnSSLCertificateError( + URLRequest* request, int cert_error, net::X509Certificate* cert) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnResponseStarted(URLRequest* request) { + DCHECK_EQ(request_, request); + if (!request->status().is_success()) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + int64 error = file_stream_->Seek(net::FROM_BEGIN, offset_); + if (error != offset_) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + Read(); +} + +void FileWriterDelegate::OnReadCompleted(URLRequest* request, int bytes_read) { + DCHECK_EQ(request_, request); + if (!request->status().is_success()) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + OnDataReceived(bytes_read); +} + +void FileWriterDelegate::Read() { + bytes_written_ = 0; + bytes_read_ = 0; + if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &FileWriterDelegate::OnDataReceived, bytes_read_)); + } else if (!request_->status().is_io_pending()) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + } +} + +void FileWriterDelegate::OnDataReceived(int bytes_read) { + bytes_read_ = bytes_read; + if (!bytes_read_) { // We're done. + OnProgress(0, true); + } else { + // This could easily be optimized to rotate between a pool of buffers, so + // that we could read and write at the same time. It's not yet clear that + // it's necessary. + Write(); + } +} + +void FileWriterDelegate::Write() { + int write_response = file_stream_->Write( + io_buffer_->data() + bytes_written_, + bytes_read_ - bytes_written_, + callback_factory_.NewCallback(&FileWriterDelegate::OnDataWritten)); + if (write_response > 0) + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &FileWriterDelegate::OnDataWritten, write_response)); + else if (net::ERR_IO_PENDING != write_response) + OnError(base::PLATFORM_FILE_ERROR_FAILED); +} + +void FileWriterDelegate::OnDataWritten(int write_response) { + if (write_response > 0) { + OnProgress(write_response, false); + bytes_written_ += write_response; + if (bytes_written_ == bytes_read_) + Read(); + else + Write(); + } else { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + } +} + +void FileWriterDelegate::OnError(base::PlatformFileError error) { + request_->Cancel(); + file_system_operation_->DidWrite(error, 0, true); +} + +void FileWriterDelegate::OnProgress(int bytes_read, bool done) { + DCHECK(bytes_read + bytes_read_backlog_ >= bytes_read_backlog_); + static const int kMinProgressDelayMS = 200; + base::Time currentTime = base::Time::Now(); + if (done || last_progress_event_time_.is_null() || + (currentTime - last_progress_event_time_).InMilliseconds() > + kMinProgressDelayMS) { + file_system_operation_->DidWrite(base::PLATFORM_FILE_OK, + bytes_read + bytes_read_backlog_, done); + last_progress_event_time_ = currentTime; + bytes_read_backlog_ = 0; + } else { + bytes_read_backlog_ += bytes_read; + } +} + +} // namespace fileapi + diff --git a/webkit/fileapi/file_writer_delegate.h b/webkit/fileapi/file_writer_delegate.h new file mode 100644 index 0000000..ed35ce6 --- /dev/null +++ b/webkit/fileapi/file_writer_delegate.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef WEBKIT_FILEAPI_FILE_WRITER_DELEGATE_H_ +#define WEBKIT_FILEAPI_FILE_WRITER_DELEGATE_H_ + +#include "base/file_path.h" +#include "base/platform_file.h" +#include "base/ref_counted.h" +#include "base/scoped_callback_factory.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/time.h" +#include "net/base/file_stream.h" +#include "net/base/io_buffer.h" +#include "net/url_request/url_request.h" + +namespace fileapi { + +class FileSystemOperation; + +class FileWriterDelegate : public URLRequest::Delegate { + public: + FileWriterDelegate( + FileSystemOperation* write_operation, + int64 offset); + virtual ~FileWriterDelegate(); + + void Start(base::PlatformFile file, URLRequest* request); + base::PlatformFile file() { + return file_; + } + + virtual void OnReceivedRedirect( + URLRequest* request, const GURL& new_url, bool* defer_redirect); + virtual void OnAuthRequired( + URLRequest* request, net::AuthChallengeInfo* auth_info); + virtual void OnCertificateRequested( + URLRequest* request, net::SSLCertRequestInfo* cert_request_info); + virtual void OnSSLCertificateError( + URLRequest* request, int cert_error, net::X509Certificate* cert); + virtual void OnResponseStarted(URLRequest* request); + virtual void OnReadCompleted(URLRequest* request, int bytes_read); + + private: + void Read(); + void OnDataReceived(int bytes_read); + void Write(); + void OnDataWritten(int write_response); + void OnError(base::PlatformFileError error); + void OnProgress(int bytes_read, bool done); + + FileSystemOperation* file_system_operation_; + base::PlatformFile file_; + int64 offset_; + base::Time last_progress_event_time_; + int bytes_read_backlog_; + int bytes_written_; + int bytes_read_; + scoped_refptr<net::IOBufferWithSize> io_buffer_; + scoped_ptr<net::FileStream> file_stream_; + URLRequest* request_; + base::ScopedCallbackFactory<FileWriterDelegate> callback_factory_; + ScopedRunnableMethodFactory<FileWriterDelegate> method_factory_; +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_FILE_WRITER_DELEGATE_H_ + diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index b94cf9e..a795da4 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -18,6 +18,8 @@ 'file_system_operation.cc', 'file_system_operation.h', 'file_system_types.h', + 'file_writer_delegate.cc', + 'file_writer_delegate.h', ], 'conditions': [ ['inside_chromium_build==0', { |