| // 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/blob/shareable_file_reference.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/sandbox_mount_point_provider.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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (!is_directory_operation_enabled_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_INVALID_OPERATION); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| file_system_operation_->DirectoryExists(url, callback); |
| delete this; |
| } |
| |
| void SyncableFileSystemOperation::FileExists( |
| const FileSystemURL& url, |
| const StatusCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| file_system_operation_->FileExists(url, callback); |
| delete this; |
| } |
| |
| void SyncableFileSystemOperation::GetMetadata( |
| const FileSystemURL& url, |
| const GetMetadataCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (!operation_runner_) { |
| callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, |
| base::PlatformFileInfo(), FilePath()); |
| delete file_system_operation_; |
| delete this; |
| return; |
| } |
| file_system_operation_->GetMetadata(url, callback); |
| delete this; |
| } |
| |
| void SyncableFileSystemOperation::ReadDirectory( |
| const FileSystemURL& url, |
| const ReadDirectoryCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (!operation_runner_) { |
| callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, FileEntryList(), false); |
| delete file_system_operation_; |
| delete this; |
| return; |
| } |
| // This is a read operation and there'd be no hard to let it go even if |
| // directory operation is disabled. (And we should allow this if it's made |
| // on the root directory) |
| file_system_operation_->ReadDirectory(url, callback); |
| delete this; |
| } |
| |
| void SyncableFileSystemOperation::Remove( |
| const FileSystemURL& url, bool recursive, |
| const StatusCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, 0, true); |
| delete file_system_operation_; |
| delete this; |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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()); |
| if (!operation_runner_) { |
| AbortOperation(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| 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_); |
| completion_callback_ = cancel_callback; |
| file_system_operation_->Cancel( |
| base::Bind(&self::DidFinish, base::Owned(this))); |
| } |
| |
| LocalFileSystemOperation* |
| SyncableFileSystemOperation::AsLocalFileSystemOperation() { |
| // This must be called for nested sub-task operations |
| // where we don't need extra task queueing. |
| // Just return the internal file_system_operation_ and let this |
| // instance goes away when the operation dies. |
| file_system_operation_->set_termination_callback( |
| base::Bind(&SyncableFileSystemOperation::Destruct, |
| base::Unretained(this))); |
| return file_system_operation_; |
| } |
| |
| void SyncableFileSystemOperation::CreateSnapshotFile( |
| const FileSystemURL& path, |
| const SnapshotFileCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (!operation_runner_) { |
| callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, |
| base::PlatformFileInfo(), FilePath(), NULL); |
| delete file_system_operation_; |
| delete this; |
| return; |
| } |
| 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); |
| file_system_operation_ = file_system_operation->AsLocalFileSystemOperation(); |
| DCHECK(file_system_operation_); |
| if (!file_system_context->sync_context()) { |
| // Syncable FileSystem is opened in a file system context which doesn't |
| // support (or is not initialized for) the API. |
| // Returning here to leave operation_runner_ as NULL. |
| return; |
| } |
| operation_runner_ = file_system_context->sync_context()->operation_runner(); |
| is_directory_operation_enabled_ = file_system_context->sandbox_provider()-> |
| is_sync_directory_operation_enabled(); |
| } |
| |
| 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_; |
| } |
| |
| void SyncableFileSystemOperation::AbortOperation( |
| const StatusCallback& callback, |
| base::PlatformFileError error) { |
| callback.Run(error); |
| delete file_system_operation_; |
| delete this; |
| } |
| |
| void SyncableFileSystemOperation::Destruct() { |
| delete this; |
| } |
| |
| } // namespace fileapi |