summaryrefslogtreecommitdiffstats
path: root/webkit/fileapi
diff options
context:
space:
mode:
authorkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 14:06:11 +0000
committerkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 14:06:11 +0000
commitfc2e14faf27be8cb92f54029b4b8eccb9e5705b0 (patch)
treee17a402d900fef82804e5a817d37181450055d01 /webkit/fileapi
parent3e62727dcb4fbb74386271beea01dd0708491883 (diff)
downloadchromium_src-fc2e14faf27be8cb92f54029b4b8eccb9e5705b0.zip
chromium_src-fc2e14faf27be8cb92f54029b4b8eccb9e5705b0.tar.gz
chromium_src-fc2e14faf27be8cb92f54029b4b8eccb9e5705b0.tar.bz2
Add operation runner for SyncableFileSystem
BUG=148897 TEST=Syncable*,LocalFileSync* Review URL: https://codereview.chromium.org/11093071 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162700 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/fileapi')
-rw-r--r--webkit/fileapi/local_file_system_test_helper.cc2
-rw-r--r--webkit/fileapi/sandbox_mount_point_provider.cc11
-rw-r--r--webkit/fileapi/syncable/canned_syncable_file_system.cc30
-rw-r--r--webkit/fileapi/syncable/canned_syncable_file_system.h6
-rw-r--r--webkit/fileapi/syncable/local_file_sync_context.cc15
-rw-r--r--webkit/fileapi/syncable/local_file_sync_context.h8
-rw-r--r--webkit/fileapi/syncable/local_file_sync_status.cc20
-rw-r--r--webkit/fileapi/syncable/local_file_sync_status.h19
-rw-r--r--webkit/fileapi/syncable/local_file_sync_status_unittest.cc51
-rw-r--r--webkit/fileapi/syncable/syncable_file_operation_runner.cc83
-rw-r--r--webkit/fileapi/syncable/syncable_file_operation_runner.h85
-rw-r--r--webkit/fileapi/syncable/syncable_file_operation_runner_unittest.cc334
-rw-r--r--webkit/fileapi/syncable/syncable_file_system_operation.cc305
-rw-r--r--webkit/fileapi/syncable/syncable_file_system_operation.h104
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi4
15 files changed, 1007 insertions, 70 deletions
diff --git a/webkit/fileapi/local_file_system_test_helper.cc b/webkit/fileapi/local_file_system_test_helper.cc
index bdd9957..1983c1e 100644
--- a/webkit/fileapi/local_file_system_test_helper.cc
+++ b/webkit/fileapi/local_file_system_test_helper.cc
@@ -174,6 +174,8 @@ LocalFileSystemTestOriginHelper::ComputeCurrentDirectoryDatabaseUsage() const {
LocalFileSystemOperation* LocalFileSystemTestOriginHelper::NewOperation() {
DCHECK(file_system_context_.get());
DCHECK(file_util_);
+ scoped_ptr<FileSystemOperationContext> operation_context(
+ NewOperationContext());
LocalFileSystemOperation* operation = static_cast<LocalFileSystemOperation*>(
file_system_context_->CreateFileSystemOperation(
CreateURL(FilePath()), NULL));
diff --git a/webkit/fileapi/sandbox_mount_point_provider.cc b/webkit/fileapi/sandbox_mount_point_provider.cc
index 400cea6..3773f227 100644
--- a/webkit/fileapi/sandbox_mount_point_provider.cc
+++ b/webkit/fileapi/sandbox_mount_point_provider.cc
@@ -29,6 +29,7 @@
#include "webkit/fileapi/obfuscated_file_util.h"
#include "webkit/fileapi/sandbox_file_stream_writer.h"
#include "webkit/fileapi/sandbox_quota_observer.h"
+#include "webkit/fileapi/syncable/syncable_file_system_operation.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/quota/quota_manager.h"
@@ -458,11 +459,15 @@ FileSystemOperation* SandboxMountPointProvider::CreateFileSystemOperation(
if (url.type() == kFileSystemTypeSyncable) {
operation_context->set_update_observers(syncable_update_observers_);
operation_context->set_change_observers(syncable_change_observers_);
- } else {
- operation_context->set_update_observers(update_observers_);
+ operation_context->set_access_observers(access_observers_);
+ return new SyncableFileSystemOperation(
+ context,
+ new LocalFileSystemOperation(context, operation_context.Pass()));
}
- operation_context->set_access_observers(access_observers_);
+ // For regular sandboxed types.
+ operation_context->set_update_observers(update_observers_);
+ operation_context->set_access_observers(access_observers_);
return new LocalFileSystemOperation(context, operation_context.Pass());
}
diff --git a/webkit/fileapi/syncable/canned_syncable_file_system.cc b/webkit/fileapi/syncable/canned_syncable_file_system.cc
index ff7992b..d78c328 100644
--- a/webkit/fileapi/syncable/canned_syncable_file_system.cc
+++ b/webkit/fileapi/syncable/canned_syncable_file_system.cc
@@ -226,6 +226,24 @@ PlatformFileError CannedSyncableFileSystem::Remove(
base::Unretained(this), url, recursive));
}
+PlatformFileError CannedSyncableFileSystem::FileExists(
+ const FileSystemURL& url) {
+ return RunOnThread<PlatformFileError>(
+ io_task_runner_,
+ FROM_HERE,
+ base::Bind(&CannedSyncableFileSystem::DoFileExists,
+ base::Unretained(this), url));
+}
+
+PlatformFileError CannedSyncableFileSystem::DirectoryExists(
+ const FileSystemURL& url) {
+ return RunOnThread<PlatformFileError>(
+ io_task_runner_,
+ FROM_HERE,
+ base::Bind(&CannedSyncableFileSystem::DoDirectoryExists,
+ base::Unretained(this), url));
+}
+
int64 CannedSyncableFileSystem::Write(
net::URLRequestContext* url_request_context,
const FileSystemURL& url, const GURL& blob_url) {
@@ -296,6 +314,18 @@ void CannedSyncableFileSystem::DoRemove(
NewOperation()->Remove(url, recursive, callback);
}
+void CannedSyncableFileSystem::DoFileExists(
+ const FileSystemURL& url, const StatusCallback& callback) {
+ EXPECT_TRUE(is_filesystem_opened_);
+ NewOperation()->FileExists(url, callback);
+}
+
+void CannedSyncableFileSystem::DoDirectoryExists(
+ const FileSystemURL& url, const StatusCallback& callback) {
+ EXPECT_TRUE(is_filesystem_opened_);
+ NewOperation()->DirectoryExists(url, callback);
+}
+
void CannedSyncableFileSystem::DoWrite(
net::URLRequestContext* url_request_context,
const FileSystemURL& url, const GURL& blob_url,
diff --git a/webkit/fileapi/syncable/canned_syncable_file_system.h b/webkit/fileapi/syncable/canned_syncable_file_system.h
index 4fa634e..6c94b01 100644
--- a/webkit/fileapi/syncable/canned_syncable_file_system.h
+++ b/webkit/fileapi/syncable/canned_syncable_file_system.h
@@ -87,6 +87,8 @@ class CannedSyncableFileSystem {
const FileSystemURL& dest_url);
base::PlatformFileError TruncateFile(const FileSystemURL& url, int64 size);
base::PlatformFileError Remove(const FileSystemURL& url, bool recursive);
+ base::PlatformFileError FileExists(const FileSystemURL& url);
+ base::PlatformFileError DirectoryExists(const FileSystemURL& url);
// Returns the # of bytes written (>=0) or an error code (<0).
int64 Write(net::URLRequestContext* url_request_context,
@@ -116,6 +118,10 @@ class CannedSyncableFileSystem {
void DoRemove(const FileSystemURL& url,
bool recursive,
const StatusCallback& callback);
+ void DoFileExists(const FileSystemURL& url,
+ const StatusCallback& callback);
+ void DoDirectoryExists(const FileSystemURL& url,
+ const StatusCallback& callback);
void DoWrite(net::URLRequestContext* url_request_context,
const FileSystemURL& url,
const GURL& blob_url,
diff --git a/webkit/fileapi/syncable/local_file_sync_context.cc b/webkit/fileapi/syncable/local_file_sync_context.cc
index a8a89f5..a325829 100644
--- a/webkit/fileapi/syncable/local_file_sync_context.cc
+++ b/webkit/fileapi/syncable/local_file_sync_context.cc
@@ -13,6 +13,7 @@
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_task_runners.h"
#include "webkit/fileapi/syncable/local_file_change_tracker.h"
+#include "webkit/fileapi/syncable/syncable_file_operation_runner.h"
namespace fileapi {
@@ -59,10 +60,20 @@ void LocalFileSyncContext::ShutdownOnUIThread() {
this));
}
-LocalFileSyncContext::~LocalFileSyncContext() {}
+base::WeakPtr<SyncableFileOperationRunner>
+LocalFileSyncContext::operation_runner() const {
+ DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ if (operation_runner_.get())
+ return operation_runner_->AsWeakPtr();
+ return base::WeakPtr<SyncableFileOperationRunner>();
+}
+
+LocalFileSyncContext::~LocalFileSyncContext() {
+}
void LocalFileSyncContext::ShutdownOnIOThread() {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
+ operation_runner_.reset();
}
void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
@@ -87,6 +98,8 @@ void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
make_scoped_refptr(file_system_context)));
return;
}
+ if (!operation_runner_.get())
+ operation_runner_.reset(new SyncableFileOperationRunner);
file_system_context->set_sync_context(this);
DidInitialize(source_url, file_system_context, SYNC_STATUS_OK);
}
diff --git a/webkit/fileapi/syncable/local_file_sync_context.h b/webkit/fileapi/syncable/local_file_sync_context.h
index aeba4d4..c02d81a 100644
--- a/webkit/fileapi/syncable/local_file_sync_context.h
+++ b/webkit/fileapi/syncable/local_file_sync_context.h
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "googleurl/src/gurl.h"
#include "webkit/fileapi/syncable/file_change.h"
#include "webkit/fileapi/syncable/sync_status_code.h"
@@ -28,6 +29,7 @@ namespace fileapi {
class FileSystemContext;
class LocalFileChangeTracker;
+class SyncableFileOperationRunner;
// This class works as a bridge between LocalFileSyncService (which is a
// per-profile object) and FileSystemContext's (which is a per-storage-partition
@@ -54,6 +56,9 @@ class WEBKIT_STORAGE_EXPORT LocalFileSyncContext
// This method must be called on UI thread.
void ShutdownOnUIThread();
+ // OperationRunner is accessible only on IO thread.
+ base::WeakPtr<SyncableFileOperationRunner> operation_runner() const;
+
private:
typedef std::deque<StatusCallback> StatusCallbackQueue;
friend class base::RefCountedThreadSafe<LocalFileSyncContext>;
@@ -83,6 +88,9 @@ class WEBKIT_STORAGE_EXPORT LocalFileSyncContext
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ // OperationRunner. This must be accessed only on IO thread.
+ scoped_ptr<SyncableFileOperationRunner> operation_runner_;
+
// Pointers to file system contexts that have been initialized for
// synchronization (i.e. that own this instance).
// This must be accessed only on UI thread.
diff --git a/webkit/fileapi/syncable/local_file_sync_status.cc b/webkit/fileapi/syncable/local_file_sync_status.cc
index b3aea58..3be00e3 100644
--- a/webkit/fileapi/syncable/local_file_sync_status.cc
+++ b/webkit/fileapi/syncable/local_file_sync_status.cc
@@ -12,32 +12,26 @@ LocalFileSyncStatus::LocalFileSyncStatus() {}
LocalFileSyncStatus::~LocalFileSyncStatus() {}
-bool LocalFileSyncStatus::TryIncrementWriting(const FileSystemURL& url) {
+void LocalFileSyncStatus::StartWriting(const FileSystemURL& url) {
DCHECK(CalledOnValidThread());
- if (IsChildOrParentSyncing(url))
- return false;
+ DCHECK(!IsChildOrParentSyncing(url));
writing_[url]++;
- return true;
}
-void LocalFileSyncStatus::DecrementWriting(const FileSystemURL& url) {
+void LocalFileSyncStatus::EndWriting(const FileSystemURL& url) {
DCHECK(CalledOnValidThread());
int count = --writing_[url];
- if (count == 0) {
+ if (count == 0)
writing_.erase(url);
- // TODO(kinuko): fire NeedsSynchronization notification.
- }
}
-bool LocalFileSyncStatus::TryDisableWriting(const FileSystemURL& url) {
+void LocalFileSyncStatus::StartSyncing(const FileSystemURL& url) {
DCHECK(CalledOnValidThread());
- if (IsChildOrParentWriting(url))
- return false;
+ DCHECK(!IsChildOrParentWriting(url));
syncing_.insert(url);
- return true;
}
-void LocalFileSyncStatus::EnableWriting(const FileSystemURL& url) {
+void LocalFileSyncStatus::EndSyncing(const FileSystemURL& url) {
DCHECK(CalledOnValidThread());
syncing_.erase(url);
}
diff --git a/webkit/fileapi/syncable/local_file_sync_status.h b/webkit/fileapi/syncable/local_file_sync_status.h
index 3410a54..f99b84e 100644
--- a/webkit/fileapi/syncable/local_file_sync_status.h
+++ b/webkit/fileapi/syncable/local_file_sync_status.h
@@ -30,19 +30,19 @@ class WEBKIT_STORAGE_EXPORT LocalFileSyncStatus : public base::NonThreadSafe {
LocalFileSyncStatus();
~LocalFileSyncStatus();
- // Tries to increment writing counter for |url|.
- // This fails if the target |url| is in syncing.
- bool TryIncrementWriting(const FileSystemURL& url);
+ // Increment writing counter for |url|.
+ // This should not be called if the |url| is not writable.
+ void StartWriting(const FileSystemURL& url);
// Decrement writing counter for |url|.
- void DecrementWriting(const FileSystemURL& url);
+ void EndWriting(const FileSystemURL& url);
- // Tries to mark syncing flag for |url| to enable writing.
- // This fails if the target |url| is in writing.
- bool TryDisableWriting(const FileSystemURL& url);
+ // Start syncing for |url| and disable writing.
+ // This should not be called if |url| is in writing.
+ void StartSyncing(const FileSystemURL& url);
- // Clears the syncing flag for |url| to disable writing.
- void EnableWriting(const FileSystemURL& url);
+ // Clears the syncing flag for |url| and enable writing.
+ void EndSyncing(const FileSystemURL& url);
// Returns true if the |url| or its parent or child is in writing.
bool IsWriting(const FileSystemURL& url) const;
@@ -54,7 +54,6 @@ class WEBKIT_STORAGE_EXPORT LocalFileSyncStatus : public base::NonThreadSafe {
typedef std::map<FileSystemURL, int64, FileSystemURL::Comparator> URLCountMap;
typedef std::set<FileSystemURL, FileSystemURL::Comparator> URLSet;
- // These private methods must be called with the lock_ held.
bool IsChildOrParentWriting(const FileSystemURL& url) const;
bool IsChildOrParentSyncing(const FileSystemURL& url) const;
diff --git a/webkit/fileapi/syncable/local_file_sync_status_unittest.cc b/webkit/fileapi/syncable/local_file_sync_status_unittest.cc
index d165aca..c402369 100644
--- a/webkit/fileapi/syncable/local_file_sync_status_unittest.cc
+++ b/webkit/fileapi/syncable/local_file_sync_status_unittest.cc
@@ -27,9 +27,10 @@ FileSystemURL URL(const char* spec) {
TEST(LocalFileSyncStatusTest, WritingSimple) {
LocalFileSyncStatus status;
- ASSERT_TRUE(status.TryIncrementWriting(URL(kFile)));
- ASSERT_TRUE(status.TryIncrementWriting(URL(kFile)));
- status.DecrementWriting(URL(kFile));
+
+ status.StartWriting(URL(kFile));
+ status.StartWriting(URL(kFile));
+ status.EndWriting(URL(kFile));
EXPECT_TRUE(status.IsWriting(URL(kFile)));
EXPECT_TRUE(status.IsWriting(URL(kParent)));
@@ -37,16 +38,16 @@ TEST(LocalFileSyncStatusTest, WritingSimple) {
EXPECT_FALSE(status.IsWriting(URL(kOther1)));
EXPECT_FALSE(status.IsWriting(URL(kOther2)));
- status.DecrementWriting(URL(kFile));
+ status.EndWriting(URL(kFile));
EXPECT_FALSE(status.IsWriting(URL(kFile)));
EXPECT_FALSE(status.IsWriting(URL(kParent)));
EXPECT_FALSE(status.IsWriting(URL(kChild)));
}
-TEST(LocalFileSyncStatusTest, DisableWritingSimple) {
+TEST(LocalFileSyncStatusTest, SyncingSimple) {
LocalFileSyncStatus status;
- ASSERT_TRUE(status.TryDisableWriting(URL(kFile)));
+ status.StartSyncing(URL(kFile));
EXPECT_FALSE(status.IsWritable(URL(kFile)));
EXPECT_FALSE(status.IsWritable(URL(kParent)));
@@ -54,47 +55,11 @@ TEST(LocalFileSyncStatusTest, DisableWritingSimple) {
EXPECT_TRUE(status.IsWritable(URL(kOther1)));
EXPECT_TRUE(status.IsWritable(URL(kOther2)));
- status.EnableWriting(URL(kFile));
+ status.EndSyncing(URL(kFile));
EXPECT_TRUE(status.IsWritable(URL(kFile)));
EXPECT_TRUE(status.IsWritable(URL(kParent)));
EXPECT_TRUE(status.IsWritable(URL(kChild)));
}
-TEST(LocalFileSyncStatusTest, TryWriting) {
- LocalFileSyncStatus status;
-
- ASSERT_TRUE(status.TryDisableWriting(URL(kFile)));
- EXPECT_FALSE(status.IsWritable(URL(kFile)));
-
- ASSERT_FALSE(status.TryIncrementWriting(URL(kFile)));
- ASSERT_FALSE(status.TryIncrementWriting(URL(kParent)));
- ASSERT_FALSE(status.TryIncrementWriting(URL(kChild)));
-
- status.EnableWriting(URL(kFile));
- EXPECT_TRUE(status.IsWritable(URL(kFile)));
-
- ASSERT_TRUE(status.TryIncrementWriting(URL(kFile)));
- ASSERT_TRUE(status.TryIncrementWriting(URL(kParent)));
- ASSERT_TRUE(status.TryIncrementWriting(URL(kChild)));
-}
-
-TEST(LocalFileSyncStatusTest, TryDisableWriting) {
- LocalFileSyncStatus status;
-
- ASSERT_TRUE(status.TryIncrementWriting(URL(kFile)));
- EXPECT_TRUE(status.IsWritable(URL(kFile)));
-
- ASSERT_FALSE(status.TryDisableWriting(URL(kFile)));
- ASSERT_FALSE(status.TryDisableWriting(URL(kParent)));
- ASSERT_FALSE(status.TryDisableWriting(URL(kChild)));
-
- status.DecrementWriting(URL(kFile));
- EXPECT_FALSE(status.IsWriting(URL(kFile)));
-
- ASSERT_TRUE(status.TryDisableWriting(URL(kFile)));
- ASSERT_TRUE(status.TryDisableWriting(URL(kParent)));
- ASSERT_TRUE(status.TryDisableWriting(URL(kChild)));
-}
-
} // namespace fileapi
diff --git a/webkit/fileapi/syncable/syncable_file_operation_runner.cc b/webkit/fileapi/syncable/syncable_file_operation_runner.cc
new file mode 100644
index 0000000..ef9c899
--- /dev/null
+++ b/webkit/fileapi/syncable/syncable_file_operation_runner.cc
@@ -0,0 +1,83 @@
+// 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/syncable/syncable_file_operation_runner.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/callback.h"
+#include "base/stl_util.h"
+#include "webkit/fileapi/syncable/local_file_sync_status.h"
+
+namespace fileapi {
+
+// SyncableFileOperationRunner::Task -------------------------------------------
+
+// static
+void SyncableFileOperationRunner::Task::CancelAndDelete(
+ SyncableFileOperationRunner::Task* task) {
+ task->Cancel();
+ delete task;
+}
+
+bool SyncableFileOperationRunner::Task::IsRunnable(
+ LocalFileSyncStatus* status) const {
+ for (size_t i = 0; i < target_paths().size(); ++i) {
+ if (!status->IsWritable(target_paths()[i]))
+ return false;
+ }
+ return true;
+}
+
+void SyncableFileOperationRunner::Task::Start(LocalFileSyncStatus* status) {
+ for (size_t i = 0; i < target_paths().size(); ++i) {
+ DCHECK(status->IsWritable(target_paths()[i]));
+ status->StartWriting(target_paths()[i]);
+ }
+ Run();
+}
+
+// SyncableFileOperationRunner -------------------------------------------------
+
+SyncableFileOperationRunner::SyncableFileOperationRunner()
+ : sync_status_(new LocalFileSyncStatus) {
+ DCHECK(CalledOnValidThread());
+}
+
+SyncableFileOperationRunner::~SyncableFileOperationRunner() {
+ DCHECK(CalledOnValidThread());
+ for_each(pending_operations_.begin(), pending_operations_.end(),
+ SyncableFileOperationRunner::Task::CancelAndDelete);
+}
+
+void SyncableFileOperationRunner::PostOperationTask(scoped_ptr<Task> task) {
+ DCHECK(CalledOnValidThread());
+ pending_operations_.push_back(task.release());
+ RunNextRunnableTask();
+}
+
+void SyncableFileOperationRunner::RunNextRunnableTask() {
+ DCHECK(CalledOnValidThread());
+ for (std::list<Task*>::iterator iter = pending_operations_.begin();
+ iter != pending_operations_.end(); ++iter) {
+ if ((*iter)->IsRunnable(sync_status())) {
+ scoped_ptr<Task> task(*iter);
+ pending_operations_.erase(iter);
+ task->Start(sync_status());
+ return;
+ }
+ }
+}
+
+void SyncableFileOperationRunner::OnOperationCompleted(
+ const std::vector<FileSystemURL>& target_paths) {
+ for (size_t i = 0; i < target_paths.size(); ++i) {
+ DCHECK(sync_status()->IsWriting(target_paths[i]));
+ sync_status()->EndWriting(target_paths[i]);
+ }
+ RunNextRunnableTask();
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/syncable/syncable_file_operation_runner.h b/webkit/fileapi/syncable/syncable_file_operation_runner.h
new file mode 100644
index 0000000..95cf5c6
--- /dev/null
+++ b/webkit/fileapi/syncable/syncable_file_operation_runner.h
@@ -0,0 +1,85 @@
+// 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_SYNCABLE_SYNCABLE_FILE_OPERATION_RUNNER_H_
+#define WEBKIT_FILEAPI_SYNCABLE_SYNCABLE_FILE_OPERATION_RUNNER_H_
+
+#include <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "webkit/fileapi/file_system_url.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace fileapi {
+
+class FileSystemURL;
+class LocalFileSyncStatus;
+
+// This class must run only on IO thread.
+// Owned by LocalFileSyncContext.
+class WEBKIT_STORAGE_EXPORT SyncableFileOperationRunner
+ : public base::NonThreadSafe,
+ public base::SupportsWeakPtr<SyncableFileOperationRunner> {
+ public:
+ // Represents an operation task (which usually wraps one FileSystemOperation).
+ class Task {
+ public:
+ Task() {}
+ virtual ~Task() {}
+
+ // Only one of Run() or Cancel() is called.
+ virtual void Run() = 0;
+ virtual void Cancel() = 0;
+
+ protected:
+ // This is never called after Run() or Cancel() is called.
+ virtual const std::vector<FileSystemURL>& target_paths() const = 0;
+
+ private:
+ friend class SyncableFileOperationRunner;
+ bool IsRunnable(LocalFileSyncStatus* status) const;
+ void Start(LocalFileSyncStatus* status);
+ static void CancelAndDelete(Task* task);
+
+ DISALLOW_COPY_AND_ASSIGN(Task);
+ };
+
+ SyncableFileOperationRunner();
+ ~SyncableFileOperationRunner();
+
+ // Runs the given |task| if no sync operation is running on any of
+ // its target_paths(). This also runs pending operations that have become
+ // runnable (before running the given operation).
+ // If there're ongoing sync operations on the target_paths this method
+ // just queues up the |task|.
+ // Pending operations are cancelled when this class is destructed.
+ void PostOperationTask(scoped_ptr<Task> task);
+
+ // Runs a next runnable task (if there's any).
+ void RunNextRunnableTask();
+
+ // Called when an operation is completed. This will make |target_paths|
+ // writable and may start a next runnable task.
+ void OnOperationCompleted(const std::vector<FileSystemURL>& target_paths);
+
+ // For syncable file systems.
+ LocalFileSyncStatus* sync_status() const { return sync_status_.get(); }
+
+ private:
+ // Keeps track of the writing/syncing status.
+ scoped_ptr<LocalFileSyncStatus> sync_status_;
+
+ std::list<Task*> pending_operations_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunner);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_SYNCABLE_SYNCABLE_FILE_OPERATION_RUNNER_H_
diff --git a/webkit/fileapi/syncable/syncable_file_operation_runner_unittest.cc b/webkit/fileapi/syncable/syncable_file_operation_runner_unittest.cc
new file mode 100644
index 0000000..f577e91
--- /dev/null
+++ b/webkit/fileapi/syncable/syncable_file_operation_runner_unittest.cc
@@ -0,0 +1,334 @@
+// 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 "base/basictypes.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/blob/mock_blob_url_request_context.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/syncable/canned_syncable_file_system.h"
+#include "webkit/fileapi/syncable/local_file_change_tracker.h"
+#include "webkit/fileapi/syncable/local_file_sync_context.h"
+#include "webkit/fileapi/syncable/local_file_sync_status.h"
+#include "webkit/fileapi/syncable/syncable_file_operation_runner.h"
+#include "webkit/fileapi/syncable/syncable_file_system_operation.h"
+#include "webkit/fileapi/syncable/syncable_file_system_util.h"
+
+using webkit_blob::MockBlobURLRequestContext;
+using webkit_blob::ScopedTextBlob;
+using base::PlatformFileError;
+
+namespace fileapi {
+
+namespace {
+const std::string kServiceName = "test";
+const std::string kParent = "foo";
+const std::string kFile = "foo/file";
+const std::string kDir = "foo/dir";
+const std::string kChild = "foo/dir/bar";
+const std::string kOther = "bar";
+} // namespace
+
+class SyncableFileOperationRunnerTest : public testing::Test {
+ protected:
+ typedef FileSystemOperation::StatusCallback StatusCallback;
+
+ // Use the current thread as IO thread so that we can directly call
+ // operations in the tests.
+ SyncableFileOperationRunnerTest()
+ : message_loop_(MessageLoop::TYPE_IO),
+ file_system_(GURL("http://example.com"), kServiceName,
+ base::MessageLoopProxy::current()),
+ callback_count_(0),
+ write_status_(base::PLATFORM_FILE_ERROR_FAILED),
+ write_bytes_(0),
+ write_complete_(false),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+ virtual void SetUp() OVERRIDE {
+ file_system_.SetUp();
+ sync_context_ = new LocalFileSyncContext(base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current());
+ ASSERT_EQ(SYNC_STATUS_OK,
+ file_system_.MaybeInitializeFileSystemContext(sync_context_));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ file_system_.CreateDirectory(URL(kParent)));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (sync_context_)
+ sync_context_->ShutdownOnUIThread();
+ sync_context_ = NULL;
+
+ file_system_.TearDown();
+ message_loop_.RunAllPending();
+ RevokeSyncableFileSystem(kServiceName);
+ }
+
+ FileSystemURL URL(const std::string& path) {
+ return file_system_.URL(path);
+ }
+
+ LocalFileSyncContext* sync_context() {
+ return file_system_.file_system_context()->sync_context();
+ }
+
+ SyncableFileOperationRunner* operation_runner() {
+ return sync_context()->operation_runner().get();
+ }
+
+ LocalFileSyncStatus* sync_status() {
+ return operation_runner()->sync_status();
+ }
+
+ void ResetCallbackStatus() {
+ write_status_ = base::PLATFORM_FILE_ERROR_FAILED;
+ write_bytes_ = 0;
+ write_complete_ = false;
+ callback_count_ = 0;
+ }
+
+ StatusCallback ExpectStatus(const tracked_objects::Location& location,
+ PlatformFileError expect) {
+ return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
+ weak_factory_.GetWeakPtr(), location, expect);
+ }
+
+ FileSystemOperation::WriteCallback GetWriteCallback(
+ const tracked_objects::Location& location) {
+ return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
+ weak_factory_.GetWeakPtr(), location);
+ }
+
+ void DidWrite(const tracked_objects::Location& location,
+ PlatformFileError status, int64 bytes, bool complete) {
+ SCOPED_TRACE(testing::Message() << location.ToString());
+ write_status_ = status;
+ write_bytes_ += bytes;
+ write_complete_ = complete;
+ ++callback_count_;
+ }
+
+ void DidFinish(const tracked_objects::Location& location,
+ PlatformFileError expect, PlatformFileError status) {
+ SCOPED_TRACE(testing::Message() << location.ToString());
+ EXPECT_EQ(expect, status);
+ ++callback_count_;
+ }
+
+ MessageLoop message_loop_;
+ CannedSyncableFileSystem file_system_;
+ scoped_refptr<LocalFileSyncContext> sync_context_;
+
+ int callback_count_;
+ PlatformFileError write_status_;
+ size_t write_bytes_;
+ bool write_complete_;
+
+ MockBlobURLRequestContext url_request_context_;
+
+ base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
+};
+
+TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
+ sync_status()->StartSyncing(URL(kFile));
+ ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
+
+ // The URL is in syncing so the write operations won't run.
+ ResetCallbackStatus();
+ file_system_.NewOperation()->CreateFile(
+ URL(kFile), false /* exclusive */,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ file_system_.NewOperation()->Truncate(
+ URL(kFile), 1,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, callback_count_);
+
+ // Read operations are not blocked (and are executed before queued ones).
+ file_system_.NewOperation()->FileExists(
+ URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_NOT_FOUND));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // End syncing (to enable write).
+ sync_status()->EndSyncing(URL(kFile));
+ ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
+
+ ResetCallbackStatus();
+ operation_runner()->RunNextRunnableTask();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(2, callback_count_);
+
+ // Now the file must have been created and updated.
+ ResetCallbackStatus();
+ file_system_.NewOperation()->FileExists(
+ URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+}
+
+TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) {
+ // First create the kDir directory and kChild in the dir.
+ EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir)));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild)));
+
+ // Start syncing the kDir directory.
+ sync_status()->StartSyncing(URL(kDir));
+ ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
+
+ // Writes to kParent, kDir and kChild should be all queued up.
+ ResetCallbackStatus();
+ file_system_.NewOperation()->Truncate(
+ URL(kChild), 1, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ file_system_.NewOperation()->Remove(
+ URL(kDir), true /* recursive */,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ file_system_.NewOperation()->Remove(
+ URL(kParent), true /* recursive */,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, callback_count_);
+
+ // Read operations are not blocked (and are executed before queued ones).
+ file_system_.NewOperation()->DirectoryExists(
+ URL(kDir), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // Writes to unrelated files must succeed as well.
+ ResetCallbackStatus();
+ file_system_.NewOperation()->CreateDirectory(
+ URL(kOther), false /* exclusive */, false /* recursive */,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // End syncing (to enable write).
+ sync_status()->EndSyncing(URL(kDir));
+ ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
+
+ ResetCallbackStatus();
+ operation_runner()->RunNextRunnableTask();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(3, callback_count_);
+}
+
+TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) {
+ // First create the kDir directory and kChild in the dir.
+ EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir)));
+ EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild)));
+
+ // Start syncing the kParent directory.
+ sync_status()->StartSyncing(URL(kParent));
+
+ // Copying kDir to other directory should succeed, while moving would fail
+ // (since the source directory is in syncing).
+ ResetCallbackStatus();
+ file_system_.NewOperation()->Copy(
+ URL(kDir), URL("dest-copy"),
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ file_system_.NewOperation()->Move(
+ URL(kDir), URL("dest-move"),
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // Only "dest-copy1" should exist.
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_system_.DirectoryExists(URL("dest-copy")));
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ file_system_.DirectoryExists(URL("dest-move")));
+
+ // Start syncing the "dest-copy2" directory.
+ sync_status()->StartSyncing(URL("dest-copy2"));
+
+ // Now the destination is also locked copying kDir should be queued.
+ ResetCallbackStatus();
+ file_system_.NewOperation()->Copy(
+ URL(kDir), URL("dest-copy2"),
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, callback_count_);
+
+ // Finish syncing the "dest-copy2" directory to unlock Copy.
+ sync_status()->EndSyncing(URL("dest-copy2"));
+ ResetCallbackStatus();
+ operation_runner()->RunNextRunnableTask();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // Now we should have "dest-copy2".
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_system_.DirectoryExists(URL("dest-copy2")));
+
+ // Finish syncing the kParent to unlock Move.
+ sync_status()->EndSyncing(URL(kParent));
+ ResetCallbackStatus();
+ operation_runner()->RunNextRunnableTask();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, callback_count_);
+
+ // Now we should have "dest-move".
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ file_system_.DirectoryExists(URL("dest-move")));
+}
+
+TEST_F(SyncableFileOperationRunnerTest, Write) {
+ EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile)));
+ const GURL kBlobURL("blob:foo");
+ const std::string kData("Lorem ipsum.");
+ ScopedTextBlob blob(url_request_context_, kBlobURL, kData);
+
+ sync_status()->StartSyncing(URL(kFile));
+
+ ResetCallbackStatus();
+ file_system_.NewOperation()->Write(
+ &url_request_context_,
+ URL(kFile), kBlobURL, 0, GetWriteCallback(FROM_HERE));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, callback_count_);
+
+ sync_status()->EndSyncing(URL(kFile));
+ ResetCallbackStatus();
+ operation_runner()->RunNextRunnableTask();
+
+ while (!write_complete_)
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_);
+ EXPECT_EQ(kData.size(), write_bytes_);
+ EXPECT_TRUE(write_complete_);
+}
+
+TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
+ sync_status()->StartSyncing(URL(kFile));
+ ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
+
+ ResetCallbackStatus();
+ file_system_.NewOperation()->CreateFile(
+ URL(kFile), false /* exclusive */,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT));
+ file_system_.NewOperation()->Truncate(
+ URL(kFile), 1,
+ ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT));
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, callback_count_);
+
+ ResetCallbackStatus();
+
+ // This shouldn't crash nor leak memory.
+ sync_context_->ShutdownOnUIThread();
+ sync_context_ = NULL;
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(2, callback_count_);
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/syncable/syncable_file_system_operation.cc b/webkit/fileapi/syncable/syncable_file_system_operation.cc
new file mode 100644
index 0000000..33cf51d
--- /dev/null
+++ b/webkit/fileapi/syncable/syncable_file_system_operation.cc
@@ -0,0 +1,305 @@
+// 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/syncable/syncable_file_system_operation.h"
+
+#include "base/logging.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/file_system_url.h"
+#include "webkit/fileapi/local_file_system_operation.h"
+#include "webkit/fileapi/syncable/local_file_sync_context.h"
+#include "webkit/fileapi/syncable/syncable_file_operation_runner.h"
+
+namespace fileapi {
+
+namespace {
+
+void WriteCallbackAdapter(
+ const SyncableFileSystemOperation::WriteCallback& callback,
+ base::PlatformFileError status) {
+ callback.Run(status, 0, true);
+}
+
+} // namespace
+
+class SyncableFileSystemOperation::QueueableTask
+ : public SyncableFileOperationRunner::Task {
+ public:
+ QueueableTask(SyncableFileSystemOperation* operation,
+ const base::Closure& task)
+ : operation_(operation), task_(task) {}
+
+ virtual ~QueueableTask() {
+ DCHECK(!operation_);
+ }
+
+ virtual void Run() OVERRIDE {
+ DCHECK(!task_.is_null());
+ task_.Run();
+ operation_ = NULL;
+ }
+
+ virtual void Cancel() OVERRIDE {
+ DCHECK(!task_.is_null());
+ DCHECK(operation_);
+ operation_->OnCancelled();
+ task_.Reset(); // This will delete operation_.
+ operation_ = NULL;
+ }
+
+ virtual std::vector<FileSystemURL>& target_paths() const OVERRIDE {
+ DCHECK(operation_);
+ return operation_->target_paths_;
+ }
+
+ private:
+ SyncableFileSystemOperation* operation_;
+ base::Closure task_;
+ DISALLOW_COPY_AND_ASSIGN(QueueableTask);
+};
+
+SyncableFileSystemOperation::~SyncableFileSystemOperation() {}
+
+void SyncableFileSystemOperation::CreateFile(
+ const FileSystemURL& url,
+ bool exclusive,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::CreateFile,
+ base::Unretained(file_system_operation_),
+ url, exclusive,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::CreateDirectory(
+ const FileSystemURL& url,
+ bool exclusive,
+ bool recursive,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::CreateDirectory,
+ base::Unretained(file_system_operation_),
+ url, exclusive, recursive,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::Copy(
+ const FileSystemURL& src_url,
+ const FileSystemURL& dest_url,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(dest_url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::Copy,
+ base::Unretained(file_system_operation_),
+ src_url, dest_url,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::Move(
+ const FileSystemURL& src_url,
+ const FileSystemURL& dest_url,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(src_url);
+ target_paths_.push_back(dest_url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::Move,
+ base::Unretained(file_system_operation_),
+ src_url, dest_url,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::DirectoryExists(
+ const FileSystemURL& url,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->DirectoryExists(url, callback);
+ delete this;
+}
+
+void SyncableFileSystemOperation::FileExists(
+ const FileSystemURL& url,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->FileExists(url, callback);
+ delete this;
+}
+
+void SyncableFileSystemOperation::GetMetadata(
+ const FileSystemURL& url,
+ const GetMetadataCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->GetMetadata(url, callback);
+ delete this;
+}
+
+void SyncableFileSystemOperation::ReadDirectory(
+ const FileSystemURL& url,
+ const ReadDirectoryCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->ReadDirectory(url, callback);
+ delete this;
+}
+
+void SyncableFileSystemOperation::Remove(
+ const FileSystemURL& url, bool recursive,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::Remove,
+ base::Unretained(file_system_operation_),
+ url, recursive,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::Write(
+ const net::URLRequestContext* url_request_context,
+ const FileSystemURL& url,
+ const GURL& blob_url,
+ int64 offset,
+ const WriteCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(url);
+ completion_callback_ = base::Bind(&WriteCallbackAdapter, callback);
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ file_system_operation_->GetWriteClosure(
+ url_request_context, url, blob_url, offset,
+ base::Bind(&self::DidWrite, base::Owned(this), callback))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::Truncate(
+ const FileSystemURL& url, int64 length,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(operation_runner_.get());
+ target_paths_.push_back(url);
+ completion_callback_ = callback;
+ scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
+ this,
+ base::Bind(&FileSystemOperation::Truncate,
+ base::Unretained(file_system_operation_),
+ url, length,
+ base::Bind(&self::DidFinish, base::Owned(this)))));
+ operation_runner_->PostOperationTask(task.Pass());
+}
+
+void SyncableFileSystemOperation::TouchFile(
+ const FileSystemURL& url,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time,
+ const StatusCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->TouchFile(url, last_access_time,
+ last_modified_time, callback);
+ delete this;
+}
+
+void SyncableFileSystemOperation::OpenFile(
+ const FileSystemURL& url,
+ int file_flags,
+ base::ProcessHandle peer_handle,
+ const OpenFileCallback& callback) {
+ NOTREACHED();
+ delete this;
+}
+
+void SyncableFileSystemOperation::NotifyCloseFile(
+ const FileSystemURL& url) {
+ NOTREACHED();
+ delete this;
+}
+
+void SyncableFileSystemOperation::Cancel(
+ const StatusCallback& cancel_callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(file_system_operation_);
+ DCHECK(file_system_operation_);
+ completion_callback_ = cancel_callback;
+ file_system_operation_->Cancel(
+ base::Bind(&self::DidFinish, base::Owned(this)));
+}
+
+LocalFileSystemOperation*
+SyncableFileSystemOperation::AsLocalFileSystemOperation() {
+ NOTREACHED();
+ return NULL;
+}
+
+void SyncableFileSystemOperation::CreateSnapshotFile(
+ const FileSystemURL& path,
+ const SnapshotFileCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ file_system_operation_->CreateSnapshotFile(path, callback);
+ delete this;
+}
+
+SyncableFileSystemOperation::SyncableFileSystemOperation(
+ FileSystemContext* file_system_context,
+ FileSystemOperation* file_system_operation) {
+ DCHECK(file_system_context);
+ DCHECK(file_system_operation);
+ operation_runner_ = file_system_context->sync_context()->operation_runner();
+ file_system_operation_ = file_system_operation->AsLocalFileSystemOperation();
+ DCHECK(file_system_operation_);
+}
+
+void SyncableFileSystemOperation::DidFinish(base::PlatformFileError status) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!completion_callback_.is_null());
+ if (operation_runner_.get())
+ operation_runner_->OnOperationCompleted(target_paths_);
+ completion_callback_.Run(status);
+}
+
+void SyncableFileSystemOperation::DidWrite(
+ const WriteCallback& callback,
+ base::PlatformFileError result,
+ int64 bytes,
+ bool complete) {
+ DCHECK(CalledOnValidThread());
+ if (!complete) {
+ callback.Run(result, bytes, complete);
+ return;
+ }
+ if (operation_runner_.get())
+ operation_runner_->OnOperationCompleted(target_paths_);
+ callback.Run(result, bytes, complete);
+}
+
+void SyncableFileSystemOperation::OnCancelled() {
+ DCHECK(!completion_callback_.is_null());
+ completion_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT);
+ delete file_system_operation_;
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/syncable/syncable_file_system_operation.h b/webkit/fileapi/syncable/syncable_file_system_operation.h
new file mode 100644
index 0000000..009759e
--- /dev/null
+++ b/webkit/fileapi/syncable/syncable_file_system_operation.h
@@ -0,0 +1,104 @@
+// 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_SYNCABLE_SYNCABLE_FILE_SYSTEM_OPERATION_H_
+#define WEBKIT_FILEAPI_SYNCABLE_SYNCABLE_FILE_SYSTEM_OPERATION_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "webkit/fileapi/file_system_operation.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace fileapi {
+
+class FileSystemContext;
+class SyncableFileOperationRunner;
+
+// A wrapper class of LocalFileSystemOperation for syncable file system.
+class WEBKIT_STORAGE_EXPORT SyncableFileSystemOperation
+ : public NON_EXPORTED_BASE(FileSystemOperation),
+ public base::NonThreadSafe {
+ public:
+ virtual ~SyncableFileSystemOperation();
+
+ // FileSystemOperation overrides.
+ virtual void CreateFile(const FileSystemURL& url,
+ bool exclusive,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void CreateDirectory(const FileSystemURL& url,
+ bool exclusive,
+ bool recursive,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void Copy(const FileSystemURL& src_url,
+ const FileSystemURL& dest_url,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void Move(const FileSystemURL& src_url,
+ const FileSystemURL& dest_url,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void DirectoryExists(const FileSystemURL& url,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void FileExists(const FileSystemURL& url,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void GetMetadata(const FileSystemURL& url,
+ const GetMetadataCallback& callback) OVERRIDE;
+ virtual void ReadDirectory(const FileSystemURL& url,
+ const ReadDirectoryCallback& callback) OVERRIDE;
+ virtual void Remove(const FileSystemURL& url, bool recursive,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void Write(const net::URLRequestContext* url_request_context,
+ const FileSystemURL& url,
+ const GURL& blob_url,
+ int64 offset,
+ const WriteCallback& callback) OVERRIDE;
+ virtual void Truncate(const FileSystemURL& url, int64 length,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void TouchFile(const FileSystemURL& url,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time,
+ const StatusCallback& callback) OVERRIDE;
+ virtual void OpenFile(const FileSystemURL& url,
+ int file_flags,
+ base::ProcessHandle peer_handle,
+ const OpenFileCallback& callback) OVERRIDE;
+ virtual void NotifyCloseFile(const FileSystemURL& url) OVERRIDE;
+ virtual void Cancel(const StatusCallback& cancel_callback) OVERRIDE;
+ virtual LocalFileSystemOperation* AsLocalFileSystemOperation() OVERRIDE;
+ virtual void CreateSnapshotFile(
+ const FileSystemURL& path,
+ const SnapshotFileCallback& callback) OVERRIDE;
+
+ private:
+ typedef SyncableFileSystemOperation self;
+ class QueueableTask;
+
+ // Only MountPointProviders can create a new operation directly.
+ friend class SandboxMountPointProvider;
+ SyncableFileSystemOperation(FileSystemContext* file_system_context,
+ FileSystemOperation* file_system_operation);
+
+ void DidFinish(base::PlatformFileError status);
+ void DidWrite(const WriteCallback& callback,
+ base::PlatformFileError result,
+ int64 bytes,
+ bool complete);
+
+ void OnCancelled();
+
+ base::WeakPtr<SyncableFileOperationRunner> operation_runner_;
+ LocalFileSystemOperation* file_system_operation_;
+ std::vector<FileSystemURL> target_paths_;
+
+ StatusCallback completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncableFileSystemOperation);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_SYNCABLE_SYNCABLE_FILE_SYSTEM_OPERATION_H_
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index 6a0e20c..cfeb95f 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -88,6 +88,10 @@
'../fileapi/syncable/local_file_sync_status.h',
'../fileapi/syncable/sync_status_code.cc',
'../fileapi/syncable/sync_status_code.h',
+ '../fileapi/syncable/syncable_file_operation_runner.cc',
+ '../fileapi/syncable/syncable_file_operation_runner.h',
+ '../fileapi/syncable/syncable_file_system_operation.cc',
+ '../fileapi/syncable/syncable_file_system_operation.h',
'../fileapi/syncable/syncable_file_system_util.cc',
'../fileapi/syncable/syncable_file_system_util.h',
'../fileapi/task_runner_bound_observer_list.h',