summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-22 09:19:31 +0000
committerkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-22 09:19:31 +0000
commit397a1f4db17560722bcb45108ccf7bf07fd5139f (patch)
tree4527d92f136e2e32c2cea613e69e1cd468cab89a
parent1adcd7531107e881f31ce3f1cb18a87d94935347 (diff)
downloadchromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.zip
chromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.tar.gz
chromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.tar.bz2
Implement SandboxFileWriter and rewrite FileWriterDelegate to use it
BUG=127529 TEST=FileWriterDelegate*, FileSystemOperationWrite* Review URL: https://chromiumcodereview.appspot.com/10387054 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138236 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--webkit/fileapi/file_system_operation.cc54
-rw-r--r--webkit/fileapi/file_system_operation.h8
-rw-r--r--webkit/fileapi/file_writer.h14
-rw-r--r--webkit/fileapi/file_writer_delegate.cc192
-rw-r--r--webkit/fileapi/file_writer_delegate.h26
-rw-r--r--webkit/fileapi/file_writer_delegate_unittest.cc195
-rw-r--r--webkit/fileapi/sandbox_file_writer.cc248
-rw-r--r--webkit/fileapi/sandbox_file_writer.h90
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi2
9 files changed, 495 insertions, 334 deletions
diff --git a/webkit/fileapi/file_system_operation.cc b/webkit/fileapi/file_system_operation.cc
index 0ca2490..95ee278 100644
--- a/webkit/fileapi/file_system_operation.cc
+++ b/webkit/fileapi/file_system_operation.cc
@@ -19,6 +19,7 @@
#include "webkit/fileapi/file_system_types.h"
#include "webkit/fileapi/file_system_util.h"
#include "webkit/fileapi/file_writer_delegate.h"
+#include "webkit/fileapi/sandbox_file_writer.h"
#include "webkit/quota/quota_manager.h"
#include "webkit/quota/quota_types.h"
@@ -79,17 +80,6 @@ FileSystemOperation::TaskParamsForDidGetQuota::TaskParamsForDidGetQuota()
FileSystemOperation::TaskParamsForDidGetQuota::~TaskParamsForDidGetQuota() {}
FileSystemOperation::~FileSystemOperation() {
- if (file_writer_delegate_.get()) {
- FileSystemOperationContext* c =
- new FileSystemOperationContext(operation_context_);
- base::FileUtilProxy::RelayClose(
- file_system_context()->file_task_runner(),
- base::Bind(&FileSystemFileUtil::Close,
- base::Unretained(src_util_),
- base::Owned(c)),
- file_writer_delegate_->file(),
- base::FileUtilProxy::StatusCallback());
- }
}
void FileSystemOperation::CreateFile(const GURL& path_url,
@@ -288,17 +278,15 @@ void FileSystemOperation::Write(
}
DCHECK(blob_url.is_valid());
file_writer_delegate_.reset(new FileWriterDelegate(
- this, src_path_, offset));
+ base::Bind(&FileSystemOperation::DidWrite, weak_factory_.GetWeakPtr()),
+ scoped_ptr<FileWriter>(
+ new SandboxFileWriter(file_system_context(), path_url, offset))));
set_write_callback(callback);
scoped_ptr<net::URLRequest> blob_request(
new net::URLRequest(blob_url, file_writer_delegate_.get()));
blob_request->set_context(url_request_context);
- GetUsageAndQuotaThenRunTask(
- src_path_.origin(), src_path_.type(),
- base::Bind(&FileSystemOperation::DoWrite, weak_factory_.GetWeakPtr(),
- base::Passed(&blob_request)),
- base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 0, true));
+ file_writer_delegate_->Start(blob_request.Pass());
}
void FileSystemOperation::Truncate(const GURL& path_url, int64 length,
@@ -542,23 +530,6 @@ void FileSystemOperation::DoMove(const StatusCallback& callback) {
base::Owned(this), callback));
}
-void FileSystemOperation::DoWrite(scoped_ptr<net::URLRequest> blob_request) {
- int file_flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
-
- // We may get deleted on the way so allocate a new operation context
- // to keep it alive.
- FileSystemOperationContext* write_context = new FileSystemOperationContext(
- operation_context_);
- FileSystemFileUtilProxy::CreateOrOpen(
- write_context, src_util_, src_path_, file_flags,
- base::Bind(&FileSystemOperation::OnFileOpenedForWrite,
- weak_factory_.GetWeakPtr(),
- base::Passed(&blob_request),
- base::Owned(write_context)));
-}
-
void FileSystemOperation::DoTruncate(const StatusCallback& callback,
int64 length) {
FileSystemFileUtilProxy::Truncate(
@@ -671,21 +642,6 @@ void FileSystemOperation::DidOpenFile(
callback.Run(rv, file.ReleaseValue(), peer_handle_);
}
-void FileSystemOperation::OnFileOpenedForWrite(
- scoped_ptr<net::URLRequest> blob_request,
- FileSystemOperationContext* unused,
- base::PlatformFileError rv,
- base::PassPlatformFile file,
- bool created) {
- if (rv != base::PLATFORM_FILE_OK) {
- if (!write_callback_.is_null())
- write_callback_.Run(rv, 0, false);
- delete this;
- return;
- }
- file_writer_delegate_->Start(file.ReleaseValue(), blob_request.Pass());
-}
-
base::PlatformFileError FileSystemOperation::SetUpFileSystemPath(
const GURL& path_url,
FileSystemPath* file_system_path,
diff --git a/webkit/fileapi/file_system_operation.h b/webkit/fileapi/file_system_operation.h
index 1373df4..6036bc6 100644
--- a/webkit/fileapi/file_system_operation.h
+++ b/webkit/fileapi/file_system_operation.h
@@ -174,7 +174,6 @@ class FileSystemOperation : public FileSystemOperationInterface {
bool recursive);
void DoCopy(const StatusCallback& callback);
void DoMove(const StatusCallback& callback);
- void DoWrite(scoped_ptr<net::URLRequest> blob_request);
void DoTruncate(const StatusCallback& callback, int64 length);
void DoOpenFile(const OpenFileCallback& callback, int file_flags);
@@ -218,13 +217,6 @@ class FileSystemOperation : public FileSystemOperationInterface {
base::PassPlatformFile file,
bool created);
- // Helper for Write().
- void OnFileOpenedForWrite(scoped_ptr<net::URLRequest> blob_request,
- FileSystemOperationContext* context_unused,
- base::PlatformFileError rv,
- base::PassPlatformFile file,
- bool created);
-
// Checks the validity of a given |path_url| and and populates
// |path| and |file_util| for |mode|.
base::PlatformFileError SetUpFileSystemPath(
diff --git a/webkit/fileapi/file_writer.h b/webkit/fileapi/file_writer.h
index c28c5d9..109b8c7 100644
--- a/webkit/fileapi/file_writer.h
+++ b/webkit/fileapi/file_writer.h
@@ -16,6 +16,12 @@ class IOBuffer;
namespace fileapi {
// A generic interface for writing to a file-like object.
+//
+// TODO(kinuko): Consider better naming. (http://crbug.com/128483)
+// Note: this does not directly correspond to FileWriter in File API (which
+// is implemented by WebCore::FileWriter), though this class is used to
+// implement a part of it. FileWriterDelegate is NOT a delegate of this
+// class either.
class FileWriter {
public:
// Closes the file. If there's an in-flight operation, it is canceled (i.e.,
@@ -31,6 +37,13 @@ class FileWriter {
// callback will be run on the thread where Write() was called when the write
// has completed.
//
+ // This errors out (either synchronously or via callback) with:
+ // net::ERR_FILE_NOT_FOUND: When the target file is not found.
+ // net::ERR_ACCESS_DENIED: When the target file is a directory or
+ // the writer has no permission on the file.
+ // net::ERR_FILE_NO_SPACE: When the write will result in out of quota
+ // or there is not enough room left on the disk.
+ //
// It is invalid to call Write while there is an in-flight async operation.
virtual int Write(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback) = 0;
@@ -53,4 +66,3 @@ class FileWriter {
} // namespace fileapi
#endif // WEBKIT_FILEAPI_FILE_WRITER_H_
-
diff --git a/webkit/fileapi/file_writer_delegate.cc b/webkit/fileapi/file_writer_delegate.cc
index 283cbe0..d8c33ba 100644
--- a/webkit/fileapi/file_writer_delegate.cc
+++ b/webkit/fileapi/file_writer_delegate.cc
@@ -13,9 +13,7 @@
#include "base/threading/thread_restrictions.h"
#include "net/base/net_errors.h"
#include "webkit/fileapi/file_system_context.h"
-#include "webkit/fileapi/file_system_operation.h"
-#include "webkit/fileapi/file_system_operation_context.h"
-#include "webkit/fileapi/file_system_quota_util.h"
+#include "webkit/fileapi/file_writer.h"
namespace fileapi {
@@ -23,70 +21,30 @@ static const int kReadBufSize = 32768;
namespace {
-typedef base::Callback<void(base::PlatformFileError /* error code */,
- const base::PlatformFileInfo& /* file_info */)>
- InitializeTaskCallback;
-
-class InitializeTask : public base::RefCountedThreadSafe<InitializeTask> {
- public:
- InitializeTask(
- base::PlatformFile file,
- const InitializeTaskCallback& callback)
- : original_loop_(base::MessageLoopProxy::current()),
- error_code_(base::PLATFORM_FILE_OK),
- file_(file),
- callback_(callback) {
- DCHECK_EQ(false, callback.is_null());
- }
-
- bool Start(base::SequencedTaskRunner* task_runner,
- const tracked_objects::Location& from_here) {
- return task_runner->PostTask(
- from_here,
- base::Bind(&InitializeTask::ProcessOnTargetThread, this));
- }
-
- private:
- friend class base::RefCountedThreadSafe<InitializeTask>;
- ~InitializeTask() {}
-
- void RunCallback() {
- callback_.Run(error_code_, file_info_);
+base::PlatformFileError NetErrorToPlatformFileError(int error) {
+// TODO(kinuko): Move this static method to more convenient place.
+ switch (error) {
+ case net::ERR_FILE_NO_SPACE:
+ return base::PLATFORM_FILE_ERROR_NO_SPACE;
+ case net::ERR_FILE_NOT_FOUND:
+ return base::PLATFORM_FILE_ERROR_NOT_FOUND;
+ case net::ERR_ACCESS_DENIED:
+ return base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ default:
+ return base::PLATFORM_FILE_ERROR_FAILED;
}
+}
- void ProcessOnTargetThread() {
- if (!base::GetPlatformFileInfo(file_, &file_info_))
- error_code_ = base::PLATFORM_FILE_ERROR_FAILED;
- original_loop_->PostTask(
- FROM_HERE,
- base::Bind(&InitializeTask::RunCallback, this));
- }
-
- scoped_refptr<base::MessageLoopProxy> original_loop_;
- base::PlatformFileError error_code_;
-
- base::PlatformFile file_;
- InitializeTaskCallback callback_;
-
- base::PlatformFileInfo file_info_;
-};
-
-} // namespace (anonymous)
+} // namespace
FileWriterDelegate::FileWriterDelegate(
- FileSystemOperation* file_system_operation,
- const FileSystemPath& path,
- int64 offset)
- : file_system_operation_(file_system_operation),
- file_(base::kInvalidPlatformFileValue),
- path_(path),
- offset_(offset),
- has_pending_write_(false),
+ const FileSystemOperationInterface::WriteCallback& write_callback,
+ scoped_ptr<FileWriter> file_writer)
+ : write_callback_(write_callback),
+ file_writer_(file_writer.Pass()),
bytes_written_backlog_(0),
bytes_written_(0),
bytes_read_(0),
- total_bytes_written_(0),
- allowed_bytes_to_write_(0),
io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}
@@ -94,44 +52,11 @@ FileWriterDelegate::FileWriterDelegate(
FileWriterDelegate::~FileWriterDelegate() {
}
-void FileWriterDelegate::OnGetFileInfoAndStartRequest(
- scoped_ptr<net::URLRequest> request,
- base::PlatformFileError error,
- const base::PlatformFileInfo& file_info) {
- if (error != base::PLATFORM_FILE_OK) {
- OnError(error);
- return;
- }
- int64 allowed_bytes_growth =
- file_system_operation_context()->allowed_bytes_growth();
- if (allowed_bytes_growth < 0)
- allowed_bytes_growth = 0;
- int64 overlap = file_info.size - offset_;
- allowed_bytes_to_write_ = allowed_bytes_growth;
- if (kint64max - overlap > allowed_bytes_growth)
- allowed_bytes_to_write_ += overlap;
- size_ = file_info.size;
- file_stream_.reset(new net::FileStream(
- file_,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC,
- NULL));
- DCHECK(!request_.get());
+void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request) {
request_ = request.Pass();
request_->Start();
}
-void FileWriterDelegate::Start(base::PlatformFile file,
- scoped_ptr<net::URLRequest> request) {
- file_ = file;
-
- scoped_refptr<InitializeTask> relay = new InitializeTask(
- file_,
- base::Bind(&FileWriterDelegate::OnGetFileInfoAndStartRequest,
- weak_factory_.GetWeakPtr(), base::Passed(&request)));
- relay->Start(file_system_operation_context()->file_task_runner(), FROM_HERE);
-}
-
bool FileWriterDelegate::Cancel() {
if (request_.get()) {
// This halts any callbacks on this delegate.
@@ -139,9 +64,12 @@ bool FileWriterDelegate::Cancel() {
request_->Cancel();
}
- // Return true to finish immediately if we're not writing.
- // Otherwise we'll do the final cleanup in the write callback.
- return !has_pending_write_;
+ const int status = file_writer_->Cancel(
+ base::Bind(&FileWriterDelegate::OnWriteCancelled,
+ weak_factory_.GetWeakPtr()));
+ // Return true to finish immediately if we have no pending writes.
+ // Otherwise we'll do the final cleanup in the Cancel callback.
+ return (status != net::ERR_IO_PENDING);
}
void FileWriterDelegate::OnReceivedRedirect(net::URLRequest* request,
@@ -173,18 +101,10 @@ void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request,
void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) {
DCHECK_EQ(request_.get(), request);
- // file_stream_->Seek() blocks the IO thread.
- // See http://crbug.com/75548.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
if (!request->status().is_success() || request->GetResponseCode() != 200) {
OnError(base::PLATFORM_FILE_ERROR_FAILED);
return;
}
- int64 error = file_stream_->SeekSync(net::FROM_BEGIN, offset_);
- if (error != offset_) {
- OnError(base::PLATFORM_FILE_ERROR_FAILED);
- return;
- }
Read();
}
@@ -201,8 +121,7 @@ void FileWriterDelegate::OnReadCompleted(net::URLRequest* request,
void FileWriterDelegate::Read() {
bytes_written_ = 0;
bytes_read_ = 0;
- if (request_->Read(io_buffer_.get(), io_buffer_->size(),
- &bytes_read_)) {
+ if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&FileWriterDelegate::OnDataReceived,
@@ -226,24 +145,9 @@ void FileWriterDelegate::OnDataReceived(int bytes_read) {
}
void FileWriterDelegate::Write() {
- // allowed_bytes_to_write could be negative if the file size is
- // greater than the current (possibly new) quota.
- // (The UI should clear the entire origin data if the smaller quota size
- // is set in general, though the UI/deletion code is not there yet.)
- DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ ||
- allowed_bytes_to_write_ < 0);
- if (total_bytes_written_ >= allowed_bytes_to_write_) {
- OnError(base::PLATFORM_FILE_ERROR_NO_SPACE);
- return;
- }
-
int64 bytes_to_write = bytes_read_ - bytes_written_;
- if (bytes_to_write > allowed_bytes_to_write_ - total_bytes_written_)
- bytes_to_write = allowed_bytes_to_write_ - total_bytes_written_;
-
- has_pending_write_ = true;
int write_response =
- file_stream_->Write(cursor_,
+ file_writer_->Write(cursor_,
static_cast<int>(bytes_to_write),
base::Bind(&FileWriterDelegate::OnDataWritten,
weak_factory_.GetWeakPtr()));
@@ -253,26 +157,20 @@ void FileWriterDelegate::Write() {
base::Bind(&FileWriterDelegate::OnDataWritten,
weak_factory_.GetWeakPtr(), write_response));
else if (net::ERR_IO_PENDING != write_response)
- OnError(base::PLATFORM_FILE_ERROR_FAILED);
+ OnError(NetErrorToPlatformFileError(write_response));
}
void FileWriterDelegate::OnDataWritten(int write_response) {
- has_pending_write_ = false;
if (write_response > 0) {
- if (request_->status().status() == net::URLRequestStatus::CANCELED) {
- OnProgress(write_response, true);
- return;
- }
OnProgress(write_response, false);
cursor_->DidConsume(write_response);
bytes_written_ += write_response;
- total_bytes_written_ += write_response;
if (bytes_written_ == bytes_read_)
Read();
else
Write();
} else {
- OnError(base::PLATFORM_FILE_ERROR_FAILED);
+ OnError(NetErrorToPlatformFileError(write_response));
}
}
@@ -282,22 +180,11 @@ void FileWriterDelegate::OnError(base::PlatformFileError error) {
request_->Cancel();
}
- file_system_operation_->DidWrite(error, 0, true);
+ write_callback_.Run(error, 0, true);
}
void FileWriterDelegate::OnProgress(int bytes_written, bool done) {
DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_);
- if (quota_util() &&
- bytes_written > 0 &&
- total_bytes_written_ + bytes_written + offset_ > size_) {
- int overlapped = 0;
- if (total_bytes_written_ + offset_ < size_)
- overlapped = size_ - total_bytes_written_ - offset_;
- quota_util()->proxy()->UpdateOriginUsage(
- file_system_operation_->file_system_context()->quota_manager_proxy(),
- path_.origin(), path_.type(),
- bytes_written - overlapped);
- }
static const int kMinProgressDelayMS = 200;
base::Time currentTime = base::Time::Now();
if (done || last_progress_event_time_.is_null() ||
@@ -306,28 +193,15 @@ void FileWriterDelegate::OnProgress(int bytes_written, bool done) {
bytes_written += bytes_written_backlog_;
last_progress_event_time_ = currentTime;
bytes_written_backlog_ = 0;
- if (done && quota_util())
- quota_util()->proxy()->EndUpdateOrigin(path_.origin(), path_.type());
- file_system_operation_->DidWrite(
+ write_callback_.Run(
base::PLATFORM_FILE_OK, bytes_written, done);
return;
}
bytes_written_backlog_ += bytes_written;
}
-FileSystemOperationContext*
-FileWriterDelegate::file_system_operation_context() const {
- DCHECK(file_system_operation_);
- DCHECK(file_system_operation_->file_system_operation_context());
- return file_system_operation_->file_system_operation_context();
-}
-
-FileSystemQuotaUtil* FileWriterDelegate::quota_util() const {
- DCHECK(file_system_operation_);
- DCHECK(file_system_operation_->file_system_context());
- DCHECK(file_system_operation_->file_system_operation_context());
- return file_system_operation_->file_system_context()->GetQuotaUtil(
- path_.type());
+void FileWriterDelegate::OnWriteCancelled(int status) {
+ write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0, true);
}
} // namespace fileapi
diff --git a/webkit/fileapi/file_writer_delegate.h b/webkit/fileapi/file_writer_delegate.h
index 3b58ae0..e2bf74d 100644
--- a/webkit/fileapi/file_writer_delegate.h
+++ b/webkit/fileapi/file_writer_delegate.h
@@ -13,24 +13,23 @@
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
#include "net/url_request/url_request.h"
+#include "webkit/fileapi/file_system_operation_interface.h"
#include "webkit/fileapi/file_system_path.h"
namespace fileapi {
-class FileSystemOperation;
class FileSystemOperationContext;
class FileSystemQuotaUtil;
+class FileWriter;
class FileWriterDelegate : public net::URLRequest::Delegate {
public:
FileWriterDelegate(
- FileSystemOperation* write_operation,
- const FileSystemPath& path,
- int64 offset);
+ const FileSystemOperationInterface::WriteCallback& write_callback,
+ scoped_ptr<FileWriter> file_writer);
virtual ~FileWriterDelegate();
- void Start(base::PlatformFile file,
- scoped_ptr<net::URLRequest> request);
+ void Start(scoped_ptr<net::URLRequest> request);
// Cancels the current write operation. Returns true if it is ok to
// delete this instance immediately. Otherwise this will call
@@ -38,8 +37,6 @@ class FileWriterDelegate : public net::URLRequest::Delegate {
// perform the final cleanup.
bool Cancel();
- base::PlatformFile file() const { return file_; }
-
virtual void OnReceivedRedirect(net::URLRequest* request,
const GURL& new_url,
bool* defer_redirect) OVERRIDE;
@@ -66,25 +63,18 @@ class FileWriterDelegate : public net::URLRequest::Delegate {
void OnDataWritten(int write_response);
void OnError(base::PlatformFileError error);
void OnProgress(int bytes_read, bool done);
+ void OnWriteCancelled(int status);
- FileSystemOperationContext* file_system_operation_context() const;
FileSystemQuotaUtil* quota_util() const;
- FileSystemOperation* file_system_operation_;
- base::PlatformFile file_;
- FileSystemPath path_;
- int64 size_;
- int64 offset_;
- bool has_pending_write_;
+ FileSystemOperationInterface::WriteCallback write_callback_;
+ scoped_ptr<FileWriter> file_writer_;
base::Time last_progress_event_time_;
int bytes_written_backlog_;
int bytes_written_;
int bytes_read_;
- int64 total_bytes_written_;
- int64 allowed_bytes_to_write_;
scoped_refptr<net::IOBufferWithSize> io_buffer_;
scoped_refptr<net::DrainableIOBuffer> cursor_;
- scoped_ptr<net::FileStream> file_stream_;
scoped_ptr<net::URLRequest> request_;
base::WeakPtrFactory<FileWriterDelegate> weak_factory_;
};
diff --git a/webkit/fileapi/file_writer_delegate_unittest.cc b/webkit/fileapi/file_writer_delegate_unittest.cc
index 1ce152e..1ff0e83 100644
--- a/webkit/fileapi/file_writer_delegate_unittest.cc
+++ b/webkit/fileapi/file_writer_delegate_unittest.cc
@@ -13,7 +13,6 @@
#include "base/basictypes.h"
#include "base/bind.h"
-#include "base/file_util_proxy.h"
#include "base/message_loop.h"
#include "base/scoped_temp_dir.h"
#include "googleurl/src/gurl.h"
@@ -24,12 +23,9 @@
#include "testing/platform_test.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_operation.h"
-#include "webkit/fileapi/file_system_operation_context.h"
#include "webkit/fileapi/file_system_test_helper.h"
-#include "webkit/fileapi/file_system_usage_cache.h"
#include "webkit/fileapi/file_writer_delegate.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/fileapi/sandbox_mount_point_provider.h"
+#include "webkit/fileapi/sandbox_file_writer.h"
namespace fileapi {
@@ -81,46 +77,54 @@ class FileWriterDelegateTest : public PlatformTest {
public:
FileWriterDelegateTest()
: loop_(MessageLoop::TYPE_IO),
- test_helper_(GURL("http://example.com"), kFileSystemTypeTest),
- file_(base::kInvalidPlatformFileValue) {}
+ test_helper_(GURL("http://example.com"), kFileSystemTypeTest) {}
protected:
- virtual void SetUp();
- virtual void TearDown();
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
- virtual void SetUpTestHelper(const FilePath& base_dir) {
- test_helper_.SetUp(base_dir, NULL);
+ FileSystemFileUtil* file_util() {
+ return test_helper_.file_util();
}
int64 ComputeCurrentOriginUsage() {
- base::FlushPlatformFile(file_);
return test_helper_.ComputeCurrentOriginUsage();
}
- // Creates and sets up a FileWriterDelegate for writing the given |blob_url|
- // to a file (file_) from |offset| with |allowed_growth| quota setting.
+ FileSystemPath GetFileSystemPath(const char* file_name) const {
+ return test_helper_.CreatePathFromUTF8(file_name);
+ }
+
+ GURL GetFileSystemURL(const char* file_name) const {
+ return test_helper_.GetURLForPath(FilePath().AppendASCII(file_name));
+ }
+
+ FileWriterDelegate* CreateWriterDelegate(
+ const char* test_file_path,
+ int64 offset,
+ int64 allowed_growth,
+ Result* result) {
+ SandboxFileWriter* writer = new SandboxFileWriter(
+ test_helper_.file_system_context(),
+ GetFileSystemURL(test_file_path),
+ offset);
+ writer->set_default_quota(allowed_growth);
+ return new FileWriterDelegate(
+ base::Bind(&Result::DidWrite, base::Unretained(result)),
+ scoped_ptr<FileWriter>(writer));
+ }
+
+ // Creates and sets up a FileWriterDelegate for writing the given |blob_url|,
+ // and creates a new FileWriterDelegate for the file.
void PrepareForWrite(const GURL& blob_url,
int64 offset,
int64 allowed_growth) {
- bool created;
- base::PlatformFileError error_code;
- file_ = base::CreatePlatformFile(
- file_path_,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC,
- &created, &error_code);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error_code);
-
result_.reset(new Result());
- file_writer_delegate_.reset(new FileWriterDelegate(
- CreateNewOperation(result_.get(), allowed_growth),
- test_helper_.CreatePath(file_path_),
- offset));
+ file_writer_delegate_.reset(
+ CreateWriterDelegate("test", offset, allowed_growth, result_.get()));
request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get()));
}
- FileSystemOperation* CreateNewOperation(Result* result, int64 quota);
-
static net::URLRequest::ProtocolFactory Factory;
// This should be alive until the very end of this instance.
@@ -132,11 +136,10 @@ class FileWriterDelegateTest : public PlatformTest {
FileSystemTestOriginHelper test_helper_;
ScopedTempDir dir_;
- FilePath file_path_;
- PlatformFile file_;
static const char* content_;
};
+
const char* FileWriterDelegateTest::content_ = NULL;
namespace {
@@ -200,29 +203,26 @@ net::URLRequestJob* FileWriterDelegateTest::Factory(
void FileWriterDelegateTest::SetUp() {
ASSERT_TRUE(dir_.CreateUniqueTempDir());
FilePath base_dir = dir_.path().AppendASCII("filesystem");
- SetUpTestHelper(base_dir);
- ASSERT_TRUE(file_util::CreateTemporaryFileInDir(
- test_helper_.GetOriginRootPath(), &file_path_));
+ test_helper_.SetUp(base_dir, NULL);
+
+ scoped_ptr<FileSystemOperationContext> context(
+ test_helper_.NewOperationContext());
+ context->set_allowed_bytes_growth(kint64max);
+ bool created = false;
+ base::PlatformFileError error = file_util()->EnsureFileExists(
+ context.get(),
+ GetFileSystemPath("test"),
+ &created);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_TRUE(created);
net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory);
}
void FileWriterDelegateTest::TearDown() {
net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL);
- base::ClosePlatformFile(file_);
test_helper_.TearDown();
}
-FileSystemOperation* FileWriterDelegateTest::CreateNewOperation(
- Result* result, int64 quota) {
- FileSystemOperation* operation = test_helper_.NewOperation();
- operation->set_write_callback(base::Bind(&Result::DidWrite,
- base::Unretained(result)));
- FileSystemOperationContext* context =
- operation->file_system_operation_context();
- context->set_allowed_bytes_growth(quota);
- return operation;
-}
-
TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
const GURL kBlobURL("blob:nolimit");
content_ = kData;
@@ -230,16 +230,16 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
PrepareForWrite(kBlobURL, 0, quota::QuotaManager::kNoLimit);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
-
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
-
- file_writer_delegate_.reset();
}
TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
@@ -249,16 +249,16 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
PrepareForWrite(kBlobURL, 0, kAllowedGrowth);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
- file_writer_delegate_.reset();
-
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
}
TEST_F(FileWriterDelegateTest, WriteFailureByQuota) {
@@ -268,13 +268,14 @@ TEST_F(FileWriterDelegateTest, WriteFailureByQuota) {
PrepareForWrite(kBlobURL, 0, kAllowedGrowth);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
- file_writer_delegate_.reset();
-
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status());
EXPECT_TRUE(result_->complete());
@@ -287,13 +288,14 @@ TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) {
PrepareForWrite(kBlobURL, 0, kAllowedGrowth);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
- file_writer_delegate_.reset();
-
EXPECT_EQ(kAllowedGrowth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
EXPECT_TRUE(result_->complete());
@@ -304,18 +306,13 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
scoped_ptr<net::URLRequest> request2;
scoped_ptr<Result> result2;
- FilePath file_path2;
- PlatformFile file2;
- bool created;
- base::PlatformFileError error_code;
- ASSERT_TRUE(file_util::CreateTemporaryFileInDir(
- test_helper_.GetOriginRootPath(), &file_path2));
- file2 = base::CreatePlatformFile(
- file_path2,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC,
- &created, &error_code);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error_code);
+ scoped_ptr<FileSystemOperationContext> context(
+ test_helper_.NewOperationContext());
+ bool created = false;
+ file_util()->EnsureFileExists(context.get(),
+ GetFileSystemPath("test2"),
+ &created);
+ ASSERT_TRUE(created);
const GURL kBlobURL("blob:nolimitconcurrent");
const GURL kBlobURL2("blob:nolimitconcurrent2");
@@ -325,32 +322,29 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
// Credate another FileWriterDelegate for concurrent write.
result2.reset(new Result());
- file_writer_delegate2.reset(new FileWriterDelegate(
- CreateNewOperation(result2.get(), quota::QuotaManager::kNoLimit),
- test_helper_.CreatePath(file_path2), 0));
+ file_writer_delegate2.reset(CreateWriterDelegate(
+ "test2", 0, quota::QuotaManager::kNoLimit, result2.get()));
request2.reset(new net::URLRequest(kBlobURL2, file_writer_delegate2.get()));
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
- file_writer_delegate2->Start(file2, request2.Pass());
+ file_writer_delegate_->Start(request_.Pass());
+ file_writer_delegate2->Start(request2.Pass());
MessageLoop::current()->Run();
if (!result_->complete() || !result2->complete())
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ ASSERT_TRUE(result2->complete());
+ file_writer_delegate_.reset();
+ file_writer_delegate2.reset();
+
ASSERT_EQ(kDataSize * 2, test_helper_.GetCachedOriginUsage());
- base::FlushPlatformFile(file2);
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
- file_writer_delegate_.reset();
-
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
EXPECT_EQ(kDataSize, result2->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result2->status());
- EXPECT_TRUE(result2->complete());
-
- base::ClosePlatformFile(file2);
}
TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
@@ -364,20 +358,22 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
PrepareForWrite(kBlobURL, offset, allowed_growth);
ASSERT_EQ(0, test_helper_.GetCachedOriginUsage());
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
// Trying to overwrite kDataSize bytes data while allowed_growth is 20.
offset = 0;
allowed_growth = 20;
PrepareForWrite(kBlobURL, offset, allowed_growth);
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
EXPECT_EQ(kDataSize, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
@@ -391,13 +387,15 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
allowed_growth = 55;
PrepareForWrite(kBlobURL, offset, allowed_growth);
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
EXPECT_EQ(offset + kDataSize, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
// Trying to overwrite 45 bytes data while allowed_growth is -20.
offset = 0;
@@ -405,13 +403,15 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
PrepareForWrite(kBlobURL, offset, allowed_growth);
int64 pre_write_usage = ComputeCurrentOriginUsage();
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
EXPECT_EQ(pre_write_usage, test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kDataSize, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status());
- EXPECT_TRUE(result_->complete());
// Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
// while allowed_growth is 10.
@@ -420,19 +420,16 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
allowed_growth = 10;
PrepareForWrite(kBlobURL, offset, allowed_growth);
- file_writer_delegate_->Start(file_, request_.Pass());
+ file_writer_delegate_->Start(request_.Pass());
MessageLoop::current()->Run();
+ ASSERT_TRUE(result_->complete());
+ file_writer_delegate_.reset();
+
EXPECT_EQ(pre_write_usage + allowed_growth,
test_helper_.GetCachedOriginUsage());
EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage());
EXPECT_EQ(kOverlap + allowed_growth, result_->bytes_written());
EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status());
- EXPECT_TRUE(result_->complete());
}
-class FileWriterDelegateUnlimitedTest : public FileWriterDelegateTest {
- protected:
- virtual void SetUpTestHelper(const FilePath& path) OVERRIDE;
-};
-
} // namespace fileapi
diff --git a/webkit/fileapi/sandbox_file_writer.cc b/webkit/fileapi/sandbox_file_writer.cc
new file mode 100644
index 0000000..e686d03
--- /dev/null
+++ b/webkit/fileapi/sandbox_file_writer.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2012 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/sandbox_file_writer.h"
+
+#include "base/file_util_proxy.h"
+#include "base/platform_file.h"
+#include "base/sequenced_task_runner.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "webkit/blob/local_file_reader.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/file_system_operation_interface.h"
+#include "webkit/fileapi/file_system_quota_util.h"
+#include "webkit/fileapi/file_system_util.h"
+#include "webkit/fileapi/local_file_writer.h"
+#include "webkit/quota/quota_manager.h"
+
+namespace fileapi {
+
+namespace {
+
+int PlatformFileErrorToNetError(base::PlatformFileError error) {
+ // TODO(kinuko): Move this static method to more convenient place.
+ return webkit_blob::LocalFileReader::PlatformFileErrorToNetError(error);
+}
+
+// Adjust the |quota| value in overwriting case (i.e. |file_size| > 0 and
+// |file_offset| < |file_size|) to make the remaining quota calculation easier.
+// Specifically this widens the quota for overlapping range (so that we can
+// simply compare written bytes against the adjusted quota).
+int64 AdjustQuotaForOverlap(int64 quota,
+ int64 file_offset,
+ int64 file_size) {
+ DCHECK_LE(file_offset, file_size);
+ if (quota < 0)
+ quota = 0;
+ int64 overlap = file_size - file_offset;
+ if (kint64max - overlap > quota)
+ quota += overlap;
+ return quota;
+}
+
+} // namespace
+
+SandboxFileWriter::SandboxFileWriter(
+ FileSystemContext* file_system_context,
+ const GURL& url,
+ int64 initial_offset)
+ : file_system_context_(file_system_context),
+ url_(url),
+ initial_offset_(initial_offset),
+ file_size_(0),
+ total_bytes_written_(0),
+ allowed_bytes_to_write_(0),
+ has_pending_operation_(false),
+ default_quota_(kint64max),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ const bool result = CrackFileSystemURL(
+ url_, &origin_, &file_system_type_, &virtual_path_);
+ DCHECK(result);
+}
+
+SandboxFileWriter::~SandboxFileWriter() {
+ if (quota_util())
+ quota_util()->proxy()->EndUpdateOrigin(origin_, file_system_type_);
+}
+
+int SandboxFileWriter::Write(
+ net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) {
+ has_pending_operation_ = true;
+ if (local_file_writer_.get())
+ return WriteInternal(buf, buf_len, callback);
+
+ FileSystemOperationInterface* operation =
+ file_system_context_->CreateFileSystemOperation(url_);
+ DCHECK(operation);
+ net::CompletionCallback write_task =
+ base::Bind(&SandboxFileWriter::DidInitializeForWrite,
+ weak_factory_.GetWeakPtr(),
+ make_scoped_refptr(buf), buf_len, callback);
+ operation->GetMetadata(
+ url_, base::Bind(&SandboxFileWriter::DidGetFileInfo,
+ weak_factory_.GetWeakPtr(), write_task));
+ return net::ERR_IO_PENDING;
+}
+
+int SandboxFileWriter::Cancel(const net::CompletionCallback& callback) {
+ if (!has_pending_operation_)
+ return net::ERR_UNEXPECTED;
+
+ DCHECK(!callback.is_null());
+ cancel_callback_ = callback;
+ return net::ERR_IO_PENDING;
+}
+
+int SandboxFileWriter::WriteInternal(
+ net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) {
+ // allowed_bytes_to_write could be negative if the file size is
+ // greater than the current (possibly new) quota.
+ DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ ||
+ allowed_bytes_to_write_ < 0);
+ if (total_bytes_written_ >= allowed_bytes_to_write_) {
+ has_pending_operation_ = false;
+ return net::ERR_FILE_NO_SPACE;
+ }
+
+ if (buf_len > allowed_bytes_to_write_ - total_bytes_written_)
+ buf_len = allowed_bytes_to_write_ - total_bytes_written_;
+
+ DCHECK(local_file_writer_.get());
+ const int result = local_file_writer_->Write(
+ buf, buf_len,
+ base::Bind(&SandboxFileWriter::DidWrite, weak_factory_.GetWeakPtr(),
+ callback));
+ if (result != net::ERR_IO_PENDING)
+ has_pending_operation_ = false;
+ return result;
+}
+
+void SandboxFileWriter::DidGetFileInfo(
+ const net::CompletionCallback& callback,
+ base::PlatformFileError file_error,
+ const base::PlatformFileInfo& file_info,
+ const FilePath& platform_path) {
+ if (CancelIfRequested())
+ return;
+ if (file_error != base::PLATFORM_FILE_OK) {
+ callback.Run(PlatformFileErrorToNetError(file_error));
+ return;
+ }
+ if (file_info.is_directory) {
+ // We should not be writing to a directory.
+ callback.Run(net::ERR_ACCESS_DENIED);
+ return;
+ }
+ file_size_ = file_info.size;
+ if (initial_offset_ > file_size_) {
+ LOG(ERROR) << initial_offset_ << ", " << file_size_;
+ // This shouldn't happen as long as we check offset in the renderer.
+ NOTREACHED();
+ initial_offset_ = file_size_;
+ }
+ DCHECK(!local_file_writer_.get());
+ local_file_writer_.reset(new LocalFileWriter(platform_path, initial_offset_));
+
+ quota::QuotaManagerProxy* quota_manager_proxy =
+ file_system_context_->quota_manager_proxy();
+ if (!quota_manager_proxy || !quota_util()) {
+ // If we don't have the quota manager or the requested filesystem type
+ // does not support quota, we should be able to let it go.
+ allowed_bytes_to_write_ = default_quota_;
+ callback.Run(net::OK);
+ return;
+ }
+
+ quota_util()->proxy()->StartUpdateOrigin(origin_, file_system_type_);
+ DCHECK(quota_manager_proxy->quota_manager());
+ quota_manager_proxy->quota_manager()->GetUsageAndQuota(
+ origin_,
+ FileSystemTypeToQuotaStorageType(file_system_type_),
+ base::Bind(&SandboxFileWriter::DidGetUsageAndQuota,
+ weak_factory_.GetWeakPtr(), callback));
+}
+
+void SandboxFileWriter::DidGetUsageAndQuota(
+ const net::CompletionCallback& callback,
+ quota::QuotaStatusCode status,
+ int64 usage, int64 quota) {
+ if (CancelIfRequested())
+ return;
+ if (status != quota::kQuotaStatusOk) {
+ LOG(WARNING) << "Got unexpected quota error : " << status;
+ callback.Run(net::ERR_FAILED);
+ return;
+ }
+
+ allowed_bytes_to_write_ = quota - usage;
+ callback.Run(net::OK);
+}
+
+void SandboxFileWriter::DidInitializeForWrite(
+ net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback,
+ int init_status) {
+ if (CancelIfRequested())
+ return;
+ if (init_status != net::OK) {
+ has_pending_operation_ = false;
+ callback.Run(init_status);
+ return;
+ }
+ allowed_bytes_to_write_ = AdjustQuotaForOverlap(
+ allowed_bytes_to_write_, initial_offset_, file_size_);
+ const int result = WriteInternal(buf, buf_len, callback);
+ if (result != net::ERR_IO_PENDING)
+ callback.Run(result);
+}
+
+void SandboxFileWriter::DidWrite(
+ const net::CompletionCallback& callback,
+ int write_response) {
+ DCHECK(has_pending_operation_);
+ has_pending_operation_ = false;
+
+ if (write_response <= 0) {
+ if (CancelIfRequested())
+ return;
+ callback.Run(write_response);
+ return;
+ }
+
+ if (quota_util() &&
+ total_bytes_written_ + write_response + initial_offset_ > file_size_) {
+ int overlapped = file_size_ - total_bytes_written_ - initial_offset_;
+ if (overlapped < 0)
+ overlapped = 0;
+ quota_util()->proxy()->UpdateOriginUsage(
+ file_system_context_->quota_manager_proxy(),
+ origin_, file_system_type_, write_response - overlapped);
+ }
+ total_bytes_written_ += write_response;
+
+ if (CancelIfRequested())
+ return;
+ callback.Run(write_response);
+}
+
+bool SandboxFileWriter::CancelIfRequested() {
+ if (cancel_callback_.is_null())
+ return false;
+
+ net::CompletionCallback pending_cancel = cancel_callback_;
+ has_pending_operation_ = false;
+ cancel_callback_.Reset();
+ pending_cancel.Run(net::OK);
+ return true;
+}
+
+FileSystemQuotaUtil* SandboxFileWriter::quota_util() const {
+ DCHECK(file_system_context_.get());
+ return file_system_context_->GetQuotaUtil(file_system_type_);
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/sandbox_file_writer.h b/webkit/fileapi/sandbox_file_writer.h
new file mode 100644
index 0000000..7f03a6c8
--- /dev/null
+++ b/webkit/fileapi/sandbox_file_writer.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 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_SANDBOX_FILE_WRITER_H_
+#define WEBKIT_FILEAPI_SANDBOX_FILE_WRITER_H_
+
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/platform_file.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/fileapi/file_system_types.h"
+#include "webkit/fileapi/file_writer.h"
+#include "webkit/quota/quota_types.h"
+
+namespace fileapi {
+
+class FileSystemContext;
+class FileSystemQuotaUtil;
+class LocalFileWriter;
+
+class SandboxFileWriter : public FileWriter {
+ public:
+ SandboxFileWriter(FileSystemContext* file_system_context,
+ const GURL& url,
+ int64 initial_offset);
+ virtual ~SandboxFileWriter();
+
+ // FileWriter overrides.
+ virtual int Write(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual int Cancel(const net::CompletionCallback& callback) OVERRIDE;
+
+ // Used only by tests.
+ void set_default_quota(int64 quota) {
+ default_quota_ = quota;
+ }
+
+ private:
+ // Performs quota calculation and calls local_file_writer_->Write().
+ int WriteInternal(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback);
+
+ // Callbacks that are chained for the first write. This eventually calls
+ // WriteInternal.
+ void DidGetFileInfo(const net::CompletionCallback& callback,
+ base::PlatformFileError file_error,
+ const base::PlatformFileInfo& file_info,
+ const FilePath& platform_path);
+ void DidGetUsageAndQuota(const net::CompletionCallback& callback,
+ quota::QuotaStatusCode status,
+ int64 usage, int64 quota);
+ void DidInitializeForWrite(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback,
+ int init_status);
+
+ void DidWrite(const net::CompletionCallback& callback, int write_response);
+
+ // Stops the in-flight operation, calls |cancel_callback_| and returns true
+ // if there's a pending cancel request.
+ bool CancelIfRequested();
+
+ FileSystemQuotaUtil* quota_util() const;
+
+ scoped_refptr<FileSystemContext> file_system_context_;
+ const GURL url_;
+ int64 initial_offset_;
+ scoped_ptr<LocalFileWriter> local_file_writer_;
+ net::CompletionCallback cancel_callback_;
+
+ GURL origin_;
+ FileSystemType file_system_type_;
+ FilePath virtual_path_;
+
+ FilePath file_path_;
+ int64 file_size_;
+ int64 total_bytes_written_;
+ int64 allowed_bytes_to_write_;
+ bool has_pending_operation_;
+
+ int64 default_quota_;
+
+ base::WeakPtrFactory<SandboxFileWriter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxFileWriter);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_SANDBOX_FILE_WRITER_H_
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index aaf5237..0543c74 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -74,6 +74,8 @@
'native_file_util.h',
'obfuscated_file_util.cc',
'obfuscated_file_util.h',
+ 'sandbox_file_writer.cc',
+ 'sandbox_file_writer.h',
'sandbox_mount_point_provider.cc',
'sandbox_mount_point_provider.h',
'test_mount_point_provider.cc',