summaryrefslogtreecommitdiffstats
path: root/webkit/fileapi
diff options
context:
space:
mode:
authorericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 23:24:38 +0000
committerericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 23:24:38 +0000
commitbd1523876a769c375e0a40b8cfe8c999f40e6e99 (patch)
tree2a2f91b4feee685386936f897f84a41d99d8e721 /webkit/fileapi
parente7dd6d8bb28270d49684714aa03f8162ab558bc6 (diff)
downloadchromium_src-bd1523876a769c375e0a40b8cfe8c999f40e6e99.zip
chromium_src-bd1523876a769c375e0a40b8cfe8c999f40e6e99.tar.gz
chromium_src-bd1523876a769c375e0a40b8cfe8c999f40e6e99.tar.bz2
Resubmit of http://codereview.chromium.org/3476002/show but without the crashing bug.
BUG=none TEST=none Review URL: http://codereview.chromium.org/3618016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61879 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/fileapi')
-rw-r--r--webkit/fileapi/file_system_operation.cc60
-rw-r--r--webkit/fileapi/file_system_operation.h19
-rw-r--r--webkit/fileapi/file_system_operation_unittest.cc2
-rw-r--r--webkit/fileapi/file_writer_delegate.cc164
-rw-r--r--webkit/fileapi/file_writer_delegate.h71
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi2
6 files changed, 307 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_system_operation_unittest.cc b/webkit/fileapi/file_system_operation_unittest.cc
index 9b07c03..00e13ad 100644
--- a/webkit/fileapi/file_system_operation_unittest.cc
+++ b/webkit/fileapi/file_system_operation_unittest.cc
@@ -727,6 +727,8 @@ TEST_F(FileSystemOperationTest, TestRemoveSuccess) {
EXPECT_EQ(request_id_, mock_dispatcher_->request_id());
}
+// TODO(ericu): Add tests for Write, Cancel.
+
TEST_F(FileSystemOperationTest, TestTruncate) {
ScopedTempDir dir;
ASSERT_TRUE(dir.CreateUniqueTempDir());
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', {