// Copyright 2013 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/browser/fileapi/file_system_operation_runner.h" #include "base/bind.h" #include "net/url_request/url_request_context.h" #include "webkit/browser/fileapi/file_observers.h" #include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_writer_delegate.h" #include "webkit/browser/fileapi/local_file_system_operation.h" #include "webkit/common/blob/shareable_file_reference.h" namespace fileapi { typedef FileSystemOperationRunner::OperationID OperationID; const OperationID FileSystemOperationRunner::kErrorOperationID = -1; FileSystemOperationRunner::~FileSystemOperationRunner() { } void FileSystemOperationRunner::Shutdown() { operations_.Clear(); } OperationID FileSystemOperationRunner::CreateFile( const FileSystemURL& url, bool exclusive, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->CreateFile( url, exclusive, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::CreateDirectory( const FileSystemURL& url, bool exclusive, bool recursive, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->CreateDirectory( url, exclusive, recursive, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::Copy( const FileSystemURL& src_url, const FileSystemURL& dest_url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(dest_url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, dest_url); PrepareForRead(id, src_url); operation->Copy( src_url, dest_url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::Move( const FileSystemURL& src_url, const FileSystemURL& dest_url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(dest_url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, dest_url); PrepareForWrite(id, src_url); operation->Move( src_url, dest_url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::DirectoryExists( const FileSystemURL& url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForRead(id, url); operation->DirectoryExists( url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::FileExists( const FileSystemURL& url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForRead(id, url); operation->FileExists( url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::GetMetadata( const FileSystemURL& url, const GetMetadataCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error, base::PlatformFileInfo()); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForRead(id, url); operation->GetMetadata( url, base::Bind(&FileSystemOperationRunner::DidGetMetadata, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::ReadDirectory( const FileSystemURL& url, const ReadDirectoryCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error, std::vector(), false); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForRead(id, url); operation->ReadDirectory( url, base::Bind(&FileSystemOperationRunner::DidReadDirectory, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::Remove( const FileSystemURL& url, bool recursive, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->Remove( url, recursive, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::Write( const net::URLRequestContext* url_request_context, const FileSystemURL& url, const GURL& blob_url, int64 offset, const WriteCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error, 0, true); return kErrorOperationID; } scoped_ptr writer( file_system_context_->CreateFileStreamWriter(url, offset)); if (!writer) { // Write is not supported. callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, 0, true); return kErrorOperationID; } DCHECK(blob_url.is_valid()); scoped_ptr writer_delegate( new FileWriterDelegate(writer.Pass())); scoped_ptr blob_request(url_request_context->CreateRequest( blob_url, writer_delegate.get())); OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->Write( url, writer_delegate.Pass(), blob_request.Pass(), base::Bind(&FileSystemOperationRunner::DidWrite, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::Truncate( const FileSystemURL& url, int64 length, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->Truncate( url, length, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } void FileSystemOperationRunner::Cancel( OperationID id, const StatusCallback& callback) { FileSystemOperation* operation = operations_.Lookup(id); if (!operation) { // The operation is already finished; report that we failed to stop it. callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); return; } operation->Cancel(callback); } OperationID FileSystemOperationRunner::TouchFile( const FileSystemURL& url, const base::Time& last_access_time, const base::Time& last_modified_time, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForWrite(id, url); operation->TouchFile( url, last_access_time, last_modified_time, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::OpenFile( const FileSystemURL& url, int file_flags, base::ProcessHandle peer_handle, const OpenFileCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error, base::kInvalidPlatformFileValue, base::Closure(), base::ProcessHandle()); return kErrorOperationID; } OperationID id = operations_.Add(operation); if (file_flags & (base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_TRUNCATED | base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE | base::PLATFORM_FILE_DELETE_ON_CLOSE | base::PLATFORM_FILE_WRITE_ATTRIBUTES)) { PrepareForWrite(id, url); } else { PrepareForRead(id, url); } operation->OpenFile( url, file_flags, peer_handle, base::Bind(&FileSystemOperationRunner::DidOpenFile, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::CreateSnapshotFile( const FileSystemURL& url, const SnapshotFileCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, &error); if (!operation) { callback.Run(error, base::PlatformFileInfo(), base::FilePath(), NULL); return kErrorOperationID; } OperationID id = operations_.Add(operation); PrepareForRead(id, url); operation->CreateSnapshotFile( url, base::Bind(&FileSystemOperationRunner::DidCreateSnapshot, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::CopyInForeignFile( const base::FilePath& src_local_disk_path, const FileSystemURL& dest_url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation( dest_url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); operation->AsLocalFileSystemOperation()->CopyInForeignFile( src_local_disk_path, dest_url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::RemoveFile( const FileSystemURL& url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); operation->AsLocalFileSystemOperation()->RemoveFile( url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::RemoveDirectory( const FileSystemURL& url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation(url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); operation->AsLocalFileSystemOperation()->RemoveDirectory( url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::CopyFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation( src_url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); operation->AsLocalFileSystemOperation()->CopyFileLocal( src_url, dest_url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } OperationID FileSystemOperationRunner::MoveFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation( src_url, &error); if (!operation) { callback.Run(error); return kErrorOperationID; } OperationID id = operations_.Add(operation); operation->AsLocalFileSystemOperation()->MoveFileLocal( src_url, dest_url, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), id, callback)); return id; } base::PlatformFileError FileSystemOperationRunner::SyncGetPlatformPath( const FileSystemURL& url, base::FilePath* platform_path) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = CreateLocalFileSystemOperation(url, &error); if (!operation) return error; return operation->AsLocalFileSystemOperation()->SyncGetPlatformPath( url, platform_path); } FileSystemOperationRunner::FileSystemOperationRunner( FileSystemContext* file_system_context) : file_system_context_(file_system_context) {} void FileSystemOperationRunner::DidFinish( OperationID id, const StatusCallback& callback, base::PlatformFileError rv) { callback.Run(rv); FinishOperation(id); } void FileSystemOperationRunner::DidGetMetadata( OperationID id, const GetMetadataCallback& callback, base::PlatformFileError rv, const base::PlatformFileInfo& file_info) { callback.Run(rv, file_info); FinishOperation(id); } void FileSystemOperationRunner::DidReadDirectory( OperationID id, const ReadDirectoryCallback& callback, base::PlatformFileError rv, const std::vector& entries, bool has_more) { callback.Run(rv, entries, has_more); if (rv != base::PLATFORM_FILE_OK || !has_more) FinishOperation(id); } void FileSystemOperationRunner::DidWrite( OperationID id, const WriteCallback& callback, base::PlatformFileError rv, int64 bytes, bool complete) { callback.Run(rv, bytes, complete); if (rv != base::PLATFORM_FILE_OK || complete) FinishOperation(id); } void FileSystemOperationRunner::DidOpenFile( OperationID id, const OpenFileCallback& callback, base::PlatformFileError rv, base::PlatformFile file, const base::Closure& on_close_callback, base::ProcessHandle peer_handle) { callback.Run(rv, file, on_close_callback, peer_handle); FinishOperation(id); } void FileSystemOperationRunner::DidCreateSnapshot( OperationID id, const SnapshotFileCallback& callback, base::PlatformFileError rv, const base::PlatformFileInfo& file_info, const base::FilePath& platform_path, const scoped_refptr& file_ref) { callback.Run(rv, file_info, platform_path, file_ref); FinishOperation(id); } FileSystemOperation* FileSystemOperationRunner::CreateLocalFileSystemOperation( const FileSystemURL& url, base::PlatformFileError* error) { FileSystemOperation* operation = file_system_context_->CreateFileSystemOperation(url, error); if (!operation) return NULL; if (!operation->AsLocalFileSystemOperation()) { *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; delete operation; return NULL; } return operation; } void FileSystemOperationRunner::PrepareForWrite(OperationID id, const FileSystemURL& url) { if (file_system_context_->GetUpdateObservers(url.type())) { file_system_context_->GetUpdateObservers(url.type())->Notify( &FileUpdateObserver::OnStartUpdate, MakeTuple(url)); } write_target_urls_[id].insert(url); } void FileSystemOperationRunner::PrepareForRead(OperationID id, const FileSystemURL& url) { if (file_system_context_->GetAccessObservers(url.type())) { file_system_context_->GetAccessObservers(url.type())->Notify( &FileAccessObserver::OnAccess, MakeTuple(url)); } } void FileSystemOperationRunner::FinishOperation(OperationID id) { OperationToURLSet::iterator found = write_target_urls_.find(id); if (found != write_target_urls_.end()) { const FileSystemURLSet& urls = found->second; for (FileSystemURLSet::const_iterator iter = urls.begin(); iter != urls.end(); ++iter) { if (file_system_context_->GetUpdateObservers(iter->type())) { file_system_context_->GetUpdateObservers(iter->type())->Notify( &FileUpdateObserver::OnEndUpdate, MakeTuple(*iter)); } } write_target_urls_.erase(found); } DCHECK(operations_.Lookup(id)); operations_.Remove(id); } } // namespace fileapi