diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-28 02:08:07 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-28 02:08:07 +0000 |
commit | c6f9203a46211bb08b84f73b4e1df303f1ab8c42 (patch) | |
tree | b8774c1e80f5e37c7f31a889023962fb3eb1bb7e /webkit/browser | |
parent | ba7e19a2704f1c4772d1f1f3a45a2b2b78c0bbd7 (diff) | |
download | chromium_src-c6f9203a46211bb08b84f73b4e1df303f1ab8c42.zip chromium_src-c6f9203a46211bb08b84f73b4e1df303f1ab8c42.tar.gz chromium_src-c6f9203a46211bb08b84f73b4e1df303f1ab8c42.tar.bz2 |
Move browser-specific FileAPI code from webkit/fileapi to webkit/browser/fileapi
Moving following files:
- file_system_context*
- file_system_operation*
- file_system_url*
- and all others but not in syncable/ ones
BUG=239710
TBR=avi@chromium.org, tzik@chromium.org
Review URL: https://codereview.chromium.org/15859007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202482 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/browser')
85 files changed, 6910 insertions, 102 deletions
diff --git a/webkit/browser/fileapi/async_file_test_helper.cc b/webkit/browser/fileapi/async_file_test_helper.cc new file mode 100644 index 0000000..b71e9e5 --- /dev/null +++ b/webkit/browser/fileapi/async_file_test_helper.cc @@ -0,0 +1,235 @@ +// Copyright (c) 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 "base/bind.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/quota/quota_manager.h" + +namespace fileapi { + +namespace { + +typedef FileSystemOperation::FileEntryList FileEntryList; + +void AssignAndQuit(base::RunLoop* run_loop, + base::PlatformFileError* result_out, + base::PlatformFileError result) { + *result_out = result; + run_loop->Quit(); +} + +base::Callback<void(base::PlatformFileError)> +AssignAndQuitCallback(base::RunLoop* run_loop, + base::PlatformFileError* result) { + return base::Bind(&AssignAndQuit, run_loop, base::Unretained(result)); +} + +void GetMetadataCallback(base::RunLoop* run_loop, + base::PlatformFileError* result_out, + base::PlatformFileInfo* file_info_out, + base::FilePath* platform_path_out, + base::PlatformFileError result, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path) { + *result_out = result; + if (file_info_out) + *file_info_out = file_info; + if (platform_path_out) + *platform_path_out = platform_path; + run_loop->Quit(); +} + +void ReadDirectoryCallback(base::RunLoop* run_loop, + base::PlatformFileError* result_out, + FileEntryList* entries_out, + base::PlatformFileError result, + const FileEntryList& entries, + bool has_more) { + *result_out = result; + *entries_out = entries; + if (result != base::PLATFORM_FILE_OK || !has_more) + run_loop->Quit(); +} + +void DidGetUsageAndQuota(quota::QuotaStatusCode* status_out, + int64* usage_out, + int64* quota_out, + quota::QuotaStatusCode status, + int64 usage, + int64 quota) { + if (status_out) + *status_out = status; + if (usage_out) + *usage_out = usage; + if (quota_out) + *quota_out = quota; +} + +} // namespace + +const int64 AsyncFileTestHelper::kDontCheckSize = -1; + +base::PlatformFileError AsyncFileTestHelper::Copy( + FileSystemContext* context, + const FileSystemURL& src, + const FileSystemURL& dest) { + DCHECK(context); + FileSystemOperation* operation = + context->CreateFileSystemOperation(dest, NULL); + EXPECT_TRUE(operation != NULL); + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + base::RunLoop run_loop; + operation->Copy(src, dest, AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::Move( + FileSystemContext* context, + const FileSystemURL& src, + const FileSystemURL& dest) { + FileSystemOperation* operation = + context->CreateFileSystemOperation(dest, NULL); + EXPECT_TRUE(operation != NULL); + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + base::RunLoop run_loop; + operation->Move(src, dest, AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::Remove( + FileSystemContext* context, + const FileSystemURL& url, + bool recursive) { + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + base::RunLoop run_loop; + operation->Remove(url, recursive, AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::ReadDirectory( + FileSystemContext* context, + const FileSystemURL& url, + FileEntryList* entries) { + DCHECK(entries); + entries->clear(); + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + base::RunLoop run_loop; + operation->ReadDirectory( + url, base::Bind(&ReadDirectoryCallback, &run_loop, &result, entries)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::CreateDirectory( + FileSystemContext* context, + const FileSystemURL& url) { + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + base::RunLoop run_loop; + operation->CreateDirectory(url, + false /* exclusive */, + false /* recursive */, + AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::CreateFile( + FileSystemContext* context, + const FileSystemURL& url) { + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + base::RunLoop run_loop; + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + operation->CreateFile(url, false /* exclusive */, + AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::TruncateFile( + FileSystemContext* context, + const FileSystemURL& url, + size_t size) { + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + base::RunLoop run_loop; + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + operation->Truncate(url, size, + AssignAndQuitCallback(&run_loop, &result)); + run_loop.Run(); + return result; +} + +base::PlatformFileError AsyncFileTestHelper::GetMetadata( + FileSystemContext* context, + const FileSystemURL& url, + base::PlatformFileInfo* file_info, + base::FilePath* platform_path) { + base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; + base::RunLoop run_loop; + FileSystemOperation* operation = + context->CreateFileSystemOperation(url, NULL); + EXPECT_TRUE(operation != NULL); + operation->GetMetadata(url, base::Bind(&GetMetadataCallback, + &run_loop, &result, + file_info, platform_path)); + run_loop.Run(); + return result; +} + +bool AsyncFileTestHelper::FileExists( + FileSystemContext* context, + const FileSystemURL& url, + int64 expected_size) { + base::PlatformFileInfo file_info; + base::PlatformFileError result = GetMetadata(context, url, &file_info, NULL); + if (result != base::PLATFORM_FILE_OK || file_info.is_directory) + return false; + return expected_size == kDontCheckSize || file_info.size == expected_size; +} + +bool AsyncFileTestHelper::DirectoryExists( + FileSystemContext* context, + const FileSystemURL& url) { + base::PlatformFileInfo file_info; + base::PlatformFileError result = GetMetadata(context, url, &file_info, NULL); + return (result == base::PLATFORM_FILE_OK) && file_info.is_directory; +} + +quota::QuotaStatusCode AsyncFileTestHelper::GetUsageAndQuota( + quota::QuotaManager* quota_manager, + const GURL& origin, + FileSystemType type, + int64* usage, + int64* quota) { + quota::QuotaStatusCode status = quota::kQuotaStatusUnknown; + quota_manager->GetUsageAndQuota( + origin, + FileSystemTypeToQuotaStorageType(type), + base::Bind(&DidGetUsageAndQuota, &status, usage, quota)); + base::MessageLoop::current()->RunUntilIdle(); + return status; +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/async_file_test_helper.h b/webkit/browser/fileapi/async_file_test_helper.h new file mode 100644 index 0000000..8e400a4 --- /dev/null +++ b/webkit/browser/fileapi/async_file_test_helper.h @@ -0,0 +1,90 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_TEST_HELPER_H_ +#define WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_TEST_HELPER_H_ + +#include "base/basictypes.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/quota/quota_status_code.h" + +namespace quota { +class QuotaManager; +} + +namespace fileapi { + +class FileSystemContext; +class FileSystemURL; + +// A helper class to perform async file operations in a synchronous way. +class AsyncFileTestHelper { + public: + typedef FileSystemOperation::FileEntryList FileEntryList; + + static const int64 kDontCheckSize; + + // Performs Copy from |src| to |dest| and returns the status code. + static base::PlatformFileError Copy(FileSystemContext* context, + const FileSystemURL& src, + const FileSystemURL& dest); + + // Performs Move from |src| to |dest| and returns the status code. + static base::PlatformFileError Move(FileSystemContext* context, + const FileSystemURL& src, + const FileSystemURL& dest); + + // Removes the given |url|. + static base::PlatformFileError Remove(FileSystemContext* context, + const FileSystemURL& url, + bool recursive); + + // Performs ReadDirectory on |url|. + static base::PlatformFileError ReadDirectory(FileSystemContext* context, + const FileSystemURL& url, + FileEntryList* entries); + + // Creates a directory at |url|. + static base::PlatformFileError CreateDirectory(FileSystemContext* context, + const FileSystemURL& url); + + // Creates a file at |url|. + static base::PlatformFileError CreateFile(FileSystemContext* context, + const FileSystemURL& url); + + // Truncates the file |url| to |size|. + static base::PlatformFileError TruncateFile(FileSystemContext* context, + const FileSystemURL& url, + size_t size); + + // Retrieves PlatformFileInfo for |url| and populates |file_info|. + static base::PlatformFileError GetMetadata(FileSystemContext* context, + const FileSystemURL& url, + base::PlatformFileInfo* file_info, + base::FilePath* platform_path); + + // Returns true if a file exists at |url| with |size|. If |size| is + // kDontCheckSize it doesn't check the file size (but just check its + // existence). + static bool FileExists(FileSystemContext* context, + const FileSystemURL& url, + int64 size); + + // Returns true if a directory exists at |url|. + static bool DirectoryExists(FileSystemContext* context, + const FileSystemURL& url); + + // Returns usage and quota. It's valid to pass NULL to |usage| and/or |quota|. + static quota::QuotaStatusCode GetUsageAndQuota( + quota::QuotaManager* quota_manager, + const GURL& origin, + FileSystemType type, + int64* usage, + int64* quota); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_TEST_HELPER_H_ diff --git a/webkit/browser/fileapi/async_file_util.h b/webkit/browser/fileapi/async_file_util.h new file mode 100644 index 0000000..190e69b --- /dev/null +++ b/webkit/browser/fileapi/async_file_util.h @@ -0,0 +1,338 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_H_ +#define WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_H_ + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/files/file_util_proxy.h" +#include "base/platform_file.h" +#include "webkit/fileapi/directory_entry.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class Time; +} + +namespace webkit_blob { +class ShareableFileReference; +} + +namespace fileapi { + +class FileSystemOperationContext; +class FileSystemURL; + +// An interface which provides filesystem-specific file operations for +// LocalFileSystemOperation. +// +// Each filesystem which needs to be dispatched from LocalFileSystemOperation +// must implement this interface or a synchronous version of interface: +// FileSystemFileUtil. +// +class WEBKIT_STORAGE_EXPORT AsyncFileUtil { + public: + typedef base::Callback< + void(base::PlatformFileError result)> StatusCallback; + + typedef base::FileUtilProxy::CreateOrOpenCallback CreateOrOpenCallback; + + typedef base::Callback< + void(base::PlatformFileError result, + bool created)> EnsureFileExistsCallback; + + typedef base::Callback< + void(base::PlatformFileError result, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path)> GetFileInfoCallback; + + typedef std::vector<DirectoryEntry> EntryList; + typedef base::Callback< + void(base::PlatformFileError result, + const EntryList& file_list, + bool has_more)> ReadDirectoryCallback; + + typedef base::Callback< + void(base::PlatformFileError result, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path, + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref + )> CreateSnapshotFileCallback; + + AsyncFileUtil() {} + virtual ~AsyncFileUtil() {} + + // Creates or opens a file with the given flags. + // If PLATFORM_FILE_CREATE is set in |file_flags| it always tries to create + // a new file at the given |url| and calls back with + // PLATFORM_FILE_ERROR_FILE_EXISTS if the |url| already exists. + // + // LocalFileSystemOperation::OpenFile calls this. + // This is used only by Pepper/NaCL File API. + // + // This returns false if it fails to post an async task. + // + virtual bool CreateOrOpen( + FileSystemOperationContext* context, + const FileSystemURL& url, + int file_flags, + const CreateOrOpenCallback& callback) = 0; + + // Ensures that the given |url| exist. This creates a empty new file + // at |url| if the |url| does not exist. + // + // LocalFileSystemOperation::CreateFile calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_OK and created==true if a file has not existed and + // is created at |url|. + // - PLATFORM_FILE_OK and created==false if the file already exists. + // - Other error code (with created=false) if a file hasn't existed yet + // and there was an error while creating a new file. + // + virtual bool EnsureFileExists( + FileSystemOperationContext* context, + const FileSystemURL& url, + const EnsureFileExistsCallback& callback) = 0; + + // Creates directory at given url. + // + // LocalFileSystemOperation::CreateDirectory calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if the |url|'s parent directory + // does not exist and |recursive| is false. + // - PLATFORM_FILE_ERROR_EXISTS if a directory already exists at |url| + // and |exclusive| is true. + // - PLATFORM_FILE_ERROR_EXISTS if a file already exists at |url| + // (regardless of |exclusive| value). + // - Other error code if it failed to create a directory. + // + virtual bool CreateDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + bool exclusive, + bool recursive, + const StatusCallback& callback) = 0; + + // Retrieves the information about a file. + // + // LocalFileSystemOperation::GetMetadata calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if the file doesn't exist. + // - Other error code if there was an error while retrieving the file info. + // + virtual bool GetFileInfo( + FileSystemOperationContext* context, + const FileSystemURL& url, + const GetFileInfoCallback& callback) = 0; + + // Reads contents of a directory at |path|. + // + // LocalFileSystemOperation::ReadDirectory calls this. + // + // Note that the |name| field of each entry in |file_list| + // returned by |callback| should have a base file name + // of the entry relative to the directory, but not an absolute path. + // + // (E.g. if ReadDirectory is called for a directory + // 'path/to/dir' and the directory has entries 'a' and 'b', + // the returned |file_list| should include entries whose names + // are 'a' and 'b', but not '/path/to/dir/a' and '/path/to/dir/b'.) + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if the target directory doesn't exist. + // - PLATFORM_FILE_ERROR_NOT_A_DIRECTORY if an entry exists at |url| but + // is a file (not a directory). + // + virtual bool ReadDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const ReadDirectoryCallback& callback) = 0; + + // Modifies timestamps of a file or directory at |url| with + // |last_access_time| and |last_modified_time|. The function DOES NOT + // create a file unlike 'touch' command on Linux. + // + // LocalFileSystemOperation::TouchFile calls this. + // This is used only by Pepper/NaCL File API. + // + // This returns false if it fails to post an async task. + virtual bool Touch( + FileSystemOperationContext* context, + const FileSystemURL& url, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) = 0; + + // Truncates a file at |path| to |length|. If |length| is larger than + // the original file size, the file will be extended, and the extended + // part is filled with null bytes. + // + // LocalFileSystemOperation::Truncate calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if the file doesn't exist. + // + virtual bool Truncate( + FileSystemOperationContext* context, + const FileSystemURL& url, + int64 length, + const StatusCallback& callback) = 0; + + // Copies a file from |src_url| to |dest_url|. + // This must be called for files that belong to the same filesystem + // (i.e. type() and origin() of the |src_url| and |dest_url| must match). + // + // LocalFileSystemOperation::Copy calls this for same-filesystem copy case. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| + // or the parent directory of |dest_url| does not exist. + // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file. + // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and + // is not a file. + // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and + // its parent path is a file. + // + virtual bool CopyFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) = 0; + + // Moves a local file from |src_url| to |dest_url|. + // This must be called for files that belong to the same filesystem + // (i.e. type() and origin() of the |src_url| and |dest_url| must match). + // + // LocalFileSystemOperation::Move calls this for same-filesystem move case. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| + // or the parent directory of |dest_url| does not exist. + // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file. + // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and + // is not a file. + // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and + // its parent path is a file. + // + virtual bool MoveFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) = 0; + + // Copies in a single file from a different filesystem. + // + // LocalFileSystemOperation::Copy or Move calls this for cross-filesystem + // cases. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_file_path| + // or the parent directory of |dest_url| does not exist. + // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and + // is not a file. + // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and + // its parent path is a file. + // + virtual bool CopyInForeignFile( + FileSystemOperationContext* context, + const base::FilePath& src_file_path, + const FileSystemURL& dest_url, + const StatusCallback& callback) = 0; + + // Deletes a single file. + // + // LocalFileSystemOperation::RemoveFile calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist. + // - PLATFORM_FILE_ERROR_NOT_A_FILE if |url| is not a file. + // + virtual bool DeleteFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) = 0; + + // Removes a single empty directory. + // + // LocalFileSystemOperation::RemoveDirectory calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist. + // - PLATFORM_FILE_ERROR_NOT_A_DIRECTORY if |url| is not a directory. + // - PLATFORM_FILE_ERROR_NOT_EMPTY if |url| is not empty. + // + virtual bool DeleteDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) = 0; + + // Creates a local snapshot file for a given |url| and returns the + // metadata and platform path of the snapshot file via |callback|. + // In regular filesystem cases the implementation may simply return + // the metadata of the file itself (as well as GetMetadata does), + // while in non-regular filesystem case the backend may create a + // temporary snapshot file which holds the file data and return + // the metadata of the temporary file. + // + // In the callback, it returns: + // |file_info| is the metadata of the snapshot file created. + // |platform_path| is the full absolute platform path to the snapshot + // file created. If a file is not backed by a real local file in + // the implementor's FileSystem, the implementor must create a + // local snapshot file and return the path of the created file. + // + // If implementors creates a temporary file for snapshotting and wants + // FileAPI backend to take care of the lifetime of the file (so that + // it won't get deleted while JS layer has any references to the created + // File/Blob object), it should return non-empty |file_ref|. + // Via the |file_ref| implementors can schedule a file deletion + // or arbitrary callbacks when the last reference of File/Blob is dropped. + // + // LocalFileSystemOperation::CreateSnapshotFile calls this. + // + // This returns false if it fails to post an async task. + // + // This reports following error code via |callback|: + // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist. + // - PLATFORM_FILE_ERROR_NOT_A_FILE if |url| exists but is a directory. + // + // The field values of |file_info| are undefined (implementation + // dependent) in error cases, and the caller should always + // check the return code. + virtual bool CreateSnapshotFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const CreateSnapshotFileCallback& callback) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AsyncFileUtil); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_H_ diff --git a/webkit/browser/fileapi/async_file_util_adapter.cc b/webkit/browser/fileapi/async_file_util_adapter.cc new file mode 100644 index 0000000..5046bbe --- /dev/null +++ b/webkit/browser/fileapi/async_file_util_adapter.cc @@ -0,0 +1,307 @@ +// Copyright (c) 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/async_file_util_adapter.h" + +#include "base/bind.h" +#include "base/sequenced_task_runner.h" +#include "base/task_runner_util.h" +#include "webkit/blob/shareable_file_reference.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/fileapi/file_system_util.h" + +using base::Bind; +using base::Callback; +using base::Owned; +using base::PlatformFileError; +using base::Unretained; +using webkit_blob::ShareableFileReference; + +namespace fileapi { + +namespace { + +class EnsureFileExistsHelper { + public: + EnsureFileExistsHelper() : error_(base::PLATFORM_FILE_OK), created_(false) {} + + void RunWork(FileSystemFileUtil* file_util, + FileSystemOperationContext* context, + const FileSystemURL& url) { + error_ = file_util->EnsureFileExists(context, url, &created_); + } + + void Reply(const AsyncFileUtil::EnsureFileExistsCallback& callback) { + if (!callback.is_null()) + callback.Run(error_, created_); + } + + private: + base::PlatformFileError error_; + bool created_; + DISALLOW_COPY_AND_ASSIGN(EnsureFileExistsHelper); +}; + +class GetFileInfoHelper { + public: + GetFileInfoHelper() + : error_(base::PLATFORM_FILE_OK) {} + + void GetFileInfo(FileSystemFileUtil* file_util, + FileSystemOperationContext* context, + const FileSystemURL& url) { + error_ = file_util->GetFileInfo(context, url, &file_info_, &platform_path_); + } + + void CreateSnapshotFile(FileSystemFileUtil* file_util, + FileSystemOperationContext* context, + const FileSystemURL& url) { + scoped_file_ = file_util->CreateSnapshotFile( + context, url, &error_, &file_info_, &platform_path_); + } + + void ReplyFileInfo(const AsyncFileUtil::GetFileInfoCallback& callback) { + if (!callback.is_null()) + callback.Run(error_, file_info_, platform_path_); + } + + void ReplySnapshotFile( + const AsyncFileUtil::CreateSnapshotFileCallback& callback) { + if (!callback.is_null()) + callback.Run(error_, file_info_, platform_path_, + ShareableFileReference::GetOrCreate(scoped_file_.Pass())); + } + + private: + base::PlatformFileError error_; + base::PlatformFileInfo file_info_; + base::FilePath platform_path_; + webkit_blob::ScopedFile scoped_file_; + DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper); +}; + +class ReadDirectoryHelper { + public: + ReadDirectoryHelper() : error_(base::PLATFORM_FILE_OK) {} + + void RunWork(FileSystemFileUtil* file_util, + FileSystemOperationContext* context, + const FileSystemURL& url) { + base::PlatformFileInfo file_info; + base::FilePath platform_path; + PlatformFileError error = file_util->GetFileInfo( + context, url, &file_info, &platform_path); + if (error != base::PLATFORM_FILE_OK) { + error_ = error; + return; + } + if (!file_info.is_directory) { + error_ = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; + return; + } + + scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( + file_util->CreateFileEnumerator(context, url)); + + base::FilePath current; + while (!(current = file_enum->Next()).empty()) { + DirectoryEntry entry; + entry.is_directory = file_enum->IsDirectory(); + entry.name = VirtualPath::BaseName(current).value(); + entry.size = file_enum->Size(); + entry.last_modified_time = file_enum->LastModifiedTime(); + entries_.push_back(entry); + } + error_ = base::PLATFORM_FILE_OK; + } + + void Reply(const AsyncFileUtil::ReadDirectoryCallback& callback) { + if (!callback.is_null()) + callback.Run(error_, entries_, false /* has_more */); + } + + private: + base::PlatformFileError error_; + std::vector<DirectoryEntry> entries_; + DISALLOW_COPY_AND_ASSIGN(ReadDirectoryHelper); +}; + +} // namespace + +AsyncFileUtilAdapter::AsyncFileUtilAdapter( + FileSystemFileUtil* sync_file_util) + : sync_file_util_(sync_file_util) { + DCHECK(sync_file_util_.get()); +} + +AsyncFileUtilAdapter::~AsyncFileUtilAdapter() { +} + +bool AsyncFileUtilAdapter::CreateOrOpen( + FileSystemOperationContext* context, + const FileSystemURL& url, + int file_flags, + const CreateOrOpenCallback& callback) { + return base::FileUtilProxy::RelayCreateOrOpen( + context->task_runner(), + Bind(&FileSystemFileUtil::CreateOrOpen, Unretained(sync_file_util_.get()), + context, url, file_flags), + Bind(&FileSystemFileUtil::Close, Unretained(sync_file_util_.get()), + context), + callback); +} + +bool AsyncFileUtilAdapter::EnsureFileExists( + FileSystemOperationContext* context, + const FileSystemURL& url, + const EnsureFileExistsCallback& callback) { + EnsureFileExistsHelper* helper = new EnsureFileExistsHelper; + return context->task_runner()->PostTaskAndReply( + FROM_HERE, + Bind(&EnsureFileExistsHelper::RunWork, Unretained(helper), + sync_file_util_.get(), context, url), + Bind(&EnsureFileExistsHelper::Reply, Owned(helper), callback)); +} + +bool AsyncFileUtilAdapter::CreateDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + bool exclusive, + bool recursive, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::CreateDirectory, + Unretained(sync_file_util_.get()), + context, url, exclusive, recursive), + callback); +} + +bool AsyncFileUtilAdapter::GetFileInfo( + FileSystemOperationContext* context, + const FileSystemURL& url, + const GetFileInfoCallback& callback) { + GetFileInfoHelper* helper = new GetFileInfoHelper; + return context->task_runner()->PostTaskAndReply( + FROM_HERE, + Bind(&GetFileInfoHelper::GetFileInfo, Unretained(helper), + sync_file_util_.get(), context, url), + Bind(&GetFileInfoHelper::ReplyFileInfo, Owned(helper), callback)); +} + +bool AsyncFileUtilAdapter::ReadDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const ReadDirectoryCallback& callback) { + ReadDirectoryHelper* helper = new ReadDirectoryHelper; + return context->task_runner()->PostTaskAndReply( + FROM_HERE, + Bind(&ReadDirectoryHelper::RunWork, Unretained(helper), + sync_file_util_.get(), context, url), + Bind(&ReadDirectoryHelper::Reply, Owned(helper), callback)); +} + +bool AsyncFileUtilAdapter::Touch( + FileSystemOperationContext* context, + const FileSystemURL& url, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::Touch, Unretained(sync_file_util_.get()), + context, url, last_access_time, last_modified_time), + callback); +} + +bool AsyncFileUtilAdapter::Truncate( + FileSystemOperationContext* context, + const FileSystemURL& url, + int64 length, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::Truncate, Unretained(sync_file_util_.get()), + context, url, length), + callback); +} + +bool AsyncFileUtilAdapter::CopyFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::CopyOrMoveFile, + Unretained(sync_file_util_.get()), + context, src_url, dest_url, true /* copy */), + callback); +} + +bool AsyncFileUtilAdapter::MoveFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::CopyOrMoveFile, + Unretained(sync_file_util_.get()), + context, src_url, dest_url, false /* copy */), + callback); +} + +bool AsyncFileUtilAdapter::CopyInForeignFile( + FileSystemOperationContext* context, + const base::FilePath& src_file_path, + const FileSystemURL& dest_url, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::CopyInForeignFile, + Unretained(sync_file_util_.get()), + context, src_file_path, dest_url), + callback); +} + +bool AsyncFileUtilAdapter::DeleteFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::DeleteFile, + Unretained(sync_file_util_.get()), context, url), + callback); +} + +bool AsyncFileUtilAdapter::DeleteDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) { + return base::PostTaskAndReplyWithResult( + context->task_runner(), FROM_HERE, + Bind(&FileSystemFileUtil::DeleteDirectory, + Unretained(sync_file_util_.get()), + context, url), + callback); +} + +bool AsyncFileUtilAdapter::CreateSnapshotFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const CreateSnapshotFileCallback& callback) { + GetFileInfoHelper* helper = new GetFileInfoHelper; + return context->task_runner()->PostTaskAndReply( + FROM_HERE, + Bind(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper), + sync_file_util_.get(), context, url), + Bind(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper), callback)); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/async_file_util_adapter.h b/webkit/browser/fileapi/async_file_util_adapter.h new file mode 100644 index 0000000..3a85028 --- /dev/null +++ b/webkit/browser/fileapi/async_file_util_adapter.h @@ -0,0 +1,109 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_ADAPTER_H_ +#define WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_ADAPTER_H_ + +#include "base/memory/scoped_ptr.h" +#include "webkit/browser/fileapi/async_file_util.h" + +namespace fileapi { + +class FileSystemFileUtil; + +// An adapter class for FileSystemFileUtil classes to provide asynchronous +// interface. +// +// A filesystem can do either: +// - implement a synchronous version of FileUtil by extending +// FileSystemFileUtil and atach it to this adapter, or +// - directly implement AsyncFileUtil. +// +class WEBKIT_STORAGE_EXPORT_PRIVATE AsyncFileUtilAdapter + : public AsyncFileUtil { + public: + // Creates a new AsyncFileUtil for |sync_file_util|. This takes the + // ownership of |sync_file_util|. (This doesn't take scoped_ptr<> just + // to save extra make_scoped_ptr; in all use cases a new fresh FileUtil is + // created only for this adapter.) + explicit AsyncFileUtilAdapter(FileSystemFileUtil* sync_file_util); + + virtual ~AsyncFileUtilAdapter(); + + FileSystemFileUtil* sync_file_util() { + return sync_file_util_.get(); + } + + // AsyncFileUtil overrides. + virtual bool CreateOrOpen( + FileSystemOperationContext* context, + const FileSystemURL& url, + int file_flags, + const CreateOrOpenCallback& callback) OVERRIDE; + virtual bool EnsureFileExists( + FileSystemOperationContext* context, + const FileSystemURL& url, + const EnsureFileExistsCallback& callback) OVERRIDE; + virtual bool CreateDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + bool exclusive, + bool recursive, + const StatusCallback& callback) OVERRIDE; + virtual bool GetFileInfo( + FileSystemOperationContext* context, + const FileSystemURL& url, + const GetFileInfoCallback& callback) OVERRIDE; + virtual bool ReadDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const ReadDirectoryCallback& callback) OVERRIDE; + virtual bool Touch( + FileSystemOperationContext* context, + const FileSystemURL& url, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) OVERRIDE; + virtual bool Truncate( + FileSystemOperationContext* context, + const FileSystemURL& url, + int64 length, + const StatusCallback& callback) OVERRIDE; + virtual bool CopyFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) OVERRIDE; + virtual bool MoveFileLocal( + FileSystemOperationContext* context, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const StatusCallback& callback) OVERRIDE; + virtual bool CopyInForeignFile( + FileSystemOperationContext* context, + const base::FilePath& src_file_path, + const FileSystemURL& dest_url, + const StatusCallback& callback) OVERRIDE; + virtual bool DeleteFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; + virtual bool DeleteDirectory( + FileSystemOperationContext* context, + const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; + virtual bool CreateSnapshotFile( + FileSystemOperationContext* context, + const FileSystemURL& url, + const CreateSnapshotFileCallback& callback) OVERRIDE; + + private: + scoped_ptr<FileSystemFileUtil> sync_file_util_; + + DISALLOW_COPY_AND_ASSIGN(AsyncFileUtilAdapter); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_ADAPTER_H_ diff --git a/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc b/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc index 383b7ef..9f12178 100644 --- a/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc +++ b/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc @@ -8,16 +8,16 @@ #include "base/files/scoped_temp_dir.h" #include "base/message_loop.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" #include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/mock_file_system_context.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/test_mount_point_provider.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/test_mount_point_provider.h" #include "webkit/quota/mock_special_storage_policy.h" namespace fileapi { diff --git a/webkit/browser/fileapi/cross_operation_delegate.cc b/webkit/browser/fileapi/cross_operation_delegate.cc index 7c1c7d6..f0b74b8 100644 --- a/webkit/browser/fileapi/cross_operation_delegate.cc +++ b/webkit/browser/fileapi/cross_operation_delegate.cc @@ -8,10 +8,10 @@ #include "base/files/file_path.h" #include "webkit/blob/shareable_file_reference.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/local_file_system_operation.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" namespace fileapi { diff --git a/webkit/browser/fileapi/dump_file_system.cc b/webkit/browser/fileapi/dump_file_system.cc new file mode 100644 index 0000000..452dc5f --- /dev/null +++ b/webkit/browser/fileapi/dump_file_system.cc @@ -0,0 +1,205 @@ +// Copyright (c) 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. +// +// A tool to dump HTML5 filesystem from CUI. +// +// Usage: +// +// ./out/Release/dump_file_system [options] <filesystem dir> [origin]... +// +// If no origin is specified, this dumps all origins in the profile dir. +// +// Available options: +// +// -t : dumps temporary files instead of persistent. +// -s : dumps syncable files instead of persistent. +// -l : more information will be displayed. +// +// The format of -l option is: +// +// === ORIGIN origin_name origin_dir === +// file_name file_id file_size file_content_path +// ... +// +// where file_name has a trailing slash, file_size is the number of +// children, and file_content_path is empty if the file is a directory. +// + +#include <stdio.h> +#include <stdlib.h> + +#include <stack> +#include <string> +#include <utility> +#include <vector> + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/format_macros.h" +#include "base/stringprintf.h" +#include "webkit/browser/fileapi/obfuscated_file_util.h" +#include "webkit/browser/fileapi/sandbox_directory_database.h" +#include "webkit/browser/fileapi/sandbox_mount_point_provider.h" +#include "webkit/browser/fileapi/sandbox_origin_database.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_util.h" + +namespace { + +bool g_opt_long; +fileapi::FileSystemType g_opt_fs_type = fileapi::kFileSystemTypePersistent; + +void ShowMessageAndExit(const std::string& msg) { + fprintf(stderr, "%s\n", msg.c_str()); + exit(EXIT_FAILURE); +} + +void ShowUsageAndExit(const std::string& arg0) { + ShowMessageAndExit( + "Usage: " + arg0 + + " [-l] [-t] [-s] <filesystem dir> [origin]..."); +} + +} // namespace + +namespace fileapi { + +static void DumpDirectoryTree(const std::string& origin_name, + base::FilePath origin_dir) { + origin_dir = origin_dir.Append( + ObfuscatedFileUtil::GetDirectoryNameForType(g_opt_fs_type)); + + printf("=== ORIGIN %s %s ===\n", + origin_name.c_str(), FilePathToString(origin_dir).c_str()); + + if (!file_util::DirectoryExists(origin_dir)) + return; + + SandboxDirectoryDatabase directory_db(origin_dir); + SandboxDirectoryDatabase::FileId root_id; + if (!directory_db.GetFileWithPath(StringToFilePath("/"), &root_id)) + return; + + std::stack<std::pair<SandboxDirectoryDatabase::FileId, + std::string> > paths; + paths.push(std::make_pair(root_id, "")); + while (!paths.empty()) { + SandboxDirectoryDatabase::FileId id = paths.top().first; + const std::string dirname = paths.top().second; + paths.pop(); + + SandboxDirectoryDatabase::FileInfo info; + if (!directory_db.GetFileInfo(id, &info)) { + ShowMessageAndExit(base::StringPrintf("GetFileInfo failed for %"PRId64, + id)); + } + + const std::string name = + dirname + "/" + FilePathToString(base::FilePath(info.name)); + std::vector<SandboxDirectoryDatabase::FileId> children; + if (info.is_directory()) { + if (!directory_db.ListChildren(id, &children)) { + ShowMessageAndExit(base::StringPrintf( + "ListChildren failed for %s (%"PRId64")", + info.name.c_str(), id)); + } + + for (size_t j = children.size(); j; j--) + paths.push(make_pair(children[j-1], name)); + } + + // +1 for the leading extra slash. + const char* display_name = name.c_str() + 1; + const char* directory_suffix = info.is_directory() ? "/" : ""; + if (g_opt_long) { + int64 size; + if (info.is_directory()) { + size = static_cast<int64>(children.size()); + } else { + file_util::GetFileSize(origin_dir.Append(info.data_path), &size); + } + // TODO(hamaji): Modification time? + printf("%s%s %"PRId64" %"PRId64" %s\n", + display_name, + directory_suffix, + id, + size, + FilePathToString(info.data_path).c_str()); + } else { + printf("%s%s\n", display_name, directory_suffix); + } + } +} + +static void DumpOrigin(const base::FilePath& file_system_dir, + const std::string& origin_name) { + SandboxOriginDatabase origin_db(file_system_dir); + base::FilePath origin_dir; + if (!origin_db.HasOriginPath(origin_name)) { + ShowMessageAndExit("Origin " + origin_name + " is not in " + + FilePathToString(file_system_dir)); + } + + if (!origin_db.GetPathForOrigin(origin_name, &origin_dir)) { + ShowMessageAndExit("Failed to get path of origin " + origin_name + + " in " + FilePathToString(file_system_dir)); + } + DumpDirectoryTree(origin_name, file_system_dir.Append(origin_dir)); +} + +static void DumpFileSystem(const base::FilePath& file_system_dir) { + SandboxOriginDatabase origin_db(file_system_dir); + std::vector<SandboxOriginDatabase::OriginRecord> origins; + origin_db.ListAllOrigins(&origins); + for (size_t i = 0; i < origins.size(); i++) { + const SandboxOriginDatabase::OriginRecord& origin = origins[i]; + DumpDirectoryTree(origin.origin, file_system_dir.Append(origin.path)); + puts(""); + } +} + +} // namespace fileapi + +int main(int argc, char* argv[]) { + const char* arg0 = argv[0]; + std::string username = "Default"; + while (true) { + if (argc < 2) + ShowUsageAndExit(arg0); + + if (std::string(argv[1]) == "-l") { + g_opt_long = true; + argc--; + argv++; + } else if (std::string(argv[1]) == "-t") { + g_opt_fs_type = fileapi::kFileSystemTypeTemporary; + argc--; + argv++; + } else if (std::string(argv[1]) == "-s") { + g_opt_fs_type = fileapi::kFileSystemTypeSyncable; + argc--; + argv++; + } else { + break; + } + } + + if (argc < 2) + ShowUsageAndExit(arg0); + + const base::FilePath file_system_dir = fileapi::StringToFilePath(argv[1]); + if (!file_util::DirectoryExists(file_system_dir)) { + ShowMessageAndExit(fileapi::FilePathToString(file_system_dir) + + " is not a filesystem directory"); + } + + if (argc == 2) { + fileapi::DumpFileSystem(file_system_dir); + } else { + for (int i = 2; i < argc; i++) { + fileapi::DumpOrigin(file_system_dir, argv[i]); + } + } + return 0; +} diff --git a/webkit/browser/fileapi/external_mount_points.cc b/webkit/browser/fileapi/external_mount_points.cc index 5dddfe3..269c09e 100644 --- a/webkit/browser/fileapi/external_mount_points.cc +++ b/webkit/browser/fileapi/external_mount_points.cc @@ -8,8 +8,8 @@ #include "base/lazy_instance.h" #include "base/path_service.h" #include "base/stl_util.h" -#include "webkit/fileapi/file_system_url.h" -#include "webkit/fileapi/remote_file_system_proxy.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/remote_file_system_proxy.h" namespace { diff --git a/webkit/browser/fileapi/external_mount_points_unittest.cc b/webkit/browser/fileapi/external_mount_points_unittest.cc index 8dad0f7..cc2acbe 100644 --- a/webkit/browser/fileapi/external_mount_points_unittest.cc +++ b/webkit/browser/fileapi/external_mount_points_unittest.cc @@ -8,7 +8,7 @@ #include "base/files/file_path.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_url.h" #define FPL FILE_PATH_LITERAL diff --git a/webkit/browser/fileapi/file_permission_policy.cc b/webkit/browser/fileapi/file_permission_policy.cc new file mode 100644 index 0000000..1645487 --- /dev/null +++ b/webkit/browser/fileapi/file_permission_policy.cc @@ -0,0 +1,34 @@ +// Copyright (c) 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_permission_policy.h" + +#include "base/platform_file.h" + +namespace fileapi { + +const int kReadFilePermissions = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_ASYNC; + +const int kWriteFilePermissions = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_EXCLUSIVE_WRITE | + base::PLATFORM_FILE_ASYNC | + base::PLATFORM_FILE_WRITE_ATTRIBUTES; + +const int kCreateFilePermissions = base::PLATFORM_FILE_CREATE; + +const int kOpenFilePermissions = 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; + + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_permission_policy.h b/webkit/browser/fileapi/file_permission_policy.h new file mode 100644 index 0000000..baecbb1 --- /dev/null +++ b/webkit/browser/fileapi/file_permission_policy.h @@ -0,0 +1,34 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_FILE_PERMISSION_POLICY_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_PERMISSION_POLICY_H_ + +#include "webkit/storage/webkit_storage_export.h" + +namespace fileapi { + +WEBKIT_STORAGE_EXPORT extern const int kReadFilePermissions; +WEBKIT_STORAGE_EXPORT extern const int kWriteFilePermissions; +WEBKIT_STORAGE_EXPORT extern const int kCreateFilePermissions; +WEBKIT_STORAGE_EXPORT extern const int kOpenFilePermissions; + +enum FilePermissionPolicy { + // Any access should be always denied. + FILE_PERMISSION_ALWAYS_DENY, + + // Any access should be always allowed. (This should be used only for + // access to sandbox directories.) + FILE_PERMISSION_ALWAYS_ALLOW, + + // Access should be examined by per-file permission policy. + FILE_PERMISSION_USE_FILE_PERMISSION, + + // Access should be examined by per-filesystem permission policy. + FILE_PERMISSION_USE_FILESYSTEM_PERMISSION, +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_PERMISSION_POLICY_H_ diff --git a/webkit/browser/fileapi/file_stream_writer.h b/webkit/browser/fileapi/file_stream_writer.h new file mode 100644 index 0000000..fe79a24 --- /dev/null +++ b/webkit/browser/fileapi/file_stream_writer.h @@ -0,0 +1,72 @@ +// 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_BROWSER_FILEAPI_FILE_STREAM_WRITER_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_STREAM_WRITER_H_ + +#include "base/basictypes.h" +#include "net/base/completion_callback.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace net { +class IOBuffer; +} + +namespace fileapi { + +// A generic interface for writing to a file-like object. +class WEBKIT_STORAGE_EXPORT_PRIVATE FileStreamWriter { + public: + // Closes the file. If there's an in-flight operation, it is canceled (i.e., + // the callback function associated with the operation is not called). + virtual ~FileStreamWriter() {} + + // Writes to the current cursor position asynchronously. + // + // Up to buf_len bytes will be written. (In other words, partial + // writes are allowed.) If the write completed synchronously, it returns + // the number of bytes written. If the operation could not be performed, it + // returns an error code. Otherwise, net::ERR_IO_PENDING is returned, and the + // 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; + + // Cancels an in-flight async operation. + // + // If the cancel is finished synchronously, it returns net::OK. If the + // cancel could not be performed, it returns an error code. Especially when + // there is no in-flight operation, net::ERR_UNEXPECTED is returned. + // Otherwise, net::ERR_IO_PENDING is returned, and the callback will be run on + // the thread where Cancel() was called when the cancel has completed. It is + // invalid to call Cancel() more than once on the same async operation. + // + // In either case, the callback function passed to the in-flight async + // operation is dismissed immediately when Cancel() is called, and thus + // will never be called. + virtual int Cancel(const net::CompletionCallback& callback) = 0; + + // Flushes the data written so far. + // + // If the flush finished synchronously, it return net::OK. If the flush could + // not be performed, it returns an error code. Otherwise, net::ERR_IO_PENDING + // is returned, and the callback will be run on the thread where Flush() was + // called when the flush has completed. + // + // It is invalid to call Flush while there is an in-flight async operation. + virtual int Flush(const net::CompletionCallback& callback) = 0; +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_STREAM_WRITER_H_ diff --git a/webkit/browser/fileapi/file_system_context.cc b/webkit/browser/fileapi/file_system_context.cc new file mode 100644 index 0000000..a8c42aa --- /dev/null +++ b/webkit/browser/fileapi/file_system_context.cc @@ -0,0 +1,421 @@ +// 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/browser/fileapi/file_system_context.h" + +#include "base/bind.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "googleurl/src/gurl.h" +#include "webkit/blob/file_stream_reader.h" +#include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_stream_writer.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_options.h" +#include "webkit/browser/fileapi/file_system_quota_client.h" +#include "webkit/browser/fileapi/file_system_task_runners.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/isolated_context.h" +#include "webkit/browser/fileapi/isolated_mount_point_provider.h" +#include "webkit/browser/fileapi/mount_points.h" +#include "webkit/browser/fileapi/sandbox_mount_point_provider.h" +#include "webkit/browser/fileapi/test_mount_point_provider.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/syncable/local_file_change_tracker.h" +#include "webkit/fileapi/syncable/local_file_sync_context.h" +#include "webkit/fileapi/syncable/syncable_file_system_util.h" +#include "webkit/quota/quota_manager.h" +#include "webkit/quota/special_storage_policy.h" + +#if defined(OS_CHROMEOS) +#include "webkit/chromeos/fileapi/cros_mount_point_provider.h" +#endif + +using quota::QuotaClient; + +namespace fileapi { + +namespace { + +QuotaClient* CreateQuotaClient( + FileSystemContext* context, + bool is_incognito) { + return new FileSystemQuotaClient(context, is_incognito); +} + +void DidOpenFileSystem( + const FileSystemContext::OpenFileSystemCallback& callback, + const GURL& filesystem_root, + const std::string& filesystem_name, + base::PlatformFileError error) { + callback.Run(error, filesystem_name, filesystem_root); +} + +} // namespace + +FileSystemContext::FileSystemContext( + scoped_ptr<FileSystemTaskRunners> task_runners, + ExternalMountPoints* external_mount_points, + quota::SpecialStoragePolicy* special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy, + ScopedVector<FileSystemMountPointProvider> additional_providers, + const base::FilePath& partition_path, + const FileSystemOptions& options) + : task_runners_(task_runners.Pass()), + quota_manager_proxy_(quota_manager_proxy), + sandbox_provider_( + new SandboxMountPointProvider( + quota_manager_proxy, + task_runners_->file_task_runner(), + partition_path, + options, + special_storage_policy)), + isolated_provider_(new IsolatedMountPointProvider()), + additional_providers_(additional_providers.Pass()), + external_mount_points_(external_mount_points), + partition_path_(partition_path) { + DCHECK(task_runners_.get()); + + if (quota_manager_proxy) { + quota_manager_proxy->RegisterClient(CreateQuotaClient( + this, options.is_incognito())); + } + + RegisterMountPointProvider(sandbox_provider_.get()); + RegisterMountPointProvider(isolated_provider_.get()); + +#if defined(OS_CHROMEOS) + // TODO(kinuko): Move this out of webkit/fileapi layer. + DCHECK(external_mount_points); + external_provider_.reset( + new chromeos::CrosMountPointProvider( + special_storage_policy, + external_mount_points, + ExternalMountPoints::GetSystemInstance())); + RegisterMountPointProvider(external_provider_.get()); +#endif + + for (ScopedVector<FileSystemMountPointProvider>::const_iterator iter = + additional_providers_.begin(); + iter != additional_providers_.end(); ++iter) { + RegisterMountPointProvider(*iter); + } + + // Additional mount points must be added before regular system-wide + // mount points. + if (external_mount_points) + url_crackers_.push_back(external_mount_points); + url_crackers_.push_back(ExternalMountPoints::GetSystemInstance()); + url_crackers_.push_back(IsolatedContext::GetInstance()); +} + +bool FileSystemContext::DeleteDataForOriginOnFileThread( + const GURL& origin_url) { + DCHECK(task_runners_->file_task_runner()->RunsTasksOnCurrentThread()); + DCHECK(sandbox_provider()); + DCHECK(origin_url == origin_url.GetOrigin()); + + // Delete temporary and persistent data. + return + (sandbox_provider()->DeleteOriginDataOnFileThread( + this, quota_manager_proxy(), origin_url, + kFileSystemTypeTemporary) == + base::PLATFORM_FILE_OK) && + (sandbox_provider()->DeleteOriginDataOnFileThread( + this, quota_manager_proxy(), origin_url, + kFileSystemTypePersistent) == + base::PLATFORM_FILE_OK) && + (sandbox_provider()->DeleteOriginDataOnFileThread( + this, quota_manager_proxy(), origin_url, + kFileSystemTypeSyncable) == + base::PLATFORM_FILE_OK); +} + +FileSystemQuotaUtil* +FileSystemContext::GetQuotaUtil(FileSystemType type) const { + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) + return NULL; + return mount_point_provider->GetQuotaUtil(); +} + +AsyncFileUtil* FileSystemContext::GetAsyncFileUtil( + FileSystemType type) const { + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) + return NULL; + return mount_point_provider->GetAsyncFileUtil(type); +} + +FileSystemFileUtil* FileSystemContext::GetFileUtil( + FileSystemType type) const { + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) + return NULL; + return mount_point_provider->GetFileUtil(type); +} + +CopyOrMoveFileValidatorFactory* +FileSystemContext::GetCopyOrMoveFileValidatorFactory( + FileSystemType type, base::PlatformFileError* error_code) const { + DCHECK(error_code); + *error_code = base::PLATFORM_FILE_OK; + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) + return NULL; + return mount_point_provider->GetCopyOrMoveFileValidatorFactory( + type, error_code); +} + +FileSystemMountPointProvider* FileSystemContext::GetMountPointProvider( + FileSystemType type) const { + MountPointProviderMap::const_iterator found = provider_map_.find(type); + if (found != provider_map_.end()) + return found->second; + NOTREACHED() << "Unknown filesystem type: " << type; + return NULL; +} + +const UpdateObserverList* FileSystemContext::GetUpdateObservers( + FileSystemType type) const { + // Currently update observer is only available in SandboxMountPointProvider + // and TestMountPointProvider. + // TODO(kinuko): Probably GetUpdateObservers() virtual method should be + // added to FileSystemMountPointProvider interface and be called like + // other GetFoo() methods do. + if (SandboxMountPointProvider::IsSandboxType(type)) + return sandbox_provider()->GetUpdateObservers(type); + if (type != kFileSystemTypeTest) + return NULL; + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + return static_cast<TestMountPointProvider*>( + mount_point_provider)->GetUpdateObservers(type); +} + +SandboxMountPointProvider* +FileSystemContext::sandbox_provider() const { + return sandbox_provider_.get(); +} + +ExternalFileSystemMountPointProvider* +FileSystemContext::external_provider() const { + return external_provider_.get(); +} + +void FileSystemContext::OpenFileSystem( + const GURL& origin_url, + FileSystemType type, + bool create, + const OpenFileSystemCallback& callback) { + DCHECK(!callback.is_null()); + + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) { + callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, std::string(), GURL()); + return; + } + + GURL root_url = GetFileSystemRootURI(origin_url, type); + std::string name = GetFileSystemName(origin_url, type); + + mount_point_provider->ValidateFileSystemRoot( + origin_url, type, create, + base::Bind(&DidOpenFileSystem, callback, root_url, name)); +} + +void FileSystemContext::OpenSyncableFileSystem( + const std::string& mount_name, + const GURL& origin_url, + FileSystemType type, + bool create, + const OpenFileSystemCallback& callback) { + DCHECK(!callback.is_null()); + + DCHECK(type == kFileSystemTypeSyncable); + + GURL root_url = sync_file_system::GetSyncableFileSystemRootURI( + origin_url, mount_name); + std::string name = GetFileSystemName(origin_url, kFileSystemTypeSyncable); + + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + DCHECK(mount_point_provider); + mount_point_provider->ValidateFileSystemRoot( + origin_url, type, create, + base::Bind(&DidOpenFileSystem, callback, root_url, name)); +} + +void FileSystemContext::DeleteFileSystem( + const GURL& origin_url, + FileSystemType type, + const DeleteFileSystemCallback& callback) { + DCHECK(origin_url == origin_url.GetOrigin()); + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(type); + if (!mount_point_provider) { + callback.Run(base::PLATFORM_FILE_ERROR_SECURITY); + return; + } + + mount_point_provider->DeleteFileSystem(origin_url, type, this, callback); +} + +FileSystemOperation* FileSystemContext::CreateFileSystemOperation( + const FileSystemURL& url, base::PlatformFileError* error_code) { + if (!url.is_valid()) { + if (error_code) + *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL; + return NULL; + } + + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(url.type()); + if (!mount_point_provider) { + if (error_code) + *error_code = base::PLATFORM_FILE_ERROR_FAILED; + return NULL; + } + + base::PlatformFileError fs_error = base::PLATFORM_FILE_OK; + FileSystemOperation* operation = + mount_point_provider->CreateFileSystemOperation(url, this, &fs_error); + + if (error_code) + *error_code = fs_error; + return operation; +} + +scoped_ptr<webkit_blob::FileStreamReader> +FileSystemContext::CreateFileStreamReader( + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time) { + if (!url.is_valid()) + return scoped_ptr<webkit_blob::FileStreamReader>(); + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(url.type()); + if (!mount_point_provider) + return scoped_ptr<webkit_blob::FileStreamReader>(); + return mount_point_provider->CreateFileStreamReader( + url, offset, expected_modification_time, this); +} + +scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter( + const FileSystemURL& url, + int64 offset) { + if (!url.is_valid()) + return scoped_ptr<FileStreamWriter>(); + FileSystemMountPointProvider* mount_point_provider = + GetMountPointProvider(url.type()); + if (!mount_point_provider) + return scoped_ptr<FileStreamWriter>(); + return mount_point_provider->CreateFileStreamWriter(url, offset, this); +} + +void FileSystemContext::SetLocalFileChangeTracker( + scoped_ptr<sync_file_system::LocalFileChangeTracker> tracker) { + DCHECK(!change_tracker_.get()); + DCHECK(tracker.get()); + change_tracker_ = tracker.Pass(); + sandbox_provider_->AddSyncableFileUpdateObserver( + change_tracker_.get(), + task_runners_->file_task_runner()); + sandbox_provider_->AddSyncableFileChangeObserver( + change_tracker_.get(), + task_runners_->file_task_runner()); +} + +void FileSystemContext::set_sync_context( + sync_file_system::LocalFileSyncContext* sync_context) { + sync_context_ = sync_context; +} + +FileSystemURL FileSystemContext::CrackURL(const GURL& url) const { + return CrackFileSystemURL(FileSystemURL(url)); +} + +FileSystemURL FileSystemContext::CreateCrackedFileSystemURL( + const GURL& origin, + FileSystemType type, + const base::FilePath& path) const { + return CrackFileSystemURL(FileSystemURL(origin, type, path)); +} + +FileSystemContext::~FileSystemContext() { + task_runners_->file_task_runner()->DeleteSoon( + FROM_HERE, change_tracker_.release()); +} + +void FileSystemContext::DeleteOnCorrectThread() const { + if (!task_runners_->io_task_runner()->RunsTasksOnCurrentThread() && + task_runners_->io_task_runner()->DeleteSoon(FROM_HERE, this)) { + return; + } + delete this; +} + +FileSystemURL FileSystemContext::CrackFileSystemURL( + const FileSystemURL& url) const { + if (!url.is_valid()) + return FileSystemURL(); + + // The returned value in case there is no crackers which can crack the url. + // This is valid situation for non isolated/external file systems. + FileSystemURL current = url; + + // File system may be mounted multiple times (e.g., an isolated filesystem on + // top of an external filesystem). Hence cracking needs to be iterated. + for (;;) { + FileSystemURL cracked = current; + for (size_t i = 0; i < url_crackers_.size(); ++i) { + if (!url_crackers_[i]->HandlesFileSystemMountType(current.type())) + continue; + cracked = url_crackers_[i]->CrackFileSystemURL(current); + if (cracked.is_valid()) + break; + } + if (cracked == current) + break; + current = cracked; + } + return current; +} + +void FileSystemContext::RegisterMountPointProvider( + FileSystemMountPointProvider* provider) { + const FileSystemType mount_types[] = { + kFileSystemTypeTemporary, + kFileSystemTypePersistent, + kFileSystemTypeIsolated, + kFileSystemTypeExternal, + }; + // Register mount point providers for public mount types. + for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) { + if (provider->CanHandleType(mount_types[j])) { + const bool inserted = provider_map_.insert( + std::make_pair(mount_types[j], provider)).second; + DCHECK(inserted); + } + } + // Register mount point providers for internal types. + for (int t = kFileSystemInternalTypeEnumStart + 1; + t < kFileSystemInternalTypeEnumEnd; ++t) { + FileSystemType type = static_cast<FileSystemType>(t); + if (provider->CanHandleType(type)) { + const bool inserted = provider_map_.insert( + std::make_pair(type, provider)).second; + DCHECK(inserted); + } + } +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_context.h b/webkit/browser/fileapi/file_system_context.h new file mode 100644 index 0000000..1c15e31 --- /dev/null +++ b/webkit/browser/fileapi/file_system_context.h @@ -0,0 +1,309 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/platform_file.h" +#include "base/sequenced_task_runner_helpers.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/task_runner_bound_observer_list.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class FilePath; +} + +namespace chrome { +class NativeMediaFileUtilTest; +} + +namespace quota { +class QuotaManagerProxy; +class SpecialStoragePolicy; +} + +namespace sync_file_system { +class LocalFileChangeTracker; +class LocalFileSyncContext; +} + +namespace webkit_blob { +class BlobURLRequestJobTest; +class FileStreamReader; +} + +namespace fileapi { + +class AsyncFileUtil; +class CopyOrMoveFileValidatorFactory; +class ExternalFileSystemMountPointProvider; +class ExternalMountPoints; +class FileStreamWriter; +class FileSystemFileUtil; +class FileSystemMountPointProvider; +class FileSystemOperation; +class FileSystemOptions; +class FileSystemQuotaUtil; +class FileSystemTaskRunners; +class FileSystemURL; +class IsolatedMountPointProvider; +class MountPoints; +class SandboxMountPointProvider; + +struct DefaultContextDeleter; + +// This class keeps and provides a file system context for FileSystem API. +// An instance of this class is created and owned by profile. +class WEBKIT_STORAGE_EXPORT FileSystemContext + : public base::RefCountedThreadSafe<FileSystemContext, + DefaultContextDeleter> { + public: + // task_runners->file_task_runner() is used as default TaskRunner. + // Unless a MountPointProvider is overridden in CreateFileSystemOperation, + // it is used for all file operations and file related meta operations. + // The code assumes that + // task_runners->file_task_runner()->RunsTasksOnCurrentThread() + // returns false if the current task is not running on the thread that allows + // blocking file operations (like SequencedWorkerPool implementation does). + // + // |external_mount_points| contains non-system external mount points available + // in the context. If not NULL, it will be used during URL cracking. On + // ChromeOS, it will be passed to external_mount_point_provider. + // |external_mount_points| may be NULL only on platforms different from + // ChromeOS (i.e. platforms that don't use external_mount_point_provider). + // + // |additional_providers| are added to the internal provider map + // to serve filesystem requests for non-regular types. + // If none is given, this context only handles HTML5 Sandbox FileSystem + // and Drag-and-drop Isolated FileSystem requests. + FileSystemContext( + scoped_ptr<FileSystemTaskRunners> task_runners, + ExternalMountPoints* external_mount_points, + quota::SpecialStoragePolicy* special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy, + ScopedVector<FileSystemMountPointProvider> additional_providers, + const base::FilePath& partition_path, + const FileSystemOptions& options); + + bool DeleteDataForOriginOnFileThread(const GURL& origin_url); + + quota::QuotaManagerProxy* quota_manager_proxy() const { + return quota_manager_proxy_.get(); + } + + // Returns a quota util for a given filesystem type. This may + // return NULL if the type does not support the usage tracking or + // it is not a quota-managed storage. + FileSystemQuotaUtil* GetQuotaUtil(FileSystemType type) const; + + // Returns the appropriate AsyncFileUtil instance for the given |type|. + AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) const; + + // Returns the appropriate FileUtil instance for the given |type|. + // This may return NULL if it is given an invalid type or the filesystem + // does not support synchronous file operations. + FileSystemFileUtil* GetFileUtil(FileSystemType type) const; + + // Returns the appropriate CopyOrMoveFileValidatorFactory for the given + // |type|. If |error_code| is PLATFORM_FILE_OK and the result is NULL, + // then no validator is required. + CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( + FileSystemType type, base::PlatformFileError* error_code) const; + + // Returns the mount point provider instance for the given |type|. + // This may return NULL if it is given an invalid or unsupported filesystem + // type. + FileSystemMountPointProvider* GetMountPointProvider( + FileSystemType type) const; + + // Returns update observers for the given filesystem type. + const UpdateObserverList* GetUpdateObservers(FileSystemType type) const; + + // Returns a FileSystemMountPointProvider instance for sandboxed filesystem + // types (e.g. TEMPORARY or PERSISTENT). This is equivalent to calling + // GetMountPointProvider(kFileSystemType{Temporary, Persistent}). + SandboxMountPointProvider* sandbox_provider() const; + + // Returns a FileSystemMountPointProvider instance for external filesystem + // type, which is used only by chromeos for now. This is equivalent to + // calling GetMountPointProvider(kFileSystemTypeExternal). + ExternalFileSystemMountPointProvider* external_provider() const; + + // Used for OpenFileSystem. + typedef base::Callback<void(base::PlatformFileError result, + const std::string& name, + const GURL& root)> OpenFileSystemCallback; + + // Used for DeleteFileSystem. + typedef base::Callback<void(base::PlatformFileError result)> + DeleteFileSystemCallback; + + // Opens the filesystem for the given |origin_url| and |type|, and dispatches + // |callback| on completion. + // If |create| is true this may actually set up a filesystem instance + // (e.g. by creating the root directory or initializing the database + // entry etc). + void OpenFileSystem( + const GURL& origin_url, + FileSystemType type, + bool create, + const OpenFileSystemCallback& callback); + + // Opens a syncable filesystem for the given |origin_url|. + // The file system is internally mounted as an external file system at the + // given |mount_name|. + // Currently only kFileSystemTypeSyncable type is supported. + void OpenSyncableFileSystem( + const std::string& mount_name, + const GURL& origin_url, + FileSystemType type, + bool create, + const OpenFileSystemCallback& callback); + + // Deletes the filesystem for the given |origin_url| and |type|. This should + // be called on the IO Thread. + void DeleteFileSystem( + const GURL& origin_url, + FileSystemType type, + const DeleteFileSystemCallback& callback); + + // Creates a new FileSystemOperation instance by getting an appropriate + // MountPointProvider for |url| and calling the provider's corresponding + // CreateFileSystemOperation method. + // The resolved MountPointProvider could perform further specialization + // depending on the filesystem type pointed by the |url|. + FileSystemOperation* CreateFileSystemOperation( + const FileSystemURL& url, + base::PlatformFileError* error_code); + + // Creates new FileStreamReader instance to read a file pointed by the given + // filesystem URL |url| starting from |offset|. |expected_modification_time| + // specifies the expected last modification if the value is non-null, the + // reader will check the underlying file's actual modification time to see if + // the file has been modified, and if it does any succeeding read operations + // should fail with ERR_UPLOAD_FILE_CHANGED error. + // This method internally cracks the |url|, get an appropriate + // MountPointProvider for the URL and call the provider's CreateFileReader. + // The resolved MountPointProvider could perform further specialization + // depending on the filesystem type pointed by the |url|. + scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader( + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time); + + // Creates new FileStreamWriter instance to write into a file pointed by + // |url| from |offset|. + scoped_ptr<FileStreamWriter> CreateFileStreamWriter( + const FileSystemURL& url, + int64 offset); + + FileSystemTaskRunners* task_runners() { return task_runners_.get(); } + + sync_file_system::LocalFileChangeTracker* change_tracker() { + return change_tracker_.get(); + } + void SetLocalFileChangeTracker( + scoped_ptr<sync_file_system::LocalFileChangeTracker> tracker); + + sync_file_system::LocalFileSyncContext* sync_context() { + return sync_context_.get(); + } + void set_sync_context(sync_file_system::LocalFileSyncContext* sync_context); + + const base::FilePath& partition_path() const { return partition_path_; } + + // Same as |CrackFileSystemURL|, but cracks FileSystemURL created from |url|. + FileSystemURL CrackURL(const GURL& url) const; + // Same as |CrackFileSystemURL|, but cracks FileSystemURL created from method + // arguments. + FileSystemURL CreateCrackedFileSystemURL(const GURL& origin, + FileSystemType type, + const base::FilePath& path) const; + + private: + typedef std::map<FileSystemType, FileSystemMountPointProvider*> + MountPointProviderMap; + + // These classes know the target filesystem (i.e. sandbox filesystem) + // supports synchronous FileUtil. + friend class LocalFileSystemOperation; + friend class sync_file_system::LocalFileChangeTracker; + friend class sync_file_system::LocalFileSyncContext; + + // Deleters. + friend struct DefaultContextDeleter; + friend class base::DeleteHelper<FileSystemContext>; + friend class base::RefCountedThreadSafe<FileSystemContext, + DefaultContextDeleter>; + ~FileSystemContext(); + + void DeleteOnCorrectThread() const; + + // For non-cracked isolated and external mount points, returns a FileSystemURL + // created by cracking |url|. The url is cracked using MountPoints registered + // as |url_crackers_|. If the url cannot be cracked, returns invalid + // FileSystemURL. + // + // If the original url does not point to an isolated or external filesystem, + // returns the original url, without attempting to crack it. + FileSystemURL CrackFileSystemURL(const FileSystemURL& url) const; + + // For initial provider_map construction. This must be called only from + // the constructor. + void RegisterMountPointProvider(FileSystemMountPointProvider* provider); + + scoped_ptr<FileSystemTaskRunners> task_runners_; + + scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; + + // Regular mount point providers. + scoped_ptr<SandboxMountPointProvider> sandbox_provider_; + scoped_ptr<IsolatedMountPointProvider> isolated_provider_; + scoped_ptr<ExternalFileSystemMountPointProvider> external_provider_; + + // Additional mount point providers. + ScopedVector<FileSystemMountPointProvider> additional_providers_; + + // Registered mount point providers. + // The map must be constructed in the constructor since it can be accessed + // on multiple threads. + // The ownership of each provider is held by mount_point_providers_. + MountPointProviderMap provider_map_; + // External mount points visible in the file system context (excluding system + // external mount points). + scoped_refptr<ExternalMountPoints> external_mount_points_; + + // MountPoints used to crack FileSystemURLs. The MountPoints are ordered + // in order they should try to crack a FileSystemURL. + std::vector<MountPoints*> url_crackers_; + + // The base path of the storage partition for this context. + const base::FilePath partition_path_; + + // For syncable file systems. + scoped_ptr<sync_file_system::LocalFileChangeTracker> change_tracker_; + scoped_refptr<sync_file_system::LocalFileSyncContext> sync_context_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FileSystemContext); +}; + +struct DefaultContextDeleter { + static void Destruct(const FileSystemContext* context) { + context->DeleteOnCorrectThread(); + } +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_ diff --git a/webkit/browser/fileapi/file_system_context_unittest.cc b/webkit/browser/fileapi/file_system_context_unittest.cc new file mode 100644 index 0000000..18ead11 --- /dev/null +++ b/webkit/browser/fileapi/file_system_context_unittest.cc @@ -0,0 +1,329 @@ +// Copyright (c) 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_context.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "base/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_task_runners.h" +#include "webkit/browser/fileapi/isolated_context.h" +#include "webkit/browser/fileapi/mock_file_system_options.h" +#include "webkit/quota/mock_quota_manager.h" +#include "webkit/quota/mock_special_storage_policy.h" + +#define FPL(x) FILE_PATH_LITERAL(x) + +#if defined(FILE_PATH_USES_DRIVE_LETTERS) +#define DRIVE FPL("C:") +#else +#define DRIVE +#endif + +namespace fileapi { + +namespace { + +const char kTestOrigin[] = "http://chromium.org/"; +const base::FilePath::CharType kVirtualPathNoRoot[] = FPL("root/file"); + +GURL CreateRawFileSystemURL(const std::string& type_str, + const std::string& fs_id) { + std::string url_str = base::StringPrintf( + "filesystem:http://chromium.org/%s/%s/root/file", + type_str.c_str(), + fs_id.c_str()); + return GURL(url_str); +} + +class FileSystemContextTest : public testing::Test { + public: + FileSystemContextTest() {} + + virtual void SetUp() { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + + storage_policy_ = new quota::MockSpecialStoragePolicy(); + + mock_quota_manager_ = new quota::MockQuotaManager( + false /* is_incognito */, + data_dir_.path(), + base::MessageLoopProxy::current(), + base::MessageLoopProxy::current(), + storage_policy_); + } + + protected: + FileSystemContext* CreateFileSystemContextForTest( + ExternalMountPoints* external_mount_points) { + return new FileSystemContext( + FileSystemTaskRunners::CreateMockTaskRunners(), + external_mount_points, + storage_policy_, + mock_quota_manager_->proxy(), + ScopedVector<FileSystemMountPointProvider>(), + data_dir_.path(), + CreateAllowFileAccessOptions()); + } + + // Verifies a *valid* filesystem url has expected values. + void ExpectFileSystemURLMatches(const FileSystemURL& url, + const GURL& expect_origin, + FileSystemType expect_mount_type, + FileSystemType expect_type, + const base::FilePath& expect_path, + const base::FilePath& expect_virtual_path, + const std::string& expect_filesystem_id) { + EXPECT_TRUE(url.is_valid()); + + EXPECT_EQ(expect_origin, url.origin()); + EXPECT_EQ(expect_mount_type, url.mount_type()); + EXPECT_EQ(expect_type, url.type()); + EXPECT_EQ(expect_path, url.path()); + EXPECT_EQ(expect_virtual_path, url.virtual_path()); + EXPECT_EQ(expect_filesystem_id, url.filesystem_id()); + } + + private: + base::ScopedTempDir data_dir_; + base::MessageLoop message_loop_; + scoped_refptr<quota::SpecialStoragePolicy> storage_policy_; + scoped_refptr<quota::MockQuotaManager> mock_quota_manager_; +}; + +// It is not valid to pass NULL ExternalMountPoints to FileSystemContext on +// ChromeOS. +#if !defined(OS_CHROMEOS) +TEST_F(FileSystemContextTest, NullExternalMountPoints) { + scoped_refptr<FileSystemContext> file_system_context( + CreateFileSystemContextForTest(NULL)); + + // Cracking system external mount and isolated mount points should work. + std::string isolated_name = "root"; + std::string isolated_id = + IsolatedContext::GetInstance()->RegisterFileSystemForPath( + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/isolated/root")), + &isolated_name); + // Register system external mount point. + ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + "system", + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/sys/")))); + + FileSystemURL cracked_isolated = file_system_context->CrackURL( + CreateRawFileSystemURL("isolated", isolated_id)); + + ExpectFileSystemURLMatches( + cracked_isolated, + GURL(kTestOrigin), + kFileSystemTypeIsolated, + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/isolated/root/file")).NormalizePathSeparators(), + base::FilePath::FromUTF8Unsafe(isolated_id).Append(FPL("root/file")). + NormalizePathSeparators(), + isolated_id); + + FileSystemURL cracked_external = file_system_context->CrackURL( + CreateRawFileSystemURL("external", "system")); + + ExpectFileSystemURLMatches( + cracked_external, + GURL(kTestOrigin), + kFileSystemTypeExternal, + kFileSystemTypeNativeLocal, + base::FilePath( + DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(), + base::FilePath(FPL("system/root/file")).NormalizePathSeparators(), + "system"); + + + IsolatedContext::GetInstance()->RevokeFileSystem(isolated_id); + ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system"); +} +#endif // !defiend(OS_CHROMEOS) + +TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) { + scoped_refptr<ExternalMountPoints> mount_points = + ExternalMountPoints::CreateRefCounted(); + + // Register system external mount point. + ASSERT_TRUE(mount_points->RegisterFileSystem( + "system", + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/sys/")))); + + scoped_refptr<FileSystemContext> file_system_context( + CreateFileSystemContextForTest(mount_points.get())); + + // Release a MountPoints reference created in the test. + mount_points = NULL; + + // FileSystemContext should keep a reference to the |mount_points|, so it + // should be able to resolve the URL. + FileSystemURL cracked_external = file_system_context->CrackURL( + CreateRawFileSystemURL("external", "system")); + + ExpectFileSystemURLMatches( + cracked_external, + GURL(kTestOrigin), + kFileSystemTypeExternal, + kFileSystemTypeNativeLocal, + base::FilePath( + DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(), + base::FilePath(FPL("system/root/file")).NormalizePathSeparators(), + "system"); + + // No need to revoke the registered filesystem since |mount_points| lifetime + // is bound to this test. +} + +TEST_F(FileSystemContextTest, CrackFileSystemURL) { + scoped_refptr<ExternalMountPoints> external_mount_points( + ExternalMountPoints::CreateRefCounted()); + scoped_refptr<FileSystemContext> file_system_context( + CreateFileSystemContextForTest(external_mount_points)); + + // Register an isolated mount point. + std::string isolated_file_system_name = "root"; + const std::string kIsolatedFileSystemID = + IsolatedContext::GetInstance()->RegisterFileSystemForPath( + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/isolated/root")), + &isolated_file_system_name); + // Register system external mount point. + ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + "system", + kFileSystemTypeDrive, + base::FilePath(DRIVE FPL("/test/sys/")))); + ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + "ext", + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/ext")))); + // Register a system external mount point with the same name/id as the + // registered isolated mount point. + ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + kIsolatedFileSystemID, + kFileSystemTypeRestrictedNativeLocal, + base::FilePath(DRIVE FPL("/test/system/isolated")))); + // Add a mount points with the same name as a system mount point to + // FileSystemContext's external mount points. + ASSERT_TRUE(external_mount_points->RegisterFileSystem( + "ext", + kFileSystemTypeNativeLocal, + base::FilePath(DRIVE FPL("/test/local/ext/")))); + + const GURL kTestOrigin = GURL("http://chromium.org/"); + const base::FilePath kVirtualPathNoRoot = base::FilePath(FPL("root/file")); + + struct TestCase { + // Test case values. + std::string root; + std::string type_str; + + // Expected test results. + bool expect_is_valid; + FileSystemType expect_mount_type; + FileSystemType expect_type; + const base::FilePath::CharType* expect_path; + std::string expect_filesystem_id; + }; + + const TestCase kTestCases[] = { + // Following should not be handled by the url crackers: + { + "pers_mount", "persistent", true /* is_valid */, + kFileSystemTypePersistent, kFileSystemTypePersistent, + FPL("pers_mount/root/file"), + std::string() /* filesystem id */ + }, + { + "temp_mount", "temporary", true /* is_valid */, + kFileSystemTypeTemporary, kFileSystemTypeTemporary, + FPL("temp_mount/root/file"), + std::string() /* filesystem id */ + }, + // Should be cracked by isolated mount points: + { + kIsolatedFileSystemID, "isolated", true /* is_valid */, + kFileSystemTypeIsolated, kFileSystemTypeNativeLocal, + DRIVE FPL("/test/isolated/root/file"), + kIsolatedFileSystemID + }, + // Should be cracked by system mount points: + { + "system", "external", true /* is_valid */, + kFileSystemTypeExternal, kFileSystemTypeDrive, + DRIVE FPL("/test/sys/root/file"), + "system" + }, + { + kIsolatedFileSystemID, "external", true /* is_valid */, + kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal, + DRIVE FPL("/test/system/isolated/root/file"), + kIsolatedFileSystemID + }, + // Should be cracked by FileSystemContext's ExternalMountPoints. + { + "ext", "external", true /* is_valid */, + kFileSystemTypeExternal, kFileSystemTypeNativeLocal, + DRIVE FPL("/test/local/ext/root/file"), + "ext" + }, + // Test for invalid filesystem url (made invalid by adding invalid + // filesystem type). + { + "sytem", "external", false /* is_valid */, + // The rest of values will be ignored. + kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""), + std::string() + }, + // Test for URL with non-existing filesystem id. + { + "invalid", "external", false /* is_valid */, + // The rest of values will be ignored. + kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""), + std::string() + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { + const base::FilePath virtual_path = + base::FilePath::FromUTF8Unsafe(kTestCases[i].root).Append(kVirtualPathNoRoot); + + GURL raw_url = + CreateRawFileSystemURL(kTestCases[i].type_str, kTestCases[i].root); + FileSystemURL cracked_url = file_system_context->CrackURL(raw_url); + + SCOPED_TRACE(testing::Message() << "Test case " << i << ": " + << "Cracking URL: " << raw_url); + + EXPECT_EQ(kTestCases[i].expect_is_valid, cracked_url.is_valid()); + if (!kTestCases[i].expect_is_valid) + continue; + + ExpectFileSystemURLMatches( + cracked_url, + GURL(kTestOrigin), + kTestCases[i].expect_mount_type, + kTestCases[i].expect_type, + base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(), + virtual_path.NormalizePathSeparators(), + kTestCases[i].expect_filesystem_id); + } + + IsolatedContext::GetInstance()->RevokeFileSystemByPath( + base::FilePath(DRIVE FPL("/test/isolated/root"))); + ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system"); + ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("ext"); + ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kIsolatedFileSystemID); +} + +} // namespace + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_dir_url_request_job.cc b/webkit/browser/fileapi/file_system_dir_url_request_job.cc index f94f99e..48a13c7 100644 --- a/webkit/browser/fileapi/file_system_dir_url_request_job.cc +++ b/webkit/browser/fileapi/file_system_dir_url_request_job.cc @@ -19,10 +19,10 @@ #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/url_request/url_request.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/fileapi/directory_entry.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_url.h" using net::NetworkDelegate; using net::URLRequest; diff --git a/webkit/browser/fileapi/file_system_dir_url_request_job.h b/webkit/browser/fileapi/file_system_dir_url_request_job.h index f3cae42..4811dd9 100644 --- a/webkit/browser/fileapi/file_system_dir_url_request_job.h +++ b/webkit/browser/fileapi/file_system_dir_url_request_job.h @@ -13,7 +13,7 @@ #include "base/message_loop_proxy.h" #include "base/platform_file.h" #include "net/url_request/url_request_job.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/storage/webkit_storage_export.h" namespace fileapi { diff --git a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc index 65b9492..7de553a 100644 --- a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc +++ b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc @@ -22,12 +22,12 @@ #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/icu/public/i18n/unicode/regex.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/quota/mock_special_storage_policy.h" namespace fileapi { diff --git a/webkit/browser/fileapi/file_system_file_stream_reader.cc b/webkit/browser/fileapi/file_system_file_stream_reader.cc new file mode 100644 index 0000000..29268a7 --- /dev/null +++ b/webkit/browser/fileapi/file_system_file_stream_reader.cc @@ -0,0 +1,132 @@ +// 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/browser/fileapi/file_system_file_stream_reader.h" + +#include "base/files/file_util_proxy.h" +#include "base/platform_file.h" +#include "base/single_thread_task_runner.h" +#include "net/base/file_stream.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "webkit/blob/local_file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_task_runners.h" + +using webkit_blob::LocalFileStreamReader; + +namespace fileapi { + +namespace { + +void ReadAdapter(base::WeakPtr<FileSystemFileStreamReader> reader, + net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) { + if (!reader) + return; + int rv = reader->Read(buf, buf_len, callback); + if (rv != net::ERR_IO_PENDING) + callback.Run(rv); +} + +void GetLengthAdapter(base::WeakPtr<FileSystemFileStreamReader> reader, + const net::Int64CompletionCallback& callback) { + if (!reader) + return; + int rv = reader->GetLength(callback); + if (rv != net::ERR_IO_PENDING) + callback.Run(rv); +} + +void Int64CallbackAdapter(const net::Int64CompletionCallback& callback, + int value) { + callback.Run(value); +} + +} // namespace + +FileSystemFileStreamReader::FileSystemFileStreamReader( + FileSystemContext* file_system_context, + const FileSystemURL& url, + int64 initial_offset, + const base::Time& expected_modification_time) + : file_system_context_(file_system_context), + url_(url), + initial_offset_(initial_offset), + expected_modification_time_(expected_modification_time), + has_pending_create_snapshot_(false), + weak_factory_(this) { +} + +FileSystemFileStreamReader::~FileSystemFileStreamReader() { +} + +int FileSystemFileStreamReader::Read( + net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) { + if (local_file_reader_) + return local_file_reader_->Read(buf, buf_len, callback); + return CreateSnapshot( + base::Bind(&ReadAdapter, weak_factory_.GetWeakPtr(), + make_scoped_refptr(buf), buf_len, callback), + callback); +} + +int64 FileSystemFileStreamReader::GetLength( + const net::Int64CompletionCallback& callback) { + if (local_file_reader_) + return local_file_reader_->GetLength(callback); + return CreateSnapshot( + base::Bind(&GetLengthAdapter, weak_factory_.GetWeakPtr(), callback), + base::Bind(&Int64CallbackAdapter, callback)); +} + +int FileSystemFileStreamReader::CreateSnapshot( + const base::Closure& callback, + const net::CompletionCallback& error_callback) { + DCHECK(!has_pending_create_snapshot_); + base::PlatformFileError error_code; + FileSystemOperation* operation = + file_system_context_->CreateFileSystemOperation(url_, &error_code); + if (error_code != base::PLATFORM_FILE_OK) + return net::PlatformFileErrorToNetError(error_code); + has_pending_create_snapshot_ = true; + operation->CreateSnapshotFile( + url_, + base::Bind(&FileSystemFileStreamReader::DidCreateSnapshot, + weak_factory_.GetWeakPtr(), + callback, + error_callback)); + return net::ERR_IO_PENDING; +} + +void FileSystemFileStreamReader::DidCreateSnapshot( + const base::Closure& callback, + const net::CompletionCallback& error_callback, + base::PlatformFileError file_error, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path, + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) { + DCHECK(has_pending_create_snapshot_); + DCHECK(!local_file_reader_.get()); + has_pending_create_snapshot_ = false; + + if (file_error != base::PLATFORM_FILE_OK) { + error_callback.Run(net::PlatformFileErrorToNetError(file_error)); + return; + } + + // Keep the reference (if it's non-null) so that the file won't go away. + snapshot_ref_ = file_ref; + + local_file_reader_.reset( + new LocalFileStreamReader( + file_system_context_->task_runners()->file_task_runner(), + platform_path, initial_offset_, expected_modification_time_)); + + callback.Run(); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_file_stream_reader.h b/webkit/browser/fileapi/file_system_file_stream_reader.h new file mode 100644 index 0000000..5a2fc9a --- /dev/null +++ b/webkit/browser/fileapi/file_system_file_stream_reader.h @@ -0,0 +1,79 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_FILE_STREAM_READER_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_FILE_STREAM_READER_H_ + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/platform_file.h" +#include "base/time.h" +#include "webkit/blob/file_stream_reader.h" +#include "webkit/blob/shareable_file_reference.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class FilePath; +class SequencedTaskRunner; +} + +namespace webkit_blob { +class LocalFileStreamReader; +} + +namespace fileapi { + +class FileSystemContext; + +// TODO(kinaba,satorux): This generic implementation would work for any +// filesystems but remote filesystem should implement its own reader +// rather than relying on FileSystemOperation::GetSnapshotFile() which +// may force downloading the entire contents for remote files. +class WEBKIT_STORAGE_EXPORT_PRIVATE FileSystemFileStreamReader + : public webkit_blob::FileStreamReader { + public: + // Creates a new FileReader for a filesystem URL |url| form |initial_offset|. + // |expected_modification_time| specifies the expected last modification if + // the value is non-null, the reader will check the underlying file's actual + // modification time to see if the file has been modified, and if it does any + // succeeding read operations should fail with ERR_UPLOAD_FILE_CHANGED error. + FileSystemFileStreamReader(FileSystemContext* file_system_context, + const FileSystemURL& url, + int64 initial_offset, + const base::Time& expected_modification_time); + virtual ~FileSystemFileStreamReader(); + + // FileStreamReader overrides. + virtual int Read(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) OVERRIDE; + virtual int64 GetLength( + const net::Int64CompletionCallback& callback) OVERRIDE; + + private: + int CreateSnapshot(const base::Closure& callback, + const net::CompletionCallback& error_callback); + void DidCreateSnapshot( + const base::Closure& callback, + const net::CompletionCallback& error_callback, + base::PlatformFileError file_error, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path, + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref); + + scoped_refptr<FileSystemContext> file_system_context_; + FileSystemURL url_; + const int64 initial_offset_; + const base::Time expected_modification_time_; + scoped_ptr<webkit_blob::LocalFileStreamReader> local_file_reader_; + scoped_refptr<webkit_blob::ShareableFileReference> snapshot_ref_; + bool has_pending_create_snapshot_; + base::WeakPtrFactory<FileSystemFileStreamReader> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemFileStreamReader); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_FILE_STREAM_READER_H_ diff --git a/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc b/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc new file mode 100644 index 0000000..466acda --- /dev/null +++ b/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc @@ -0,0 +1,281 @@ +// 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/browser/fileapi/file_system_file_stream_reader.h" + +#include <limits> +#include <string> + +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/platform_file.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/mock_file_system_context.h" +#include "webkit/browser/fileapi/sandbox_mount_point_provider.h" + +namespace fileapi { + +namespace { + +const char kURLOrigin[] = "http://remote/"; +const char kTestFileName[] = "test.dat"; +const char kTestData[] = "0123456789"; +const int kTestDataSize = arraysize(kTestData) - 1; + +void ReadFromReader(FileSystemFileStreamReader* reader, + std::string* data, + size_t size, + int* result) { + ASSERT_TRUE(reader != NULL); + ASSERT_TRUE(result != NULL); + *result = net::OK; + net::TestCompletionCallback callback; + size_t total_bytes_read = 0; + while (total_bytes_read < size) { + scoped_refptr<net::IOBufferWithSize> buf( + new net::IOBufferWithSize(size - total_bytes_read)); + int rv = reader->Read(buf, buf->size(), callback.callback()); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv < 0) + *result = rv; + if (rv <= 0) + break; + total_bytes_read += rv; + data->append(buf->data(), rv); + } +} + +void NeverCalled(int unused) { ADD_FAILURE(); } + +} // namespace + +class FileSystemFileStreamReaderTest : public testing::Test { + public: + FileSystemFileStreamReaderTest() + : message_loop_(base::MessageLoop::TYPE_IO) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + file_system_context_ = CreateFileSystemContextForTesting( + NULL, temp_dir_.path()); + + file_system_context_->sandbox_provider()->ValidateFileSystemRoot( + GURL(kURLOrigin), kFileSystemTypeTemporary, true, // create + base::Bind(&OnValidateFileSystem)); + base::MessageLoop::current()->RunUntilIdle(); + + WriteFile(kTestFileName, kTestData, kTestDataSize, + &test_file_modification_time_); + } + + virtual void TearDown() OVERRIDE { + base::MessageLoop::current()->RunUntilIdle(); + } + + protected: + FileSystemFileStreamReader* CreateFileReader( + const std::string& file_name, + int64 initial_offset, + const base::Time& expected_modification_time) { + return new FileSystemFileStreamReader(file_system_context_, + GetFileSystemURL(file_name), + initial_offset, + expected_modification_time); + } + + base::Time test_file_modification_time() const { + return test_file_modification_time_; + } + + void WriteFile(const std::string& file_name, + const char* buf, + int buf_size, + base::Time* modification_time) { + FileSystemFileUtil* file_util = file_system_context_-> + sandbox_provider()->GetFileUtil(kFileSystemTypeTemporary); + FileSystemURL url = GetFileSystemURL(file_name); + + FileSystemOperationContext context(file_system_context_); + context.set_allowed_bytes_growth(1024); + + base::PlatformFile handle = base::kInvalidPlatformFileValue; + bool created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen( + &context, + url, + base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, + &handle, + &created)); + EXPECT_TRUE(created); + ASSERT_NE(base::kInvalidPlatformFileValue, handle); + ASSERT_EQ(buf_size, + base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size)); + base::ClosePlatformFile(handle); + + base::PlatformFileInfo file_info; + base::FilePath platform_path; + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util->GetFileInfo(&context, url, &file_info, + &platform_path)); + if (modification_time) + *modification_time = file_info.last_modified; + } + + private: + static void OnValidateFileSystem(base::PlatformFileError result) { + ASSERT_EQ(base::PLATFORM_FILE_OK, result); + } + + FileSystemURL GetFileSystemURL(const std::string& file_name) { + return file_system_context_->CreateCrackedFileSystemURL( + GURL(kURLOrigin), + kFileSystemTypeTemporary, + base::FilePath().AppendASCII(file_name)); + } + + base::MessageLoop message_loop_; + base::ScopedTempDir temp_dir_; + scoped_refptr<FileSystemContext> file_system_context_; + base::Time test_file_modification_time_; +}; + +TEST_F(FileSystemFileStreamReaderTest, NonExistent) { + const char kFileName[] = "nonexistent"; + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kFileName, 0, base::Time())); + int result = 0; + std::string data; + ReadFromReader(reader.get(), &data, 10, &result); + ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result); + ASSERT_EQ(0U, data.size()); +} + +TEST_F(FileSystemFileStreamReaderTest, Empty) { + const char kFileName[] = "empty"; + WriteFile(kFileName, NULL, 0, NULL); + + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kFileName, 0, base::Time())); + int result = 0; + std::string data; + ReadFromReader(reader.get(), &data, 10, &result); + ASSERT_EQ(net::OK, result); + ASSERT_EQ(0U, data.size()); + + net::TestInt64CompletionCallback callback; + int64 length_result = reader->GetLength(callback.callback()); + if (length_result == net::ERR_IO_PENDING) + length_result = callback.WaitForResult(); + ASSERT_EQ(0, length_result); +} + +TEST_F(FileSystemFileStreamReaderTest, GetLengthNormal) { + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 0, test_file_modification_time())); + net::TestInt64CompletionCallback callback; + int64 result = reader->GetLength(callback.callback()); + if (result == net::ERR_IO_PENDING) + result = callback.WaitForResult(); + ASSERT_EQ(kTestDataSize, result); +} + +TEST_F(FileSystemFileStreamReaderTest, GetLengthAfterModified) { + // Pass a fake expected modifictaion time so that the expectation fails. + base::Time fake_expected_modification_time = + test_file_modification_time() - base::TimeDelta::FromSeconds(10); + + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 0, fake_expected_modification_time)); + net::TestInt64CompletionCallback callback; + int64 result = reader->GetLength(callback.callback()); + if (result == net::ERR_IO_PENDING) + result = callback.WaitForResult(); + ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); + + // With NULL expected modification time this should work. + reader.reset(CreateFileReader(kTestFileName, 0, base::Time())); + result = reader->GetLength(callback.callback()); + if (result == net::ERR_IO_PENDING) + result = callback.WaitForResult(); + ASSERT_EQ(kTestDataSize, result); +} + +TEST_F(FileSystemFileStreamReaderTest, GetLengthWithOffset) { + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 3, base::Time())); + net::TestInt64CompletionCallback callback; + int64 result = reader->GetLength(callback.callback()); + if (result == net::ERR_IO_PENDING) + result = callback.WaitForResult(); + // Initial offset does not affect the result of GetLength. + ASSERT_EQ(kTestDataSize, result); +} + +TEST_F(FileSystemFileStreamReaderTest, ReadNormal) { + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 0, test_file_modification_time())); + int result = 0; + std::string data; + ReadFromReader(reader.get(), &data, kTestDataSize, &result); + ASSERT_EQ(net::OK, result); + ASSERT_EQ(kTestData, data); +} + +TEST_F(FileSystemFileStreamReaderTest, ReadAfterModified) { + // Pass a fake expected modifictaion time so that the expectation fails. + base::Time fake_expected_modification_time = + test_file_modification_time() - base::TimeDelta::FromSeconds(10); + + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 0, fake_expected_modification_time)); + int result = 0; + std::string data; + ReadFromReader(reader.get(), &data, kTestDataSize, &result); + ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); + ASSERT_EQ(0U, data.size()); + + // With NULL expected modification time this should work. + data.clear(); + reader.reset(CreateFileReader(kTestFileName, 0, base::Time())); + ReadFromReader(reader.get(), &data, kTestDataSize, &result); + ASSERT_EQ(net::OK, result); + ASSERT_EQ(kTestData, data); +} + +TEST_F(FileSystemFileStreamReaderTest, ReadWithOffset) { + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 3, base::Time())); + int result = 0; + std::string data; + ReadFromReader(reader.get(), &data, kTestDataSize, &result); + ASSERT_EQ(net::OK, result); + ASSERT_EQ(&kTestData[3], data); +} + +TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) { + scoped_ptr<FileSystemFileStreamReader> reader( + CreateFileReader(kTestFileName, 0, base::Time())); + + net::TestCompletionCallback callback; + scoped_refptr<net::IOBufferWithSize> buf( + new net::IOBufferWithSize(kTestDataSize)); + int rv = reader->Read(buf, buf->size(), base::Bind(&NeverCalled)); + ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0); + + // Delete immediately. + // Should not crash; nor should NeverCalled be callback. + reader.reset(); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_mount_point_provider.h b/webkit/browser/fileapi/file_system_mount_point_provider.h index d8aff2b..400a041 100644 --- a/webkit/browser/fileapi/file_system_mount_point_provider.h +++ b/webkit/browser/fileapi/file_system_mount_point_provider.h @@ -12,7 +12,7 @@ #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" -#include "webkit/fileapi/file_permission_policy.h" +#include "webkit/browser/fileapi/file_permission_policy.h" #include "webkit/fileapi/file_system_types.h" #include "webkit/storage/webkit_storage_export.h" diff --git a/webkit/browser/fileapi/file_system_operation.h b/webkit/browser/fileapi/file_system_operation.h new file mode 100644 index 0000000..bf960cb --- /dev/null +++ b/webkit/browser/fileapi/file_system_operation.h @@ -0,0 +1,278 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/platform_file.h" +#include "base/process.h" +#include "webkit/fileapi/directory_entry.h" + +namespace base { +class Time; +} // namespace base + +namespace net { +class URLRequestContext; +} // namespace net + +namespace webkit_blob { +class ShareableFileReference; +} + +class GURL; + +namespace fileapi { + +class FileSystemURL; +class LocalFileSystemOperation; + +// The interface class for FileSystemOperation implementations. +// +// This interface defines file system operations required to implement +// "File API: Directories and System" +// http://www.w3.org/TR/file-system-api/ +// +// DESIGN NOTES +// +// This class is designed to +// +// 1) Serve one-time file system operation per instance. Only one +// method(CreateFile, CreateDirectory, Copy, Move, DirectoryExists, +// GetMetadata, ReadDirectory and Remove) may be called during the +// lifetime of this object and it should be called no more than once. +// +// 2) Be self-destructed, or get deleted via base::Owned() after the +// operation finishes and completion callback is called. +// +// 3) Deliver the results of operations to the client via the callback function +// passed as the last parameter of the method. +// +class FileSystemOperation { + public: + virtual ~FileSystemOperation() {} + + // Used for CreateFile(), etc. |result| is the return code of the operation. + typedef base::Callback<void(base::PlatformFileError result)> StatusCallback; + + // Used for GetMetadata(). |result| is the return code of the operation, + // |file_info| is the obtained file info, and |platform_path| is the path + // of the file. + typedef base::Callback< + void(base::PlatformFileError result, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path)> GetMetadataCallback; + + // Used for OpenFile(). |result| is the return code of the operation. + // |on_close_callback| will be called after the file is closed in the child + // process. + typedef base::Callback< + void(base::PlatformFileError result, + base::PlatformFile file, + const base::Closure& on_close_callback, + base::ProcessHandle peer_handle)> OpenFileCallback; + + // Used for ReadDirectoryCallback. + typedef std::vector<DirectoryEntry> FileEntryList; + + // Used for ReadDirectory(). |result| is the return code of the operation, + // |file_list| is the list of files read, and |has_more| is true if some files + // are yet to be read. + typedef base::Callback< + void(base::PlatformFileError result, + const FileEntryList& file_list, + bool has_more)> ReadDirectoryCallback; + + // Used for CreateSnapshotFile(). (Please see the comment at + // CreateSnapshotFile() below for how the method is called) + // |result| is the return code of the operation. + // |file_info| is the metadata of the snapshot file created. + // |platform_path| is the path to the snapshot file created. + // + // The snapshot file could simply be of the local file pointed by the given + // filesystem URL in local filesystem cases; remote filesystems + // may want to download the file into a temporary snapshot file and then + // return the metadata of the temporary file. + // + // |file_ref| is used to manage the lifetime of the returned + // snapshot file. It can be set to let the chromium backend take + // care of the life time of the snapshot file. Otherwise (if the returned + // file does not require any handling) the implementation can just + // return NULL. In a more complex case, the implementaiton can manage + // the lifetime of the snapshot file on its own (e.g. by its cache system) + // but also can be notified via the reference when the file becomes no + // longer necessary in the javascript world. + // Please see the comment for ShareableFileReference for details. + // + typedef base::Callback< + void(base::PlatformFileError result, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path, + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref)> + SnapshotFileCallback; + + // Used for Write(). + typedef base::Callback<void(base::PlatformFileError result, + int64 bytes, + bool complete)> WriteCallback; + + // Creates a file at |path|. If |exclusive| is true, an error is raised + // in case a file is already present at the URL. + virtual void CreateFile(const FileSystemURL& path, + bool exclusive, + const StatusCallback& callback) = 0; + + // Creates a directory at |path|. If |exclusive| is true, an error is + // raised in case a directory is already present at the URL. If + // |recursive| is true, create parent directories as needed just like + // mkdir -p does. + virtual void CreateDirectory(const FileSystemURL& path, + bool exclusive, + bool recursive, + const StatusCallback& callback) = 0; + + // Copies a file or directory from |src_path| to |dest_path|. If + // |src_path| is a directory, the contents of |src_path| are copied to + // |dest_path| recursively. A new file or directory is created at + // |dest_path| as needed. + virtual void Copy(const FileSystemURL& src_path, + const FileSystemURL& dest_path, + const StatusCallback& callback) = 0; + + // Moves a file or directory from |src_path| to |dest_path|. A new file + // or directory is created at |dest_path| as needed. + virtual void Move(const FileSystemURL& src_path, + const FileSystemURL& dest_path, + const StatusCallback& callback) = 0; + + // Checks if a directory is present at |path|. + virtual void DirectoryExists(const FileSystemURL& path, + const StatusCallback& callback) = 0; + + // Checks if a file is present at |path|. + virtual void FileExists(const FileSystemURL& path, + const StatusCallback& callback) = 0; + + // Gets the metadata of a file or directory at |path|. + virtual void GetMetadata(const FileSystemURL& path, + const GetMetadataCallback& callback) = 0; + + // Reads contents of a directory at |path|. + virtual void ReadDirectory(const FileSystemURL& path, + const ReadDirectoryCallback& callback) = 0; + + // Removes a file or directory at |path|. If |recursive| is true, remove + // all files and directories under the directory at |path| recursively. + virtual void Remove(const FileSystemURL& path, bool recursive, + const StatusCallback& callback) = 0; + + // Writes contents of |blob_url| to |path| at |offset|. + // |url_request_context| is used to read contents in |blob_url|. + virtual void Write(const net::URLRequestContext* url_request_context, + const FileSystemURL& path, + const GURL& blob_url, + int64 offset, + const WriteCallback& callback) = 0; + + // Truncates a file at |path| to |length|. If |length| is larger than + // the original file size, the file will be extended, and the extended + // part is filled with null bytes. + virtual void Truncate(const FileSystemURL& path, int64 length, + const StatusCallback& callback) = 0; + + // Tries to cancel the current operation [we support cancelling write or + // truncate only]. Reports failure for the current operation, then reports + // success for the cancel operation itself via the |cancel_dispatcher|. + // + // E.g. a typical cancel implementation would look like: + // + // virtual void SomeOperationImpl::Cancel( + // const StatusCallback& cancel_callback) { + // // Abort the current inflight operation first. + // ... + // + // // Dispatch ABORT error for the current operation by invoking + // // the callback function for the ongoing operation, + // operation_callback.Run(base::PLATFORM_FILE_ERROR_ABORT, ...); + // + // // Dispatch 'success' for the cancel (or dispatch appropriate + // // error code with DidFail() if the cancel has somehow failed). + // cancel_callback.Run(base::PLATFORM_FILE_OK); + // } + // + // Note that, for reporting failure, the callback function passed to a + // cancellable operations are kept around with the operation instance + // (as |operation_callback_| in the code example). + virtual void Cancel(const StatusCallback& cancel_callback) = 0; + + // Modifies timestamps of a file or directory at |path| with + // |last_access_time| and |last_modified_time|. The function DOES NOT + // create a file unlike 'touch' command on Linux. + // + // This function is used only by Pepper as of writing. + virtual void TouchFile(const FileSystemURL& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) = 0; + + // Opens a file at |path| with |file_flags|, where flags are OR'ed + // values of base::PlatformFileFlags. + // + // |peer_handle| is the process handle of a pepper plugin process, which + // is necessary for underlying IPC calls with Pepper plugins. + // + // This function is used only by Pepper as of writing. + virtual void OpenFile(const FileSystemURL& path, + int file_flags, + base::ProcessHandle peer_handle, + const OpenFileCallback& callback) = 0; + + // For downcasting to FileSystemOperation. + // TODO(kinuko): this hack should go away once appropriate upload-stream + // handling based on element types is supported. + virtual LocalFileSystemOperation* AsLocalFileSystemOperation() = 0; + + // Creates a local snapshot file for a given |path| and returns the + // metadata and platform path of the snapshot file via |callback|. + // In local filesystem cases the implementation may simply return + // the metadata of the file itself (as well as GetMetadata does), + // while in remote filesystem case the backend may want to download the file + // into a temporary snapshot file and return the metadata of the + // temporary file. Or if the implementaiton already has the local cache + // data for |path| it can simply return the path to the cache. + virtual void CreateSnapshotFile(const FileSystemURL& path, + const SnapshotFileCallback& callback) = 0; + + protected: + // Used only for internal assertions. + enum OperationType { + kOperationNone, + kOperationCreateFile, + kOperationCreateDirectory, + kOperationCreateSnapshotFile, + kOperationCopy, + kOperationCopyInForeignFile, + kOperationMove, + kOperationDirectoryExists, + kOperationFileExists, + kOperationGetMetadata, + kOperationReadDirectory, + kOperationRemove, + kOperationWrite, + kOperationTruncate, + kOperationTouchFile, + kOperationOpenFile, + kOperationCloseFile, + kOperationGetLocalPath, + kOperationCancel, + }; +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_H_ diff --git a/webkit/browser/fileapi/file_system_operation_context.cc b/webkit/browser/fileapi/file_system_operation_context.cc new file mode 100644 index 0000000..3866bf0 --- /dev/null +++ b/webkit/browser/fileapi/file_system_operation_context.cc @@ -0,0 +1,33 @@ +// 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/browser/fileapi/file_system_operation_context.h" + +#include "base/sequenced_task_runner.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_task_runners.h" + +namespace fileapi { + +FileSystemOperationContext::FileSystemOperationContext( + FileSystemContext* context) + : file_system_context_(context), + task_runner_(file_system_context_->task_runners()->file_task_runner()), + allowed_bytes_growth_(0), + quota_limit_type_(quota::kQuotaLimitTypeUnknown) {} + +FileSystemOperationContext::FileSystemOperationContext( + FileSystemContext* context, + base::SequencedTaskRunner* task_runner) + : file_system_context_(context), + task_runner_(task_runner), + allowed_bytes_growth_(0), + quota_limit_type_(quota::kQuotaLimitTypeUnknown) {} + +FileSystemOperationContext::~FileSystemOperationContext() { + DetachUserDataThread(); + setter_thread_checker_.DetachFromThread(); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_operation_context.h b/webkit/browser/fileapi/file_system_operation_context.h new file mode 100644 index 0000000..68c7ce5 --- /dev/null +++ b/webkit/browser/fileapi/file_system_operation_context.h @@ -0,0 +1,127 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_CONTEXT_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_CONTEXT_H_ + +#include "base/supports_user_data.h" +#include "base/threading/thread_checker.h" +#include "webkit/browser/fileapi/task_runner_bound_observer_list.h" +#include "webkit/quota/quota_types.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace fileapi { + +class FileSystemContext; + +// A context class which is carried around by FileSystemOperation and +// its delegated tasks. It is valid to reuse one context instance across +// multiple operations as far as those operations are supposed to share +// the same context (e.g. use the same task runner, share the quota etc). +// Note that the remaining quota bytes (allowed_bytes_growth) may be +// updated during the execution of write operations. +class WEBKIT_STORAGE_EXPORT_PRIVATE FileSystemOperationContext + : public base::SupportsUserData { + public: + explicit FileSystemOperationContext(FileSystemContext* context); + + // Specifies |task_runner| which the operation is performed on. + FileSystemOperationContext(FileSystemContext* context, + base::SequencedTaskRunner* task_runner); + + virtual ~FileSystemOperationContext(); + + FileSystemContext* file_system_context() const { + return file_system_context_; + } + + // Updates the current remaining quota. + // This can be called to update the remaining quota during the operation. + void set_allowed_bytes_growth(const int64& allowed_bytes_growth) { + allowed_bytes_growth_ = allowed_bytes_growth; + } + + // Returns the current remaining quota. + int64 allowed_bytes_growth() const { return allowed_bytes_growth_; } + + quota::QuotaLimitType quota_limit_type() const { + return quota_limit_type_; + } + + // Returns TaskRunner which the operation is performed on. + base::SequencedTaskRunner* task_runner() const { + return task_runner_.get(); + } + + ChangeObserverList* change_observers() { return &change_observers_; } + AccessObserverList* access_observers() { return &access_observers_; } + UpdateObserverList* update_observers() { return &update_observers_; } + + // Following setters should be called only on the same thread as the + // FileSystemOperationContext is created (i.e. are not supposed be updated + // after the context's passed onto other task runners). + void set_change_observers(const ChangeObserverList& list) { + DCHECK(setter_thread_checker_.CalledOnValidThread()); + change_observers_ = list; + } + void set_access_observers(const AccessObserverList& list) { + DCHECK(setter_thread_checker_.CalledOnValidThread()); + access_observers_ = list; + } + void set_update_observers(const UpdateObserverList& list) { + DCHECK(setter_thread_checker_.CalledOnValidThread()); + update_observers_ = list; + } + void set_quota_limit_type(quota::QuotaLimitType limit_type) { + DCHECK(setter_thread_checker_.CalledOnValidThread()); + quota_limit_type_ = limit_type; + } + + // Gets and sets value-type (or not-owned) variable as UserData. + // (SetUserValue can be called only on the same thread as this context + // is created as well as other setters.) + template <typename T> T GetUserValue(const char* key) const { + ValueAdapter<T>* v = static_cast<ValueAdapter<T>*>(GetUserData(key)); + return v ? v->value() : T(); + } + template <typename T> void SetUserValue(const char* key, const T& value) { + DCHECK(setter_thread_checker_.CalledOnValidThread()); + SetUserData(key, new ValueAdapter<T>(value)); + } + + private: + // An adapter for setting a value-type (or not owned) data as UserData. + template <typename T> class ValueAdapter + : public base::SupportsUserData::Data { + public: + ValueAdapter(const T& value) : value_(value) {} + const T& value() const { return value_; } + private: + T value_; + DISALLOW_COPY_AND_ASSIGN(ValueAdapter); + }; + + FileSystemContext* file_system_context_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + + int64 allowed_bytes_growth_; + quota::QuotaLimitType quota_limit_type_; + + AccessObserverList access_observers_; + ChangeObserverList change_observers_; + UpdateObserverList update_observers_; + + // Used to check its setters are not called on arbitrary thread. + base::ThreadChecker setter_thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemOperationContext); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_CONTEXT_H_ diff --git a/webkit/browser/fileapi/file_system_options.cc b/webkit/browser/fileapi/file_system_options.cc new file mode 100644 index 0000000..4100b5d --- /dev/null +++ b/webkit/browser/fileapi/file_system_options.cc @@ -0,0 +1,19 @@ +// 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/browser/fileapi/file_system_options.h" + +namespace fileapi { + +FileSystemOptions::FileSystemOptions( + ProfileMode profile_mode, + const std::vector<std::string>& additional_allowed_schemes) + : profile_mode_(profile_mode), + additional_allowed_schemes_(additional_allowed_schemes) { +} + +FileSystemOptions::~FileSystemOptions() { +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_options.h b/webkit/browser/fileapi/file_system_options.h new file mode 100644 index 0000000..27cc9d2 --- /dev/null +++ b/webkit/browser/fileapi/file_system_options.h @@ -0,0 +1,53 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_OPTIONS_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPTIONS_H_ + +#include <string> +#include <vector> + +#include "webkit/storage/webkit_storage_export.h" + +namespace fileapi { + +// Provides runtime options that may change FileSystem API behavior. +// This object is copyable. +class WEBKIT_STORAGE_EXPORT FileSystemOptions { + public: + enum ProfileMode { + PROFILE_MODE_NORMAL = 0, + PROFILE_MODE_INCOGNITO + }; + + // |profile_mode| specifies if the profile (for this filesystem) + // is running in incognito mode (PROFILE_MODE_INCOGNITO) or no + // (PROFILE_MODE_NORMAL). + // |additional_allowed_schemes| specifies schemes that are allowed + // to access FileSystem API in addition to "http" and "https". + FileSystemOptions( + ProfileMode profile_mode, + const std::vector<std::string>& additional_allowed_schemes); + + ~FileSystemOptions(); + + // Returns true if it is running in the incognito mode. + bool is_incognito() const { return profile_mode_ == PROFILE_MODE_INCOGNITO; } + + // Returns the schemes that must be allowed to access FileSystem API + // in addition to standard "http" and "https". + // (e.g. If the --allow-file-access-from-files option is given in chrome + // "file" scheme will also need to be allowed). + const std::vector<std::string>& additional_allowed_schemes() const { + return additional_allowed_schemes_; + } + + private: + const ProfileMode profile_mode_; + const std::vector<std::string> additional_allowed_schemes_; +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPTIONS_H_ diff --git a/webkit/browser/fileapi/file_system_quota_client.cc b/webkit/browser/fileapi/file_system_quota_client.cc index 04a54fc..8a5f51b 100644 --- a/webkit/browser/fileapi/file_system_quota_client.cc +++ b/webkit/browser/fileapi/file_system_quota_client.cc @@ -18,11 +18,11 @@ #include "base/task_runner_util.h" #include "googleurl/src/gurl.h" #include "net/base/net_util.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_quota_util.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_context.h" #include "webkit/fileapi/file_system_util.h" using quota::StorageType; diff --git a/webkit/browser/fileapi/file_system_quota_client_unittest.cc b/webkit/browser/fileapi/file_system_quota_client_unittest.cc index 4c719a4..7f1c728 100644 --- a/webkit/browser/fileapi/file_system_quota_client_unittest.cc +++ b/webkit/browser/fileapi/file_system_quota_client_unittest.cc @@ -11,13 +11,13 @@ #include "base/platform_file.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_quota_client.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/obfuscated_file_util.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_types.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/quota/quota_types.h" diff --git a/webkit/browser/fileapi/file_system_url.cc b/webkit/browser/fileapi/file_system_url.cc new file mode 100644 index 0000000..f4c582c --- /dev/null +++ b/webkit/browser/fileapi/file_system_url.cc @@ -0,0 +1,188 @@ +// 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/browser/fileapi/file_system_url.h" + +#include <sstream> + +#include "base/logging.h" +#include "base/string_util.h" +#include "net/base/escape.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_util.h" + +namespace fileapi { + +namespace { + +} // namespace + +FileSystemURL::FileSystemURL() + : is_valid_(false), + mount_type_(kFileSystemTypeUnknown), + type_(kFileSystemTypeUnknown) { +} + +// static +FileSystemURL FileSystemURL::CreateForTest(const GURL& url) { + return FileSystemURL(url); +} + +FileSystemURL FileSystemURL::CreateForTest(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path) { + return FileSystemURL(origin, mount_type, virtual_path); +} + +// static +bool FileSystemURL::ParseFileSystemSchemeURL( + const GURL& url, + GURL* origin_url, + FileSystemType* mount_type, + base::FilePath* virtual_path) { + GURL origin; + FileSystemType file_system_type = kFileSystemTypeUnknown; + + if (!url.is_valid() || !url.SchemeIsFileSystem()) + return false; + DCHECK(url.inner_url()); + + std::string inner_path = url.inner_url()->path(); + + const struct { + FileSystemType type; + const char* dir; + } kValidTypes[] = { + { kFileSystemTypePersistent, kPersistentDir }, + { kFileSystemTypeTemporary, kTemporaryDir }, + { kFileSystemTypeIsolated, kIsolatedDir }, + { kFileSystemTypeExternal, kExternalDir }, + { kFileSystemTypeTest, kTestDir }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidTypes); ++i) { + if (StartsWithASCII(inner_path, kValidTypes[i].dir, true)) { + file_system_type = kValidTypes[i].type; + break; + } + } + + if (file_system_type == kFileSystemTypeUnknown) + return false; + + std::string path = net::UnescapeURLComponent(url.path(), + net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS | + net::UnescapeRule::CONTROL_CHARS); + + // Ensure the path is relative. + while (!path.empty() && path[0] == '/') + path.erase(0, 1); + + base::FilePath converted_path = base::FilePath::FromUTF8Unsafe(path); + + // All parent references should have been resolved in the renderer. + if (converted_path.ReferencesParent()) + return false; + + if (origin_url) + *origin_url = url.GetOrigin(); + if (mount_type) + *mount_type = file_system_type; + if (virtual_path) + *virtual_path = converted_path.NormalizePathSeparators(). + StripTrailingSeparators(); + + return true; +} + +FileSystemURL::FileSystemURL(const GURL& url) + : mount_type_(kFileSystemTypeUnknown), + type_(kFileSystemTypeUnknown) { + is_valid_ = ParseFileSystemSchemeURL(url, &origin_, &mount_type_, + &virtual_path_); + path_ = virtual_path_; + type_ = mount_type_; +} + +FileSystemURL::FileSystemURL(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path) + : is_valid_(true), + origin_(origin), + mount_type_(mount_type), + virtual_path_(virtual_path.NormalizePathSeparators()), + type_(mount_type), + path_(virtual_path.NormalizePathSeparators()) { +} + +FileSystemURL::FileSystemURL(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path, + const std::string& mount_filesystem_id, + FileSystemType cracked_type, + const base::FilePath& cracked_path, + const std::string& filesystem_id) + : is_valid_(true), + origin_(origin), + mount_type_(mount_type), + virtual_path_(virtual_path.NormalizePathSeparators()), + mount_filesystem_id_(mount_filesystem_id), + type_(cracked_type), + path_(cracked_path.NormalizePathSeparators()), + filesystem_id_(filesystem_id) { +} + +FileSystemURL::~FileSystemURL() {} + +std::string FileSystemURL::DebugString() const { + if (!is_valid_) + return "invalid filesystem: URL"; + std::ostringstream ss; + ss << GetFileSystemRootURI(origin_, mount_type_); + + // filesystem_id_ will be non empty for (and only for) cracked URLs. + if (!filesystem_id_.empty()) { + ss << virtual_path_.value(); + ss << " ("; + ss << GetFileSystemTypeString(type_) << "@" << filesystem_id_ << ":"; + ss << path_.value(); + ss << ")"; + } else { + ss << path_.value(); + } + return ss.str(); +} + +bool FileSystemURL::IsParent(const FileSystemURL& child) const { + return IsInSameFileSystem(child) && + path().IsParent(child.path()); +} + +bool FileSystemURL::IsInSameFileSystem(const FileSystemURL& other) const { + return origin() == other.origin() && + type() == other.type() && + filesystem_id() == other.filesystem_id(); +} + +bool FileSystemURL::operator==(const FileSystemURL& that) const { + return origin_ == that.origin_ && + type_ == that.type_ && + path_ == that.path_ && + filesystem_id_ == that.filesystem_id_ && + is_valid_ == that.is_valid_; +} + +bool FileSystemURL::Comparator::operator()(const FileSystemURL& lhs, + const FileSystemURL& rhs) const { + DCHECK(lhs.is_valid_ && rhs.is_valid_); + if (lhs.origin_ != rhs.origin_) + return lhs.origin_ < rhs.origin_; + if (lhs.type_ != rhs.type_) + return lhs.type_ < rhs.type_; + if (lhs.filesystem_id_ != rhs.filesystem_id_) + return lhs.filesystem_id_ < rhs.filesystem_id_; + return lhs.path_ < rhs.path_; +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_system_url.h b/webkit/browser/fileapi/file_system_url.h new file mode 100644 index 0000000..0b88dd4 --- /dev/null +++ b/webkit/browser/fileapi/file_system_url.h @@ -0,0 +1,173 @@ +// 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_BROWSER_FILEAPI_FILE_SYSTEM_URL_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_URL_H_ + +#include <set> +#include <string> + +#include "base/platform_file.h" +#include "googleurl/src/gurl.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace fileapi { + +// A class representing a filesystem URL which consists of origin URL, +// type and an internal path used inside the filesystem. +// +// When a FileSystemURL instance is created for a GURL (for filesystem: scheme), +// each accessor method would return following values: +// +// Example: For a URL 'filesystem:http://foo.com/temporary/foo/bar': +// origin() returns 'http://foo.com', +// mount_type() returns kFileSystemTypeTemporary, +// virtual_path() returns 'foo/bar', +// type() returns the same value as mount_type(), +// path() returns the same value as virtual_path(), +// +// All other accessors return empty or invalid value. +// +// FileSystemURL can also be created to represent a 'cracked' filesystem URL if +// the original URL's type/path is pointing to a mount point which can be +// further resolved to a lower filesystem type/path. +// +// Example: Assume a path '/media/removable' is mounted at mount name +// 'mount_name' with type kFileSystemTypeFoo as an external file system. +// +// The original URL would look like: +// 'filesystem:http://bar.com/external/mount_name/foo/bar': +// +// FileSystemURL('http://bar.com', +// kFileSystemTypeExternal, +// 'mount_name/foo/bar' +// 'mount_name', +// kFileSystemTypeFoo, +// '/media/removable/foo/bar'); +// would create a FileSystemURL whose accessors return: +// +// origin() returns 'http://bar.com', +// mount_type() returns kFileSystemTypeExternal, +// virtual_path() returns 'mount_name/foo/bar', +// type() returns the kFileSystemTypeFoo, +// path() returns '/media/removable/foo/bar', +// +// Note that in either case virtual_path() always returns the path part after +// 'type' part in the original URL, and mount_type() always returns the 'type' +// part in the original URL. +// +// Additionally, following accessors would return valid values: +// filesystem_id() returns 'mount_name'. +// +// It is impossible to directly create a valid FileSystemURL instance (except by +// using CreatedForTest methods, which should not be used in production code). +// To get a valid FileSystemURL, one of the following methods can be used: +// <Friend>::CrackURL, <Friend>::CreateCrackedFileSystemURL, where <Friend> is +// one of the friended classes. +// +// TODO(ericu): Look into making virtual_path() [and all FileSystem API virtual +// paths] just an std::string, to prevent platform-specific base::FilePath behavior +// from getting invoked by accident. Currently the base::FilePath returned here needs +// special treatment, as it may contain paths that are illegal on the current +// platform. To avoid problems, use VirtualPath::BaseName and +// VirtualPath::GetComponents instead of the base::FilePath methods. +class WEBKIT_STORAGE_EXPORT FileSystemURL { + public: + FileSystemURL(); + ~FileSystemURL(); + + // Methods for creating FileSystemURL without attempting to crack them. + // Should be used only in tests. + static FileSystemURL CreateForTest(const GURL& url); + static FileSystemURL CreateForTest(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path); + + // Parses filesystem scheme |url| into uncracked FileSystemURL components. + static bool ParseFileSystemSchemeURL(const GURL& url, + GURL* origin, + FileSystemType* mount_type, + base::FilePath* virtual_path); + + // Returns true if this instance represents a valid FileSystem URL. + bool is_valid() const { return is_valid_; } + + // Returns the origin part of this URL. See the class comment for details. + const GURL& origin() const { return origin_; } + + // Returns the type part of this URL. See the class comment for details. + FileSystemType type() const { return type_; } + + // Returns the cracked path of this URL. See the class comment for details. + const base::FilePath& path() const { return path_; } + + // Returns the original path part of this URL. + // See the class comment for details. + // TODO(kinuko): this must return std::string. + const base::FilePath& virtual_path() const { return virtual_path_; } + + // Returns the filesystem ID/mount name for isolated/external filesystem URLs. + // See the class comment for details. + const std::string& filesystem_id() const { return filesystem_id_; } + const std::string& mount_filesystem_id() const { + return mount_filesystem_id_; + } + + FileSystemType mount_type() const { return mount_type_; } + + std::string DebugString() const; + + // Returns true if this URL is a strict parent of the |child|. + bool IsParent(const FileSystemURL& child) const; + + bool IsInSameFileSystem(const FileSystemURL& other) const; + + bool operator==(const FileSystemURL& that) const; + + struct WEBKIT_STORAGE_EXPORT Comparator { + bool operator() (const FileSystemURL& lhs, const FileSystemURL& rhs) const; + }; + + private: + friend class FileSystemContext; + friend class ExternalMountPoints; + friend class IsolatedContext; + + explicit FileSystemURL(const GURL& filesystem_url); + FileSystemURL(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path); + // Creates a cracked FileSystemURL. + FileSystemURL(const GURL& origin, + FileSystemType mount_type, + const base::FilePath& virtual_path, + const std::string& mount_filesystem_id, + FileSystemType cracked_type, + const base::FilePath& cracked_path, + const std::string& filesystem_id); + + bool is_valid_; + + // Values parsed from the original URL. + GURL origin_; + FileSystemType mount_type_; + base::FilePath virtual_path_; + + // Values obtained by cracking URLs. + // |mount_filesystem_id_| is retrieved from the first round of cracking, + // and the rest of the fields are from recursive cracking. Permission + // checking on the top-level mount information should be done with the former, + // and the low-level file operation should be implemented with the latter. + std::string mount_filesystem_id_; + FileSystemType type_; + base::FilePath path_; + std::string filesystem_id_; +}; + +typedef std::set<FileSystemURL, FileSystemURL::Comparator> FileSystemURLSet; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_URL_H_ diff --git a/webkit/browser/fileapi/file_system_url_request_job.cc b/webkit/browser/fileapi/file_system_url_request_job.cc index b6a022c..ca0e312 100644 --- a/webkit/browser/fileapi/file_system_url_request_job.cc +++ b/webkit/browser/fileapi/file_system_url_request_job.cc @@ -27,8 +27,8 @@ #include "net/http/http_util.h" #include "net/url_request/url_request.h" #include "webkit/blob/file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/local_file_system_operation.h" -#include "webkit/fileapi/file_system_context.h" #include "webkit/fileapi/file_system_util.h" using net::NetworkDelegate; diff --git a/webkit/browser/fileapi/file_system_url_request_job.h b/webkit/browser/fileapi/file_system_url_request_job.h index 49b4dc0..c372fa8 100644 --- a/webkit/browser/fileapi/file_system_url_request_job.h +++ b/webkit/browser/fileapi/file_system_url_request_job.h @@ -13,7 +13,7 @@ #include "base/platform_file.h" #include "net/http/http_byte_range.h" #include "net/url_request/url_request_job.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/storage/webkit_storage_export.h" class GURL; diff --git a/webkit/browser/fileapi/file_system_url_request_job_unittest.cc b/webkit/browser/fileapi/file_system_url_request_job_unittest.cc index 2456b78..5b4c2ec 100644 --- a/webkit/browser/fileapi/file_system_url_request_job_unittest.cc +++ b/webkit/browser/fileapi/file_system_url_request_job_unittest.cc @@ -28,11 +28,11 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" namespace fileapi { namespace { diff --git a/webkit/browser/fileapi/file_system_url_unittest.cc b/webkit/browser/fileapi/file_system_url_unittest.cc new file mode 100644 index 0000000..90336a6 --- /dev/null +++ b/webkit/browser/fileapi/file_system_url_unittest.cc @@ -0,0 +1,195 @@ +// 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/browser/fileapi/file_system_url.h" + +#include "base/files/file_path.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/syncable/syncable_file_system_util.h" + +#define FPL FILE_PATH_LITERAL + +#if defined(FILE_PATH_USES_DRIVE_LETTERS) +#define DRIVE FPL("C:") +#else +#define DRIVE FPL("/a/") +#endif + +namespace fileapi { + +namespace { + +FileSystemURL CreateFileSystemURL(const std::string& url_string) { + FileSystemURL url = FileSystemURL::CreateForTest(GURL(url_string)); + EXPECT_TRUE(url.type() != kFileSystemTypeExternal && + url.type() != kFileSystemTypeIsolated); + return url; +} + +std::string NormalizedUTF8Path(const base::FilePath& path) { + return path.NormalizePathSeparators().AsUTF8Unsafe(); +} + +} // namespace + +TEST(FileSystemURLTest, ParsePersistent) { + FileSystemURL url = CreateFileSystemURL( + "filesystem:http://chromium.org/persistent/directory/file"); + ASSERT_TRUE(url.is_valid()); + EXPECT_EQ("http://chromium.org/", url.origin().spec()); + EXPECT_EQ(kFileSystemTypePersistent, url.type()); + EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value()); + EXPECT_EQ(FPL("directory"), url.path().DirName().value()); +} + +TEST(FileSystemURLTest, ParseTemporary) { + FileSystemURL url = CreateFileSystemURL( + "filesystem:http://chromium.org/temporary/directory/file"); + ASSERT_TRUE(url.is_valid()); + EXPECT_EQ("http://chromium.org/", url.origin().spec()); + EXPECT_EQ(kFileSystemTypeTemporary, url.type()); + EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value()); + EXPECT_EQ(FPL("directory"), url.path().DirName().value()); +} + +TEST(FileSystemURLTest, EnsureFilePathIsRelative) { + FileSystemURL url = CreateFileSystemURL( + "filesystem:http://chromium.org/temporary/////directory/file"); + ASSERT_TRUE(url.is_valid()); + EXPECT_EQ("http://chromium.org/", url.origin().spec()); + EXPECT_EQ(kFileSystemTypeTemporary, url.type()); + EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value()); + EXPECT_EQ(FPL("directory"), url.path().DirName().value()); + EXPECT_FALSE(url.path().IsAbsolute()); +} + +TEST(FileSystemURLTest, RejectBadSchemes) { + EXPECT_FALSE(CreateFileSystemURL("http://chromium.org/").is_valid()); + EXPECT_FALSE(CreateFileSystemURL("https://chromium.org/").is_valid()); + EXPECT_FALSE(CreateFileSystemURL("file:///foo/bar").is_valid()); + EXPECT_FALSE(CreateFileSystemURL("foobar:///foo/bar").is_valid()); +} + +TEST(FileSystemURLTest, UnescapePath) { + FileSystemURL url = CreateFileSystemURL( + "filesystem:http://chromium.org/persistent/%7Echromium/space%20bar"); + ASSERT_TRUE(url.is_valid()); + EXPECT_EQ(FPL("space bar"), VirtualPath::BaseName(url.path()).value()); + EXPECT_EQ(FPL("~chromium"), url.path().DirName().value()); +} + +TEST(FileSystemURLTest, RejectBadType) { + EXPECT_FALSE(CreateFileSystemURL( + "filesystem:http://c.org/foobar/file").is_valid()); +} + +TEST(FileSystemURLTest, RejectMalformedURL) { + EXPECT_FALSE(CreateFileSystemURL("filesystem:///foobar/file").is_valid()); + EXPECT_FALSE(CreateFileSystemURL("filesystem:foobar/file").is_valid()); +} + +TEST(FileSystemURLTest, CompareURLs) { + const GURL urls[] = { + GURL("filesystem:http://chromium.org/temporary/dir a/file a"), + GURL("filesystem:http://chromium.org/temporary/dir a/file a"), + GURL("filesystem:http://chromium.org/temporary/dir a/file b"), + GURL("filesystem:http://chromium.org/temporary/dir a/file aa"), + GURL("filesystem:http://chromium.org/temporary/dir b/file a"), + GURL("filesystem:http://chromium.org/temporary/dir aa/file b"), + GURL("filesystem:http://chromium.com/temporary/dir a/file a"), + GURL("filesystem:https://chromium.org/temporary/dir a/file a") + }; + + FileSystemURL::Comparator compare; + for (size_t i = 0; i < arraysize(urls); ++i) { + for (size_t j = 0; j < arraysize(urls); ++j) { + SCOPED_TRACE(testing::Message() << i << " < " << j); + EXPECT_EQ(urls[i] < urls[j], + compare(FileSystemURL::CreateForTest(urls[i]), + FileSystemURL::CreateForTest(urls[j]))); + } + } + + const FileSystemURL a = CreateFileSystemURL( + "filesystem:http://chromium.org/temporary/dir a/file a"); + const FileSystemURL b = CreateFileSystemURL( + "filesystem:http://chromium.org/persistent/dir a/file a"); + EXPECT_EQ(a.type() < b.type(), compare(a, b)); + EXPECT_EQ(b.type() < a.type(), compare(b, a)); +} + +TEST(FileSystemURLTest, IsParent) { + const std::string root1 = GetFileSystemRootURI( + GURL("http://example.com"), kFileSystemTypeTemporary).spec(); + const std::string root2 = GetFileSystemRootURI( + GURL("http://example.com"), kFileSystemTypePersistent).spec(); + const std::string root3 = GetFileSystemRootURI( + GURL("http://chromium.org"), kFileSystemTypeTemporary).spec(); + + const std::string parent("dir"); + const std::string child("dir/child"); + const std::string other("other"); + + // True cases. + EXPECT_TRUE(CreateFileSystemURL(root1 + parent).IsParent( + CreateFileSystemURL(root1 + child))); + EXPECT_TRUE(CreateFileSystemURL(root2 + parent).IsParent( + CreateFileSystemURL(root2 + child))); + + // False cases: the path is not a child. + EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent( + CreateFileSystemURL(root1 + other))); + EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent( + CreateFileSystemURL(root1 + parent))); + EXPECT_FALSE(CreateFileSystemURL(root1 + child).IsParent( + CreateFileSystemURL(root1 + parent))); + + // False case: different types. + EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent( + CreateFileSystemURL(root2 + child))); + + // False case: different origins. + EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent( + CreateFileSystemURL(root3 + child))); +} + +TEST(FileSystemURLTest, DebugString) { + const GURL kOrigin("http://example.com"); + const base::FilePath kPath(FPL("dir/file")); + + const FileSystemURL kURL1 = FileSystemURL::CreateForTest( + kOrigin, kFileSystemTypeTemporary, kPath); + EXPECT_EQ("filesystem:http://example.com/temporary/" + + NormalizedUTF8Path(kPath), + kURL1.DebugString()); +} + +TEST(FileSystemURLTest, IsInSameFileSystem) { + FileSystemURL url_foo_temp_a = FileSystemURL::CreateForTest( + GURL("http://foo"), kFileSystemTypeTemporary, + base::FilePath::FromUTF8Unsafe("a")); + FileSystemURL url_foo_temp_b = FileSystemURL::CreateForTest( + GURL("http://foo"), kFileSystemTypeTemporary, + base::FilePath::FromUTF8Unsafe("b")); + FileSystemURL url_foo_perm_a = FileSystemURL::CreateForTest( + GURL("http://foo"), kFileSystemTypePersistent, + base::FilePath::FromUTF8Unsafe("a")); + FileSystemURL url_bar_temp_a = FileSystemURL::CreateForTest( + GURL("http://bar"), kFileSystemTypeTemporary, + base::FilePath::FromUTF8Unsafe("a")); + FileSystemURL url_bar_perm_a = FileSystemURL::CreateForTest( + GURL("http://bar"), kFileSystemTypePersistent, + base::FilePath::FromUTF8Unsafe("a")); + + EXPECT_TRUE(url_foo_temp_a.IsInSameFileSystem(url_foo_temp_a)); + EXPECT_TRUE(url_foo_temp_a.IsInSameFileSystem(url_foo_temp_b)); + EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_foo_perm_a)); + EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_bar_temp_a)); + EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_bar_perm_a)); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_writer_delegate.cc b/webkit/browser/fileapi/file_writer_delegate.cc new file mode 100644 index 0000000..f3a2d94 --- /dev/null +++ b/webkit/browser/fileapi/file_writer_delegate.cc @@ -0,0 +1,253 @@ +// 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/browser/fileapi/file_writer_delegate.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_util_proxy.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/sequenced_task_runner.h" +#include "base/threading/thread_restrictions.h" +#include "net/base/net_errors.h" +#include "webkit/browser/fileapi/file_stream_writer.h" +#include "webkit/browser/fileapi/file_system_context.h" + +namespace fileapi { + +static const int kReadBufSize = 32768; + +namespace { + +base::PlatformFileError NetErrorToPlatformFileError(int error) { +// TODO(kinuko): Move this static method to more convenient place. + switch (error) { + case net::OK: + return base::PLATFORM_FILE_OK; + 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; + } +} + +} // namespace + +FileWriterDelegate::FileWriterDelegate( + const DelegateWriteCallback& write_callback, + scoped_ptr<FileStreamWriter> file_stream_writer) + : write_callback_(write_callback), + file_stream_writer_(file_stream_writer.Pass()), + writing_started_(false), + bytes_written_backlog_(0), + bytes_written_(0), + bytes_read_(0), + io_buffer_(new net::IOBufferWithSize(kReadBufSize)), + weak_factory_(this) { +} + +FileWriterDelegate::~FileWriterDelegate() { +} + +void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request) { + request_ = request.Pass(); + request_->Start(); +} + +bool FileWriterDelegate::Cancel() { + if (request_) { + // This halts any callbacks on this delegate. + request_->set_delegate(NULL); + request_->Cancel(); + } + + const int status = file_stream_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, + const GURL& new_url, + bool* defer_redirect) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnAuthRequired(net::URLRequest* request, + net::AuthChallengeInfo* auth_info) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnCertificateRequested( + net::URLRequest* request, + net::SSLCertRequestInfo* cert_request_info) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request, + const net::SSLInfo& ssl_info, + bool fatal) { + NOTREACHED(); + OnError(base::PLATFORM_FILE_ERROR_SECURITY); +} + +void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) { + DCHECK_EQ(request_.get(), request); + if (!request->status().is_success() || request->GetResponseCode() != 200) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + Read(); +} + +void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, + int bytes_read) { + DCHECK_EQ(request_.get(), request); + if (!request->status().is_success()) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + OnDataReceived(bytes_read); +} + +void FileWriterDelegate::Read() { + bytes_written_ = 0; + bytes_read_ = 0; + if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&FileWriterDelegate::OnDataReceived, + weak_factory_.GetWeakPtr(), + bytes_read_)); + } else if (!request_->status().is_io_pending()) { + OnError(base::PLATFORM_FILE_ERROR_FAILED); + } +} + +void FileWriterDelegate::OnDataReceived(int bytes_read) { + bytes_read_ = bytes_read; + if (!bytes_read_) { // We're done. + OnProgress(0, true); + } else { + // This could easily be optimized to rotate between a pool of buffers, so + // that we could read and write at the same time. It's not yet clear that + // it's necessary. + cursor_ = new net::DrainableIOBuffer(io_buffer_, bytes_read_); + Write(); + } +} + +void FileWriterDelegate::Write() { + writing_started_ = true; + int64 bytes_to_write = bytes_read_ - bytes_written_; + int write_response = + file_stream_writer_->Write(cursor_, + static_cast<int>(bytes_to_write), + base::Bind(&FileWriterDelegate::OnDataWritten, + weak_factory_.GetWeakPtr())); + if (write_response > 0) + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&FileWriterDelegate::OnDataWritten, + weak_factory_.GetWeakPtr(), + write_response)); + else if (net::ERR_IO_PENDING != write_response) + OnError(NetErrorToPlatformFileError(write_response)); +} + +void FileWriterDelegate::OnDataWritten(int write_response) { + if (write_response > 0) { + OnProgress(write_response, false); + cursor_->DidConsume(write_response); + bytes_written_ += write_response; + if (bytes_written_ == bytes_read_) + Read(); + else + Write(); + } else { + OnError(NetErrorToPlatformFileError(write_response)); + } +} + +FileWriterDelegate::WriteProgressStatus +FileWriterDelegate::GetCompletionStatusOnError() const { + return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED; +} + +void FileWriterDelegate::OnError(base::PlatformFileError error) { + if (request_) { + request_->set_delegate(NULL); + request_->Cancel(); + } + + if (writing_started_) + FlushForCompletion(error, 0, ERROR_WRITE_STARTED); + else + write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED); +} + +void FileWriterDelegate::OnProgress(int bytes_written, bool done) { + DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_); + static const int kMinProgressDelayMS = 200; + base::Time currentTime = base::Time::Now(); + if (done || last_progress_event_time_.is_null() || + (currentTime - last_progress_event_time_).InMilliseconds() > + kMinProgressDelayMS) { + bytes_written += bytes_written_backlog_; + last_progress_event_time_ = currentTime; + bytes_written_backlog_ = 0; + + if (done) { + FlushForCompletion(base::PLATFORM_FILE_OK, bytes_written, + SUCCESS_COMPLETED); + } else { + write_callback_.Run(base::PLATFORM_FILE_OK, bytes_written, + SUCCESS_IO_PENDING); + } + return; + } + bytes_written_backlog_ += bytes_written; +} + +void FileWriterDelegate::OnWriteCancelled(int status) { + write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0, + GetCompletionStatusOnError()); +} + +void FileWriterDelegate::FlushForCompletion( + base::PlatformFileError error, + int bytes_written, + WriteProgressStatus progress_status) { + int flush_error = file_stream_writer_->Flush( + base::Bind(&FileWriterDelegate::OnFlushed, + weak_factory_.GetWeakPtr(), + error, bytes_written, progress_status)); + if (flush_error != net::ERR_IO_PENDING) + OnFlushed(error, bytes_written, progress_status, flush_error); +} + +void FileWriterDelegate::OnFlushed(base::PlatformFileError error, + int bytes_written, + WriteProgressStatus progress_status, + int flush_error) { + if (error == base::PLATFORM_FILE_OK && flush_error != net::OK) { + // If the Flush introduced an error, overwrite the status. + // Otherwise, keep the original error status. + error = NetErrorToPlatformFileError(flush_error); + progress_status = GetCompletionStatusOnError(); + } + write_callback_.Run(error, bytes_written, progress_status); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/file_writer_delegate.h b/webkit/browser/fileapi/file_writer_delegate.h new file mode 100644 index 0000000..6fd8fab --- /dev/null +++ b/webkit/browser/fileapi/file_writer_delegate.h @@ -0,0 +1,106 @@ +// 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_BROWSER_FILEAPI_FILE_WRITER_DELEGATE_H_ +#define WEBKIT_BROWSER_FILEAPI_FILE_WRITER_DELEGATE_H_ + +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "base/time.h" +#include "net/base/file_stream.h" +#include "net/base/io_buffer.h" +#include "net/url_request/url_request.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace fileapi { + +class FileStreamWriter; +class FileSystemOperationContext; +class FileSystemQuotaUtil; + +class WEBKIT_STORAGE_EXPORT_PRIVATE FileWriterDelegate + : public net::URLRequest::Delegate { + public: + enum WriteProgressStatus { + SUCCESS_IO_PENDING, + SUCCESS_COMPLETED, + ERROR_WRITE_STARTED, + ERROR_WRITE_NOT_STARTED, + }; + + typedef base::Callback<void(base::PlatformFileError result, + int64 bytes, + WriteProgressStatus write_status)> + DelegateWriteCallback; + + FileWriterDelegate( + const DelegateWriteCallback& write_callback, + scoped_ptr<FileStreamWriter> file_writer); + virtual ~FileWriterDelegate(); + + 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 + // |write_operation|->DidWrite() with complete=true to let the operation + // perform the final cleanup. + bool Cancel(); + + virtual void OnReceivedRedirect(net::URLRequest* request, + const GURL& new_url, + bool* defer_redirect) OVERRIDE; + virtual void OnAuthRequired(net::URLRequest* request, + net::AuthChallengeInfo* auth_info) OVERRIDE; + virtual void OnCertificateRequested( + net::URLRequest* request, + net::SSLCertRequestInfo* cert_request_info) OVERRIDE; + virtual void OnSSLCertificateError(net::URLRequest* request, + const net::SSLInfo& ssl_info, + bool fatal) OVERRIDE; + virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; + virtual void OnReadCompleted(net::URLRequest* request, + int bytes_read) OVERRIDE; + + private: + void OnGetFileInfoAndStartRequest( + scoped_ptr<net::URLRequest> request, + base::PlatformFileError error, + const base::PlatformFileInfo& file_info); + void Read(); + void OnDataReceived(int bytes_read); + void Write(); + void OnDataWritten(int write_response); + void OnError(base::PlatformFileError error); + void OnProgress(int bytes_read, bool done); + void OnWriteCancelled(int status); + void FlushForCompletion(base::PlatformFileError error, + int bytes_written, + WriteProgressStatus progress_status); + void OnFlushed(base::PlatformFileError error, + int bytes_written, + WriteProgressStatus progress_status, + int flush_error); + + FileSystemQuotaUtil* quota_util() const; + WriteProgressStatus GetCompletionStatusOnError() const; + + DelegateWriteCallback write_callback_; + scoped_ptr<FileStreamWriter> file_stream_writer_; + base::Time last_progress_event_time_; + bool writing_started_; + int bytes_written_backlog_; + int bytes_written_; + int bytes_read_; + scoped_refptr<net::IOBufferWithSize> io_buffer_; + scoped_refptr<net::DrainableIOBuffer> cursor_; + scoped_ptr<net::URLRequest> request_; + base::WeakPtrFactory<FileWriterDelegate> weak_factory_; +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_FILE_WRITER_DELEGATE_H_ diff --git a/webkit/browser/fileapi/file_writer_delegate_unittest.cc b/webkit/browser/fileapi/file_writer_delegate_unittest.cc new file mode 100644 index 0000000..320fee8e --- /dev/null +++ b/webkit/browser/fileapi/file_writer_delegate_unittest.cc @@ -0,0 +1,465 @@ +// 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 <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "googleurl/src/gurl.h" +#include "net/base/io_buffer.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_job.h" +#include "net/url_request/url_request_status.h" +#include "testing/platform_test.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_quota_util.h" +#include "webkit/browser/fileapi/file_writer_delegate.h" +#include "webkit/browser/fileapi/local_file_system_operation.h" +#include "webkit/browser/fileapi/mock_file_system_context.h" +#include "webkit/browser/fileapi/sandbox_file_stream_writer.h" + +namespace fileapi { + +namespace { + +const GURL kOrigin("http://example.com"); +const FileSystemType kFileSystemType = kFileSystemTypeTest; + +class Result { + public: + Result() + : status_(base::PLATFORM_FILE_OK), + bytes_written_(0), + write_status_(FileWriterDelegate::SUCCESS_IO_PENDING) {} + + base::PlatformFileError status() const { return status_; } + int64 bytes_written() const { return bytes_written_; } + FileWriterDelegate::WriteProgressStatus write_status() const { + return write_status_; + } + + void DidWrite(base::PlatformFileError status, int64 bytes, + FileWriterDelegate::WriteProgressStatus write_status) { + write_status_ = write_status; + if (status == base::PLATFORM_FILE_OK) { + bytes_written_ += bytes; + if (write_status_ != FileWriterDelegate::SUCCESS_IO_PENDING) + base::MessageLoop::current()->Quit(); + } else { + EXPECT_EQ(base::PLATFORM_FILE_OK, status_); + status_ = status; + base::MessageLoop::current()->Quit(); + } + } + + private: + // For post-operation status. + base::PlatformFileError status_; + int64 bytes_written_; + FileWriterDelegate::WriteProgressStatus write_status_; +}; + +const char kData[] = "The quick brown fox jumps over the lazy dog.\n"; +const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1; + +} // namespace (anonymous) + +class FileWriterDelegateTest : public PlatformTest { + public: + FileWriterDelegateTest() + : loop_(base::MessageLoop::TYPE_IO) {} + + protected: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + FileSystemFileUtil* file_util() { + return file_system_context_->GetFileUtil(kFileSystemType); + } + + int64 usage() { + return file_system_context_->GetQuotaUtil(kFileSystemType)-> + GetOriginUsageOnFileThread(file_system_context_, + kOrigin, + kFileSystemType); + } + + int64 GetFileSizeOnDisk(const char* test_file_path) { + // There might be in-flight flush/write. + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&base::DoNothing)); + base::MessageLoop::current()->RunUntilIdle(); + + FileSystemURL url = GetFileSystemURL(test_file_path); + base::PlatformFileInfo file_info; + base::FilePath platform_path; + EXPECT_EQ(base::PLATFORM_FILE_OK, + file_util()->GetFileInfo(NewOperationContext().get(), url, + &file_info, &platform_path)); + return file_info.size; + } + + FileSystemURL GetFileSystemURL(const char* file_name) const { + return file_system_context_->CreateCrackedFileSystemURL( + kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name)); + } + + scoped_ptr<FileSystemOperationContext> NewOperationContext() { + FileSystemOperationContext* context = + new FileSystemOperationContext(file_system_context_); + context->set_update_observers( + *file_system_context_->GetUpdateObservers(kFileSystemType)); + return make_scoped_ptr(context); + } + + FileWriterDelegate* CreateWriterDelegate( + const char* test_file_path, + int64 offset, + int64 allowed_growth, + Result* result) { + SandboxFileStreamWriter* writer = new SandboxFileStreamWriter( + file_system_context_, + GetFileSystemURL(test_file_path), + offset, + *file_system_context_->GetUpdateObservers(kFileSystemType)); + writer->set_default_quota(allowed_growth); + return new FileWriterDelegate( + base::Bind(&Result::DidWrite, base::Unretained(result)), + scoped_ptr<FileStreamWriter>(writer)); + } + + // Creates and sets up a FileWriterDelegate for writing the given |blob_url|, + // and creates a new FileWriterDelegate for the file. + void PrepareForWrite(const char* test_file_path, + const GURL& blob_url, + int64 offset, + int64 allowed_growth) { + result_.reset(new Result()); + file_writer_delegate_.reset( + CreateWriterDelegate(test_file_path, offset, allowed_growth, + result_.get())); + request_.reset(empty_context_.CreateRequest( + blob_url, file_writer_delegate_.get())); + } + + static net::URLRequest::ProtocolFactory Factory; + + // This should be alive until the very end of this instance. + base::MessageLoop loop_; + + scoped_refptr<FileSystemContext> file_system_context_; + + net::URLRequestContext empty_context_; + scoped_ptr<FileWriterDelegate> file_writer_delegate_; + scoped_ptr<net::URLRequest> request_; + scoped_ptr<Result> result_; + + base::ScopedTempDir dir_; + + static const char* content_; +}; + +const char* FileWriterDelegateTest::content_ = NULL; + +namespace { + +static std::string g_content; + +class FileWriterDelegateTestJob : public net::URLRequestJob { + public: + FileWriterDelegateTestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const std::string& content) + : net::URLRequestJob(request, network_delegate), + content_(content), + remaining_bytes_(content.length()), + cursor_(0) { + } + + virtual void Start() OVERRIDE { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete, this)); + } + + virtual bool ReadRawData(net::IOBuffer* buf, + int buf_size, + int *bytes_read) OVERRIDE { + if (remaining_bytes_ < buf_size) + buf_size = static_cast<int>(remaining_bytes_); + + for (int i = 0; i < buf_size; ++i) + buf->data()[i] = content_[cursor_++]; + remaining_bytes_ -= buf_size; + + SetStatus(net::URLRequestStatus()); + *bytes_read = buf_size; + return true; + } + + virtual int GetResponseCode() const OVERRIDE { + return 200; + } + + protected: + virtual ~FileWriterDelegateTestJob() {} + + private: + std::string content_; + int remaining_bytes_; + int cursor_; +}; + +} // namespace (anonymous) + +// static +net::URLRequestJob* FileWriterDelegateTest::Factory( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const std::string& scheme) { + return new FileWriterDelegateTestJob( + request, network_delegate, FileWriterDelegateTest::content_); +} + +void FileWriterDelegateTest::SetUp() { + ASSERT_TRUE(dir_.CreateUniqueTempDir()); + + file_system_context_ = CreateFileSystemContextForTesting( + NULL, dir_.path()); + + bool created = false; + scoped_ptr<FileSystemOperationContext> context = NewOperationContext(); + context->set_allowed_bytes_growth(kint64max); + base::PlatformFileError error = file_util()->EnsureFileExists( + context.get(), + GetFileSystemURL("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); + file_system_context_ = NULL; + base::MessageLoop::current()->RunUntilIdle(); +} + +TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { + const GURL kBlobURL("blob:nolimit"); + content_ = kData; + + PrepareForWrite("test", kBlobURL, 0, kint64max); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + ASSERT_EQ(kDataSize, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); +} + +TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) { + const GURL kBlobURL("blob:just"); + content_ = kData; + const int64 kAllowedGrowth = kDataSize; + PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + ASSERT_EQ(kAllowedGrowth, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + + EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); +} + +TEST_F(FileWriterDelegateTest, DISABLED_WriteFailureByQuota) { + const GURL kBlobURL("blob:failure"); + content_ = kData; + const int64 kAllowedGrowth = kDataSize - 1; + PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status()); + file_writer_delegate_.reset(); + + ASSERT_EQ(kAllowedGrowth, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + + EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); + ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status()); +} + +TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) { + const GURL kBlobURL("blob:zero"); + content_ = ""; + int64 kAllowedGrowth = 0; + PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + ASSERT_EQ(kAllowedGrowth, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + + EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); +} + +TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { + scoped_ptr<FileWriterDelegate> file_writer_delegate2; + scoped_ptr<net::URLRequest> request2; + scoped_ptr<Result> result2; + + bool created = false; + file_util()->EnsureFileExists(NewOperationContext().get(), + GetFileSystemURL("test2"), + &created); + ASSERT_TRUE(created); + + const GURL kBlobURL("blob:nolimitconcurrent"); + const GURL kBlobURL2("blob:nolimitconcurrent2"); + content_ = kData; + + PrepareForWrite("test", kBlobURL, 0, kint64max); + + // Credate another FileWriterDelegate for concurrent write. + result2.reset(new Result()); + file_writer_delegate2.reset(CreateWriterDelegate( + "test2", 0, kint64max, result2.get())); + request2.reset(empty_context_.CreateRequest( + kBlobURL2, file_writer_delegate2.get())); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + file_writer_delegate2->Start(request2.Pass()); + base::MessageLoop::current()->Run(); + if (result_->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING || + result2->write_status() == FileWriterDelegate::SUCCESS_IO_PENDING) + base::MessageLoop::current()->Run(); + + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result2->write_status()); + file_writer_delegate_.reset(); + file_writer_delegate2.reset(); + + ASSERT_EQ(kDataSize * 2, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage()); + + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + EXPECT_EQ(kDataSize, result2->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result2->status()); +} + +TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { + const GURL kBlobURL("blob:failure-with-updated-quota"); + content_ = kData; + + // Writing kDataSize (=45) bytes data while allowed_growth is 100. + int64 offset = 0; + int64 allowed_growth = 100; + ASSERT_LT(kDataSize, allowed_growth); + PrepareForWrite("test", kBlobURL, offset, allowed_growth); + + ASSERT_EQ(0, usage()); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + ASSERT_EQ(kDataSize, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + + // Trying to overwrite kDataSize bytes data while allowed_growth is 20. + offset = 0; + allowed_growth = 20; + PrepareForWrite("test", kBlobURL, offset, allowed_growth); + + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + EXPECT_EQ(kDataSize, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + + // Trying to write kDataSize bytes data from offset 25 while + // allowed_growth is 55. + offset = 25; + allowed_growth = 55; + PrepareForWrite("test", kBlobURL, offset, allowed_growth); + + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + EXPECT_EQ(offset + kDataSize, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + + // Trying to overwrite 45 bytes data while allowed_growth is -20. + offset = 0; + allowed_growth = -20; + PrepareForWrite("test", kBlobURL, offset, allowed_growth); + + int64 pre_write_usage = GetFileSizeOnDisk("test"); + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result_->write_status()); + file_writer_delegate_.reset(); + + EXPECT_EQ(pre_write_usage, usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kDataSize, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); + + // Trying to overwrite 45 bytes data with offset pre_write_usage - 20, + // while allowed_growth is 10. + const int kOverlap = 20; + offset = pre_write_usage - kOverlap; + allowed_growth = 10; + PrepareForWrite("test", kBlobURL, offset, allowed_growth); + + file_writer_delegate_->Start(request_.Pass()); + base::MessageLoop::current()->Run(); + ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result_->write_status()); + file_writer_delegate_.reset(); + + EXPECT_EQ(pre_write_usage + allowed_growth, + usage()); + EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); + EXPECT_EQ(kOverlap + allowed_growth, result_->bytes_written()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/isolated_context.cc b/webkit/browser/fileapi/isolated_context.cc index 978ca0f..38aed76 100644 --- a/webkit/browser/fileapi/isolated_context.cc +++ b/webkit/browser/fileapi/isolated_context.cc @@ -12,7 +12,7 @@ #include "base/string_util.h" #include "base/stringprintf.h" #include "base/strings/string_number_conversions.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace fileapi { diff --git a/webkit/browser/fileapi/isolated_context_unittest.cc b/webkit/browser/fileapi/isolated_context_unittest.cc index 17c9ab5..36073ca 100644 --- a/webkit/browser/fileapi/isolated_context_unittest.cc +++ b/webkit/browser/fileapi/isolated_context_unittest.cc @@ -7,8 +7,8 @@ #include "base/basictypes.h" #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" -#include "webkit/fileapi/file_system_url.h" #define FPL(x) FILE_PATH_LITERAL(x) diff --git a/webkit/browser/fileapi/isolated_file_util.cc b/webkit/browser/fileapi/isolated_file_util.cc index f8812ce..4ed1f8c 100644 --- a/webkit/browser/fileapi/isolated_file_util.cc +++ b/webkit/browser/fileapi/isolated_file_util.cc @@ -9,11 +9,11 @@ #include "base/file_util.h" #include "webkit/blob/shareable_file_reference.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_url.h" using base::PlatformFileError; using base::PlatformFileInfo; diff --git a/webkit/browser/fileapi/isolated_file_util_unittest.cc b/webkit/browser/fileapi/isolated_file_util_unittest.cc index c6d8f81..dfedabd 100644 --- a/webkit/browser/fileapi/isolated_file_util_unittest.cc +++ b/webkit/browser/fileapi/isolated_file_util_unittest.cc @@ -15,6 +15,9 @@ #include "base/message_loop_proxy.h" #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/isolated_file_util.h" @@ -22,10 +25,7 @@ #include "webkit/browser/fileapi/local_file_util.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/test_file_set.h" +#include "webkit/browser/fileapi/test_file_set.h" using file_util::FileEnumerator; diff --git a/webkit/browser/fileapi/isolated_mount_point_provider.cc b/webkit/browser/fileapi/isolated_mount_point_provider.cc index 60a3aee..0cec867 100644 --- a/webkit/browser/fileapi/isolated_mount_point_provider.cc +++ b/webkit/browser/fileapi/isolated_mount_point_provider.cc @@ -14,20 +14,20 @@ #include "base/platform_file.h" #include "base/sequenced_task_runner.h" #include "webkit/blob/local_file_stream_reader.h" +#include "webkit/browser/fileapi/async_file_util_adapter.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/isolated_file_util.h" +#include "webkit/browser/fileapi/local_file_stream_writer.h" #include "webkit/browser/fileapi/local_file_system_operation.h" #include "webkit/browser/fileapi/native_file_util.h" #include "webkit/browser/fileapi/transient_file_util.h" -#include "webkit/fileapi/async_file_util_adapter.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_file_stream_reader.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_types.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/local_file_stream_writer.h" namespace fileapi { diff --git a/webkit/browser/fileapi/local_file_stream_writer.cc b/webkit/browser/fileapi/local_file_stream_writer.cc new file mode 100644 index 0000000..a34f803 --- /dev/null +++ b/webkit/browser/fileapi/local_file_stream_writer.cc @@ -0,0 +1,230 @@ +// 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/browser/fileapi/local_file_stream_writer.h" + +#include "base/callback.h" +#include "base/message_loop.h" +#include "net/base/file_stream.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" + +namespace fileapi { + +namespace { + +const int kOpenFlagsForWrite = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC; + +} // namespace + +LocalFileStreamWriter::LocalFileStreamWriter(const base::FilePath& file_path, + int64 initial_offset) + : file_path_(file_path), + initial_offset_(initial_offset), + has_pending_operation_(false), + weak_factory_(this) {} + +LocalFileStreamWriter::~LocalFileStreamWriter() { + // Invalidate weak pointers so that we won't receive any callbacks from + // in-flight stream operations, which might be triggered during the file close + // in the FileStream destructor. + weak_factory_.InvalidateWeakPtrs(); + + // FileStream's destructor closes the file safely, since we opened the file + // by its Open() method. +} + +int LocalFileStreamWriter::Write(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) { + DCHECK(!has_pending_operation_); + DCHECK(cancel_callback_.is_null()); + + has_pending_operation_ = true; + if (stream_impl_) { + int result = InitiateWrite(buf, buf_len, callback); + if (result != net::ERR_IO_PENDING) + has_pending_operation_ = false; + return result; + } + return InitiateOpen(callback, + base::Bind(&LocalFileStreamWriter::ReadyToWrite, + weak_factory_.GetWeakPtr(), + make_scoped_refptr(buf), buf_len, callback)); +} + +int LocalFileStreamWriter::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 LocalFileStreamWriter::Flush(const net::CompletionCallback& callback) { + DCHECK(!has_pending_operation_); + DCHECK(cancel_callback_.is_null()); + + // Write() is not called yet, so there's nothing to flush. + if (!stream_impl_) + return net::OK; + + has_pending_operation_ = true; + int result = InitiateFlush(callback); + if (result != net::ERR_IO_PENDING) + has_pending_operation_ = false; + return result; +} + +int LocalFileStreamWriter::InitiateOpen( + const net::CompletionCallback& error_callback, + const base::Closure& main_operation) { + DCHECK(has_pending_operation_); + DCHECK(!stream_impl_.get()); + + stream_impl_.reset(new net::FileStream(NULL)); + return stream_impl_->Open(file_path_, + kOpenFlagsForWrite, + base::Bind(&LocalFileStreamWriter::DidOpen, + weak_factory_.GetWeakPtr(), + error_callback, + main_operation)); +} + +void LocalFileStreamWriter::DidOpen( + const net::CompletionCallback& error_callback, + const base::Closure& main_operation, + int result) { + DCHECK(has_pending_operation_); + DCHECK(stream_impl_.get()); + + if (CancelIfRequested()) + return; + + if (result != net::OK) { + has_pending_operation_ = false; + stream_impl_.reset(NULL); + error_callback.Run(result); + return; + } + + InitiateSeek(error_callback, main_operation); +} + +void LocalFileStreamWriter::InitiateSeek( + const net::CompletionCallback& error_callback, + const base::Closure& main_operation) { + DCHECK(has_pending_operation_); + DCHECK(stream_impl_.get()); + + if (initial_offset_ == 0) { + // No need to seek. + main_operation.Run(); + return; + } + + int result = stream_impl_->Seek(net::FROM_BEGIN, initial_offset_, + base::Bind(&LocalFileStreamWriter::DidSeek, + weak_factory_.GetWeakPtr(), + error_callback, + main_operation)); + if (result != net::ERR_IO_PENDING) { + has_pending_operation_ = false; + error_callback.Run(result); + } +} + +void LocalFileStreamWriter::DidSeek( + const net::CompletionCallback& error_callback, + const base::Closure& main_operation, + int64 result) { + DCHECK(has_pending_operation_); + + if (CancelIfRequested()) + return; + + if (result != initial_offset_) { + // TODO(kinaba) add a more specific error code. + result = net::ERR_FAILED; + } + + if (result < 0) { + has_pending_operation_ = false; + error_callback.Run(static_cast<int>(result)); + return; + } + + main_operation.Run(); +} + +void LocalFileStreamWriter::ReadyToWrite( + net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) { + DCHECK(has_pending_operation_); + + int result = InitiateWrite(buf, buf_len, callback); + if (result != net::ERR_IO_PENDING) { + has_pending_operation_ = false; + callback.Run(result); + } +} + +int LocalFileStreamWriter::InitiateWrite( + net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) { + DCHECK(has_pending_operation_); + DCHECK(stream_impl_.get()); + + return stream_impl_->Write(buf, buf_len, + base::Bind(&LocalFileStreamWriter::DidWrite, + weak_factory_.GetWeakPtr(), + callback)); +} + +void LocalFileStreamWriter::DidWrite(const net::CompletionCallback& callback, + int result) { + DCHECK(has_pending_operation_); + + if (CancelIfRequested()) + return; + has_pending_operation_ = false; + callback.Run(result); +} + +int LocalFileStreamWriter::InitiateFlush( + const net::CompletionCallback& callback) { + DCHECK(has_pending_operation_); + DCHECK(stream_impl_.get()); + + return stream_impl_->Flush(base::Bind(&LocalFileStreamWriter::DidFlush, + weak_factory_.GetWeakPtr(), + callback)); +} + +void LocalFileStreamWriter::DidFlush(const net::CompletionCallback& callback, + int result) { + DCHECK(has_pending_operation_); + + if (CancelIfRequested()) + return; + has_pending_operation_ = false; + callback.Run(result); +} + +bool LocalFileStreamWriter::CancelIfRequested() { + DCHECK(has_pending_operation_); + + 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; +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/local_file_stream_writer.h b/webkit/browser/fileapi/local_file_stream_writer.h new file mode 100644 index 0000000..ed3ee68 --- /dev/null +++ b/webkit/browser/fileapi/local_file_stream_writer.h @@ -0,0 +1,89 @@ +// 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_BROWSER_FILEAPI_LOCAL_FILE_STREAM_WRITER_H_ +#define WEBKIT_BROWSER_FILEAPI_LOCAL_FILE_STREAM_WRITER_H_ + +#include <utility> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "webkit/browser/fileapi/file_stream_writer.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace net { +class FileStream; +} + +namespace fileapi { + +// This class is a thin wrapper around net::FileStream for writing local files. +class WEBKIT_STORAGE_EXPORT_PRIVATE LocalFileStreamWriter + : public FileStreamWriter { + public: + // Create a writer for the existing file in the path |file_path| starting from + // |initial_offset|. + LocalFileStreamWriter(const base::FilePath& file_path, int64 initial_offset); + virtual ~LocalFileStreamWriter(); + + // FileStreamWriter overrides. + virtual int Write(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) OVERRIDE; + virtual int Cancel(const net::CompletionCallback& callback) OVERRIDE; + virtual int Flush(const net::CompletionCallback& callback) OVERRIDE; + + private: + // Opens |file_path_| and if it succeeds, proceeds to InitiateSeek(). + // If failed, the error code is returned by calling |error_callback|. + int InitiateOpen(const net::CompletionCallback& error_callback, + const base::Closure& main_operation); + void DidOpen(const net::CompletionCallback& error_callback, + const base::Closure& main_operation, + int result); + + // Seeks to |initial_offset_| and proceeds to |main_operation| if it succeeds. + // If failed, the error code is returned by calling |error_callback|. + void InitiateSeek(const net::CompletionCallback& error_callback, + const base::Closure& main_operation); + void DidSeek(const net::CompletionCallback& error_callback, + const base::Closure& main_operation, + int64 result); + + // Passed as the |main_operation| of InitiateOpen() function. + void ReadyToWrite(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback); + + // Writes asynchronously to the file. + int InitiateWrite(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback); + void DidWrite(const net::CompletionCallback& callback, int result); + + // Flushes asynchronously to the file. + int InitiateFlush(const net::CompletionCallback& callback); + void DidFlush(const net::CompletionCallback& callback, int result); + + // Stops the in-flight operation and calls |cancel_callback_| if it has been + // set by Cancel() for the current operation. + bool CancelIfRequested(); + + // Initialization parameters. + const base::FilePath file_path_; + const int64 initial_offset_; + + // Current states of the operation. + bool has_pending_operation_; + scoped_ptr<net::FileStream> stream_impl_; + net::CompletionCallback cancel_callback_; + + base::WeakPtrFactory<LocalFileStreamWriter> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(LocalFileStreamWriter); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_LOCAL_FILE_STREAM_WRITER_H_ diff --git a/webkit/browser/fileapi/local_file_stream_writer_unittest.cc b/webkit/browser/fileapi/local_file_stream_writer_unittest.cc new file mode 100644 index 0000000..984a114 --- /dev/null +++ b/webkit/browser/fileapi/local_file_stream_writer_unittest.cc @@ -0,0 +1,156 @@ +// 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/browser/fileapi/local_file_stream_writer.h" + +#include <string> + +#include "base/callback.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "net/base/io_buffer.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +using fileapi::LocalFileStreamWriter; + +class LocalFileStreamWriterTest : public testing::Test { + public: + LocalFileStreamWriterTest() : message_loop_(base::MessageLoop::TYPE_IO) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + protected: + base::FilePath Path(const std::string& name) { + return temp_dir_.path().AppendASCII(name); + } + + int WriteStringToWriter(LocalFileStreamWriter* writer, + const std::string& data) { + scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(data)); + scoped_refptr<net::DrainableIOBuffer> drainable( + new net::DrainableIOBuffer(buffer, buffer->size())); + + while (drainable->BytesRemaining() > 0) { + net::TestCompletionCallback callback; + int result = writer->Write(drainable, drainable->BytesRemaining(), + callback.callback()); + if (result == net::ERR_IO_PENDING) + result = callback.WaitForResult(); + if (result <= 0) + return result; + drainable->DidConsume(result); + } + return net::OK; + } + + std::string GetFileContent(const base::FilePath& path) { + std::string content; + file_util::ReadFileToString(path, &content); + return content; + } + + base::FilePath CreateFileWithContent(const std::string& name, + const std::string& data) { + base::FilePath path = Path(name); + file_util::WriteFile(path, data.c_str(), data.size()); + return path; + } + + private: + base::MessageLoop message_loop_; + base::ScopedTempDir temp_dir_; +}; + +void NeverCalled(int unused) { + ADD_FAILURE(); +} + +} // namespace + +TEST_F(LocalFileStreamWriterTest, Write) { + base::FilePath path = CreateFileWithContent("file_a", std::string()); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 0)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo")); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "bar")); + writer.reset(); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(file_util::PathExists(path)); + EXPECT_EQ("foobar", GetFileContent(path)); +} + +TEST_F(LocalFileStreamWriterTest, WriteMiddle) { + base::FilePath path = CreateFileWithContent("file_a", "foobar"); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 2)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(file_util::PathExists(path)); + EXPECT_EQ("foxxxr", GetFileContent(path)); +} + +TEST_F(LocalFileStreamWriterTest, WriteEnd) { + base::FilePath path = CreateFileWithContent("file_a", "foobar"); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 6)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); + writer.reset(); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(file_util::PathExists(path)); + EXPECT_EQ("foobarxxx", GetFileContent(path)); +} + +TEST_F(LocalFileStreamWriterTest, WriteFailForNonexistingFile) { + base::FilePath path = Path("file_a"); + ASSERT_FALSE(file_util::PathExists(path)); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 0)); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, WriteStringToWriter(writer.get(), "foo")); + writer.reset(); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_FALSE(file_util::PathExists(path)); +} + +TEST_F(LocalFileStreamWriterTest, CancelBeforeOperation) { + base::FilePath path = Path("file_a"); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 0)); + // Cancel immediately fails when there's no in-flight operation. + int cancel_result = writer->Cancel(base::Bind(&NeverCalled)); + EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result); +} + +TEST_F(LocalFileStreamWriterTest, CancelAfterFinishedOperation) { + base::FilePath path = CreateFileWithContent("file_a", std::string()); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 0)); + EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo")); + + // Cancel immediately fails when there's no in-flight operation. + int cancel_result = writer->Cancel(base::Bind(&NeverCalled)); + EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result); + + writer.reset(); + base::MessageLoop::current()->RunUntilIdle(); + // Write operation is already completed. + EXPECT_TRUE(file_util::PathExists(path)); + EXPECT_EQ("foo", GetFileContent(path)); +} + +TEST_F(LocalFileStreamWriterTest, CancelWrite) { + base::FilePath path = CreateFileWithContent("file_a", "foobar"); + scoped_ptr<LocalFileStreamWriter> writer(new LocalFileStreamWriter(path, 0)); + + scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer("xxx")); + int result = writer->Write(buffer, buffer->size(), base::Bind(&NeverCalled)); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + net::TestCompletionCallback callback; + writer->Cancel(callback.callback()); + int cancel_result = callback.WaitForResult(); + EXPECT_EQ(net::OK, cancel_result); +} diff --git a/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc b/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc index 13a3f39..1b600d9 100644 --- a/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc +++ b/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc @@ -12,14 +12,14 @@ #include "base/run_loop.h" #include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/mock_file_system_context.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/test_file_set.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/test_file_set.h" #include "webkit/quota/mock_quota_manager.h" #include "webkit/quota/quota_manager.h" diff --git a/webkit/browser/fileapi/local_file_system_operation.cc b/webkit/browser/fileapi/local_file_system_operation.cc index 4a10db9..f278cf2 100644 --- a/webkit/browser/fileapi/local_file_system_operation.cc +++ b/webkit/browser/fileapi/local_file_system_operation.cc @@ -11,20 +11,20 @@ #include "net/base/escape.h" #include "net/url_request/url_request_context.h" #include "webkit/blob/shareable_file_reference.h" +#include "webkit/browser/fileapi/async_file_util.h" #include "webkit/browser/fileapi/cross_operation_delegate.h" #include "webkit/browser/fileapi/file_observers.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_task_runners.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_writer_delegate.h" +#include "webkit/browser/fileapi/remove_operation_delegate.h" #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" -#include "webkit/fileapi/async_file_util.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_types.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/file_writer_delegate.h" -#include "webkit/fileapi/remove_operation_delegate.h" #include "webkit/quota/quota_manager.h" #include "webkit/quota/quota_types.h" diff --git a/webkit/browser/fileapi/local_file_system_operation.h b/webkit/browser/fileapi/local_file_system_operation.h index bc951d0..bca7834 100644 --- a/webkit/browser/fileapi/local_file_system_operation.h +++ b/webkit/browser/fileapi/local_file_system_operation.h @@ -10,9 +10,9 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "webkit/blob/scoped_file.h" -#include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_url.h" -#include "webkit/fileapi/file_writer_delegate.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_writer_delegate.h" #include "webkit/quota/quota_types.h" #include "webkit/storage/webkit_storage_export.h" diff --git a/webkit/browser/fileapi/local_file_system_operation_unittest.cc b/webkit/browser/fileapi/local_file_system_operation_unittest.cc index b5a81ab..cbbd0f7 100644 --- a/webkit/browser/fileapi/local_file_system_operation_unittest.cc +++ b/webkit/browser/fileapi/local_file_system_operation_unittest.cc @@ -15,13 +15,13 @@ #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/blob/shareable_file_reference.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/mock_file_change_observer.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/sandbox_file_system_test_helper.h" #include "webkit/quota/mock_quota_manager.h" #include "webkit/quota/quota_manager.h" diff --git a/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc b/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc index 15ca130..ba72758 100644 --- a/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc +++ b/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc @@ -19,14 +19,14 @@ #include "webkit/blob/blob_storage_controller.h" #include "webkit/blob/blob_url_request_job.h" #include "webkit/blob/mock_blob_url_request_context.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/local_file_system_operation.h" #include "webkit/browser/fileapi/local_file_util.h" #include "webkit/browser/fileapi/mock_file_change_observer.h" #include "webkit/browser/fileapi/mock_file_system_context.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_util.h" #include "webkit/quota/mock_quota_manager.h" diff --git a/webkit/browser/fileapi/local_file_util.cc b/webkit/browser/fileapi/local_file_util.cc index 7302170..8dcd277 100644 --- a/webkit/browser/fileapi/local_file_util.cc +++ b/webkit/browser/fileapi/local_file_util.cc @@ -7,12 +7,12 @@ #include "base/file_util.h" #include "base/files/file_util_proxy.h" #include "googleurl/src/gurl.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_types.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" namespace fileapi { diff --git a/webkit/browser/fileapi/local_file_util_unittest.cc b/webkit/browser/fileapi/local_file_util_unittest.cc index efc8da96..4ca052f 100644 --- a/webkit/browser/fileapi/local_file_util_unittest.cc +++ b/webkit/browser/fileapi/local_file_util_unittest.cc @@ -13,13 +13,13 @@ #include "base/strings/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/local_file_util.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_types.h" namespace fileapi { diff --git a/webkit/browser/fileapi/mock_file_change_observer.h b/webkit/browser/fileapi/mock_file_change_observer.h index 36ff3ae..3835323d 100644 --- a/webkit/browser/fileapi/mock_file_change_observer.h +++ b/webkit/browser/fileapi/mock_file_change_observer.h @@ -8,8 +8,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "webkit/browser/fileapi/file_observers.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/task_runner_bound_observer_list.h" -#include "webkit/fileapi/file_system_url.h" namespace fileapi { diff --git a/webkit/browser/fileapi/mock_file_system_context.cc b/webkit/browser/fileapi/mock_file_system_context.cc index 2fda4e9..f6c930a 100644 --- a/webkit/browser/fileapi/mock_file_system_context.cc +++ b/webkit/browser/fileapi/mock_file_system_context.cc @@ -6,11 +6,11 @@ #include "base/memory/scoped_vector.h" #include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/mock_file_system_options.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/test_mount_point_provider.h" +#include "webkit/browser/fileapi/test_mount_point_provider.h" #include "webkit/quota/mock_special_storage_policy.h" namespace fileapi { diff --git a/webkit/browser/fileapi/mock_file_system_options.h b/webkit/browser/fileapi/mock_file_system_options.h index 32ebd74..f46f635 100644 --- a/webkit/browser/fileapi/mock_file_system_options.h +++ b/webkit/browser/fileapi/mock_file_system_options.h @@ -5,7 +5,7 @@ #ifndef WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_OPTIONS_H_ #define WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_OPTIONS_H_ -#include "webkit/fileapi/file_system_options.h" +#include "webkit/browser/fileapi/file_system_options.h" namespace fileapi { diff --git a/webkit/browser/fileapi/native_file_util.cc b/webkit/browser/fileapi/native_file_util.cc index e3a9519..3900244 100644 --- a/webkit/browser/fileapi/native_file_util.cc +++ b/webkit/browser/fileapi/native_file_util.cc @@ -6,7 +6,7 @@ #include "base/file_util.h" #include "base/memory/scoped_ptr.h" -#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" namespace fileapi { diff --git a/webkit/browser/fileapi/obfuscated_file_util.cc b/webkit/browser/fileapi/obfuscated_file_util.cc index 5d9d377..668ec37 100644 --- a/webkit/browser/fileapi/obfuscated_file_util.cc +++ b/webkit/browser/fileapi/obfuscated_file_util.cc @@ -20,11 +20,11 @@ #include "googleurl/src/gurl.h" #include "webkit/base/origin_url_conversions.h" #include "webkit/browser/fileapi/file_observers.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/native_file_util.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/fileapi/syncable/syncable_file_system_util.h" #include "webkit/quota/quota_manager.h" diff --git a/webkit/browser/fileapi/obfuscated_file_util.h b/webkit/browser/fileapi/obfuscated_file_util.h index e4915a3..24cd246 100644 --- a/webkit/browser/fileapi/obfuscated_file_util.h +++ b/webkit/browser/fileapi/obfuscated_file_util.h @@ -15,10 +15,10 @@ #include "base/timer.h" #include "webkit/blob/shareable_file_reference.h" #include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/sandbox_directory_database.h" #include "webkit/browser/fileapi/sandbox_origin_database.h" #include "webkit/fileapi/file_system_types.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/storage/webkit_storage_export.h" namespace base { diff --git a/webkit/browser/fileapi/obfuscated_file_util_unittest.cc b/webkit/browser/fileapi/obfuscated_file_util_unittest.cc index 1b42186..a0e788d 100644 --- a/webkit/browser/fileapi/obfuscated_file_util_unittest.cc +++ b/webkit/browser/fileapi/obfuscated_file_util_unittest.cc @@ -14,18 +14,18 @@ #include "base/message_loop.h" #include "base/platform_file.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_test_helper.h" #include "webkit/browser/fileapi/external_mount_points.h" +#include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/mock_file_change_observer.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/obfuscated_file_util.h" -#include "webkit/fileapi/async_file_test_helper.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/sandbox_file_system_test_helper.h" -#include "webkit/fileapi/test_file_set.h" +#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" +#include "webkit/browser/fileapi/test_file_set.h" #include "webkit/quota/mock_special_storage_policy.h" #include "webkit/quota/quota_manager.h" #include "webkit/quota/quota_types.h" diff --git a/webkit/browser/fileapi/recursive_operation_delegate.cc b/webkit/browser/fileapi/recursive_operation_delegate.cc index a358c21..80d7c14 100644 --- a/webkit/browser/fileapi/recursive_operation_delegate.cc +++ b/webkit/browser/fileapi/recursive_operation_delegate.cc @@ -5,9 +5,9 @@ #include "webkit/browser/fileapi/recursive_operation_delegate.h" #include "base/bind.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/local_file_system_operation.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" namespace fileapi { diff --git a/webkit/browser/fileapi/recursive_operation_delegate.h b/webkit/browser/fileapi/recursive_operation_delegate.h index 23e0735..ba8c81e 100644 --- a/webkit/browser/fileapi/recursive_operation_delegate.h +++ b/webkit/browser/fileapi/recursive_operation_delegate.h @@ -10,8 +10,8 @@ #include "base/basictypes.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace fileapi { diff --git a/webkit/browser/fileapi/remote_file_system_proxy.h b/webkit/browser/fileapi/remote_file_system_proxy.h new file mode 100644 index 0000000..0b59a73 --- /dev/null +++ b/webkit/browser/fileapi/remote_file_system_proxy.h @@ -0,0 +1,150 @@ +// 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_BROWSER_FILEAPI_REMOTE_FILE_SYSTEM_PROXY_H_ +#define WEBKIT_BROWSER_FILEAPI_REMOTE_FILE_SYSTEM_PROXY_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "webkit/browser/fileapi/file_system_operation.h" + +namespace base { +class SequencedTaskRunner; +} // namespace base + +namespace webkit_blob { +class FileStreamReader; +} // namespace webkit_blob + +namespace fileapi { + +typedef base::Callback< + void(base::PlatformFileError result, + const base::FilePath& platform_path, + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref)> + WritableSnapshotFile; + +// The interface class for remote file system proxy. +class RemoteFileSystemProxyInterface : + public base::RefCountedThreadSafe<RemoteFileSystemProxyInterface> { + public: + // Used for OpenFile(). |result| is the return code of the operation. + typedef base::Callback< + void(base::PlatformFileError result, + base::PlatformFile file, + base::ProcessHandle peer_handle)> OpenFileCallback; + + + // Gets the file or directory info for given|path|. + virtual void GetFileInfo(const FileSystemURL& url, + const FileSystemOperation::GetMetadataCallback& callback) = 0; + + // Copies a file or directory from |src_url| to |dest_url|. If + // |src_url| is a directory, the contents of |src_url| are copied to + // |dest_url| recursively. A new file or directory is created at + // |dest_url| as needed. + virtual void Copy( + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Moves a file or directory from |src_url| to |dest_url|. A new file + // or directory is created at |dest_url| as needed. + virtual void Move( + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Reads contents of a directory at |url|. + virtual void ReadDirectory(const FileSystemURL& url, + const FileSystemOperation::ReadDirectoryCallback& callback) = 0; + + // Removes a file or directory at |url|. If |recursive| is true, remove + // all files and directories under the directory at |url| recursively. + virtual void Remove(const FileSystemURL& url, bool recursive, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Creates a directory at |url|. If |exclusive| is true, an error is + // raised in case a directory is already present at the URL. If + // |recursive| is true, create parent directories as needed just like + // mkdir -p does. + virtual void CreateDirectory( + const FileSystemURL& url, + bool exclusive, + bool recursive, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Creates a file at |url|. If the flag |is_exclusive| is true, an + // error is raised when a file already exists at the path. It is + // an error if a directory or a hosted document is already present at the + // path, or the parent directory of the path is not present yet. + virtual void CreateFile( + const FileSystemURL& url, + bool exclusive, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Changes the length of an existing file at |url| to |length|. If |length| + // is negative, an error is raised. If |length| is more than the current size + // of the file, zero is padded for the extended part. + virtual void Truncate( + const FileSystemURL& url, + int64 length, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Creates a local snapshot file for a given |url| and returns the + // metadata and platform path of the snapshot file via |callback|. + // See also FileSystemOperation::CreateSnapshotFile(). + virtual void CreateSnapshotFile( + const FileSystemURL& url, + const FileSystemOperation::SnapshotFileCallback& callback) = 0; + + // Creates a local snapshot file for a given |url| and marks it for + // modification. A webkit_blob::ShareableFileReference is passed to + // |callback|, and when the reference is released, modification to the + // snapshot is marked for uploading to the remote file system. + virtual void CreateWritableSnapshotFile( + const FileSystemURL& url, + const WritableSnapshotFile& callback) = 0; + + // Opens file for a given |url| with specified |flags| (see + // base::PlatformFileFlags for details). + virtual void OpenFile( + const FileSystemURL& url, + int flags, + base::ProcessHandle peer_handle, + const OpenFileCallback& callback) = 0; + + // Notifies that a file opened by OpenFile (at |path|) is closed. + virtual void NotifyCloseFile(const FileSystemURL& url) = 0; + + // Modifies the timestamp of a given |url| to |last_access_time| and + // |last_modified_time|. Note that unlike 'touch' command of Linux, it + // does not create a new file. + virtual void TouchFile( + const FileSystemURL& url, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const FileSystemOperation::StatusCallback& callback) = 0; + + // Creates a new file stream reader for the file at |url| with an |offset|. + // |expected_modification_time| specifies the expected last modification + // if it isn't null, and the reader will return ERR_UPLOAD_FILE_CHANGED error + // if the file has been modified. + // The error will be notified via error code of FileStreamReader's methods, + // and this method itself doesn't check if the file exists and is a regular + // file. + virtual scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader( + base::SequencedTaskRunner* file_task_runner, + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time) = 0; + + protected: + friend class base::RefCountedThreadSafe<RemoteFileSystemProxyInterface>; + virtual ~RemoteFileSystemProxyInterface() {} +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_REMOTE_FILE_SYSTEM_PROXY_H_ diff --git a/webkit/browser/fileapi/remove_operation_delegate.cc b/webkit/browser/fileapi/remove_operation_delegate.cc new file mode 100644 index 0000000..f7238c8 --- /dev/null +++ b/webkit/browser/fileapi/remove_operation_delegate.cc @@ -0,0 +1,87 @@ +// Copyright (c) 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/remove_operation_delegate.h" + +#include "base/bind.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/local_file_system_operation.h" + +namespace fileapi { + +RemoveOperationDelegate::RemoveOperationDelegate( + FileSystemContext* file_system_context, + LocalFileSystemOperation* operation, + const FileSystemURL& url, + const StatusCallback& callback) + : RecursiveOperationDelegate(file_system_context, operation), + url_(url), + callback_(callback) { +} + +RemoveOperationDelegate::~RemoveOperationDelegate() {} + +void RemoveOperationDelegate::Run() { + NewNestedOperation()->RemoveFile(url_, base::Bind( + &RemoveOperationDelegate::DidTryRemoveFile, AsWeakPtr())); +} + +void RemoveOperationDelegate::RunRecursively() { + StartRecursiveOperation( + url_, + base::Bind(&RemoveOperationDelegate::RemoveNextDirectory, AsWeakPtr())); +} + +void RemoveOperationDelegate::ProcessFile(const FileSystemURL& url, + const StatusCallback& callback) { + if (to_remove_directories_.size() == 1u && + to_remove_directories_.top() == url) { + // We seem to have been re-directed from ProcessDirectory. + to_remove_directories_.pop(); + } + NewNestedOperation()->RemoveFile(url, base::Bind( + &RemoveOperationDelegate::DidRemoveFile, AsWeakPtr(), callback)); +} + +void RemoveOperationDelegate::ProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) { + to_remove_directories_.push(url); + callback.Run(base::PLATFORM_FILE_OK); +} + +void RemoveOperationDelegate::DidTryRemoveFile( + base::PlatformFileError error) { + if (error == base::PLATFORM_FILE_OK || + error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) { + callback_.Run(error); + return; + } + NewNestedOperation()->RemoveDirectory(url_, callback_); +} + +void RemoveOperationDelegate::DidRemoveFile(const StatusCallback& callback, + base::PlatformFileError error) { + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { + callback.Run(base::PLATFORM_FILE_OK); + return; + } + callback.Run(error); +} + +void RemoveOperationDelegate::RemoveNextDirectory( + base::PlatformFileError error) { + if (error != base::PLATFORM_FILE_OK || + to_remove_directories_.empty()) { + callback_.Run(error); + return; + } + FileSystemURL url = to_remove_directories_.top(); + to_remove_directories_.pop(); + NewNestedOperation()->RemoveDirectory(url, base::Bind( + &RemoveOperationDelegate::RemoveNextDirectory, + AsWeakPtr())); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/remove_operation_delegate.h b/webkit/browser/fileapi/remove_operation_delegate.h new file mode 100644 index 0000000..2fa4b68 --- /dev/null +++ b/webkit/browser/fileapi/remove_operation_delegate.h @@ -0,0 +1,50 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_REMOVE_OPERATION_DELEGATE_H_ +#define WEBKIT_BROWSER_FILEAPI_REMOVE_OPERATION_DELEGATE_H_ + +#include <stack> + +#include "webkit/browser/fileapi/recursive_operation_delegate.h" + +namespace fileapi { + +class RemoveOperationDelegate + : public RecursiveOperationDelegate, + public base::SupportsWeakPtr<RemoveOperationDelegate> { + public: + RemoveOperationDelegate(FileSystemContext* file_system_context, + LocalFileSystemOperation* operation, + const FileSystemURL& url, + const StatusCallback& callback); + virtual ~RemoveOperationDelegate(); + + // RecursiveOperationDelegate overrides: + virtual void Run() OVERRIDE; + virtual void RunRecursively() OVERRIDE; + virtual void ProcessFile(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; + virtual void ProcessDirectory(const FileSystemURL& url, + const StatusCallback& callback) OVERRIDE; + + using base::SupportsWeakPtr<RemoveOperationDelegate>::AsWeakPtr; + + private: + void DidTryRemoveFile(base::PlatformFileError error); + void DidRemoveFile(const StatusCallback& callback, + base::PlatformFileError error); + void RemoveNextDirectory(base::PlatformFileError error); + + FileSystemURL url_; + StatusCallback callback_; + + std::stack<FileSystemURL> to_remove_directories_; + + DISALLOW_COPY_AND_ASSIGN(RemoveOperationDelegate); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_REMOVE_OPERATION_DELEGATE_H_ diff --git a/webkit/browser/fileapi/sandbox_file_stream_writer.cc b/webkit/browser/fileapi/sandbox_file_stream_writer.cc index 490d62b..a937476 100644 --- a/webkit/browser/fileapi/sandbox_file_stream_writer.cc +++ b/webkit/browser/fileapi/sandbox_file_stream_writer.cc @@ -11,10 +11,10 @@ #include "net/base/net_errors.h" #include "webkit/blob/local_file_stream_reader.h" #include "webkit/browser/fileapi/file_observers.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/local_file_stream_writer.h" #include "webkit/fileapi/file_system_util.h" -#include "webkit/fileapi/local_file_stream_writer.h" #include "webkit/quota/quota_manager.h" namespace fileapi { diff --git a/webkit/browser/fileapi/sandbox_file_stream_writer.h b/webkit/browser/fileapi/sandbox_file_stream_writer.h index 19fc7bf..3de8d0d 100644 --- a/webkit/browser/fileapi/sandbox_file_stream_writer.h +++ b/webkit/browser/fileapi/sandbox_file_stream_writer.h @@ -9,10 +9,10 @@ #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" #include "googleurl/src/gurl.h" +#include "webkit/browser/fileapi/file_stream_writer.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/task_runner_bound_observer_list.h" -#include "webkit/fileapi/file_stream_writer.h" #include "webkit/fileapi/file_system_types.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/quota/quota_types.h" #include "webkit/storage/webkit_storage_export.h" diff --git a/webkit/browser/fileapi/sandbox_file_system_test_helper.cc b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc new file mode 100644 index 0000000..01a0a27 --- /dev/null +++ b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc @@ -0,0 +1,153 @@ +// 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/browser/fileapi/sandbox_file_system_test_helper.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "googleurl/src/gurl.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_task_runners.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_usage_cache.h" +#include "webkit/browser/fileapi/local_file_system_operation.h" +#include "webkit/browser/fileapi/mock_file_system_context.h" +#include "webkit/browser/fileapi/sandbox_mount_point_provider.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/quota/mock_special_storage_policy.h" + +namespace fileapi { + +SandboxFileSystemTestHelper::SandboxFileSystemTestHelper( + const GURL& origin, FileSystemType type) + : origin_(origin), type_(type), file_util_(NULL) { +} + +SandboxFileSystemTestHelper::SandboxFileSystemTestHelper() + : origin_(GURL("http://foo.com")), + type_(kFileSystemTypeTemporary), + file_util_(NULL) { +} + +SandboxFileSystemTestHelper::~SandboxFileSystemTestHelper() { +} + +void SandboxFileSystemTestHelper::SetUp(const base::FilePath& base_dir) { + SetUp(base_dir, NULL); +} + +void SandboxFileSystemTestHelper::SetUp( + FileSystemContext* file_system_context) { + file_system_context_ = file_system_context; + + SetUpFileSystem(); +} + +void SandboxFileSystemTestHelper::SetUp( + const base::FilePath& base_dir, + quota::QuotaManagerProxy* quota_manager_proxy) { + file_system_context_ = CreateFileSystemContextForTesting( + quota_manager_proxy, base_dir); + + SetUpFileSystem(); +} + +void SandboxFileSystemTestHelper::TearDown() { + file_system_context_ = NULL; + base::MessageLoop::current()->RunUntilIdle(); +} + +base::FilePath SandboxFileSystemTestHelper::GetOriginRootPath() const { + return file_system_context_->sandbox_provider()-> + GetBaseDirectoryForOriginAndType(origin_, type_, false); +} + +base::FilePath SandboxFileSystemTestHelper::GetLocalPath( + const base::FilePath& path) { + DCHECK(file_util_); + base::FilePath local_path; + scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); + file_util_->GetLocalFilePath(context.get(), CreateURL(path), &local_path); + return local_path; +} + +base::FilePath SandboxFileSystemTestHelper::GetLocalPathFromASCII( + const std::string& path) { + return GetLocalPath(base::FilePath().AppendASCII(path)); +} + +base::FilePath SandboxFileSystemTestHelper::GetUsageCachePath() const { + return file_system_context_-> + sandbox_provider()->GetUsageCachePathForOriginAndType(origin_, type_); +} + +FileSystemURL SandboxFileSystemTestHelper::CreateURL( + const base::FilePath& path) const { + return file_system_context_->CreateCrackedFileSystemURL(origin_, type_, path); +} + +int64 SandboxFileSystemTestHelper::GetCachedOriginUsage() const { + return file_system_context_->GetQuotaUtil(type_)->GetOriginUsageOnFileThread( + file_system_context_, origin_, type_); +} + +int64 SandboxFileSystemTestHelper::ComputeCurrentOriginUsage() { + usage_cache()->CloseCacheFiles(); + int64 size = file_util::ComputeDirectorySize(GetOriginRootPath()); + if (file_util::PathExists(GetUsageCachePath())) + size -= FileSystemUsageCache::kUsageFileSize; + return size; +} + +int64 +SandboxFileSystemTestHelper::ComputeCurrentDirectoryDatabaseUsage() const { + return file_util::ComputeDirectorySize( + GetOriginRootPath().AppendASCII("Paths")); +} + +LocalFileSystemOperation* SandboxFileSystemTestHelper::NewOperation() { + DCHECK(file_system_context_.get()); + DCHECK(file_util_); + LocalFileSystemOperation* operation = static_cast<LocalFileSystemOperation*>( + file_system_context_->CreateFileSystemOperation( + CreateURL(base::FilePath()), NULL)); + return operation; +} + +FileSystemOperationContext* +SandboxFileSystemTestHelper::NewOperationContext() { + DCHECK(file_system_context_.get()); + FileSystemOperationContext* context = + new FileSystemOperationContext(file_system_context_.get()); + context->set_update_observers( + *file_system_context_->GetUpdateObservers(type_)); + return context; +} + +FileSystemUsageCache* SandboxFileSystemTestHelper::usage_cache() { + return file_system_context()->sandbox_provider()->usage_cache(); +} + +void SandboxFileSystemTestHelper::SetUpFileSystem() { + DCHECK(file_system_context_); + DCHECK(file_system_context_->sandbox_provider()->CanHandleType(type_)); + + file_util_ = file_system_context_->GetFileUtil(type_); + DCHECK(file_util_); + + // Prepare the origin's root directory. + file_system_context_->sandbox_provider()-> + GetFileSystemRootPathOnFileThread(CreateURL(base::FilePath()), + true /* create */); + + // Initialize the usage cache file. + base::FilePath usage_cache_path = GetUsageCachePath(); + if (!usage_cache_path.empty()) + usage_cache()->UpdateUsage(usage_cache_path, 0); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/sandbox_file_system_test_helper.h b/webkit/browser/fileapi/sandbox_file_system_test_helper.h new file mode 100644 index 0000000..13abe17 --- /dev/null +++ b/webkit/browser/fileapi/sandbox_file_system_test_helper.h @@ -0,0 +1,100 @@ +// 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_BROWSER_FILEAPI_SANDBOX_FILE_SYSTEM_TEST_HELPER_H_ +#define WEBKIT_BROWSER_FILEAPI_SANDBOX_FILE_SYSTEM_TEST_HELPER_H_ + +#include <string> + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "googleurl/src/gurl.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_usage_cache.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/quota/quota_types.h" + +namespace base { +class FilePath; +} + +namespace quota { +class QuotaManagerProxy; +} + +namespace fileapi { + +class FileSystemContext; +class FileSystemFileUtil; +class FileSystemOperationContext; +class LocalFileSystemOperation; + +// Filesystem test helper class that encapsulates test environment for +// a given {origin, type} pair. This helper only works for sandboxed +// file systems (Temporary or Persistent). +class SandboxFileSystemTestHelper { + public: + SandboxFileSystemTestHelper(const GURL& origin, FileSystemType type); + SandboxFileSystemTestHelper(); + ~SandboxFileSystemTestHelper(); + + void SetUp(const base::FilePath& base_dir); + // If you want to use more than one SandboxFileSystemTestHelper in + // a single base directory, they have to share a context, so that they don't + // have multiple databases fighting over the lock to the origin directory + // [deep down inside ObfuscatedFileUtil]. + void SetUp(FileSystemContext* file_system_context); + void SetUp(const base::FilePath& base_dir, + quota::QuotaManagerProxy* quota_manager_proxy); + void TearDown(); + + base::FilePath GetOriginRootPath() const; + base::FilePath GetLocalPath(const base::FilePath& path); + base::FilePath GetLocalPathFromASCII(const std::string& path); + + // Returns empty path if filesystem type is neither temporary nor persistent. + base::FilePath GetUsageCachePath() const; + + FileSystemURL CreateURL(const base::FilePath& path) const; + FileSystemURL CreateURLFromUTF8(const std::string& utf8) const { + return CreateURL(base::FilePath::FromUTF8Unsafe(utf8)); + } + + // This returns cached usage size returned by QuotaUtil. + int64 GetCachedOriginUsage() const; + + // This doesn't work with OFSFU. + int64 ComputeCurrentOriginUsage(); + + int64 ComputeCurrentDirectoryDatabaseUsage() const; + + LocalFileSystemOperation* NewOperation(); + FileSystemOperationContext* NewOperationContext(); + + FileSystemContext* file_system_context() const { + return file_system_context_.get(); + } + + const GURL& origin() const { return origin_; } + FileSystemType type() const { return type_; } + quota::StorageType storage_type() const { + return FileSystemTypeToQuotaStorageType(type_); + } + FileSystemFileUtil* file_util() const { return file_util_; } + FileSystemUsageCache* usage_cache(); + + private: + void SetUpFileSystem(); + + scoped_refptr<FileSystemContext> file_system_context_; + + const GURL origin_; + const FileSystemType type_; + FileSystemFileUtil* file_util_; +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_SANDBOX_FILE_SYSTEM_TEST_HELPER_H_ diff --git a/webkit/browser/fileapi/sandbox_mount_point_provider.cc b/webkit/browser/fileapi/sandbox_mount_point_provider.cc index d701d9f..23c7cee 100644 --- a/webkit/browser/fileapi/sandbox_mount_point_provider.cc +++ b/webkit/browser/fileapi/sandbox_mount_point_provider.cc @@ -14,18 +14,18 @@ #include "base/task_runner_util.h" #include "googleurl/src/gurl.h" #include "net/base/net_util.h" +#include "webkit/browser/fileapi/async_file_util_adapter.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_options.h" #include "webkit/browser/fileapi/file_system_task_runners.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/local_file_system_operation.h" #include "webkit/browser/fileapi/obfuscated_file_util.h" #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" #include "webkit/browser/fileapi/sandbox_quota_observer.h" -#include "webkit/fileapi/async_file_util_adapter.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_file_stream_reader.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_options.h" #include "webkit/fileapi/file_system_types.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/fileapi/syncable/syncable_file_system_operation.h" diff --git a/webkit/browser/fileapi/sandbox_mount_point_provider.h b/webkit/browser/fileapi/sandbox_mount_point_provider.h index 0b779c1..763a5af 100644 --- a/webkit/browser/fileapi/sandbox_mount_point_provider.h +++ b/webkit/browser/fileapi/sandbox_mount_point_provider.h @@ -16,9 +16,9 @@ #include "base/memory/weak_ptr.h" #include "googleurl/src/gurl.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_options.h" #include "webkit/browser/fileapi/file_system_quota_util.h" #include "webkit/browser/fileapi/task_runner_bound_observer_list.h" -#include "webkit/fileapi/file_system_options.h" #include "webkit/quota/special_storage_policy.h" #include "webkit/storage/webkit_storage_export.h" diff --git a/webkit/browser/fileapi/sandbox_mount_point_provider_unittest.cc b/webkit/browser/fileapi/sandbox_mount_point_provider_unittest.cc index 3f40f34..9a6e152 100644 --- a/webkit/browser/fileapi/sandbox_mount_point_provider_unittest.cc +++ b/webkit/browser/fileapi/sandbox_mount_point_provider_unittest.cc @@ -15,8 +15,8 @@ #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/mock_file_system_options.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" // PS stands for path separator. diff --git a/webkit/browser/fileapi/sandbox_quota_observer.cc b/webkit/browser/fileapi/sandbox_quota_observer.cc index e6e9422..3dca091 100644 --- a/webkit/browser/fileapi/sandbox_quota_observer.cc +++ b/webkit/browser/fileapi/sandbox_quota_observer.cc @@ -5,9 +5,9 @@ #include "webkit/browser/fileapi/sandbox_quota_observer.h" #include "base/sequenced_task_runner.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/sandbox_mount_point_provider.h" -#include "webkit/fileapi/file_system_url.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/quota/quota_client.h" #include "webkit/quota/quota_manager.h" diff --git a/webkit/browser/fileapi/sandbox_quota_observer.h b/webkit/browser/fileapi/sandbox_quota_observer.h index 788899d..908e2b2 100644 --- a/webkit/browser/fileapi/sandbox_quota_observer.h +++ b/webkit/browser/fileapi/sandbox_quota_observer.h @@ -14,7 +14,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "webkit/browser/fileapi/file_observers.h" -#include "webkit/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace base { class SequencedTaskRunner; diff --git a/webkit/browser/fileapi/test_file_set.cc b/webkit/browser/fileapi/test_file_set.cc new file mode 100644 index 0000000..7b0b0f0 --- /dev/null +++ b/webkit/browser/fileapi/test_file_set.cc @@ -0,0 +1,76 @@ +// 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/browser/fileapi/test_file_set.h" + +#include <string> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/platform_file.h" +#include "base/rand_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace fileapi { + +namespace test { + +const TestCaseRecord kRegularTestCases[] = { + {true, FILE_PATH_LITERAL("dir a"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir A"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir d"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir d/dir e"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir d/dir e/dir f"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir d/dir e/dir g"), 0}, + {true, FILE_PATH_LITERAL("dir a/dir d/dir e/dir h"), 0}, + {true, FILE_PATH_LITERAL("dir b"), 0}, + {true, FILE_PATH_LITERAL("dir b/dir a"), 0}, + {true, FILE_PATH_LITERAL("dir c"), 0}, + {false, FILE_PATH_LITERAL("file 0"), 38}, + {false, FILE_PATH_LITERAL("file 2"), 60}, + {false, FILE_PATH_LITERAL("file 3"), 0}, + {false, FILE_PATH_LITERAL("dir a/file 0"), 39}, + {false, FILE_PATH_LITERAL("dir a/dir d/dir e/dir g/file 0"), 40}, + {false, FILE_PATH_LITERAL("dir a/dir d/dir e/dir g/file 1"), 41}, + {false, FILE_PATH_LITERAL("dir a/dir d/dir e/dir g/file 2"), 42}, + {false, FILE_PATH_LITERAL("dir a/dir d/dir e/dir g/file 3"), 50}, +}; + +const size_t kRegularTestCaseSize = arraysize(kRegularTestCases); + +void SetUpOneTestCase(const base::FilePath& root_path, + const TestCaseRecord& test_case) { + base::FilePath path = root_path.Append(test_case.path); + if (test_case.is_directory) { + ASSERT_TRUE(file_util::CreateDirectory(path)); + return; + } + base::PlatformFileError error_code; + bool created = false; + int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE; + base::PlatformFile file_handle = + base::CreatePlatformFile(path, file_flags, &created, &error_code); + EXPECT_TRUE(created); + ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); + ASSERT_NE(base::kInvalidPlatformFileValue, file_handle); + EXPECT_TRUE(base::ClosePlatformFile(file_handle)); + if (test_case.data_file_size > 0U) { + std::string content = base::RandBytesAsString(test_case.data_file_size); + ASSERT_EQ(static_cast<int>(content.size()), + file_util::WriteFile(path, content.data(), content.size())); + } +} + + +void SetUpRegularTestCases(const base::FilePath& root_path) { + for (size_t i = 0; i < arraysize(kRegularTestCases); ++i) { + SCOPED_TRACE(testing::Message() << "Creating kRegularTestCases " << i); + SetUpOneTestCase(root_path, kRegularTestCases[i]); + } +} + +} // namespace test + +} // namespace fileapi diff --git a/webkit/browser/fileapi/test_file_set.h b/webkit/browser/fileapi/test_file_set.h new file mode 100644 index 0000000..59e90ab --- /dev/null +++ b/webkit/browser/fileapi/test_file_set.h @@ -0,0 +1,41 @@ +// 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_BROWSER_FILEAPI_TEST_FILE_SET_H_ +#define WEBKIT_BROWSER_FILEAPI_TEST_FILE_SET_H_ + +#include <set> + +#include "base/files/file_path.h" + +// Common test data structures and test cases. + +namespace fileapi { + +class FileSystemFileUtil; + +namespace test { + +struct TestCaseRecord { + bool is_directory; + const base::FilePath::CharType path[64]; + int64 data_file_size; +}; + +extern const TestCaseRecord kRegularTestCases[]; +extern const size_t kRegularTestCaseSize; + +size_t GetRegularTestCaseSize(); + +// Creates one file or directory specified by |record|. +void SetUpOneTestCase(const base::FilePath& root_path, const TestCaseRecord& record); + +// Creates the files and directories specified in kRegularTestCases. +void SetUpRegularTestCases(const base::FilePath& root_path); + +} // namespace test + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_TEST_FILE_SET_H_ diff --git a/webkit/browser/fileapi/test_mount_point_provider.cc b/webkit/browser/fileapi/test_mount_point_provider.cc new file mode 100644 index 0000000..e0caaa4 --- /dev/null +++ b/webkit/browser/fileapi/test_mount_point_provider.cc @@ -0,0 +1,203 @@ +// 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/browser/fileapi/test_mount_point_provider.h" + +#include <set> +#include <string> +#include <vector> + +#include "base/file_util.h" +#include "base/sequenced_task_runner.h" +#include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/file_observers.h" +#include "webkit/browser/fileapi/file_system_file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_quota_util.h" +#include "webkit/browser/fileapi/local_file_system_operation.h" +#include "webkit/browser/fileapi/local_file_util.h" +#include "webkit/browser/fileapi/native_file_util.h" +#include "webkit/browser/fileapi/sandbox_file_stream_writer.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/quota/quota_manager.h" + +namespace fileapi { + +// This only supports single origin. +class TestMountPointProvider::QuotaUtil + : public FileSystemQuotaUtil, + public FileUpdateObserver { + public: + QuotaUtil() : usage_(0) {} + virtual ~QuotaUtil() {} + + // FileSystemQuotaUtil overrides. + virtual void GetOriginsForTypeOnFileThread( + FileSystemType type, + std::set<GURL>* origins) OVERRIDE { + NOTREACHED(); + } + virtual void GetOriginsForHostOnFileThread( + FileSystemType type, + const std::string& host, + std::set<GURL>* origins) OVERRIDE { + NOTREACHED(); + } + virtual int64 GetOriginUsageOnFileThread( + FileSystemContext* context, + const GURL& origin_url, + FileSystemType type) OVERRIDE { + return usage_; + } + virtual void InvalidateUsageCache(const GURL& origin_url, + FileSystemType type) OVERRIDE { + // Do nothing. + } + virtual void StickyInvalidateUsageCache( + const GURL& origin, + FileSystemType type) OVERRIDE { + // Do nothing. + } + + // FileUpdateObserver overrides. + virtual void OnStartUpdate(const FileSystemURL& url) OVERRIDE {} + virtual void OnUpdate(const FileSystemURL& url, int64 delta) OVERRIDE { + usage_ += delta; + } + virtual void OnEndUpdate(const FileSystemURL& url) OVERRIDE {} + + private: + int64 usage_; +}; + +TestMountPointProvider::TestMountPointProvider( + base::SequencedTaskRunner* task_runner, + const base::FilePath& base_path) + : base_path_(base_path), + task_runner_(task_runner), + local_file_util_(new AsyncFileUtilAdapter(new LocalFileUtil())), + quota_util_(new QuotaUtil), + require_copy_or_move_validator_(false) { + UpdateObserverList::Source source; + source.AddObserver(quota_util_.get(), task_runner_); + observers_ = UpdateObserverList(source); +} + +TestMountPointProvider::~TestMountPointProvider() { +} + +bool TestMountPointProvider::CanHandleType(FileSystemType type) const { + return (type == kFileSystemTypeTest); +} + +void TestMountPointProvider::ValidateFileSystemRoot( + const GURL& origin_url, + FileSystemType type, + bool create, + const ValidateFileSystemCallback& callback) { + // This won't be called unless we add test code that opens a test + // filesystem by OpenFileSystem. + NOTREACHED(); +} + +base::FilePath TestMountPointProvider::GetFileSystemRootPathOnFileThread( + const FileSystemURL& url, + bool create) { + DCHECK_EQ(kFileSystemTypeTest, url.type()); + bool success = true; + if (create) + success = file_util::CreateDirectory(base_path_); + else + success = file_util::DirectoryExists(base_path_); + return success ? base_path_ : base::FilePath(); +} + +FileSystemFileUtil* TestMountPointProvider::GetFileUtil(FileSystemType type) { + DCHECK(local_file_util_.get()); + return local_file_util_->sync_file_util(); +} + +AsyncFileUtil* TestMountPointProvider::GetAsyncFileUtil(FileSystemType type) { + return local_file_util_.get(); +} + +CopyOrMoveFileValidatorFactory* +TestMountPointProvider::GetCopyOrMoveFileValidatorFactory( + FileSystemType type, base::PlatformFileError* error_code) { + DCHECK(error_code); + *error_code = base::PLATFORM_FILE_OK; + if (require_copy_or_move_validator_) { + if (!copy_or_move_file_validator_factory_) + *error_code = base::PLATFORM_FILE_ERROR_SECURITY; + return copy_or_move_file_validator_factory_.get(); + } + return NULL; +} + +void TestMountPointProvider::InitializeCopyOrMoveFileValidatorFactory( + FileSystemType type, scoped_ptr<CopyOrMoveFileValidatorFactory> factory) { + if (!require_copy_or_move_validator_) { + DCHECK(!factory); + return; + } + if (!copy_or_move_file_validator_factory_) + copy_or_move_file_validator_factory_ = factory.Pass(); +} + +FilePermissionPolicy TestMountPointProvider::GetPermissionPolicy( + const FileSystemURL& url, int permissions) const { + return FILE_PERMISSION_ALWAYS_DENY; +} + +FileSystemOperation* TestMountPointProvider::CreateFileSystemOperation( + const FileSystemURL& url, + FileSystemContext* context, + base::PlatformFileError* error_code) const { + scoped_ptr<FileSystemOperationContext> operation_context( + new FileSystemOperationContext(context)); + operation_context->set_update_observers(observers_); + return new LocalFileSystemOperation(context, operation_context.Pass()); +} + +scoped_ptr<webkit_blob::FileStreamReader> +TestMountPointProvider::CreateFileStreamReader( + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time, + FileSystemContext* context) const { + return scoped_ptr<webkit_blob::FileStreamReader>( + new FileSystemFileStreamReader( + context, url, offset, expected_modification_time)); +} + +scoped_ptr<fileapi::FileStreamWriter> +TestMountPointProvider::CreateFileStreamWriter( + const FileSystemURL& url, + int64 offset, + FileSystemContext* context) const { + return scoped_ptr<fileapi::FileStreamWriter>( + new SandboxFileStreamWriter(context, url, offset, observers_)); +} + +FileSystemQuotaUtil* TestMountPointProvider::GetQuotaUtil() { + return quota_util_.get(); +} + +void TestMountPointProvider::DeleteFileSystem( + const GURL& origin_url, + FileSystemType type, + FileSystemContext* context, + const DeleteFileSystemCallback& callback) { + // This won't be called unless we add test code that opens a test + // filesystem by OpenFileSystem. + NOTREACHED(); + callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); +} + +const UpdateObserverList* TestMountPointProvider::GetUpdateObservers( + FileSystemType type) const { + return &observers_; +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/test_mount_point_provider.h b/webkit/browser/fileapi/test_mount_point_provider.h new file mode 100644 index 0000000..2dddb3c --- /dev/null +++ b/webkit/browser/fileapi/test_mount_point_provider.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_BROWSER_FILEAPI_TEST_MOUNT_POINT_PROVIDER_H_ +#define WEBKIT_BROWSER_FILEAPI_TEST_MOUNT_POINT_PROVIDER_H_ + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "webkit/browser/fileapi/async_file_util_adapter.h" +#include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/task_runner_bound_observer_list.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace fileapi { + +class AsyncFileUtilAdapter; +class FileSystemQuotaUtil; + +// This should be only used for testing. +// This mount point provider uses LocalFileUtil and stores data file +// under the given directory. +class WEBKIT_STORAGE_EXPORT_PRIVATE TestMountPointProvider + : public FileSystemMountPointProvider { + public: + TestMountPointProvider( + base::SequencedTaskRunner* task_runner, + const base::FilePath& base_path); + virtual ~TestMountPointProvider(); + + // FileSystemMountPointProvider implementation. + virtual bool CanHandleType(FileSystemType type) const OVERRIDE; + virtual void ValidateFileSystemRoot( + const GURL& origin_url, + FileSystemType type, + bool create, + const ValidateFileSystemCallback& callback) OVERRIDE; + virtual base::FilePath GetFileSystemRootPathOnFileThread( + const FileSystemURL& url, + bool create) OVERRIDE; + virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE; + virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; + virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( + FileSystemType type, + base::PlatformFileError* error_code) OVERRIDE; + virtual void InitializeCopyOrMoveFileValidatorFactory( + FileSystemType type, + scoped_ptr<CopyOrMoveFileValidatorFactory> factory) OVERRIDE; + virtual FilePermissionPolicy GetPermissionPolicy( + const FileSystemURL& url, + int permissions) const OVERRIDE; + virtual FileSystemOperation* CreateFileSystemOperation( + const FileSystemURL& url, + FileSystemContext* context, + base::PlatformFileError* error_code) const OVERRIDE; + virtual scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader( + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time, + FileSystemContext* context) const OVERRIDE; + virtual scoped_ptr<FileStreamWriter> CreateFileStreamWriter( + const FileSystemURL& url, + int64 offset, + FileSystemContext* context) const OVERRIDE; + virtual FileSystemQuotaUtil* GetQuotaUtil() OVERRIDE; + virtual void DeleteFileSystem( + const GURL& origin_url, + FileSystemType type, + FileSystemContext* context, + const DeleteFileSystemCallback& callback) OVERRIDE; + + const UpdateObserverList* GetUpdateObservers(FileSystemType type) const; + + // For CopyOrMoveFileValidatorFactory testing. Once it's set to true + // GetCopyOrMoveFileValidatorFactory will start returning security + // error if validator is not initialized. + void set_require_copy_or_move_validator(bool flag) { + require_copy_or_move_validator_ = flag; + } + + private: + class QuotaUtil; + + base::FilePath base_path_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + scoped_ptr<AsyncFileUtilAdapter> local_file_util_; + scoped_ptr<QuotaUtil> quota_util_; + UpdateObserverList observers_; + + bool require_copy_or_move_validator_; + scoped_ptr<CopyOrMoveFileValidatorFactory> + copy_or_move_file_validator_factory_; + + DISALLOW_COPY_AND_ASSIGN(TestMountPointProvider); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_TEST_MOUNT_POINT_PROVIDER_H_ diff --git a/webkit/browser/fileapi/transient_file_util.cc b/webkit/browser/fileapi/transient_file_util.cc index d9ee207..74c2dbea 100644 --- a/webkit/browser/fileapi/transient_file_util.cc +++ b/webkit/browser/fileapi/transient_file_util.cc @@ -8,9 +8,9 @@ #include "base/bind.h" #include "base/files/file_path.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_url.h" using webkit_blob::ScopedFile; diff --git a/webkit/browser/fileapi/transient_file_util_unittest.cc b/webkit/browser/fileapi/transient_file_util_unittest.cc index 47d50c5..2b774f5 100644 --- a/webkit/browser/fileapi/transient_file_util_unittest.cc +++ b/webkit/browser/fileapi/transient_file_util_unittest.cc @@ -11,11 +11,11 @@ #include "base/platform_file.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/blob/scoped_file.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/browser/fileapi/transient_file_util.h" -#include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation_context.h" namespace fileapi { diff --git a/webkit/browser/fileapi/upload_file_system_file_element_reader.cc b/webkit/browser/fileapi/upload_file_system_file_element_reader.cc new file mode 100644 index 0000000..f5d47f9 --- /dev/null +++ b/webkit/browser/fileapi/upload_file_system_file_element_reader.cc @@ -0,0 +1,115 @@ +// Copyright (c) 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/upload_file_system_file_element_reader.h" + +#include "base/bind.h" +#include "net/base/net_errors.h" +#include "webkit/blob/file_stream_reader.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_url.h" + +namespace fileapi { + +UploadFileSystemFileElementReader::UploadFileSystemFileElementReader( + FileSystemContext* file_system_context, + const GURL& url, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time) + : file_system_context_(file_system_context), + url_(url), + range_offset_(range_offset), + range_length_(range_length), + expected_modification_time_(expected_modification_time), + stream_length_(0), + position_(0), + weak_ptr_factory_(this) { +} + +UploadFileSystemFileElementReader::~UploadFileSystemFileElementReader() { +} + +int UploadFileSystemFileElementReader::Init( + const net::CompletionCallback& callback) { + // Reset states. + weak_ptr_factory_.InvalidateWeakPtrs(); + stream_length_ = 0; + position_ = 0; + + // Initialize the stream reader and the length. + stream_reader_ = + file_system_context_->CreateFileStreamReader( + file_system_context_->CrackURL(url_), + range_offset_, + expected_modification_time_); + DCHECK(stream_reader_); + + const int64 result = stream_reader_->GetLength( + base::Bind(&UploadFileSystemFileElementReader::OnGetLength, + weak_ptr_factory_.GetWeakPtr(), + callback)); + if (result >= 0) { + stream_length_ = result; + return net::OK; + } + + // The error code can be casted to int. + return static_cast<int>(result); +} + +uint64 UploadFileSystemFileElementReader::GetContentLength() const { + return std::min(stream_length_, range_length_); +} + +uint64 UploadFileSystemFileElementReader::BytesRemaining() const { + return GetContentLength() - position_; +} + +int UploadFileSystemFileElementReader::Read( + net::IOBuffer* buf, + int buf_length, + const net::CompletionCallback& callback) { + DCHECK_LT(0, buf_length); + DCHECK(stream_reader_); + + const uint64 num_bytes_to_read = + std::min(BytesRemaining(), static_cast<uint64>(buf_length)); + + if (num_bytes_to_read == 0) + return 0; + + const int result = stream_reader_->Read( + buf, num_bytes_to_read, + base::Bind(&UploadFileSystemFileElementReader::OnRead, + weak_ptr_factory_.GetWeakPtr(), + callback)); + if (result >= 0) + OnRead(net::CompletionCallback(), result); + return result; +} + +void UploadFileSystemFileElementReader::OnGetLength( + const net::CompletionCallback& callback, + int64 result) { + if (result >= 0) { + stream_length_ = result; + callback.Run(net::OK); + return; + } + callback.Run(result); +} + +void UploadFileSystemFileElementReader::OnRead( + const net::CompletionCallback& callback, + int result) { + if (result > 0) { + position_ += result; + DCHECK_LE(position_, GetContentLength()); + } + if (!callback.is_null()) + callback.Run(result); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/upload_file_system_file_element_reader.h b/webkit/browser/fileapi/upload_file_system_file_element_reader.h new file mode 100644 index 0000000..525dc00 --- /dev/null +++ b/webkit/browser/fileapi/upload_file_system_file_element_reader.h @@ -0,0 +1,64 @@ +// Copyright (c) 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. + +#ifndef WEBKIT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ +#define WEBKIT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "net/base/upload_element_reader.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace webkit_blob { +class FileStreamReader; +} + +namespace fileapi { + +class FileSystemContext; + +// An UploadElementReader implementation for filesystem file. +class WEBKIT_STORAGE_EXPORT UploadFileSystemFileElementReader + : public net::UploadElementReader { + public: + UploadFileSystemFileElementReader( + FileSystemContext* file_system_context, + const GURL& url, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time); + virtual ~UploadFileSystemFileElementReader(); + + // UploadElementReader overrides: + virtual int Init(const net::CompletionCallback& callback) OVERRIDE; + virtual uint64 GetContentLength() const OVERRIDE; + virtual uint64 BytesRemaining() const OVERRIDE; + virtual int Read(net::IOBuffer* buf, + int buf_length, + const net::CompletionCallback& callback) OVERRIDE; + + private: + void OnGetLength(const net::CompletionCallback& callback, int64 result); + void OnRead(const net::CompletionCallback& callback, int result); + + scoped_refptr<FileSystemContext> file_system_context_; + const GURL url_; + const uint64 range_offset_; + const uint64 range_length_; + const base::Time expected_modification_time_; + + scoped_ptr<webkit_blob::FileStreamReader> stream_reader_; + + uint64 stream_length_; + uint64 position_; + + base::WeakPtrFactory<UploadFileSystemFileElementReader> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(UploadFileSystemFileElementReader); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ diff --git a/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc b/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc new file mode 100644 index 0000000..fb993c8 --- /dev/null +++ b/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc @@ -0,0 +1,284 @@ +// Copyright (c) 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/upload_file_system_file_element_reader.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "net/base/io_buffer.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_file_util.h" +#include "webkit/browser/fileapi/file_system_mount_point_provider.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/mock_file_system_context.h" + +namespace fileapi { + +namespace { + +const char kFileSystemURLOrigin[] = "http://remote"; +const fileapi::FileSystemType kFileSystemType = + fileapi::kFileSystemTypeTemporary; + +} // namespace + +class UploadFileSystemFileElementReaderTest : public testing::Test { + public: + UploadFileSystemFileElementReaderTest() + : message_loop_(base::MessageLoop::TYPE_IO) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + file_system_context_ = fileapi::CreateFileSystemContextForTesting( + NULL, temp_dir_.path()); + + file_system_context_->OpenFileSystem( + GURL(kFileSystemURLOrigin), + kFileSystemType, + true, // create + base::Bind(&UploadFileSystemFileElementReaderTest::OnValidateFileSystem, + base::Unretained(this))); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_TRUE(file_system_root_url_.is_valid()); + + // Prepare a file on file system. + const char kTestData[] = "abcdefghijklmnop0123456789"; + file_data_.assign(kTestData, kTestData + arraysize(kTestData) - 1); + const char kFilename[] = "File.dat"; + file_url_ = GetFileSystemURL(kFilename); + WriteFileSystemFile(kFilename, &file_data_[0], file_data_.size(), + &file_modification_time_); + + // Create and initialize a reader. + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, 0, kuint64max, + file_modification_time_)); + net::TestCompletionCallback callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(callback.callback())); + EXPECT_EQ(net::OK, callback.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + EXPECT_FALSE(reader_->IsInMemory()); + } + + protected: + GURL GetFileSystemURL(const std::string& filename) { + return GURL(file_system_root_url_.spec() + filename); + } + + void WriteFileSystemFile(const std::string& filename, + const char* buf, + int buf_size, + base::Time* modification_time) { + fileapi::FileSystemURL url = + file_system_context_->CreateCrackedFileSystemURL( + GURL(kFileSystemURLOrigin), + kFileSystemType, + base::FilePath().AppendASCII(filename)); + + fileapi::FileSystemFileUtil* file_util = + file_system_context_->GetFileUtil(kFileSystemType); + + fileapi::FileSystemOperationContext context(file_system_context_); + context.set_allowed_bytes_growth(1024); + + base::PlatformFile handle = base::kInvalidPlatformFileValue; + bool created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen( + &context, + url, + base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, + &handle, + &created)); + EXPECT_TRUE(created); + ASSERT_NE(base::kInvalidPlatformFileValue, handle); + ASSERT_EQ(buf_size, + base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size)); + base::ClosePlatformFile(handle); + + base::PlatformFileInfo file_info; + base::FilePath platform_path; + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util->GetFileInfo(&context, url, &file_info, + &platform_path)); + *modification_time = file_info.last_modified; + } + + void OnValidateFileSystem(base::PlatformFileError result, + const std::string& name, + const GURL& root) { + ASSERT_EQ(base::PLATFORM_FILE_OK, result); + ASSERT_TRUE(root.is_valid()); + file_system_root_url_ = root; + } + + base::MessageLoop message_loop_; + base::ScopedTempDir temp_dir_; + scoped_refptr<FileSystemContext> file_system_context_; + GURL file_system_root_url_; + std::vector<char> file_data_; + GURL file_url_; + base::Time file_modification_time_; + scoped_ptr<UploadFileSystemFileElementReader> reader_; +}; + +TEST_F(UploadFileSystemFileElementReaderTest, ReadAll) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(buf->size(), read_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); + // Try to read again. + EXPECT_EQ(0, reader_->Read(buf, buf->size(), read_callback.callback())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, ReadPartially) { + const size_t kHalfSize = file_data_.size() / 2; + ASSERT_EQ(file_data_.size(), kHalfSize * 2); + + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(kHalfSize); + + net::TestCompletionCallback read_callback1; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + EXPECT_EQ(buf->size(), read_callback1.WaitForResult()); + EXPECT_EQ(file_data_.size() - buf->size(), reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + kHalfSize, + buf->data())); + + net::TestCompletionCallback read_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback2.callback())); + EXPECT_EQ(buf->size(), read_callback2.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin() + kHalfSize, file_data_.end(), + buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, ReadTooMuch) { + const size_t kTooLargeSize = file_data_.size() * 2; + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(kTooLargeSize); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(static_cast<int>(file_data_.size()), read_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, MultipleInit) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + + // Read all. + net::TestCompletionCallback read_callback1; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + EXPECT_EQ(buf->size(), read_callback1.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); + + // Call Init() again to reset the state. + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::OK, init_callback.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + + // Read again. + net::TestCompletionCallback read_callback2; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback2.callback())); + EXPECT_EQ(buf->size(), read_callback2.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, InitDuringAsyncOperation) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + + // Start reading all. + net::TestCompletionCallback read_callback1; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + + // Call Init to cancel the previous read. + net::TestCompletionCallback init_callback1; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback1.callback())); + + // Call Init again to cancel the previous init. + net::TestCompletionCallback init_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback2.callback())); + EXPECT_EQ(net::OK, init_callback2.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + + // Read half. + scoped_refptr<net::IOBufferWithSize> buf2 = + new net::IOBufferWithSize(file_data_.size() / 2); + net::TestCompletionCallback read_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf2, buf2->size(), + read_callback2.callback())); + EXPECT_EQ(buf2->size(), read_callback2.WaitForResult()); + EXPECT_EQ(file_data_.size() - buf2->size(), reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + buf2->size(), + buf2->data())); + + // Make sure callbacks are not called for cancelled operations. + EXPECT_FALSE(read_callback1.have_result()); + EXPECT_FALSE(init_callback1.have_result()); +} + +TEST_F(UploadFileSystemFileElementReaderTest, Range) { + const int kOffset = 2; + const int kLength = file_data_.size() - kOffset * 3; + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, kOffset, kLength, base::Time())); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::OK, init_callback.WaitForResult()); + EXPECT_EQ(static_cast<uint64>(kLength), reader_->GetContentLength()); + EXPECT_EQ(static_cast<uint64>(kLength), reader_->BytesRemaining()); + scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(kLength); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(kLength, read_callback.WaitForResult()); + EXPECT_TRUE(std::equal(file_data_.begin() + kOffset, + file_data_.begin() + kOffset + kLength, + buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, FileChanged) { + // Expect one second before the actual modification time to simulate change. + const base::Time expected_modification_time = + file_modification_time_ - base::TimeDelta::FromSeconds(1); + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, 0, kuint64max, + expected_modification_time)); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, init_callback.WaitForResult()); +} + +TEST_F(UploadFileSystemFileElementReaderTest, WrongURL) { + const GURL wrong_url = GetFileSystemURL("wrong_file_name.dat"); + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, wrong_url, 0, kuint64max, base::Time())); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, init_callback.WaitForResult()); +} + +} // namespace fileapi diff --git a/webkit/browser/fileapi/webkit_browser_fileapi.gypi b/webkit/browser/fileapi/webkit_browser_fileapi.gypi index 70f5f7d..333a71d 100644 --- a/webkit/browser/fileapi/webkit_browser_fileapi.gypi +++ b/webkit/browser/fileapi/webkit_browser_fileapi.gypi @@ -5,34 +5,55 @@ { 'variables': { 'webkit_browser_fileapi_sources': [ + '../browser/fileapi/async_file_util.h', + '../browser/fileapi/async_file_util_adapter.cc', + '../browser/fileapi/async_file_util_adapter.h', '../browser/fileapi/copy_or_move_file_validator.h', '../browser/fileapi/cross_operation_delegate.cc', '../browser/fileapi/cross_operation_delegate.h', '../browser/fileapi/external_mount_points.cc', '../browser/fileapi/external_mount_points.h', '../browser/fileapi/file_observers.h', + '../browser/fileapi/file_permission_policy.cc', + '../browser/fileapi/file_permission_policy.h', + '../browser/fileapi/file_stream_writer.h', + '../browser/fileapi/file_system_context.cc', + '../browser/fileapi/file_system_context.h', '../browser/fileapi/file_system_dir_url_request_job.cc', '../browser/fileapi/file_system_dir_url_request_job.h', + '../browser/fileapi/file_system_file_stream_reader.cc', + '../browser/fileapi/file_system_file_stream_reader.h', '../browser/fileapi/file_system_file_util.cc', '../browser/fileapi/file_system_file_util.h', '../browser/fileapi/file_system_mount_point_provider.h', + '../browser/fileapi/file_system_operation.h', + '../browser/fileapi/file_system_operation_context.cc', + '../browser/fileapi/file_system_operation_context.h', + '../browser/fileapi/file_system_options.cc', + '../browser/fileapi/file_system_options.h', '../browser/fileapi/file_system_quota_client.cc', '../browser/fileapi/file_system_quota_client.h', '../browser/fileapi/file_system_quota_util.h', '../browser/fileapi/file_system_task_runners.cc', '../browser/fileapi/file_system_task_runners.h', + '../browser/fileapi/file_system_url.cc', + '../browser/fileapi/file_system_url.h', '../browser/fileapi/file_system_url_request_job.cc', '../browser/fileapi/file_system_url_request_job.h', '../browser/fileapi/file_system_url_request_job_factory.cc', '../browser/fileapi/file_system_url_request_job_factory.h', '../browser/fileapi/file_system_usage_cache.cc', '../browser/fileapi/file_system_usage_cache.h', + '../browser/fileapi/file_writer_delegate.cc', + '../browser/fileapi/file_writer_delegate.h', '../browser/fileapi/isolated_context.cc', '../browser/fileapi/isolated_context.h', '../browser/fileapi/isolated_file_util.cc', '../browser/fileapi/isolated_file_util.h', '../browser/fileapi/isolated_mount_point_provider.cc', '../browser/fileapi/isolated_mount_point_provider.h', + '../browser/fileapi/local_file_stream_writer.cc', + '../browser/fileapi/local_file_stream_writer.h', '../browser/fileapi/local_file_system_operation.cc', '../browser/fileapi/local_file_system_operation.h', '../browser/fileapi/local_file_util.cc', @@ -45,6 +66,9 @@ '../browser/fileapi/obfuscated_file_util.h', '../browser/fileapi/recursive_operation_delegate.cc', '../browser/fileapi/recursive_operation_delegate.h', + '../browser/fileapi/remote_file_system_proxy.h', + '../browser/fileapi/remove_operation_delegate.cc', + '../browser/fileapi/remove_operation_delegate.h', '../browser/fileapi/sandbox_directory_database.cc', '../browser/fileapi/sandbox_directory_database.h', '../browser/fileapi/sandbox_file_stream_writer.cc', @@ -56,8 +80,25 @@ '../browser/fileapi/sandbox_quota_observer.cc', '../browser/fileapi/sandbox_quota_observer.h', '../browser/fileapi/task_runner_bound_observer_list.h', + '../browser/fileapi/test_mount_point_provider.cc', + '../browser/fileapi/test_mount_point_provider.h', '../browser/fileapi/transient_file_util.cc', '../browser/fileapi/transient_file_util.h', + '../browser/fileapi/upload_file_system_file_element_reader.cc', + '../browser/fileapi/upload_file_system_file_element_reader.h', ], }, + 'targets': [ + { + 'target_name': 'dump_file_system', + 'type': 'executable', + 'sources': [ + 'dump_file_system.cc', + ], + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '../support/webkit_support.gyp:webkit_storage', + ], + }, + ], } |