summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 19:55:00 +0000
committerericu@google.com <ericu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 19:55:00 +0000
commit368ddad78d3f2eac1ea7a92cfb9716ecd0166279 (patch)
tree12991a1cf9204760a254dfacd954158035e4d52b
parentc1973bdf0c94687f96155af8249358a4b585e381 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/file_system/file_system_dispatcher_host.cc14
-rw-r--r--chrome/browser/file_system/file_system_dispatcher_host.h11
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc3
-rw-r--r--chrome/browser/worker_host/worker_process_host.cc3
-rw-r--r--chrome/common/file_system/webfilewriter_impl.cc66
-rw-r--r--chrome/common/file_system/webfilewriter_impl.h28
-rw-r--r--webkit/fileapi/file_system_operation.cc60
-rw-r--r--webkit/fileapi/file_system_operation.h19
-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
11 files changed, 391 insertions, 50 deletions
diff --git a/chrome/browser/file_system/file_system_dispatcher_host.cc b/chrome/browser/file_system/file_system_dispatcher_host.cc
index 177c98d..e81fc27 100644
--- a/chrome/browser/file_system/file_system_dispatcher_host.cc
+++ b/chrome/browser/file_system/file_system_dispatcher_host.cc
@@ -13,9 +13,11 @@
#include "chrome/browser/file_system/file_system_host_context.h"
#include "chrome/browser/host_content_settings_map.h"
#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/common/net/url_request_context_getter.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/render_messages_params.h"
#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_context.h"
// A class to hold an ongoing openFileSystem completion task.
struct OpenFileSystemCompletionTask {
@@ -69,12 +71,14 @@ struct OpenFileSystemCompletionTask {
FileSystemDispatcherHost::FileSystemDispatcherHost(
IPC::Message::Sender* sender,
FileSystemHostContext* file_system_host_context,
- HostContentSettingsMap* host_content_settings_map)
+ HostContentSettingsMap* host_content_settings_map,
+ URLRequestContextGetter* request_context_getter)
: message_sender_(sender),
process_handle_(0),
shutdown_(false),
context_(file_system_host_context),
- host_content_settings_map_(host_content_settings_map) {
+ host_content_settings_map_(host_content_settings_map),
+ request_context_getter_(request_context_getter) {
DCHECK(message_sender_);
}
@@ -87,6 +91,9 @@ void FileSystemDispatcherHost::Init(base::ProcessHandle process_handle) {
DCHECK(!process_handle_);
DCHECK(process_handle);
process_handle_ = process_handle;
+ DCHECK(request_context_getter_.get());
+ DCHECK(!request_context_.get());
+ request_context_ = request_context_getter_->GetURLRequestContext();
}
void FileSystemDispatcherHost::Shutdown() {
@@ -214,7 +221,8 @@ void FileSystemDispatcherHost::OnWrite(
int64 offset) {
if (!CheckValidFileSystemPath(path, request_id))
return;
- GetNewOperation(request_id)->Write(path, blob_url, offset);
+ GetNewOperation(request_id)->Write(
+ request_context_, path, blob_url, offset);
}
void FileSystemDispatcherHost::OnTruncate(
diff --git a/chrome/browser/file_system/file_system_dispatcher_host.h b/chrome/browser/file_system/file_system_dispatcher_host.h
index 68110be..201ade6 100644
--- a/chrome/browser/file_system/file_system_dispatcher_host.h
+++ b/chrome/browser/file_system/file_system_dispatcher_host.h
@@ -26,13 +26,17 @@ class GURL;
class HostContentSettingsMap;
class Receiver;
class ResourceMessageFilter;
+class URLRequestContextGetter;
class FileSystemDispatcherHost
: public base::RefCountedThreadSafe<FileSystemDispatcherHost> {
public:
+ // TODO(ericu): Split this into two constructors when adding worker FileWriter
+ // support.
FileSystemDispatcherHost(IPC::Message::Sender* sender,
FileSystemHostContext* file_system_host_context,
- HostContentSettingsMap* host_content_settings_map);
+ HostContentSettingsMap* host_content_settings_map,
+ URLRequestContextGetter* url_request_context_getter);
~FileSystemDispatcherHost();
void Init(base::ProcessHandle process_handle);
void Shutdown();
@@ -97,6 +101,11 @@ class FileSystemDispatcherHost
typedef IDMap<fileapi::FileSystemOperation, IDMapOwnPointer> OperationsMap;
OperationsMap operations_;
+ // This holds the URLRequestContextGetter until Init() can be called from the
+ // IO thread, which will extract the URLRequestContext from it.
+ scoped_refptr<URLRequestContextGetter> request_context_getter_;
+ scoped_refptr<URLRequestContext> request_context_;
+
DISALLOW_COPY_AND_ASSIGN(FileSystemDispatcherHost);
};
diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc
index 0e05808..a03cfe1 100644
--- a/chrome/browser/renderer_host/resource_message_filter.cc
+++ b/chrome/browser/renderer_host/resource_message_filter.cc
@@ -257,7 +257,8 @@ ResourceMessageFilter::ResourceMessageFilter(
ALLOW_THIS_IN_INITIALIZER_LIST(file_system_dispatcher_host_(
new FileSystemDispatcherHost(this,
profile->GetFileSystemHostContext(),
- profile->GetHostContentSettingsMap()))),
+ profile->GetHostContentSettingsMap(),
+ profile->GetRequestContext()))),
ALLOW_THIS_IN_INITIALIZER_LIST(blob_dispatcher_host_(
new BlobDispatcherHost(
this->id(), profile->GetBlobStorageContext()))) {
diff --git a/chrome/browser/worker_host/worker_process_host.cc b/chrome/browser/worker_host/worker_process_host.cc
index 4532e36..12dfe2d 100644
--- a/chrome/browser/worker_host/worker_process_host.cc
+++ b/chrome/browser/worker_host/worker_process_host.cc
@@ -72,7 +72,8 @@ WorkerProcessHost::WorkerProcessHost(
ALLOW_THIS_IN_INITIALIZER_LIST(file_system_dispatcher_host_(
new FileSystemDispatcherHost(this,
request_context->file_system_host_context(),
- request_context->host_content_settings_map()))) {
+ request_context->host_content_settings_map(),
+ NULL /* TODO(ericu)*/))) {
next_route_id_callback_.reset(NewCallbackWithReturnValue(
WorkerService::GetInstance(), &WorkerService::next_worker_route_id));
db_dispatcher_host_ = new DatabaseDispatcherHost(
diff --git a/chrome/common/file_system/webfilewriter_impl.cc b/chrome/common/file_system/webfilewriter_impl.cc
index f579e25..6314c91 100644
--- a/chrome/common/file_system/webfilewriter_impl.cc
+++ b/chrome/common/file_system/webfilewriter_impl.cc
@@ -11,6 +11,44 @@
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "webkit/glue/webkit_glue.h"
+class WebFileWriterImpl::FileSystemCallbackDispatcherImpl :
+ public fileapi::FileSystemCallbackDispatcher {
+ public:
+ explicit FileSystemCallbackDispatcherImpl(
+ const base::WeakPtr<WebFileWriterImpl>& impl) : m_impl(impl) {
+ }
+ virtual ~FileSystemCallbackDispatcherImpl() {
+ }
+
+ virtual void DidReadMetadata(const base::PlatformFileInfo&) {
+ NOTREACHED();
+ }
+ virtual void DidReadDirectory(
+ const std::vector<base::file_util_proxy::Entry>& entries,
+ bool has_more) {
+ NOTREACHED();
+ }
+ virtual void DidOpenFileSystem(const std::string& name,
+ const FilePath& root_path) {
+ NOTREACHED();
+ }
+ virtual void DidSucceed() {
+ if (m_impl)
+ m_impl->DidSucceed();
+ }
+ virtual void DidFail(base::PlatformFileError error_code) {
+ if (m_impl)
+ m_impl->DidFail(error_code);
+ }
+ virtual void DidWrite(int64 bytes, bool complete) {
+ if (m_impl)
+ m_impl->DidWrite(bytes, complete);
+ }
+
+ private:
+ base::WeakPtr<WebFileWriterImpl> m_impl;
+};
+
WebFileWriterImpl::WebFileWriterImpl(
const WebKit::WebString& path, WebKit::WebFileWriterClient* client)
: path_(webkit_glue::WebStringToFilePath(path)),
@@ -30,7 +68,9 @@ void WebFileWriterImpl::truncate(
operation_ = kOperationTruncate;
FileSystemDispatcher* dispatcher =
ChildThread::current()->file_system_dispatcher();
- dispatcher->Truncate(path_, length, &request_id_, this);
+ dispatcher->Truncate(
+ path_, length, &request_id_,
+ new FileSystemCallbackDispatcherImpl(AsWeakPtr()));
}
void WebFileWriterImpl::write(
@@ -41,7 +81,9 @@ void WebFileWriterImpl::write(
operation_ = kOperationWrite;
FileSystemDispatcher* dispatcher =
ChildThread::current()->file_system_dispatcher();
- dispatcher->Write(path_, blob_url, position, &request_id_, this);
+ dispatcher->Write(
+ path_, blob_url, position, &request_id_,
+ new FileSystemCallbackDispatcherImpl(AsWeakPtr()));
}
// When we cancel a write/truncate, we always get back the result of the write
@@ -65,15 +107,8 @@ void WebFileWriterImpl::cancel() {
cancel_state_ = kCancelSent;
FileSystemDispatcher* dispatcher =
ChildThread::current()->file_system_dispatcher();
- dispatcher->Cancel(request_id_, this);
-}
-
-void WebFileWriterImpl::FinishCancel() {
- DCHECK(kCancelReceivedWriteResponse == cancel_state_);
- DCHECK(kOperationNone != operation_);
- cancel_state_ = kCancelNotInProgress;
- operation_ = kOperationNone;
- client_->didFail(WebKit::WebFileErrorAbort);
+ dispatcher->Cancel(
+ request_id_, new FileSystemCallbackDispatcherImpl(AsWeakPtr()));
}
void WebFileWriterImpl::DidSucceed() {
@@ -148,3 +183,12 @@ void WebFileWriterImpl::DidWrite(int64 bytes, bool complete) {
NOTREACHED();
}
}
+
+void WebFileWriterImpl::FinishCancel() {
+ DCHECK(kCancelReceivedWriteResponse == cancel_state_);
+ DCHECK(kOperationNone != operation_);
+ cancel_state_ = kCancelNotInProgress;
+ operation_ = kOperationNone;
+ client_->didFail(WebKit::WebFileErrorAbort);
+}
+
diff --git a/chrome/common/file_system/webfilewriter_impl.h b/chrome/common/file_system/webfilewriter_impl.h
index 574440f..6f1c0a7 100644
--- a/chrome/common/file_system/webfilewriter_impl.h
+++ b/chrome/common/file_system/webfilewriter_impl.h
@@ -5,10 +5,8 @@
#ifndef CHROME_COMMON_FILE_SYSTEM_WEBFILEWRITER_IMPL_H_
#define CHROME_COMMON_FILE_SYSTEM_WEBFILEWRITER_IMPL_H_
-#include <string>
-#include <vector>
-
#include "base/basictypes.h"
+#include "base/weak_ptr.h"
#include "chrome/common/file_system/file_system_dispatcher.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFileWriter.h"
@@ -20,7 +18,7 @@ class WebURL;
class WebFileWriterImpl
: public WebKit::WebFileWriter,
- public fileapi::FileSystemCallbackDispatcher {
+ public base::SupportsWeakPtr<WebFileWriterImpl> {
public:
WebFileWriterImpl(
const WebKit::WebString& path, WebKit::WebFileWriterClient* client);
@@ -31,24 +29,8 @@ class WebFileWriterImpl
virtual void write(long long position, const WebKit::WebURL& blobURL);
virtual void cancel();
- // FileSystemCallbackDispatcher implementation
- virtual void DidReadMetadata(const base::PlatformFileInfo&) {
- NOTREACHED();
- }
- virtual void DidReadDirectory(
- const std::vector<base::file_util_proxy::Entry>& entries,
- bool has_more) {
- NOTREACHED();
- }
- virtual void DidOpenFileSystem(const std::string& name,
- const FilePath& root_path) {
- NOTREACHED();
- }
- virtual void DidSucceed();
- virtual void DidFail(base::PlatformFileError error_code);
- virtual void DidWrite(int64 bytes, bool complete);
-
private:
+ class FileSystemCallbackDispatcherImpl;
enum OperationType {
kOperationNone,
kOperationWrite,
@@ -61,6 +43,9 @@ class WebFileWriterImpl
kCancelReceivedWriteResponse,
};
+ void DidSucceed();
+ void DidFail(base::PlatformFileError error_code);
+ void DidWrite(int64 bytes, bool complete);
void FinishCancel();
FilePath path_;
@@ -71,4 +56,3 @@ class WebFileWriterImpl
};
#endif // CHROME_COMMON_FILE_SYSTEM_WEBFILEWRITER_IMPL_H_
-
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', {