diff options
Diffstat (limited to 'chromium/webkit/browser/fileapi')
111 files changed, 4358 insertions, 10961 deletions
diff --git a/chromium/webkit/browser/fileapi/OWNERS b/chromium/webkit/browser/fileapi/OWNERS index 6ca58a9dbe5..8338b2a1442 100644 --- a/chromium/webkit/browser/fileapi/OWNERS +++ b/chromium/webkit/browser/fileapi/OWNERS @@ -1,2 +1,3 @@ ericu@chromium.org tzik@chromium.org +nhiroki@chromium.org diff --git a/chromium/webkit/browser/fileapi/async_file_test_helper.cc b/chromium/webkit/browser/fileapi/async_file_test_helper.cc index e3343d51935..50fd7cda34c 100644 --- a/chromium/webkit/browser/fileapi/async_file_test_helper.cc +++ b/chromium/webkit/browser/fileapi/async_file_test_helper.cc @@ -105,7 +105,7 @@ base::PlatformFileError AsyncFileTestHelper::CopyWithProgress( base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; base::RunLoop run_loop; context->operation_runner()->Copy( - src, dest, progress_callback, + src, dest, FileSystemOperation::OPTION_NONE, progress_callback, AssignAndQuitCallback(&run_loop, &result)); run_loop.Run(); return result; @@ -118,7 +118,8 @@ base::PlatformFileError AsyncFileTestHelper::Move( base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; base::RunLoop run_loop; context->operation_runner()->Move( - src, dest, AssignAndQuitCallback(&run_loop, &result)); + src, dest, FileSystemOperation::OPTION_NONE, + AssignAndQuitCallback(&run_loop, &result)); run_loop.Run(); return result; } diff --git a/chromium/webkit/browser/fileapi/async_file_util.h b/chromium/webkit/browser/fileapi/async_file_util.h index 84a033b7a4e..c1e6d1e4725 100644 --- a/chromium/webkit/browser/fileapi/async_file_util.h +++ b/chromium/webkit/browser/fileapi/async_file_util.h @@ -5,11 +5,14 @@ #ifndef WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_H_ #define WEBKIT_BROWSER_FILEAPI_ASYNC_FILE_UTIL_H_ +#include <vector> + #include "base/basictypes.h" #include "base/callback_forward.h" #include "base/files/file_util_proxy.h" #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" +#include "webkit/browser/fileapi/file_system_operation.h" #include "webkit/browser/webkit_storage_browser_export.h" #include "webkit/common/fileapi/directory_entry.h" @@ -73,12 +76,14 @@ class AsyncFileUtil { void(base::PlatformFileError result, const base::PlatformFileInfo& file_info, const base::FilePath& platform_path, - const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref - )> CreateSnapshotFileCallback; + const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref)> + CreateSnapshotFileCallback; typedef base::Callback<void(int64 size)> CopyFileProgressCallback; + typedef FileSystemOperation::CopyOrMoveOption CopyOrMoveOption; + // Creates an AsyncFileUtil instance which performs file operations on // local native file system. The created instance assumes // FileSystemURL::path() has the target platform path. @@ -94,7 +99,7 @@ class AsyncFileUtil { // PLATFORM_FILE_ERROR_FILE_EXISTS if the |url| already exists. // // FileSystemOperationImpl::OpenFile calls this. - // This is used only by Pepper/NaCL File API. + // This is used only by Pepper/NaCl File API. // virtual void CreateOrOpen( scoped_ptr<FileSystemOperationContext> context, @@ -180,7 +185,7 @@ class AsyncFileUtil { // create a file unlike 'touch' command on Linux. // // FileSystemOperationImpl::TouchFile calls this. - // This is used only by Pepper/NaCL File API. + // This is used only by Pepper/NaCl File API. // virtual void Touch( scoped_ptr<FileSystemOperationContext> context, @@ -229,6 +234,7 @@ class AsyncFileUtil { scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) = 0; @@ -251,6 +257,7 @@ class AsyncFileUtil { scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) = 0; // Copies in a single file from a different filesystem. diff --git a/chromium/webkit/browser/fileapi/async_file_util_adapter.cc b/chromium/webkit/browser/fileapi/async_file_util_adapter.cc index 54d67976dc7..b25ddf24967 100644 --- a/chromium/webkit/browser/fileapi/async_file_util_adapter.cc +++ b/chromium/webkit/browser/fileapi/async_file_util_adapter.cc @@ -4,6 +4,8 @@ #include "webkit/browser/fileapi/async_file_util_adapter.h" +#include <vector> + #include "base/bind.h" #include "base/sequenced_task_runner.h" #include "base/task_runner_util.h" @@ -168,10 +170,10 @@ void AsyncFileUtilAdapter::EnsureFileExists( EnsureFileExistsHelper* helper = new EnsureFileExistsHelper; FileSystemOperationContext* context_ptr = context.release(); const bool success = context_ptr->task_runner()->PostTaskAndReply( - FROM_HERE, - Bind(&EnsureFileExistsHelper::RunWork, Unretained(helper), - sync_file_util_.get(), base::Owned(context_ptr), url), - Bind(&EnsureFileExistsHelper::Reply, Owned(helper), callback)); + FROM_HERE, + Bind(&EnsureFileExistsHelper::RunWork, Unretained(helper), + sync_file_util_.get(), base::Owned(context_ptr), url), + Bind(&EnsureFileExistsHelper::Reply, Owned(helper), callback)); DCHECK(success); } @@ -198,10 +200,10 @@ void AsyncFileUtilAdapter::GetFileInfo( FileSystemOperationContext* context_ptr = context.release(); GetFileInfoHelper* helper = new GetFileInfoHelper; const bool success = context_ptr->task_runner()->PostTaskAndReply( - FROM_HERE, - Bind(&GetFileInfoHelper::GetFileInfo, Unretained(helper), - sync_file_util_.get(), base::Owned(context_ptr), url), - Bind(&GetFileInfoHelper::ReplyFileInfo, Owned(helper), callback)); + FROM_HERE, + Bind(&GetFileInfoHelper::GetFileInfo, Unretained(helper), + sync_file_util_.get(), base::Owned(context_ptr), url), + Bind(&GetFileInfoHelper::ReplyFileInfo, Owned(helper), callback)); DCHECK(success); } @@ -212,10 +214,10 @@ void AsyncFileUtilAdapter::ReadDirectory( FileSystemOperationContext* context_ptr = context.release(); ReadDirectoryHelper* helper = new ReadDirectoryHelper; const bool success = context_ptr->task_runner()->PostTaskAndReply( - FROM_HERE, - Bind(&ReadDirectoryHelper::RunWork, Unretained(helper), - sync_file_util_.get(), base::Owned(context_ptr), url), - Bind(&ReadDirectoryHelper::Reply, Owned(helper), callback)); + FROM_HERE, + Bind(&ReadDirectoryHelper::RunWork, Unretained(helper), + sync_file_util_.get(), base::Owned(context_ptr), url), + Bind(&ReadDirectoryHelper::Reply, Owned(helper), callback)); DCHECK(success); } @@ -253,6 +255,7 @@ void AsyncFileUtilAdapter::CopyFileLocal( scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) { // TODO(hidehiko): Support progress_callback. @@ -260,8 +263,8 @@ void AsyncFileUtilAdapter::CopyFileLocal( const bool success = base::PostTaskAndReplyWithResult( context_ptr->task_runner(), FROM_HERE, Bind(&FileSystemFileUtil::CopyOrMoveFile, - Unretained(sync_file_util_.get()), - base::Owned(context_ptr), src_url, dest_url, true /* copy */), + Unretained(sync_file_util_.get()), base::Owned(context_ptr), + src_url, dest_url, option, true /* copy */), callback); DCHECK(success); } @@ -270,13 +273,14 @@ void AsyncFileUtilAdapter::MoveFileLocal( scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { FileSystemOperationContext* context_ptr = context.release(); const bool success = base::PostTaskAndReplyWithResult( context_ptr->task_runner(), FROM_HERE, Bind(&FileSystemFileUtil::CopyOrMoveFile, - Unretained(sync_file_util_.get()), - base::Owned(context_ptr), src_url, dest_url, false /* copy */), + Unretained(sync_file_util_.get()), base::Owned(context_ptr), + src_url, dest_url, option, false /* copy */), callback); DCHECK(success); } @@ -338,10 +342,10 @@ void AsyncFileUtilAdapter::CreateSnapshotFile( FileSystemOperationContext* context_ptr = context.release(); GetFileInfoHelper* helper = new GetFileInfoHelper; const bool success = context_ptr->task_runner()->PostTaskAndReply( - FROM_HERE, - Bind(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper), - sync_file_util_.get(), base::Owned(context_ptr), url), - Bind(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper), callback)); + FROM_HERE, + Bind(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper), + sync_file_util_.get(), base::Owned(context_ptr), url), + Bind(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper), callback)); DCHECK(success); } diff --git a/chromium/webkit/browser/fileapi/async_file_util_adapter.h b/chromium/webkit/browser/fileapi/async_file_util_adapter.h index 0122b11b9d5..8e90e5d1409 100644 --- a/chromium/webkit/browser/fileapi/async_file_util_adapter.h +++ b/chromium/webkit/browser/fileapi/async_file_util_adapter.h @@ -76,12 +76,14 @@ class WEBKIT_STORAGE_BROWSER_EXPORT AsyncFileUtilAdapter scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) OVERRIDE; virtual void MoveFileLocal( scoped_ptr<FileSystemOperationContext> context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) OVERRIDE; virtual void CopyInForeignFile( scoped_ptr<FileSystemOperationContext> context, diff --git a/chromium/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc b/chromium/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc deleted file mode 100644 index 366efa3dad5..00000000000 --- a/chromium/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.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/copy_or_move_file_validator.h" -#include "webkit/browser/fileapi/external_mount_points.h" -#include "webkit/browser/fileapi/file_system_backend.h" -#include "webkit/browser/fileapi/file_system_context.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/browser/fileapi/test_file_system_backend.h" -#include "webkit/browser/quota/mock_special_storage_policy.h" -#include "webkit/common/blob/shareable_file_reference.h" -#include "webkit/common/fileapi/file_system_util.h" - -namespace fileapi { - -namespace { - -const FileSystemType kNoValidatorType = kFileSystemTypeTemporary; -const FileSystemType kWithValidatorType = kFileSystemTypeTest; - -void ExpectOk(const GURL& origin_url, - const std::string& name, - base::PlatformFileError error) { - ASSERT_EQ(base::PLATFORM_FILE_OK, error); -} - -class CopyOrMoveFileValidatorTestHelper { - public: - CopyOrMoveFileValidatorTestHelper( - const GURL& origin, - FileSystemType src_type, - FileSystemType dest_type) - : origin_(origin), - src_type_(src_type), - dest_type_(dest_type) {} - - ~CopyOrMoveFileValidatorTestHelper() { - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - void SetUp() { - ASSERT_TRUE(base_.CreateUniqueTempDir()); - base::FilePath base_dir = base_.path(); - - file_system_context_ = CreateFileSystemContextForTesting(NULL, base_dir); - - // Set up TestFileSystemBackend to require CopyOrMoveFileValidator. - FileSystemBackend* test_file_system_backend = - file_system_context_->GetFileSystemBackend(kWithValidatorType); - static_cast<TestFileSystemBackend*>(test_file_system_backend)-> - set_require_copy_or_move_validator(true); - - // Sets up source. - FileSystemBackend* src_file_system_backend = - file_system_context_->GetFileSystemBackend(src_type_); - src_file_system_backend->OpenFileSystem( - origin_, src_type_, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&ExpectOk)); - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(SourceURL(""))); - - // Sets up dest. - DCHECK_EQ(kWithValidatorType, dest_type_); - ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(DestURL(""))); - - copy_src_ = SourceURL("copy_src.jpg"); - move_src_ = SourceURL("move_src.jpg"); - copy_dest_ = DestURL("copy_dest.jpg"); - move_dest_ = DestURL("move_dest.jpg"); - - ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(copy_src_, 10)); - ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(move_src_, 10)); - - ASSERT_TRUE(FileExists(copy_src_, 10)); - ASSERT_TRUE(FileExists(move_src_, 10)); - ASSERT_FALSE(FileExists(copy_dest_, 10)); - ASSERT_FALSE(FileExists(move_dest_, 10)); - } - - void SetMediaCopyOrMoveFileValidatorFactory( - scoped_ptr<CopyOrMoveFileValidatorFactory> factory) { - TestFileSystemBackend* backend = static_cast<TestFileSystemBackend*>( - file_system_context_->GetFileSystemBackend(kWithValidatorType)); - backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass()); - } - - void CopyTest(base::PlatformFileError expected) { - ASSERT_TRUE(FileExists(copy_src_, 10)); - ASSERT_FALSE(FileExists(copy_dest_, 10)); - - EXPECT_EQ(expected, - AsyncFileTestHelper::Copy( - file_system_context_.get(), copy_src_, copy_dest_)); - - EXPECT_TRUE(FileExists(copy_src_, 10)); - if (expected == base::PLATFORM_FILE_OK) - EXPECT_TRUE(FileExists(copy_dest_, 10)); - else - EXPECT_FALSE(FileExists(copy_dest_, 10)); - }; - - void MoveTest(base::PlatformFileError expected) { - ASSERT_TRUE(FileExists(move_src_, 10)); - ASSERT_FALSE(FileExists(move_dest_, 10)); - - EXPECT_EQ(expected, - AsyncFileTestHelper::Move( - file_system_context_.get(), move_src_, move_dest_)); - - if (expected == base::PLATFORM_FILE_OK) { - EXPECT_FALSE(FileExists(move_src_, 10)); - EXPECT_TRUE(FileExists(move_dest_, 10)); - } else { - EXPECT_TRUE(FileExists(move_src_, 10)); - EXPECT_FALSE(FileExists(move_dest_, 10)); - } - }; - - private: - FileSystemURL SourceURL(const std::string& path) { - return file_system_context_->CreateCrackedFileSystemURL( - origin_, src_type_, - base::FilePath().AppendASCII("src").AppendASCII(path)); - } - - FileSystemURL DestURL(const std::string& path) { - return file_system_context_->CreateCrackedFileSystemURL( - origin_, dest_type_, - base::FilePath().AppendASCII("dest").AppendASCII(path)); - } - - base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) { - base::PlatformFileError result = - AsyncFileTestHelper::CreateFile(file_system_context_.get(), url); - if (result != base::PLATFORM_FILE_OK) - return result; - return AsyncFileTestHelper::TruncateFile( - file_system_context_.get(), url, size); - } - - base::PlatformFileError CreateDirectory(const FileSystemURL& url) { - return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(), - url); - } - - bool FileExists(const FileSystemURL& url, int64 expected_size) { - return AsyncFileTestHelper::FileExists( - file_system_context_.get(), url, expected_size); - } - - base::ScopedTempDir base_; - - const GURL origin_; - - const FileSystemType src_type_; - const FileSystemType dest_type_; - std::string src_fsid_; - std::string dest_fsid_; - - base::MessageLoop message_loop_; - scoped_refptr<FileSystemContext> file_system_context_; - - FileSystemURL copy_src_; - FileSystemURL copy_dest_; - FileSystemURL move_src_; - FileSystemURL move_dest_; - - DISALLOW_COPY_AND_ASSIGN(CopyOrMoveFileValidatorTestHelper); -}; - -// For TestCopyOrMoveFileValidatorFactory -enum Validity { - VALID, - PRE_WRITE_INVALID, - POST_WRITE_INVALID -}; - -class TestCopyOrMoveFileValidatorFactory - : public CopyOrMoveFileValidatorFactory { - public: - // A factory that creates validators that accept everything or nothing. - // TODO(gbillock): switch args to enum or something - explicit TestCopyOrMoveFileValidatorFactory(Validity validity) - : validity_(validity) {} - virtual ~TestCopyOrMoveFileValidatorFactory() {} - - virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator( - const FileSystemURL& /*src_url*/, - const base::FilePath& /*platform_path*/) OVERRIDE { - return new TestCopyOrMoveFileValidator(validity_); - } - - private: - class TestCopyOrMoveFileValidator : public CopyOrMoveFileValidator { - public: - explicit TestCopyOrMoveFileValidator(Validity validity) - : result_(validity == VALID || validity == POST_WRITE_INVALID - ? base::PLATFORM_FILE_OK - : base::PLATFORM_FILE_ERROR_SECURITY), - write_result_(validity == VALID || validity == PRE_WRITE_INVALID - ? base::PLATFORM_FILE_OK - : base::PLATFORM_FILE_ERROR_SECURITY) { - } - virtual ~TestCopyOrMoveFileValidator() {} - - virtual void StartPreWriteValidation( - const ResultCallback& result_callback) OVERRIDE { - // Post the result since a real validator must do work asynchronously. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(result_callback, result_)); - } - - virtual void StartPostWriteValidation( - const base::FilePath& dest_platform_path, - const ResultCallback& result_callback) OVERRIDE { - // Post the result since a real validator must do work asynchronously. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(result_callback, write_result_)); - } - - private: - base::PlatformFileError result_; - base::PlatformFileError write_result_; - - DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidator); - }; - - Validity validity_; - - DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidatorFactory); -}; - -} // namespace - -TEST(CopyOrMoveFileValidatorTest, NoValidatorWithinSameFSType) { - // Within a file system type, validation is not expected, so it should - // work for kWithValidatorType without a validator set. - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kWithValidatorType, - kWithValidatorType); - helper.SetUp(); - helper.CopyTest(base::PLATFORM_FILE_OK); - helper.MoveTest(base::PLATFORM_FILE_OK); -} - -TEST(CopyOrMoveFileValidatorTest, MissingValidator) { - // Copying or moving into a kWithValidatorType requires a file - // validator. An error is expected if copy is attempted without a validator. - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kNoValidatorType, - kWithValidatorType); - helper.SetUp(); - helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY); - helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY); -} - -TEST(CopyOrMoveFileValidatorTest, AcceptAll) { - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kNoValidatorType, - kWithValidatorType); - helper.SetUp(); - scoped_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(VALID)); - helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass()); - - helper.CopyTest(base::PLATFORM_FILE_OK); - helper.MoveTest(base::PLATFORM_FILE_OK); -} - -TEST(CopyOrMoveFileValidatorTest, AcceptNone) { - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kNoValidatorType, - kWithValidatorType); - helper.SetUp(); - scoped_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID)); - helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass()); - - helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY); - helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY); -} - -TEST(CopyOrMoveFileValidatorTest, OverrideValidator) { - // Once set, you can not override the validator. - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kNoValidatorType, - kWithValidatorType); - helper.SetUp(); - scoped_ptr<CopyOrMoveFileValidatorFactory> reject_factory( - new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID)); - helper.SetMediaCopyOrMoveFileValidatorFactory(reject_factory.Pass()); - - scoped_ptr<CopyOrMoveFileValidatorFactory> accept_factory( - new TestCopyOrMoveFileValidatorFactory(VALID)); - helper.SetMediaCopyOrMoveFileValidatorFactory(accept_factory.Pass()); - - helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY); - helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY); -} - -TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) { - CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"), - kNoValidatorType, - kWithValidatorType); - helper.SetUp(); - scoped_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestCopyOrMoveFileValidatorFactory(POST_WRITE_INVALID)); - helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass()); - - helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY); - helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.cc b/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.cc index aba17b55d75..967b5c28f0d 100644 --- a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.cc +++ b/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.cc @@ -6,7 +6,11 @@ #include "base/bind.h" #include "base/files/file_path.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "webkit/browser/blob/file_stream_reader.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" +#include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_operation_runner.h" #include "webkit/browser/fileapi/file_system_url.h" @@ -16,13 +20,19 @@ namespace fileapi { +const int64 kFlushIntervalInBytes = 10 << 20; // 10MB. + class CopyOrMoveOperationDelegate::CopyOrMoveImpl { public: virtual ~CopyOrMoveImpl() {} virtual void Run( const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0; + virtual void Cancel() = 0; + protected: CopyOrMoveImpl() {} + + private: DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl); }; @@ -38,30 +48,39 @@ class CopyOrMoveOnSameFileSystemImpl CopyOrMoveOperationDelegate::OperationType operation_type, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOperationDelegate::CopyOrMoveOption option, const FileSystemOperation::CopyFileProgressCallback& file_progress_callback) : operation_runner_(operation_runner), operation_type_(operation_type), src_url_(src_url), dest_url_(dest_url), + option_(option), file_progress_callback_(file_progress_callback) { } virtual void Run( const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) { - operation_runner_->MoveFileLocal(src_url_, dest_url_, callback); + operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback); } else { operation_runner_->CopyFileLocal( - src_url_, dest_url_, file_progress_callback_, callback); + src_url_, dest_url_, option_, file_progress_callback_, callback); } } + virtual void Cancel() OVERRIDE { + // We can do nothing for the copy/move operation on a local file system. + // Assuming the operation is quickly done, it should be ok to just wait + // for the completion. + } + private: FileSystemOperationRunner* operation_runner_; CopyOrMoveOperationDelegate::OperationType operation_type_; FileSystemURL src_url_; FileSystemURL dest_url_; + CopyOrMoveOperationDelegate::CopyOrMoveOption option_; FileSystemOperation::CopyFileProgressCallback file_progress_callback_; DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl); }; @@ -78,6 +97,7 @@ class SnapshotCopyOrMoveImpl CopyOrMoveOperationDelegate::OperationType operation_type, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOperationDelegate::CopyOrMoveOption option, CopyOrMoveFileValidatorFactory* validator_factory, const FileSystemOperation::CopyFileProgressCallback& file_progress_callback) @@ -85,8 +105,10 @@ class SnapshotCopyOrMoveImpl operation_type_(operation_type), src_url_(src_url), dest_url_(dest_url), + option_(option), validator_factory_(validator_factory), file_progress_callback_(file_progress_callback), + cancel_requested_(false), weak_factory_(this) { } @@ -99,6 +121,10 @@ class SnapshotCopyOrMoveImpl weak_factory_.GetWeakPtr(), callback)); } + virtual void Cancel() OVERRIDE { + cancel_requested_ = true; + } + private: void RunAfterCreateSnapshot( const CopyOrMoveOperationDelegate::StatusCallback& callback, @@ -106,6 +132,9 @@ class SnapshotCopyOrMoveImpl const base::PlatformFileInfo& file_info, const base::FilePath& platform_path, const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error != base::PLATFORM_FILE_OK) { callback.Run(error); return; @@ -136,6 +165,9 @@ class SnapshotCopyOrMoveImpl const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref, const CopyOrMoveOperationDelegate::StatusCallback& callback, base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error != base::PLATFORM_FILE_OK) { callback.Run(error); return; @@ -154,6 +186,9 @@ class SnapshotCopyOrMoveImpl const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref, const CopyOrMoveOperationDelegate::StatusCallback& callback, base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error != base::PLATFORM_FILE_OK) { callback.Run(error); return; @@ -161,6 +196,28 @@ class SnapshotCopyOrMoveImpl file_progress_callback_.Run(file_info.size); + if (option_ == FileSystemOperation::OPTION_NONE) { + RunAfterTouchFile(callback, base::PLATFORM_FILE_OK); + return; + } + + operation_runner_->TouchFile( + dest_url_, base::Time::Now() /* last_access */, + file_info.last_modified, + base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile, + weak_factory_.GetWeakPtr(), callback)); + } + + void RunAfterTouchFile( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + base::PlatformFileError error) { + // Even if TouchFile is failed, just ignore it. + + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + // |validator_| is NULL when the destination filesystem does not do // validation. if (!validator_) { @@ -177,6 +234,11 @@ class SnapshotCopyOrMoveImpl void RunAfterPostWriteValidation( const CopyOrMoveOperationDelegate::StatusCallback& callback, base::PlatformFileError error) { + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + if (error != base::PLATFORM_FILE_OK) { // Failed to validate. Remove the destination file. operation_runner_->Remove( @@ -203,6 +265,9 @@ class SnapshotCopyOrMoveImpl void RunAfterRemoveSourceForMove( const CopyOrMoveOperationDelegate::StatusCallback& callback, base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) error = base::PLATFORM_FILE_OK; callback.Run(error); @@ -246,6 +311,9 @@ class SnapshotCopyOrMoveImpl const base::PlatformFileInfo& file_info, const base::FilePath& platform_path, const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error != base::PLATFORM_FILE_OK) { callback.Run(error); return; @@ -273,28 +341,352 @@ class SnapshotCopyOrMoveImpl CopyOrMoveOperationDelegate::OperationType operation_type_; FileSystemURL src_url_; FileSystemURL dest_url_; + + CopyOrMoveOperationDelegate::CopyOrMoveOption option_; CopyOrMoveFileValidatorFactory* validator_factory_; scoped_ptr<CopyOrMoveFileValidator> validator_; FileSystemOperation::CopyFileProgressCallback file_progress_callback_; - + bool cancel_requested_; base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); }; +// The size of buffer for StreamCopyHelper. +const int kReadBufferSize = 32768; + +// To avoid too many progress callbacks, it should be called less +// frequently than 50ms. +const int kMinProgressCallbackInvocationSpanInMilliseconds = 50; + +// Specifically for cross file system copy/move operation, this class uses +// stream reader and writer for copying. Validator is not supported, so if +// necessary SnapshotCopyOrMoveImpl should be used. +class StreamCopyOrMoveImpl + : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { + public: + StreamCopyOrMoveImpl( + FileSystemOperationRunner* operation_runner, + CopyOrMoveOperationDelegate::OperationType operation_type, + const FileSystemURL& src_url, + const FileSystemURL& dest_url, + CopyOrMoveOperationDelegate::CopyOrMoveOption option, + scoped_ptr<webkit_blob::FileStreamReader> reader, + scoped_ptr<FileStreamWriter> writer, + const FileSystemOperation::CopyFileProgressCallback& + file_progress_callback) + : operation_runner_(operation_runner), + operation_type_(operation_type), + src_url_(src_url), + dest_url_(dest_url), + option_(option), + reader_(reader.Pass()), + writer_(writer.Pass()), + file_progress_callback_(file_progress_callback), + cancel_requested_(false), + weak_factory_(this) { + } + + virtual void Run( + const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { + // Reader can be created even if the entry does not exist or the entry is + // a directory. To check errors before destination file creation, + // check metadata first. + operation_runner_->GetMetadata( + src_url_, + base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource, + weak_factory_.GetWeakPtr(), callback)); + } + + virtual void Cancel() OVERRIDE { + cancel_requested_ = true; + if (copy_helper_) + copy_helper_->Cancel(); + } + + private: + void RunAfterGetMetadataForSource( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + base::PlatformFileError error, + const base::PlatformFileInfo& file_info) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + + if (error != base::PLATFORM_FILE_OK) { + callback.Run(error); + return; + } + + if (file_info.is_directory) { + // If not a directory, failed with appropriate error code. + callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE); + return; + } + + // To use FileStreamWriter, we need to ensure the destination file exists. + operation_runner_->CreateFile( + dest_url_, false /* exclusive */, + base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination, + weak_factory_.GetWeakPtr(), + callback, file_info.last_modified)); + } + + void RunAfterCreateFileForDestination( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + const base::Time& last_modified, + base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + + if (error != base::PLATFORM_FILE_OK) { + callback.Run(error); + return; + } + + const bool need_flush = dest_url_.mount_option().copy_sync_option() == + fileapi::COPY_SYNC_OPTION_SYNC; + + DCHECK(!copy_helper_); + copy_helper_.reset( + new CopyOrMoveOperationDelegate::StreamCopyHelper( + reader_.Pass(), writer_.Pass(), + need_flush, + kReadBufferSize, + file_progress_callback_, + base::TimeDelta::FromMilliseconds( + kMinProgressCallbackInvocationSpanInMilliseconds))); + copy_helper_->Run( + base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy, + weak_factory_.GetWeakPtr(), callback, last_modified)); + } + + void RunAfterStreamCopy( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + const base::Time& last_modified, + base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + + if (error != base::PLATFORM_FILE_OK) { + callback.Run(error); + return; + } + + if (option_ == FileSystemOperation::OPTION_NONE) { + RunAfterTouchFile(callback, base::PLATFORM_FILE_OK); + return; + } + + operation_runner_->TouchFile( + dest_url_, base::Time::Now() /* last_access */, last_modified, + base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile, + weak_factory_.GetWeakPtr(), callback)); + } + + void RunAfterTouchFile( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + base::PlatformFileError error) { + // Even if TouchFile is failed, just ignore it. + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + + if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { + callback.Run(base::PLATFORM_FILE_OK); + return; + } + + DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); + + // Remove the source for finalizing move operation. + operation_runner_->Remove( + src_url_, false /* recursive */, + base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove, + weak_factory_.GetWeakPtr(), callback)); + } + + void RunAfterRemoveForMove( + const CopyOrMoveOperationDelegate::StatusCallback& callback, + base::PlatformFileError error) { + if (cancel_requested_) + error = base::PLATFORM_FILE_ERROR_ABORT; + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) + error = base::PLATFORM_FILE_OK; + callback.Run(error); + } + + FileSystemOperationRunner* operation_runner_; + CopyOrMoveOperationDelegate::OperationType operation_type_; + FileSystemURL src_url_; + FileSystemURL dest_url_; + CopyOrMoveOperationDelegate::CopyOrMoveOption option_; + scoped_ptr<webkit_blob::FileStreamReader> reader_; + scoped_ptr<FileStreamWriter> writer_; + FileSystemOperation::CopyFileProgressCallback file_progress_callback_; + scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_; + bool cancel_requested_; + base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl); +}; + } // namespace +CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper( + scoped_ptr<webkit_blob::FileStreamReader> reader, + scoped_ptr<FileStreamWriter> writer, + bool need_flush, + int buffer_size, + const FileSystemOperation::CopyFileProgressCallback& + file_progress_callback, + const base::TimeDelta& min_progress_callback_invocation_span) + : reader_(reader.Pass()), + writer_(writer.Pass()), + need_flush_(need_flush), + file_progress_callback_(file_progress_callback), + io_buffer_(new net::IOBufferWithSize(buffer_size)), + num_copied_bytes_(0), + previous_flush_offset_(0), + min_progress_callback_invocation_span_( + min_progress_callback_invocation_span), + cancel_requested_(false), + weak_factory_(this) { +} + +CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() { +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::Run( + const StatusCallback& callback) { + file_progress_callback_.Run(0); + last_progress_callback_invocation_time_ = base::Time::Now(); + Read(callback); +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() { + cancel_requested_ = true; +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::Read( + const StatusCallback& callback) { + int result = reader_->Read( + io_buffer_.get(), io_buffer_->size(), + base::Bind(&StreamCopyHelper::DidRead, + weak_factory_.GetWeakPtr(), callback)); + if (result != net::ERR_IO_PENDING) + DidRead(callback, result); +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead( + const StatusCallback& callback, int result) { + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + + if (result < 0) { + callback.Run(NetErrorToPlatformFileError(result)); + return; + } + + if (result == 0) { + // Here is the EOF. + if (need_flush_) + Flush(callback, true /* is_eof */); + else + callback.Run(base::PLATFORM_FILE_OK); + return; + } + + Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result)); +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::Write( + const StatusCallback& callback, + scoped_refptr<net::DrainableIOBuffer> buffer) { + DCHECK_GT(buffer->BytesRemaining(), 0); + + int result = writer_->Write( + buffer.get(), buffer->BytesRemaining(), + base::Bind(&StreamCopyHelper::DidWrite, + weak_factory_.GetWeakPtr(), callback, buffer)); + if (result != net::ERR_IO_PENDING) + DidWrite(callback, buffer, result); +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite( + const StatusCallback& callback, + scoped_refptr<net::DrainableIOBuffer> buffer, + int result) { + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + + if (result < 0) { + callback.Run(NetErrorToPlatformFileError(result)); + return; + } + + buffer->DidConsume(result); + num_copied_bytes_ += result; + + // Check the elapsed time since last |file_progress_callback_| invocation. + base::Time now = base::Time::Now(); + if (now - last_progress_callback_invocation_time_ >= + min_progress_callback_invocation_span_) { + file_progress_callback_.Run(num_copied_bytes_); + last_progress_callback_invocation_time_ = now; + } + + if (buffer->BytesRemaining() > 0) { + Write(callback, buffer); + return; + } + + if (need_flush_ && + (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) { + Flush(callback, false /* not is_eof */); + } else { + Read(callback); + } +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush( + const StatusCallback& callback, bool is_eof) { + int result = writer_->Flush( + base::Bind(&StreamCopyHelper::DidFlush, + weak_factory_.GetWeakPtr(), callback, is_eof)); + if (result != net::ERR_IO_PENDING) + DidFlush(callback, is_eof, result); +} + +void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush( + const StatusCallback& callback, bool is_eof, int result) { + if (cancel_requested_) { + callback.Run(base::PLATFORM_FILE_ERROR_ABORT); + return; + } + + previous_flush_offset_ = num_copied_bytes_; + if (is_eof) + callback.Run(NetErrorToPlatformFileError(result)); + else + Read(callback); +} CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( FileSystemContext* file_system_context, const FileSystemURL& src_root, const FileSystemURL& dest_root, OperationType operation_type, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback) : RecursiveOperationDelegate(file_system_context), src_root_(src_root), dest_root_(dest_root), operation_type_(operation_type), + option_(option), progress_callback_(progress_callback), callback_(callback), weak_factory_(this) { @@ -344,12 +736,11 @@ void CopyOrMoveOperationDelegate::ProcessFile( CopyOrMoveImpl* impl = NULL; if (same_file_system_) { impl = new CopyOrMoveOnSameFileSystemImpl( - operation_runner(), operation_type_, src_url, dest_url, + operation_runner(), operation_type_, src_url, dest_url, option_, base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, weak_factory_.GetWeakPtr(), src_url)); } else { // Cross filesystem case. - // TODO(hidehiko): Support stream based copy. crbug.com/279287. base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; CopyOrMoveFileValidatorFactory* validator_factory = file_system_context()->GetCopyOrMoveFileValidatorFactory( @@ -359,11 +750,28 @@ void CopyOrMoveOperationDelegate::ProcessFile( return; } - impl = new SnapshotCopyOrMoveImpl( - operation_runner(), operation_type_, src_url, dest_url, - validator_factory, - base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, - weak_factory_.GetWeakPtr(), src_url)); + if (!validator_factory) { + scoped_ptr<webkit_blob::FileStreamReader> reader = + file_system_context()->CreateFileStreamReader( + src_url, 0, base::Time()); + scoped_ptr<FileStreamWriter> writer = + file_system_context()->CreateFileStreamWriter(dest_url, 0); + if (reader && writer) { + impl = new StreamCopyOrMoveImpl( + operation_runner(), operation_type_, src_url, dest_url, option_, + reader.Pass(), writer.Pass(), + base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, + weak_factory_.GetWeakPtr(), src_url)); + } + } + + if (!impl) { + impl = new SnapshotCopyOrMoveImpl( + operation_runner(), operation_type_, src_url, dest_url, option_, + validator_factory, + base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, + weak_factory_.GetWeakPtr(), src_url)); + } } // Register the running task. @@ -400,19 +808,24 @@ void CopyOrMoveOperationDelegate::ProcessDirectory( void CopyOrMoveOperationDelegate::PostProcessDirectory( const FileSystemURL& src_url, const StatusCallback& callback) { - if (operation_type_ == OPERATION_COPY) { - callback.Run(base::PLATFORM_FILE_OK); + if (option_ == FileSystemOperation::OPTION_NONE) { + PostProcessDirectoryAfterTouchFile( + src_url, callback, base::PLATFORM_FILE_OK); return; } - DCHECK_EQ(OPERATION_MOVE, operation_type_); + operation_runner()->GetMetadata( + src_url, + base::Bind( + &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata, + weak_factory_.GetWeakPtr(), src_url, callback)); +} - // All files and subdirectories in the directory should be moved here, - // so remove the source directory for finalizing move operation. - operation_runner()->Remove( - src_url, false /* recursive */, - base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove, - weak_factory_.GetWeakPtr(), callback)); +void CopyOrMoveOperationDelegate::OnCancel() { + // Request to cancel all running Copy/Move file. + for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin(); + iter != running_copy_set_.end(); ++iter) + (*iter)->Cancel(); } void CopyOrMoveOperationDelegate::DidCopyOrMoveFile( @@ -475,6 +888,47 @@ void CopyOrMoveOperationDelegate::DidCreateDirectory( callback.Run(error); } +void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata( + const FileSystemURL& src_url, + const StatusCallback& callback, + base::PlatformFileError error, + const base::PlatformFileInfo& file_info) { + if (error != base::PLATFORM_FILE_OK) { + // Ignore the error, and run post process which should run after TouchFile. + PostProcessDirectoryAfterTouchFile( + src_url, callback, base::PLATFORM_FILE_OK); + return; + } + + operation_runner()->TouchFile( + CreateDestURL(src_url), base::Time::Now() /* last access */, + file_info.last_modified, + base::Bind( + &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile, + weak_factory_.GetWeakPtr(), src_url, callback)); +} + +void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile( + const FileSystemURL& src_url, + const StatusCallback& callback, + base::PlatformFileError error) { + // Even if the TouchFile is failed, just ignore it. + + if (operation_type_ == OPERATION_COPY) { + callback.Run(base::PLATFORM_FILE_OK); + return; + } + + DCHECK_EQ(OPERATION_MOVE, operation_type_); + + // All files and subdirectories in the directory should be moved here, + // so remove the source directory for finalizing move operation. + operation_runner()->Remove( + src_url, false /* recursive */, + base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove, + weak_factory_.GetWeakPtr(), callback)); +} + void CopyOrMoveOperationDelegate::DidRemoveSourceForMove( const StatusCallback& callback, base::PlatformFileError error) { diff --git a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.h b/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.h index 77d1a76efc6..2247c87202e 100644 --- a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.h +++ b/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate.h @@ -5,19 +5,28 @@ #ifndef WEBKIT_BROWSER_FILEAPI_COPY_OR_MOVE_OPERATION_DELEGATE_H_ #define WEBKIT_BROWSER_FILEAPI_COPY_OR_MOVE_OPERATION_DELEGATE_H_ +#include <set> #include <stack> #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "webkit/browser/fileapi/recursive_operation_delegate.h" +namespace net { +class DrainableIOBuffer; +class IOBufferWithSize; +} + namespace webkit_blob { +class FileStreamReader; class ShareableFileReference; } namespace fileapi { class CopyOrMoveFileValidator; +class FileStreamWriter; // A delegate class for recursive copy or move operations. class CopyOrMoveOperationDelegate @@ -25,17 +34,68 @@ class CopyOrMoveOperationDelegate public: class CopyOrMoveImpl; typedef FileSystemOperation::CopyProgressCallback CopyProgressCallback; + typedef FileSystemOperation::CopyOrMoveOption CopyOrMoveOption; enum OperationType { OPERATION_COPY, OPERATION_MOVE }; + // Helper to copy a file by reader and writer streams. + // Export for testing. + class WEBKIT_STORAGE_BROWSER_EXPORT StreamCopyHelper { + public: + StreamCopyHelper( + scoped_ptr<webkit_blob::FileStreamReader> reader, + scoped_ptr<FileStreamWriter> writer, + bool need_flush, + int buffer_size, + const FileSystemOperation::CopyFileProgressCallback& + file_progress_callback, + const base::TimeDelta& min_progress_callback_invocation_span); + ~StreamCopyHelper(); + + void Run(const StatusCallback& callback); + + // Requests cancelling. After the cancelling is done, |callback| passed to + // Run will be called. + void Cancel(); + + private: + // Reads the content from the |reader_|. + void Read(const StatusCallback& callback); + void DidRead(const StatusCallback& callback, int result); + + // Writes the content in |buffer| to |writer_|. + void Write(const StatusCallback& callback, + scoped_refptr<net::DrainableIOBuffer> buffer); + void DidWrite(const StatusCallback& callback, + scoped_refptr<net::DrainableIOBuffer> buffer, int result); + + // Flushes the written content in |writer_|. + void Flush(const StatusCallback& callback, bool is_eof); + void DidFlush(const StatusCallback& callback, bool is_eof, int result); + + scoped_ptr<webkit_blob::FileStreamReader> reader_; + scoped_ptr<FileStreamWriter> writer_; + const bool need_flush_; + FileSystemOperation::CopyFileProgressCallback file_progress_callback_; + scoped_refptr<net::IOBufferWithSize> io_buffer_; + int64 num_copied_bytes_; + int64 previous_flush_offset_; + base::Time last_progress_callback_invocation_time_; + base::TimeDelta min_progress_callback_invocation_span_; + bool cancel_requested_; + base::WeakPtrFactory<StreamCopyHelper> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(StreamCopyHelper); + }; + CopyOrMoveOperationDelegate( FileSystemContext* file_system_context, const FileSystemURL& src_root, const FileSystemURL& dest_root, OperationType operation_type, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback); virtual ~CopyOrMoveOperationDelegate(); @@ -50,6 +110,10 @@ class CopyOrMoveOperationDelegate virtual void PostProcessDirectory(const FileSystemURL& url, const StatusCallback& callback) OVERRIDE; + + protected: + virtual void OnCancel() OVERRIDE; + private: void DidCopyOrMoveFile(const FileSystemURL& src_url, const FileSystemURL& dest_url, @@ -65,6 +129,14 @@ class CopyOrMoveOperationDelegate const FileSystemURL& dest_url, const StatusCallback& callback, base::PlatformFileError error); + void PostProcessDirectoryAfterGetMetadata( + const FileSystemURL& src_url, + const StatusCallback& callback, + base::PlatformFileError error, + const base::PlatformFileInfo& file_info); + void PostProcessDirectoryAfterTouchFile(const FileSystemURL& src_url, + const StatusCallback& callback, + base::PlatformFileError error); void DidRemoveSourceForMove(const StatusCallback& callback, base::PlatformFileError error); @@ -75,6 +147,7 @@ class CopyOrMoveOperationDelegate FileSystemURL dest_root_; bool same_file_system_; OperationType operation_type_; + CopyOrMoveOption option_; CopyProgressCallback progress_callback_; StatusCallback callback_; diff --git a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate_unittest.cc b/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate_unittest.cc deleted file mode 100644 index 322341dc40f..00000000000 --- a/chromium/webkit/browser/fileapi/copy_or_move_operation_delegate_unittest.cc +++ /dev/null @@ -1,665 +0,0 @@ -// 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 <map> -#include <queue> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop.h" -#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/copy_or_move_file_validator.h" -#include "webkit/browser/fileapi/file_system_backend.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/browser/fileapi/mock_file_system_context.h" -#include "webkit/browser/fileapi/test_file_set.h" -#include "webkit/browser/fileapi/test_file_system_backend.h" -#include "webkit/browser/quota/mock_quota_manager.h" -#include "webkit/browser/quota/quota_manager.h" -#include "webkit/common/fileapi/file_system_util.h" - -namespace fileapi { - -typedef FileSystemOperation::FileEntryList FileEntryList; - -namespace { - -void ExpectOk(const GURL& origin_url, - const std::string& name, - base::PlatformFileError error) { - ASSERT_EQ(base::PLATFORM_FILE_OK, error); -} - -class TestValidatorFactory : public CopyOrMoveFileValidatorFactory { - public: - // A factory that creates validators that accept everything or nothing. - TestValidatorFactory() {} - virtual ~TestValidatorFactory() {} - - virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator( - const FileSystemURL& /*src_url*/, - const base::FilePath& /*platform_path*/) OVERRIDE { - // Move arg management to TestValidator? - return new TestValidator(true, true, std::string("2")); - } - - private: - class TestValidator : public CopyOrMoveFileValidator { - public: - explicit TestValidator(bool pre_copy_valid, - bool post_copy_valid, - const std::string& reject_string) - : result_(pre_copy_valid ? base::PLATFORM_FILE_OK - : base::PLATFORM_FILE_ERROR_SECURITY), - write_result_(post_copy_valid ? base::PLATFORM_FILE_OK - : base::PLATFORM_FILE_ERROR_SECURITY), - reject_string_(reject_string) { - } - virtual ~TestValidator() {} - - virtual void StartPreWriteValidation( - const ResultCallback& result_callback) OVERRIDE { - // Post the result since a real validator must do work asynchronously. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(result_callback, result_)); - } - - virtual void StartPostWriteValidation( - const base::FilePath& dest_platform_path, - const ResultCallback& result_callback) OVERRIDE { - base::PlatformFileError result = write_result_; - std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe(); - if (unsafe.find(reject_string_) != std::string::npos) { - result = base::PLATFORM_FILE_ERROR_SECURITY; - } - // Post the result since a real validator must do work asynchronously. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(result_callback, result)); - } - - private: - base::PlatformFileError result_; - base::PlatformFileError write_result_; - std::string reject_string_; - - DISALLOW_COPY_AND_ASSIGN(TestValidator); - }; -}; - -// Records CopyProgressCallback invocations. -struct ProgressRecord { - FileSystemOperation::CopyProgressType type; - FileSystemURL source_url; - FileSystemURL dest_url; - int64 size; -}; - -void RecordProgressCallback(std::vector<ProgressRecord>* records, - FileSystemOperation::CopyProgressType type, - const FileSystemURL& source_url, - const FileSystemURL& dest_url, - int64 size) { - ProgressRecord record; - record.type = type; - record.source_url = source_url; - record.dest_url = dest_url; - record.size = size; - records->push_back(record); -} - -} // namespace - -class CopyOrMoveOperationTestHelper { - public: - CopyOrMoveOperationTestHelper( - const GURL& origin, - FileSystemType src_type, - FileSystemType dest_type) - : origin_(origin), - src_type_(src_type), - dest_type_(dest_type) {} - - ~CopyOrMoveOperationTestHelper() { - file_system_context_ = NULL; - quota_manager_proxy_->SimulateQuotaManagerDestroyed(); - quota_manager_ = NULL; - quota_manager_proxy_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - void SetUp() { - SetUp(true, true); - } - - void SetUpNoValidator() { - SetUp(true, false); - } - - void SetUp(bool require_copy_or_move_validator, - bool init_copy_or_move_validator) { - ASSERT_TRUE(base_.CreateUniqueTempDir()); - base::FilePath base_dir = base_.path(); - quota_manager_ = - new quota::MockQuotaManager(false /* is_incognito */, - base_dir, - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - NULL /* special storage policy */); - quota_manager_proxy_ = new quota::MockQuotaManagerProxy( - quota_manager_.get(), base::MessageLoopProxy::current().get()); - file_system_context_ = - CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir); - - // Prepare the origin's root directory. - FileSystemBackend* backend = - file_system_context_->GetFileSystemBackend(src_type_); - backend->OpenFileSystem(origin_, src_type_, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&ExpectOk)); - backend = file_system_context_->GetFileSystemBackend(dest_type_); - if (dest_type_ == kFileSystemTypeTest) { - TestFileSystemBackend* test_backend = - static_cast<TestFileSystemBackend*>(backend); - scoped_ptr<CopyOrMoveFileValidatorFactory> factory( - new TestValidatorFactory); - test_backend->set_require_copy_or_move_validator( - require_copy_or_move_validator); - if (init_copy_or_move_validator) - test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass()); - } - backend->OpenFileSystem(origin_, dest_type_, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&ExpectOk)); - base::RunLoop().RunUntilIdle(); - - // Grant relatively big quota initially. - quota_manager_->SetQuota(origin_, - FileSystemTypeToQuotaStorageType(src_type_), - 1024 * 1024); - quota_manager_->SetQuota(origin_, - FileSystemTypeToQuotaStorageType(dest_type_), - 1024 * 1024); - } - - int64 GetSourceUsage() { - int64 usage = 0; - GetUsageAndQuota(src_type_, &usage, NULL); - return usage; - } - - int64 GetDestUsage() { - int64 usage = 0; - GetUsageAndQuota(dest_type_, &usage, NULL); - return usage; - } - - FileSystemURL SourceURL(const std::string& path) { - return file_system_context_->CreateCrackedFileSystemURL( - origin_, src_type_, base::FilePath::FromUTF8Unsafe(path)); - } - - FileSystemURL DestURL(const std::string& path) { - return file_system_context_->CreateCrackedFileSystemURL( - origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path)); - } - - base::PlatformFileError Copy(const FileSystemURL& src, - const FileSystemURL& dest) { - return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest); - } - - base::PlatformFileError CopyWithProgress( - const FileSystemURL& src, - const FileSystemURL& dest, - const AsyncFileTestHelper::CopyProgressCallback& progress_callback) { - return AsyncFileTestHelper::CopyWithProgress( - file_system_context_.get(), src, dest, progress_callback); - } - - base::PlatformFileError Move(const FileSystemURL& src, - const FileSystemURL& dest) { - return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest); - } - - base::PlatformFileError SetUpTestCaseFiles( - const FileSystemURL& root, - const test::TestCaseRecord* const test_cases, - size_t test_case_size) { - base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; - for (size_t i = 0; i < test_case_size; ++i) { - const test::TestCaseRecord& test_case = test_cases[i]; - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - root.origin(), - root.mount_type(), - root.virtual_path().Append(test_case.path)); - if (test_case.is_directory) - result = CreateDirectory(url); - else - result = CreateFile(url, test_case.data_file_size); - EXPECT_EQ(base::PLATFORM_FILE_OK, result) << url.DebugString(); - if (result != base::PLATFORM_FILE_OK) - return result; - } - return result; - } - - void VerifyTestCaseFiles( - const FileSystemURL& root, - const test::TestCaseRecord* const test_cases, - size_t test_case_size) { - std::map<base::FilePath, const test::TestCaseRecord*> test_case_map; - for (size_t i = 0; i < test_case_size; ++i) { - test_case_map[ - base::FilePath(test_cases[i].path).NormalizePathSeparators()] = - &test_cases[i]; - } - - std::queue<FileSystemURL> directories; - FileEntryList entries; - directories.push(root); - while (!directories.empty()) { - FileSystemURL dir = directories.front(); - directories.pop(); - ASSERT_EQ(base::PLATFORM_FILE_OK, ReadDirectory(dir, &entries)); - for (size_t i = 0; i < entries.size(); ++i) { - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - dir.origin(), - dir.mount_type(), - dir.virtual_path().Append(entries[i].name)); - base::FilePath relative; - root.virtual_path().AppendRelativePath(url.virtual_path(), &relative); - relative = relative.NormalizePathSeparators(); - ASSERT_TRUE(ContainsKey(test_case_map, relative)); - if (entries[i].is_directory) { - EXPECT_TRUE(test_case_map[relative]->is_directory); - directories.push(url); - } else { - EXPECT_FALSE(test_case_map[relative]->is_directory); - EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size)); - } - test_case_map.erase(relative); - } - } - EXPECT_TRUE(test_case_map.empty()); - std::map<base::FilePath, const test::TestCaseRecord*>::const_iterator it; - for (it = test_case_map.begin(); it != test_case_map.end(); ++it) { - LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName(); - } - } - - base::PlatformFileError ReadDirectory(const FileSystemURL& url, - FileEntryList* entries) { - return AsyncFileTestHelper::ReadDirectory( - file_system_context_.get(), url, entries); - } - - base::PlatformFileError CreateDirectory(const FileSystemURL& url) { - return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(), - url); - } - - base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) { - base::PlatformFileError result = - AsyncFileTestHelper::CreateFile(file_system_context_.get(), url); - if (result != base::PLATFORM_FILE_OK) - return result; - return AsyncFileTestHelper::TruncateFile( - file_system_context_.get(), url, size); - } - - bool FileExists(const FileSystemURL& url, int64 expected_size) { - return AsyncFileTestHelper::FileExists( - file_system_context_.get(), url, expected_size); - } - - bool DirectoryExists(const FileSystemURL& url) { - return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(), - url); - } - - private: - void GetUsageAndQuota(FileSystemType type, int64* usage, int64* quota) { - quota::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota( - quota_manager_.get(), origin_, type, usage, quota); - ASSERT_EQ(quota::kQuotaStatusOk, status); - } - - private: - base::ScopedTempDir base_; - - const GURL origin_; - const FileSystemType src_type_; - const FileSystemType dest_type_; - - base::MessageLoop message_loop_; - scoped_refptr<FileSystemContext> file_system_context_; - scoped_refptr<quota::MockQuotaManagerProxy> quota_manager_proxy_; - scoped_refptr<quota::MockQuotaManager> quota_manager_; - - DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper); -}; - -TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source file. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Copy it. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest)); - - // Verify. - ASSERT_TRUE(helper.FileExists(src, 10)); - ASSERT_TRUE(helper.FileExists(dest, 10)); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source file. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Move it. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); - - // Verify. - ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize)); - ASSERT_TRUE(helper.FileExists(dest, 10)); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Copy it. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest)); - - // Verify. - ASSERT_TRUE(helper.DirectoryExists(src)); - ASSERT_TRUE(helper.DirectoryExists(dest)); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Move it. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); - - // Verify. - ASSERT_FALSE(helper.DirectoryExists(src)); - ASSERT_TRUE(helper.DirectoryExists(dest)); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.SetUpTestCaseFiles(src, - test::kRegularTestCases, - test::kRegularTestCaseSize)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Copy it. - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.CopyWithProgress( - src, dest, - AsyncFileTestHelper::CopyProgressCallback())); - - // Verify. - ASSERT_TRUE(helper.DirectoryExists(src)); - ASSERT_TRUE(helper.DirectoryExists(dest)); - - helper.VerifyTestCaseFiles(dest, - test::kRegularTestCases, - test::kRegularTestCaseSize); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - int64 src_initial_usage = helper.GetSourceUsage(); - int64 dest_initial_usage = helper.GetDestUsage(); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.SetUpTestCaseFiles(src, - test::kRegularTestCases, - test::kRegularTestCaseSize)); - int64 src_increase = helper.GetSourceUsage() - src_initial_usage; - - // Move it. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); - - // Verify. - ASSERT_FALSE(helper.DirectoryExists(src)); - ASSERT_TRUE(helper.DirectoryExists(dest)); - - helper.VerifyTestCaseFiles(dest, - test::kRegularTestCases, - test::kRegularTestCaseSize); - - int64 src_new_usage = helper.GetSourceUsage(); - ASSERT_EQ(src_initial_usage, src_new_usage); - - int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; - ASSERT_EQ(src_increase, dest_increase); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, - MoveDirectoryFailPostWriteValidation) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypeTest); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.SetUpTestCaseFiles(src, - test::kRegularTestCases, - test::kRegularTestCaseSize)); - - // Move it. - helper.Move(src, dest); - - // Verify. - ASSERT_TRUE(helper.DirectoryExists(src)); - ASSERT_TRUE(helper.DirectoryExists(dest)); - - test::TestCaseRecord kMoveDirResultCases[] = { - {false, FILE_PATH_LITERAL("file 0"), 38}, - {false, FILE_PATH_LITERAL("file 3"), 0}, - }; - - helper.VerifyTestCaseFiles(dest, - kMoveDirResultCases, - arraysize(kMoveDirResultCases)); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypeTest); - helper.SetUpNoValidator(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - - // Set up a source file. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); - - // The copy attempt should fail with a security error -- getting - // the factory returns a security error, and the copy operation must - // respect that. - ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, helper.Copy(src, dest)); -} - -TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) { - CopyOrMoveOperationTestHelper helper(GURL("http://foo"), - kFileSystemTypeTemporary, - kFileSystemTypePersistent); - helper.SetUp(); - - FileSystemURL src = helper.SourceURL("a"); - FileSystemURL dest = helper.DestURL("b"); - - // Set up a source directory. - ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.SetUpTestCaseFiles(src, - test::kRegularTestCases, - test::kRegularTestCaseSize)); - - std::vector<ProgressRecord> records; - ASSERT_EQ(base::PLATFORM_FILE_OK, - helper.CopyWithProgress(src, dest, - base::Bind(&RecordProgressCallback, - base::Unretained(&records)))); - - // Verify progress callback. - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - - FileSystemURL src_url = helper.SourceURL( - std::string("a/") + base::FilePath(test_case.path).AsUTF8Unsafe()); - FileSystemURL dest_url = helper.DestURL( - std::string("b/") + base::FilePath(test_case.path).AsUTF8Unsafe()); - - // Find the first and last progress record. - size_t begin_index = records.size(); - size_t end_index = records.size(); - for (size_t j = 0; j < records.size(); ++j) { - if (records[j].source_url == src_url) { - if (begin_index == records.size()) - begin_index = j; - end_index = j; - } - } - - // The record should be found. - ASSERT_NE(begin_index, records.size()); - ASSERT_NE(end_index, records.size()); - ASSERT_NE(begin_index, end_index); - - EXPECT_EQ(FileSystemOperation::BEGIN_COPY_ENTRY, - records[begin_index].type); - EXPECT_FALSE(records[begin_index].dest_url.is_valid()); - EXPECT_EQ(FileSystemOperation::END_COPY_ENTRY, records[end_index].type); - EXPECT_EQ(dest_url, records[end_index].dest_url); - - if (test_case.is_directory) { - // For directory copy, the progress shouldn't be interlaced. - EXPECT_EQ(begin_index + 1, end_index); - } else { - // PROGRESS event's size should be assending order. - int64 current_size = 0; - for (size_t j = begin_index + 1; j < end_index; ++j) { - if (records[j].source_url == src_url) { - EXPECT_EQ(FileSystemOperation::PROGRESS, records[j].type); - EXPECT_FALSE(records[j].dest_url.is_valid()); - EXPECT_GE(records[j].size, current_size); - current_size = records[j].size; - } - } - } - } -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/dragged_file_util.cc b/chromium/webkit/browser/fileapi/dragged_file_util.cc index b4536a70f9a..3e81242532f 100644 --- a/chromium/webkit/browser/fileapi/dragged_file_util.cc +++ b/chromium/webkit/browser/fileapi/dragged_file_util.cc @@ -81,7 +81,7 @@ PlatformFileError DraggedFileUtil::GetFileInfo( } base::PlatformFileError error = NativeFileUtil::GetFileInfo(url.path(), file_info); - if (file_util::IsLink(url.path()) && !base::FilePath().IsParent(url.path())) { + if (base::IsLink(url.path()) && !base::FilePath().IsParent(url.path())) { // Don't follow symlinks unless it's the one that are selected by the user. return base::PLATFORM_FILE_ERROR_NOT_FOUND; } diff --git a/chromium/webkit/browser/fileapi/dragged_file_util_unittest.cc b/chromium/webkit/browser/fileapi/dragged_file_util_unittest.cc deleted file mode 100644 index af15af8f9fd..00000000000 --- a/chromium/webkit/browser/fileapi/dragged_file_util_unittest.cc +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <map> -#include <queue> -#include <set> -#include <string> -#include <vector> - -#include "base/file_util.h" -#include "base/files/file_enumerator.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/fileapi/async_file_test_helper.h" -#include "webkit/browser/fileapi/dragged_file_util.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/local_file_util.h" -#include "webkit/browser/fileapi/mock_file_system_context.h" -#include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/browser/fileapi/test_file_set.h" - -namespace fileapi { - -namespace { - -typedef AsyncFileTestHelper::FileEntryList FileEntryList; - -// Used in DraggedFileUtilTest::SimulateDropFiles(). -// Random root paths in which we create each file/directory of the -// RegularTestCases (so that we can simulate a drop with files/directories -// from multiple directories). -static const base::FilePath::CharType* kRootPaths[] = { - FILE_PATH_LITERAL("a"), - FILE_PATH_LITERAL("b/c"), - FILE_PATH_LITERAL("etc"), -}; - -base::FilePath GetTopLevelPath(const base::FilePath& path) { - std::vector<base::FilePath::StringType> components; - path.GetComponents(&components); - return base::FilePath(components[0]); -} - -bool IsDirectoryEmpty(FileSystemContext* context, const FileSystemURL& url) { - FileEntryList entries; - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory(context, url, &entries)); - return entries.empty(); -} - -FileSystemURL GetEntryURL(FileSystemContext* file_system_context, - const FileSystemURL& dir, - const base::FilePath::StringType& name) { - return file_system_context->CreateCrackedFileSystemURL( - dir.origin(), - dir.mount_type(), - dir.virtual_path().Append(name)); -} - -base::FilePath GetRelativeVirtualPath(const FileSystemURL& root, - const FileSystemURL& url) { - if (root.virtual_path().empty()) - return url.virtual_path(); - base::FilePath relative; - const bool success = root.virtual_path().AppendRelativePath( - url.virtual_path(), &relative); - DCHECK(success); - return relative; -} - -FileSystemURL GetOtherURL(FileSystemContext* file_system_context, - const FileSystemURL& root, - const FileSystemURL& other_root, - const FileSystemURL& url) { - return file_system_context->CreateCrackedFileSystemURL( - other_root.origin(), - other_root.mount_type(), - other_root.virtual_path().Append(GetRelativeVirtualPath(root, url))); -} - -} // namespace - -class DraggedFileUtilTest : public testing::Test { - public: - DraggedFileUtilTest() {} - - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - ASSERT_TRUE(partition_dir_.CreateUniqueTempDir()); - file_util_.reset(new DraggedFileUtil()); - - // Register the files/directories of RegularTestCases (with random - // root paths) as dropped files. - SimulateDropFiles(); - - file_system_context_ = CreateFileSystemContextForTesting( - NULL /* quota_manager */, - partition_dir_.path()); - - isolated_context()->AddReference(filesystem_id_); - } - - virtual void TearDown() { - isolated_context()->RemoveReference(filesystem_id_); - } - - protected: - IsolatedContext* isolated_context() const { - return IsolatedContext::GetInstance(); - } - const base::FilePath& root_path() const { - return data_dir_.path(); - } - FileSystemContext* file_system_context() const { - return file_system_context_.get(); - } - FileSystemFileUtil* file_util() const { return file_util_.get(); } - std::string filesystem_id() const { return filesystem_id_; } - - base::FilePath GetTestCasePlatformPath( - const base::FilePath::StringType& path) { - return toplevel_root_map_[GetTopLevelPath(base::FilePath(path))] - .Append(path).NormalizePathSeparators(); - } - - base::FilePath GetTestCaseLocalPath(const base::FilePath& path) { - base::FilePath relative; - if (data_dir_.path().AppendRelativePath(path, &relative)) - return relative; - return path; - } - - FileSystemURL GetFileSystemURL(const base::FilePath& path) const { - base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath( - filesystem_id()).Append(path); - return file_system_context_->CreateCrackedFileSystemURL( - GURL("http://example.com"), - kFileSystemTypeIsolated, - virtual_path); - } - - FileSystemURL GetOtherFileSystemURL(const base::FilePath& path) const { - return file_system_context()->CreateCrackedFileSystemURL( - GURL("http://example.com"), - kFileSystemTypeTemporary, - base::FilePath().AppendASCII("dest").Append(path)); - } - - void VerifyFilesHaveSameContent(const FileSystemURL& url1, - const FileSystemURL& url2) { - // Get the file info and the platform path for url1. - base::PlatformFileInfo info1; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetMetadata( - file_system_context(), url1, &info1)); - base::FilePath platform_path1; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetPlatformPath( - file_system_context(), url1, &platform_path1)); - - // Get the file info and the platform path for url2. - base::PlatformFileInfo info2; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetMetadata( - file_system_context(), url2, &info2)); - base::FilePath platform_path2; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetPlatformPath( - file_system_context(), url2, &platform_path2)); - - // See if file info matches with the other one. - EXPECT_EQ(info1.is_directory, info2.is_directory); - EXPECT_EQ(info1.size, info2.size); - EXPECT_EQ(info1.is_symbolic_link, info2.is_symbolic_link); - EXPECT_NE(platform_path1, platform_path2); - - std::string content1, content2; - EXPECT_TRUE(base::ReadFileToString(platform_path1, &content1)); - EXPECT_TRUE(base::ReadFileToString(platform_path2, &content2)); - EXPECT_EQ(content1, content2); - } - - void VerifyDirectoriesHaveSameContent(const FileSystemURL& root1, - const FileSystemURL& root2) { - base::FilePath root_path1 = root1.path(); - base::FilePath root_path2 = root2.path(); - - FileEntryList entries; - std::queue<FileSystemURL> directories; - - directories.push(root1); - std::set<base::FilePath> file_set1; - while (!directories.empty()) { - FileSystemURL dir = directories.front(); - directories.pop(); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), dir, &entries)); - for (size_t i = 0; i < entries.size(); ++i) { - FileSystemURL url = GetEntryURL(file_system_context(), - dir, entries[i].name); - if (entries[i].is_directory) { - directories.push(url); - continue; - } - file_set1.insert(GetRelativeVirtualPath(root1, url)); - } - } - - directories.push(root2); - while (!directories.empty()) { - FileSystemURL dir = directories.front(); - directories.pop(); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), dir, &entries)); - for (size_t i = 0; i < entries.size(); ++i) { - FileSystemURL url2 = GetEntryURL(file_system_context(), - dir, entries[i].name); - FileSystemURL url1 = GetOtherURL(file_system_context(), - root2, root1, url2); - if (entries[i].is_directory) { - directories.push(url2); - EXPECT_EQ(IsDirectoryEmpty(file_system_context(), url1), - IsDirectoryEmpty(file_system_context(), url2)); - continue; - } - base::FilePath relative = GetRelativeVirtualPath(root2, url2); - EXPECT_TRUE(file_set1.find(relative) != file_set1.end()); - VerifyFilesHaveSameContent(url1, url2); - } - } - } - - scoped_ptr<FileSystemOperationContext> GetOperationContext() { - return make_scoped_ptr( - new FileSystemOperationContext(file_system_context())).Pass(); - } - - - private: - void SimulateDropFiles() { - size_t root_path_index = 0; - - IsolatedContext::FileInfoSet toplevels; - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - base::FilePath path(test_case.path); - base::FilePath toplevel = GetTopLevelPath(path); - - // We create the test case files under one of the kRootPaths - // to simulate a drop with multiple directories. - if (toplevel_root_map_.find(toplevel) == toplevel_root_map_.end()) { - base::FilePath root = root_path().Append( - kRootPaths[(root_path_index++) % arraysize(kRootPaths)]); - toplevel_root_map_[toplevel] = root; - toplevels.AddPath(root.Append(path), NULL); - } - - test::SetUpOneTestCase(toplevel_root_map_[toplevel], test_case); - } - - // Register the toplevel entries. - filesystem_id_ = isolated_context()->RegisterDraggedFileSystem(toplevels); - } - - base::ScopedTempDir data_dir_; - base::ScopedTempDir partition_dir_; - base::MessageLoop message_loop_; - std::string filesystem_id_; - scoped_refptr<FileSystemContext> file_system_context_; - std::map<base::FilePath, base::FilePath> toplevel_root_map_; - scoped_ptr<DraggedFileUtil> file_util_; - DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest); -}; - -TEST_F(DraggedFileUtilTest, BasicTest) { - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i); - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - - // See if we can query the file info via the isolated FileUtil. - // (This should succeed since we have registered all the top-level - // entries of the test cases in SetUp()) - base::PlatformFileInfo info; - base::FilePath platform_path; - FileSystemOperationContext context(file_system_context()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetFileInfo(&context, url, &info, &platform_path)); - - // See if the obtained file info is correct. - if (!test_case.is_directory) - ASSERT_EQ(test_case.data_file_size, info.size); - ASSERT_EQ(test_case.is_directory, info.is_directory); - ASSERT_EQ(GetTestCasePlatformPath(test_case.path), - platform_path.NormalizePathSeparators()); - } -} - -TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) { - static const fileapi::test::TestCaseRecord kUnregisteredCases[] = { - {true, FILE_PATH_LITERAL("nonexistent"), 0}, - {true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0}, - {false, FILE_PATH_LITERAL("nonexistent/false"), 0}, - {false, FILE_PATH_LITERAL("foo"), 30}, - {false, FILE_PATH_LITERAL("bar"), 20}, - }; - - for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) { - SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i); - const test::TestCaseRecord& test_case = kUnregisteredCases[i]; - - // Prepare the test file/directory. - SetUpOneTestCase(root_path(), test_case); - - // Make sure regular GetFileInfo succeeds. - base::PlatformFileInfo info; - ASSERT_TRUE(file_util::GetFileInfo( - root_path().Append(test_case.path), &info)); - if (!test_case.is_directory) - ASSERT_EQ(test_case.data_file_size, info.size); - ASSERT_EQ(test_case.is_directory, info.is_directory); - } - - for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) { - SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i); - const test::TestCaseRecord& test_case = kUnregisteredCases[i]; - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - - // We should not be able to get the valid URL for unregistered files. - ASSERT_FALSE(url.is_valid()); - } -} - -TEST_F(DraggedFileUtilTest, ReadDirectoryTest) { - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - if (!test_case.is_directory) - continue; - - SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i - << ": " << test_case.path); - - // Read entries in the directory to construct the expected results map. - typedef std::map<base::FilePath::StringType, DirectoryEntry> EntryMap; - EntryMap expected_entry_map; - - base::FilePath dir_path = GetTestCasePlatformPath(test_case.path); - base::FileEnumerator file_enum( - dir_path, false /* not recursive */, - base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - base::FilePath current; - while (!(current = file_enum.Next()).empty()) { - base::FileEnumerator::FileInfo file_info = file_enum.GetInfo(); - DirectoryEntry entry; - entry.is_directory = file_info.IsDirectory(); - entry.name = current.BaseName().value(); - entry.size = file_info.GetSize(); - entry.last_modified_time = file_info.GetLastModifiedTime(); - expected_entry_map[entry.name] = entry; - -#if defined(OS_POSIX) - // Creates a symlink for each file/directory. - // They should be ignored by ReadDirectory, so we don't add them - // to expected_entry_map. - file_util::CreateSymbolicLink( - current, - dir_path.Append(current.BaseName().AddExtension( - FILE_PATH_LITERAL("link")))); -#endif - } - - // Perform ReadDirectory in the isolated filesystem. - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - FileEntryList entries; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), url, &entries)); - - EXPECT_EQ(expected_entry_map.size(), entries.size()); - for (size_t i = 0; i < entries.size(); ++i) { - const DirectoryEntry& entry = entries[i]; - EntryMap::iterator found = expected_entry_map.find(entry.name); - EXPECT_TRUE(found != expected_entry_map.end()); - EXPECT_EQ(found->second.name, entry.name); - EXPECT_EQ(found->second.is_directory, entry.is_directory); - EXPECT_EQ(found->second.size, entry.size); - EXPECT_EQ(found->second.last_modified_time.ToDoubleT(), - entry.last_modified_time.ToDoubleT()); - } - } -} - -TEST_F(DraggedFileUtilTest, GetLocalFilePathTest) { - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - - FileSystemOperationContext context(file_system_context()); - - base::FilePath local_file_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetLocalFilePath(&context, url, &local_file_path)); - EXPECT_EQ(GetTestCasePlatformPath(test_case.path).value(), - local_file_path.value()); - } -} - -TEST_F(DraggedFileUtilTest, CopyOutFileTest) { - FileSystemURL src_root = GetFileSystemURL(base::FilePath()); - FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); - - FileEntryList entries; - std::queue<FileSystemURL> directories; - directories.push(src_root); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateDirectory(file_system_context(), - dest_root)); - - while (!directories.empty()) { - FileSystemURL dir = directories.front(); - directories.pop(); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory(file_system_context(), - dir, &entries)); - for (size_t i = 0; i < entries.size(); ++i) { - FileSystemURL src_url = GetEntryURL(file_system_context(), - dir, entries[i].name); - FileSystemURL dest_url = GetOtherURL(file_system_context(), - src_root, dest_root, src_url); - - if (entries[i].is_directory) { - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateDirectory(file_system_context(), - dest_url)); - directories.push(src_url); - continue; - } - SCOPED_TRACE(testing::Message() << "Testing file copy " - << src_url.path().value()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy(file_system_context(), - src_url, dest_url)); - VerifyFilesHaveSameContent(src_url, dest_url); - } - } -} - -TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) { - FileSystemURL src_root = GetFileSystemURL(base::FilePath()); - FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateDirectory(file_system_context(), - dest_root)); - - FileEntryList entries; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory(file_system_context(), - src_root, &entries)); - for (size_t i = 0; i < entries.size(); ++i) { - if (!entries[i].is_directory) - continue; - FileSystemURL src_url = GetEntryURL(file_system_context(), - src_root, entries[i].name); - FileSystemURL dest_url = GetOtherURL(file_system_context(), - src_root, dest_root, src_url); - SCOPED_TRACE(testing::Message() << "Testing file copy " - << src_url.path().value()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy(file_system_context(), - src_url, dest_url)); - VerifyDirectoriesHaveSameContent(src_url, dest_url); - } -} - -TEST_F(DraggedFileUtilTest, TouchTest) { - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - if (test_case.is_directory) - continue; - SCOPED_TRACE(testing::Message() << test_case.path); - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - - base::Time last_access_time = base::Time::FromTimeT(1000); - base::Time last_modified_time = base::Time::FromTimeT(2000); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Touch(GetOperationContext().get(), url, - last_access_time, - last_modified_time)); - - // Verification. - base::PlatformFileInfo info; - base::FilePath platform_path; - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetFileInfo(GetOperationContext().get(), url, - &info, &platform_path)); - EXPECT_EQ(last_access_time.ToTimeT(), info.last_accessed.ToTimeT()); - EXPECT_EQ(last_modified_time.ToTimeT(), info.last_modified.ToTimeT()); - } -} - -TEST_F(DraggedFileUtilTest, TruncateTest) { - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - if (test_case.is_directory) - continue; - - SCOPED_TRACE(testing::Message() << test_case.path); - FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path)); - - // Truncate to 0. - base::PlatformFileInfo info; - base::FilePath platform_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(GetOperationContext().get(), url, 0)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetFileInfo(GetOperationContext().get(), url, - &info, &platform_path)); - EXPECT_EQ(0, info.size); - - // Truncate (extend) to 999. - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(GetOperationContext().get(), url, 999)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetFileInfo(GetOperationContext().get(), url, - &info, &platform_path)); - EXPECT_EQ(999, info.size); - } -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/dump_file_system.cc b/chromium/webkit/browser/fileapi/dump_file_system.cc index 4dd60f26b36..a1ffa4a19c6 100644 --- a/chromium/webkit/browser/fileapi/dump_file_system.cc +++ b/chromium/webkit/browser/fileapi/dump_file_system.cc @@ -48,7 +48,7 @@ namespace { bool g_opt_long; -fileapi::FileSystemType g_opt_fs_type = fileapi::kFileSystemTypePersistent; +const char* g_opt_fs_type = "p"; void ShowMessageAndExit(const std::string& msg) { fprintf(stderr, "%s\n", msg.c_str()); @@ -67,8 +67,7 @@ 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)); + origin_dir = origin_dir.Append(g_opt_fs_type); printf("=== ORIGIN %s %s ===\n", origin_name.c_str(), FilePathToString(origin_dir).c_str()); @@ -117,7 +116,7 @@ static void DumpDirectoryTree(const std::string& origin_name, if (info.is_directory()) { size = static_cast<int64>(children.size()); } else { - file_util::GetFileSize(origin_dir.Append(info.data_path), &size); + base::GetFileSize(origin_dir.Append(info.data_path), &size); } // TODO(hamaji): Modification time? printf("%s%s %"PRId64" %"PRId64" %s\n", @@ -173,11 +172,11 @@ int main(int argc, char* argv[]) { argc--; argv++; } else if (std::string(argv[1]) == "-t") { - g_opt_fs_type = fileapi::kFileSystemTypeTemporary; + g_opt_fs_type = "t"; argc--; argv++; } else if (std::string(argv[1]) == "-s") { - g_opt_fs_type = fileapi::kFileSystemTypeSyncable; + g_opt_fs_type = "s"; argc--; argv++; } else { diff --git a/chromium/webkit/browser/fileapi/external_mount_points.cc b/chromium/webkit/browser/fileapi/external_mount_points.cc index a3ecd4b6678..42b6de7754c 100644 --- a/chromium/webkit/browser/fileapi/external_mount_points.cc +++ b/chromium/webkit/browser/fileapi/external_mount_points.cc @@ -57,16 +57,22 @@ namespace fileapi { class ExternalMountPoints::Instance { public: - Instance(FileSystemType type, const base::FilePath& path) - : type_(type), path_(path.StripTrailingSeparators()) {} + Instance(FileSystemType type, + const base::FilePath& path, + const FileSystemMountOption& mount_option) + : type_(type), + path_(path.StripTrailingSeparators()), + mount_option_(mount_option) {} ~Instance() {} FileSystemType type() const { return type_; } const base::FilePath& path() const { return path_; } + const FileSystemMountOption& mount_option() const { return mount_option_; } private: const FileSystemType type_; const base::FilePath path_; + const FileSystemMountOption mount_option_; DISALLOW_COPY_AND_ASSIGN(Instance); }; @@ -86,14 +92,19 @@ scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() { bool ExternalMountPoints::RegisterFileSystem( const std::string& mount_name, FileSystemType type, + const FileSystemMountOption& mount_option, const base::FilePath& path_in) { + // COPY_SYNC_OPTION_SYNC is only applicable to native local file system. + DCHECK(type == kFileSystemTypeNativeLocal || + mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC); + base::AutoLock locker(lock_); base::FilePath path = NormalizeFilePath(path_in); if (!ValidateNewMountPoint(mount_name, path)) return false; - instance_map_[mount_name] = new Instance(type, path); + instance_map_[mount_name] = new Instance(type, path, mount_option); if (!path.empty()) path_to_name_map_.insert(std::make_pair(path, mount_name)); return true; @@ -128,10 +139,12 @@ bool ExternalMountPoints::GetRegisteredPath( return true; } -bool ExternalMountPoints::CrackVirtualPath(const base::FilePath& virtual_path, - std::string* mount_name, - FileSystemType* type, - base::FilePath* path) const { +bool ExternalMountPoints::CrackVirtualPath( + const base::FilePath& virtual_path, + std::string* mount_name, + FileSystemType* type, + base::FilePath* path, + FileSystemMountOption* mount_option) const { DCHECK(mount_name); DCHECK(path); @@ -165,6 +178,7 @@ bool ExternalMountPoints::CrackVirtualPath(const base::FilePath& virtual_path, if (type) *type = instance->type(); cracked_path = instance->path(); + *mount_option = instance->mount_option(); } for (; component_iter != components.end(); ++component_iter) @@ -233,6 +247,18 @@ FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL( base::FilePath::kSeparators[0] + path.value())); } +void ExternalMountPoints::RevokeAllFileSystems() { + NameToInstance instance_map_copy; + { + base::AutoLock locker(lock_); + instance_map_copy = instance_map_; + instance_map_.clear(); + path_to_name_map_.clear(); + } + STLDeleteContainerPairSecondPointers(instance_map_copy.begin(), + instance_map_copy.end()); +} + ExternalMountPoints::ExternalMountPoints() {} ExternalMountPoints::~ExternalMountPoints() { @@ -256,23 +282,24 @@ FileSystemURL ExternalMountPoints::CrackFileSystemURL( return FileSystemURL( url.origin(), url.mount_type(), url.virtual_path(), url.mount_filesystem_id(), kFileSystemTypeNativeLocal, - url.path(), url.filesystem_id()); + url.path(), url.filesystem_id(), url.mount_option()); #endif } std::string mount_name; FileSystemType cracked_type; base::FilePath cracked_path; + FileSystemMountOption cracked_mount_option; if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type, - &cracked_path)) { + &cracked_path, &cracked_mount_option)) { return FileSystemURL(); } return FileSystemURL( url.origin(), url.mount_type(), url.virtual_path(), !url.filesystem_id().empty() ? url.filesystem_id() : mount_name, - cracked_type, cracked_path, mount_name); + cracked_type, cracked_path, mount_name, cracked_mount_option); } bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name, @@ -314,22 +341,4 @@ bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name, !path.IsParent(potential_child->first); } -ScopedExternalFileSystem::ScopedExternalFileSystem( - const std::string& mount_name, - FileSystemType type, - const base::FilePath& path) - : mount_name_(mount_name) { - ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( - mount_name, type, path); -} - -base::FilePath ScopedExternalFileSystem::GetVirtualRootPath() const { - return ExternalMountPoints::GetSystemInstance()-> - CreateVirtualRootPath(mount_name_); -} - -ScopedExternalFileSystem::~ScopedExternalFileSystem() { - ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(mount_name_); -} - } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/external_mount_points.h b/chromium/webkit/browser/fileapi/external_mount_points.h index 9118443b7b6..9fb3cc36cc3 100644 --- a/chromium/webkit/browser/fileapi/external_mount_points.h +++ b/chromium/webkit/browser/fileapi/external_mount_points.h @@ -13,6 +13,7 @@ #include "base/synchronization/lock.h" #include "webkit/browser/fileapi/mount_points.h" #include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_mount_option.h" #include "webkit/common/fileapi/file_system_types.h" namespace base { @@ -61,6 +62,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT ExternalMountPoints // by calling RevokeFileSystem with |mount_name|. bool RegisterFileSystem(const std::string& mount_name, FileSystemType type, + const FileSystemMountOption& mount_option, const base::FilePath& path); // MountPoints overrides. @@ -68,10 +70,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT ExternalMountPoints virtual bool RevokeFileSystem(const std::string& mount_name) OVERRIDE; virtual bool GetRegisteredPath(const std::string& mount_name, base::FilePath* path) const OVERRIDE; - virtual bool CrackVirtualPath(const base::FilePath& virtual_path, - std::string* mount_name, - FileSystemType* type, - base::FilePath* path) const OVERRIDE; + virtual bool CrackVirtualPath( + const base::FilePath& virtual_path, + std::string* mount_name, + FileSystemType* type, + base::FilePath* path, + FileSystemMountOption* mount_option) const OVERRIDE; virtual FileSystemURL CrackURL(const GURL& url) const OVERRIDE; virtual FileSystemURL CreateCrackedFileSystemURL( const GURL& origin, @@ -101,6 +105,9 @@ class WEBKIT_STORAGE_BROWSER_EXPORT ExternalMountPoints const std::string& mount_name, const base::FilePath& path) const; + // Revoke all registered filesystems. Used only by testing (for clean-ups). + void RevokeAllFileSystems(); + private: friend class base::RefCountedThreadSafe<ExternalMountPoints>; @@ -140,20 +147,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT ExternalMountPoints DISALLOW_COPY_AND_ASSIGN(ExternalMountPoints); }; -// Registers a scoped external filesystem which gets revoked when it scopes out. -class WEBKIT_STORAGE_BROWSER_EXPORT ScopedExternalFileSystem { - public: - ScopedExternalFileSystem(const std::string& mount_name, - FileSystemType type, - const base::FilePath& path); - ~ScopedExternalFileSystem(); - - base::FilePath GetVirtualRootPath() const; - - private: - const std::string mount_name_; -}; - } // namespace fileapi #endif // WEBKIT_BROWSER_FILEAPI_EXTERNAL_MOUNT_POINTS_H_ diff --git a/chromium/webkit/browser/fileapi/external_mount_points_unittest.cc b/chromium/webkit/browser/fileapi/external_mount_points_unittest.cc index cc2acbe2a16..fa5d8d1de46 100644 --- a/chromium/webkit/browser/fileapi/external_mount_points_unittest.cc +++ b/chromium/webkit/browser/fileapi/external_mount_points_unittest.cc @@ -111,6 +111,7 @@ TEST(ExternalMountPointsTest, AddMountPoint) { mount_points->RegisterFileSystem( kTestCases[i].name, fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(kTestCases[i].path))) << "Adding mount point: " << kTestCases[i].name << " with path " << kTestCases[i].path; @@ -136,24 +137,30 @@ TEST(ExternalMountPointsTest, GetVirtualPath) { mount_points->RegisterFileSystem("c", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); // Note that "/a/b/c" < "/a/b/c(1)" < "/a/b/c/". mount_points->RegisterFileSystem("c(1)", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c(1)"))); mount_points->RegisterFileSystem("x", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/z/y/x"))); mount_points->RegisterFileSystem("o", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/m/n/o"))); // A mount point whose name does not match its path base name. mount_points->RegisterFileSystem("mount", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/root/foo"))); // A mount point with an empty path. mount_points->RegisterFileSystem("empty_path", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath()); struct TestCase { @@ -265,15 +272,19 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { mount_points->RegisterFileSystem("c", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); mount_points->RegisterFileSystem("c(1)", fileapi::kFileSystemTypeDrive, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c(1)"))); mount_points->RegisterFileSystem("empty_path", fileapi::kFileSystemTypeSyncable, + fileapi::FileSystemMountOption(), base::FilePath()); mount_points->RegisterFileSystem("mount", fileapi::kFileSystemTypeDrive, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/root"))); // Try cracking invalid GURL. @@ -287,7 +298,9 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { // Try native local which is not cracked. FileSystemURL native_local = mount_points->CreateCrackedFileSystemURL( - kTestOrigin, fileapi::kFileSystemTypeNativeLocal, base::FilePath(FPL("c"))); + kTestOrigin, + fileapi::kFileSystemTypeNativeLocal, + base::FilePath(FPL("c"))); EXPECT_FALSE(native_local.is_valid()); struct TestCase { @@ -355,8 +368,8 @@ TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) { << "Test case index: " << i; EXPECT_EQ(kTestCases[i].expect_type, cracked.type()) << "Test case index: " << i; - EXPECT_EQ(base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(), - cracked.path()) + EXPECT_EQ(base::FilePath( + kTestCases[i].expect_path).NormalizePathSeparators(), cracked.path()) << "Test case index: " << i; EXPECT_EQ(base::FilePath(kTestCases[i].path).NormalizePathSeparators(), cracked.virtual_path()) @@ -376,15 +389,19 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { mount_points->RegisterFileSystem("c", fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c"))); mount_points->RegisterFileSystem("c(1)", fileapi::kFileSystemTypeDrive, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/a/b/c(1)"))); mount_points->RegisterFileSystem("empty_path", fileapi::kFileSystemTypeSyncable, + fileapi::FileSystemMountOption(), base::FilePath()); mount_points->RegisterFileSystem("mount", fileapi::kFileSystemTypeDrive, + fileapi::FileSystemMountOption(), base::FilePath(DRIVE FPL("/root"))); struct TestCase { @@ -440,9 +457,10 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { std::string cracked_name; fileapi::FileSystemType cracked_type; base::FilePath cracked_path; + fileapi::FileSystemMountOption cracked_option; EXPECT_EQ(kTestCases[i].expect_valid, mount_points->CrackVirtualPath(base::FilePath(kTestCases[i].path), - &cracked_name, &cracked_type, &cracked_path)) + &cracked_name, &cracked_type, &cracked_path, &cracked_option)) << "Test case index: " << i; if (!kTestCases[i].expect_valid) @@ -450,13 +468,40 @@ TEST(ExternalMountPointsTest, CrackVirtualPath) { EXPECT_EQ(kTestCases[i].expect_type, cracked_type) << "Test case index: " << i; - EXPECT_EQ(base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(), - cracked_path) + EXPECT_EQ(base::FilePath( + kTestCases[i].expect_path).NormalizePathSeparators(), cracked_path) << "Test case index: " << i; EXPECT_EQ(kTestCases[i].expect_name, cracked_name) << "Test case index: " << i; } } +TEST(ExternalMountPointsTest, MountOption) { + scoped_refptr<fileapi::ExternalMountPoints> mount_points( + fileapi::ExternalMountPoints::CreateRefCounted()); + + mount_points->RegisterFileSystem( + "nosync", + fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(fileapi::COPY_SYNC_OPTION_NO_SYNC), + base::FilePath(DRIVE FPL("/nosync"))); + mount_points->RegisterFileSystem( + "sync", + fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(fileapi::COPY_SYNC_OPTION_SYNC), + base::FilePath(DRIVE FPL("/sync"))); + + std::string name; + fileapi::FileSystemType type; + fileapi::FileSystemMountOption option; + base::FilePath path; + EXPECT_TRUE(mount_points->CrackVirtualPath( + base::FilePath(FPL("nosync/file")), &name, &type, &path, &option)); + EXPECT_EQ(fileapi::COPY_SYNC_OPTION_NO_SYNC, option.copy_sync_option()); + EXPECT_TRUE(mount_points->CrackVirtualPath( + base::FilePath(FPL("sync/file")), &name, &type, &path, &option)); + EXPECT_EQ(fileapi::COPY_SYNC_OPTION_SYNC, option.copy_sync_option()); +} + } // namespace diff --git a/chromium/webkit/browser/fileapi/file_permission_policy.cc b/chromium/webkit/browser/fileapi/file_permission_policy.cc deleted file mode 100644 index 6f1a36c3440..00000000000 --- a/chromium/webkit/browser/fileapi/file_permission_policy.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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 kOpenPepperFilePermissions = base::PLATFORM_FILE_OPEN | - base::PLATFORM_FILE_CREATE | - base::PLATFORM_FILE_OPEN_ALWAYS | - base::PLATFORM_FILE_CREATE_ALWAYS | - base::PLATFORM_FILE_OPEN_TRUNCATED | - base::PLATFORM_FILE_READ | - base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_APPEND | - base::PLATFORM_FILE_EXCLUSIVE_WRITE | - base::PLATFORM_FILE_DELETE_ON_CLOSE | - base::PLATFORM_FILE_WRITE_ATTRIBUTES; - - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_permission_policy.h b/chromium/webkit/browser/fileapi/file_permission_policy.h index 5c9ced54e8a..f1b6376183e 100644 --- a/chromium/webkit/browser/fileapi/file_permission_policy.h +++ b/chromium/webkit/browser/fileapi/file_permission_policy.h @@ -9,11 +9,6 @@ namespace fileapi { -WEBKIT_STORAGE_BROWSER_EXPORT extern const int kReadFilePermissions; -WEBKIT_STORAGE_BROWSER_EXPORT extern const int kWriteFilePermissions; -WEBKIT_STORAGE_BROWSER_EXPORT extern const int kCreateFilePermissions; -WEBKIT_STORAGE_BROWSER_EXPORT extern const int kOpenPepperFilePermissions; - enum FilePermissionPolicy { // Any access should be always denied. FILE_PERMISSION_ALWAYS_DENY = 0x0, diff --git a/chromium/webkit/browser/fileapi/file_system_backend.h b/chromium/webkit/browser/fileapi/file_system_backend.h index 2c8be930b3e..048c86d2a3b 100644 --- a/chromium/webkit/browser/fileapi/file_system_backend.h +++ b/chromium/webkit/browser/fileapi/file_system_backend.h @@ -98,10 +98,10 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemBackend { // This method itself does *not* check if the given path exists and is a // regular file. virtual scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader( - const FileSystemURL& url, - int64 offset, - const base::Time& expected_modification_time, - FileSystemContext* context) const = 0; + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time, + FileSystemContext* context) const = 0; // Creates a new file stream writer for a given filesystem URL |url| with an // offset |offset|. diff --git a/chromium/webkit/browser/fileapi/file_system_context.cc b/chromium/webkit/browser/fileapi/file_system_context.cc index 9098649f245..869410498c9 100644 --- a/chromium/webkit/browser/fileapi/file_system_context.cc +++ b/chromium/webkit/browser/fileapi/file_system_context.cc @@ -23,8 +23,8 @@ #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/isolated_file_system_backend.h" #include "webkit/browser/fileapi/mount_points.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" #include "webkit/browser/fileapi/sandbox_file_system_backend.h" -#include "webkit/browser/fileapi/test_file_system_backend.h" #include "webkit/browser/quota/quota_manager.h" #include "webkit/browser/quota/special_storage_policy.h" #include "webkit/common/fileapi/file_system_info.h" @@ -42,13 +42,6 @@ QuotaClient* CreateQuotaClient( 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); -} void DidGetMetadataForResolveURL( const base::FilePath& path, @@ -87,9 +80,11 @@ int FileSystemContext::GetPermissionPolicy(FileSystemType type) { case kFileSystemTypeDeviceMedia: case kFileSystemTypeDragged: case kFileSystemTypeForTransientFile: + case kFileSystemTypeIphoto: case kFileSystemTypeItunes: case kFileSystemTypeNativeMedia: case kFileSystemTypePicasa: + case kFileSystemTypePluginPrivate: return FILE_PERMISSION_ALWAYS_DENY; // Following types only appear as mount_type, and will not be @@ -131,6 +126,11 @@ FileSystemContext::FileSystemContext( sandbox_backend_(new SandboxFileSystemBackend( sandbox_delegate_.get())), isolated_backend_(new IsolatedFileSystemBackend()), + plugin_private_backend_(new PluginPrivateFileSystemBackend( + file_task_runner, + partition_path, + special_storage_policy, + options)), additional_backends_(additional_backends.Pass()), external_mount_points_(external_mount_points), partition_path_(partition_path), @@ -138,6 +138,7 @@ FileSystemContext::FileSystemContext( operation_runner_(new FileSystemOperationRunner(this)) { RegisterBackend(sandbox_backend_.get()); RegisterBackend(isolated_backend_.get()); + RegisterBackend(plugin_private_backend_.get()); for (ScopedVector<FileSystemBackend>::const_iterator iter = additional_backends_.begin(); @@ -153,6 +154,7 @@ FileSystemContext::FileSystemContext( sandbox_backend_->Initialize(this); isolated_backend_->Initialize(this); + plugin_private_backend_->Initialize(this); for (ScopedVector<FileSystemBackend>::const_iterator iter = additional_backends_.begin(); iter != additional_backends_.end(); ++iter) { @@ -190,6 +192,18 @@ bool FileSystemContext::DeleteDataForOriginOnFileThread( return success; } +scoped_refptr<QuotaReservation> +FileSystemContext::CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type) { + DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread()); + FileSystemBackend* backend = GetFileSystemBackend(type); + if (!backend || !backend->GetQuotaUtil()) + return scoped_refptr<QuotaReservation>(); + return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner( + origin_url, type); +} + void FileSystemContext::Shutdown() { if (!io_task_runner_->RunsTasksOnCurrentThread()) { io_task_runner_->PostTask( @@ -277,16 +291,22 @@ void FileSystemContext::OpenFileSystem( FileSystemType type, OpenFileSystemMode mode, const OpenFileSystemCallback& callback) { + DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(!callback.is_null()); + if (!FileSystemContext::IsSandboxFileSystem(type)) { + // Disallow opening a non-sandboxed filesystem. + callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); + return; + } + FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) { - callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, std::string(), GURL()); + callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); return; } - backend->OpenFileSystem(origin_url, type, mode, - base::Bind(&DidOpenFileSystem, callback)); + backend->OpenFileSystem(origin_url, type, mode, callback); } void FileSystemContext::ResolveURL( @@ -295,6 +315,22 @@ void FileSystemContext::ResolveURL( DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(!callback.is_null()); + if (!FileSystemContext::IsSandboxFileSystem(url.type())) { +#ifdef OS_CHROMEOS + // Do not have to open a non-sandboxed filesystem. + // TODO(nhiroki): For now we assume this path is called only on ChromeOS, + // but this assumption may be broken in the future and we should handle + // more generally. http://crbug.com/304062. + FileSystemInfo info = GetFileSystemInfoForChromeOS(url.origin()); + DidOpenFileSystemForResolveURL( + url, callback, info.root_url, info.name, base::PLATFORM_FILE_OK); + return; +#endif + callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, + FileSystemInfo(), base::FilePath(), false); + return; + } + FileSystemBackend* backend = GetFileSystemBackend(url.type()); if (!backend) { callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, @@ -312,8 +348,11 @@ void FileSystemContext::ResolveURL( void FileSystemContext::DeleteFileSystem( const GURL& origin_url, FileSystemType type, - const DeleteFileSystemCallback& callback) { + const StatusCallback& callback) { + DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(origin_url == origin_url.GetOrigin()); + DCHECK(!callback.is_null()); + FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) { callback.Run(base::PLATFORM_FILE_ERROR_SECURITY); @@ -378,14 +417,17 @@ FileSystemURL FileSystemContext::CreateCrackedFileSystemURL( return CrackFileSystemURL(FileSystemURL(origin, type, path)); } -#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD) +#if defined(OS_CHROMEOS) void FileSystemContext::EnableTemporaryFileSystemInIncognito() { sandbox_backend_->set_enable_temporary_file_system_in_incognito(true); } #endif bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const { -#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD) + // We never support accessing files in isolated filesystems via an URL. + if (url.mount_type() == kFileSystemTypeIsolated) + return false; +#if defined(OS_CHROMEOS) if (url.type() == kFileSystemTypeTemporary && sandbox_backend_->enable_temporary_file_system_in_incognito()) { return true; @@ -394,6 +436,18 @@ bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const { return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type()); } +void FileSystemContext::OpenPluginPrivateFileSystem( + const GURL& origin_url, + FileSystemType type, + const std::string& filesystem_id, + const std::string& plugin_id, + OpenFileSystemMode mode, + const StatusCallback& callback) { + DCHECK(plugin_private_backend_); + plugin_private_backend_->OpenPrivateFileSystem( + origin_url, type, filesystem_id, plugin_id, mode, callback); +} + FileSystemContext::~FileSystemContext() { } @@ -500,12 +554,13 @@ void FileSystemContext::DidOpenFileSystemForResolveURL( filesystem_name, filesystem_root, url.mount_type()); // Extract the virtual path not containing a filesystem type part from |url|. - base::FilePath parent = - base::FilePath::FromUTF8Unsafe(filesystem_root.path()); - base::FilePath child = base::FilePath::FromUTF8Unsafe(url.ToGURL().path()); + base::FilePath parent = CrackURL(filesystem_root).virtual_path(); + base::FilePath child = url.virtual_path(); base::FilePath path; - if (parent != child) { + if (parent.empty()) { + path = child; + } else if (parent != child) { bool result = parent.AppendRelativePath(child, &path); DCHECK(result); } diff --git a/chromium/webkit/browser/fileapi/file_system_context.h b/chromium/webkit/browser/fileapi/file_system_context.h index 21a31b89eb8..2ceb96fb1aa 100644 --- a/chromium/webkit/browser/fileapi/file_system_context.h +++ b/chromium/webkit/browser/fileapi/file_system_context.h @@ -17,6 +17,7 @@ #include "base/sequenced_task_runner_helpers.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/open_file_system_mode.h" +#include "webkit/browser/fileapi/plugin_private_file_system_backend.h" #include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h" #include "webkit/browser/fileapi/task_runner_bound_observer_list.h" #include "webkit/browser/webkit_storage_browser_export.h" @@ -58,6 +59,7 @@ class FileSystemQuotaUtil; class FileSystemURL; class IsolatedFileSystemBackend; class MountPoints; +class QuotaReservation; class SandboxFileSystemBackend; struct DefaultContextDeleter; @@ -103,8 +105,18 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext const base::FilePath& partition_path, const FileSystemOptions& options); + // TODO(nhiroki): Rename *OnFileThread methods since these are no longer on + // FILE thread. bool DeleteDataForOriginOnFileThread(const GURL& origin_url); + // Creates a new QuotaReservation for the given |origin_url| and |type|. + // Returns NULL if |type| does not support quota or reservation fails. + // This should be run on |default_file_task_runner_| and the returned value + // should be destroyed on the runner. + scoped_refptr<QuotaReservation> CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type); + quota::QuotaManagerProxy* quota_manager_proxy() const { return quota_manager_proxy_.get(); } @@ -151,9 +163,10 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext ExternalFileSystemBackend* external_backend() const; // Used for OpenFileSystem. - typedef base::Callback<void(base::PlatformFileError result, + typedef base::Callback<void(const GURL& root, const std::string& name, - const GURL& root)> OpenFileSystemCallback; + base::PlatformFileError result)> + OpenFileSystemCallback; // Used for ResolveURL. typedef base::Callback<void(base::PlatformFileError result, @@ -161,9 +174,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext const base::FilePath& file_path, bool is_directory)> ResolveURLCallback; - // Used for DeleteFileSystem. - typedef base::Callback<void(base::PlatformFileError result)> - DeleteFileSystemCallback; + // Used for DeleteFileSystem and OpenPluginPrivateFileSystem. + typedef base::Callback<void(base::PlatformFileError result)> StatusCallback; // Opens the filesystem for the given |origin_url| and |type|, and dispatches // |callback| on completion. @@ -188,7 +200,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext void DeleteFileSystem( const GURL& origin_url, FileSystemType type, - const DeleteFileSystemCallback& callback); + const StatusCallback& callback); // Creates new FileStreamReader instance to read a file pointed by the given // filesystem URL |url| starting from |offset|. |expected_modification_time| @@ -232,7 +244,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext FileSystemType type, const base::FilePath& path) const; -#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD) +#if defined(OS_CHROMEOS) // Used only on ChromeOS for now. void EnableTemporaryFileSystemInIncognito(); #endif @@ -245,6 +257,16 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext // (E.g. this returns false if the context is created for incognito mode) bool CanServeURLRequest(const FileSystemURL& url) const; + // This must be used to open 'plugin private' filesystem. + // See "plugin_private_file_system_backend.h" for more details. + void OpenPluginPrivateFileSystem( + const GURL& origin_url, + FileSystemType type, + const std::string& filesystem_id, + const std::string& plugin_id, + OpenFileSystemMode mode, + const StatusCallback& callback); + private: typedef std::map<FileSystemType, FileSystemBackend*> FileSystemBackendMap; @@ -255,6 +277,9 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext // For sandbox_backend(). friend class SandboxFileSystemTestHelper; + // For plugin_private_backend(). + friend class PluginPrivateFileSystemBackendTest; + // Deleters. friend struct DefaultContextDeleter; friend class base::DeleteHelper<FileSystemContext>; @@ -300,6 +325,11 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext return sandbox_backend_.get(); } + // Used only by test code. + PluginPrivateFileSystemBackend* plugin_private_backend() const { + return plugin_private_backend_.get(); + } + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; scoped_refptr<base::SequencedTaskRunner> default_file_task_runner_; @@ -312,6 +342,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext scoped_ptr<IsolatedFileSystemBackend> isolated_backend_; // Additional file system backends. + scoped_ptr<PluginPrivateFileSystemBackend> plugin_private_backend_; ScopedVector<FileSystemBackend> additional_backends_; // Registered file system backends. diff --git a/chromium/webkit/browser/fileapi/file_system_context_unittest.cc b/chromium/webkit/browser/fileapi/file_system_context_unittest.cc deleted file mode 100644 index 92256af625b..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_context_unittest.cc +++ /dev/null @@ -1,328 +0,0 @@ -// 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/message_loop.h" -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/fileapi/external_mount_points.h" -#include "webkit/browser/fileapi/file_system_backend.h" -#include "webkit/browser/fileapi/isolated_context.h" -#include "webkit/browser/fileapi/mock_file_system_options.h" -#include "webkit/browser/quota/mock_quota_manager.h" -#include "webkit/browser/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().get(), - base::MessageLoopProxy::current().get(), - storage_policy_.get()); - } - - protected: - FileSystemContext* CreateFileSystemContextForTest( - ExternalMountPoints* external_mount_points) { - return new FileSystemContext(base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - external_mount_points, - storage_policy_.get(), - mock_quota_manager_->proxy(), - ScopedVector<FileSystemBackend>(), - 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.get())); - - // 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/chromium/webkit/browser/fileapi/file_system_dir_url_request_job.cc b/chromium/webkit/browser/fileapi/file_system_dir_url_request_job.cc index 721a228b94e..7589b3732a0 100644 --- a/chromium/webkit/browser/fileapi/file_system_dir_url_request_job.cc +++ b/chromium/webkit/browser/fileapi/file_system_dir_url_request_job.cc @@ -117,7 +117,8 @@ void FileSystemDirURLRequestJob::DidReadDirectory( if (data_.empty()) { base::FilePath relative_path = url_.path(); #if defined(OS_POSIX) - relative_path = base::FilePath(FILE_PATH_LITERAL("/") + relative_path.value()); + relative_path = + base::FilePath(FILE_PATH_LITERAL("/") + relative_path.value()); #endif const base::string16& title = relative_path.LossyDisplayName(); data_.append(net::GetDirectoryListingHeader(title)); diff --git a/chromium/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/chromium/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc deleted file mode 100644 index e43ee4dfa3b..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc +++ /dev/null @@ -1,320 +0,0 @@ -// 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_dir_url_request_job.h" - -#include <string> - -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/format_macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/platform_file.h" -#include "base/run_loop.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/http/http_request_headers.h" -#include "net/url_request/url_request.h" -#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 "third_party/icu/source/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/quota/mock_special_storage_policy.h" - -namespace fileapi { -namespace { - -// We always use the TEMPORARY FileSystem in this test. -static const char kFileSystemURLPrefix[] = - "filesystem:http://remote/temporary/"; - -} // namespace - -class FileSystemDirURLRequestJobTest : public testing::Test { - protected: - FileSystemDirURLRequestJobTest() - : message_loop_(base::MessageLoop::TYPE_IO), // simulate an IO thread - weak_factory_(this) { - } - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - - special_storage_policy_ = new quota::MockSpecialStoragePolicy; - file_system_context_ = CreateFileSystemContextForTesting( - NULL, temp_dir_.path()); - - file_system_context_->OpenFileSystem( - GURL("http://remote/"), kFileSystemTypeTemporary, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&FileSystemDirURLRequestJobTest::OnOpenFileSystem, - weak_factory_.GetWeakPtr())); - base::RunLoop().RunUntilIdle(); - - net::URLRequest::Deprecated::RegisterProtocolFactory( - "filesystem", &FileSystemDirURLRequestJobFactory); - } - - virtual void TearDown() OVERRIDE { - // NOTE: order matters, request must die before delegate - request_.reset(NULL); - delegate_.reset(NULL); - - net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL); - ClearUnusedJob(); - } - - void OnOpenFileSystem(base::PlatformFileError result, - const std::string& name, - const GURL& root_url) { - ASSERT_EQ(base::PLATFORM_FILE_OK, result); - } - - void TestRequestHelper(const GURL& url, bool run_to_completion, - FileSystemContext* file_system_context) { - delegate_.reset(new net::TestDelegate()); - delegate_->set_quit_on_redirect(true); - request_.reset(empty_context_.CreateRequest(url, delegate_.get())); - job_ = new FileSystemDirURLRequestJob( - request_.get(), NULL, file_system_context); - - request_->Start(); - ASSERT_TRUE(request_->is_pending()); // verify that we're starting async - if (run_to_completion) - base::MessageLoop::current()->Run(); - } - - void TestRequest(const GURL& url) { - TestRequestHelper(url, true, file_system_context_.get()); - } - - void TestRequestWithContext(const GURL& url, - FileSystemContext* file_system_context) { - TestRequestHelper(url, true, file_system_context); - } - - void TestRequestNoRun(const GURL& url) { - TestRequestHelper(url, false, file_system_context_.get()); - } - - FileSystemURL CreateURL(const base::FilePath& file_path) { - return file_system_context_->CreateCrackedFileSystemURL( - GURL("http://remote"), - fileapi::kFileSystemTypeTemporary, - file_path); - } - - FileSystemOperationContext* NewOperationContext() { - FileSystemOperationContext* context( - new FileSystemOperationContext(file_system_context_.get())); - context->set_allowed_bytes_growth(1024); - return context; - } - - void CreateDirectory(const base::StringPiece& dir_name) { - base::FilePath path = base::FilePath().AppendASCII(dir_name); - scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->CreateDirectory( - context.get(), - CreateURL(path), - false /* exclusive */, - false /* recursive */)); - } - - void EnsureFileExists(const base::StringPiece file_name) { - base::FilePath path = base::FilePath().AppendASCII(file_name); - scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->EnsureFileExists( - context.get(), CreateURL(path), NULL)); - } - - void TruncateFile(const base::StringPiece file_name, int64 length) { - base::FilePath path = base::FilePath().AppendASCII(file_name); - scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->Truncate( - context.get(), CreateURL(path), length)); - } - - base::PlatformFileError GetFileInfo(const base::FilePath& path, - base::PlatformFileInfo* file_info, - base::FilePath* platform_file_path) { - scoped_ptr<FileSystemOperationContext> context(NewOperationContext()); - return file_util()->GetFileInfo(context.get(), - CreateURL(path), - file_info, platform_file_path); - } - - void VerifyListingEntry(const std::string& entry_line, - const std::string& name, - const std::string& url, - bool is_directory, - int64 size) { -#define STR "([^\"]*)" - icu::UnicodeString pattern("^<script>addRow\\(\"" STR "\",\"" STR - "\",(0|1),\"" STR "\",\"" STR "\"\\);</script>"); -#undef STR - icu::UnicodeString input(entry_line.c_str()); - - UErrorCode status = U_ZERO_ERROR; - icu::RegexMatcher match(pattern, input, 0, status); - - EXPECT_TRUE(match.find()); - EXPECT_EQ(5, match.groupCount()); - EXPECT_EQ(icu::UnicodeString(name.c_str()), match.group(1, status)); - EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status)); - EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"), - match.group(3, status)); - icu::UnicodeString size_string(FormatBytesUnlocalized(size).c_str()); - EXPECT_EQ(size_string, match.group(4, status)); - - base::Time date; - icu::UnicodeString date_ustr(match.group(5, status)); - std::string date_str; - UTF16ToUTF8(date_ustr.getBuffer(), date_ustr.length(), &date_str); - EXPECT_TRUE(base::Time::FromString(date_str.c_str(), &date)); - EXPECT_FALSE(date.is_null()); - } - - GURL CreateFileSystemURL(const std::string path) { - return GURL(kFileSystemURLPrefix + path); - } - - static net::URLRequestJob* FileSystemDirURLRequestJobFactory( - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& scheme) { - DCHECK(job_); - net::URLRequestJob* temp = job_; - job_ = NULL; - return temp; - } - - static void ClearUnusedJob() { - if (job_) { - scoped_refptr<net::URLRequestJob> deleter = job_; - job_ = NULL; - } - } - - FileSystemFileUtil* file_util() { - return file_system_context_->sandbox_delegate()->sync_file_util(); - } - - // Put the message loop at the top, so that it's the last thing deleted. - // Delete all MessageLoopProxy objects before the MessageLoop, to help prevent - // leaks caused by tasks posted during shutdown. - base::MessageLoop message_loop_; - - base::ScopedTempDir temp_dir_; - net::URLRequestContext empty_context_; - scoped_ptr<net::TestDelegate> delegate_; - scoped_ptr<net::URLRequest> request_; - scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_; - scoped_refptr<FileSystemContext> file_system_context_; - base::WeakPtrFactory<FileSystemDirURLRequestJobTest> weak_factory_; - - static net::URLRequestJob* job_; -}; - -// static -net::URLRequestJob* FileSystemDirURLRequestJobTest::job_ = NULL; - -namespace { - -TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) { - CreateDirectory("foo"); - CreateDirectory("foo/bar"); - CreateDirectory("foo/bar/baz"); - - EnsureFileExists("foo/bar/hoge"); - TruncateFile("foo/bar/hoge", 10); - - TestRequest(CreateFileSystemURL("foo/bar/")); - - ASSERT_FALSE(request_->is_pending()); - EXPECT_EQ(1, delegate_->response_started_count()); - EXPECT_FALSE(delegate_->received_data_before_response()); - EXPECT_GT(delegate_->bytes_received(), 0); - - std::istringstream in(delegate_->data_received()); - std::string line; - EXPECT_TRUE(std::getline(in, line)); - -#if defined(OS_WIN) - EXPECT_EQ("<script>start(\"foo\\\\bar\");</script>", line); -#elif defined(OS_POSIX) - EXPECT_EQ("<script>start(\"/foo/bar\");</script>", line); -#endif - - EXPECT_TRUE(std::getline(in, line)); - VerifyListingEntry(line, "hoge", "hoge", false, 10); - - EXPECT_TRUE(std::getline(in, line)); - VerifyListingEntry(line, "baz", "baz", true, 0); -} - -TEST_F(FileSystemDirURLRequestJobTest, InvalidURL) { - TestRequest(GURL("filesystem:/foo/bar/baz")); - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - ASSERT_FALSE(request_->status().is_success()); - EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error()); -} - -TEST_F(FileSystemDirURLRequestJobTest, NoSuchRoot) { - TestRequest(GURL("filesystem:http://remote/persistent/somedir/")); - ASSERT_FALSE(request_->is_pending()); - ASSERT_FALSE(request_->status().is_success()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); -} - -TEST_F(FileSystemDirURLRequestJobTest, NoSuchDirectory) { - TestRequest(CreateFileSystemURL("somedir/")); - ASSERT_FALSE(request_->is_pending()); - ASSERT_FALSE(request_->status().is_success()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); -} - -TEST_F(FileSystemDirURLRequestJobTest, Cancel) { - CreateDirectory("foo"); - TestRequestNoRun(CreateFileSystemURL("foo/")); - // Run StartAsync() and only StartAsync(). - base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); - base::RunLoop().RunUntilIdle(); - // If we get here, success! we didn't crash! -} - -TEST_F(FileSystemDirURLRequestJobTest, Incognito) { - CreateDirectory("foo"); - - scoped_refptr<FileSystemContext> file_system_context = - CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path()); - - TestRequestWithContext(CreateFileSystemURL("/"), - file_system_context.get()); - ASSERT_FALSE(request_->is_pending()); - ASSERT_TRUE(request_->status().is_success()); - - std::istringstream in(delegate_->data_received()); - std::string line; - EXPECT_TRUE(std::getline(in, line)); - EXPECT_FALSE(std::getline(in, line)); - - TestRequestWithContext(CreateFileSystemURL("foo"), - file_system_context.get()); - ASSERT_FALSE(request_->is_pending()); - ASSERT_FALSE(request_->status().is_success()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); -} - -} // namespace (anonymous) -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_file_stream_reader.cc b/chromium/webkit/browser/fileapi/file_system_file_stream_reader.cc index d3697c201e5..f5427b7620f 100644 --- a/chromium/webkit/browser/fileapi/file_system_file_stream_reader.cc +++ b/chromium/webkit/browser/fileapi/file_system_file_stream_reader.cc @@ -32,7 +32,7 @@ FileStreamReader* FileStreamReader::CreateForFileSystemFile( expected_modification_time); } -} // webkit_blob +} // namespace webkit_blob namespace fileapi { diff --git a/chromium/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc b/chromium/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc deleted file mode 100644 index 0ce56585e42..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc +++ /dev/null @@ -1,267 +0,0 @@ -// 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/platform_file.h" -#include "base/run_loop.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/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_file_util.h" -#include "webkit/browser/fileapi/mock_file_system_context.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.get(), 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_->OpenFileSystem( - GURL(kURLOrigin), kFileSystemTypeTemporary, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&OnOpenFileSystem)); - base::RunLoop().RunUntilIdle(); - - WriteFile(kTestFileName, kTestData, kTestDataSize, - &test_file_modification_time_); - } - - virtual void TearDown() OVERRIDE { - base::RunLoop().RunUntilIdle(); - } - - protected: - FileSystemFileStreamReader* CreateFileReader( - const std::string& file_name, - int64 initial_offset, - const base::Time& expected_modification_time) { - return new FileSystemFileStreamReader(file_system_context_.get(), - 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) { - FileSystemURL url = GetFileSystemURL(file_name); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - fileapi::AsyncFileTestHelper::CreateFileWithData( - file_system_context_, url, buf, buf_size)); - - base::PlatformFileInfo file_info; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetMetadata( - file_system_context_, url, &file_info)); - if (modification_time) - *modification_time = file_info.last_modified; - } - - private: - static void OnOpenFileSystem(base::PlatformFileError result, - const std::string& name, - const GURL& root_url) { - 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.get(), 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/chromium/webkit/browser/fileapi/file_system_file_util.h b/chromium/webkit/browser/fileapi/file_system_file_util.h index 13ea0e40a4d..5a4d730b5c8 100644 --- a/chromium/webkit/browser/fileapi/file_system_file_util.h +++ b/chromium/webkit/browser/fileapi/file_system_file_util.h @@ -8,6 +8,7 @@ #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/platform_file.h" +#include "webkit/browser/fileapi/file_system_operation.h" #include "webkit/browser/webkit_storage_browser_export.h" #include "webkit/common/blob/scoped_file.h" @@ -27,6 +28,8 @@ class FileSystemURL; // See http://crbug.com/128136 if you need it. class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemFileUtil { public: + typedef FileSystemOperation::CopyOrMoveOption CopyOrMoveOption; + // It will be implemented by each subclass such as FileSystemFileEnumerator. class WEBKIT_STORAGE_BROWSER_EXPORT AbstractFileEnumerator { public: @@ -56,7 +59,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemFileUtil { // Creates or opens a file with the given flags. // See header comments for AsyncFileUtil::CreateOrOpen() for more details. - // This is used only by Pepper/NaCL File API. + // This is used only by Pepper/NaCl File API. virtual base::PlatformFileError CreateOrOpen( FileSystemOperationContext* context, const FileSystemURL& url, @@ -65,7 +68,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemFileUtil { bool* created) = 0; // Closes the given file handle. - // This is used only for Pepper/NaCL File API. + // This is used only for Pepper/NaCl File API. virtual base::PlatformFileError Close( FileSystemOperationContext* context, base::PlatformFile file) = 0; @@ -129,6 +132,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemFileUtil { // Copies or moves a single file from |src_url| to |dest_url|. // The filesystem type of |src_url| and |dest_url| MUST be same. + // For |option|, please see file_system_operation.h // // This returns: // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| @@ -143,6 +147,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemFileUtil { FileSystemOperationContext* context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, bool copy) = 0; // Copies in a single file from a different filesystem. diff --git a/chromium/webkit/browser/fileapi/file_system_operation.h b/chromium/webkit/browser/fileapi/file_system_operation.h index 99d3b3d6a97..ded87f71ec2 100644 --- a/chromium/webkit/browser/fileapi/file_system_operation.h +++ b/chromium/webkit/browser/fileapi/file_system_operation.h @@ -80,8 +80,7 @@ class FileSystemOperation { typedef base::Callback< void(base::PlatformFileError result, base::PlatformFile file, - const base::Closure& on_close_callback, - base::ProcessHandle peer_handle)> OpenFileCallback; + const base::Closure& on_close_callback)> OpenFileCallback; // Used for ReadDirectoryCallback. typedef std::vector<DirectoryEntry> FileEntryList; @@ -209,6 +208,18 @@ class FileSystemOperation { // set to the copied file size. typedef base::Callback<void(int64 size)> CopyFileProgressCallback; + // The option for copy or move operation. + enum CopyOrMoveOption { + // No additional operation. + OPTION_NONE, + + // Preserves last modified time if possible. If the operation to update + // last modified time is not supported on the file system for the + // destination file, this option would be simply ignored (i.e. Copy would + // be successfully done without preserving last modified time). + OPTION_PRESERVE_LAST_MODIFIED, + }; + // Used for Write(). typedef base::Callback<void(base::PlatformFileError result, int64 bytes, @@ -233,6 +244,8 @@ class FileSystemOperation { // |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. + // |option| specifies the minor behavior of Copy(). See CopyOrMoveOption's + // comment for details. // |progress_callback| is periodically called to report the progress // update. See also the comment of CopyProgressCallback. This callback is // optional. @@ -247,11 +260,14 @@ class FileSystemOperation { // virtual void Copy(const FileSystemURL& src_path, const FileSystemURL& dest_path, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, 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. + // |option| specifies the minor behavior of Copy(). See CopyOrMoveOption's + // comment for details. // // For recursive case this internally creates new FileSystemOperations and // calls: @@ -263,6 +279,7 @@ class FileSystemOperation { // virtual void Move(const FileSystemURL& src_path, const FileSystemURL& dest_path, + CopyOrMoveOption option, const StatusCallback& callback) = 0; // Checks if a directory is present at |path|. @@ -337,13 +354,9 @@ class FileSystemOperation { // 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; // Creates a local snapshot file for a given |path| and returns the @@ -393,6 +406,8 @@ class FileSystemOperation { // 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). + // |option| specifies the minor behavior of Copy(). See CopyOrMoveOption's + // comment for details. // |progress_callback| is periodically called to report the progress // update. See also the comment of CopyFileProgressCallback. This callback is // optional. @@ -408,12 +423,15 @@ class FileSystemOperation { // virtual void CopyFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, 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). + // |option| specifies the minor behavior of Copy(). See CopyOrMoveOption's + // comment for details. // // This returns: // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| @@ -426,6 +444,7 @@ class FileSystemOperation { // virtual void MoveFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) = 0; // Synchronously gets the platform path for the given |url|. diff --git a/chromium/webkit/browser/fileapi/file_system_operation_impl.cc b/chromium/webkit/browser/fileapi/file_system_operation_impl.cc index c9e75eb9acd..180e1205b4e 100644 --- a/chromium/webkit/browser/fileapi/file_system_operation_impl.cc +++ b/chromium/webkit/browser/fileapi/file_system_operation_impl.cc @@ -69,6 +69,7 @@ void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url, void FileSystemOperationImpl::Copy( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback) { DCHECK(SetPendingOperationType(kOperationCopy)); @@ -80,6 +81,7 @@ void FileSystemOperationImpl::Copy( file_system_context(), src_url, dest_url, CopyOrMoveOperationDelegate::OPERATION_COPY, + option, progress_callback, base::Bind(&FileSystemOperationImpl::DidFinishOperation, weak_factory_.GetWeakPtr(), callback))); @@ -88,6 +90,7 @@ void FileSystemOperationImpl::Copy( void FileSystemOperationImpl::Move(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { DCHECK(SetPendingOperationType(kOperationMove)); DCHECK(!recursive_operation_delegate_); @@ -96,6 +99,7 @@ void FileSystemOperationImpl::Move(const FileSystemURL& src_url, file_system_context(), src_url, dest_url, CopyOrMoveOperationDelegate::OPERATION_MOVE, + option, FileSystemOperation::CopyProgressCallback(), base::Bind(&FileSystemOperationImpl::DidFinishOperation, weak_factory_.GetWeakPtr(), callback))); @@ -195,18 +199,14 @@ void FileSystemOperationImpl::TouchFile(const FileSystemURL& url, void FileSystemOperationImpl::OpenFile(const FileSystemURL& url, int file_flags, - base::ProcessHandle peer_handle, const OpenFileCallback& callback) { DCHECK(SetPendingOperationType(kOperationOpenFile)); - peer_handle_ = peer_handle; - if (file_flags & ( - (base::PLATFORM_FILE_ENUMERATE | base::PLATFORM_FILE_TEMPORARY | - base::PLATFORM_FILE_HIDDEN))) { + if (file_flags & + (base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN)) { callback.Run(base::PLATFORM_FILE_ERROR_FAILED, base::kInvalidPlatformFileValue, - base::Closure(), - base::kNullProcessHandle); + base::Closure()); return; } GetUsageAndQuotaThenRunTask( @@ -216,8 +216,7 @@ void FileSystemOperationImpl::OpenFile(const FileSystemURL& url, url, callback, file_flags), base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, base::kInvalidPlatformFileValue, - base::Closure(), - base::kNullProcessHandle)); + base::Closure())); } // We can only get here on a write or truncate that's not yet completed. @@ -284,6 +283,7 @@ void FileSystemOperationImpl::RemoveDirectory( void FileSystemOperationImpl::CopyFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) { DCHECK(SetPendingOperationType(kOperationCopy)); @@ -292,7 +292,7 @@ void FileSystemOperationImpl::CopyFileLocal( GetUsageAndQuotaThenRunTask( dest_url, base::Bind(&FileSystemOperationImpl::DoCopyFileLocal, - weak_factory_.GetWeakPtr(), src_url, dest_url, + weak_factory_.GetWeakPtr(), src_url, dest_url, option, progress_callback, callback), base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); } @@ -300,13 +300,15 @@ void FileSystemOperationImpl::CopyFileLocal( void FileSystemOperationImpl::MoveFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { DCHECK(SetPendingOperationType(kOperationMove)); DCHECK(src_url.IsInSameFileSystem(dest_url)); GetUsageAndQuotaThenRunTask( dest_url, base::Bind(&FileSystemOperationImpl::DoMoveFileLocal, - weak_factory_.GetWeakPtr(), src_url, dest_url, callback), + weak_factory_.GetWeakPtr(), + src_url, dest_url, option, callback), base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); } @@ -329,7 +331,6 @@ FileSystemOperationImpl::FileSystemOperationImpl( : file_system_context_(file_system_context), operation_context_(operation_context.Pass()), async_file_util_(NULL), - peer_handle_(base::kNullProcessHandle), pending_operation_(kOperationNone), weak_factory_(this) { DCHECK(operation_context_.get()); @@ -404,10 +405,11 @@ void FileSystemOperationImpl::DoCreateDirectory( void FileSystemOperationImpl::DoCopyFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) { async_file_util_->CopyFileLocal( - operation_context_.Pass(), src_url, dest_url, progress_callback, + operation_context_.Pass(), src_url, dest_url, option, progress_callback, base::Bind(&FileSystemOperationImpl::DidFinishOperation, weak_factory_.GetWeakPtr(), callback)); } @@ -415,9 +417,10 @@ void FileSystemOperationImpl::DoCopyFileLocal( void FileSystemOperationImpl::DoMoveFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { async_file_util_->MoveFileLocal( - operation_context_.Pass(), src_url, dest_url, + operation_context_.Pass(), src_url, dest_url, option, base::Bind(&FileSystemOperationImpl::DidFinishOperation, weak_factory_.GetWeakPtr(), callback)); } @@ -545,9 +548,7 @@ void FileSystemOperationImpl::DidOpenFile( base::PlatformFileError rv, base::PassPlatformFile file, const base::Closure& on_close_callback) { - if (rv == base::PLATFORM_FILE_OK) - CHECK_NE(base::kNullProcessHandle, peer_handle_); - callback.Run(rv, file.ReleaseValue(), on_close_callback, peer_handle_); + callback.Run(rv, file.ReleaseValue(), on_close_callback); } bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) { diff --git a/chromium/webkit/browser/fileapi/file_system_operation_impl.h b/chromium/webkit/browser/fileapi/file_system_operation_impl.h index 8433479f881..06259950ce2 100644 --- a/chromium/webkit/browser/fileapi/file_system_operation_impl.h +++ b/chromium/webkit/browser/fileapi/file_system_operation_impl.h @@ -40,10 +40,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl const StatusCallback& callback) OVERRIDE; virtual void Copy(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback) OVERRIDE; virtual void Move(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) OVERRIDE; virtual void DirectoryExists(const FileSystemURL& url, const StatusCallback& callback) OVERRIDE; @@ -67,7 +69,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl const StatusCallback& callback) OVERRIDE; virtual void OpenFile(const FileSystemURL& url, int file_flags, - base::ProcessHandle peer_handle, const OpenFileCallback& callback) OVERRIDE; virtual void Cancel(const StatusCallback& cancel_callback) OVERRIDE; virtual void CreateSnapshotFile( @@ -82,10 +83,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl const StatusCallback& callback) OVERRIDE; virtual void CopyFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) OVERRIDE; virtual void MoveFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) OVERRIDE; virtual base::PlatformFileError SyncGetPlatformPath( const FileSystemURL& url, @@ -131,10 +134,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl bool recursive); void DoCopyFileLocal(const FileSystemURL& src, const FileSystemURL& dest, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback); void DoMoveFileLocal(const FileSystemURL& src, const FileSystemURL& dest, + CopyOrMoveOption option, const StatusCallback& callback); void DoCopyInForeignFile(const base::FilePath& src_local_disk_file_path, const FileSystemURL& dest, @@ -189,10 +194,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl StatusCallback cancel_callback_; - // Used only by OpenFile, in order to clone the file handle back to the - // requesting process. - base::ProcessHandle peer_handle_; - // A flag to make sure we call operation only once per instance. OperationType pending_operation_; diff --git a/chromium/webkit/browser/fileapi/file_system_operation_impl_unittest.cc b/chromium/webkit/browser/fileapi/file_system_operation_impl_unittest.cc deleted file mode 100644 index 7443aa066d1..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_operation_impl_unittest.cc +++ /dev/null @@ -1,1210 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "webkit/browser/fileapi/file_system_operation_impl.h" - -#include "base/bind.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/memory/weak_ptr.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.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/file_system_operation_runner.h" -#include "webkit/browser/fileapi/mock_file_change_observer.h" -#include "webkit/browser/fileapi/sandbox_file_system_backend.h" -#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" -#include "webkit/browser/quota/mock_quota_manager.h" -#include "webkit/browser/quota/quota_manager.h" -#include "webkit/common/blob/shareable_file_reference.h" -#include "webkit/common/fileapi/file_system_util.h" - -using quota::QuotaManager; -using quota::QuotaManagerProxy; -using webkit_blob::ShareableFileReference; - -namespace fileapi { - -namespace { - -const int kFileOperationStatusNotSet = 1; - -void AssertFileErrorEq(const tracked_objects::Location& from_here, - base::PlatformFileError expected, - base::PlatformFileError actual) { - ASSERT_EQ(expected, actual) << from_here.ToString(); -} - -} // namespace (anonymous) - -// Test class for FileSystemOperationImpl. -class FileSystemOperationImplTest - : public testing::Test { - public: - FileSystemOperationImplTest() - : status_(kFileOperationStatusNotSet), - weak_factory_(this) {} - - protected: - virtual void SetUp() OVERRIDE { - EXPECT_TRUE(base_.CreateUniqueTempDir()); - change_observers_ = MockFileChangeObserver::CreateList(&change_observer_); - - base::FilePath base_dir = base_.path().AppendASCII("filesystem"); - quota_manager_ = - new quota::MockQuotaManager(false /* is_incognito */, - base_dir, - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - NULL /* special storage policy */); - quota_manager_proxy_ = new quota::MockQuotaManagerProxy( - quota_manager(), base::MessageLoopProxy::current().get()); - sandbox_file_system_.SetUp(base_dir, quota_manager_proxy_.get()); - sandbox_file_system_.AddFileChangeObserver(&change_observer_); - } - - virtual void TearDown() OVERRIDE { - // Let the client go away before dropping a ref of the quota manager proxy. - quota_manager_proxy()->SimulateQuotaManagerDestroyed(); - quota_manager_ = NULL; - quota_manager_proxy_ = NULL; - sandbox_file_system_.TearDown(); - } - - FileSystemOperationRunner* operation_runner() { - return sandbox_file_system_.operation_runner(); - } - - int status() const { return status_; } - const base::PlatformFileInfo& info() const { return info_; } - const base::FilePath& path() const { return path_; } - const std::vector<DirectoryEntry>& entries() const { - return entries_; - } - - const ShareableFileReference* shareable_file_ref() const { - return shareable_file_ref_.get(); - } - - quota::MockQuotaManager* quota_manager() { - return static_cast<quota::MockQuotaManager*>(quota_manager_.get()); - } - - quota::MockQuotaManagerProxy* quota_manager_proxy() { - return static_cast<quota::MockQuotaManagerProxy*>( - quota_manager_proxy_.get()); - } - - FileSystemFileUtil* file_util() { - return sandbox_file_system_.file_util(); - } - - MockFileChangeObserver* change_observer() { - return &change_observer_; - } - - scoped_ptr<FileSystemOperationContext> NewContext() { - FileSystemOperationContext* context = - sandbox_file_system_.NewOperationContext(); - // Grant enough quota for all test cases. - context->set_allowed_bytes_growth(1000000); - return make_scoped_ptr(context); - } - - FileSystemURL URLForPath(const std::string& path) const { - return sandbox_file_system_.CreateURLFromUTF8(path); - } - - base::FilePath PlatformPath(const std::string& path) { - return sandbox_file_system_.GetLocalPath( - base::FilePath::FromUTF8Unsafe(path)); - } - - bool FileExists(const std::string& path) { - return AsyncFileTestHelper::FileExists( - sandbox_file_system_.file_system_context(), URLForPath(path), - AsyncFileTestHelper::kDontCheckSize); - } - - bool DirectoryExists(const std::string& path) { - return AsyncFileTestHelper::DirectoryExists( - sandbox_file_system_.file_system_context(), URLForPath(path)); - } - - FileSystemURL CreateFile(const std::string& path) { - FileSystemURL url = URLForPath(path); - bool created = false; - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->EnsureFileExists(NewContext().get(), - url, &created)); - EXPECT_TRUE(created); - return url; - } - - FileSystemURL CreateDirectory(const std::string& path) { - FileSystemURL url = URLForPath(path); - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->CreateDirectory(NewContext().get(), url, - false /* exclusive */, true)); - return url; - } - - int64 GetFileSize(const std::string& path) { - base::PlatformFileInfo info; - EXPECT_TRUE(file_util::GetFileInfo(PlatformPath(path), &info)); - return info.size; - } - - // Callbacks for recording test results. - FileSystemOperation::StatusCallback RecordStatusCallback() { - return base::Bind(&FileSystemOperationImplTest::DidFinish, - weak_factory_.GetWeakPtr()); - } - - FileSystemOperation::ReadDirectoryCallback - RecordReadDirectoryCallback() { - return base::Bind(&FileSystemOperationImplTest::DidReadDirectory, - weak_factory_.GetWeakPtr()); - } - - FileSystemOperation::GetMetadataCallback RecordMetadataCallback() { - return base::Bind(&FileSystemOperationImplTest::DidGetMetadata, - weak_factory_.GetWeakPtr()); - } - - FileSystemOperation::SnapshotFileCallback RecordSnapshotFileCallback() { - return base::Bind(&FileSystemOperationImplTest::DidCreateSnapshotFile, - weak_factory_.GetWeakPtr()); - } - - void DidFinish(base::PlatformFileError status) { - status_ = status; - } - - void DidReadDirectory( - base::PlatformFileError status, - const std::vector<DirectoryEntry>& entries, - bool /* has_more */) { - entries_ = entries; - status_ = status; - } - - void DidGetMetadata(base::PlatformFileError status, - const base::PlatformFileInfo& info) { - info_ = info; - status_ = status; - } - - void DidCreateSnapshotFile( - base::PlatformFileError status, - const base::PlatformFileInfo& info, - const base::FilePath& platform_path, - const scoped_refptr<ShareableFileReference>& shareable_file_ref) { - info_ = info; - path_ = platform_path; - status_ = status; - shareable_file_ref_ = shareable_file_ref; - } - - int64 GetDataSizeOnDisk() { - return sandbox_file_system_.ComputeCurrentOriginUsage() - - sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage(); - } - - void GetUsageAndQuota(int64* usage, int64* quota) { - quota::QuotaStatusCode status = - AsyncFileTestHelper::GetUsageAndQuota(quota_manager_.get(), - sandbox_file_system_.origin(), - sandbox_file_system_.type(), - usage, - quota); - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(quota::kQuotaStatusOk, status); - } - - int64 ComputePathCost(const FileSystemURL& url) { - int64 base_usage; - GetUsageAndQuota(&base_usage, NULL); - - AsyncFileTestHelper::CreateFile( - sandbox_file_system_.file_system_context(), url); - operation_runner()->Remove(url, false /* recursive */, - base::Bind(&AssertFileErrorEq, FROM_HERE, - base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - change_observer()->ResetCount(); - - int64 total_usage; - GetUsageAndQuota(&total_usage, NULL); - return total_usage - base_usage; - } - - void GrantQuotaForCurrentUsage() { - int64 usage; - GetUsageAndQuota(&usage, NULL); - quota_manager()->SetQuota(sandbox_file_system_.origin(), - sandbox_file_system_.storage_type(), - usage); - } - - int64 GetUsage() { - int64 usage = 0; - GetUsageAndQuota(&usage, NULL); - return usage; - } - - void AddQuota(int64 quota_delta) { - int64 quota; - GetUsageAndQuota(NULL, "a); - quota_manager()->SetQuota(sandbox_file_system_.origin(), - sandbox_file_system_.storage_type(), - quota + quota_delta); - } - - base::MessageLoop message_loop_; - scoped_refptr<QuotaManager> quota_manager_; - scoped_refptr<QuotaManagerProxy> quota_manager_proxy_; - - // Common temp base for nondestructive uses. - base::ScopedTempDir base_; - - SandboxFileSystemTestHelper sandbox_file_system_; - - // For post-operation status. - int status_; - base::PlatformFileInfo info_; - base::FilePath path_; - std::vector<DirectoryEntry> entries_; - scoped_refptr<ShareableFileReference> shareable_file_ref_; - - MockFileChangeObserver change_observer_; - ChangeObserverList change_observers_; - - base::WeakPtrFactory<FileSystemOperationImplTest> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplTest); -}; - -TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDoesntExist) { - change_observer()->ResetCount(); - operation_runner()->Move(URLForPath("a"), URLForPath("b"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveFailureContainsPath) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("src/dest")); - - operation_runner()->Move(src_dir, dest_dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDirExistsDestFile) { - // Src exists and is dir. Dest is a file. - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - FileSystemURL dest_file(CreateFile("dest/file")); - - operation_runner()->Move(src_dir, dest_file, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, - TestMoveFailureSrcFileExistsDestNonEmptyDir) { - // Src exists and is a directory. Dest is a non-empty directory. - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - FileSystemURL dest_file(CreateFile("dest/file")); - - operation_runner()->Move(src_dir, dest_dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcFileExistsDestDir) { - // Src exists and is a file. Dest is a directory. - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL src_file(CreateFile("src/file")); - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Move(src_file, dest_dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveFailureDestParentDoesntExist) { - // Dest. parent path does not exist. - FileSystemURL src_dir(CreateDirectory("src")); - operation_runner()->Move(src_dir, URLForPath("nonexistent/deset"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndOverwrite) { - FileSystemURL src_file(CreateFile("src")); - FileSystemURL dest_file(CreateFile("dest")); - - operation_runner()->Move(src_file, dest_file, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("dest")); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndNew) { - FileSystemURL src_file(CreateFile("src")); - - operation_runner()->Move(src_file, URLForPath("new"), RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("new")); - - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndOverwrite) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Move(src_dir, dest_dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(DirectoryExists("src")); - - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - EXPECT_EQ(2, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Make sure we've overwritten but not moved the source under the |dest_dir|. - EXPECT_TRUE(DirectoryExists("dest")); - EXPECT_FALSE(DirectoryExists("dest/src")); -} - -TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndNew) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Move(src_dir, URLForPath("dest/new"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(DirectoryExists("src")); - EXPECT_TRUE(DirectoryExists("dest/new")); - - EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirRecursive) { - FileSystemURL src_dir(CreateDirectory("src")); - CreateDirectory("src/dir"); - CreateFile("src/dir/sub"); - - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Move(src_dir, dest_dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(DirectoryExists("dest/dir")); - EXPECT_TRUE(FileExists("dest/dir/sub")); - - EXPECT_EQ(3, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDoesntExist) { - operation_runner()->Copy(URLForPath("a"), URLForPath("b"), - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureContainsPath) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("src/dir")); - - operation_runner()->Copy(src_dir, dest_dir, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDirExistsDestFile) { - // Src exists and is dir. Dest is a file. - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - FileSystemURL dest_file(CreateFile("dest/file")); - - operation_runner()->Copy(src_dir, dest_file, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, - TestCopyFailureSrcFileExistsDestNonEmptyDir) { - // Src exists and is a directory. Dest is a non-empty directory. - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - FileSystemURL dest_file(CreateFile("dest/file")); - - operation_runner()->Copy(src_dir, dest_dir, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcFileExistsDestDir) { - // Src exists and is a file. Dest is a directory. - FileSystemURL src_file(CreateFile("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Copy(src_file, dest_dir, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureDestParentDoesntExist) { - // Dest. parent path does not exist. - FileSystemURL src_dir(CreateDirectory("src")); - - operation_runner()->Copy(src_dir, URLForPath("nonexistent/dest"), - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyFailureByQuota) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL src_file(CreateFile("src/file")); - FileSystemURL dest_dir(CreateDirectory("dest")); - operation_runner()->Truncate(src_file, 6, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_EQ(6, GetFileSize("src/file")); - - FileSystemURL dest_file(URLForPath("dest/file")); - int64 dest_path_cost = ComputePathCost(dest_file); - GrantQuotaForCurrentUsage(); - AddQuota(6 + dest_path_cost - 1); - - operation_runner()->Copy(src_file, dest_file, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status()); - EXPECT_FALSE(FileExists("dest/file")); -} - -TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndOverwrite) { - FileSystemURL src_file(CreateFile("src")); - FileSystemURL dest_file(CreateFile("dest")); - - operation_runner()->Copy(src_file, dest_file, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("dest")); - EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndNew) { - FileSystemURL src_file(CreateFile("src")); - - operation_runner()->Copy(src_file, URLForPath("new"), - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("new")); - EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count()); - - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndOverwrite) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Copy(src_dir, dest_dir, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - - // Make sure we've overwritten but not copied the source under the |dest_dir|. - EXPECT_TRUE(DirectoryExists("dest")); - EXPECT_FALSE(DirectoryExists("dest/src")); - EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 3); - - EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndNew) { - FileSystemURL src_dir(CreateDirectory("src")); - FileSystemURL dest_dir_new(URLForPath("dest")); - - operation_runner()->Copy(src_dir, dest_dir_new, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(DirectoryExists("dest")); - EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 2); - - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirRecursive) { - FileSystemURL src_dir(CreateDirectory("src")); - CreateDirectory("src/dir"); - CreateFile("src/dir/sub"); - - FileSystemURL dest_dir(CreateDirectory("dest")); - - operation_runner()->Copy(src_dir, dest_dir, - FileSystemOperationRunner::CopyProgressCallback(), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(DirectoryExists("dest/dir")); - EXPECT_TRUE(FileExists("dest/dir/sub")); - - // For recursive copy we may record multiple read access. - EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 1); - - EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileSuccess) { - base::FilePath src_local_disk_file_path; - file_util::CreateTemporaryFile(&src_local_disk_file_path); - const char test_data[] = "foo"; - int data_size = ARRAYSIZE_UNSAFE(test_data); - file_util::WriteFile(src_local_disk_file_path, test_data, data_size); - - FileSystemURL dest_dir(CreateDirectory("dest")); - - int64 before_usage; - GetUsageAndQuota(&before_usage, NULL); - - // Check that the file copied and corresponding usage increased. - operation_runner()->CopyInForeignFile(src_local_disk_file_path, - URLForPath("dest/file"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - - EXPECT_EQ(1, change_observer()->create_file_count()); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("dest/file")); - int64 after_usage; - GetUsageAndQuota(&after_usage, NULL); - EXPECT_GT(after_usage, before_usage); - - // Compare contents of src and copied file. - char buffer[100]; - EXPECT_EQ(data_size, file_util::ReadFile(PlatformPath("dest/file"), - buffer, data_size)); - for (int i = 0; i < data_size; ++i) - EXPECT_EQ(test_data[i], buffer[i]); -} - -TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileFailureByQuota) { - base::FilePath src_local_disk_file_path; - file_util::CreateTemporaryFile(&src_local_disk_file_path); - const char test_data[] = "foo"; - file_util::WriteFile(src_local_disk_file_path, test_data, - ARRAYSIZE_UNSAFE(test_data)); - - FileSystemURL dest_dir(CreateDirectory("dest")); - - GrantQuotaForCurrentUsage(); - operation_runner()->CopyInForeignFile(src_local_disk_file_path, - URLForPath("dest/file"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(FileExists("dest/file")); - EXPECT_EQ(0, change_observer()->create_file_count()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateFileFailure) { - // Already existing file and exclusive true. - FileSystemURL file(CreateFile("file")); - operation_runner()->CreateFile(file, true, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileExists) { - // Already existing file and exclusive false. - FileSystemURL file(CreateFile("file")); - operation_runner()->CreateFile(file, false, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("file")); - - // The file was already there; did nothing. - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessExclusive) { - // File doesn't exist but exclusive is true. - operation_runner()->CreateFile(URLForPath("new"), true, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(FileExists("new")); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileDoesntExist) { - // Non existing file. - operation_runner()->CreateFile(URLForPath("nonexistent"), false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); -} - -TEST_F(FileSystemOperationImplTest, - TestCreateDirFailureDestParentDoesntExist) { - // Dest. parent path does not exist. - operation_runner()->CreateDirectory( - URLForPath("nonexistent/dir"), false, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateDirFailureDirExists) { - // Exclusive and dir existing at path. - FileSystemURL dir(CreateDirectory("dir")); - operation_runner()->CreateDirectory(dir, true, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateDirFailureFileExists) { - // Exclusive true and file existing at path. - FileSystemURL file(CreateFile("file")); - operation_runner()->CreateDirectory(file, true, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateDirSuccess) { - // Dir exists and exclusive is false. - FileSystemURL dir(CreateDirectory("dir")); - operation_runner()->CreateDirectory(dir, false, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Dir doesn't exist. - operation_runner()->CreateDirectory(URLForPath("new"), false, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(DirectoryExists("new")); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateDirSuccessExclusive) { - // Dir doesn't exist. - operation_runner()->CreateDirectory(URLForPath("new"), true, false, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(DirectoryExists("new")); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataFailure) { - operation_runner()->GetMetadata(URLForPath("nonexistent"), - RecordMetadataCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - - operation_runner()->FileExists(URLForPath("nonexistent"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - - operation_runner()->DirectoryExists(URLForPath("nonexistent"), - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataSuccess) { - FileSystemURL dir(CreateDirectory("dir")); - FileSystemURL file(CreateFile("dir/file")); - int read_access = 0; - - operation_runner()->DirectoryExists(dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - ++read_access; - - operation_runner()->GetMetadata(dir, RecordMetadataCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(info().is_directory); - ++read_access; - - operation_runner()->FileExists(file, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - ++read_access; - - operation_runner()->GetMetadata(file, RecordMetadataCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(info().is_directory); - ++read_access; - - EXPECT_EQ(read_access, - quota_manager_proxy()->notify_storage_accessed_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestTypeMismatchErrors) { - FileSystemURL dir(CreateDirectory("dir")); - operation_runner()->FileExists(dir, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, status()); - - FileSystemURL file(CreateFile("file")); - operation_runner()->DirectoryExists(file, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status()); -} - -TEST_F(FileSystemOperationImplTest, TestReadDirFailure) { - // Path doesn't exist - operation_runner()->ReadDirectory(URLForPath("nonexistent"), - RecordReadDirectoryCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - - // File exists. - FileSystemURL file(CreateFile("file")); - operation_runner()->ReadDirectory(file, RecordReadDirectoryCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestReadDirSuccess) { - // parent_dir - // | | - // child_dir child_file - // Verify reading parent_dir. - FileSystemURL parent_dir(CreateDirectory("dir")); - FileSystemURL child_dir(CreateDirectory("dir/child_dir")); - FileSystemURL child_file(CreateFile("dir/child_file")); - - operation_runner()->ReadDirectory(parent_dir, RecordReadDirectoryCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_EQ(2u, entries().size()); - - for (size_t i = 0; i < entries().size(); ++i) { - if (entries()[i].is_directory) - EXPECT_EQ(FILE_PATH_LITERAL("child_dir"), entries()[i].name); - else - EXPECT_EQ(FILE_PATH_LITERAL("child_file"), entries()[i].name); - } - EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestRemoveFailure) { - // Path doesn't exist. - operation_runner()->Remove(URLForPath("nonexistent"), false /* recursive */, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - - // It's an error to try to remove a non-empty directory if recursive flag - // is false. - // parent_dir - // | | - // child_dir child_file - // Verify deleting parent_dir. - FileSystemURL parent_dir(CreateDirectory("dir")); - FileSystemURL child_dir(CreateDirectory("dir/child_dir")); - FileSystemURL child_file(CreateFile("dir/child_file")); - - operation_runner()->Remove(parent_dir, false /* recursive */, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestRemoveSuccess) { - FileSystemURL empty_dir(CreateDirectory("empty_dir")); - EXPECT_TRUE(DirectoryExists("empty_dir")); - operation_runner()->Remove(empty_dir, false /* recursive */, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(DirectoryExists("empty_dir")); - - EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestRemoveSuccessRecursive) { - // Removing a non-empty directory with recursive flag == true should be ok. - // parent_dir - // | | - // child_dir child_files - // | - // child_files - // - // Verify deleting parent_dir. - FileSystemURL parent_dir(CreateDirectory("dir")); - for (int i = 0; i < 8; ++i) - CreateFile(base::StringPrintf("dir/file-%d", i)); - FileSystemURL child_dir(CreateDirectory("dir/child_dir")); - for (int i = 0; i < 8; ++i) - CreateFile(base::StringPrintf("dir/child_dir/file-%d", i)); - - operation_runner()->Remove(parent_dir, true /* recursive */, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(DirectoryExists("parent_dir")); - - EXPECT_EQ(2, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(16, change_observer()->get_and_reset_remove_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(FileSystemOperationImplTest, TestTruncate) { - FileSystemURL file(CreateFile("file")); - base::FilePath platform_path = PlatformPath("file"); - - char test_data[] = "test data"; - int data_size = static_cast<int>(sizeof(test_data)); - EXPECT_EQ(data_size, - file_util::WriteFile(platform_path, test_data, data_size)); - - // Check that its length is the size of the data written. - operation_runner()->GetMetadata(file, RecordMetadataCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(info().is_directory); - EXPECT_EQ(data_size, info().size); - - // Extend the file by truncating it. - int length = 17; - operation_runner()->Truncate(file, length, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Check that its length is now 17 and that it's all zeroes after the test - // data. - EXPECT_EQ(length, GetFileSize("file")); - char data[100]; - EXPECT_EQ(length, file_util::ReadFile(platform_path, data, length)); - for (int i = 0; i < length; ++i) { - if (i < static_cast<int>(sizeof(test_data))) - EXPECT_EQ(test_data[i], data[i]); - else - EXPECT_EQ(0, data[i]); - } - - // Shorten the file by truncating it. - length = 3; - operation_runner()->Truncate(file, length, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Check that its length is now 3 and that it contains only bits of test data. - EXPECT_EQ(length, GetFileSize("file")); - EXPECT_EQ(length, file_util::ReadFile(platform_path, data, length)); - for (int i = 0; i < length; ++i) - EXPECT_EQ(test_data[i], data[i]); - - // Truncate is not a 'read' access. (Here expected access count is 1 - // since we made 1 read access for GetMetadata.) - EXPECT_EQ(1, quota_manager_proxy()->notify_storage_accessed_count()); -} - -TEST_F(FileSystemOperationImplTest, TestTruncateFailureByQuota) { - FileSystemURL dir(CreateDirectory("dir")); - FileSystemURL file(CreateFile("dir/file")); - - GrantQuotaForCurrentUsage(); - AddQuota(10); - - operation_runner()->Truncate(file, 10, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - EXPECT_EQ(10, GetFileSize("dir/file")); - - operation_runner()->Truncate(file, 11, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - EXPECT_EQ(10, GetFileSize("dir/file")); -} - -TEST_F(FileSystemOperationImplTest, TestTouchFile) { - FileSystemURL file(CreateFile("file")); - base::FilePath platform_path = PlatformPath("file"); - - base::PlatformFileInfo info; - EXPECT_TRUE(file_util::GetFileInfo(platform_path, &info)); - EXPECT_FALSE(info.is_directory); - EXPECT_EQ(0, info.size); - const base::Time last_modified = info.last_modified; - const base::Time last_accessed = info.last_accessed; - - const base::Time new_modified_time = base::Time::UnixEpoch(); - const base::Time new_accessed_time = new_modified_time + - base::TimeDelta::FromHours(77); - ASSERT_NE(last_modified, new_modified_time); - ASSERT_NE(last_accessed, new_accessed_time); - - operation_runner()->TouchFile(file, new_accessed_time, new_modified_time, - RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - EXPECT_TRUE(file_util::GetFileInfo(platform_path, &info)); - // We compare as time_t here to lower our resolution, to avoid false - // negatives caused by conversion to the local filesystem's native - // representation and back. - EXPECT_EQ(new_modified_time.ToTimeT(), info.last_modified.ToTimeT()); - EXPECT_EQ(new_accessed_time.ToTimeT(), info.last_accessed.ToTimeT()); -} - -TEST_F(FileSystemOperationImplTest, TestCreateSnapshotFile) { - FileSystemURL dir(CreateDirectory("dir")); - - // Create a file for the testing. - operation_runner()->DirectoryExists(dir, RecordStatusCallback()); - FileSystemURL file(CreateFile("dir/file")); - operation_runner()->FileExists(file, RecordStatusCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - - // See if we can get a 'snapshot' file info for the file. - // Since FileSystemOperationImpl assumes the file exists in the local - // directory it should just returns the same metadata and platform_path - // as the file itself. - operation_runner()->CreateSnapshotFile(file, RecordSnapshotFileCallback()); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_FALSE(info().is_directory); - EXPECT_EQ(PlatformPath("dir/file"), path()); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // The FileSystemOpration implementation does not create a - // shareable file reference. - EXPECT_EQ(NULL, shareable_file_ref()); -} - -TEST_F(FileSystemOperationImplTest, - TestMoveSuccessSrcDirRecursiveWithQuota) { - FileSystemURL src(CreateDirectory("src")); - int src_path_cost = GetUsage(); - - FileSystemURL dest(CreateDirectory("dest")); - FileSystemURL child_file1(CreateFile("src/file1")); - FileSystemURL child_file2(CreateFile("src/file2")); - FileSystemURL child_dir(CreateDirectory("src/dir")); - FileSystemURL grandchild_file1(CreateFile("src/dir/file1")); - FileSystemURL grandchild_file2(CreateFile("src/dir/file2")); - - int total_path_cost = GetUsage(); - EXPECT_EQ(0, GetDataSizeOnDisk()); - - operation_runner()->Truncate( - child_file1, 5000, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - child_file2, 400, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - grandchild_file1, 30, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - grandchild_file2, 2, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - - const int64 all_file_size = 5000 + 400 + 30 + 2; - EXPECT_EQ(all_file_size, GetDataSizeOnDisk()); - EXPECT_EQ(all_file_size + total_path_cost, GetUsage()); - - operation_runner()->Move( - src, dest, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(DirectoryExists("src/dir")); - EXPECT_FALSE(FileExists("src/dir/file2")); - EXPECT_TRUE(DirectoryExists("dest/dir")); - EXPECT_TRUE(FileExists("dest/dir/file2")); - - EXPECT_EQ(all_file_size, GetDataSizeOnDisk()); - EXPECT_EQ(all_file_size + total_path_cost - src_path_cost, - GetUsage()); -} - -TEST_F(FileSystemOperationImplTest, - TestCopySuccessSrcDirRecursiveWithQuota) { - FileSystemURL src(CreateDirectory("src")); - FileSystemURL dest1(CreateDirectory("dest1")); - FileSystemURL dest2(CreateDirectory("dest2")); - - int64 usage = GetUsage(); - FileSystemURL child_file1(CreateFile("src/file1")); - FileSystemURL child_file2(CreateFile("src/file2")); - FileSystemURL child_dir(CreateDirectory("src/dir")); - int64 child_path_cost = GetUsage() - usage; - usage += child_path_cost; - - FileSystemURL grandchild_file1(CreateFile("src/dir/file1")); - FileSystemURL grandchild_file2(CreateFile("src/dir/file2")); - int64 total_path_cost = GetUsage(); - int64 grandchild_path_cost = total_path_cost - usage; - - EXPECT_EQ(0, GetDataSizeOnDisk()); - - operation_runner()->Truncate( - child_file1, 8000, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - child_file2, 700, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - grandchild_file1, 60, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - operation_runner()->Truncate( - grandchild_file2, 5, - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - - const int64 child_file_size = 8000 + 700; - const int64 grandchild_file_size = 60 + 5; - const int64 all_file_size = child_file_size + grandchild_file_size; - int64 expected_usage = all_file_size + total_path_cost; - - usage = GetUsage(); - EXPECT_EQ(all_file_size, GetDataSizeOnDisk()); - EXPECT_EQ(expected_usage, usage); - - // Copy src to dest1. - operation_runner()->Copy( - src, dest1, - FileSystemOperationRunner::CopyProgressCallback(), - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - - expected_usage += all_file_size + child_path_cost + grandchild_path_cost; - EXPECT_TRUE(DirectoryExists("src/dir")); - EXPECT_TRUE(FileExists("src/dir/file2")); - EXPECT_TRUE(DirectoryExists("dest1/dir")); - EXPECT_TRUE(FileExists("dest1/dir/file2")); - - EXPECT_EQ(2 * all_file_size, GetDataSizeOnDisk()); - EXPECT_EQ(expected_usage, GetUsage()); - - // Copy src/dir to dest2. - operation_runner()->Copy( - child_dir, dest2, - FileSystemOperationRunner::CopyProgressCallback(), - base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK)); - base::RunLoop().RunUntilIdle(); - - expected_usage += grandchild_file_size + grandchild_path_cost; - usage = GetUsage(); - EXPECT_EQ(2 * child_file_size + 3 * grandchild_file_size, - GetDataSizeOnDisk()); - EXPECT_EQ(expected_usage, usage); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_operation_impl_write_unittest.cc b/chromium/webkit/browser/fileapi/file_system_operation_impl_write_unittest.cc deleted file mode 100644 index 7fb9aa4ae86..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_operation_impl_write_unittest.cc +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <vector> - -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/run_loop.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_job_factory_impl.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" -#include "webkit/browser/blob/blob_storage_context.h" -#include "webkit/browser/blob/blob_url_request_job.h" -#include "webkit/browser/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_context.h" -#include "webkit/browser/fileapi/file_system_operation_runner.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/browser/fileapi/test_file_system_backend.h" -#include "webkit/browser/quota/mock_quota_manager.h" -#include "webkit/common/blob/blob_data.h" -#include "webkit/common/fileapi/file_system_util.h" - -using webkit_blob::MockBlobURLRequestContext; -using webkit_blob::ScopedTextBlob; - -namespace fileapi { - -namespace { - -const GURL kOrigin("http://example.com"); -const FileSystemType kFileSystemType = kFileSystemTypeTest; - -void AssertStatusEq(base::PlatformFileError expected, - base::PlatformFileError actual) { - ASSERT_EQ(expected, actual); -} - -} // namespace - -class FileSystemOperationImplWriteTest - : public testing::Test { - public: - FileSystemOperationImplWriteTest() - : loop_(base::MessageLoop::TYPE_IO), - status_(base::PLATFORM_FILE_OK), - cancel_status_(base::PLATFORM_FILE_ERROR_FAILED), - bytes_written_(0), - complete_(false), - weak_factory_(this) { - change_observers_ = MockFileChangeObserver::CreateList(&change_observer_); - } - - virtual void SetUp() { - ASSERT_TRUE(dir_.CreateUniqueTempDir()); - - quota_manager_ = - new quota::MockQuotaManager(false /* is_incognito */, - dir_.path(), - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - NULL /* special storage policy */); - virtual_path_ = base::FilePath(FILE_PATH_LITERAL("temporary file")); - - file_system_context_ = CreateFileSystemContextForTesting( - quota_manager_->proxy(), dir_.path()); - url_request_context_.reset( - new MockBlobURLRequestContext(file_system_context_.get())); - - file_system_context_->operation_runner()->CreateFile( - URLForPath(virtual_path_), true /* exclusive */, - base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK)); - - static_cast<TestFileSystemBackend*>( - file_system_context_->GetFileSystemBackend(kFileSystemType)) - ->AddFileChangeObserver(change_observer()); - } - - virtual void TearDown() { - quota_manager_ = NULL; - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - base::PlatformFileError status() const { return status_; } - base::PlatformFileError cancel_status() const { return cancel_status_; } - void add_bytes_written(int64 bytes, bool complete) { - bytes_written_ += bytes; - EXPECT_FALSE(complete_); - complete_ = complete; - } - int64 bytes_written() const { return bytes_written_; } - bool complete() const { return complete_; } - - protected: - const ChangeObserverList& change_observers() const { - return change_observers_; - } - - MockFileChangeObserver* change_observer() { - return &change_observer_; - } - - FileSystemURL URLForPath(const base::FilePath& path) const { - return file_system_context_->CreateCrackedFileSystemURL( - kOrigin, kFileSystemType, path); - } - - // Callback function for recording test results. - FileSystemOperation::WriteCallback RecordWriteCallback() { - return base::Bind(&FileSystemOperationImplWriteTest::DidWrite, - weak_factory_.GetWeakPtr()); - } - - FileSystemOperation::StatusCallback RecordCancelCallback() { - return base::Bind(&FileSystemOperationImplWriteTest::DidCancel, - weak_factory_.GetWeakPtr()); - } - - void DidWrite(base::PlatformFileError status, int64 bytes, bool complete) { - if (status == base::PLATFORM_FILE_OK) { - add_bytes_written(bytes, complete); - if (complete) - base::MessageLoop::current()->Quit(); - } else { - EXPECT_FALSE(complete_); - EXPECT_EQ(status_, base::PLATFORM_FILE_OK); - complete_ = true; - status_ = status; - if (base::MessageLoop::current()->is_running()) - base::MessageLoop::current()->Quit(); - } - } - - void DidCancel(base::PlatformFileError status) { - cancel_status_ = status; - } - - const MockBlobURLRequestContext& url_request_context() const { - return *url_request_context_; - } - - scoped_refptr<FileSystemContext> file_system_context_; - scoped_refptr<quota::MockQuotaManager> quota_manager_; - - base::MessageLoop loop_; - - base::ScopedTempDir dir_; - base::FilePath virtual_path_; - - // For post-operation status. - base::PlatformFileError status_; - base::PlatformFileError cancel_status_; - int64 bytes_written_; - bool complete_; - - scoped_ptr<MockBlobURLRequestContext> url_request_context_; - - MockFileChangeObserver change_observer_; - ChangeObserverList change_observers_; - - base::WeakPtrFactory<FileSystemOperationImplWriteTest> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplWriteTest); -}; - -TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) { - ScopedTextBlob blob(url_request_context(), - "blob-id:success", - "Hello, world!\n"); - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_path_), - blob.GetBlobDataHandle(), - 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(14, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) { - ScopedTextBlob blob(url_request_context(), "blob_id:zero", ""); - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_path_), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(0, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_OK, status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); -} - - -TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidBlobUrl) { - scoped_ptr<webkit_blob::BlobDataHandle> null_handle; - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_path_), - null_handle.Pass(), 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(0, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_FAILED, status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) { - ScopedTextBlob blob(url_request_context(), "blob_id:writeinvalidfile", - "It\'ll not be written."); - file_system_context_->operation_runner()->Write( - &url_request_context(), - URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(0, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) { - base::FilePath virtual_dir_path(FILE_PATH_LITERAL("d")); - file_system_context_->operation_runner()->CreateDirectory( - URLForPath(virtual_dir_path), - true /* exclusive */, false /* recursive */, - base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK)); - - ScopedTextBlob blob(url_request_context(), "blob:writedir", - "It\'ll not be written, too."); - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_dir_path), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(0, bytes_written()); - // TODO(kinuko): This error code is platform- or fileutil- dependent - // right now. Make it return PLATFORM_FILE_ERROR_NOT_A_FILE in every case. - EXPECT_TRUE(status() == base::PLATFORM_FILE_ERROR_NOT_A_FILE || - status() == base::PLATFORM_FILE_ERROR_ACCESS_DENIED || - status() == base::PLATFORM_FILE_ERROR_FAILED); - EXPECT_TRUE(complete()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) { - ScopedTextBlob blob(url_request_context(), "blob:success", - "Hello, world!\n"); - quota_manager_->SetQuota( - kOrigin, FileSystemTypeToQuotaStorageType(kFileSystemType), 10); - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_path_), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - base::MessageLoop::current()->Run(); - - EXPECT_EQ(10, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) { - ScopedTextBlob blob(url_request_context(), "blob:success", - "Hello, world!\n"); - FileSystemOperationRunner::OperationID id = - file_system_context_->operation_runner()->Write( - &url_request_context(), URLForPath(virtual_path_), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback()); - // We use RunAllPendings() instead of Run() here, because we won't dispatch - // callbacks after Cancel() is issued (so no chance to Quit) nor do we need - // to run another write cycle. - base::RunLoop().RunUntilIdle(); - - // Issued Cancel() before receiving any response from Write(), - // so nothing should have happen. - EXPECT_EQ(0, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status()); - EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); -} - -TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) { - ScopedTextBlob blob(url_request_context(), "blob:writeinvalidfile", - "It\'ll not be written."); - FileSystemOperationRunner::OperationID id = - file_system_context_->operation_runner()->Write( - &url_request_context(), - URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))), - blob.GetBlobDataHandle(), 0, RecordWriteCallback()); - file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback()); - // We use RunAllPendings() instead of Run() here, because we won't dispatch - // callbacks after Cancel() is issued (so no chance to Quit) nor do we need - // to run another write cycle. - base::RunLoop().RunUntilIdle(); - - // Issued Cancel() before receiving any response from Write(), - // so nothing should have happen. - EXPECT_EQ(0, bytes_written()); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status()); - EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status()); - EXPECT_TRUE(complete()); - - EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); -} - -// TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases. - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_operation_runner.cc b/chromium/webkit/browser/fileapi/file_system_operation_runner.cc index 5a31ecc44ec..d552a783193 100644 --- a/chromium/webkit/browser/fileapi/file_system_operation_runner.cc +++ b/chromium/webkit/browser/fileapi/file_system_operation_runner.cc @@ -86,6 +86,7 @@ OperationID FileSystemOperationRunner::CreateDirectory( OperationID FileSystemOperationRunner::Copy( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; @@ -100,7 +101,7 @@ OperationID FileSystemOperationRunner::Copy( PrepareForWrite(handle.id, dest_url); PrepareForRead(handle.id, src_url); operation->Copy( - src_url, dest_url, + src_url, dest_url, option, progress_callback.is_null() ? CopyProgressCallback() : base::Bind(&FileSystemOperationRunner::OnCopyProgress, AsWeakPtr(), @@ -113,6 +114,7 @@ OperationID FileSystemOperationRunner::Copy( OperationID FileSystemOperationRunner::Move( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = @@ -126,7 +128,7 @@ OperationID FileSystemOperationRunner::Move( PrepareForWrite(handle.id, dest_url); PrepareForWrite(handle.id, src_url); operation->Move( - src_url, dest_url, + src_url, dest_url, option, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), handle, callback)); return handle.id; @@ -337,7 +339,6 @@ OperationID FileSystemOperationRunner::TouchFile( OperationID FileSystemOperationRunner::OpenFile( const FileSystemURL& url, int file_flags, - base::ProcessHandle peer_handle, const OpenFileCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = @@ -346,7 +347,7 @@ OperationID FileSystemOperationRunner::OpenFile( OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr()); if (!operation) { DidOpenFile(handle, callback, error, base::kInvalidPlatformFileValue, - base::Closure(), base::ProcessHandle()); + base::Closure()); return handle.id; } if (file_flags & @@ -360,7 +361,7 @@ OperationID FileSystemOperationRunner::OpenFile( PrepareForRead(handle.id, url); } operation->OpenFile( - url, file_flags, peer_handle, + url, file_flags, base::Bind(&FileSystemOperationRunner::DidOpenFile, AsWeakPtr(), handle, callback)); return handle.id; @@ -448,6 +449,7 @@ OperationID FileSystemOperationRunner::RemoveDirectory( OperationID FileSystemOperationRunner::CopyFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; @@ -460,7 +462,7 @@ OperationID FileSystemOperationRunner::CopyFileLocal( return handle.id; } operation->CopyFileLocal( - src_url, dest_url, progress_callback, + src_url, dest_url, option, progress_callback, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), handle, callback)); return handle.id; @@ -469,6 +471,7 @@ OperationID FileSystemOperationRunner::CopyFileLocal( OperationID FileSystemOperationRunner::MoveFileLocal( const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback) { base::PlatformFileError error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = @@ -480,7 +483,7 @@ OperationID FileSystemOperationRunner::MoveFileLocal( return handle.id; } operation->MoveFileLocal( - src_url, dest_url, + src_url, dest_url, option, base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(), handle, callback)); return handle.id; @@ -490,9 +493,9 @@ base::PlatformFileError FileSystemOperationRunner::SyncGetPlatformPath( const FileSystemURL& url, base::FilePath* platform_path) { base::PlatformFileError error = base::PLATFORM_FILE_OK; - FileSystemOperation* operation = - file_system_context_->CreateFileSystemOperation(url, &error); - if (!operation) + scoped_ptr<FileSystemOperation> operation( + file_system_context_->CreateFileSystemOperation(url, &error)); + if (!operation.get()) return error; return operation->SyncGetPlatformPath(url, platform_path); } @@ -574,17 +577,16 @@ void FileSystemOperationRunner::DidOpenFile( const OpenFileCallback& callback, base::PlatformFileError rv, base::PlatformFile file, - const base::Closure& on_close_callback, - base::ProcessHandle peer_handle) { + const base::Closure& on_close_callback) { if (handle.scope) { finished_operations_.insert(handle.id); base::MessageLoopProxy::current()->PostTask( FROM_HERE, base::Bind(&FileSystemOperationRunner::DidOpenFile, AsWeakPtr(), handle, callback, rv, file, - on_close_callback, peer_handle)); + on_close_callback)); return; } - callback.Run(rv, file, on_close_callback, peer_handle); + callback.Run(rv, file, on_close_callback); FinishOperation(handle.id); } diff --git a/chromium/webkit/browser/fileapi/file_system_operation_runner.h b/chromium/webkit/browser/fileapi/file_system_operation_runner.h index ea93f38e1fc..fba6b1cab96 100644 --- a/chromium/webkit/browser/fileapi/file_system_operation_runner.h +++ b/chromium/webkit/browser/fileapi/file_system_operation_runner.h @@ -6,6 +6,8 @@ #define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_OPERATION_RUNNER_H_ #include <map> +#include <set> +#include <vector> #include "base/basictypes.h" #include "base/id_map.h" @@ -44,6 +46,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner typedef FileSystemOperation::CopyProgressCallback CopyProgressCallback; typedef FileSystemOperation::CopyFileProgressCallback CopyFileProgressCallback; + typedef FileSystemOperation::CopyOrMoveOption CopyOrMoveOption; typedef int OperationID; @@ -67,16 +70,20 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner // |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. - // For |progress_callback|, see file_system_operation.h for details. + // For |option| and |progress_callback|, see file_system_operation.h for + // details. OperationID Copy(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyProgressCallback& progress_callback, const StatusCallback& callback); // Moves a file or directory from |src_url| to |dest_url|. A new file // or directory is created at |dest_url| as needed. + // For |option|, see file_system_operation.h for details. OperationID Move(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback); // Checks if a directory is present at |url|. @@ -138,7 +145,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner // This function is used only by Pepper as of writing. OperationID OpenFile(const FileSystemURL& url, int file_flags, - base::ProcessHandle peer_handle, const OpenFileCallback& callback); // Creates a local snapshot file for a given |url| and returns the @@ -188,7 +194,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner // 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). - // For |progress_callback|, see file_system_operation.h for details. + // For |option| and |progress_callback|, see file_system_operation.h for + // details. // // This returns: // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| @@ -201,12 +208,14 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner // OperationID CopyFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const CopyFileProgressCallback& progress_callback, const StatusCallback& callback); // 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). + // For |option|, see file_system_operation.h for details. // // This returns: // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url| @@ -219,6 +228,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner // OperationID MoveFileLocal(const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, const StatusCallback& callback); // This is called only by pepper plugin as of writing to synchronously get @@ -263,8 +273,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner const OpenFileCallback& callback, base::PlatformFileError rv, base::PlatformFile file, - const base::Closure& on_close_callback, - base::ProcessHandle peer_handle); + const base::Closure& on_close_callback); void DidCreateSnapshot( const OperationHandle& handle, const SnapshotFileCallback& callback, diff --git a/chromium/webkit/browser/fileapi/file_system_operation_runner_unittest.cc b/chromium/webkit/browser/fileapi/file_system_operation_runner_unittest.cc deleted file mode 100644 index 086c9999ad5..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_operation_runner_unittest.cc +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/platform_file.h" -#include "base/run_loop.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/file_system_operation_runner.h" -#include "webkit/browser/fileapi/mock_file_system_context.h" - -namespace fileapi { - -void GetStatus(bool* done, - base::PlatformFileError *status_out, - base::PlatformFileError status) { - ASSERT_FALSE(*done); - *done = true; - *status_out = status; -} - -void GetCancelStatus(bool* operation_done, - bool* cancel_done, - base::PlatformFileError *status_out, - base::PlatformFileError status) { - // Cancel callback must be always called after the operation's callback. - ASSERT_TRUE(*operation_done); - ASSERT_FALSE(*cancel_done); - *cancel_done = true; - *status_out = status; -} - -class FileSystemOperationRunnerTest : public testing::Test { - protected: - FileSystemOperationRunnerTest() {} - virtual ~FileSystemOperationRunnerTest() {} - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(base_.CreateUniqueTempDir()); - base::FilePath base_dir = base_.path(); - file_system_context_ = - CreateFileSystemContextForTesting(NULL, base_dir); - } - - virtual void TearDown() OVERRIDE { - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - FileSystemURL URL(const std::string& path) { - return file_system_context_->CreateCrackedFileSystemURL( - GURL("http://example.com"), kFileSystemTypeTemporary, - base::FilePath::FromUTF8Unsafe(path)); - } - - FileSystemOperationRunner* operation_runner() { - return file_system_context_->operation_runner(); - } - - private: - base::ScopedTempDir base_; - base::MessageLoop message_loop_; - scoped_refptr<FileSystemContext> file_system_context_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemOperationRunnerTest); -}; - -TEST_F(FileSystemOperationRunnerTest, NotFoundError) { - bool done = false; - base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED; - - // Regular NOT_FOUND error, which is called asynchronously. - operation_runner()->Truncate(URL("foo"), 0, - base::Bind(&GetStatus, &done, &status)); - ASSERT_FALSE(done); - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(done); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status); -} - -TEST_F(FileSystemOperationRunnerTest, InvalidURLError) { - bool done = false; - base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED; - - // Invalid URL error, which calls DidFinish synchronously. - operation_runner()->Truncate(FileSystemURL(), 0, - base::Bind(&GetStatus, &done, &status)); - // The error call back shouldn't be fired synchronously. - ASSERT_FALSE(done); - - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(done); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status); -} - -TEST_F(FileSystemOperationRunnerTest, NotFoundErrorAndCancel) { - bool done = false; - bool cancel_done = false; - base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED; - base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED; - - // Call Truncate with non-existent URL, and try to cancel it immediately - // after that (before its callback is fired). - FileSystemOperationRunner::OperationID id = - operation_runner()->Truncate(URL("foo"), 0, - base::Bind(&GetStatus, &done, &status)); - operation_runner()->Cancel(id, base::Bind(&GetCancelStatus, - &done, &cancel_done, - &cancel_status)); - - ASSERT_FALSE(done); - ASSERT_FALSE(cancel_done); - base::RunLoop().RunUntilIdle(); - - ASSERT_TRUE(done); - ASSERT_TRUE(cancel_done); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status); -} - -TEST_F(FileSystemOperationRunnerTest, InvalidURLErrorAndCancel) { - bool done = false; - bool cancel_done = false; - base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED; - base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED; - - // Call Truncate with invalid URL, and try to cancel it immediately - // after that (before its callback is fired). - FileSystemOperationRunner::OperationID id = - operation_runner()->Truncate(FileSystemURL(), 0, - base::Bind(&GetStatus, &done, &status)); - operation_runner()->Cancel(id, base::Bind(&GetCancelStatus, - &done, &cancel_done, - &cancel_status)); - - ASSERT_FALSE(done); - ASSERT_FALSE(cancel_done); - base::RunLoop().RunUntilIdle(); - - ASSERT_TRUE(done); - ASSERT_TRUE(cancel_done); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status); -} - -TEST_F(FileSystemOperationRunnerTest, CancelWithInvalidId) { - const FileSystemOperationRunner::OperationID kInvalidId = -1; - bool done = true; // The operation is not running. - bool cancel_done = false; - base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED; - operation_runner()->Cancel(kInvalidId, base::Bind(&GetCancelStatus, - &done, &cancel_done, - &cancel_status)); - - ASSERT_TRUE(cancel_done); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_quota_client_unittest.cc b/chromium/webkit/browser/fileapi/file_system_quota_client_unittest.cc deleted file mode 100644 index 1a9121c1a7a..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_quota_client_unittest.cc +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/platform_file.h" -#include "base/run_loop.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" -#include "webkit/browser/fileapi/async_file_test_helper.h" -#include "webkit/browser/fileapi/file_system_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/common/fileapi/file_system_types.h" -#include "webkit/common/fileapi/file_system_util.h" -#include "webkit/common/quota/quota_types.h" - -namespace fileapi { -namespace { - -const char kDummyURL1[] = "http://www.dummy.org"; -const char kDummyURL2[] = "http://www.example.com"; -const char kDummyURL3[] = "http://www.bleh"; - -// Declared to shorten the variable names. -const quota::StorageType kTemporary = quota::kStorageTypeTemporary; -const quota::StorageType kPersistent = quota::kStorageTypePersistent; - -} // namespace - -class FileSystemQuotaClientTest : public testing::Test { - public: - FileSystemQuotaClientTest() - : weak_factory_(this), - additional_callback_count_(0), - deletion_status_(quota::kQuotaStatusUnknown) { - } - - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - file_system_context_ = CreateFileSystemContextForTesting( - NULL, data_dir_.path()); - } - - struct TestFile { - bool isDirectory; - const char* name; - int64 size; - const char* origin_url; - quota::StorageType type; - }; - - protected: - FileSystemQuotaClient* NewQuotaClient(bool is_incognito) { - return new FileSystemQuotaClient(file_system_context_.get(), is_incognito); - } - - void GetOriginUsageAsync(FileSystemQuotaClient* quota_client, - const std::string& origin_url, - quota::StorageType type) { - quota_client->GetOriginUsage( - GURL(origin_url), type, - base::Bind(&FileSystemQuotaClientTest::OnGetUsage, - weak_factory_.GetWeakPtr())); - } - - int64 GetOriginUsage(FileSystemQuotaClient* quota_client, - const std::string& origin_url, - quota::StorageType type) { - GetOriginUsageAsync(quota_client, origin_url, type); - base::RunLoop().RunUntilIdle(); - return usage_; - } - - const std::set<GURL>& GetOriginsForType(FileSystemQuotaClient* quota_client, - quota::StorageType type) { - origins_.clear(); - quota_client->GetOriginsForType( - type, - base::Bind(&FileSystemQuotaClientTest::OnGetOrigins, - weak_factory_.GetWeakPtr())); - base::RunLoop().RunUntilIdle(); - return origins_; - } - - const std::set<GURL>& GetOriginsForHost(FileSystemQuotaClient* quota_client, - quota::StorageType type, - const std::string& host) { - origins_.clear(); - quota_client->GetOriginsForHost( - type, host, - base::Bind(&FileSystemQuotaClientTest::OnGetOrigins, - weak_factory_.GetWeakPtr())); - base::RunLoop().RunUntilIdle(); - return origins_; - } - - void RunAdditionalOriginUsageTask(FileSystemQuotaClient* quota_client, - const std::string& origin_url, - quota::StorageType type) { - quota_client->GetOriginUsage( - GURL(origin_url), type, - base::Bind(&FileSystemQuotaClientTest::OnGetAdditionalUsage, - weak_factory_.GetWeakPtr())); - } - - bool CreateFileSystemDirectory(const base::FilePath& file_path, - const std::string& origin_url, - quota::StorageType storage_type) { - FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - GURL(origin_url), type, file_path); - - base::PlatformFileError result = - AsyncFileTestHelper::CreateDirectory(file_system_context_, url); - return result == base::PLATFORM_FILE_OK; - } - - bool CreateFileSystemFile(const base::FilePath& file_path, - int64 file_size, - const std::string& origin_url, - quota::StorageType storage_type) { - if (file_path.empty()) - return false; - - FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - GURL(origin_url), type, file_path); - - base::PlatformFileError result = - AsyncFileTestHelper::CreateFile(file_system_context_, url); - if (result != base::PLATFORM_FILE_OK) - return false; - - result = AsyncFileTestHelper::TruncateFile( - file_system_context_, url, file_size); - return result == base::PLATFORM_FILE_OK; - } - - void InitializeOriginFiles(FileSystemQuotaClient* quota_client, - const TestFile* files, - int num_files) { - for (int i = 0; i < num_files; i++) { - base::FilePath path = base::FilePath().AppendASCII(files[i].name); - if (files[i].isDirectory) { - ASSERT_TRUE(CreateFileSystemDirectory( - path, files[i].origin_url, files[i].type)); - if (path.empty()) { - // Create the usage cache. - // HACK--we always create the root [an empty path] first. If we - // create it later, this will fail due to a quota mismatch. If we - // call this before we create the root, it succeeds, but hasn't - // actually created the cache. - ASSERT_EQ(0, GetOriginUsage( - quota_client, files[i].origin_url, files[i].type)); - } - } else { - ASSERT_TRUE(CreateFileSystemFile( - path, files[i].size, files[i].origin_url, files[i].type)); - } - } - } - - // This is a bit fragile--it depends on the test data always creating a - // directory before adding a file or directory to it, so that we can just - // count the basename of each addition. A recursive creation of a path, which - // created more than one directory in a single shot, would break this. - int64 ComputeFilePathsCostForOriginAndType(const TestFile* files, - int num_files, - const std::string& origin_url, - quota::StorageType type) { - int64 file_paths_cost = 0; - for (int i = 0; i < num_files; i++) { - if (files[i].type == type && - GURL(files[i].origin_url) == GURL(origin_url)) { - base::FilePath path = base::FilePath().AppendASCII(files[i].name); - if (!path.empty()) { - file_paths_cost += ObfuscatedFileUtil::ComputeFilePathCost(path); - } - } - } - return file_paths_cost; - } - - void DeleteOriginData(FileSystemQuotaClient* quota_client, - const std::string& origin, - quota::StorageType type) { - deletion_status_ = quota::kQuotaStatusUnknown; - quota_client->DeleteOriginData( - GURL(origin), type, - base::Bind(&FileSystemQuotaClientTest::OnDeleteOrigin, - weak_factory_.GetWeakPtr())); - } - - int64 usage() const { return usage_; } - quota::QuotaStatusCode status() { return deletion_status_; } - int additional_callback_count() const { return additional_callback_count_; } - void set_additional_callback_count(int count) { - additional_callback_count_ = count; - } - - private: - void OnGetUsage(int64 usage) { - usage_ = usage; - } - - void OnGetOrigins(const std::set<GURL>& origins) { - origins_ = origins; - } - - void OnGetAdditionalUsage(int64 usage_unused) { - ++additional_callback_count_; - } - - void OnDeleteOrigin(quota::QuotaStatusCode status) { - deletion_status_ = status; - } - - base::ScopedTempDir data_dir_; - base::MessageLoop message_loop_; - scoped_refptr<FileSystemContext> file_system_context_; - base::WeakPtrFactory<FileSystemQuotaClientTest> weak_factory_; - int64 usage_; - int additional_callback_count_; - std::set<GURL> origins_; - quota::QuotaStatusCode deletion_status_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemQuotaClientTest); -}; - -TEST_F(FileSystemQuotaClientTest, NoFileSystemTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - - EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); -} - -TEST_F(FileSystemQuotaClientTest, NoFileTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemQuotaClientTest, OneFileTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 4921, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(4921 + file_paths_cost, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemQuotaClientTest, TwoFilesTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 10310, kDummyURL1, kTemporary}, - {false, "bar", 41, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(10310 + 41 + file_paths_cost, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemQuotaClientTest, EmptyFilesTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 0, kDummyURL1, kTemporary}, - {false, "bar", 0, kDummyURL1, kTemporary}, - {false, "baz", 0, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(file_paths_cost, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemQuotaClientTest, SubDirectoryTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dirtest", 0, kDummyURL1, kTemporary}, - {false, "dirtest/foo", 11921, kDummyURL1, kTemporary}, - {false, "bar", 4814, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(11921 + 4814 + file_paths_cost, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemQuotaClientTest, MultiTypeTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dirtest", 0, kDummyURL1, kTemporary}, - {false, "dirtest/foo", 133, kDummyURL1, kTemporary}, - {false, "bar", 14, kDummyURL1, kTemporary}, - {true, NULL, 0, kDummyURL1, kPersistent}, - {true, "dirtest", 0, kDummyURL1, kPersistent}, - {false, "dirtest/foo", 193, kDummyURL1, kPersistent}, - {false, "bar", 9, kDummyURL1, kPersistent}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost_temporary = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - const int64 file_paths_cost_persistent = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(133 + 14 + file_paths_cost_temporary, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - EXPECT_EQ(193 + 9 + file_paths_cost_persistent, - GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); - } -} - -TEST_F(FileSystemQuotaClientTest, MultiDomainTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dir1", 0, kDummyURL1, kTemporary}, - {false, "dir1/foo", 1331, kDummyURL1, kTemporary}, - {false, "bar", 134, kDummyURL1, kTemporary}, - {true, NULL, 0, kDummyURL1, kPersistent}, - {true, "dir2", 0, kDummyURL1, kPersistent}, - {false, "dir2/foo", 1903, kDummyURL1, kPersistent}, - {false, "bar", 19, kDummyURL1, kPersistent}, - {true, NULL, 0, kDummyURL2, kTemporary}, - {true, "dom", 0, kDummyURL2, kTemporary}, - {false, "dom/fan", 1319, kDummyURL2, kTemporary}, - {false, "bar", 113, kDummyURL2, kTemporary}, - {true, NULL, 0, kDummyURL2, kPersistent}, - {true, "dom", 0, kDummyURL2, kPersistent}, - {false, "dom/fan", 2013, kDummyURL2, kPersistent}, - {false, "baz", 18, kDummyURL2, kPersistent}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost_temporary1 = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - const int64 file_paths_cost_persistent1 = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - kDummyURL1, kPersistent); - const int64 file_paths_cost_temporary2 = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL2, kTemporary); - const int64 file_paths_cost_persistent2 = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - kDummyURL2, kPersistent); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1, - GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - EXPECT_EQ(1903 + 19 + file_paths_cost_persistent1, - GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); - EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2, - GetOriginUsage(quota_client.get(), kDummyURL2, kTemporary)); - EXPECT_EQ(2013 + 18 + file_paths_cost_persistent2, - GetOriginUsage(quota_client.get(), kDummyURL2, kPersistent)); - } -} - -TEST_F(FileSystemQuotaClientTest, GetUsage_MultipleTasks) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 11, kDummyURL1, kTemporary}, - {false, "bar", 22, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost = ComputeFilePathsCostForOriginAndType( - kFiles, ARRAYSIZE_UNSAFE(kFiles), kDummyURL1, kTemporary); - - // Dispatching three GetUsage tasks. - set_additional_callback_count(0); - GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary); - RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); - RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(11 + 22 + file_paths_cost, usage()); - EXPECT_EQ(2, additional_callback_count()); - - // Once more, in a different order. - set_additional_callback_count(0); - RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); - GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary); - RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(11 + 22 + file_paths_cost, usage()); - EXPECT_EQ(2, additional_callback_count()); -} - -TEST_F(FileSystemQuotaClientTest, GetOriginsForType) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, NULL, 0, kDummyURL2, kTemporary}, - {true, NULL, 0, kDummyURL3, kPersistent}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - - std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary); - EXPECT_EQ(2U, origins.size()); - EXPECT_TRUE(origins.find(GURL(kDummyURL1)) != origins.end()); - EXPECT_TRUE(origins.find(GURL(kDummyURL2)) != origins.end()); - EXPECT_TRUE(origins.find(GURL(kDummyURL3)) == origins.end()); -} - -TEST_F(FileSystemQuotaClientTest, GetOriginsForHost) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const char* kURL1 = "http://foo.com/"; - const char* kURL2 = "https://foo.com/"; - const char* kURL3 = "http://foo.com:1/"; - const char* kURL4 = "http://foo2.com/"; - const char* kURL5 = "http://foo.com:2/"; - const TestFile kFiles[] = { - {true, NULL, 0, kURL1, kTemporary}, - {true, NULL, 0, kURL2, kTemporary}, - {true, NULL, 0, kURL3, kTemporary}, - {true, NULL, 0, kURL4, kTemporary}, - {true, NULL, 0, kURL5, kPersistent}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - - std::set<GURL> origins = GetOriginsForHost( - quota_client.get(), kTemporary, "foo.com"); - EXPECT_EQ(3U, origins.size()); - EXPECT_TRUE(origins.find(GURL(kURL1)) != origins.end()); - EXPECT_TRUE(origins.find(GURL(kURL2)) != origins.end()); - EXPECT_TRUE(origins.find(GURL(kURL3)) != origins.end()); - EXPECT_TRUE(origins.find(GURL(kURL4)) == origins.end()); // Different host. - EXPECT_TRUE(origins.find(GURL(kURL5)) == origins.end()); // Different type. -} - -TEST_F(FileSystemQuotaClientTest, IncognitoTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(true)); - const TestFile kFiles[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 10, kDummyURL1, kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - - // Having files in the usual directory wouldn't affect the result - // queried in incognito mode. - EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); - EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); - - std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary); - EXPECT_EQ(0U, origins.size()); - origins = GetOriginsForHost(quota_client.get(), kTemporary, "www.dummy.org"); - EXPECT_EQ(0U, origins.size()); -} - -TEST_F(FileSystemQuotaClientTest, DeleteOriginTest) { - scoped_ptr<FileSystemQuotaClient> quota_client(NewQuotaClient(false)); - const TestFile kFiles[] = { - {true, NULL, 0, "http://foo.com/", kTemporary}, - {false, "a", 1, "http://foo.com/", kTemporary}, - {true, NULL, 0, "https://foo.com/", kTemporary}, - {false, "b", 2, "https://foo.com/", kTemporary}, - {true, NULL, 0, "http://foo.com/", kPersistent}, - {false, "c", 4, "http://foo.com/", kPersistent}, - {true, NULL, 0, "http://bar.com/", kTemporary}, - {false, "d", 8, "http://bar.com/", kTemporary}, - {true, NULL, 0, "http://bar.com/", kPersistent}, - {false, "e", 16, "http://bar.com/", kPersistent}, - {true, NULL, 0, "https://bar.com/", kPersistent}, - {false, "f", 32, "https://bar.com/", kPersistent}, - {true, NULL, 0, "https://bar.com/", kTemporary}, - {false, "g", 64, "https://bar.com/", kTemporary}, - }; - InitializeOriginFiles(quota_client.get(), kFiles, ARRAYSIZE_UNSAFE(kFiles)); - const int64 file_paths_cost_temporary_foo_https = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - "https://foo.com/", kTemporary); - const int64 file_paths_cost_persistent_foo = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - "http://foo.com/", kPersistent); - const int64 file_paths_cost_temporary_bar = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - "http://bar.com/", kTemporary); - const int64 file_paths_cost_temporary_bar_https = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - "https://bar.com/", kTemporary); - const int64 file_paths_cost_persistent_bar_https = - ComputeFilePathsCostForOriginAndType(kFiles, ARRAYSIZE_UNSAFE(kFiles), - "https://bar.com/", kPersistent); - - DeleteOriginData(quota_client.get(), "http://foo.com/", kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(quota::kQuotaStatusOk, status()); - - DeleteOriginData(quota_client.get(), "http://bar.com/", kPersistent); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(quota::kQuotaStatusOk, status()); - - DeleteOriginData(quota_client.get(), "http://buz.com/", kTemporary); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(quota::kQuotaStatusOk, status()); - - EXPECT_EQ(0, GetOriginUsage( - quota_client.get(), "http://foo.com/", kTemporary)); - EXPECT_EQ(0, GetOriginUsage( - quota_client.get(), "http://bar.com/", kPersistent)); - EXPECT_EQ(0, GetOriginUsage( - quota_client.get(), "http://buz.com/", kTemporary)); - - EXPECT_EQ(2 + file_paths_cost_temporary_foo_https, - GetOriginUsage(quota_client.get(), - "https://foo.com/", - kTemporary)); - EXPECT_EQ(4 + file_paths_cost_persistent_foo, - GetOriginUsage(quota_client.get(), - "http://foo.com/", - kPersistent)); - EXPECT_EQ(8 + file_paths_cost_temporary_bar, - GetOriginUsage(quota_client.get(), - "http://bar.com/", - kTemporary)); - EXPECT_EQ(32 + file_paths_cost_persistent_bar_https, - GetOriginUsage(quota_client.get(), - "https://bar.com/", - kPersistent)); - EXPECT_EQ(64 + file_paths_cost_temporary_bar_https, - GetOriginUsage(quota_client.get(), - "https://bar.com/", - kTemporary)); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_quota_util.h b/chromium/webkit/browser/fileapi/file_system_quota_util.h index 5f3c7dd77ae..e3930862f87 100644 --- a/chromium/webkit/browser/fileapi/file_system_quota_util.h +++ b/chromium/webkit/browser/fileapi/file_system_quota_util.h @@ -26,6 +26,7 @@ class QuotaManagerProxy; namespace fileapi { class FileSystemContext; +class QuotaReservation; // An abstract interface that provides common quota-related utility functions // for file_system_quota_client. @@ -56,6 +57,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemQuotaUtil { const GURL& origin_url, fileapi::FileSystemType type) = 0; + // Creates new reservation object for the origin and the type. + virtual scoped_refptr<QuotaReservation> + CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type) = 0; + virtual void AddFileUpdateObserver( FileSystemType type, FileUpdateObserver* observer, diff --git a/chromium/webkit/browser/fileapi/file_system_url.cc b/chromium/webkit/browser/fileapi/file_system_url.cc index 2f8875a701b..b045ed3f5d3 100644 --- a/chromium/webkit/browser/fileapi/file_system_url.cc +++ b/chromium/webkit/browser/fileapi/file_system_url.cc @@ -21,7 +21,8 @@ namespace { FileSystemURL::FileSystemURL() : is_valid_(false), mount_type_(kFileSystemTypeUnknown), - type_(kFileSystemTypeUnknown) { + type_(kFileSystemTypeUnknown), + mount_option_(COPY_SYNC_OPTION_NO_SYNC) { } // static @@ -98,7 +99,8 @@ bool FileSystemURL::ParseFileSystemSchemeURL( FileSystemURL::FileSystemURL(const GURL& url) : mount_type_(kFileSystemTypeUnknown), - type_(kFileSystemTypeUnknown) { + type_(kFileSystemTypeUnknown), + mount_option_(COPY_SYNC_OPTION_NO_SYNC) { is_valid_ = ParseFileSystemSchemeURL(url, &origin_, &mount_type_, &virtual_path_); path_ = virtual_path_; @@ -113,7 +115,8 @@ FileSystemURL::FileSystemURL(const GURL& origin, mount_type_(mount_type), virtual_path_(virtual_path.NormalizePathSeparators()), type_(mount_type), - path_(virtual_path.NormalizePathSeparators()) { + path_(virtual_path.NormalizePathSeparators()), + mount_option_(COPY_SYNC_OPTION_NO_SYNC) { } FileSystemURL::FileSystemURL(const GURL& origin, @@ -122,7 +125,8 @@ FileSystemURL::FileSystemURL(const GURL& origin, const std::string& mount_filesystem_id, FileSystemType cracked_type, const base::FilePath& cracked_path, - const std::string& filesystem_id) + const std::string& filesystem_id, + const FileSystemMountOption& mount_option) : is_valid_(true), origin_(origin), mount_type_(mount_type), @@ -130,7 +134,8 @@ FileSystemURL::FileSystemURL(const GURL& origin, mount_filesystem_id_(mount_filesystem_id), type_(cracked_type), path_(cracked_path.NormalizePathSeparators()), - filesystem_id_(filesystem_id) { + filesystem_id_(filesystem_id), + mount_option_(mount_option) { } FileSystemURL::~FileSystemURL() {} diff --git a/chromium/webkit/browser/fileapi/file_system_url.h b/chromium/webkit/browser/fileapi/file_system_url.h index d6443e69189..c00231d80d3 100644 --- a/chromium/webkit/browser/fileapi/file_system_url.h +++ b/chromium/webkit/browser/fileapi/file_system_url.h @@ -11,6 +11,7 @@ #include "base/platform_file.h" #include "url/gurl.h" #include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_mount_option.h" #include "webkit/common/fileapi/file_system_types.h" namespace fileapi { @@ -118,6 +119,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemURL { FileSystemType mount_type() const { return mount_type_; } + const FileSystemMountOption& mount_option() const { return mount_option_; } + // Returns the formatted URL of this instance. GURL ToGURL() const; @@ -154,7 +157,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemURL { const std::string& mount_filesystem_id, FileSystemType cracked_type, const base::FilePath& cracked_path, - const std::string& filesystem_id); + const std::string& filesystem_id, + const FileSystemMountOption& mount_option); bool is_valid_; @@ -172,6 +176,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemURL { FileSystemType type_; base::FilePath path_; std::string filesystem_id_; + FileSystemMountOption mount_option_; }; typedef std::set<FileSystemURL, FileSystemURL::Comparator> FileSystemURLSet; diff --git a/chromium/webkit/browser/fileapi/file_system_url_request_job.cc b/chromium/webkit/browser/fileapi/file_system_url_request_job.cc index 7d1f8613138..57db3006557 100644 --- a/chromium/webkit/browser/fileapi/file_system_url_request_job.cc +++ b/chromium/webkit/browser/fileapi/file_system_url_request_job.cc @@ -60,9 +60,9 @@ FileSystemURLRequestJob::FileSystemURLRequestJob( FileSystemContext* file_system_context) : URLRequestJob(request, network_delegate), file_system_context_(file_system_context), - weak_factory_(this), is_directory_(false), - remaining_bytes_(0) { + remaining_bytes_(0), + weak_factory_(this) { } FileSystemURLRequestJob::~FileSystemURLRequestJob() {} diff --git a/chromium/webkit/browser/fileapi/file_system_url_request_job.h b/chromium/webkit/browser/fileapi/file_system_url_request_job.h index 6b829e6e861..a784d629251 100644 --- a/chromium/webkit/browser/fileapi/file_system_url_request_job.h +++ b/chromium/webkit/browser/fileapi/file_system_url_request_job.h @@ -67,13 +67,13 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE FileSystemURLRequestJob void NotifyFailed(int rv); FileSystemContext* file_system_context_; - base::WeakPtrFactory<FileSystemURLRequestJob> weak_factory_; scoped_ptr<webkit_blob::FileStreamReader> reader_; FileSystemURL url_; bool is_directory_; scoped_ptr<net::HttpResponseInfo> response_info_; int64 remaining_bytes_; net::HttpByteRange byte_range_; + base::WeakPtrFactory<FileSystemURLRequestJob> weak_factory_; DISALLOW_COPY_AND_ASSIGN(FileSystemURLRequestJob); }; diff --git a/chromium/webkit/browser/fileapi/file_system_url_request_job_unittest.cc b/chromium/webkit/browser/fileapi/file_system_url_request_job_unittest.cc deleted file mode 100644 index 8f54e62b984..00000000000 --- a/chromium/webkit/browser/fileapi/file_system_url_request_job_unittest.cc +++ /dev/null @@ -1,365 +0,0 @@ -// 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_url_request_job.h" - -#include <string> - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/format_macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/platform_file.h" -#include "base/rand_util.h" -#include "base/run_loop.h" -#include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/load_flags.h" -#include "net/base/mime_util.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/http/http_request_headers.h" -#include "net/url_request/url_request.h" -#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/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_file_util.h" -#include "webkit/browser/fileapi/mock_file_system_context.h" - -namespace fileapi { -namespace { - -// We always use the TEMPORARY FileSystem in this test. -const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/"; -const char kTestFileData[] = "0123456789"; - -void FillBuffer(char* buffer, size_t len) { - base::RandBytes(buffer, len); -} - -} // namespace - -class FileSystemURLRequestJobTest : public testing::Test { - protected: - FileSystemURLRequestJobTest() - : message_loop_(base::MessageLoop::TYPE_IO), // simulate an IO thread - weak_factory_(this) { - } - - virtual void SetUp() OVERRIDE { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - - // We use the main thread so that we can get the root path synchronously. - // TODO(adamk): Run this on the FILE thread we've created as well. - file_system_context_ = - CreateFileSystemContextForTesting(NULL, temp_dir_.path()); - - file_system_context_->OpenFileSystem( - GURL("http://remote/"), kFileSystemTypeTemporary, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&FileSystemURLRequestJobTest::OnOpenFileSystem, - weak_factory_.GetWeakPtr())); - base::RunLoop().RunUntilIdle(); - - net::URLRequest::Deprecated::RegisterProtocolFactory( - "filesystem", &FileSystemURLRequestJobFactory); - } - - virtual void TearDown() OVERRIDE { - net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL); - ClearUnusedJob(); - if (pending_job_.get()) { - pending_job_->Kill(); - pending_job_ = NULL; - } - // FileReader posts a task to close the file in destructor. - base::RunLoop().RunUntilIdle(); - } - - void OnOpenFileSystem(base::PlatformFileError result, - const std::string& name, - const GURL& root_url) { - ASSERT_EQ(base::PLATFORM_FILE_OK, result); - } - - void TestRequestHelper(const GURL& url, - const net::HttpRequestHeaders* headers, - bool run_to_completion, - FileSystemContext* file_system_context) { - delegate_.reset(new net::TestDelegate()); - // Make delegate_ exit the MessageLoop when the request is done. - delegate_->set_quit_on_complete(true); - delegate_->set_quit_on_redirect(true); - request_.reset(empty_context_.CreateRequest(url, delegate_.get())); - if (headers) - request_->SetExtraRequestHeaders(*headers); - ASSERT_TRUE(!job_); - job_ = new FileSystemURLRequestJob( - request_.get(), NULL, file_system_context); - pending_job_ = job_; - - request_->Start(); - ASSERT_TRUE(request_->is_pending()); // verify that we're starting async - if (run_to_completion) - base::MessageLoop::current()->Run(); - } - - void TestRequest(const GURL& url) { - TestRequestHelper(url, NULL, true, file_system_context_.get()); - } - - void TestRequestWithContext(const GURL& url, - FileSystemContext* file_system_context) { - TestRequestHelper(url, NULL, true, file_system_context); - } - - void TestRequestWithHeaders(const GURL& url, - const net::HttpRequestHeaders* headers) { - TestRequestHelper(url, headers, true, file_system_context_.get()); - } - - void TestRequestNoRun(const GURL& url) { - TestRequestHelper(url, NULL, false, file_system_context_.get()); - } - - void CreateDirectory(const base::StringPiece& dir_name) { - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - GURL("http://remote"), - kFileSystemTypeTemporary, - base::FilePath().AppendASCII(dir_name)); - ASSERT_EQ(base::PLATFORM_FILE_OK, AsyncFileTestHelper::CreateDirectory( - file_system_context_, url)); - } - - void WriteFile(const base::StringPiece& file_name, - const char* buf, int buf_size) { - FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( - GURL("http://remote"), - kFileSystemTypeTemporary, - base::FilePath().AppendASCII(file_name)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateFileWithData( - file_system_context_, url, buf, buf_size)); - } - - GURL CreateFileSystemURL(const std::string& path) { - return GURL(kFileSystemURLPrefix + path); - } - - static net::URLRequestJob* FileSystemURLRequestJobFactory( - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& scheme) { - DCHECK(job_); - net::URLRequestJob* temp = job_; - job_ = NULL; - return temp; - } - - static void ClearUnusedJob() { - if (job_) { - scoped_refptr<net::URLRequestJob> deleter = job_; - job_ = NULL; - } - } - - // Put the message loop at the top, so that it's the last thing deleted. - base::MessageLoop message_loop_; - - base::ScopedTempDir temp_dir_; - scoped_refptr<FileSystemContext> file_system_context_; - base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_; - - net::URLRequestContext empty_context_; - - // NOTE: order matters, request must die before delegate - scoped_ptr<net::TestDelegate> delegate_; - scoped_ptr<net::URLRequest> request_; - - scoped_refptr<net::URLRequestJob> pending_job_; - static net::URLRequestJob* job_; -}; - -// static -net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL; - -namespace { - -TEST_F(FileSystemURLRequestJobTest, FileTest) { - WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); - TestRequest(CreateFileSystemURL("file1.dat")); - - ASSERT_FALSE(request_->is_pending()); - EXPECT_EQ(1, delegate_->response_started_count()); - EXPECT_FALSE(delegate_->received_data_before_response()); - EXPECT_EQ(kTestFileData, delegate_->data_received()); - EXPECT_EQ(200, request_->GetResponseCode()); - std::string cache_control; - request_->GetResponseHeaderByName("cache-control", &cache_control); - EXPECT_EQ("no-cache", cache_control); -} - -TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) { - const size_t buffer_size = 4000; - scoped_ptr<char[]> buffer(new char[buffer_size]); - FillBuffer(buffer.get(), buffer_size); - WriteFile("bigfile", buffer.get(), buffer_size); - - const size_t first_byte_position = 500; - const size_t last_byte_position = buffer_size - first_byte_position; - std::string partial_buffer_string(buffer.get() + first_byte_position, - buffer.get() + last_byte_position + 1); - - net::HttpRequestHeaders headers; - headers.SetHeader(net::HttpRequestHeaders::kRange, - base::StringPrintf( - "bytes=%" PRIuS "-%" PRIuS, - first_byte_position, last_byte_position)); - TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers); - - ASSERT_FALSE(request_->is_pending()); - EXPECT_EQ(1, delegate_->response_started_count()); - EXPECT_FALSE(delegate_->received_data_before_response()); - EXPECT_TRUE(partial_buffer_string == delegate_->data_received()); -} - -TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) { - const size_t buffer_size = 4000; - scoped_ptr<char[]> buffer(new char[buffer_size]); - FillBuffer(buffer.get(), buffer_size); - WriteFile("bigfile", buffer.get(), buffer_size); - - const size_t first_byte_position = 500; - std::string partial_buffer_string(buffer.get() + first_byte_position, - buffer.get() + buffer_size); - - net::HttpRequestHeaders headers; - headers.SetHeader(net::HttpRequestHeaders::kRange, - base::StringPrintf("bytes=%" PRIuS "-", - first_byte_position)); - TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers); - ASSERT_FALSE(request_->is_pending()); - EXPECT_EQ(1, delegate_->response_started_count()); - EXPECT_FALSE(delegate_->received_data_before_response()); - // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed. - EXPECT_TRUE(partial_buffer_string == delegate_->data_received()); -} - - -TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) { - WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); - net::HttpRequestHeaders headers; - headers.SetHeader(net::HttpRequestHeaders::kRange, - "bytes=0-5,10-200,200-300"); - TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, - request_->status().error()); -} - -TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) { - WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); - net::HttpRequestHeaders headers; - headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=500-1000"); - TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers); - - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, - request_->status().error()); -} - -TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) { - CreateDirectory("dir"); - TestRequest(CreateFileSystemURL("dir")); - - EXPECT_EQ(1, delegate_->received_redirect_count()); - EXPECT_TRUE(request_->status().is_success()); - EXPECT_FALSE(delegate_->request_failed()); - - // We've deferred the redirect; now cancel the request to avoid following it. - request_->Cancel(); - base::MessageLoop::current()->Run(); -} - -TEST_F(FileSystemURLRequestJobTest, InvalidURL) { - TestRequest(GURL("filesystem:/foo/bar/baz")); - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error()); -} - -TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) { - TestRequest(GURL("filesystem:http://remote/persistent/somefile")); - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); -} - -TEST_F(FileSystemURLRequestJobTest, NoSuchFile) { - TestRequest(CreateFileSystemURL("somefile")); - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); -} - -TEST_F(FileSystemURLRequestJobTest, Cancel) { - WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); - TestRequestNoRun(CreateFileSystemURL("file1.dat")); - - // Run StartAsync() and only StartAsync(). - base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); - base::RunLoop().RunUntilIdle(); - // If we get here, success! we didn't crash! -} - -TEST_F(FileSystemURLRequestJobTest, GetMimeType) { - const char kFilename[] = "hoge.html"; - - std::string mime_type_direct; - base::FilePath::StringType extension = - base::FilePath().AppendASCII(kFilename).Extension(); - if (!extension.empty()) - extension = extension.substr(1); - EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension( - extension, &mime_type_direct)); - - TestRequest(CreateFileSystemURL(kFilename)); - - std::string mime_type_from_job; - request_->GetMimeType(&mime_type_from_job); - EXPECT_EQ(mime_type_direct, mime_type_from_job); -} - -TEST_F(FileSystemURLRequestJobTest, Incognito) { - WriteFile("file", kTestFileData, arraysize(kTestFileData) - 1); - - // Creates a new filesystem context for incognito mode. - scoped_refptr<FileSystemContext> file_system_context = - CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path()); - - // The request should return NOT_FOUND error if it's in incognito mode. - TestRequestWithContext(CreateFileSystemURL("file"), - file_system_context.get()); - ASSERT_FALSE(request_->is_pending()); - EXPECT_TRUE(delegate_->request_failed()); - EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); - - // Make sure it returns success with regular (non-incognito) context. - TestRequest(CreateFileSystemURL("file")); - ASSERT_FALSE(request_->is_pending()); - EXPECT_EQ(kTestFileData, delegate_->data_received()); - EXPECT_EQ(200, request_->GetResponseCode()); -} - -} // namespace -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/file_system_usage_cache.cc b/chromium/webkit/browser/fileapi/file_system_usage_cache.cc index c1896fb1f95..56b1fb43566 100644 --- a/chromium/webkit/browser/fileapi/file_system_usage_cache.cc +++ b/chromium/webkit/browser/fileapi/file_system_usage_cache.cc @@ -23,8 +23,8 @@ const size_t kMaxHandleCacheSize = 2; FileSystemUsageCache::FileSystemUsageCache( base::SequencedTaskRunner* task_runner) - : weak_factory_(this), - task_runner_(task_runner) { + : task_runner_(task_runner), + weak_factory_(this) { } FileSystemUsageCache::~FileSystemUsageCache() { diff --git a/chromium/webkit/browser/fileapi/file_system_usage_cache.h b/chromium/webkit/browser/fileapi/file_system_usage_cache.h index b4d8464884b..c3950a58561 100644 --- a/chromium/webkit/browser/fileapi/file_system_usage_cache.h +++ b/chromium/webkit/browser/fileapi/file_system_usage_cache.h @@ -93,10 +93,11 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE FileSystemUsageCache { scoped_ptr<TimedTaskHelper> timer_; std::map<base::FilePath, base::PlatformFile> cache_files_; - base::WeakPtrFactory<FileSystemUsageCache> weak_factory_; scoped_refptr<base::SequencedTaskRunner> task_runner_; + base::WeakPtrFactory<FileSystemUsageCache> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(FileSystemUsageCache); }; diff --git a/chromium/webkit/browser/fileapi/file_writer_delegate.cc b/chromium/webkit/browser/fileapi/file_writer_delegate.cc index 7a0094ee4d5..295cd642ab1 100644 --- a/chromium/webkit/browser/fileapi/file_writer_delegate.cc +++ b/chromium/webkit/browser/fileapi/file_writer_delegate.cc @@ -14,31 +14,12 @@ #include "net/base/net_errors.h" #include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/common/fileapi/file_system_util.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( scoped_ptr<FileStreamWriter> file_stream_writer) : file_stream_writer_(file_stream_writer.Pass()), diff --git a/chromium/webkit/browser/fileapi/file_writer_delegate.h b/chromium/webkit/browser/fileapi/file_writer_delegate.h index ae63e7f01de..f7ce16dfb6f 100644 --- a/chromium/webkit/browser/fileapi/file_writer_delegate.h +++ b/chromium/webkit/browser/fileapi/file_writer_delegate.h @@ -34,7 +34,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE FileWriterDelegate WriteProgressStatus write_status)> DelegateWriteCallback; - FileWriterDelegate(scoped_ptr<FileStreamWriter> file_writer); + explicit FileWriterDelegate(scoped_ptr<FileStreamWriter> file_writer); virtual ~FileWriterDelegate(); void Start(scoped_ptr<net::URLRequest> request, diff --git a/chromium/webkit/browser/fileapi/file_writer_delegate_unittest.cc b/chromium/webkit/browser/fileapi/file_writer_delegate_unittest.cc deleted file mode 100644 index 28c93e8a0db..00000000000 --- a/chromium/webkit/browser/fileapi/file_writer_delegate_unittest.cc +++ /dev/null @@ -1,454 +0,0 @@ -// 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/run_loop.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 "url/gurl.h" -#include "webkit/browser/fileapi/async_file_test_helper.h" -#include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/file_system_quota_util.h" -#include "webkit/browser/fileapi/file_writer_delegate.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; - -const char kData[] = "The quick brown fox jumps over the lazy dog.\n"; -const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1; - -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_; -}; - -} // namespace (anonymous) - -class FileWriterDelegateTest : public PlatformTest { - public: - FileWriterDelegateTest() - : loop_(base::MessageLoop::TYPE_IO) {} - - protected: - virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; - - int64 usage() { - return file_system_context_->GetQuotaUtil(kFileSystemType) - ->GetOriginUsageOnFileThread( - file_system_context_.get(), 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::RunLoop().RunUntilIdle(); - - FileSystemURL url = GetFileSystemURL(test_file_path); - base::PlatformFileInfo file_info; - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetMetadata( - file_system_context_, url, &file_info)); - return file_info.size; - } - - FileSystemURL GetFileSystemURL(const char* file_name) const { - return file_system_context_->CreateCrackedFileSystemURL( - kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name)); - } - - FileWriterDelegate* CreateWriterDelegate( - const char* test_file_path, - int64 offset, - int64 allowed_growth) { - SandboxFileStreamWriter* writer = new SandboxFileStreamWriter( - file_system_context_.get(), - GetFileSystemURL(test_file_path), - offset, - *file_system_context_->GetUpdateObservers(kFileSystemType)); - writer->set_default_quota(allowed_growth); - return new FileWriterDelegate(scoped_ptr<FileStreamWriter>(writer)); - } - - FileWriterDelegate::DelegateWriteCallback GetWriteCallback(Result* result) { - return base::Bind(&Result::DidWrite, base::Unretained(result)); - } - - // 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) { - file_writer_delegate_.reset( - CreateWriterDelegate(test_file_path, offset, allowed_growth)); - 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_; - - 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()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateFile( - file_system_context_, GetFileSystemURL("test"))); - net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory); -} - -void FileWriterDelegateTest::TearDown() { - net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL); - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); -} - -TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { - const GURL kBlobURL("blob:nolimit"); - content_ = kData; - - PrepareForWrite("test", kBlobURL, 0, kint64max); - - Result result; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - Result result; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - Result result; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - Result result; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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; - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateFile( - file_system_context_, GetFileSystemURL("test2"))); - - const GURL kBlobURL("blob:nolimitconcurrent"); - const GURL kBlobURL2("blob:nolimitconcurrent2"); - content_ = kData; - - PrepareForWrite("test", kBlobURL, 0, kint64max); - - // Credate another FileWriterDelegate for concurrent write. - file_writer_delegate2.reset(CreateWriterDelegate("test2", 0, kint64max)); - request2.reset(empty_context_.CreateRequest( - kBlobURL2, file_writer_delegate2.get())); - - Result result, result2; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - file_writer_delegate2->Start(request2.Pass(), GetWriteCallback(&result2)); - 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); - - { - Result result; - ASSERT_EQ(0, usage()); - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - { - Result result; - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - { - Result result; - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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"); - - { - Result result; - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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); - - { - Result result; - file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); - 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/chromium/webkit/browser/fileapi/isolated_context.cc b/chromium/webkit/browser/fileapi/isolated_context.cc index d10438aa046..74716104154 100644 --- a/chromium/webkit/browser/fileapi/isolated_context.cc +++ b/chromium/webkit/browser/fileapi/isolated_context.cc @@ -26,8 +26,8 @@ base::FilePath::StringType GetRegisterNameForPath(const base::FilePath& path) { #if defined(FILE_PATH_USES_DRIVE_LETTERS) base::FilePath::StringType name; for (size_t i = 0; - i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]); - ++i) { + i < path.value().size() && !base::FilePath::IsSeparator(path.value()[i]); + ++i) { if (path.value()[i] == L':') { name.append(L"_drive"); break; @@ -78,8 +78,10 @@ bool IsolatedContext::FileInfoSet::AddPath( fileset_.insert(MountPointInfo(utf8name, normalized_path)).second; if (!inserted) { int suffix = 1; - std::string basepart = base::FilePath(name).RemoveExtension().AsUTF8Unsafe(); - std::string ext = base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe(); + std::string basepart = + base::FilePath(name).RemoveExtension().AsUTF8Unsafe(); + std::string ext = + base::FilePath(base::FilePath(name).Extension()).AsUTF8Unsafe(); while (!inserted) { utf8name = base::StringPrintf("%s (%d)", basepart.c_str(), suffix++); if (!ext.empty()) @@ -286,10 +288,12 @@ bool IsolatedContext::GetRegisteredPath( return true; } -bool IsolatedContext::CrackVirtualPath(const base::FilePath& virtual_path, - std::string* id_or_name, - FileSystemType* type, - base::FilePath* path) const { +bool IsolatedContext::CrackVirtualPath( + const base::FilePath& virtual_path, + std::string* id_or_name, + FileSystemType* type, + base::FilePath* path, + FileSystemMountOption* mount_option) const { DCHECK(id_or_name); DCHECK(path); @@ -297,6 +301,9 @@ bool IsolatedContext::CrackVirtualPath(const base::FilePath& virtual_path, if (virtual_path.ReferencesParent()) return false; + // Set the default mount option. + *mount_option = FileSystemMountOption(); + // The virtual_path should comprise <id_or_name> and <relative_path> parts. std::vector<base::FilePath::StringType> components; virtual_path.GetComponents(&components); @@ -426,13 +433,16 @@ FileSystemURL IsolatedContext::CrackFileSystemURL( std::string mount_name; FileSystemType cracked_type; base::FilePath cracked_path; - if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type, &cracked_path)) + FileSystemMountOption cracked_mount_option; + if (!CrackVirtualPath(url.path(), &mount_name, &cracked_type, + &cracked_path, &cracked_mount_option)) { return FileSystemURL(); + } return FileSystemURL( url.origin(), url.mount_type(), url.virtual_path(), !url.filesystem_id().empty() ? url.filesystem_id() : mount_name, - cracked_type, cracked_path, mount_name); + cracked_type, cracked_path, mount_name, cracked_mount_option); } bool IsolatedContext::UnregisterFileSystem(const std::string& filesystem_id) { diff --git a/chromium/webkit/browser/fileapi/isolated_context.h b/chromium/webkit/browser/fileapi/isolated_context.h index 6b3f85ad98e..2a4ae47dd0d 100644 --- a/chromium/webkit/browser/fileapi/isolated_context.h +++ b/chromium/webkit/browser/fileapi/isolated_context.h @@ -144,10 +144,12 @@ class WEBKIT_STORAGE_BROWSER_EXPORT IsolatedContext : public MountPoints { virtual bool RevokeFileSystem(const std::string& filesystem_id) OVERRIDE; virtual bool GetRegisteredPath(const std::string& filesystem_id, base::FilePath* path) const OVERRIDE; - virtual bool CrackVirtualPath(const base::FilePath& virtual_path, - std::string* filesystem_id, - FileSystemType* type, - base::FilePath* path) const OVERRIDE; + virtual bool CrackVirtualPath( + const base::FilePath& virtual_path, + std::string* filesystem_id, + FileSystemType* type, + base::FilePath* path, + FileSystemMountOption* mount_option) const OVERRIDE; virtual FileSystemURL CrackURL(const GURL& url) const OVERRIDE; virtual FileSystemURL CreateCrackedFileSystemURL( const GURL& origin, diff --git a/chromium/webkit/browser/fileapi/isolated_context_unittest.cc b/chromium/webkit/browser/fileapi/isolated_context_unittest.cc index 36073ca5f81..156181d6210 100644 --- a/chromium/webkit/browser/fileapi/isolated_context_unittest.cc +++ b/chromium/webkit/browser/fileapi/isolated_context_unittest.cc @@ -96,8 +96,10 @@ TEST_F(IsolatedContextTest, RegisterAndRevokeTest) { std::string cracked_id; base::FilePath cracked_path; FileSystemType cracked_type; + FileSystemMountOption cracked_option; ASSERT_TRUE(isolated_context()->CrackVirtualPath( - virtual_path, &cracked_id, &cracked_type, &cracked_path)); + virtual_path, &cracked_id, &cracked_type, &cracked_path, + &cracked_option)); ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(), cracked_path.value()); ASSERT_EQ(id_, cracked_id); @@ -186,18 +188,22 @@ TEST_F(IsolatedContextTest, CrackWithRelativePaths) { for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) { SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value() << " " << relatives[j].path); - base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_) - .AppendASCII(names_[i]).Append(relatives[j].path); + base::FilePath virtual_path = + isolated_context()->CreateVirtualRootPath(id_).AppendASCII( + names_[i]).Append(relatives[j].path); std::string cracked_id; base::FilePath cracked_path; FileSystemType cracked_type; + FileSystemMountOption cracked_option; if (!relatives[j].valid) { ASSERT_FALSE(isolated_context()->CrackVirtualPath( - virtual_path, &cracked_id, &cracked_type, &cracked_path)); + virtual_path, &cracked_id, &cracked_type, &cracked_path, + &cracked_option)); continue; } ASSERT_TRUE(isolated_context()->CrackVirtualPath( - virtual_path, &cracked_id, &cracked_type, &cracked_path)); + virtual_path, &cracked_id, &cracked_type, &cracked_path, + &cracked_option)); ASSERT_EQ(kTestPaths[i].Append(relatives[j].path) .NormalizePathSeparators().value(), cracked_path.value()); @@ -230,8 +236,9 @@ TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) { for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) { SCOPED_TRACE(testing::Message() << "Testing " << kTestPaths[i].value() << " " << relatives[j].path); - base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_) - .AppendASCII(names_[i]).Append(relatives[j].path); + base::FilePath virtual_path = + isolated_context()->CreateVirtualRootPath(id_).AppendASCII( + names_[i]).Append(relatives[j].path); FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL( GURL("http://chromium.org"), kFileSystemTypeIsolated, virtual_path); @@ -255,13 +262,14 @@ TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) { TEST_F(IsolatedContextTest, TestWithVirtualRoot) { std::string cracked_id; base::FilePath cracked_path; + FileSystemMountOption cracked_option; // Trying to crack virtual root "/" returns true but with empty cracked path // as "/" of the isolated filesystem is a pure virtual directory // that has no corresponding platform directory. base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_); ASSERT_TRUE(isolated_context()->CrackVirtualPath( - virtual_path, &cracked_id, NULL, &cracked_path)); + virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option)); ASSERT_EQ(FPL(""), cracked_path.value()); ASSERT_EQ(id_, cracked_id); @@ -270,7 +278,7 @@ TEST_F(IsolatedContextTest, TestWithVirtualRoot) { virtual_path = isolated_context()->CreateVirtualRootPath( id_).AppendASCII("foo"); ASSERT_FALSE(isolated_context()->CrackVirtualPath( - virtual_path, &cracked_id, NULL, &cracked_path)); + virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option)); } TEST_F(IsolatedContextTest, CanHandleURL) { @@ -327,8 +335,9 @@ TEST_F(IsolatedContextTest, VirtualFileSystemTests) { std::string cracked_id; base::FilePath cracked_path; + FileSystemMountOption cracked_option; ASSERT_TRUE(isolated_context()->CrackVirtualPath( - whole_virtual_path, &cracked_id, NULL, &cracked_path)); + whole_virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option)); ASSERT_EQ(database_fsid, cracked_id); ASSERT_EQ(test_virtual_path, cracked_path); } diff --git a/chromium/webkit/browser/fileapi/local_file_stream_writer.cc b/chromium/webkit/browser/fileapi/local_file_stream_writer.cc index 75c2cad8bae..1796de5f4e8 100644 --- a/chromium/webkit/browser/fileapi/local_file_stream_writer.cc +++ b/chromium/webkit/browser/fileapi/local_file_stream_writer.cc @@ -84,9 +84,9 @@ LocalFileStreamWriter::LocalFileStreamWriter(base::TaskRunner* task_runner, int64 initial_offset) : file_path_(file_path), initial_offset_(initial_offset), + task_runner_(task_runner), has_pending_operation_(false), - weak_factory_(this), - task_runner_(task_runner) {} + weak_factory_(this) {} int LocalFileStreamWriter::InitiateOpen( const net::CompletionCallback& error_callback, diff --git a/chromium/webkit/browser/fileapi/local_file_stream_writer_unittest.cc b/chromium/webkit/browser/fileapi/local_file_stream_writer_unittest.cc index cf3c06a18cd..96bccf64b26 100644 --- a/chromium/webkit/browser/fileapi/local_file_stream_writer_unittest.cc +++ b/chromium/webkit/browser/fileapi/local_file_stream_writer_unittest.cc @@ -23,8 +23,7 @@ namespace fileapi { class LocalFileStreamWriterTest : public testing::Test { public: LocalFileStreamWriterTest() - : message_loop_(base::MessageLoop::TYPE_IO), - file_thread_("FileUtilProxyTestFileThread") {} + : file_thread_("FileUtilProxyTestFileThread") {} virtual void SetUp() OVERRIDE { ASSERT_TRUE(file_thread_.Start()); @@ -85,7 +84,7 @@ class LocalFileStreamWriterTest : public testing::Test { } private: - base::MessageLoop message_loop_; + base::MessageLoopForIO message_loop_; base::Thread file_thread_; base::ScopedTempDir temp_dir_; }; diff --git a/chromium/webkit/browser/fileapi/local_file_util.cc b/chromium/webkit/browser/fileapi/local_file_util.cc index d2d653ac78c..8f47859a187 100644 --- a/chromium/webkit/browser/fileapi/local_file_util.cc +++ b/chromium/webkit/browser/fileapi/local_file_util.cc @@ -51,7 +51,7 @@ class LocalFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator { base::FilePath LocalFileEnumerator::Next() { base::FilePath next = file_enum_.Next(); // Don't return symlinks. - while (!next.empty() && file_util::IsLink(next)) + while (!next.empty() && base::IsLink(next)) next = file_enum_.Next(); if (next.empty()) return next; @@ -88,7 +88,7 @@ PlatformFileError LocalFileUtil::CreateOrOpen( if (error != base::PLATFORM_FILE_OK) return error; // Disallow opening files in symlinked paths. - if (file_util::IsLink(file_path)) + if (base::IsLink(file_path)) return base::PLATFORM_FILE_ERROR_NOT_FOUND; return NativeFileUtil::CreateOrOpen( file_path, file_flags, file_handle, created); @@ -132,7 +132,7 @@ PlatformFileError LocalFileUtil::GetFileInfo( if (error != base::PLATFORM_FILE_OK) return error; // We should not follow symbolic links in sandboxed file system. - if (file_util::IsLink(file_path)) + if (base::IsLink(file_path)) return base::PLATFORM_FILE_ERROR_NOT_FOUND; error = NativeFileUtil::GetFileInfo(file_path, file_info); if (error == base::PLATFORM_FILE_OK) @@ -197,6 +197,7 @@ PlatformFileError LocalFileUtil::CopyOrMoveFile( FileSystemOperationContext* context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, bool copy) { base::FilePath src_file_path; PlatformFileError error = GetLocalFilePath(context, src_url, &src_file_path); @@ -208,7 +209,9 @@ PlatformFileError LocalFileUtil::CopyOrMoveFile( if (error != base::PLATFORM_FILE_OK) return error; - return NativeFileUtil::CopyOrMoveFile(src_file_path, dest_file_path, copy); + return NativeFileUtil::CopyOrMoveFile( + src_file_path, dest_file_path, option, + fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, copy)); } PlatformFileError LocalFileUtil::CopyInForeignFile( @@ -223,7 +226,10 @@ PlatformFileError LocalFileUtil::CopyInForeignFile( GetLocalFilePath(context, dest_url, &dest_file_path); if (error != base::PLATFORM_FILE_OK) return error; - return NativeFileUtil::CopyOrMoveFile(src_file_path, dest_file_path, true); + return NativeFileUtil::CopyOrMoveFile( + src_file_path, dest_file_path, FileSystemOperation::OPTION_NONE, + fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, + true /* copy */)); } PlatformFileError LocalFileUtil::DeleteFile( diff --git a/chromium/webkit/browser/fileapi/local_file_util.h b/chromium/webkit/browser/fileapi/local_file_util.h index 38f285a6b04..21efc72ffe4 100644 --- a/chromium/webkit/browser/fileapi/local_file_util.h +++ b/chromium/webkit/browser/fileapi/local_file_util.h @@ -72,6 +72,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT LocalFileUtil FileSystemOperationContext* context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, bool copy) OVERRIDE; virtual base::PlatformFileError CopyInForeignFile( FileSystemOperationContext* context, diff --git a/chromium/webkit/browser/fileapi/local_file_util_unittest.cc b/chromium/webkit/browser/fileapi/local_file_util_unittest.cc deleted file mode 100644 index bdd2d879925..00000000000 --- a/chromium/webkit/browser/fileapi/local_file_util_unittest.cc +++ /dev/null @@ -1,388 +0,0 @@ -// 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 "base/file_util.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/platform_file.h" -#include "base/run_loop.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/fileapi/async_file_test_helper.h" -#include "webkit/browser/fileapi/async_file_util_adapter.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/common/fileapi/file_system_types.h" - -namespace fileapi { - -namespace { - -const GURL kOrigin("http://foo/"); -const FileSystemType kFileSystemType = kFileSystemTypeTest; - -} // namespace - -class LocalFileUtilTest : public testing::Test { - public: - LocalFileUtilTest() {} - - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - file_system_context_ = CreateFileSystemContextForTesting( - NULL, data_dir_.path()); - } - - virtual void TearDown() { - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - protected: - FileSystemOperationContext* NewContext() { - FileSystemOperationContext* context = - new FileSystemOperationContext(file_system_context_.get()); - context->set_update_observers( - *file_system_context_->GetUpdateObservers(kFileSystemType)); - return context; - } - - LocalFileUtil* file_util() { - AsyncFileUtilAdapter* adapter = static_cast<AsyncFileUtilAdapter*>( - file_system_context_->GetAsyncFileUtil(kFileSystemType)); - return static_cast<LocalFileUtil*>(adapter->sync_file_util()); - } - - FileSystemURL CreateURL(const std::string& file_name) { - return file_system_context_->CreateCrackedFileSystemURL( - kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name)); - } - - base::FilePath LocalPath(const char *file_name) { - base::FilePath path; - scoped_ptr<FileSystemOperationContext> context(NewContext()); - file_util()->GetLocalFilePath(context.get(), CreateURL(file_name), &path); - return path; - } - - bool FileExists(const char *file_name) { - return base::PathExists(LocalPath(file_name)) && - !base::DirectoryExists(LocalPath(file_name)); - } - - bool DirectoryExists(const char *file_name) { - return base::DirectoryExists(LocalPath(file_name)); - } - - int64 GetSize(const char *file_name) { - base::PlatformFileInfo info; - file_util::GetFileInfo(LocalPath(file_name), &info); - return info.size; - } - - base::PlatformFileError CreateFile(const char* file_name, - base::PlatformFile* file_handle, - bool* created) { - int file_flags = base::PLATFORM_FILE_CREATE | - base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC; - - scoped_ptr<FileSystemOperationContext> context(NewContext()); - return file_util()->CreateOrOpen( - context.get(), - CreateURL(file_name), - file_flags, file_handle, created); - } - - base::PlatformFileError EnsureFileExists(const char* file_name, - bool* created) { - scoped_ptr<FileSystemOperationContext> context(NewContext()); - return file_util()->EnsureFileExists( - context.get(), - CreateURL(file_name), created); - } - - FileSystemContext* file_system_context() { - return file_system_context_.get(); - } - - private: - base::MessageLoop message_loop_; - scoped_refptr<FileSystemContext> file_system_context_; - base::ScopedTempDir data_dir_; - - DISALLOW_COPY_AND_ASSIGN(LocalFileUtilTest); -}; - -TEST_F(LocalFileUtilTest, CreateAndClose) { - const char *file_name = "test_file"; - base::PlatformFile file_handle; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, - CreateFile(file_name, &file_handle, &created)); - ASSERT_TRUE(created); - - EXPECT_TRUE(FileExists(file_name)); - EXPECT_EQ(0, GetSize(file_name)); - - scoped_ptr<FileSystemOperationContext> context(NewContext()); - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Close(context.get(), file_handle)); -} - -// file_util::CreateSymbolicLink is only supported on POSIX. -#if defined(OS_POSIX) -TEST_F(LocalFileUtilTest, CreateFailForSymlink) { - // Create symlink target file. - const char *target_name = "symlink_target"; - base::PlatformFile target_handle; - bool symlink_target_created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - CreateFile(target_name, &target_handle, &symlink_target_created)); - ASSERT_TRUE(symlink_target_created); - base::FilePath target_path = LocalPath(target_name); - - // Create symlink where target must be real file. - const char *symlink_name = "symlink_file"; - base::FilePath symlink_path = LocalPath(symlink_name); - ASSERT_TRUE(file_util::CreateSymbolicLink(target_path, symlink_path)); - ASSERT_TRUE(FileExists(symlink_name)); - - // Try to open the symlink file which should fail. - scoped_ptr<FileSystemOperationContext> context(NewContext()); - FileSystemURL url = CreateURL(symlink_name); - int file_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; - base::PlatformFile file_handle; - bool created = false; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_util()->CreateOrOpen( - context.get(), url, file_flags, &file_handle, &created)); - EXPECT_FALSE(created); -} -#endif - -TEST_F(LocalFileUtilTest, EnsureFileExists) { - const char *file_name = "foobar"; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created)); - ASSERT_TRUE(created); - - EXPECT_TRUE(FileExists(file_name)); - EXPECT_EQ(0, GetSize(file_name)); - - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created)); - EXPECT_FALSE(created); -} - -TEST_F(LocalFileUtilTest, TouchFile) { - const char *file_name = "test_file"; - base::PlatformFile file_handle; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, - CreateFile(file_name, &file_handle, &created)); - ASSERT_TRUE(created); - - scoped_ptr<FileSystemOperationContext> context(NewContext()); - - base::PlatformFileInfo info; - ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info)); - const base::Time new_accessed = - info.last_accessed + base::TimeDelta::FromHours(10); - const base::Time new_modified = - info.last_modified + base::TimeDelta::FromHours(5); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Touch(context.get(), CreateURL(file_name), - new_accessed, new_modified)); - - ASSERT_TRUE(file_util::GetFileInfo(LocalPath(file_name), &info)); - EXPECT_EQ(new_accessed, info.last_accessed); - EXPECT_EQ(new_modified, info.last_modified); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Close(context.get(), file_handle)); -} - -TEST_F(LocalFileUtilTest, TouchDirectory) { - const char *dir_name = "test_dir"; - scoped_ptr<FileSystemOperationContext> context(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->CreateDirectory(context.get(), - CreateURL(dir_name), - false /* exclusive */, - false /* recursive */)); - - base::PlatformFileInfo info; - ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info)); - const base::Time new_accessed = - info.last_accessed + base::TimeDelta::FromHours(10); - const base::Time new_modified = - info.last_modified + base::TimeDelta::FromHours(5); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->Touch(context.get(), CreateURL(dir_name), - new_accessed, new_modified)); - - ASSERT_TRUE(file_util::GetFileInfo(LocalPath(dir_name), &info)); - EXPECT_EQ(new_accessed, info.last_accessed); - EXPECT_EQ(new_modified, info.last_modified); -} - -TEST_F(LocalFileUtilTest, Truncate) { - const char *file_name = "truncated"; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created)); - ASSERT_TRUE(created); - - scoped_ptr<FileSystemOperationContext> context; - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(context.get(), CreateURL(file_name), 1020)); - - EXPECT_TRUE(FileExists(file_name)); - EXPECT_EQ(1020, GetSize(file_name)); -} - -TEST_F(LocalFileUtilTest, CopyFile) { - const char *from_file = "fromfile"; - const char *to_file1 = "tofile1"; - const char *to_file2 = "tofile2"; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created)); - ASSERT_TRUE(created); - - scoped_ptr<FileSystemOperationContext> context; - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); - - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy(file_system_context(), - CreateURL(from_file), - CreateURL(to_file1))); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy(file_system_context(), - CreateURL(from_file), - CreateURL(to_file2))); - - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - EXPECT_TRUE(FileExists(to_file1)); - EXPECT_EQ(1020, GetSize(to_file1)); - EXPECT_TRUE(FileExists(to_file2)); - EXPECT_EQ(1020, GetSize(to_file2)); -} - -TEST_F(LocalFileUtilTest, CopyDirectory) { - const char *from_dir = "fromdir"; - const char *from_file = "fromdir/fromfile"; - const char *to_dir = "todir"; - const char *to_file = "todir/fromfile"; - bool created; - scoped_ptr<FileSystemOperationContext> context; - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->CreateDirectory(context.get(), CreateURL(from_dir), - false, false)); - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created)); - ASSERT_TRUE(created); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); - - EXPECT_TRUE(DirectoryExists(from_dir)); - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - EXPECT_FALSE(DirectoryExists(to_dir)); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy(file_system_context(), - CreateURL(from_dir), CreateURL(to_dir))); - - EXPECT_TRUE(DirectoryExists(from_dir)); - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - EXPECT_TRUE(DirectoryExists(to_dir)); - EXPECT_TRUE(FileExists(to_file)); - EXPECT_EQ(1020, GetSize(to_file)); -} - -TEST_F(LocalFileUtilTest, MoveFile) { - const char *from_file = "fromfile"; - const char *to_file = "tofile"; - bool created; - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created)); - ASSERT_TRUE(created); - scoped_ptr<FileSystemOperationContext> context; - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); - - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Move(file_system_context(), - CreateURL(from_file), - CreateURL(to_file))); - - EXPECT_FALSE(FileExists(from_file)); - EXPECT_TRUE(FileExists(to_file)); - EXPECT_EQ(1020, GetSize(to_file)); -} - -TEST_F(LocalFileUtilTest, MoveDirectory) { - const char *from_dir = "fromdir"; - const char *from_file = "fromdir/fromfile"; - const char *to_dir = "todir"; - const char *to_file = "todir/fromfile"; - bool created; - scoped_ptr<FileSystemOperationContext> context; - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->CreateDirectory(context.get(), CreateURL(from_dir), - false, false)); - ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created)); - ASSERT_TRUE(created); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->Truncate(context.get(), CreateURL(from_file), 1020)); - - EXPECT_TRUE(DirectoryExists(from_dir)); - EXPECT_TRUE(FileExists(from_file)); - EXPECT_EQ(1020, GetSize(from_file)); - EXPECT_FALSE(DirectoryExists(to_dir)); - - context.reset(NewContext()); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Move(file_system_context(), - CreateURL(from_dir), - CreateURL(to_dir))); - - EXPECT_FALSE(DirectoryExists(from_dir)); - EXPECT_TRUE(DirectoryExists(to_dir)); - EXPECT_TRUE(FileExists(to_file)); - EXPECT_EQ(1020, GetSize(to_file)); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/mock_file_system_context.cc b/chromium/webkit/browser/fileapi/mock_file_system_context.cc deleted file mode 100644 index ed1715c2bbe..00000000000 --- a/chromium/webkit/browser/fileapi/mock_file_system_context.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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/mock_file_system_context.h" - -#include "base/memory/scoped_vector.h" -#include "webkit/browser/fileapi/external_mount_points.h" -#include "webkit/browser/fileapi/file_system_backend.h" -#include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/mock_file_system_options.h" -#include "webkit/browser/fileapi/test_file_system_backend.h" -#include "webkit/browser/quota/mock_special_storage_policy.h" - -namespace fileapi { - -FileSystemContext* CreateFileSystemContextForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - const base::FilePath& base_path) { - ScopedVector<FileSystemBackend> additional_providers; - additional_providers.push_back(new TestFileSystemBackend( - base::MessageLoopProxy::current().get(), base_path)); - return CreateFileSystemContextWithAdditionalProvidersForTesting( - quota_manager_proxy, additional_providers.Pass(), base_path); -} - -FileSystemContext* CreateFileSystemContextWithAdditionalProvidersForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - ScopedVector<FileSystemBackend> additional_providers, - const base::FilePath& base_path) { - return new FileSystemContext( - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - ExternalMountPoints::CreateRefCounted().get(), - make_scoped_refptr(new quota::MockSpecialStoragePolicy()).get(), - quota_manager_proxy, - additional_providers.Pass(), - base_path, - CreateAllowFileAccessOptions()); -} - -FileSystemContext* CreateIncognitoFileSystemContextForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - const base::FilePath& base_path) { - ScopedVector<FileSystemBackend> additional_providers; - return new FileSystemContext( - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - ExternalMountPoints::CreateRefCounted().get(), - make_scoped_refptr(new quota::MockSpecialStoragePolicy()).get(), - quota_manager_proxy, - additional_providers.Pass(), - base_path, - CreateIncognitoFileSystemOptions()); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/mock_file_system_context.h b/chromium/webkit/browser/fileapi/mock_file_system_context.h deleted file mode 100644 index 83b904d6fde..00000000000 --- a/chromium/webkit/browser/fileapi/mock_file_system_context.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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_MOCK_FILE_SYSTEM_CONTEXT_H_ -#define WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_CONTEXT_H_ - -#include "base/files/file_path.h" -#include "base/memory/scoped_vector.h" - -namespace quota { -class QuotaManagerProxy; -class SpecialStoragePolicy; -} - -namespace fileapi { - -class FileSystemContext; -class FileSystemBackend; - -FileSystemContext* CreateFileSystemContextForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - const base::FilePath& base_path); - -// The caller is responsible for including TestFileSystemBackend in -// |additional_providers| if needed. -FileSystemContext* CreateFileSystemContextWithAdditionalProvidersForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - ScopedVector<FileSystemBackend> additional_providers, - const base::FilePath& base_path); - -FileSystemContext* CreateIncognitoFileSystemContextForTesting( - quota::QuotaManagerProxy* quota_manager_proxy, - const base::FilePath& base_path); - -} // namespace fileapi - -#endif // WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_CONTEXT_H_ diff --git a/chromium/webkit/browser/fileapi/mock_file_system_options.cc b/chromium/webkit/browser/fileapi/mock_file_system_options.cc deleted file mode 100644 index 06fbc27c20d..00000000000 --- a/chromium/webkit/browser/fileapi/mock_file_system_options.cc +++ /dev/null @@ -1,40 +0,0 @@ -// 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/mock_file_system_options.h" - -#include <string> -#include <vector> - -namespace fileapi { - -FileSystemOptions CreateIncognitoFileSystemOptions() { - std::vector<std::string> additional_allowed_schemes; -#if defined(OS_CHROMEOS) - additional_allowed_schemes.push_back("chrome-extension"); -#endif - return FileSystemOptions(FileSystemOptions::PROFILE_MODE_INCOGNITO, - additional_allowed_schemes); -}; - -FileSystemOptions CreateAllowFileAccessOptions() { - std::vector<std::string> additional_allowed_schemes; - additional_allowed_schemes.push_back("file"); -#if defined(OS_CHROMEOS) - additional_allowed_schemes.push_back("chrome-extension"); -#endif - return FileSystemOptions(FileSystemOptions::PROFILE_MODE_NORMAL, - additional_allowed_schemes); -}; - -FileSystemOptions CreateDisallowFileAccessOptions() { - std::vector<std::string> additional_allowed_schemes; -#if defined(OS_CHROMEOS) - additional_allowed_schemes.push_back("chrome-extension"); -#endif - return FileSystemOptions(FileSystemOptions::PROFILE_MODE_NORMAL, - additional_allowed_schemes); -}; - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/mock_file_system_options.h b/chromium/webkit/browser/fileapi/mock_file_system_options.h deleted file mode 100644 index f46f6354611..00000000000 --- a/chromium/webkit/browser/fileapi/mock_file_system_options.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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_MOCK_FILE_SYSTEM_OPTIONS_H_ -#define WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_OPTIONS_H_ - -#include "webkit/browser/fileapi/file_system_options.h" - -namespace fileapi { - -// Returns Filesystem options for incognito mode. -FileSystemOptions CreateIncognitoFileSystemOptions(); - -// Returns Filesystem options that allow file access. -FileSystemOptions CreateAllowFileAccessOptions(); - -// Returns Filesystem options that disallow file access. -FileSystemOptions CreateDisallowFileAccessOptions(); - -} // namespace fileapi - -#endif // WEBKIT_BROWSER_FILEAPI_MOCK_FILE_SYSTEM_OPTIONS_H_ diff --git a/chromium/webkit/browser/fileapi/mount_points.h b/chromium/webkit/browser/fileapi/mount_points.h index 5ec67b95a9c..4ebf6e7ec3b 100644 --- a/chromium/webkit/browser/fileapi/mount_points.h +++ b/chromium/webkit/browser/fileapi/mount_points.h @@ -16,6 +16,7 @@ class GURL; namespace fileapi { +class FileSystemMountOption; class FileSystemURL; } @@ -86,7 +87,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT MountPoints { virtual bool CrackVirtualPath(const base::FilePath& virtual_path, std::string* mount_name, FileSystemType* type, - base::FilePath* path) const = 0; + base::FilePath* path, + FileSystemMountOption* mount_option) const = 0; protected: friend class FileSystemContext; diff --git a/chromium/webkit/browser/fileapi/native_file_util.cc b/chromium/webkit/browser/fileapi/native_file_util.cc index 32e9467091c..9754d6c64a8 100644 --- a/chromium/webkit/browser/fileapi/native_file_util.cc +++ b/chromium/webkit/browser/fileapi/native_file_util.cc @@ -7,7 +7,9 @@ #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/memory/scoped_ptr.h" +#include "net/base/file_stream.h" #include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace fileapi { @@ -32,6 +34,42 @@ bool SetPlatformSpecificDirectoryPermissions(const base::FilePath& dir_path) { return true; } +// Copies a file |from| to |to|, and ensure the written content is synced to +// the disk. This is essentially base::CopyFile followed by fsync(). +bool CopyFileAndSync(const base::FilePath& from, const base::FilePath& to) { + net::FileStream infile(NULL); + if (infile.OpenSync(from, + base::PLATFORM_FILE_OPEN | base:: PLATFORM_FILE_READ) < 0) { + return false; + } + + net::FileStream outfile(NULL); + if (outfile.OpenSync(to, + base::PLATFORM_FILE_CREATE_ALWAYS | base:: PLATFORM_FILE_WRITE) < 0) { + return false; + } + + const int kBufferSize = 32768; + std::vector<char> buffer(kBufferSize); + + for (;;) { + int bytes_read = infile.ReadSync(&buffer[0], kBufferSize); + if (bytes_read < 0) + return false; + if (bytes_read == 0) + break; + for (int bytes_written = 0; bytes_written < bytes_read; ) { + int bytes_written_partial = outfile.WriteSync(&buffer[bytes_written], + bytes_read - bytes_written); + if (bytes_written_partial < 0) + return false; + bytes_written += bytes_written_partial; + } + } + + return outfile.FlushSync() >= 0; +} + } // namespace using base::PlatformFile; @@ -76,6 +114,15 @@ bool NativeFileEnumerator::IsDirectory() { return file_util_info_.IsDirectory(); } +NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination( + const FileSystemURL& dest_url, bool copy) { + if (copy) { + return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ? + COPY_SYNC : COPY_NOSYNC; + } + return MOVE; +} + PlatformFileError NativeFileUtil::CreateOrOpen( const base::FilePath& path, int file_flags, PlatformFile* file_handle, bool* created) { @@ -137,11 +184,15 @@ PlatformFileError NativeFileUtil::CreateDirectory( if (path_exists && !base::DirectoryExists(path)) return base::PLATFORM_FILE_ERROR_EXISTS; - if (!file_util::CreateDirectory(path)) + if (!base::CreateDirectory(path)) return base::PLATFORM_FILE_ERROR_FAILED; - if (!SetPlatformSpecificDirectoryPermissions(path)) - return base::PLATFORM_FILE_ERROR_FAILED; + if (!SetPlatformSpecificDirectoryPermissions(path)) { + // Since some file systems don't support permission setting, we do not treat + // an error from the function as the failure of copying. Just log it. + LOG(WARNING) << "Setting directory permission failed: " + << path.AsUTF8Unsafe(); + } return base::PLATFORM_FILE_OK; } @@ -151,7 +202,7 @@ PlatformFileError NativeFileUtil::GetFileInfo( base::PlatformFileInfo* file_info) { if (!base::PathExists(path)) return base::PLATFORM_FILE_ERROR_NOT_FOUND; - if (!file_util::GetFileInfo(path, file_info)) + if (!base::GetFileInfo(path, file_info)) return base::PLATFORM_FILE_ERROR_FAILED; return base::PLATFORM_FILE_OK; } @@ -169,8 +220,7 @@ PlatformFileError NativeFileUtil::Touch( const base::FilePath& path, const base::Time& last_access_time, const base::Time& last_modified_time) { - if (!file_util::TouchFile( - path, last_access_time, last_modified_time)) + if (!base::TouchFile(path, last_access_time, last_modified_time)) return base::PLATFORM_FILE_ERROR_FAILED; return base::PLATFORM_FILE_OK; } @@ -205,13 +255,15 @@ bool NativeFileUtil::DirectoryExists(const base::FilePath& path) { PlatformFileError NativeFileUtil::CopyOrMoveFile( const base::FilePath& src_path, const base::FilePath& dest_path, - bool copy) { + FileSystemOperation::CopyOrMoveOption option, + CopyOrMoveMode mode) { base::PlatformFileInfo info; base::PlatformFileError error = NativeFileUtil::GetFileInfo(src_path, &info); if (error != base::PLATFORM_FILE_OK) return error; if (info.is_directory) return base::PLATFORM_FILE_ERROR_NOT_A_FILE; + base::Time last_modified = info.last_modified; error = NativeFileUtil::GetFileInfo(dest_path, &info); if (error != base::PLATFORM_FILE_OK && @@ -227,14 +279,27 @@ PlatformFileError NativeFileUtil::CopyOrMoveFile( return base::PLATFORM_FILE_ERROR_NOT_FOUND; } - if (copy) { - if (base::CopyFile(src_path, dest_path)) - return base::PLATFORM_FILE_OK; - } else { - if (base::Move(src_path, dest_path)) - return base::PLATFORM_FILE_OK; + switch (mode) { + case COPY_NOSYNC: + if (!base::CopyFile(src_path, dest_path)) + return base::PLATFORM_FILE_ERROR_FAILED; + break; + case COPY_SYNC: + if (!CopyFileAndSync(src_path, dest_path)) + return base::PLATFORM_FILE_ERROR_FAILED; + break; + case MOVE: + if (!base::Move(src_path, dest_path)) + return base::PLATFORM_FILE_ERROR_FAILED; + break; } - return base::PLATFORM_FILE_ERROR_FAILED; + + // Preserve the last modified time. Do not return error here even if + // the setting is failed, because the copy itself is successfully done. + if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED) + base::TouchFile(dest_path, last_modified, last_modified); + + return base::PLATFORM_FILE_OK; } PlatformFileError NativeFileUtil::DeleteFile(const base::FilePath& path) { @@ -252,7 +317,7 @@ PlatformFileError NativeFileUtil::DeleteDirectory(const base::FilePath& path) { return base::PLATFORM_FILE_ERROR_NOT_FOUND; if (!base::DirectoryExists(path)) return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; - if (!file_util::IsDirectoryEmpty(path)) + if (!base::IsDirectoryEmpty(path)) return base::PLATFORM_FILE_ERROR_NOT_EMPTY; if (!base::DeleteFile(path, false)) return base::PLATFORM_FILE_ERROR_FAILED; diff --git a/chromium/webkit/browser/fileapi/native_file_util.h b/chromium/webkit/browser/fileapi/native_file_util.h index 425e7421597..4c36e5ff951 100644 --- a/chromium/webkit/browser/fileapi/native_file_util.h +++ b/chromium/webkit/browser/fileapi/native_file_util.h @@ -30,6 +30,14 @@ namespace fileapi { // inherit from FileSystemFileUtil. class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE NativeFileUtil { public: + enum CopyOrMoveMode { + COPY_NOSYNC, + COPY_SYNC, + MOVE + }; + static CopyOrMoveMode CopyOrMoveModeForDestination( + const FileSystemURL& dest_url, bool copy); + static base::PlatformFileError CreateOrOpen( const base::FilePath& path, int file_flags, @@ -53,9 +61,11 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE NativeFileUtil { int64 length); static bool PathExists(const base::FilePath& path); static bool DirectoryExists(const base::FilePath& path); - static base::PlatformFileError CopyOrMoveFile(const base::FilePath& src_path, - const base::FilePath& dest_path, - bool copy); + static base::PlatformFileError CopyOrMoveFile( + const base::FilePath& src_path, + const base::FilePath& dest_path, + FileSystemOperation::CopyOrMoveOption option, + CopyOrMoveMode mode); static base::PlatformFileError DeleteFile(const base::FilePath& path); static base::PlatformFileError DeleteDirectory(const base::FilePath& path); diff --git a/chromium/webkit/browser/fileapi/native_file_util_unittest.cc b/chromium/webkit/browser/fileapi/native_file_util_unittest.cc index 1ed699e2e44..73b3e257025 100644 --- a/chromium/webkit/browser/fileapi/native_file_util_unittest.cc +++ b/chromium/webkit/browser/fileapi/native_file_util_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <set> + #include "base/file_util.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" @@ -35,7 +37,7 @@ class NativeFileUtilTest : public testing::Test { int64 GetSize(const base::FilePath& path) { base::PlatformFileInfo info; - file_util::GetFileInfo(path, &info); + base::GetFileInfo(path, &info); return info.size; } @@ -124,7 +126,7 @@ TEST_F(NativeFileUtilTest, TouchFileAndGetFileInfo) { ASSERT_TRUE(created); base::PlatformFileInfo info; - ASSERT_TRUE(file_util::GetFileInfo(file_name, &info)); + ASSERT_TRUE(base::GetFileInfo(file_name, &info)); ASSERT_EQ(base::PLATFORM_FILE_OK, NativeFileUtil::GetFileInfo(file_name, &native_info)); ASSERT_EQ(info.size, native_info.size); @@ -143,7 +145,7 @@ TEST_F(NativeFileUtilTest, TouchFileAndGetFileInfo) { NativeFileUtil::Touch(file_name, new_accessed, new_modified)); - ASSERT_TRUE(file_util::GetFileInfo(file_name, &info)); + ASSERT_TRUE(base::GetFileInfo(file_name, &info)); EXPECT_EQ(new_accessed, info.last_accessed); EXPECT_EQ(new_modified, info.last_modified); } @@ -213,6 +215,8 @@ TEST_F(NativeFileUtilTest, CopyFile) { base::FilePath from_file = Path("fromfile"); base::FilePath to_file1 = Path("tofile1"); base::FilePath to_file2 = Path("tofile2"); + const NativeFileUtil::CopyOrMoveMode nosync = NativeFileUtil::COPY_NOSYNC; + const NativeFileUtil::CopyOrMoveMode sync = NativeFileUtil::COPY_SYNC; bool created = false; ASSERT_EQ(base::PLATFORM_FILE_OK, NativeFileUtil::EnsureFileExists(from_file, &created)); @@ -225,10 +229,12 @@ TEST_F(NativeFileUtilTest, CopyFile) { EXPECT_EQ(1020, GetSize(from_file)); ASSERT_EQ(base::PLATFORM_FILE_OK, - NativeFileUtil::CopyOrMoveFile(from_file, to_file1, true)); + NativeFileUtil::CopyOrMoveFile( + from_file, to_file1, FileSystemOperation::OPTION_NONE, nosync)); ASSERT_EQ(base::PLATFORM_FILE_OK, - NativeFileUtil::CopyOrMoveFile(from_file, to_file2, true)); + NativeFileUtil::CopyOrMoveFile( + from_file, to_file2, FileSystemOperation::OPTION_NONE, sync)); EXPECT_TRUE(FileExists(from_file)); EXPECT_EQ(1020, GetSize(from_file)); @@ -243,37 +249,43 @@ TEST_F(NativeFileUtilTest, CopyFile) { ASSERT_TRUE(base::DirectoryExists(dir)); base::FilePath to_dir_file = dir.AppendASCII("file"); ASSERT_EQ(base::PLATFORM_FILE_OK, - NativeFileUtil::CopyOrMoveFile(from_file, to_dir_file, true)); + NativeFileUtil::CopyOrMoveFile( + from_file, to_dir_file, + FileSystemOperation::OPTION_NONE, nosync)); EXPECT_TRUE(FileExists(to_dir_file)); EXPECT_EQ(1020, GetSize(to_dir_file)); // Following tests are error checking. // Source doesn't exist. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(Path("nonexists"), Path("file"), - true)); + NativeFileUtil::CopyOrMoveFile( + Path("nonexists"), Path("file"), + FileSystemOperation::OPTION_NONE, nosync)); // Source is not a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, - NativeFileUtil::CopyOrMoveFile(dir, Path("file"), true)); + NativeFileUtil::CopyOrMoveFile( + dir, Path("file"), FileSystemOperation::OPTION_NONE, nosync)); // Destination is not a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, - NativeFileUtil::CopyOrMoveFile(from_file, dir, true)); + NativeFileUtil::CopyOrMoveFile( + from_file, dir, FileSystemOperation::OPTION_NONE, nosync)); // Destination's parent doesn't exist. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(from_file, - Path("nodir").AppendASCII("file"), - true)); + NativeFileUtil::CopyOrMoveFile( + from_file, Path("nodir").AppendASCII("file"), + FileSystemOperation::OPTION_NONE, nosync)); // Destination's parent is a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(from_file, - Path("tofile1").AppendASCII("file"), - true)); + NativeFileUtil::CopyOrMoveFile( + from_file, Path("tofile1").AppendASCII("file"), + FileSystemOperation::OPTION_NONE, nosync)); } TEST_F(NativeFileUtilTest, MoveFile) { base::FilePath from_file = Path("fromfile"); base::FilePath to_file = Path("tofile"); + const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE; bool created = false; ASSERT_EQ(base::PLATFORM_FILE_OK, NativeFileUtil::EnsureFileExists(from_file, &created)); @@ -285,7 +297,8 @@ TEST_F(NativeFileUtilTest, MoveFile) { EXPECT_EQ(1020, GetSize(from_file)); ASSERT_EQ(base::PLATFORM_FILE_OK, - NativeFileUtil::CopyOrMoveFile(from_file, to_file, false)); + NativeFileUtil::CopyOrMoveFile( + from_file, to_file, FileSystemOperation::OPTION_NONE, move)); EXPECT_FALSE(FileExists(from_file)); EXPECT_TRUE(FileExists(to_file)); @@ -302,7 +315,9 @@ TEST_F(NativeFileUtilTest, MoveFile) { ASSERT_TRUE(base::DirectoryExists(dir)); base::FilePath to_dir_file = dir.AppendASCII("file"); ASSERT_EQ(base::PLATFORM_FILE_OK, - NativeFileUtil::CopyOrMoveFile(from_file, to_dir_file, false)); + NativeFileUtil::CopyOrMoveFile( + from_file, to_dir_file, + FileSystemOperation::OPTION_NONE, move)); EXPECT_FALSE(FileExists(from_file)); EXPECT_TRUE(FileExists(to_dir_file)); EXPECT_EQ(1020, GetSize(to_dir_file)); @@ -310,32 +325,88 @@ TEST_F(NativeFileUtilTest, MoveFile) { // Following is error checking. // Source doesn't exist. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(Path("nonexists"), Path("file"), - false)); + NativeFileUtil::CopyOrMoveFile( + Path("nonexists"), Path("file"), + FileSystemOperation::OPTION_NONE, move)); // Source is not a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, - NativeFileUtil::CopyOrMoveFile(dir, Path("file"), false)); + NativeFileUtil::CopyOrMoveFile( + dir, Path("file"), FileSystemOperation::OPTION_NONE, move)); ASSERT_EQ(base::PLATFORM_FILE_OK, NativeFileUtil::EnsureFileExists(from_file, &created)); ASSERT_TRUE(FileExists(from_file)); // Destination is not a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, - NativeFileUtil::CopyOrMoveFile(from_file, dir, false)); + NativeFileUtil::CopyOrMoveFile( + from_file, dir, FileSystemOperation::OPTION_NONE, move)); ASSERT_EQ(base::PLATFORM_FILE_OK, NativeFileUtil::EnsureFileExists(from_file, &created)); ASSERT_TRUE(FileExists(from_file)); // Destination's parent doesn't exist. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(from_file, - Path("nodir").AppendASCII("file"), - false)); + NativeFileUtil::CopyOrMoveFile( + from_file, Path("nodir").AppendASCII("file"), + FileSystemOperation::OPTION_NONE, move)); // Destination's parent is a file. EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - NativeFileUtil::CopyOrMoveFile(from_file, - Path("tofile1").AppendASCII("file"), - false)); + NativeFileUtil::CopyOrMoveFile( + from_file, Path("tofile1").AppendASCII("file"), + FileSystemOperation::OPTION_NONE, move)); +} + +TEST_F(NativeFileUtilTest, PreserveLastModified) { + base::FilePath from_file = Path("fromfile"); + base::FilePath to_file1 = Path("tofile1"); + base::FilePath to_file2 = Path("tofile2"); + base::FilePath to_file3 = Path("tofile3"); + bool created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::EnsureFileExists(from_file, &created)); + ASSERT_TRUE(created); + EXPECT_TRUE(FileExists(from_file)); + + base::PlatformFileInfo file_info1; + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::GetFileInfo(from_file, &file_info1)); + + // Test for copy (nosync). + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::CopyOrMoveFile( + from_file, to_file1, + FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, + NativeFileUtil::COPY_NOSYNC)); + + base::PlatformFileInfo file_info2; + ASSERT_TRUE(FileExists(to_file1)); + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::GetFileInfo(to_file1, &file_info2)); + EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); + + // Test for copy (sync). + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::CopyOrMoveFile( + from_file, to_file2, + FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, + NativeFileUtil::COPY_SYNC)); + + ASSERT_TRUE(FileExists(to_file2)); + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::GetFileInfo(to_file1, &file_info2)); + EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); + + // Test for move. + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::CopyOrMoveFile( + from_file, to_file3, + FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, + NativeFileUtil::MOVE)); + + ASSERT_TRUE(FileExists(to_file3)); + ASSERT_EQ(base::PLATFORM_FILE_OK, + NativeFileUtil::GetFileInfo(to_file2, &file_info2)); + EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); } } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/obfuscated_file_util.cc b/chromium/webkit/browser/fileapi/obfuscated_file_util.cc index 341f7d0a8bb..c324f10800a 100644 --- a/chromium/webkit/browser/fileapi/obfuscated_file_util.cc +++ b/chromium/webkit/browser/fileapi/obfuscated_file_util.cc @@ -28,6 +28,7 @@ #include "webkit/browser/fileapi/sandbox_file_system_backend.h" #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h" #include "webkit/browser/fileapi/sandbox_origin_database.h" +#include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h" #include "webkit/browser/fileapi/timed_task_helper.h" #include "webkit/browser/quota/quota_manager.h" #include "webkit/common/database/database_identifier.h" @@ -60,9 +61,9 @@ void InitFileInfo( // Costs computed as per crbug.com/86114, based on the LevelDB implementation of // path storage under Linux. It's not clear if that will differ on Windows, on -// which base::FilePath uses wide chars [since they're converted to UTF-8 for storage -// anyway], but as long as the cost is high enough that one can't cheat on quota -// by storing data in paths, it doesn't need to be all that accurate. +// which base::FilePath uses wide chars [since they're converted to UTF-8 for +// storage anyway], but as long as the cost is high enough that one can't cheat +// on quota by storing data in paths, it doesn't need to be all that accurate. const int64 kPathCreationQuotaCost = 146; // Bytes per inode, basically. const int64 kPathByteQuotaCost = 2; // Bytes per byte of path length in UTF-8. @@ -96,10 +97,6 @@ void TouchDirectory(SandboxDirectoryDatabase* db, FileId dir_id) { NOTREACHED(); } -const base::FilePath::CharType kTemporaryDirectoryName[] = FILE_PATH_LITERAL("t"); -const base::FilePath::CharType kPersistentDirectoryName[] = FILE_PATH_LITERAL("p"); -const base::FilePath::CharType kSyncableDirectoryName[] = FILE_PATH_LITERAL("s"); - enum IsolatedOriginStatus { kIsolatedOriginMatch, kIsolatedOriginDontMatch, @@ -123,8 +120,7 @@ class ObfuscatedFileEnumerator : db_(db), context_(context), obfuscated_file_util_(obfuscated_file_util), - origin_(root_url.origin()), - type_(root_url.type()), + root_url_(root_url), recursive_(recursive), current_file_id_(0) { base::FilePath root_virtual_path = root_url.path(); @@ -151,7 +147,7 @@ class ObfuscatedFileEnumerator base::FilePath platform_file_path; base::PlatformFileError error = obfuscated_file_util_->GetFileInfoInternal( - db_, context_, origin_, type_, current_file_id_, + db_, context_, root_url_, current_file_id_, &file_info, ¤t_platform_file_info_, &platform_file_path); if (error != base::PLATFORM_FILE_OK) return Next(); @@ -201,8 +197,7 @@ class ObfuscatedFileEnumerator SandboxDirectoryDatabase* db_; FileSystemOperationContext* context_; ObfuscatedFileUtil* obfuscated_file_util_; - GURL origin_; - FileSystemType type_; + FileSystemURL root_url_; bool recursive_; std::queue<FileRecord> recurse_queue_; @@ -239,17 +234,15 @@ class ObfuscatedOriginEnumerator } // Returns the current origin's information. - virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE { + virtual bool HasTypeDirectory(const std::string& type_string) const OVERRIDE { if (current_.path.empty()) return false; - base::FilePath::StringType type_string = - ObfuscatedFileUtil::GetDirectoryNameForType(type); if (type_string.empty()) { NOTREACHED(); return false; } base::FilePath path = - base_file_path_.Append(current_.path).Append(type_string); + base_file_path_.Append(current_.path).AppendASCII(type_string); return base::DirectoryExists(path); } @@ -262,11 +255,17 @@ class ObfuscatedOriginEnumerator ObfuscatedFileUtil::ObfuscatedFileUtil( quota::SpecialStoragePolicy* special_storage_policy, const base::FilePath& file_system_directory, - base::SequencedTaskRunner* file_task_runner) + base::SequencedTaskRunner* file_task_runner, + const GetTypeStringForURLCallback& get_type_string_for_url, + const std::set<std::string>& known_type_strings, + SandboxFileSystemBackendDelegate* sandbox_delegate) : special_storage_policy_(special_storage_policy), file_system_directory_(file_system_directory), - db_flush_delay_seconds_(10 * 60), // 10 mins. - file_task_runner_(file_task_runner) { + db_flush_delay_seconds_(10 * 60), // 10 mins. + file_task_runner_(file_task_runner), + get_type_string_for_url_(get_type_string_for_url), + known_type_strings_(known_type_strings), + sandbox_delegate_(sandbox_delegate) { } ObfuscatedFileUtil::~ObfuscatedFileUtil() { @@ -281,10 +280,10 @@ PlatformFileError ObfuscatedFileUtil::CreateOrOpen( file_handle, created); if (*file_handle != base::kInvalidPlatformFileValue && file_flags & base::PLATFORM_FILE_WRITE && - context->quota_limit_type() == quota::kQuotaLimitTypeUnlimited) { + context->quota_limit_type() == quota::kQuotaLimitTypeUnlimited && + sandbox_delegate_) { DCHECK_EQ(base::PLATFORM_FILE_OK, error); - context->file_system_context()->sandbox_delegate()-> - StickyInvalidateUsageCache(url.origin(), url.type()); + sandbox_delegate_->StickyInvalidateUsageCache(url.origin(), url.type()); } return error; } @@ -299,8 +298,7 @@ PlatformFileError ObfuscatedFileUtil::EnsureFileExists( FileSystemOperationContext* context, const FileSystemURL& url, bool* created) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; @@ -329,7 +327,7 @@ PlatformFileError ObfuscatedFileUtil::EnsureFileExists( if (!AllocateQuota(context, growth)) return base::PLATFORM_FILE_ERROR_NO_SPACE; PlatformFileError error = CreateFile( - context, base::FilePath(), url.origin(), url.type(), &file_info, 0, NULL); + context, base::FilePath(), url, &file_info, 0, NULL); if (created && base::PLATFORM_FILE_OK == error) { *created = true; UpdateUsage(context, url, growth); @@ -344,8 +342,7 @@ PlatformFileError ObfuscatedFileUtil::CreateDirectory( const FileSystemURL& url, bool exclusive, bool recursive) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; @@ -374,6 +371,8 @@ PlatformFileError ObfuscatedFileUtil::CreateDirectory( if (!db->GetChildWithName(parent_id, name, &parent_id)) break; } + if (!db->IsDirectory(parent_id)) + return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; if (!recursive && components.size() - index > 1) return base::PLATFORM_FILE_ERROR_NOT_FOUND; bool first = true; @@ -406,16 +405,14 @@ PlatformFileError ObfuscatedFileUtil::GetFileInfo( const FileSystemURL& url, base::PlatformFileInfo* file_info, base::FilePath* platform_file_path) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), false); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); if (!db) return base::PLATFORM_FILE_ERROR_NOT_FOUND; FileId file_id; if (!db->GetFileWithPath(url.path(), &file_id)) return base::PLATFORM_FILE_ERROR_NOT_FOUND; FileInfo local_info; - return GetFileInfoInternal(db, context, - url.origin(), url.type(), + return GetFileInfoInternal(db, context, url, file_id, &local_info, file_info, platform_file_path); } @@ -431,8 +428,7 @@ PlatformFileError ObfuscatedFileUtil::GetLocalFilePath( FileSystemOperationContext* context, const FileSystemURL& url, base::FilePath* local_path) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), false); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); if (!db) return base::PLATFORM_FILE_ERROR_NOT_FOUND; FileId file_id; @@ -444,8 +440,7 @@ PlatformFileError ObfuscatedFileUtil::GetLocalFilePath( // Directories have no local file path. return base::PLATFORM_FILE_ERROR_NOT_FOUND; } - *local_path = DataPathToLocalPath( - url.origin(), url.type(), file_info.data_path); + *local_path = DataPathToLocalPath(url, file_info.data_path); if (local_path->empty()) return base::PLATFORM_FILE_ERROR_NOT_FOUND; @@ -457,8 +452,7 @@ PlatformFileError ObfuscatedFileUtil::Touch( const FileSystemURL& url, const base::Time& last_access_time, const base::Time& last_modified_time) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), false); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); if (!db) return base::PLATFORM_FILE_ERROR_NOT_FOUND; FileId file_id; @@ -475,10 +469,9 @@ PlatformFileError ObfuscatedFileUtil::Touch( return base::PLATFORM_FILE_ERROR_FAILED; return base::PLATFORM_FILE_OK; } - base::FilePath local_path = DataPathToLocalPath( - url.origin(), url.type(), file_info.data_path); return NativeFileUtil::Touch( - local_path, last_access_time, last_modified_time); + DataPathToLocalPath(url, file_info.data_path), + last_access_time, last_modified_time); } PlatformFileError ObfuscatedFileUtil::Truncate( @@ -508,13 +501,13 @@ PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile( FileSystemOperationContext* context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, bool copy) { // Cross-filesystem copies and moves should be handled via CopyInForeignFile. DCHECK(src_url.origin() == dest_url.origin()); DCHECK(src_url.type() == dest_url.type()); - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - src_url.origin(), src_url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(src_url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; @@ -530,7 +523,7 @@ PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile( base::PlatformFileInfo src_platform_file_info; base::FilePath src_local_path; base::PlatformFileError error = GetFileInfoInternal( - db, context, src_url.origin(), src_url.type(), src_file_id, + db, context, src_url, src_file_id, &src_file_info, &src_platform_file_info, &src_local_path); if (error != base::PLATFORM_FILE_OK) return error; @@ -542,7 +535,7 @@ PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile( base::FilePath dest_local_path; // overwrite case only if (overwrite) { base::PlatformFileError error = GetFileInfoInternal( - db, context, dest_url.origin(), dest_url.type(), dest_file_id, + db, context, dest_url, dest_file_id, &dest_file_info, &dest_platform_file_info, &dest_local_path); if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) overwrite = false; // fallback to non-overwrite case @@ -596,11 +589,12 @@ PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile( error = NativeFileUtil::CopyOrMoveFile( src_local_path, dest_local_path, - true /* copy */); + option, + fileapi::NativeFileUtil::CopyOrMoveModeForDestination( + dest_url, true /* copy */)); } else { // non-overwrite error = CreateFile(context, src_local_path, - dest_url.origin(), dest_url.type(), - &dest_file_info, 0, NULL); + dest_url, &dest_file_info, 0, NULL); } } else { if (overwrite) { @@ -649,13 +643,12 @@ PlatformFileError ObfuscatedFileUtil::CopyInForeignFile( FileSystemOperationContext* context, const base::FilePath& src_file_path, const FileSystemURL& dest_url) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - dest_url.origin(), dest_url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; base::PlatformFileInfo src_platform_file_info; - if (!file_util::GetFileInfo(src_file_path, &src_platform_file_info)) + if (!base::GetFileInfo(src_file_path, &src_platform_file_info)) return base::PLATFORM_FILE_ERROR_NOT_FOUND; FileId dest_file_id; @@ -667,7 +660,7 @@ PlatformFileError ObfuscatedFileUtil::CopyInForeignFile( if (overwrite) { base::FilePath dest_local_path; base::PlatformFileError error = GetFileInfoInternal( - db, context, dest_url.origin(), dest_url.type(), dest_file_id, + db, context, dest_url, dest_file_id, &dest_file_info, &dest_platform_file_info, &dest_local_path); if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) overwrite = false; // fallback to non-overwrite case @@ -698,14 +691,16 @@ PlatformFileError ObfuscatedFileUtil::CopyInForeignFile( base::PlatformFileError error; if (overwrite) { - base::FilePath dest_local_path = DataPathToLocalPath( - dest_url.origin(), dest_url.type(), dest_file_info.data_path); + base::FilePath dest_local_path = + DataPathToLocalPath(dest_url, dest_file_info.data_path); error = NativeFileUtil::CopyOrMoveFile( - src_file_path, dest_local_path, true); + src_file_path, dest_local_path, + FileSystemOperation::OPTION_NONE, + fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, + true /* copy */)); } else { error = CreateFile(context, src_file_path, - dest_url.origin(), dest_url.type(), - &dest_file_info, 0, NULL); + dest_url, &dest_file_info, 0, NULL); } if (error != base::PLATFORM_FILE_OK) @@ -727,8 +722,7 @@ PlatformFileError ObfuscatedFileUtil::CopyInForeignFile( PlatformFileError ObfuscatedFileUtil::DeleteFile( FileSystemOperationContext* context, const FileSystemURL& url) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; FileId file_id; @@ -739,8 +733,7 @@ PlatformFileError ObfuscatedFileUtil::DeleteFile( base::PlatformFileInfo platform_file_info; base::FilePath local_path; base::PlatformFileError error = GetFileInfoInternal( - db, context, url.origin(), url.type(), file_id, - &file_info, &platform_file_info, &local_path); + db, context, url, file_id, &file_info, &platform_file_info, &local_path); if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && error != base::PLATFORM_FILE_OK) return error; @@ -772,8 +765,7 @@ PlatformFileError ObfuscatedFileUtil::DeleteFile( PlatformFileError ObfuscatedFileUtil::DeleteDirectory( FileSystemOperationContext* context, const FileSystemURL& url) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; @@ -818,8 +810,7 @@ scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> FileSystemOperationContext* context, const FileSystemURL& root_url, bool recursive) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - root_url.origin(), root_url.type(), false); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(root_url, false); if (!db) { return scoped_ptr<AbstractFileEnumerator>(new EmptyFileEnumerator()); } @@ -830,8 +821,7 @@ scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> bool ObfuscatedFileUtil::IsDirectoryEmpty( FileSystemOperationContext* context, const FileSystemURL& url) { - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), false); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); if (!db) return true; // Not a great answer, but it's what others do. FileId file_id; @@ -854,24 +844,18 @@ bool ObfuscatedFileUtil::IsDirectoryEmpty( base::FilePath ObfuscatedFileUtil::GetDirectoryForOriginAndType( const GURL& origin, - FileSystemType type, + const std::string& type_string, bool create, base::PlatformFileError* error_code) { base::FilePath origin_dir = GetDirectoryForOrigin(origin, create, error_code); if (origin_dir.empty()) return base::FilePath(); - base::FilePath::StringType type_string = GetDirectoryNameForType(type); - if (type_string.empty()) { - LOG(WARNING) << "Unknown filesystem type requested:" << type; - - if (error_code) - *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL; - return base::FilePath(); - } - base::FilePath path = origin_dir.Append(type_string); + if (type_string.empty()) + return origin_dir; + base::FilePath path = origin_dir.AppendASCII(type_string); base::PlatformFileError error = base::PLATFORM_FILE_OK; if (!base::DirectoryExists(path) && - (!create || !file_util::CreateDirectory(path))) { + (!create || !base::CreateDirectory(path))) { error = create ? base::PLATFORM_FILE_ERROR_FAILED : base::PLATFORM_FILE_ERROR_NOT_FOUND; @@ -883,20 +867,21 @@ base::FilePath ObfuscatedFileUtil::GetDirectoryForOriginAndType( } bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType( - const GURL& origin, FileSystemType type) { + const GURL& origin, + const std::string& type_string) { base::PlatformFileError error = base::PLATFORM_FILE_OK; base::FilePath origin_type_path = GetDirectoryForOriginAndType( - origin, type, false, &error); + origin, type_string, false, &error); if (origin_type_path.empty()) return true; - if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND) { // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase. // We ignore its error now since 1) it doesn't matter the final result, and // 2) it always returns false in Windows because of LevelDB's // implementation. // Information about failure would be useful for debugging. - DestroyDirectoryDatabase(origin, type); + if (!type_string.empty()) + DestroyDirectoryDatabase(origin, type_string); if (!base::DeleteFile(origin_type_path, true /* recursive */)) return false; } @@ -905,28 +890,24 @@ bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType( DCHECK_EQ(origin_path.value(), GetDirectoryForOrigin(origin, false, NULL).value()); - // At this point we are sure we had successfully deleted the origin/type - // directory (i.e. we're ready to just return true). - // See if we have other directories in this origin directory. - std::vector<FileSystemType> other_types; - if (type != kFileSystemTypeTemporary) - other_types.push_back(kFileSystemTypeTemporary); - if (type != kFileSystemTypePersistent) - other_types.push_back(kFileSystemTypePersistent); - if (type != kFileSystemTypeSyncable) - other_types.push_back(kFileSystemTypeSyncable); - DCHECK(type != kFileSystemTypeSyncableForInternalSync); - - for (size_t i = 0; i < other_types.size(); ++i) { - if (base::DirectoryExists( - origin_path.Append(GetDirectoryNameForType(other_types[i])))) { - // Other type's directory exists; just return true here. - return true; + if (!type_string.empty()) { + // At this point we are sure we had successfully deleted the origin/type + // directory (i.e. we're ready to just return true). + // See if we have other directories in this origin directory. + for (std::set<std::string>::iterator iter = known_type_strings_.begin(); + iter != known_type_strings_.end(); + ++iter) { + if (*iter == type_string) + continue; + if (base::DirectoryExists(origin_path.AppendASCII(*iter))) { + // Other type's directory exists; just return true here. + return true; + } } } // No other directories seem exist. Try deleting the entire origin directory. - InitOriginDatabase(false); + InitOriginDatabase(origin, false); if (origin_database_) { origin_database_->RemovePathForOrigin( webkit_database::GetIdentifierFromOrigin(origin)); @@ -937,35 +918,19 @@ bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType( return true; } -// static -base::FilePath::StringType ObfuscatedFileUtil::GetDirectoryNameForType( - FileSystemType type) { - switch (type) { - case kFileSystemTypeTemporary: - return kTemporaryDirectoryName; - case kFileSystemTypePersistent: - return kPersistentDirectoryName; - case kFileSystemTypeSyncable: - case kFileSystemTypeSyncableForInternalSync: - return kSyncableDirectoryName; - case kFileSystemTypeUnknown: - default: - return base::FilePath::StringType(); - } -} - ObfuscatedFileUtil::AbstractOriginEnumerator* ObfuscatedFileUtil::CreateOriginEnumerator() { std::vector<SandboxOriginDatabase::OriginRecord> origins; - InitOriginDatabase(false); + InitOriginDatabase(GURL(), false); return new ObfuscatedOriginEnumerator( origin_database_.get(), file_system_directory_); } bool ObfuscatedFileUtil::DestroyDirectoryDatabase( - const GURL& origin, FileSystemType type) { - std::string key = GetDirectoryDatabaseKey(origin, type); + const GURL& origin, + const std::string& type_string) { + std::string key = GetDirectoryDatabaseKey(origin, type_string); if (key.empty()) return true; DirectoryMap::iterator iter = directories_.find(key); @@ -977,7 +942,7 @@ bool ObfuscatedFileUtil::DestroyDirectoryDatabase( PlatformFileError error = base::PLATFORM_FILE_OK; base::FilePath path = GetDirectoryForOriginAndType( - origin, type, false, &error); + origin, type_string, false, &error); if (path.empty() || error == base::PLATFORM_FILE_ERROR_NOT_FOUND) return true; return SandboxDirectoryDatabase::DestroyDatabase(path); @@ -988,31 +953,29 @@ int64 ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath& path) { return UsageForPath(VirtualPath::BaseName(path).value().size()); } -void ObfuscatedFileUtil::MaybePrepopulateDatabase() { - // Always disable this for now. crbug.com/264429 - return; - - base::FilePath isolated_origin_dir = file_system_directory_.Append( - SandboxIsolatedOriginDatabase::kOriginDirectory); - if (!base::DirectoryExists(isolated_origin_dir)) +void ObfuscatedFileUtil::MaybePrepopulateDatabase( + const std::vector<std::string>& type_strings_to_prepopulate) { + SandboxPrioritizedOriginDatabase database(file_system_directory_); + std::string origin_string = database.GetPrimaryOrigin(); + if (origin_string.empty() || !database.HasOriginPath(origin_string)) return; - - const FileSystemType kPrepopulateTypes[] = { - kFileSystemTypePersistent, kFileSystemTypeTemporary - }; - - // Prepulate the directory database(s) if and only if this instance is - // initialized for isolated storage dedicated for a single origin. - for (size_t i = 0; i < arraysize(kPrepopulateTypes); ++i) { - const FileSystemType type = kPrepopulateTypes[i]; - base::FilePath::StringType type_string = GetDirectoryNameForType(type); - DCHECK(!type_string.empty()); - base::FilePath path = isolated_origin_dir.Append(type_string); - if (!base::DirectoryExists(path)) + const GURL origin = webkit_database::GetOriginFromIdentifier(origin_string); + + // Prepopulate the directory database(s) if and only if this instance + // has primary origin and the directory database is already there. + for (size_t i = 0; i < type_strings_to_prepopulate.size(); ++i) { + const std::string type_string = type_strings_to_prepopulate[i]; + // Only handles known types. + if (!ContainsKey(known_type_strings_, type_string)) + continue; + PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + base::FilePath path = GetDirectoryForOriginAndType( + origin, type_string, false, &error); + if (error != base::PLATFORM_FILE_OK) continue; scoped_ptr<SandboxDirectoryDatabase> db(new SandboxDirectoryDatabase(path)); if (db->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION)) { - directories_[GetFileSystemTypeString(type)] = db.release(); + directories_[GetDirectoryDatabaseKey(origin, type_string)] = db.release(); MarkUsed(); // Don't populate more than one database, as it may rather hurt // performance. @@ -1021,11 +984,24 @@ void ObfuscatedFileUtil::MaybePrepopulateDatabase() { } } +base::FilePath ObfuscatedFileUtil::GetDirectoryForURL( + const FileSystemURL& url, + bool create, + base::PlatformFileError* error_code) { + return GetDirectoryForOriginAndType( + url.origin(), CallGetTypeStringForURL(url), create, error_code); +} + +std::string ObfuscatedFileUtil::CallGetTypeStringForURL( + const FileSystemURL& url) { + DCHECK(!get_type_string_for_url_.is_null()); + return get_type_string_for_url_.Run(url); +} + PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal( SandboxDirectoryDatabase* db, FileSystemOperationContext* context, - const GURL& origin, - FileSystemType type, + const FileSystemURL& url, FileId file_id, FileInfo* local_info, base::PlatformFileInfo* file_info, @@ -1051,12 +1027,11 @@ PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal( } if (local_info->data_path.empty()) return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; - base::FilePath local_path = DataPathToLocalPath( - origin, type, local_info->data_path); + base::FilePath local_path = DataPathToLocalPath(url, local_info->data_path); base::PlatformFileError error = NativeFileUtil::GetFileInfo( local_path, file_info); // We should not follow symbolic links in sandboxed file system. - if (file_util::IsLink(local_path)) { + if (base::IsLink(local_path)) { LOG(WARNING) << "Found a symbolic file."; error = base::PLATFORM_FILE_ERROR_NOT_FOUND; } @@ -1064,7 +1039,7 @@ PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal( *platform_file_path = local_path; } else if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { LOG(WARNING) << "Lost a backing file."; - InvalidateUsageCache(context, origin, type); + InvalidateUsageCache(context, url.origin(), url.type()); if (!db->RemoveFileInfo(file_id)) return base::PLATFORM_FILE_ERROR_FAILED; } @@ -1074,23 +1049,19 @@ PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal( PlatformFileError ObfuscatedFileUtil::CreateFile( FileSystemOperationContext* context, const base::FilePath& src_file_path, - const GURL& dest_origin, - FileSystemType dest_type, + const FileSystemURL& dest_url, FileInfo* dest_file_info, int file_flags, PlatformFile* handle) { if (handle) *handle = base::kInvalidPlatformFileValue; - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - dest_origin, dest_type, true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true); PlatformFileError error = base::PLATFORM_FILE_OK; - base::FilePath root = GetDirectoryForOriginAndType( - dest_origin, dest_type, false, &error); + base::FilePath root = GetDirectoryForURL(dest_url, false, &error); if (error != base::PLATFORM_FILE_OK) return error; base::FilePath dest_local_path; - error = GenerateNewLocalPath(db, context, dest_origin, dest_type, - &dest_local_path); + error = GenerateNewLocalPath(db, context, dest_url, &dest_local_path); if (error != base::PLATFORM_FILE_OK) return error; @@ -1099,7 +1070,10 @@ PlatformFileError ObfuscatedFileUtil::CreateFile( DCHECK(!file_flags); DCHECK(!handle); error = NativeFileUtil::CopyOrMoveFile( - src_file_path, dest_local_path, true /* copy */); + src_file_path, dest_local_path, + FileSystemOperation::OPTION_NONE, + fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, + true /* copy */)); created = true; } else { if (base::PathExists(dest_local_path)) { @@ -1108,7 +1082,7 @@ PlatformFileError ObfuscatedFileUtil::CreateFile( return base::PLATFORM_FILE_ERROR_FAILED; } LOG(WARNING) << "A stray file detected"; - InvalidateUsageCache(context, dest_origin, dest_type); + InvalidateUsageCache(context, dest_url.origin(), dest_url.type()); } if (handle) { @@ -1156,27 +1130,21 @@ PlatformFileError ObfuscatedFileUtil::CreateFile( } base::FilePath ObfuscatedFileUtil::DataPathToLocalPath( - const GURL& origin, FileSystemType type, const base::FilePath& data_path) { + const FileSystemURL& url, const base::FilePath& data_path) { PlatformFileError error = base::PLATFORM_FILE_OK; - base::FilePath root = GetDirectoryForOriginAndType( - origin, type, false, &error); + base::FilePath root = GetDirectoryForURL(url, false, &error); if (error != base::PLATFORM_FILE_OK) return base::FilePath(); return root.Append(data_path); } std::string ObfuscatedFileUtil::GetDirectoryDatabaseKey( - const GURL& origin, FileSystemType type) { - std::string type_string = GetFileSystemTypeString(type); + const GURL& origin, const std::string& type_string) { if (type_string.empty()) { - LOG(WARNING) << "Unknown filesystem type requested:" << type; + LOG(WARNING) << "Unknown filesystem type requested:" << type_string; return std::string(); } // For isolated origin we just use a type string as a key. - if (HasIsolatedStorage(origin)) { - CHECK_EQ(isolated_origin_.spec(), origin.spec()); - return type_string; - } return webkit_database::GetIdentifierFromOrigin(origin) + type_string; } @@ -1186,8 +1154,9 @@ std::string ObfuscatedFileUtil::GetDirectoryDatabaseKey( // Ah, in that case don't even get here? // Still doesn't answer the quota issue, though. SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase( - const GURL& origin, FileSystemType type, bool create) { - std::string key = GetDirectoryDatabaseKey(origin, type); + const FileSystemURL& url, bool create) { + std::string key = GetDirectoryDatabaseKey( + url.origin(), CallGetTypeStringForURL(url)); if (key.empty()) return NULL; @@ -1198,10 +1167,10 @@ SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase( } PlatformFileError error = base::PLATFORM_FILE_OK; - base::FilePath path = GetDirectoryForOriginAndType( - origin, type, create, &error); + base::FilePath path = GetDirectoryForURL(url, create, &error); if (error != base::PLATFORM_FILE_OK) { - LOG(WARNING) << "Failed to get origin+type directory: " << path.value(); + LOG(WARNING) << "Failed to get origin+type directory: " + << url.DebugString() << " error:" << error; return NULL; } MarkUsed(); @@ -1212,11 +1181,7 @@ SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase( base::FilePath ObfuscatedFileUtil::GetDirectoryForOrigin( const GURL& origin, bool create, base::PlatformFileError* error_code) { - if (HasIsolatedStorage(origin)) { - CHECK_EQ(isolated_origin_.spec(), origin.spec()); - } - - if (!InitOriginDatabase(create)) { + if (!InitOriginDatabase(origin, create)) { if (error_code) { *error_code = create ? base::PLATFORM_FILE_ERROR_FAILED : @@ -1251,7 +1216,7 @@ base::FilePath ObfuscatedFileUtil::GetDirectoryForOrigin( } if (!exists_in_fs) { - if (!create || !file_util::CreateDirectory(path)) { + if (!create || !base::CreateDirectory(path)) { if (error_code) *error_code = create ? base::PLATFORM_FILE_ERROR_FAILED : @@ -1270,8 +1235,8 @@ void ObfuscatedFileUtil::InvalidateUsageCache( FileSystemOperationContext* context, const GURL& origin, FileSystemType type) { - context->file_system_context()->sandbox_delegate()-> - InvalidateUsageCache(origin, type); + if (sandbox_delegate_) + sandbox_delegate_->InvalidateUsageCache(origin, type); } void ObfuscatedFileUtil::MarkUsed() { @@ -1296,39 +1261,50 @@ void ObfuscatedFileUtil::DropDatabases() { timer_.reset(); } -bool ObfuscatedFileUtil::InitOriginDatabase(bool create) { +bool ObfuscatedFileUtil::InitOriginDatabase(const GURL& origin_hint, + bool create) { if (origin_database_) return true; if (!create && !base::DirectoryExists(file_system_directory_)) return false; - if (!file_util::CreateDirectory(file_system_directory_)) { + if (!base::CreateDirectory(file_system_directory_)) { LOG(WARNING) << "Failed to create FileSystem directory: " << file_system_directory_.value(); return false; } - origin_database_.reset( - new SandboxOriginDatabase(file_system_directory_)); + SandboxPrioritizedOriginDatabase* prioritized_origin_database = + new SandboxPrioritizedOriginDatabase(file_system_directory_); + origin_database_.reset(prioritized_origin_database); + + if (origin_hint.is_empty() || !HasIsolatedStorage(origin_hint)) + return true; + + const std::string isolated_origin_string = + webkit_database::GetIdentifierFromOrigin(origin_hint); + // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33. base::FilePath isolated_origin_dir = file_system_directory_.Append( - SandboxIsolatedOriginDatabase::kOriginDirectory); + SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory); if (base::DirectoryExists(isolated_origin_dir) && - !isolated_origin_.is_empty()) { - SandboxIsolatedOriginDatabase::MigrateBackDatabase( - webkit_database::GetIdentifierFromOrigin(isolated_origin_), + prioritized_origin_database->GetSandboxOriginDatabase()) { + SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase( + isolated_origin_string, file_system_directory_, - static_cast<SandboxOriginDatabase*>(origin_database_.get())); + prioritized_origin_database->GetSandboxOriginDatabase()); } + prioritized_origin_database->InitializePrimaryOrigin( + isolated_origin_string); + return true; } PlatformFileError ObfuscatedFileUtil::GenerateNewLocalPath( SandboxDirectoryDatabase* db, FileSystemOperationContext* context, - const GURL& origin, - FileSystemType type, + const FileSystemURL& url, base::FilePath* local_path) { DCHECK(local_path); int64 number; @@ -1336,8 +1312,7 @@ PlatformFileError ObfuscatedFileUtil::GenerateNewLocalPath( return base::PLATFORM_FILE_ERROR_FAILED; PlatformFileError error = base::PLATFORM_FILE_OK; - base::FilePath new_local_path = GetDirectoryForOriginAndType( - origin, type, false, &error); + base::FilePath new_local_path = GetDirectoryForURL(url, false, &error); if (error != base::PLATFORM_FILE_OK) return base::PLATFORM_FILE_ERROR_FAILED; @@ -1363,8 +1338,7 @@ PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal( DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE | base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ | base::PLATFORM_FILE_EXCLUSIVE_WRITE))); - SandboxDirectoryDatabase* db = GetDirectoryDatabase( - url.origin(), url.type(), true); + SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); if (!db) return base::PLATFORM_FILE_ERROR_FAILED; FileId file_id; @@ -1386,8 +1360,7 @@ PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal( return base::PLATFORM_FILE_ERROR_NO_SPACE; PlatformFileError error = CreateFile( context, base::FilePath(), - url.origin(), url.type(), &file_info, - file_flags, file_handle); + url, &file_info, file_flags, file_handle); if (created && base::PLATFORM_FILE_OK == error) { *created = true; UpdateUsage(context, url, growth); @@ -1404,8 +1377,7 @@ PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal( base::FilePath local_path; FileInfo file_info; base::PlatformFileError error = GetFileInfoInternal( - db, context, url.origin(), url.type(), file_id, - &file_info, &platform_file_info, &local_path); + db, context, url, file_id, &file_info, &platform_file_info, &local_path); if (error != base::PLATFORM_FILE_OK) return error; if (file_info.is_directory()) @@ -1439,24 +1411,8 @@ PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal( } bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL& origin) { - if (special_storage_policy_.get() && - special_storage_policy_->HasIsolatedStorage(origin)) { - if (isolated_origin_.is_empty()) - isolated_origin_ = origin; - // Record isolated_origin_, but always disable for now. - // crbug.com/264429 - if (isolated_origin_ != origin) { - UMA_HISTOGRAM_ENUMERATION("FileSystem.IsolatedOriginStatus", - kIsolatedOriginDontMatch, - kIsolatedOriginStatusMax); - } else { - UMA_HISTOGRAM_ENUMERATION("FileSystem.IsolatedOriginStatus", - kIsolatedOriginMatch, - kIsolatedOriginStatusMax); - } - return false; - } - return false; + return special_storage_policy_.get() && + special_storage_policy_->HasIsolatedStorage(origin); } } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/obfuscated_file_util.h b/chromium/webkit/browser/fileapi/obfuscated_file_util.h index 0d9750d1fb5..05d55308d84 100644 --- a/chromium/webkit/browser/fileapi/obfuscated_file_util.h +++ b/chromium/webkit/browser/fileapi/obfuscated_file_util.h @@ -6,8 +6,11 @@ #define WEBKIT_BROWSER_FILEAPI_OBFUSCATED_FILE_UTIL_H_ #include <map> +#include <set> #include <string> +#include <vector> +#include "base/callback_forward.h" #include "base/files/file_path.h" #include "base/files/file_util_proxy.h" #include "base/gtest_prod_util.h" @@ -16,6 +19,7 @@ #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_file_system_backend_delegate.h" #include "webkit/browser/webkit_storage_browser_export.h" #include "webkit/common/blob/shareable_file_reference.h" #include "webkit/common/fileapi/file_system_types.h" @@ -37,6 +41,21 @@ class FileSystemOperationContext; class SandboxOriginDatabaseInterface; class TimedTaskHelper; +// This file util stores directory information in LevelDB to obfuscate +// and to neutralize virtual file paths given by arbitrary apps. +// Files are stored with two-level isolation: per-origin and per-type. +// The isolation is done by storing data in separate directory partitions. +// For example, a file in Temporary file system for origin 'www.example.com' +// is stored in a different partition for a file in Persistent file system +// for the same origin, or for Temporary file system for another origin. +// +// * Per-origin directory name information is stored in a separate LevelDB, +// which is maintained by SandboxOriginDatabase. +// * Per-type directory name information is given by +// GetTypeStringForURLCallback that is given in CTOR. +// We use a small static mapping (e.g. 't' for Temporary type) for +// regular sandbox filesystems. +// // The overall implementation philosophy of this class is that partial failures // should leave us with an intact database; we'd prefer to leak the occasional // backing file than have a database entry whose backing file is missing. When @@ -58,13 +77,32 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil virtual GURL Next() = 0; // Returns the current origin's information. - virtual bool HasFileSystemType(FileSystemType type) const = 0; + // |type_string| must be ascii string. + virtual bool HasTypeDirectory(const std::string& type_string) const = 0; }; + typedef base::Callback<std::string(const FileSystemURL&)> + GetTypeStringForURLCallback; + + // |get_type_string_for_url| is user-defined callback that should return + // a type string for the given FileSystemURL. The type string is used + // to provide per-type isolation in the sandboxed filesystem directory. + // Note that this method is called on file_task_runner. + // + // |known_type_strings| are known type string names that this file system + // should care about. + // This info is used to determine whether we could delete the entire + // origin directory or not in DeleteDirectoryForOriginAndType. If no directory + // for any known type exists the origin directory may get deleted when + // one origin/type pair is deleted. + // ObfuscatedFileUtil( quota::SpecialStoragePolicy* special_storage_policy, const base::FilePath& file_system_directory, - base::SequencedTaskRunner* file_task_runner); + base::SequencedTaskRunner* file_task_runner, + const GetTypeStringForURLCallback& get_type_string_for_url, + const std::set<std::string>& known_type_strings, + SandboxFileSystemBackendDelegate* sandbox_delegate); virtual ~ObfuscatedFileUtil(); // FileSystemFileUtil overrides. @@ -110,6 +148,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil FileSystemOperationContext* context, const FileSystemURL& src_url, const FileSystemURL& dest_url, + CopyOrMoveOption option, bool copy) OVERRIDE; virtual base::PlatformFileError CopyInForeignFile( FileSystemOperationContext* context, @@ -142,26 +181,24 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil // Gets the topmost directory specific to this origin and type. This will // contain both the directory database's files and all the backing file // subdirectories. - // Returns an empty path if the directory is undefined (e.g. because |type| - // is invalid). If the directory is defined, it will be returned, even if + // Returns the topmost origin directory if |type_string| is empty. + // Returns an empty path if the directory is undefined. + // If the directory is defined, it will be returned, even if // there is a file system error (e.g. the directory doesn't exist on disk and // |create| is false). Callers should always check |error_code| to make sure // the returned path is usable. base::FilePath GetDirectoryForOriginAndType( const GURL& origin, - FileSystemType type, + const std::string& type_string, bool create, base::PlatformFileError* error_code); // Deletes the topmost directory specific to this origin and type. This will // delete its directory database. - bool DeleteDirectoryForOriginAndType(const GURL& origin, FileSystemType type); - - // TODO(ericu): This doesn't really feel like it belongs in this class. - // The previous version lives in FileSystemPathManager, but perhaps - // SandboxFileSystemBackend would be better? - static base::FilePath::StringType GetDirectoryNameForType( - FileSystemType type); + // Deletes the topmost origin directory if |type_string| is empty. + bool DeleteDirectoryForOriginAndType( + const GURL& origin, + const std::string& type_string); // This method and all methods of its returned class must be called only on // the FILE thread. The caller is responsible for deleting the returned @@ -170,7 +207,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil // Deletes a directory database from the database list in the ObfuscatedFSFU // and destroys the database on the disk. - bool DestroyDirectoryDatabase(const GURL& origin, FileSystemType type); + bool DestroyDirectoryDatabase(const GURL& origin, + const std::string& type_string); // Computes a cost for storing a given file in the obfuscated FSFU. // As the cost of a file is independent of the cost of its parent directories, @@ -179,13 +217,20 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil // on each path segment and add the results. static int64 ComputeFilePathCost(const base::FilePath& path); - void MaybePrepopulateDatabase(); + // Tries to prepopulate directory database for the given type strings. + // This tries from the first one in the given type_strings and stops + // once it succeeds to do so for one database (i.e. it prepopulates + // at most one database). + void MaybePrepopulateDatabase( + const std::vector<std::string>& type_strings_to_prepopulate); private: typedef SandboxDirectoryDatabase::FileId FileId; typedef SandboxDirectoryDatabase::FileInfo FileInfo; friend class ObfuscatedFileEnumerator; + friend class ObfuscatedFileUtilTest; + friend class QuotaBackendImplTest; FRIEND_TEST_ALL_PREFIXES(ObfuscatedFileUtilTest, MaybeDropDatabasesAliveCase); FRIEND_TEST_ALL_PREFIXES(ObfuscatedFileUtilTest, MaybeDropDatabasesAlreadyDeletedCase); @@ -196,11 +241,26 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil FRIEND_TEST_ALL_PREFIXES(ObfuscatedFileUtilTest, MigrationBackFromIsolated); + // Helper method to create an obfuscated file util for regular + // (temporary, persistent) file systems. Used only for testing. + // Note: this is implemented in sandbox_file_system_backend_delegate.cc. + static ObfuscatedFileUtil* CreateForTesting( + quota::SpecialStoragePolicy* special_storage_policy, + const base::FilePath& file_system_directory, + base::SequencedTaskRunner* file_task_runner); + + base::FilePath GetDirectoryForURL( + const FileSystemURL& url, + bool create, + base::PlatformFileError* error_code); + + // This just calls get_type_string_for_url_ callback that is given in ctor. + std::string CallGetTypeStringForURL(const FileSystemURL& url); + base::PlatformFileError GetFileInfoInternal( SandboxDirectoryDatabase* db, FileSystemOperationContext* context, - const GURL& origin, - FileSystemType type, + const FileSystemURL& url, FileId file_id, FileInfo* local_info, base::PlatformFileInfo* file_info, @@ -220,8 +280,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil base::PlatformFileError CreateFile( FileSystemOperationContext* context, const base::FilePath& source_file_path, - const GURL& dest_origin, - FileSystemType dest_type, + const FileSystemURL& dest_url, FileInfo* dest_file_info, int file_flags, base::PlatformFile* handle); @@ -230,23 +289,23 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil // field] to an absolute platform path that can be given to the native // filesystem. base::FilePath DataPathToLocalPath( - const GURL& origin, - FileSystemType type, + const FileSystemURL& url, const base::FilePath& data_file_path); - std::string GetDirectoryDatabaseKey(const GURL& origin, FileSystemType type); + std::string GetDirectoryDatabaseKey(const GURL& origin, + const std::string& type_string); // This returns NULL if |create| flag is false and a filesystem does not - // exist for the given |origin_url| and |type|. + // exist for the given |url|. // For read operations |create| should be false. - SandboxDirectoryDatabase* GetDirectoryDatabase( - const GURL& origin_url, FileSystemType type, bool create); + SandboxDirectoryDatabase* GetDirectoryDatabase(const FileSystemURL& url, + bool create); // Gets the topmost directory specific to this origin. This will // contain both the filesystem type subdirectories. base::FilePath GetDirectoryForOrigin(const GURL& origin, - bool create, - base::PlatformFileError* error_code); + bool create, + base::PlatformFileError* error_code); void InvalidateUsageCache(FileSystemOperationContext* context, const GURL& origin, @@ -254,13 +313,15 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil void MarkUsed(); void DropDatabases(); - bool InitOriginDatabase(bool create); + + // Initializes the origin database. |origin_hint| may be used as a hint + // for initializing database if it's not empty. + bool InitOriginDatabase(const GURL& origin_hint, bool create); base::PlatformFileError GenerateNewLocalPath( SandboxDirectoryDatabase* db, FileSystemOperationContext* context, - const GURL& origin, - FileSystemType type, + const FileSystemURL& url, base::FilePath* local_path); base::PlatformFileError CreateOrOpenInternal( @@ -284,9 +345,11 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE ObfuscatedFileUtil scoped_refptr<base::SequencedTaskRunner> file_task_runner_; scoped_ptr<TimedTaskHelper> timer_; - // If this instance is initialized for an isolated partition, this should - // only see a single origin. - GURL isolated_origin_; + GetTypeStringForURLCallback get_type_string_for_url_; + std::set<std::string> known_type_strings_; + + // Not owned. + SandboxFileSystemBackendDelegate* sandbox_delegate_; DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtil); }; diff --git a/chromium/webkit/browser/fileapi/obfuscated_file_util_unittest.cc b/chromium/webkit/browser/fileapi/obfuscated_file_util_unittest.cc deleted file mode 100644 index c37b17caa23..00000000000 --- a/chromium/webkit/browser/fileapi/obfuscated_file_util_unittest.cc +++ /dev/null @@ -1,2403 +0,0 @@ -// 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 <set> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/platform_file.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/external_mount_points.h" -#include "webkit/browser/fileapi/file_system_backend.h" -#include "webkit/browser/fileapi/file_system_context.h" -#include "webkit/browser/fileapi/file_system_operation_context.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/browser/fileapi/sandbox_directory_database.h" -#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" -#include "webkit/browser/fileapi/sandbox_isolated_origin_database.h" -#include "webkit/browser/fileapi/sandbox_origin_database.h" -#include "webkit/browser/fileapi/test_file_set.h" -#include "webkit/browser/quota/mock_special_storage_policy.h" -#include "webkit/browser/quota/quota_manager.h" -#include "webkit/common/database/database_identifier.h" -#include "webkit/common/quota/quota_types.h" - -namespace fileapi { - -namespace { - -bool FileExists(const base::FilePath& path) { - return base::PathExists(path) && !base::DirectoryExists(path); -} - -int64 GetSize(const base::FilePath& path) { - int64 size; - EXPECT_TRUE(file_util::GetFileSize(path, &size)); - return size; -} - -// After a move, the dest exists and the source doesn't. -// After a copy, both source and dest exist. -struct CopyMoveTestCaseRecord { - bool is_copy_not_move; - const char source_path[64]; - const char dest_path[64]; - bool cause_overwrite; -}; - -const CopyMoveTestCaseRecord kCopyMoveTestCases[] = { - // This is the combinatoric set of: - // rename vs. same-name - // different directory vs. same directory - // overwrite vs. no-overwrite - // copy vs. move - // We can never be called with source and destination paths identical, so - // those cases are omitted. - {true, "dir0/file0", "dir0/file1", false}, - {false, "dir0/file0", "dir0/file1", false}, - {true, "dir0/file0", "dir0/file1", true}, - {false, "dir0/file0", "dir0/file1", true}, - - {true, "dir0/file0", "dir1/file0", false}, - {false, "dir0/file0", "dir1/file0", false}, - {true, "dir0/file0", "dir1/file0", true}, - {false, "dir0/file0", "dir1/file0", true}, - {true, "dir0/file0", "dir1/file1", false}, - {false, "dir0/file0", "dir1/file1", false}, - {true, "dir0/file0", "dir1/file1", true}, - {false, "dir0/file0", "dir1/file1", true}, -}; - -struct OriginEnumerationTestRecord { - std::string origin_url; - bool has_temporary; - bool has_persistent; -}; - -const OriginEnumerationTestRecord kOriginEnumerationTestRecords[] = { - {"http://example.com", false, true}, - {"http://example1.com", true, false}, - {"https://example1.com", true, true}, - {"file://", false, true}, - {"http://example.com:8000", false, true}, -}; - -FileSystemURL FileSystemURLAppend( - const FileSystemURL& url, const base::FilePath::StringType& child) { - return FileSystemURL::CreateForTest( - url.origin(), url.mount_type(), url.virtual_path().Append(child)); -} - -FileSystemURL FileSystemURLAppendUTF8( - const FileSystemURL& url, const std::string& child) { - return FileSystemURL::CreateForTest( - url.origin(), - url.mount_type(), - url.virtual_path().Append(base::FilePath::FromUTF8Unsafe(child))); -} - -FileSystemURL FileSystemURLDirName(const FileSystemURL& url) { - return FileSystemURL::CreateForTest( - url.origin(), url.mount_type(), VirtualPath::DirName(url.virtual_path())); -} - -} // namespace (anonymous) - -// TODO(ericu): The vast majority of this and the other FSFU subclass tests -// could theoretically be shared. It would basically be a FSFU interface -// compliance test, and only the subclass-specific bits that look into the -// implementation would need to be written per-subclass. -class ObfuscatedFileUtilTest : public testing::Test { - public: - ObfuscatedFileUtilTest() - : origin_(GURL("http://www.example.com")), - type_(kFileSystemTypeTemporary), - weak_factory_(this), - sandbox_file_system_(origin_, type_), - quota_status_(quota::kQuotaStatusUnknown), - usage_(-1) { - } - - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - - storage_policy_ = new quota::MockSpecialStoragePolicy(); - - quota_manager_ = - new quota::QuotaManager(false /* is_incognito */, - data_dir_.path(), - base::MessageLoopProxy::current().get(), - base::MessageLoopProxy::current().get(), - storage_policy_.get()); - - // Every time we create a new sandbox_file_system helper, - // it creates another context, which creates another path manager, - // another sandbox_backend, and another OFU. - // We need to pass in the context to skip all that. - file_system_context_ = CreateFileSystemContextForTesting( - quota_manager_->proxy(), - data_dir_.path()); - - sandbox_file_system_.SetUp(file_system_context_.get()); - - change_observers_ = MockFileChangeObserver::CreateList(&change_observer_); - } - - virtual void TearDown() { - quota_manager_ = NULL; - sandbox_file_system_.TearDown(); - } - - scoped_ptr<FileSystemOperationContext> LimitedContext( - int64 allowed_bytes_growth) { - scoped_ptr<FileSystemOperationContext> context( - sandbox_file_system_.NewOperationContext()); - context->set_allowed_bytes_growth(allowed_bytes_growth); - return context.Pass(); - } - - scoped_ptr<FileSystemOperationContext> UnlimitedContext() { - return LimitedContext(kint64max); - } - - FileSystemOperationContext* NewContext( - SandboxFileSystemTestHelper* file_system) { - change_observer()->ResetCount(); - FileSystemOperationContext* context; - if (file_system) - context = file_system->NewOperationContext(); - else - context = sandbox_file_system_.NewOperationContext(); - // Setting allowed_bytes_growth big enough for all tests. - context->set_allowed_bytes_growth(1024 * 1024); - context->set_change_observers(change_observers()); - return context; - } - - const ChangeObserverList& change_observers() const { - return change_observers_; - } - - MockFileChangeObserver* change_observer() { - return &change_observer_; - } - - // This can only be used after SetUp has run and created file_system_context_ - // and obfuscated_file_util_. - // Use this for tests which need to run in multiple origins; we need a test - // helper per origin. - SandboxFileSystemTestHelper* NewFileSystem( - const GURL& origin, fileapi::FileSystemType type) { - SandboxFileSystemTestHelper* file_system = - new SandboxFileSystemTestHelper(origin, type); - - file_system->SetUp(file_system_context_.get()); - return file_system; - } - - ObfuscatedFileUtil* ofu() { - return static_cast<ObfuscatedFileUtil*>(sandbox_file_system_.file_util()); - } - - const base::FilePath& test_directory() const { - return data_dir_.path(); - } - - const GURL& origin() const { - return origin_; - } - - fileapi::FileSystemType type() const { - return type_; - } - - int64 ComputeTotalFileSize() { - return sandbox_file_system_.ComputeCurrentOriginUsage() - - sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage(); - } - - void GetUsageFromQuotaManager() { - int64 quota = -1; - quota_status_ = - AsyncFileTestHelper::GetUsageAndQuota(quota_manager_.get(), - origin(), - sandbox_file_system_.type(), - &usage_, - "a); - EXPECT_EQ(quota::kQuotaStatusOk, quota_status_); - } - - void RevokeUsageCache() { - quota_manager_->ResetUsageTracker(sandbox_file_system_.storage_type()); - usage_cache()->Delete(sandbox_file_system_.GetUsageCachePath()); - } - - int64 SizeByQuotaUtil() { - return sandbox_file_system_.GetCachedOriginUsage(); - } - - int64 SizeInUsageFile() { - base::RunLoop().RunUntilIdle(); - int64 usage = 0; - return usage_cache()->GetUsage( - sandbox_file_system_.GetUsageCachePath(), &usage) ? usage : -1; - } - - bool PathExists(const FileSystemURL& url) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - base::PlatformFileInfo file_info; - base::FilePath platform_path; - base::PlatformFileError error = ofu()->GetFileInfo( - context.get(), url, &file_info, &platform_path); - return error == base::PLATFORM_FILE_OK; - } - - bool DirectoryExists(const FileSystemURL& url) { - return AsyncFileTestHelper::DirectoryExists(file_system_context(), url); - } - - int64 usage() const { return usage_; } - FileSystemUsageCache* usage_cache() { - return sandbox_file_system_.usage_cache(); - } - - FileSystemURL CreateURLFromUTF8(const std::string& path) { - return sandbox_file_system_.CreateURLFromUTF8(path); - } - - int64 PathCost(const FileSystemURL& url) { - return ObfuscatedFileUtil::ComputeFilePathCost(url.path()); - } - - FileSystemURL CreateURL(const base::FilePath& path) { - return sandbox_file_system_.CreateURL(path); - } - - void CheckFileAndCloseHandle( - const FileSystemURL& url, base::PlatformFile file_handle) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath( - context.get(), url, &local_path)); - - base::PlatformFileInfo file_info0; - base::FilePath data_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), url, &file_info0, &data_path)); - EXPECT_EQ(data_path, local_path); - EXPECT_TRUE(FileExists(data_path)); - EXPECT_EQ(0, GetSize(data_path)); - - const char data[] = "test data"; - const int length = arraysize(data) - 1; - - if (base::kInvalidPlatformFileValue == file_handle) { - bool created = true; - base::PlatformFileError error; - file_handle = base::CreatePlatformFile( - data_path, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, - &created, - &error); - ASSERT_NE(base::kInvalidPlatformFileValue, file_handle); - ASSERT_EQ(base::PLATFORM_FILE_OK, error); - EXPECT_FALSE(created); - } - ASSERT_EQ(length, base::WritePlatformFile(file_handle, 0, data, length)); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - - base::PlatformFileInfo file_info1; - EXPECT_EQ(length, GetSize(data_path)); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), url, &file_info1, &data_path)); - EXPECT_EQ(data_path, local_path); - - EXPECT_FALSE(file_info0.is_directory); - EXPECT_FALSE(file_info1.is_directory); - EXPECT_FALSE(file_info0.is_symbolic_link); - EXPECT_FALSE(file_info1.is_symbolic_link); - EXPECT_EQ(0, file_info0.size); - EXPECT_EQ(length, file_info1.size); - EXPECT_LE(file_info0.last_modified, file_info1.last_modified); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate( - context.get(), url, length * 2)); - EXPECT_EQ(length * 2, GetSize(data_path)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate( - context.get(), url, 0)); - EXPECT_EQ(0, GetSize(data_path)); - } - - void ValidateTestDirectory( - const FileSystemURL& root_url, - const std::set<base::FilePath::StringType>& files, - const std::set<base::FilePath::StringType>& directories) { - scoped_ptr<FileSystemOperationContext> context; - std::set<base::FilePath::StringType>::const_iterator iter; - for (iter = files.begin(); iter != files.end(); ++iter) { - bool created = true; - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - context.get(), FileSystemURLAppend(root_url, *iter), - &created)); - ASSERT_FALSE(created); - } - for (iter = directories.begin(); iter != directories.end(); ++iter) { - context.reset(NewContext(NULL)); - EXPECT_TRUE(DirectoryExists( - FileSystemURLAppend(root_url, *iter))); - } - } - - class UsageVerifyHelper { - public: - UsageVerifyHelper(scoped_ptr<FileSystemOperationContext> context, - SandboxFileSystemTestHelper* file_system, - int64 expected_usage) - : context_(context.Pass()), - sandbox_file_system_(file_system), - expected_usage_(expected_usage) {} - - ~UsageVerifyHelper() { - base::RunLoop().RunUntilIdle(); - Check(); - } - - FileSystemOperationContext* context() { - return context_.get(); - } - - private: - void Check() { - ASSERT_EQ(expected_usage_, - sandbox_file_system_->GetCachedOriginUsage()); - } - - scoped_ptr<FileSystemOperationContext> context_; - SandboxFileSystemTestHelper* sandbox_file_system_; - int64 expected_usage_; - }; - - scoped_ptr<UsageVerifyHelper> AllowUsageIncrease(int64 requested_growth) { - int64 usage = sandbox_file_system_.GetCachedOriginUsage(); - return scoped_ptr<UsageVerifyHelper>(new UsageVerifyHelper( - LimitedContext(requested_growth), - &sandbox_file_system_, usage + requested_growth)); - } - - scoped_ptr<UsageVerifyHelper> DisallowUsageIncrease(int64 requested_growth) { - int64 usage = sandbox_file_system_.GetCachedOriginUsage(); - return scoped_ptr<UsageVerifyHelper>(new UsageVerifyHelper( - LimitedContext(requested_growth - 1), &sandbox_file_system_, usage)); - } - - void FillTestDirectory( - const FileSystemURL& root_url, - std::set<base::FilePath::StringType>* files, - std::set<base::FilePath::StringType>* directories) { - scoped_ptr<FileSystemOperationContext> context; - std::vector<DirectoryEntry> entries; - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), root_url, &entries)); - EXPECT_EQ(0UL, entries.size()); - - files->clear(); - files->insert(FILE_PATH_LITERAL("first")); - files->insert(FILE_PATH_LITERAL("second")); - files->insert(FILE_PATH_LITERAL("third")); - directories->clear(); - directories->insert(FILE_PATH_LITERAL("fourth")); - directories->insert(FILE_PATH_LITERAL("fifth")); - directories->insert(FILE_PATH_LITERAL("sixth")); - std::set<base::FilePath::StringType>::iterator iter; - for (iter = files->begin(); iter != files->end(); ++iter) { - bool created = false; - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - context.get(), - FileSystemURLAppend(root_url, *iter), - &created)); - ASSERT_TRUE(created); - } - for (iter = directories->begin(); iter != directories->end(); ++iter) { - bool exclusive = true; - bool recursive = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory( - context.get(), - FileSystemURLAppend(root_url, *iter), - exclusive, recursive)); - } - ValidateTestDirectory(root_url, *files, *directories); - } - - void TestReadDirectoryHelper(const FileSystemURL& root_url) { - std::set<base::FilePath::StringType> files; - std::set<base::FilePath::StringType> directories; - FillTestDirectory(root_url, &files, &directories); - - scoped_ptr<FileSystemOperationContext> context; - std::vector<DirectoryEntry> entries; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), root_url, &entries)); - std::vector<DirectoryEntry>::iterator entry_iter; - EXPECT_EQ(files.size() + directories.size(), entries.size()); - EXPECT_TRUE(change_observer()->HasNoChange()); - for (entry_iter = entries.begin(); entry_iter != entries.end(); - ++entry_iter) { - const DirectoryEntry& entry = *entry_iter; - std::set<base::FilePath::StringType>::iterator iter = - files.find(entry.name); - if (iter != files.end()) { - EXPECT_FALSE(entry.is_directory); - files.erase(iter); - continue; - } - iter = directories.find(entry.name); - EXPECT_FALSE(directories.end() == iter); - EXPECT_TRUE(entry.is_directory); - directories.erase(iter); - } - } - - void TestTouchHelper(const FileSystemURL& url, bool is_file) { - base::Time last_access_time = base::Time::Now(); - base::Time last_modified_time = base::Time::Now(); - - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Touch( - context.get(), url, last_access_time, last_modified_time)); - // Currently we fire no change notifications for Touch. - EXPECT_TRUE(change_observer()->HasNoChange()); - base::FilePath local_path; - base::PlatformFileInfo file_info; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), url, &file_info, &local_path)); - // We compare as time_t here to lower our resolution, to avoid false - // negatives caused by conversion to the local filesystem's native - // representation and back. - EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); - - context.reset(NewContext(NULL)); - last_modified_time += base::TimeDelta::FromHours(1); - last_access_time += base::TimeDelta::FromHours(14); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Touch( - context.get(), url, last_access_time, last_modified_time)); - EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), url, &file_info, &local_path)); - EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT()); - if (is_file) // Directories in OFU don't support atime. - EXPECT_EQ(file_info.last_accessed.ToTimeT(), last_access_time.ToTimeT()); - } - - void TestCopyInForeignFileHelper(bool overwrite) { - base::ScopedTempDir source_dir; - ASSERT_TRUE(source_dir.CreateUniqueTempDir()); - base::FilePath root_file_path = source_dir.path(); - base::FilePath src_file_path = root_file_path.AppendASCII("file_name"); - FileSystemURL dest_url = CreateURLFromUTF8("new file"); - int64 src_file_length = 87; - - base::PlatformFileError error_code; - bool created = false; - int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; - base::PlatformFile file_handle = - base::CreatePlatformFile( - src_file_path, file_flags, &created, &error_code); - EXPECT_TRUE(created); - ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); - ASSERT_NE(base::kInvalidPlatformFileValue, file_handle); - ASSERT_TRUE(base::TruncatePlatformFile(file_handle, src_file_length)); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - - scoped_ptr<FileSystemOperationContext> context; - - if (overwrite) { - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), dest_url, &created)); - EXPECT_TRUE(created); - - // We must have observed one (and only one) create_file_count. - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - EXPECT_TRUE(change_observer()->HasNoChange()); - } - - const int64 path_cost = - ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()); - if (!overwrite) { - // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(path_cost + src_file_length - 1); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->CopyInForeignFile(context.get(), - src_file_path, dest_url)); - } - - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(path_cost + src_file_length); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyInForeignFile(context.get(), - src_file_path, dest_url)); - - EXPECT_TRUE(PathExists(dest_url)); - EXPECT_FALSE(DirectoryExists(dest_url)); - - context.reset(NewContext(NULL)); - base::PlatformFileInfo file_info; - base::FilePath data_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), dest_url, &file_info, &data_path)); - EXPECT_NE(data_path, src_file_path); - EXPECT_TRUE(FileExists(data_path)); - EXPECT_EQ(src_file_length, GetSize(data_path)); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), dest_url)); - } - - void ClearTimestamp(const FileSystemURL& url) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Touch(context.get(), url, base::Time(), base::Time())); - EXPECT_EQ(base::Time(), GetModifiedTime(url)); - } - - base::Time GetModifiedTime(const FileSystemURL& url) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - base::FilePath data_path; - base::PlatformFileInfo file_info; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetFileInfo(context.get(), url, &file_info, &data_path)); - EXPECT_TRUE(change_observer()->HasNoChange()); - return file_info.last_modified; - } - - void TestDirectoryTimestampHelper(const FileSystemURL& base_dir, - bool copy, - bool overwrite) { - scoped_ptr<FileSystemOperationContext> context; - const FileSystemURL src_dir_url( - FileSystemURLAppendUTF8(base_dir, "foo_dir")); - const FileSystemURL dest_dir_url( - FileSystemURLAppendUTF8(base_dir, "bar_dir")); - - const FileSystemURL src_file_url( - FileSystemURLAppendUTF8(src_dir_url, "hoge")); - const FileSystemURL dest_file_url( - FileSystemURLAppendUTF8(dest_dir_url, "fuga")); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), src_dir_url, true, true)); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), dest_dir_url, true, true)); - - bool created = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), src_file_url, &created)); - if (overwrite) { - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), - dest_file_url, &created)); - } - - ClearTimestamp(src_dir_url); - ClearTimestamp(dest_dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), - src_file_url, dest_file_url, - copy)); - if (copy) - EXPECT_EQ(base::Time(), GetModifiedTime(src_dir_url)); - else - EXPECT_NE(base::Time(), GetModifiedTime(src_dir_url)); - EXPECT_NE(base::Time(), GetModifiedTime(dest_dir_url)); - } - - int64 ComputeCurrentUsage() { - return sandbox_file_system_.ComputeCurrentOriginUsage() - - sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage(); - } - - FileSystemContext* file_system_context() { - return sandbox_file_system_.file_system_context(); - } - - const base::FilePath& data_dir_path() const { - return data_dir_.path(); - } - - protected: - base::ScopedTempDir data_dir_; - base::MessageLoop message_loop_; - scoped_refptr<quota::MockSpecialStoragePolicy> storage_policy_; - scoped_refptr<quota::QuotaManager> quota_manager_; - scoped_refptr<FileSystemContext> file_system_context_; - GURL origin_; - fileapi::FileSystemType type_; - base::WeakPtrFactory<ObfuscatedFileUtilTest> weak_factory_; - SandboxFileSystemTestHelper sandbox_file_system_; - quota::QuotaStatusCode quota_status_; - int64 usage_; - MockFileChangeObserver change_observer_; - ChangeObserverList change_observers_; - - DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtilTest); -}; - -TEST_F(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) { - base::PlatformFile file_handle = base::kInvalidPlatformFileValue; - bool created; - FileSystemURL url = CreateURLFromUTF8("fake/file"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; - - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->CreateOrOpen( - context.get(), url, file_flags, &file_handle, - &created)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->DeleteFile(context.get(), url)); - - url = CreateURLFromUTF8("test file"); - - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->CreateOrOpen( - context.get(), url, file_flags, &file_handle, &created)); - - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path())); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - context.get(), url, file_flags, &file_handle, &created)); - ASSERT_TRUE(created); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); - - CheckFileAndCloseHandle(url, file_handle); - - context.reset(NewContext(NULL)); - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath( - context.get(), url, &local_path)); - EXPECT_TRUE(base::PathExists(local_path)); - - // Verify that deleting a file isn't stopped by zero quota, and that it frees - // up quote from its path. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(0); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), url)); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); - EXPECT_FALSE(base::PathExists(local_path)); - EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()), - context->allowed_bytes_growth()); - - context.reset(NewContext(NULL)); - bool exclusive = true; - bool recursive = true; - FileSystemURL directory_url = CreateURLFromUTF8( - "series/of/directories"); - url = FileSystemURLAppendUTF8(directory_url, "file name"); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), directory_url, exclusive, recursive)); - // The oepration created 3 directories recursively. - EXPECT_EQ(3, change_observer()->get_and_reset_create_directory_count()); - - context.reset(NewContext(NULL)); - file_handle = base::kInvalidPlatformFileValue; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - context.get(), url, file_flags, &file_handle, &created)); - ASSERT_TRUE(created); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); - - CheckFileAndCloseHandle(url, file_handle); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath( - context.get(), url, &local_path)); - EXPECT_TRUE(base::PathExists(local_path)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), url)); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count()); - EXPECT_FALSE(base::PathExists(local_path)); - - // Make sure we have no unexpected changes. - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(ObfuscatedFileUtilTest, TestTruncate) { - bool created = false; - FileSystemURL url = CreateURLFromUTF8("file"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->Truncate(context.get(), url, 4)); - - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_TRUE(created); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - - context.reset(NewContext(NULL)); - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath( - context.get(), url, &local_path)); - EXPECT_EQ(0, GetSize(local_path)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate( - context.get(), url, 10)); - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - EXPECT_EQ(10, GetSize(local_path)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate( - context.get(), url, 1)); - EXPECT_EQ(1, GetSize(local_path)); - EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); - - EXPECT_FALSE(DirectoryExists(url)); - EXPECT_TRUE(PathExists(url)); - - // Make sure we have no unexpected changes. - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(ObfuscatedFileUtilTest, TestQuotaOnTruncation) { - bool created = false; - FileSystemURL url = CreateURLFromUTF8("file"); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(url))->context(), - url, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(0, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(1020)->context(), - url, 1020)); - ASSERT_EQ(1020, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(-1020)->context(), - url, 0)); - ASSERT_EQ(0, ComputeTotalFileSize()); - - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->Truncate( - DisallowUsageIncrease(1021)->context(), - url, 1021)); - ASSERT_EQ(0, ComputeTotalFileSize()); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(1020)->context(), - url, 1020)); - ASSERT_EQ(1020, ComputeTotalFileSize()); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(0)->context(), - url, 1020)); - ASSERT_EQ(1020, ComputeTotalFileSize()); - - // quota exceeded - { - scoped_ptr<UsageVerifyHelper> helper = AllowUsageIncrease(-1); - helper->context()->set_allowed_bytes_growth( - helper->context()->allowed_bytes_growth() - 1); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate(helper->context(), url, 1019)); - ASSERT_EQ(1019, ComputeTotalFileSize()); - } - - // Delete backing file to make following truncation fail. - base::FilePath local_path; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetLocalFilePath( - UnlimitedContext().get(), - url, &local_path)); - ASSERT_FALSE(local_path.empty()); - ASSERT_TRUE(base::DeleteFile(local_path, false)); - - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->Truncate( - LimitedContext(1234).get(), - url, 1234)); - ASSERT_EQ(0, ComputeTotalFileSize()); -} - -TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) { - FileSystemURL url = CreateURLFromUTF8("fake/file"); - bool created = false; - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->EnsureFileExists( - context.get(), url, &created)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Verify that file creation requires sufficient quota for the path. - context.reset(NewContext(NULL)); - url = CreateURLFromUTF8("test file"); - created = false; - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_FALSE(created); - EXPECT_TRUE(change_observer()->HasNoChange()); - - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path())); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_TRUE(created); - EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count()); - - CheckFileAndCloseHandle(url, base::kInvalidPlatformFileValue); - - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_FALSE(created); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Also test in a subdirectory. - url = CreateURLFromUTF8("path/to/file.txt"); - context.reset(NewContext(NULL)); - bool exclusive = true; - bool recursive = true; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), - FileSystemURLDirName(url), - exclusive, recursive)); - // 2 directories: path/ and path/to. - EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_TRUE(created); - EXPECT_FALSE(DirectoryExists(url)); - EXPECT_TRUE(PathExists(url)); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - bool exclusive = false; - bool recursive = false; - FileSystemURL url = CreateURLFromUTF8("foo/bar"); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->DeleteDirectory(context.get(), url)); - - FileSystemURL root = CreateURLFromUTF8(std::string()); - EXPECT_FALSE(DirectoryExists(url)); - EXPECT_FALSE(PathExists(url)); - context.reset(NewContext(NULL)); - EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), root)); - - context.reset(NewContext(NULL)); - exclusive = false; - recursive = true; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - - EXPECT_TRUE(DirectoryExists(url)); - EXPECT_TRUE(PathExists(url)); - - context.reset(NewContext(NULL)); - EXPECT_FALSE(ofu()->IsDirectoryEmpty(context.get(), root)); - EXPECT_TRUE(DirectoryExists(FileSystemURLDirName(url))); - - context.reset(NewContext(NULL)); - EXPECT_FALSE(ofu()->IsDirectoryEmpty(context.get(), - FileSystemURLDirName(url))); - - // Can't remove a non-empty directory. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, - ofu()->DeleteDirectory(context.get(), - FileSystemURLDirName(url))); - EXPECT_TRUE(change_observer()->HasNoChange()); - - base::PlatformFileInfo file_info; - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), url, &file_info, &local_path)); - EXPECT_TRUE(local_path.empty()); - EXPECT_TRUE(file_info.is_directory); - EXPECT_FALSE(file_info.is_symbolic_link); - - // Same create again should succeed, since exclusive is false. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - exclusive = true; - recursive = true; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - // Verify that deleting a directory isn't stopped by zero quota, and that it - // frees up quota from its path. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(0); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url)); - EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count()); - EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()), - context->allowed_bytes_growth()); - - url = CreateURLFromUTF8("foo/bop"); - - EXPECT_FALSE(DirectoryExists(url)); - EXPECT_FALSE(PathExists(url)); - - context.reset(NewContext(NULL)); - EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo( - context.get(), url, &file_info, &local_path)); - - // Verify that file creation requires sufficient quota for the path. - exclusive = true; - recursive = false; - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(url.path())); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - - EXPECT_TRUE(DirectoryExists(url)); - EXPECT_TRUE(PathExists(url)); - - exclusive = true; - recursive = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - exclusive = true; - recursive = false; - url = CreateURLFromUTF8("foo"); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); - - url = CreateURLFromUTF8("blah"); - - EXPECT_FALSE(DirectoryExists(url)); - EXPECT_FALSE(PathExists(url)); - - exclusive = true; - recursive = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count()); - - EXPECT_TRUE(DirectoryExists(url)); - EXPECT_TRUE(PathExists(url)); - - exclusive = true; - recursive = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(ObfuscatedFileUtilTest, TestReadDirectory) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - bool exclusive = true; - bool recursive = true; - FileSystemURL url = CreateURLFromUTF8("directory/to/use"); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - TestReadDirectoryHelper(url); -} - -TEST_F(ObfuscatedFileUtilTest, TestReadRootWithSlash) { - TestReadDirectoryHelper(CreateURLFromUTF8(std::string())); -} - -TEST_F(ObfuscatedFileUtilTest, TestReadRootWithEmptyString) { - TestReadDirectoryHelper(CreateURLFromUTF8("/")); -} - -TEST_F(ObfuscatedFileUtilTest, TestReadDirectoryOnFile) { - FileSystemURL url = CreateURLFromUTF8("file"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_TRUE(created); - - std::vector<DirectoryEntry> entries; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), url, &entries)); - - EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url)); -} - -TEST_F(ObfuscatedFileUtilTest, TestTouch) { - FileSystemURL url = CreateURLFromUTF8("file"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - base::Time last_access_time = base::Time::Now(); - base::Time last_modified_time = base::Time::Now(); - - // It's not there yet. - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->Touch( - context.get(), url, last_access_time, last_modified_time)); - - // OK, now create it. - context.reset(NewContext(NULL)); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - ASSERT_TRUE(created); - TestTouchHelper(url, true); - - // Now test a directory: - context.reset(NewContext(NULL)); - bool exclusive = true; - bool recursive = false; - url = CreateURLFromUTF8("dir"); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(context.get(), - url, exclusive, recursive)); - TestTouchHelper(url, false); -} - -TEST_F(ObfuscatedFileUtilTest, TestPathQuotas) { - FileSystemURL url = CreateURLFromUTF8("fake/file"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - url = CreateURLFromUTF8("file name"); - context->set_allowed_bytes_growth(5); - bool created = false; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_FALSE(created); - context->set_allowed_bytes_growth(1024); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_TRUE(created); - int64 path_cost = ObfuscatedFileUtil::ComputeFilePathCost(url.path()); - EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth()); - - context->set_allowed_bytes_growth(1024); - bool exclusive = true; - bool recursive = true; - url = CreateURLFromUTF8("directory/to/use"); - std::vector<base::FilePath::StringType> components; - url.path().GetComponents(&components); - path_cost = 0; - typedef std::vector<base::FilePath::StringType>::iterator iterator; - for (iterator iter = components.begin(); - iter != components.end(); ++iter) { - path_cost += ObfuscatedFileUtil::ComputeFilePathCost( - base::FilePath(*iter)); - } - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(1024); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), url, exclusive, recursive)); - EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth()); -} - -TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) { - FileSystemURL source_url = CreateURLFromUTF8("path0.txt"); - FileSystemURL dest_url = CreateURLFromUTF8("path1.txt"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - bool is_copy_not_move = false; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, - is_copy_not_move)); - EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(NULL)); - is_copy_not_move = true; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, - is_copy_not_move)); - EXPECT_TRUE(change_observer()->HasNoChange()); - source_url = CreateURLFromUTF8("dir/dir/file"); - bool exclusive = true; - bool recursive = true; - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), - FileSystemURLDirName(source_url), - exclusive, recursive)); - EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count()); - is_copy_not_move = false; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, - is_copy_not_move)); - EXPECT_TRUE(change_observer()->HasNoChange()); - context.reset(NewContext(NULL)); - is_copy_not_move = true; - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->CopyOrMoveFile(context.get(), source_url, dest_url, - is_copy_not_move)); - EXPECT_TRUE(change_observer()->HasNoChange()); -} - -TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) { - const int64 kSourceLength = 5; - const int64 kDestLength = 50; - - for (size_t i = 0; i < arraysize(kCopyMoveTestCases); ++i) { - SCOPED_TRACE(testing::Message() << "kCopyMoveTestCase " << i); - const CopyMoveTestCaseRecord& test_case = kCopyMoveTestCases[i]; - SCOPED_TRACE(testing::Message() << "\t is_copy_not_move " << - test_case.is_copy_not_move); - SCOPED_TRACE(testing::Message() << "\t source_path " << - test_case.source_path); - SCOPED_TRACE(testing::Message() << "\t dest_path " << - test_case.dest_path); - SCOPED_TRACE(testing::Message() << "\t cause_overwrite " << - test_case.cause_overwrite); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - bool exclusive = false; - bool recursive = true; - FileSystemURL source_url = CreateURLFromUTF8(test_case.source_path); - FileSystemURL dest_url = CreateURLFromUTF8(test_case.dest_path); - - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), - FileSystemURLDirName(source_url), - exclusive, recursive)); - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), - FileSystemURLDirName(dest_url), - exclusive, recursive)); - - bool created = false; - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), source_url, &created)); - ASSERT_TRUE(created); - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate(context.get(), source_url, kSourceLength)); - - if (test_case.cause_overwrite) { - context.reset(NewContext(NULL)); - created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), dest_url, &created)); - ASSERT_TRUE(created); - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate(context.get(), dest_url, kDestLength)); - } - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CopyOrMoveFile(context.get(), - source_url, dest_url, test_case.is_copy_not_move)); - - if (test_case.is_copy_not_move) { - base::PlatformFileInfo file_info; - base::FilePath local_path; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), source_url, &file_info, &local_path)); - EXPECT_EQ(kSourceLength, file_info.size); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), source_url)); - } else { - base::PlatformFileInfo file_info; - base::FilePath local_path; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo( - context.get(), source_url, &file_info, &local_path)); - } - base::PlatformFileInfo file_info; - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo( - context.get(), dest_url, &file_info, &local_path)); - EXPECT_EQ(kSourceLength, file_info.size); - - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), dest_url)); - } -} - -TEST_F(ObfuscatedFileUtilTest, TestCopyPathQuotas) { - FileSystemURL src_url = CreateURLFromUTF8("src path"); - FileSystemURL dest_url = CreateURLFromUTF8("destination path"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists( - context.get(), src_url, &created)); - - bool is_copy = true; - // Copy, no overwrite. - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - 1); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path())); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - - // Copy, with overwrite. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(0); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); -} - -TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) { - FileSystemURL src_url = CreateURLFromUTF8("src path"); - FileSystemURL dest_url = CreateURLFromUTF8("destination path"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists( - context.get(), src_url, &created)); - - bool is_copy = false; - // Move, rename, no overwrite. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - - ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()) - 1); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth( - ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - - ObfuscatedFileUtil::ComputeFilePathCost(src_url.path())); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists( - context.get(), src_url, &created)); - - // Move, rename, with overwrite. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(0); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); -} - -TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) { - FileSystemURL src_url = CreateURLFromUTF8("src path"); - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists( - context.get(), src_url, &created)); - - bool exclusive = true; - bool recursive = false; - FileSystemURL dir_url = CreateURLFromUTF8("directory path"); - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), dir_url, exclusive, recursive)); - - FileSystemURL dest_url = FileSystemURLAppend( - dir_url, src_url.path().value()); - - bool is_copy = false; - int64 allowed_bytes_growth = -1000; // Over quota, this should still work. - // Move, no rename, no overwrite. - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(allowed_bytes_growth); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - EXPECT_EQ(allowed_bytes_growth, context->allowed_bytes_growth()); - - // Move, no rename, with overwrite. - context.reset(NewContext(NULL)); - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists( - context.get(), src_url, &created)); - context.reset(NewContext(NULL)); - context->set_allowed_bytes_growth(allowed_bytes_growth); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), src_url, dest_url, is_copy)); - EXPECT_EQ( - allowed_bytes_growth + - ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()), - context->allowed_bytes_growth()); -} - -TEST_F(ObfuscatedFileUtilTest, TestCopyInForeignFile) { - TestCopyInForeignFileHelper(false /* overwrite */); - TestCopyInForeignFileHelper(true /* overwrite */); -} - -TEST_F(ObfuscatedFileUtilTest, TestEnumerator) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - FileSystemURL src_url = CreateURLFromUTF8("source dir"); - bool exclusive = true; - bool recursive = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory( - context.get(), src_url, exclusive, recursive)); - - std::set<base::FilePath::StringType> files; - std::set<base::FilePath::StringType> directories; - FillTestDirectory(src_url, &files, &directories); - - FileSystemURL dest_url = CreateURLFromUTF8("destination dir"); - - EXPECT_FALSE(DirectoryExists(dest_url)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Copy( - file_system_context(), src_url, dest_url)); - - ValidateTestDirectory(dest_url, files, directories); - EXPECT_TRUE(DirectoryExists(src_url)); - EXPECT_TRUE(DirectoryExists(dest_url)); - recursive = true; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Remove( - file_system_context(), dest_url, recursive)); - EXPECT_FALSE(DirectoryExists(dest_url)); -} - -TEST_F(ObfuscatedFileUtilTest, TestOriginEnumerator) { - scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> - enumerator(ofu()->CreateOriginEnumerator()); - // The test helper starts out with a single filesystem. - EXPECT_TRUE(enumerator.get()); - EXPECT_EQ(origin(), enumerator->Next()); - ASSERT_TRUE(type() == kFileSystemTypeTemporary); - EXPECT_TRUE(enumerator->HasFileSystemType(kFileSystemTypeTemporary)); - EXPECT_FALSE(enumerator->HasFileSystemType(kFileSystemTypePersistent)); - EXPECT_EQ(GURL(), enumerator->Next()); - EXPECT_FALSE(enumerator->HasFileSystemType(kFileSystemTypeTemporary)); - EXPECT_FALSE(enumerator->HasFileSystemType(kFileSystemTypePersistent)); - - std::set<GURL> origins_expected; - origins_expected.insert(origin()); - - for (size_t i = 0; i < arraysize(kOriginEnumerationTestRecords); ++i) { - SCOPED_TRACE(testing::Message() << - "Validating kOriginEnumerationTestRecords " << i); - const OriginEnumerationTestRecord& record = - kOriginEnumerationTestRecords[i]; - GURL origin_url(record.origin_url); - origins_expected.insert(origin_url); - if (record.has_temporary) { - scoped_ptr<SandboxFileSystemTestHelper> file_system( - NewFileSystem(origin_url, kFileSystemTypeTemporary)); - scoped_ptr<FileSystemOperationContext> context( - NewContext(file_system.get())); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - context.get(), - file_system->CreateURLFromUTF8("file"), - &created)); - EXPECT_TRUE(created); - } - if (record.has_persistent) { - scoped_ptr<SandboxFileSystemTestHelper> file_system( - NewFileSystem(origin_url, kFileSystemTypePersistent)); - scoped_ptr<FileSystemOperationContext> context( - NewContext(file_system.get())); - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - context.get(), - file_system->CreateURLFromUTF8("file"), - &created)); - EXPECT_TRUE(created); - } - } - enumerator.reset(ofu()->CreateOriginEnumerator()); - EXPECT_TRUE(enumerator.get()); - std::set<GURL> origins_found; - GURL origin_url; - while (!(origin_url = enumerator->Next()).is_empty()) { - origins_found.insert(origin_url); - SCOPED_TRACE(testing::Message() << "Handling " << origin_url.spec()); - bool found = false; - for (size_t i = 0; !found && i < arraysize(kOriginEnumerationTestRecords); - ++i) { - const OriginEnumerationTestRecord& record = - kOriginEnumerationTestRecords[i]; - if (GURL(record.origin_url) != origin_url) - continue; - found = true; - EXPECT_EQ(record.has_temporary, - enumerator->HasFileSystemType(kFileSystemTypeTemporary)); - EXPECT_EQ(record.has_persistent, - enumerator->HasFileSystemType(kFileSystemTypePersistent)); - } - // Deal with the default filesystem created by the test helper. - if (!found && origin_url == origin()) { - ASSERT_TRUE(type() == kFileSystemTypeTemporary); - EXPECT_EQ(true, - enumerator->HasFileSystemType(kFileSystemTypeTemporary)); - EXPECT_FALSE(enumerator->HasFileSystemType(kFileSystemTypePersistent)); - found = true; - } - EXPECT_TRUE(found); - } - - std::set<GURL> diff; - std::set_symmetric_difference(origins_expected.begin(), - origins_expected.end(), origins_found.begin(), origins_found.end(), - inserter(diff, diff.begin())); - EXPECT_TRUE(diff.empty()); -} - -TEST_F(ObfuscatedFileUtilTest, TestRevokeUsageCache) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - - int64 expected_quota = 0; - - for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) { - SCOPED_TRACE(testing::Message() << "Creating kRegularTestCase " << i); - const test::TestCaseRecord& test_case = test::kRegularTestCases[i]; - base::FilePath file_path(test_case.path); - expected_quota += ObfuscatedFileUtil::ComputeFilePathCost(file_path); - if (test_case.is_directory) { - bool exclusive = true; - bool recursive = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), CreateURL(file_path), - exclusive, recursive)); - } else { - bool created = false; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), CreateURL(file_path), - &created)); - ASSERT_TRUE(created); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate(context.get(), - CreateURL(file_path), - test_case.data_file_size)); - expected_quota += test_case.data_file_size; - } - } - - // Usually raw size in usage cache and the usage returned by QuotaUtil - // should be same. - EXPECT_EQ(expected_quota, SizeInUsageFile()); - EXPECT_EQ(expected_quota, SizeByQuotaUtil()); - - RevokeUsageCache(); - EXPECT_EQ(-1, SizeInUsageFile()); - EXPECT_EQ(expected_quota, SizeByQuotaUtil()); - - // This should reconstruct the cache. - GetUsageFromQuotaManager(); - EXPECT_EQ(expected_quota, SizeInUsageFile()); - EXPECT_EQ(expected_quota, SizeByQuotaUtil()); - EXPECT_EQ(expected_quota, usage()); -} - -TEST_F(ObfuscatedFileUtilTest, TestInconsistency) { - const FileSystemURL kPath1 = CreateURLFromUTF8("hoge"); - const FileSystemURL kPath2 = CreateURLFromUTF8("fuga"); - - scoped_ptr<FileSystemOperationContext> context; - base::PlatformFile file; - base::PlatformFileInfo file_info; - base::FilePath data_path; - bool created = false; - - // Create a non-empty file. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), kPath1, &created)); - EXPECT_TRUE(created); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate(context.get(), kPath1, 10)); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetFileInfo( - context.get(), kPath1, &file_info, &data_path)); - EXPECT_EQ(10, file_info.size); - - // Destroy database to make inconsistency between database and filesystem. - ofu()->DestroyDirectoryDatabase(origin(), type()); - - // Try to get file info of broken file. - EXPECT_FALSE(PathExists(kPath1)); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), kPath1, &created)); - EXPECT_TRUE(created); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetFileInfo( - context.get(), kPath1, &file_info, &data_path)); - EXPECT_EQ(0, file_info.size); - - // Make another broken file to |kPath2|. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), kPath2, &created)); - EXPECT_TRUE(created); - - // Destroy again. - ofu()->DestroyDirectoryDatabase(origin(), type()); - - // Repair broken |kPath1|. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->Touch(context.get(), kPath1, base::Time::Now(), - base::Time::Now())); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), kPath1, &created)); - EXPECT_TRUE(created); - - // Copy from sound |kPath1| to broken |kPath2|. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile(context.get(), kPath1, kPath2, - true /* copy */)); - - ofu()->DestroyDirectoryDatabase(origin(), type()); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - context.get(), kPath1, - base::PLATFORM_FILE_READ | base::PLATFORM_FILE_CREATE, - &file, &created)); - EXPECT_TRUE(created); - EXPECT_TRUE(base::GetPlatformFileInfo(file, &file_info)); - EXPECT_EQ(0, file_info.size); - EXPECT_TRUE(base::ClosePlatformFile(file)); -} - -TEST_F(ObfuscatedFileUtilTest, TestIncompleteDirectoryReading) { - const FileSystemURL kPath[] = { - CreateURLFromUTF8("foo"), - CreateURLFromUTF8("bar"), - CreateURLFromUTF8("baz") - }; - const FileSystemURL empty_path = CreateURL(base::FilePath()); - scoped_ptr<FileSystemOperationContext> context; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kPath); ++i) { - bool created = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), kPath[i], &created)); - EXPECT_TRUE(created); - } - - std::vector<DirectoryEntry> entries; - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), empty_path, &entries)); - EXPECT_EQ(3u, entries.size()); - - base::FilePath local_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetLocalFilePath(context.get(), kPath[0], &local_path)); - EXPECT_TRUE(base::DeleteFile(local_path, false)); - - entries.clear(); - EXPECT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::ReadDirectory( - file_system_context(), empty_path, &entries)); - EXPECT_EQ(ARRAYSIZE_UNSAFE(kPath) - 1, entries.size()); -} - -TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir"); - - // Create working directory. - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), dir_url, false, false)); - - // EnsureFileExists, create case. - FileSystemURL url(FileSystemURLAppendUTF8( - dir_url, "EnsureFileExists_file")); - bool created = false; - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_TRUE(created); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); - - // non create case. - created = true; - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_FALSE(created); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // fail case. - url = FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_dir"); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), url, false, false)); - - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // CreateOrOpen, create case. - url = FileSystemURLAppendUTF8(dir_url, "CreateOrOpen_file"); - base::PlatformFile file_handle = base::kInvalidPlatformFileValue; - created = false; - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - context.get(), url, - base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); - EXPECT_TRUE(created); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); - - // open case. - file_handle = base::kInvalidPlatformFileValue; - created = true; - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - context.get(), url, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - EXPECT_NE(base::kInvalidPlatformFileValue, file_handle); - EXPECT_FALSE(created); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // fail case - file_handle = base::kInvalidPlatformFileValue; - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, - ofu()->CreateOrOpen( - context.get(), url, - base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - EXPECT_EQ(base::kInvalidPlatformFileValue, file_handle); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // CreateDirectory, create case. - // Creating CreateDirectory_dir and CreateDirectory_dir/subdir. - url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir"); - FileSystemURL subdir_url(FileSystemURLAppendUTF8(url, "subdir")); - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), subdir_url, - true /* exclusive */, true /* recursive */)); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); - - // create subdir case. - // Creating CreateDirectory_dir/subdir2. - subdir_url = FileSystemURLAppendUTF8(url, "subdir2"); - ClearTimestamp(dir_url); - ClearTimestamp(url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), subdir_url, - true /* exclusive */, true /* recursive */)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - EXPECT_NE(base::Time(), GetModifiedTime(url)); - - // fail case. - url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir"); - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, - ofu()->CreateDirectory(context.get(), url, - true /* exclusive */, true /* recursive */)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // CopyInForeignFile, create case. - url = FileSystemURLAppendUTF8(dir_url, "CopyInForeignFile_file"); - FileSystemURL src_path = FileSystemURLAppendUTF8( - dir_url, "CopyInForeignFile_src_file"); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), src_path, &created)); - EXPECT_TRUE(created); - base::FilePath src_local_path; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetLocalFilePath(context.get(), src_path, &src_local_path)); - - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyInForeignFile(context.get(), - src_local_path, - url)); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); -} - -TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) { - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir"); - - // Create working directory. - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), dir_url, false, false)); - - // DeleteFile, delete case. - FileSystemURL url = FileSystemURLAppendUTF8( - dir_url, "DeleteFile_file"); - bool created = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url, &created)); - EXPECT_TRUE(created); - - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), url)); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); - - // fail case. - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - ofu()->DeleteFile(context.get(), url)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // DeleteDirectory, fail case. - url = FileSystemURLAppendUTF8(dir_url, "DeleteDirectory_dir"); - FileSystemURL file_path(FileSystemURLAppendUTF8(url, "pakeratta")); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), url, true, true)); - created = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), file_path, &created)); - EXPECT_TRUE(created); - - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, - ofu()->DeleteDirectory(context.get(), url)); - EXPECT_EQ(base::Time(), GetModifiedTime(dir_url)); - - // delete case. - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile(context.get(), file_path)); - - ClearTimestamp(dir_url); - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url)); - EXPECT_NE(base::Time(), GetModifiedTime(dir_url)); -} - -TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCopyAndMove) { - TestDirectoryTimestampHelper( - CreateURLFromUTF8("copy overwrite"), true, true); - TestDirectoryTimestampHelper( - CreateURLFromUTF8("copy non-overwrite"), true, false); - TestDirectoryTimestampHelper( - CreateURLFromUTF8("move overwrite"), false, true); - TestDirectoryTimestampHelper( - CreateURLFromUTF8("move non-overwrite"), false, false); -} - -TEST_F(ObfuscatedFileUtilTest, TestFileEnumeratorTimestamp) { - FileSystemURL dir = CreateURLFromUTF8("foo"); - FileSystemURL url1 = FileSystemURLAppendUTF8(dir, "bar"); - FileSystemURL url2 = FileSystemURLAppendUTF8(dir, "baz"); - - scoped_ptr<FileSystemOperationContext> context(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), dir, false, false)); - - bool created = false; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(context.get(), url1, &created)); - EXPECT_TRUE(created); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory(context.get(), url2, false, false)); - - base::FilePath file_path; - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetLocalFilePath(context.get(), url1, &file_path)); - EXPECT_FALSE(file_path.empty()); - - context.reset(NewContext(NULL)); - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->Touch(context.get(), url1, - base::Time::Now() + base::TimeDelta::FromHours(1), - base::Time())); - - context.reset(NewContext(NULL)); - scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( - ofu()->CreateFileEnumerator(context.get(), dir, false)); - - int count = 0; - base::FilePath file_path_each; - while (!(file_path_each = file_enum->Next()).empty()) { - context.reset(NewContext(NULL)); - base::PlatformFileInfo file_info; - base::FilePath file_path; - EXPECT_EQ(base::PLATFORM_FILE_OK, - ofu()->GetFileInfo(context.get(), - FileSystemURL::CreateForTest( - dir.origin(), - dir.mount_type(), - file_path_each), - &file_info, &file_path)); - EXPECT_EQ(file_info.is_directory, file_enum->IsDirectory()); - EXPECT_EQ(file_info.last_modified, file_enum->LastModifiedTime()); - EXPECT_EQ(file_info.size, file_enum->Size()); - ++count; - } - EXPECT_EQ(2, count); -} - -// crbug.com/176470 -#if defined(OS_WIN) || defined(OS_ANDROID) -#define MAYBE_TestQuotaOnCopyFile DISABLED_TestQuotaOnCopyFile -#else -#define MAYBE_TestQuotaOnCopyFile TestQuotaOnCopyFile -#endif -TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) { - FileSystemURL from_file(CreateURLFromUTF8("fromfile")); - FileSystemURL obstacle_file(CreateURLFromUTF8("obstaclefile")); - FileSystemURL to_file1(CreateURLFromUTF8("tofile1")); - FileSystemURL to_file2(CreateURLFromUTF8("tofile2")); - bool created; - - int64 expected_total_file_size = 0; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(from_file))->context(), - from_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(obstacle_file))->context(), - obstacle_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 from_file_size = 1020; - expected_total_file_size += from_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(from_file_size)->context(), - from_file, from_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 obstacle_file_size = 1; - expected_total_file_size += obstacle_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(obstacle_file_size)->context(), - obstacle_file, obstacle_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 to_file1_size = from_file_size; - expected_total_file_size += to_file1_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - AllowUsageIncrease( - PathCost(to_file1) + to_file1_size)->context(), - from_file, to_file1, true /* copy */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, - ofu()->CopyOrMoveFile( - DisallowUsageIncrease( - PathCost(to_file2) + from_file_size)->context(), - from_file, to_file2, true /* copy */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 old_obstacle_file_size = obstacle_file_size; - obstacle_file_size = from_file_size; - expected_total_file_size += obstacle_file_size - old_obstacle_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - AllowUsageIncrease( - obstacle_file_size - old_obstacle_file_size)->context(), - from_file, obstacle_file, true /* copy */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 old_from_file_size = from_file_size; - from_file_size = old_from_file_size - 1; - expected_total_file_size += from_file_size - old_from_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease( - from_file_size - old_from_file_size)->context(), - from_file, from_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - // quota exceeded - { - old_obstacle_file_size = obstacle_file_size; - obstacle_file_size = from_file_size; - expected_total_file_size += obstacle_file_size - old_obstacle_file_size; - scoped_ptr<UsageVerifyHelper> helper = AllowUsageIncrease( - obstacle_file_size - old_obstacle_file_size); - helper->context()->set_allowed_bytes_growth( - helper->context()->allowed_bytes_growth() - 1); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - helper->context(), - from_file, obstacle_file, true /* copy */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - } -} - -TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) { - FileSystemURL from_file(CreateURLFromUTF8("fromfile")); - FileSystemURL obstacle_file(CreateURLFromUTF8("obstaclefile")); - FileSystemURL to_file(CreateURLFromUTF8("tofile")); - bool created; - - int64 expected_total_file_size = 0; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(from_file))->context(), - from_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 from_file_size = 1020; - expected_total_file_size += from_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(from_file_size)->context(), - from_file, from_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 to_file_size ALLOW_UNUSED = from_file_size; - from_file_size = 0; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - AllowUsageIncrease(-PathCost(from_file) + - PathCost(to_file))->context(), - from_file, to_file, false /* move */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(from_file))->context(), - from_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(obstacle_file))->context(), - obstacle_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - from_file_size = 1020; - expected_total_file_size += from_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(from_file_size)->context(), - from_file, from_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 obstacle_file_size = 1; - expected_total_file_size += obstacle_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(1)->context(), - obstacle_file, obstacle_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - int64 old_obstacle_file_size = obstacle_file_size; - obstacle_file_size = from_file_size; - from_file_size = 0; - expected_total_file_size -= old_obstacle_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - AllowUsageIncrease( - -old_obstacle_file_size - PathCost(from_file))->context(), - from_file, obstacle_file, - false /* move */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(from_file))->context(), - from_file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - from_file_size = 10; - expected_total_file_size += from_file_size; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(from_file_size)->context(), - from_file, from_file_size)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - - // quota exceeded even after operation - old_obstacle_file_size = obstacle_file_size; - obstacle_file_size = from_file_size; - from_file_size = 0; - expected_total_file_size -= old_obstacle_file_size; - scoped_ptr<FileSystemOperationContext> context = - LimitedContext(-old_obstacle_file_size - PathCost(from_file) - 1); - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CopyOrMoveFile( - context.get(), from_file, obstacle_file, false /* move */)); - ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize()); - context.reset(); -} - -TEST_F(ObfuscatedFileUtilTest, TestQuotaOnRemove) { - FileSystemURL dir(CreateURLFromUTF8("dir")); - FileSystemURL file(CreateURLFromUTF8("file")); - FileSystemURL dfile1(CreateURLFromUTF8("dir/dfile1")); - FileSystemURL dfile2(CreateURLFromUTF8("dir/dfile2")); - bool created; - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(file))->context(), - file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(0, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateDirectory( - AllowUsageIncrease(PathCost(dir))->context(), - dir, false, false)); - ASSERT_EQ(0, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(dfile1))->context(), - dfile1, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(0, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(dfile2))->context(), - dfile2, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(0, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(340)->context(), - file, 340)); - ASSERT_EQ(340, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(1020)->context(), - dfile1, 1020)); - ASSERT_EQ(1360, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(120)->context(), - dfile2, 120)); - ASSERT_EQ(1480, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->DeleteFile( - AllowUsageIncrease(-PathCost(file) - 340)->context(), - file)); - ASSERT_EQ(1140, ComputeTotalFileSize()); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::Remove( - file_system_context(), dir, true /* recursive */)); - ASSERT_EQ(0, ComputeTotalFileSize()); -} - -TEST_F(ObfuscatedFileUtilTest, TestQuotaOnOpen) { - FileSystemURL file(CreateURLFromUTF8("file")); - base::PlatformFile file_handle; - bool created; - - // Creating a file. - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists( - AllowUsageIncrease(PathCost(file))->context(), - file, &created)); - ASSERT_TRUE(created); - ASSERT_EQ(0, ComputeTotalFileSize()); - - // Opening it, which shouldn't change the usage. - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - AllowUsageIncrease(0)->context(), file, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - ASSERT_EQ(0, ComputeTotalFileSize()); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - - const int length = 33; - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(length)->context(), file, length)); - ASSERT_EQ(length, ComputeTotalFileSize()); - - // Opening it with CREATE_ALWAYS flag, which should truncate the file size. - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - AllowUsageIncrease(-length)->context(), file, - base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - ASSERT_EQ(0, ComputeTotalFileSize()); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); - - // Extending the file again. - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->Truncate( - AllowUsageIncrease(length)->context(), file, length)); - ASSERT_EQ(length, ComputeTotalFileSize()); - - // Opening it with TRUNCATED flag, which should truncate the file size. - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->CreateOrOpen( - AllowUsageIncrease(-length)->context(), file, - base::PLATFORM_FILE_OPEN_TRUNCATED | base::PLATFORM_FILE_WRITE, - &file_handle, &created)); - ASSERT_EQ(0, ComputeTotalFileSize()); - EXPECT_TRUE(base::ClosePlatformFile(file_handle)); -} - -TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAliveCase) { - ObfuscatedFileUtil file_util(NULL, - data_dir_path(), - base::MessageLoopProxy::current().get()); - file_util.InitOriginDatabase(true /*create*/); - ASSERT_TRUE(file_util.origin_database_ != NULL); - - // Callback to Drop DB is called while ObfuscatedFileUtilTest is still alive. - file_util.db_flush_delay_seconds_ = 0; - file_util.MarkUsed(); - base::RunLoop().RunUntilIdle(); - - ASSERT_TRUE(file_util.origin_database_ == NULL); -} - -TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAlreadyDeletedCase) { - // Run message loop after OFU is already deleted to make sure callback doesn't - // cause a crash for use after free. - { - ObfuscatedFileUtil file_util(NULL, - data_dir_path(), - base::MessageLoopProxy::current().get()); - file_util.InitOriginDatabase(true /*create*/); - file_util.db_flush_delay_seconds_ = 0; - file_util.MarkUsed(); - } - - // At this point the callback is still in the message queue but OFU is gone. - base::RunLoop().RunUntilIdle(); -} - -TEST_F(ObfuscatedFileUtilTest, DestroyDirectoryDatabase_Isolated) { - storage_policy_->AddIsolated(origin_); - ObfuscatedFileUtil file_util( - storage_policy_.get(), data_dir_path(), - base::MessageLoopProxy::current().get()); - - // Create DirectoryDatabase for isolated origin. - SandboxDirectoryDatabase* db = file_util.GetDirectoryDatabase( - origin_, kFileSystemTypePersistent, true /* create */); - ASSERT_TRUE(db != NULL); - - // Destory it. - ASSERT_TRUE( - file_util.DestroyDirectoryDatabase(origin_, kFileSystemTypePersistent)); - ASSERT_TRUE(file_util.directories_.empty()); -} - -TEST_F(ObfuscatedFileUtilTest, GetDirectoryDatabase_Isolated) { - storage_policy_->AddIsolated(origin_); - ObfuscatedFileUtil file_util( - storage_policy_.get(), data_dir_path(), - base::MessageLoopProxy::current().get()); - - // Create DirectoryDatabase for isolated origin. - SandboxDirectoryDatabase* db = file_util.GetDirectoryDatabase( - origin_, kFileSystemTypePersistent, true /* create */); - ASSERT_TRUE(db != NULL); - ASSERT_EQ(1U, file_util.directories_.size()); - - // Remove isolated. - storage_policy_->RemoveIsolated(origin_); - - // This should still get the same database. - SandboxDirectoryDatabase* db2 = file_util.GetDirectoryDatabase( - origin_, kFileSystemTypePersistent, false /* create */); - ASSERT_EQ(db, db2); -} - -TEST_F(ObfuscatedFileUtilTest, MigrationBackFromIsolated) { - std::string kFakeDirectoryData("0123456789"); - base::FilePath old_directory_db_path; - - // Initialize the directory with one origin using - // SandboxIsolatedOriginDatabase. - { - std::string origin_string = - webkit_database::GetIdentifierFromOrigin(origin_); - SandboxIsolatedOriginDatabase database_old(origin_string, data_dir_path()); - base::FilePath path; - EXPECT_TRUE(database_old.GetPathForOrigin(origin_string, &path)); - EXPECT_FALSE(path.empty()); - - // Populate the origin directory with some fake data. - old_directory_db_path = data_dir_path().Append(path); - ASSERT_TRUE(file_util::CreateDirectory(old_directory_db_path)); - EXPECT_EQ(static_cast<int>(kFakeDirectoryData.size()), - file_util::WriteFile(old_directory_db_path.AppendASCII("dummy"), - kFakeDirectoryData.data(), - kFakeDirectoryData.size())); - } - - storage_policy_->AddIsolated(origin_); - ObfuscatedFileUtil file_util( - storage_policy_.get(), data_dir_path(), - base::MessageLoopProxy::current().get()); - base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; - base::FilePath origin_directory = file_util.GetDirectoryForOrigin( - origin_, true /* create */, &error); - EXPECT_EQ(base::PLATFORM_FILE_OK, error); - - // The database is migrated from the old one. - EXPECT_TRUE(base::DirectoryExists(origin_directory)); - EXPECT_FALSE(base::DirectoryExists(old_directory_db_path)); - - // Check we see the same contents in the new origin directory. - std::string origin_db_data; - EXPECT_TRUE(base::PathExists(origin_directory.AppendASCII("dummy"))); - EXPECT_TRUE(base::ReadFileToString( - origin_directory.AppendASCII("dummy"), &origin_db_data)); - EXPECT_EQ(kFakeDirectoryData, origin_db_data); -} - -TEST_F(ObfuscatedFileUtilTest, OpenPathInNonDirectory) { - FileSystemURL file(CreateURLFromUTF8("file")); - FileSystemURL path_in_file(CreateURLFromUTF8("file/file")); - bool created; - - ASSERT_EQ(base::PLATFORM_FILE_OK, - ofu()->EnsureFileExists(UnlimitedContext().get(), file, &created)); - ASSERT_TRUE(created); - - created = false; - base::PlatformFile file_handle = base::kInvalidPlatformFileValue; - int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, - ofu()->CreateOrOpen(UnlimitedContext().get(), - path_in_file, - file_flags, - &file_handle, - &created)); - ASSERT_FALSE(created); - ASSERT_EQ(base::kInvalidPlatformFileValue, file_handle); - - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, - ofu()->CreateDirectory(UnlimitedContext().get(), - path_in_file, - false /* exclusive */, - false /* recursive */)); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.cc b/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.cc new file mode 100644 index 00000000000..0426bd24911 --- /dev/null +++ b/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.cc @@ -0,0 +1,297 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/plugin_private_file_system_backend.h" + +#include <map> + +#include "base/stl_util.h" +#include "base/synchronization/lock.h" +#include "base/task_runner_util.h" +#include "net/base/net_util.h" +#include "webkit/browser/blob/file_stream_reader.h" +#include "webkit/browser/fileapi/async_file_util_adapter.h" +#include "webkit/browser/fileapi/file_stream_writer.h" +#include "webkit/browser/fileapi/file_system_context.h" +#include "webkit/browser/fileapi/file_system_operation.h" +#include "webkit/browser/fileapi/file_system_operation_context.h" +#include "webkit/browser/fileapi/file_system_options.h" +#include "webkit/browser/fileapi/isolated_context.h" +#include "webkit/browser/fileapi/obfuscated_file_util.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" +#include "webkit/common/fileapi/file_system_util.h" + +namespace fileapi { + +class PluginPrivateFileSystemBackend::FileSystemIDToPluginMap { + public: + explicit FileSystemIDToPluginMap(base::SequencedTaskRunner* task_runner) + : task_runner_(task_runner) {} + ~FileSystemIDToPluginMap() {} + + std::string GetPluginIDForURL(const FileSystemURL& url) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + Map::iterator found = map_.find(url.filesystem_id()); + if (url.type() != kFileSystemTypePluginPrivate || found == map_.end()) { + NOTREACHED() << "Unsupported url is given: " << url.DebugString(); + return std::string(); + } + return found->second; + } + + void RegisterFileSystem(const std::string& filesystem_id, + const std::string& plugin_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + DCHECK(!filesystem_id.empty()); + DCHECK(!ContainsKey(map_, filesystem_id)) << filesystem_id; + map_[filesystem_id] = plugin_id; + } + + void RemoveFileSystem(const std::string& filesystem_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + map_.erase(filesystem_id); + } + + private: + typedef std::map<std::string, std::string> Map; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + Map map_; +}; + +namespace { + +const base::FilePath::CharType* kFileSystemDirectory = + SandboxFileSystemBackendDelegate::kFileSystemDirectory; +const base::FilePath::CharType* kPluginPrivateDirectory = + FILE_PATH_LITERAL("Plugins"); + +base::PlatformFileError OpenFileSystemOnFileThread( + ObfuscatedFileUtil* file_util, + PluginPrivateFileSystemBackend::FileSystemIDToPluginMap* plugin_map, + const GURL& origin_url, + const std::string& filesystem_id, + const std::string& plugin_id, + OpenFileSystemMode mode) { + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT); + file_util->GetDirectoryForOriginAndType( + origin_url, plugin_id, create, &error); + if (error == base::PLATFORM_FILE_OK) + plugin_map->RegisterFileSystem(filesystem_id, plugin_id); + return error; +} + +} // namespace + +PluginPrivateFileSystemBackend::PluginPrivateFileSystemBackend( + base::SequencedTaskRunner* file_task_runner, + const base::FilePath& profile_path, + quota::SpecialStoragePolicy* special_storage_policy, + const FileSystemOptions& file_system_options) + : file_task_runner_(file_task_runner), + file_system_options_(file_system_options), + base_path_(profile_path.Append( + kFileSystemDirectory).Append(kPluginPrivateDirectory)), + plugin_map_(new FileSystemIDToPluginMap(file_task_runner)), + weak_factory_(this) { + file_util_.reset( + new AsyncFileUtilAdapter(new ObfuscatedFileUtil( + special_storage_policy, + base_path_, + file_task_runner, + base::Bind(&FileSystemIDToPluginMap::GetPluginIDForURL, + base::Owned(plugin_map_)), + std::set<std::string>(), + NULL))); +} + +PluginPrivateFileSystemBackend::~PluginPrivateFileSystemBackend() { + if (!file_task_runner_->RunsTasksOnCurrentThread()) { + AsyncFileUtil* file_util = file_util_.release(); + if (!file_task_runner_->DeleteSoon(FROM_HERE, file_util)) + delete file_util; + } +} + +void PluginPrivateFileSystemBackend::OpenPrivateFileSystem( + const GURL& origin_url, + FileSystemType type, + const std::string& filesystem_id, + const std::string& plugin_id, + OpenFileSystemMode mode, + const StatusCallback& callback) { + if (!CanHandleType(type) || file_system_options_.is_incognito()) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, base::Bind(callback, base::PLATFORM_FILE_ERROR_SECURITY)); + return; + } + + PostTaskAndReplyWithResult( + file_task_runner_.get(), + FROM_HERE, + base::Bind(&OpenFileSystemOnFileThread, + obfuscated_file_util(), plugin_map_, + origin_url, filesystem_id, plugin_id, mode), + callback); +} + +bool PluginPrivateFileSystemBackend::CanHandleType(FileSystemType type) const { + return type == kFileSystemTypePluginPrivate; +} + +void PluginPrivateFileSystemBackend::Initialize(FileSystemContext* context) { +} + +void PluginPrivateFileSystemBackend::OpenFileSystem( + const GURL& origin_url, + FileSystemType type, + OpenFileSystemMode mode, + const OpenFileSystemCallback& callback) { + // We never allow opening a new plugin-private filesystem via usual + // OpenFileSystem. + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(callback, GURL(), std::string(), + base::PLATFORM_FILE_ERROR_SECURITY)); +} + +AsyncFileUtil* +PluginPrivateFileSystemBackend::GetAsyncFileUtil(FileSystemType type) { + return file_util_.get(); +} + +CopyOrMoveFileValidatorFactory* +PluginPrivateFileSystemBackend::GetCopyOrMoveFileValidatorFactory( + FileSystemType type, + base::PlatformFileError* error_code) { + DCHECK(error_code); + *error_code = base::PLATFORM_FILE_OK; + return NULL; +} + +FileSystemOperation* PluginPrivateFileSystemBackend::CreateFileSystemOperation( + const FileSystemURL& url, + FileSystemContext* context, + base::PlatformFileError* error_code) const { + scoped_ptr<FileSystemOperationContext> operation_context( + new FileSystemOperationContext(context)); + return FileSystemOperation::Create(url, context, operation_context.Pass()); +} + +scoped_ptr<webkit_blob::FileStreamReader> +PluginPrivateFileSystemBackend::CreateFileStreamReader( + const FileSystemURL& url, + int64 offset, + const base::Time& expected_modification_time, + FileSystemContext* context) const { + return scoped_ptr<webkit_blob::FileStreamReader>(); +} + +scoped_ptr<FileStreamWriter> +PluginPrivateFileSystemBackend::CreateFileStreamWriter( + const FileSystemURL& url, + int64 offset, + FileSystemContext* context) const { + return scoped_ptr<FileStreamWriter>(); +} + +FileSystemQuotaUtil* PluginPrivateFileSystemBackend::GetQuotaUtil() { + return this; +} + +base::PlatformFileError +PluginPrivateFileSystemBackend::DeleteOriginDataOnFileThread( + FileSystemContext* context, + quota::QuotaManagerProxy* proxy, + const GURL& origin_url, + FileSystemType type) { + if (!CanHandleType(type)) + return base::PLATFORM_FILE_ERROR_SECURITY; + bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType( + origin_url, std::string()); + if (result) + return base::PLATFORM_FILE_OK; + return base::PLATFORM_FILE_ERROR_FAILED; +} + +void PluginPrivateFileSystemBackend::GetOriginsForTypeOnFileThread( + FileSystemType type, + std::set<GURL>* origins) { + if (!CanHandleType(type)) + return; + scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator( + obfuscated_file_util()->CreateOriginEnumerator()); + GURL origin; + while (!(origin = enumerator->Next()).is_empty()) + origins->insert(origin); +} + +void PluginPrivateFileSystemBackend::GetOriginsForHostOnFileThread( + FileSystemType type, + const std::string& host, + std::set<GURL>* origins) { + if (!CanHandleType(type)) + return; + scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator( + obfuscated_file_util()->CreateOriginEnumerator()); + GURL origin; + while (!(origin = enumerator->Next()).is_empty()) { + if (host == net::GetHostOrSpecFromURL(origin)) + origins->insert(origin); + } +} + +int64 PluginPrivateFileSystemBackend::GetOriginUsageOnFileThread( + FileSystemContext* context, + const GURL& origin_url, + FileSystemType type) { + // We don't track usage on this filesystem. + return 0; +} + +scoped_refptr<QuotaReservation> +PluginPrivateFileSystemBackend::CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type) { + // We don't track usage on this filesystem. + NOTREACHED(); + return scoped_refptr<QuotaReservation>(); +} + +void PluginPrivateFileSystemBackend::AddFileUpdateObserver( + FileSystemType type, + FileUpdateObserver* observer, + base::SequencedTaskRunner* task_runner) {} + +void PluginPrivateFileSystemBackend::AddFileChangeObserver( + FileSystemType type, + FileChangeObserver* observer, + base::SequencedTaskRunner* task_runner) {} + +void PluginPrivateFileSystemBackend::AddFileAccessObserver( + FileSystemType type, + FileAccessObserver* observer, + base::SequencedTaskRunner* task_runner) {} + +const UpdateObserverList* PluginPrivateFileSystemBackend::GetUpdateObservers( + FileSystemType type) const { + return NULL; +} + +const ChangeObserverList* PluginPrivateFileSystemBackend::GetChangeObservers( + FileSystemType type) const { + return NULL; +} + +const AccessObserverList* PluginPrivateFileSystemBackend::GetAccessObservers( + FileSystemType type) const { + return NULL; +} + +ObfuscatedFileUtil* PluginPrivateFileSystemBackend::obfuscated_file_util() { + return static_cast<ObfuscatedFileUtil*>( + static_cast<AsyncFileUtilAdapter*>(file_util_.get())->sync_file_util()); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.h b/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.h new file mode 100644 index 00000000000..d41f4b7eced --- /dev/null +++ b/chromium/webkit/browser/fileapi/plugin_private_file_system_backend.h @@ -0,0 +1,142 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_PLUGIN_PRIVATE_FILE_SYSTEM_BACKEND_H_ +#define WEBKIT_BROWSER_FILEAPI_PLUGIN_PRIVATE_FILE_SYSTEM_BACKEND_H_ + +#include <set> +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "webkit/browser/fileapi/file_system_backend.h" +#include "webkit/browser/fileapi/file_system_options.h" +#include "webkit/browser/fileapi/file_system_quota_util.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace quota { +class SpecialStoragePolicy; +} + +namespace fileapi { + +class ObfuscatedFileUtil; + +class WEBKIT_STORAGE_BROWSER_EXPORT PluginPrivateFileSystemBackend + : public FileSystemBackend, + public FileSystemQuotaUtil { + public: + class FileSystemIDToPluginMap; + typedef base::Callback<void(base::PlatformFileError result)> StatusCallback; + + PluginPrivateFileSystemBackend( + base::SequencedTaskRunner* file_task_runner, + const base::FilePath& profile_path, + quota::SpecialStoragePolicy* special_storage_policy, + const FileSystemOptions& file_system_options); + virtual ~PluginPrivateFileSystemBackend(); + + // This must be used to open 'private' filesystem instead of regular + // OpenFileSystem. + // |plugin_id| must be an identifier string for per-plugin + // isolation, e.g. name, MIME type etc. + // NOTE: |plugin_id| must be sanitized ASCII string that doesn't + // include *any* dangerous character like '/'. + void OpenPrivateFileSystem( + const GURL& origin_url, + FileSystemType type, + const std::string& filesystem_id, + const std::string& plugin_id, + OpenFileSystemMode mode, + const StatusCallback& callback); + + // FileSystemBackend overrides. + virtual bool CanHandleType(FileSystemType type) const OVERRIDE; + virtual void Initialize(FileSystemContext* context) OVERRIDE; + virtual void OpenFileSystem( + const GURL& origin_url, + FileSystemType type, + OpenFileSystemMode mode, + const OpenFileSystemCallback& callback) OVERRIDE; + virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; + virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( + FileSystemType type, + base::PlatformFileError* error_code) 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; + + // FileSystemQuotaUtil overrides. + virtual base::PlatformFileError DeleteOriginDataOnFileThread( + FileSystemContext* context, + quota::QuotaManagerProxy* proxy, + const GURL& origin_url, + FileSystemType type) OVERRIDE; + virtual void GetOriginsForTypeOnFileThread( + FileSystemType type, + std::set<GURL>* origins) OVERRIDE; + virtual void GetOriginsForHostOnFileThread( + FileSystemType type, + const std::string& host, + std::set<GURL>* origins) OVERRIDE; + virtual int64 GetOriginUsageOnFileThread( + FileSystemContext* context, + const GURL& origin_url, + FileSystemType type) OVERRIDE; + virtual scoped_refptr<QuotaReservation> + CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type) OVERRIDE; + virtual void AddFileUpdateObserver( + FileSystemType type, + FileUpdateObserver* observer, + base::SequencedTaskRunner* task_runner) OVERRIDE; + virtual void AddFileChangeObserver( + FileSystemType type, + FileChangeObserver* observer, + base::SequencedTaskRunner* task_runner) OVERRIDE; + virtual void AddFileAccessObserver( + FileSystemType type, + FileAccessObserver* observer, + base::SequencedTaskRunner* task_runner) OVERRIDE; + virtual const UpdateObserverList* GetUpdateObservers( + FileSystemType type) const OVERRIDE; + virtual const ChangeObserverList* GetChangeObservers( + FileSystemType type) const OVERRIDE; + virtual const AccessObserverList* GetAccessObservers( + FileSystemType type) const OVERRIDE; + + private: + friend class PluginPrivateFileSystemBackendTest; + + ObfuscatedFileUtil* obfuscated_file_util(); + const base::FilePath& base_path() const { return base_path_; } + + scoped_refptr<base::SequencedTaskRunner> file_task_runner_; + const FileSystemOptions file_system_options_; + const base::FilePath base_path_; + scoped_ptr<AsyncFileUtil> file_util_; + FileSystemIDToPluginMap* plugin_map_; // Owned by file_util_. + base::WeakPtrFactory<PluginPrivateFileSystemBackend> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PluginPrivateFileSystemBackend); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_PLUGIN_PRIVATE_FILE_SYSTEM_BACKEND_H_ diff --git a/chromium/webkit/browser/fileapi/quota/open_file_handle.cc b/chromium/webkit/browser/fileapi/quota/open_file_handle.cc new file mode 100644 index 00000000000..f0f69c66587 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/open_file_handle.cc @@ -0,0 +1,40 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/open_file_handle.h" + +#include "webkit/browser/fileapi/quota/open_file_handle_context.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" + +namespace fileapi { + +OpenFileHandle::~OpenFileHandle() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); +} + +int64 OpenFileHandle::UpdateMaxWrittenOffset(int64 offset) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + + int64 new_file_size = 0; + int64 growth = 0; + context_->UpdateMaxWrittenOffset(offset, &new_file_size, &growth); + + if (growth > 0) + reservation_->ConsumeReservation(growth); + + return new_file_size; +} + +int64 OpenFileHandle::base_file_size() const { + return context_->base_file_size(); +} + +OpenFileHandle::OpenFileHandle(QuotaReservation* reservation, + OpenFileHandleContext* context) + : reservation_(reservation), + context_(context) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/open_file_handle.h b/chromium/webkit/browser/fileapi/quota/open_file_handle.h new file mode 100644 index 00000000000..79f1380a437 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/open_file_handle.h @@ -0,0 +1,51 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_ +#define WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "webkit/browser/webkit_storage_browser_export.h" + +namespace fileapi { + +class QuotaReservation; +class OpenFileHandleContext; +class QuotaReservationBuffer; + +// Represents an open file like a file descriptor. +// This should be alive while a consumer keeps a file opened and should be +// deleted when the plugin closes the file. +class WEBKIT_STORAGE_BROWSER_EXPORT OpenFileHandle { + public: + ~OpenFileHandle(); + + // Updates cached file size and consumes quota for that. + // This should be called for each modified file before calling RefreshQuota + // and file close. + // Returns updated base file size that should be used to measure quota + // consumption by difference to this. + int64 UpdateMaxWrittenOffset(int64 offset); + + int64 base_file_size() const; + + private: + friend class QuotaReservationBuffer; + + OpenFileHandle(QuotaReservation* reservation, + OpenFileHandleContext* context); + + scoped_refptr<QuotaReservation> reservation_; + scoped_refptr<OpenFileHandleContext> context_; + + base::SequenceChecker sequence_checker_; + + DISALLOW_COPY_AND_ASSIGN(OpenFileHandle); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_ diff --git a/chromium/webkit/browser/fileapi/quota/open_file_handle_context.cc b/chromium/webkit/browser/fileapi/quota/open_file_handle_context.cc new file mode 100644 index 00000000000..0928fff7ee2 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/open_file_handle_context.cc @@ -0,0 +1,60 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/open_file_handle_context.h" + +#include "base/file_util.h" +#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h" + +namespace fileapi { + +OpenFileHandleContext::OpenFileHandleContext( + const base::FilePath& platform_path, + QuotaReservationBuffer* reservation_buffer) + : initial_file_size_(0), + maximum_written_offset_(0), + platform_path_(platform_path), + reservation_buffer_(reservation_buffer) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + + base::GetFileSize(platform_path, &initial_file_size_); + maximum_written_offset_ = initial_file_size_; +} + +void OpenFileHandleContext::UpdateMaxWrittenOffset( + int64 offset, + int64* new_file_size, + int64* growth) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + if (offset > maximum_written_offset_) { + *growth = offset - maximum_written_offset_; + maximum_written_offset_ = offset; + } else { + *growth = 0; + } + + *new_file_size = maximum_written_offset_; +} + +OpenFileHandleContext::~OpenFileHandleContext() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + + // TODO(tzik): Optimize this for single operation. + + int64 file_size = 0; + base::GetFileSize(platform_path_, &file_size); + int64 usage_delta = file_size - initial_file_size_; + + // |quota_consumption| may be greater than the recorded file growth when a + // plugin crashed before reporting its consumption. + // In this case, the reserved quota for the plugin should be handled as + // consumed quota. + int64 quota_consumption = + std::max(maximum_written_offset_, file_size) - initial_file_size_; + + reservation_buffer_->CommitFileGrowth(quota_consumption, usage_delta); + reservation_buffer_->DetachOpenFileHandleContext(this); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/open_file_handle_context.h b/chromium/webkit/browser/fileapi/quota/open_file_handle_context.h new file mode 100644 index 00000000000..4b5d3cc75cd --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/open_file_handle_context.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_ +#define WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "url/gurl.h" +#include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_types.h" + +namespace fileapi { + +class QuotaReservationBuffer; + +// This class represents a underlying file of a managed FileSystem file. +// The instance keeps alive while at least one consumer keeps an open file +// handle. +// This class is usually manipulated only via OpenFileHandle. +class OpenFileHandleContext : public base::RefCounted<OpenFileHandleContext> { + public: + OpenFileHandleContext(const base::FilePath& platform_path, + QuotaReservationBuffer* reservation_buffer); + + void UpdateMaxWrittenOffset(int64 offset, + int64* new_file_size, + int64* growth); + + const base::FilePath& platform_path() const { + return platform_path_; + } + + int64 base_file_size() const { return maximum_written_offset_; } + + private: + friend class base::RefCounted<OpenFileHandleContext>; + virtual ~OpenFileHandleContext(); + + int64 initial_file_size_; + int64 maximum_written_offset_; + base::FilePath platform_path_; + + scoped_refptr<QuotaReservationBuffer> reservation_buffer_; + + base::SequenceChecker sequence_checker_; + + DISALLOW_COPY_AND_ASSIGN(OpenFileHandleContext); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_ diff --git a/chromium/webkit/browser/fileapi/quota/quota_backend_impl.cc b/chromium/webkit/browser/fileapi/quota/quota_backend_impl.cc new file mode 100644 index 00000000000..41cc95297d8 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_backend_impl.cc @@ -0,0 +1,156 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_backend_impl.h" + +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/sequenced_task_runner.h" +#include "webkit/browser/fileapi/file_system_usage_cache.h" +#include "webkit/browser/quota/quota_client.h" +#include "webkit/browser/quota/quota_manager.h" +#include "webkit/common/fileapi/file_system_util.h" + +namespace fileapi { + +QuotaBackendImpl::QuotaBackendImpl( + base::SequencedTaskRunner* file_task_runner, + ObfuscatedFileUtil* obfuscated_file_util, + FileSystemUsageCache* file_system_usage_cache, + quota::QuotaManagerProxy* quota_manager_proxy) + : file_task_runner_(file_task_runner), + obfuscated_file_util_(obfuscated_file_util), + file_system_usage_cache_(file_system_usage_cache), + quota_manager_proxy_(quota_manager_proxy), + weak_ptr_factory_(this) { +} + +QuotaBackendImpl::~QuotaBackendImpl() { +} + +void QuotaBackendImpl::ReserveQuota(const GURL& origin, + FileSystemType type, + int64 delta, + const ReserveQuotaCallback& callback) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + if (!delta) { + callback.Run(base::PLATFORM_FILE_OK); + return; + } + DCHECK(quota_manager_proxy_); + quota_manager_proxy_->GetUsageAndQuota( + file_task_runner_, origin, FileSystemTypeToQuotaStorageType(type), + base::Bind(&QuotaBackendImpl::DidGetUsageAndQuotaForReserveQuota, + weak_ptr_factory_.GetWeakPtr(), + QuotaReservationInfo(origin, type, delta), callback)); +} + +void QuotaBackendImpl::ReleaseReservedQuota(const GURL& origin, + FileSystemType type, + int64 size) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + DCHECK_LE(0, size); + if (!size) + return; + ReserveQuotaInternal(QuotaReservationInfo(origin, type, -size)); +} + +void QuotaBackendImpl::CommitQuotaUsage(const GURL& origin, + FileSystemType type, + int64 delta) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + if (!delta) + return; + ReserveQuotaInternal(QuotaReservationInfo(origin, type, delta)); + base::FilePath path; + if (GetUsageCachePath(origin, type, &path) != base::PLATFORM_FILE_OK) + return; + bool result = file_system_usage_cache_->AtomicUpdateUsageByDelta(path, delta); + DCHECK(result); +} + +void QuotaBackendImpl::IncrementDirtyCount(const GURL& origin, + FileSystemType type) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + base::FilePath path; + if (GetUsageCachePath(origin, type, &path) != base::PLATFORM_FILE_OK) + return; + DCHECK(file_system_usage_cache_); + file_system_usage_cache_->IncrementDirty(path); +} + +void QuotaBackendImpl::DecrementDirtyCount(const GURL& origin, + FileSystemType type) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + base::FilePath path; + if (GetUsageCachePath(origin, type, &path) != base::PLATFORM_FILE_OK) + return; + DCHECK(file_system_usage_cache_); + file_system_usage_cache_->DecrementDirty(path); +} + +void QuotaBackendImpl::DidGetUsageAndQuotaForReserveQuota( + const QuotaReservationInfo& info, + const ReserveQuotaCallback& callback, + quota::QuotaStatusCode status, int64 usage, int64 quota) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(info.origin.is_valid()); + if (status != quota::kQuotaStatusOk) { + callback.Run(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + + if (quota < usage + info.delta) { + callback.Run(base::PLATFORM_FILE_ERROR_NO_SPACE); + return; + } + + ReserveQuotaInternal(info); + if (callback.Run(base::PLATFORM_FILE_OK)) + return; + // The requester could not accept the reserved quota. Revert it. + ReserveQuotaInternal( + QuotaReservationInfo(info.origin, info.type, -info.delta)); +} + +void QuotaBackendImpl::ReserveQuotaInternal(const QuotaReservationInfo& info) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(info.origin.is_valid()); + DCHECK(quota_manager_proxy_); + quota_manager_proxy_->NotifyStorageModified( + quota::QuotaClient::kFileSystem, info.origin, + FileSystemTypeToQuotaStorageType(info.type), info.delta); +} + +base::PlatformFileError QuotaBackendImpl::GetUsageCachePath( + const GURL& origin, + FileSystemType type, + base::FilePath* usage_file_path) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(origin.is_valid()); + DCHECK(usage_file_path); + base::PlatformFileError error = base::PLATFORM_FILE_OK; + *usage_file_path = + SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType( + obfuscated_file_util_, origin, type, &error); + return error; +} + +QuotaBackendImpl::QuotaReservationInfo::QuotaReservationInfo( + const GURL& origin, FileSystemType type, int64 delta) + : origin(origin), type(type), delta(delta) { +} + +QuotaBackendImpl::QuotaReservationInfo::~QuotaReservationInfo() { +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/quota_backend_impl.h b/chromium/webkit/browser/fileapi/quota/quota_backend_impl.h new file mode 100644 index 00000000000..10c38e7ea7c --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_backend_impl.h @@ -0,0 +1,103 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_BACKEND_IMPL_H_ +#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_BACKEND_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "webkit/browser/fileapi/quota/quota_reservation_manager.h" +#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h" +#include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/quota/quota_status_code.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace quota { +class QuotaManagerProxy; +} + +namespace fileapi { + +class FileSystemUsageCache; +class ObfuscatedFileUtil; + +// An instance of this class is owned by QuotaReservationManager. +class WEBKIT_STORAGE_BROWSER_EXPORT QuotaBackendImpl + : public QuotaReservationManager::QuotaBackend { + public: + typedef QuotaReservationManager::ReserveQuotaCallback + ReserveQuotaCallback; + + QuotaBackendImpl(base::SequencedTaskRunner* file_task_runner, + ObfuscatedFileUtil* obfuscated_file_util, + FileSystemUsageCache* file_system_usage_cache, + quota::QuotaManagerProxy* quota_manager_proxy); + virtual ~QuotaBackendImpl(); + + // QuotaReservationManager::QuotaBackend overrides. + virtual void ReserveQuota( + const GURL& origin, + FileSystemType type, + int64 delta, + const ReserveQuotaCallback& callback) OVERRIDE; + virtual void ReleaseReservedQuota( + const GURL& origin, + FileSystemType type, + int64 size) OVERRIDE; + virtual void CommitQuotaUsage( + const GURL& origin, + FileSystemType type, + int64 delta) OVERRIDE; + virtual void IncrementDirtyCount( + const GURL& origin, + FileSystemType type) OVERRIDE; + virtual void DecrementDirtyCount( + const GURL& origin, + FileSystemType type) OVERRIDE; + + private: + friend class QuotaBackendImplTest; + + struct QuotaReservationInfo { + QuotaReservationInfo(const GURL& origin, FileSystemType type, int64 delta); + ~QuotaReservationInfo(); + + GURL origin; + FileSystemType type; + int64 delta; + }; + + void DidGetUsageAndQuotaForReserveQuota( + const QuotaReservationInfo& info, + const ReserveQuotaCallback& callback, + quota::QuotaStatusCode status, + int64 usage, + int64 quota); + + void ReserveQuotaInternal( + const QuotaReservationInfo& info); + base::PlatformFileError GetUsageCachePath( + const GURL& origin, + FileSystemType type, + base::FilePath* usage_file_path); + + scoped_refptr<base::SequencedTaskRunner> file_task_runner_; + + // Owned by SandboxFileSystemBackendDelegate. + ObfuscatedFileUtil* obfuscated_file_util_; + FileSystemUsageCache* file_system_usage_cache_; + + scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; + + base::WeakPtrFactory<QuotaBackendImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuotaBackendImpl); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_BACKEND_IMPL_H_ diff --git a/chromium/webkit/browser/fileapi/quota/quota_backend_impl_unittest.cc b/chromium/webkit/browser/fileapi/quota/quota_backend_impl_unittest.cc new file mode 100644 index 00000000000..75e8db8fe8c --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_backend_impl_unittest.cc @@ -0,0 +1,248 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_backend_impl.h" + +#include <string> + +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/file_system_usage_cache.h" +#include "webkit/browser/fileapi/obfuscated_file_util.h" +#include "webkit/browser/quota/quota_manager.h" + +namespace fileapi { + +namespace { + +const char kOrigin[] = "http://example.com"; + +bool DidReserveQuota(bool accepted, + base::PlatformFileError* error_out, + base::PlatformFileError error) { + DCHECK(error_out); + *error_out = error; + return accepted; +} + +class MockQuotaManagerProxy : public quota::QuotaManagerProxy { + public: + MockQuotaManagerProxy() + : QuotaManagerProxy(NULL, NULL), + storage_modified_count_(0), + usage_(0), quota_(0) {} + + // We don't mock them. + virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {} + virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {} + virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id, + const GURL& origin, + quota::StorageType type, + bool enabled) OVERRIDE {} + + virtual void NotifyStorageModified( + quota::QuotaClient::ID client_id, + const GURL& origin, + quota::StorageType type, + int64 delta) OVERRIDE { + ++storage_modified_count_; + usage_ += delta; + ASSERT_LT(usage_, quota_); + } + + virtual void GetUsageAndQuota( + base::SequencedTaskRunner* original_task_runner, + const GURL& origin, + quota::StorageType type, + const GetUsageAndQuotaCallback& callback) OVERRIDE { + callback.Run(quota::kQuotaStatusOk, usage_, quota_); + } + + int storage_modified_count() { return storage_modified_count_; } + int64 usage() { return usage_; } + void set_usage(int64 usage) { usage_ = usage; } + void set_quota(int64 quota) { quota_ = quota; } + + protected: + virtual ~MockQuotaManagerProxy() {} + + private: + int storage_modified_count_; + int64 usage_; + int64 quota_; + + DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerProxy); +}; + +} // namespace + +class QuotaBackendImplTest : public testing::Test { + public: + QuotaBackendImplTest() + : file_system_usage_cache_(file_task_runner()), + quota_manager_proxy_(new MockQuotaManagerProxy) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + file_util_.reset(ObfuscatedFileUtil::CreateForTesting( + NULL, data_dir_.path(), file_task_runner())); + backend_.reset(new QuotaBackendImpl(file_task_runner(), + file_util_.get(), + &file_system_usage_cache_, + quota_manager_proxy_.get())); + } + + virtual void TearDown() OVERRIDE { + backend_.reset(); + quota_manager_proxy_ = NULL; + file_util_.reset(); + message_loop_.RunUntilIdle(); + } + + protected: + void InitializeForOriginAndType(const GURL& origin, FileSystemType type) { + ASSERT_TRUE(file_util_->InitOriginDatabase(origin, true /* create */)); + ASSERT_TRUE(file_util_->origin_database_ != NULL); + + std::string type_string = + SandboxFileSystemBackendDelegate::GetTypeString(type); + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + base::FilePath path = file_util_->GetDirectoryForOriginAndType( + origin, type_string, true /* create */, &error); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + + ASSERT_TRUE(file_system_usage_cache_.UpdateUsage( + GetUsageCachePath(origin, type), 0)); + } + + base::SequencedTaskRunner* file_task_runner() { + return base::MessageLoopProxy::current().get(); + } + + base::FilePath GetUsageCachePath(const GURL& origin, FileSystemType type) { + base::FilePath path; + base::PlatformFileError error = + backend_->GetUsageCachePath(origin, type, &path); + EXPECT_EQ(base::PLATFORM_FILE_OK, error); + EXPECT_FALSE(path.empty()); + return path; + } + + base::MessageLoop message_loop_; + base::ScopedTempDir data_dir_; + scoped_ptr<ObfuscatedFileUtil> file_util_; + FileSystemUsageCache file_system_usage_cache_; + scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_; + scoped_ptr<QuotaBackendImpl> backend_; + + private: + DISALLOW_COPY_AND_ASSIGN(QuotaBackendImplTest); +}; + +TEST_F(QuotaBackendImplTest, ReserveQuota_Basic) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + quota_manager_proxy_->set_quota(10000); + + const int64 kDelta1 = 1000; + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + backend_->ReserveQuota(GURL(kOrigin), type, kDelta1, + base::Bind(&DidReserveQuota, true, &error)); + EXPECT_EQ(base::PLATFORM_FILE_OK, error); + EXPECT_EQ(kDelta1, quota_manager_proxy_->usage()); + + const int64 kDelta2 = -300; + error = base::PLATFORM_FILE_ERROR_FAILED; + backend_->ReserveQuota(GURL(kOrigin), type, kDelta2, + base::Bind(&DidReserveQuota, true, &error)); + EXPECT_EQ(base::PLATFORM_FILE_OK, error); + EXPECT_EQ(kDelta1 + kDelta2, quota_manager_proxy_->usage()); + + EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count()); +} + +TEST_F(QuotaBackendImplTest, ReserveQuota_NoSpace) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + quota_manager_proxy_->set_quota(100); + + const int64 kDelta = 1000; + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + backend_->ReserveQuota(GURL(kOrigin), type, kDelta, + base::Bind(&DidReserveQuota, true, &error)); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, error); + EXPECT_EQ(0, quota_manager_proxy_->usage()); + + EXPECT_EQ(0, quota_manager_proxy_->storage_modified_count()); +} + +TEST_F(QuotaBackendImplTest, ReserveQuota_Revert) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + quota_manager_proxy_->set_quota(10000); + + const int64 kDelta = 1000; + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + backend_->ReserveQuota(GURL(kOrigin), type, kDelta, + base::Bind(&DidReserveQuota, false, &error)); + EXPECT_EQ(base::PLATFORM_FILE_OK, error); + EXPECT_EQ(0, quota_manager_proxy_->usage()); + + EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count()); +} + +TEST_F(QuotaBackendImplTest, ReleaseReservedQuota) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + const int64 kInitialUsage = 2000; + quota_manager_proxy_->set_usage(kInitialUsage); + quota_manager_proxy_->set_quota(10000); + + const int64 kSize = 1000; + backend_->ReleaseReservedQuota(GURL(kOrigin), type, kSize); + EXPECT_EQ(kInitialUsage - kSize, quota_manager_proxy_->usage()); + + EXPECT_EQ(1, quota_manager_proxy_->storage_modified_count()); +} + +TEST_F(QuotaBackendImplTest, CommitQuotaUsage) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + quota_manager_proxy_->set_quota(10000); + base::FilePath path = GetUsageCachePath(GURL(kOrigin), type); + + const int64 kDelta1 = 1000; + backend_->CommitQuotaUsage(GURL(kOrigin), type, kDelta1); + EXPECT_EQ(kDelta1, quota_manager_proxy_->usage()); + int64 usage = 0; + EXPECT_TRUE(file_system_usage_cache_.GetUsage(path, &usage)); + EXPECT_EQ(kDelta1, usage); + + const int64 kDelta2 = -300; + backend_->CommitQuotaUsage(GURL(kOrigin), type, kDelta2); + EXPECT_EQ(kDelta1 + kDelta2, quota_manager_proxy_->usage()); + usage = 0; + EXPECT_TRUE(file_system_usage_cache_.GetUsage(path, &usage)); + EXPECT_EQ(kDelta1 + kDelta2, usage); + + EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count()); +} + +TEST_F(QuotaBackendImplTest, DirtyCount) { + FileSystemType type = fileapi::kFileSystemTypeTemporary; + InitializeForOriginAndType(GURL(kOrigin), type); + base::FilePath path = GetUsageCachePath(GURL(kOrigin), type); + + backend_->IncrementDirtyCount(GURL(kOrigin), type); + uint32 dirty = 0; + ASSERT_TRUE(file_system_usage_cache_.GetDirty(path, &dirty)); + EXPECT_EQ(1u, dirty); + + backend_->DecrementDirtyCount(GURL(kOrigin), type); + ASSERT_TRUE(file_system_usage_cache_.GetDirty(path, &dirty)); + EXPECT_EQ(0u, dirty); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation.cc b/chromium/webkit/browser/fileapi/quota/quota_reservation.cc new file mode 100644 index 00000000000..c5a22ab2614 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation.cc @@ -0,0 +1,113 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_reservation.h" + +#include "base/bind.h" +#include "webkit/browser/fileapi/quota/open_file_handle.h" +#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h" + +namespace fileapi { + +void QuotaReservation::RefreshReservation( + int64 size, + const StatusCallback& callback) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(!running_refresh_request_); + if (!reservation_manager()) + return; + + running_refresh_request_ = true; + + reservation_manager()->ReserveQuota( + origin(), type(), size - remaining_quota_, + base::Bind(&QuotaReservation::AdaptDidUpdateReservedQuota, + weak_ptr_factory_.GetWeakPtr(), + size, callback)); + + if (running_refresh_request_) + remaining_quota_ = 0; +} + +scoped_ptr<OpenFileHandle> QuotaReservation::GetOpenFileHandle( + const base::FilePath& platform_path) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + return reservation_buffer_->GetOpenFileHandle(this, platform_path); +} + +void QuotaReservation::OnClientCrash() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + + if (remaining_quota_) { + reservation_buffer_->PutReservationToBuffer(remaining_quota_); + remaining_quota_ = 0; + } +} + +void QuotaReservation::ConsumeReservation(int64 size) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK_LT(0, size); + DCHECK_LT(size, remaining_quota_); + + remaining_quota_ -= size; + reservation_buffer_->PutReservationToBuffer(size); +} + +QuotaReservationManager* QuotaReservation::reservation_manager() { + return reservation_buffer_->reservation_manager(); +} + +const GURL& QuotaReservation::origin() const { + return reservation_buffer_->origin(); +} + +FileSystemType QuotaReservation::type() const { + return reservation_buffer_->type(); +} + +QuotaReservation::QuotaReservation( + QuotaReservationBuffer* reservation_buffer) + : running_refresh_request_(false), + remaining_quota_(0), + reservation_buffer_(reservation_buffer), + weak_ptr_factory_(this) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); +} + +QuotaReservation::~QuotaReservation() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + + if (remaining_quota_ && reservation_manager()) { + reservation_manager()->ReleaseReservedQuota( + origin(), type(), remaining_quota_); + } +} + +// static +bool QuotaReservation::AdaptDidUpdateReservedQuota( + const base::WeakPtr<QuotaReservation>& reservation, + int64 new_reserved_size, + const StatusCallback& callback, + base::PlatformFileError error) { + if (!reservation) + return false; + + reservation->DidUpdateReservedQuota(new_reserved_size, callback, error); + return true; +} + +void QuotaReservation::DidUpdateReservedQuota( + int64 new_reserved_size, + const StatusCallback& callback, + base::PlatformFileError error) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(running_refresh_request_); + running_refresh_request_ = false; + + if (error == base::PLATFORM_FILE_OK) + remaining_quota_ = new_reserved_size; + callback.Run(error); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation.h b/chromium/webkit/browser/fileapi/quota/quota_reservation.h new file mode 100644 index 00000000000..d534a0dcc2c --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation.h @@ -0,0 +1,89 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_ +#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_ + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "webkit/browser/fileapi/quota/quota_reservation_manager.h" +#include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_types.h" + +class GURL; + +namespace fileapi { + +class QuotaReservationBuffer; +class OpenFileHandle; + +// Represents a unit of quota reservation. +class WEBKIT_STORAGE_BROWSER_EXPORT QuotaReservation + : public base::RefCounted<QuotaReservation> { + public: + typedef base::Callback<void(base::PlatformFileError error)> StatusCallback; + + // Reclaims unused quota and reserves another |size| of quota. So that the + // resulting new |remaining_quota| will be same as |size|. + // Invokes |callback| upon completion. + void RefreshReservation(int64 size, const StatusCallback& callback); + + // Associates |platform_path| to the QuotaReservation instance. + // Returns an OpenFileHandle instance that represents a quota managed file. + scoped_ptr<OpenFileHandle> GetOpenFileHandle( + const base::FilePath& platform_path); + + // Should be called when the associated client is crashed. + // This implies the client can no longer report its consumption of the + // reserved quota. + // QuotaReservation puts all remaining quota to the QuotaReservationBuffer, so + // that the remaining quota will be reclaimed after all open files associated + // to the origin and type. + void OnClientCrash(); + + // Consumes |size| of reserved quota for a associated file. + // Consumed quota is sent to associated QuotaReservationBuffer for staging. + void ConsumeReservation(int64 size); + + // Returns amount of unused reserved quota. + int64 remaining_quota() const { return remaining_quota_; } + + QuotaReservationManager* reservation_manager(); + const GURL& origin() const; + FileSystemType type() const; + + private: + friend class QuotaReservationBuffer; + + // Use QuotaReservationManager as the entry point. + explicit QuotaReservation(QuotaReservationBuffer* reservation_buffer); + + friend class base::RefCounted<QuotaReservation>; + virtual ~QuotaReservation(); + + static bool AdaptDidUpdateReservedQuota( + const base::WeakPtr<QuotaReservation>& reservation, + int64 new_reserved_size, + const StatusCallback& callback, + base::PlatformFileError error); + void DidUpdateReservedQuota(int64 new_reserved_size, + const StatusCallback& callback, + base::PlatformFileError error); + + bool running_refresh_request_; + int64 remaining_quota_; + + scoped_refptr<QuotaReservationBuffer> reservation_buffer_; + + base::SequenceChecker sequence_checker_; + base::WeakPtrFactory<QuotaReservation> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuotaReservation); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_ diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.cc b/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.cc new file mode 100644 index 00000000000..14ea0d0cd81 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.cc @@ -0,0 +1,105 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h" + +#include "base/bind.h" +#include "webkit/browser/fileapi/quota/open_file_handle.h" +#include "webkit/browser/fileapi/quota/open_file_handle_context.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" + +namespace fileapi { + +QuotaReservationBuffer::QuotaReservationBuffer( + base::WeakPtr<QuotaReservationManager> reservation_manager, + const GURL& origin, + FileSystemType type) + : reservation_manager_(reservation_manager), + origin_(origin), + type_(type), + reserved_quota_(0) { + DCHECK(origin.is_valid()); + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + reservation_manager_->IncrementDirtyCount(origin, type); +} + +scoped_refptr<QuotaReservation> QuotaReservationBuffer::CreateReservation() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + return make_scoped_refptr(new QuotaReservation(this)); +} + +scoped_ptr<OpenFileHandle> QuotaReservationBuffer::GetOpenFileHandle( + QuotaReservation* reservation, + const base::FilePath& platform_path) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + OpenFileHandleContext** open_file = &open_files_[platform_path]; + if (!*open_file) + *open_file = new OpenFileHandleContext(platform_path, this); + return make_scoped_ptr(new OpenFileHandle(reservation, *open_file)); +} + +void QuotaReservationBuffer::CommitFileGrowth(int64 quota_consumption, + int64 usage_delta) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + if (!reservation_manager_) + return; + reservation_manager_->CommitQuotaUsage(origin_, type_, usage_delta); + + if (quota_consumption > 0) { + if (quota_consumption > reserved_quota_) { + LOG(ERROR) << "Detected over consumption of the storage quota beyond its" + << " reservation"; + quota_consumption = reserved_quota_; + } + + reserved_quota_ -= quota_consumption; + reservation_manager_->ReleaseReservedQuota( + origin_, type_, quota_consumption); + } +} + +void QuotaReservationBuffer::DetachOpenFileHandleContext( + OpenFileHandleContext* open_file) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK_EQ(open_file, open_files_[open_file->platform_path()]); + open_files_.erase(open_file->platform_path()); +} + +void QuotaReservationBuffer::PutReservationToBuffer(int64 reservation) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK_LE(0, reservation); + reserved_quota_ += reservation; +} + +QuotaReservationBuffer::~QuotaReservationBuffer() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + if (!reservation_manager_) + return; + + DCHECK_LE(0, reserved_quota_); + if (reserved_quota_ && reservation_manager_) { + reservation_manager_->ReserveQuota( + origin_, type_, -reserved_quota_, + base::Bind(&QuotaReservationBuffer::DecrementDirtyCount, + reservation_manager_, origin_, type_)); + } + reservation_manager_->ReleaseReservationBuffer(this); +} + +// static +bool QuotaReservationBuffer::DecrementDirtyCount( + base::WeakPtr<QuotaReservationManager> reservation_manager, + const GURL& origin, + FileSystemType type, + base::PlatformFileError error) { + DCHECK(origin.is_valid()); + if (error == base::PLATFORM_FILE_OK && reservation_manager) { + reservation_manager->DecrementDirtyCount(origin, type); + return true; + } + return false; +} + + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.h b/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.h new file mode 100644 index 00000000000..9a11fa5f842 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation_buffer.h @@ -0,0 +1,86 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_BUFFER_H_ +#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_BUFFER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "url/gurl.h" +#include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_types.h" + +namespace fileapi { + +class QuotaReservation; +class OpenFileHandle; +class OpenFileHandleContext; +class QuotaReservationManager; + +// QuotaReservationBuffer manages QuotaReservation instances. All consumed +// quota and leaked quota by associated QuotaReservation will be staged in +// QuotaReservationBuffer, and will be committed on a modified file is closed. +// The instance keeps alive while any associated QuotaReservation or +// OpenFileHandle alive. +// This class is usually manipulated only via OpenFileHandle and +// QuotaReservation. +class QuotaReservationBuffer : public base::RefCounted<QuotaReservationBuffer> { + public: + QuotaReservationBuffer( + base::WeakPtr<QuotaReservationManager> reservation_manager, + const GURL& origin, + FileSystemType type); + + scoped_refptr<QuotaReservation> CreateReservation(); + scoped_ptr<OpenFileHandle> GetOpenFileHandle( + QuotaReservation* reservation, + const base::FilePath& platform_path); + void CommitFileGrowth(int64 quota_consumption, int64 usage_delta); + void DetachOpenFileHandleContext(OpenFileHandleContext* context); + void PutReservationToBuffer(int64 size); + + QuotaReservationManager* reservation_manager() { + return reservation_manager_.get(); + } + + const GURL& origin() const { return origin_; } + FileSystemType type() const { return type_; } + + private: + friend class base::RefCounted<QuotaReservationBuffer>; + virtual ~QuotaReservationBuffer(); + + static bool DecrementDirtyCount( + base::WeakPtr<QuotaReservationManager> reservation_manager, + const GURL& origin, + FileSystemType type, + base::PlatformFileError error); + + typedef std::map<base::FilePath, OpenFileHandleContext*> + OpenFileHandleContextByPath; + + // Not owned. The destructor of OpenFileHandler should erase itself from + // |open_files_|. + OpenFileHandleContextByPath open_files_; + + base::WeakPtr<QuotaReservationManager> reservation_manager_; + + GURL origin_; + fileapi::FileSystemType type_; + + int64 reserved_quota_; + + base::SequenceChecker sequence_checker_; + + DISALLOW_COPY_AND_ASSIGN(QuotaReservationBuffer); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_BUFFER_H_ diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.cc b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.cc new file mode 100644 index 00000000000..efb54b2225b --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.cc @@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_reservation_manager.h" + +#include "webkit/browser/fileapi/quota/quota_reservation.h" +#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h" + +namespace fileapi { + +QuotaReservationManager::QuotaReservationManager( + scoped_ptr<QuotaBackend> backend) + : backend_(backend.Pass()), + weak_ptr_factory_(this) { + sequence_checker_.DetachFromSequence(); +} + +QuotaReservationManager::~QuotaReservationManager() { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); +} + +void QuotaReservationManager::ReserveQuota( + const GURL& origin, + FileSystemType type, + int64 size, + const ReserveQuotaCallback& callback) { + DCHECK(origin.is_valid()); + backend_->ReserveQuota(origin, type, size, callback); +} + +void QuotaReservationManager::ReleaseReservedQuota( + const GURL& origin, + FileSystemType type, + int64 size) { + DCHECK(origin.is_valid()); + backend_->ReleaseReservedQuota(origin, type, size); +} + +void QuotaReservationManager::CommitQuotaUsage( + const GURL& origin, + FileSystemType type, + int64 delta) { + DCHECK(origin.is_valid()); + backend_->CommitQuotaUsage(origin, type, delta); +} + +void QuotaReservationManager::IncrementDirtyCount(const GURL& origin, + FileSystemType type) { + DCHECK(origin.is_valid()); + backend_->IncrementDirtyCount(origin, type); +} + +void QuotaReservationManager::DecrementDirtyCount(const GURL& origin, + FileSystemType type) { + DCHECK(origin.is_valid()); + backend_->DecrementDirtyCount(origin, type); +} + +scoped_refptr<QuotaReservationBuffer> +QuotaReservationManager::GetReservationBuffer( + const GURL& origin, + FileSystemType type) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + DCHECK(origin.is_valid()); + QuotaReservationBuffer** buffer = + &reservation_buffers_[std::make_pair(origin, type)]; + if (!*buffer) { + *buffer = new QuotaReservationBuffer( + weak_ptr_factory_.GetWeakPtr(), origin, type); + } + return make_scoped_refptr(*buffer); +} + +void QuotaReservationManager::ReleaseReservationBuffer( + QuotaReservationBuffer* reservation_buffer) { + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); + std::pair<GURL, FileSystemType> key(reservation_buffer->origin(), + reservation_buffer->type()); + DCHECK_EQ(reservation_buffers_[key], reservation_buffer); + reservation_buffers_.erase(key); +} + +scoped_refptr<QuotaReservation> QuotaReservationManager::CreateReservation( + const GURL& origin, + FileSystemType type) { + DCHECK(origin.is_valid()); + return GetReservationBuffer(origin, type)->CreateReservation();; +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.h b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.h new file mode 100644 index 00000000000..07beacc3277 --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager.h @@ -0,0 +1,123 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_ +#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_ + +#include <map> +#include <utility> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "url/gurl.h" +#include "webkit/browser/webkit_storage_browser_export.h" +#include "webkit/common/fileapi/file_system_types.h" + +namespace fileapi { + +class QuotaReservation; +class QuotaReservationBuffer; +class OpenFileHandle; +class OpenFileHandleContext; + +class WEBKIT_STORAGE_BROWSER_EXPORT QuotaReservationManager { + public: + // Callback for ReserveQuota. When this callback returns false, ReserveQuota + // operation should be reverted. + typedef base::Callback<bool(base::PlatformFileError error)> + ReserveQuotaCallback; + + // An abstraction of backing quota system. + class WEBKIT_STORAGE_BROWSER_EXPORT QuotaBackend { + public: + QuotaBackend() {} + virtual ~QuotaBackend() {} + + // Reserves or reclaims |delta| of quota for |origin| and |type| pair. + // Reserved quota should be counted as usage, but it should be on-memory + // and be cleared by a browser restart. + // Invokes |callback| upon completion with an error code. + // |callback| should return false if it can't accept the reservation, in + // that case, the backend should roll back the reservation. + virtual void ReserveQuota(const GURL& origin, + FileSystemType type, + int64 delta, + const ReserveQuotaCallback& callback) = 0; + + // Reclaims |size| of quota for |origin| and |type|. + virtual void ReleaseReservedQuota(const GURL& origin, + FileSystemType type, + int64 size) = 0; + + // Updates disk usage of |origin| and |type|. + // Invokes |callback| upon completion with an error code. + virtual void CommitQuotaUsage(const GURL& origin, + FileSystemType type, + int64 delta) = 0; + + virtual void IncrementDirtyCount(const GURL& origin, + FileSystemType type) = 0; + virtual void DecrementDirtyCount(const GURL& origin, + FileSystemType type) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(QuotaBackend); + }; + + explicit QuotaReservationManager(scoped_ptr<QuotaBackend> backend); + ~QuotaReservationManager(); + + // The entry point of the quota reservation. Creates new reservation object + // for |origin| and |type|. + scoped_refptr<QuotaReservation> CreateReservation( + const GURL& origin, + FileSystemType type); + + private: + typedef std::map<std::pair<GURL, FileSystemType>, QuotaReservationBuffer*> + ReservationBufferByOriginAndType; + + friend class QuotaReservation; + friend class QuotaReservationBuffer; + friend class QuotaReservationManagerTest; + + void ReserveQuota(const GURL& origin, + FileSystemType type, + int64 delta, + const ReserveQuotaCallback& callback); + + void ReleaseReservedQuota(const GURL& origin, + FileSystemType type, + int64 size); + + void CommitQuotaUsage(const GURL& origin, + FileSystemType type, + int64 delta); + + void IncrementDirtyCount(const GURL& origin, FileSystemType type); + void DecrementDirtyCount(const GURL& origin, FileSystemType type); + + scoped_refptr<QuotaReservationBuffer> GetReservationBuffer( + const GURL& origin, + FileSystemType type); + void ReleaseReservationBuffer(QuotaReservationBuffer* reservation_pool); + + scoped_ptr<QuotaBackend> backend_; + + // Not owned. The destructor of ReservationBuffer should erase itself from + // |reservation_buffers_| by calling ReleaseReservationBuffer. + ReservationBufferByOriginAndType reservation_buffers_; + + base::SequenceChecker sequence_checker_; + base::WeakPtrFactory<QuotaReservationManager> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuotaReservationManager); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_ diff --git a/chromium/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc new file mode 100644 index 00000000000..6cdc062d16a --- /dev/null +++ b/chromium/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc @@ -0,0 +1,358 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/quota/quota_reservation_manager.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/quota/open_file_handle.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" + +namespace fileapi { + +namespace { + +const char kOrigin[] = "http://example.com"; +const FileSystemType kType = kFileSystemTypeTemporary; +const int64 kInitialFileSize = 30; + +typedef QuotaReservationManager::ReserveQuotaCallback ReserveQuotaCallback; + +class FakeBackend : public QuotaReservationManager::QuotaBackend { + public: + FakeBackend() + : on_memory_usage_(0), + on_disk_usage_(0) {} + virtual ~FakeBackend() {} + + virtual void ReserveQuota(const GURL& origin, + FileSystemType type, + int64 delta, + const ReserveQuotaCallback& callback) OVERRIDE { + EXPECT_EQ(GURL(kOrigin), origin); + EXPECT_EQ(kType, type); + on_memory_usage_ += delta; + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(base::IgnoreResult(callback), base::PLATFORM_FILE_OK)); + } + + virtual void ReleaseReservedQuota(const GURL& origin, + FileSystemType type, + int64 size) OVERRIDE { + EXPECT_LE(0, size); + EXPECT_EQ(GURL(kOrigin), origin); + EXPECT_EQ(kType, type); + on_memory_usage_ -= size; + } + + virtual void CommitQuotaUsage(const GURL& origin, + FileSystemType type, + int64 delta) OVERRIDE { + EXPECT_EQ(GURL(kOrigin), origin); + EXPECT_EQ(kType, type); + on_disk_usage_ += delta; + } + + virtual void IncrementDirtyCount(const GURL& origin, + FileSystemType type) OVERRIDE {} + virtual void DecrementDirtyCount(const GURL& origin, + FileSystemType type) OVERRIDE {} + + int64 on_memory_usage() { return on_memory_usage_; } + int64 on_disk_usage() { return on_disk_usage_; } + + private: + int64 on_memory_usage_; + int64 on_disk_usage_; + + DISALLOW_COPY_AND_ASSIGN(FakeBackend); +}; + +void ExpectSuccess(bool* done, base::PlatformFileError error) { + EXPECT_FALSE(*done); + *done = true; + EXPECT_EQ(base::PLATFORM_FILE_OK, error); +} + +void RefreshQuota(QuotaReservation* reservation, int64 size) { + DCHECK(reservation); + + bool done = false; + reservation->RefreshReservation(size, base::Bind(&ExpectSuccess, &done)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(done); +} + +} // namespace + +class QuotaReservationManagerTest : public testing::Test { + public: + QuotaReservationManagerTest() {} + virtual ~QuotaReservationManagerTest() {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(work_dir_.CreateUniqueTempDir()); + file_path_ = work_dir_.path().Append(FILE_PATH_LITERAL("hoge")); + SetFileSize(kInitialFileSize); + + scoped_ptr<QuotaReservationManager::QuotaBackend> backend(new FakeBackend); + reservation_manager_.reset(new QuotaReservationManager(backend.Pass())); + } + + virtual void TearDown() OVERRIDE { + reservation_manager_.reset(); + } + + int64 GetFileSize() { + int64 size = 0; + base::GetFileSize(file_path_, &size); + return size; + } + + void SetFileSize(int64 size) { + bool created = false; + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + base::PlatformFile file = CreatePlatformFile( + file_path_, + base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE, + &created, &error); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + ASSERT_TRUE(base::TruncatePlatformFile(file, size)); + ASSERT_TRUE(base::ClosePlatformFile(file)); + } + + void ExtendFileTo(int64 size) { + if (GetFileSize() < size) + SetFileSize(size); + } + + FakeBackend* fake_backend() { + return static_cast<FakeBackend*>(reservation_manager_->backend_.get()); + } + + QuotaReservationManager* reservation_manager() { + return reservation_manager_.get(); + } + + const base::FilePath& file_path() const { + return file_path_; + } + + private: + base::MessageLoop message_loop_; + base::ScopedTempDir work_dir_; + base::FilePath file_path_; + scoped_ptr<QuotaReservationManager> reservation_manager_; + + DISALLOW_COPY_AND_ASSIGN(QuotaReservationManagerTest); +}; + +TEST_F(QuotaReservationManagerTest, BasicTest) { + GURL origin(kOrigin); + FileSystemType type = kType; + + // Create Reservation channel for the origin and type. + // Reservation holds remaining quota reservation and provides a method to + // refresh it. + scoped_refptr<QuotaReservation> reservation = + reservation_manager()->CreateReservation(origin, type); + EXPECT_EQ(0, reservation->remaining_quota()); + + RefreshQuota(reservation, 100); + EXPECT_EQ(100, reservation->remaining_quota()); + + { + // For each open file for write, the client should create OpenFileHandle + // object. + // It's OK to create multiple OpenFileHandle for single file. + scoped_ptr<OpenFileHandle> open_file = + reservation->GetOpenFileHandle(file_path()); + + // Before reserved quota ran out, the client can perform any number of + // operation for the file. + // The client should calculate how much quota is consumed by itself. + int64 remaining_quota = reservation->remaining_quota(); + int64 base_file_size = open_file->base_file_size(); + int64 max_written_offset = base_file_size; + ExtendFileTo(90); + max_written_offset = 90; + remaining_quota -= max_written_offset - base_file_size; + + // When the reserved quota ran out, the client can request quota refresh + // through Reservation. Before requesting another portion of quota, the + // client should report maximum written offset for each modified files. + open_file->UpdateMaxWrittenOffset(max_written_offset); + EXPECT_EQ(remaining_quota, reservation->remaining_quota()); + + RefreshQuota(reservation, 100); + EXPECT_EQ(100, reservation->remaining_quota()); + } + + EXPECT_EQ(90, GetFileSize()); + EXPECT_EQ(100, fake_backend()->on_memory_usage()); + EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage()); + + reservation = NULL; + + EXPECT_EQ(90, GetFileSize()); + EXPECT_EQ(0, fake_backend()->on_memory_usage()); + EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage()); +} + +TEST_F(QuotaReservationManagerTest, MultipleWriter) { + GURL origin(kOrigin); + FileSystemType type = kType; + + scoped_refptr<QuotaReservation> reservation = + reservation_manager()->CreateReservation(origin, type); + EXPECT_EQ(0, reservation->remaining_quota()); + + RefreshQuota(reservation, 100); + EXPECT_EQ(100, reservation->remaining_quota()); + + { + scoped_ptr<OpenFileHandle> open_file1 = + reservation->GetOpenFileHandle(file_path()); + scoped_ptr<OpenFileHandle> open_file2 = + reservation->GetOpenFileHandle(file_path()); + + int64 remaining_quota = reservation->remaining_quota(); + + int64 base_file_size_for_file1 = open_file1->base_file_size(); + int64 max_written_offset_for_file1 = base_file_size_for_file1; + + int64 base_file_size_for_file2 = open_file2->base_file_size(); + int64 max_written_offset_for_file2 = base_file_size_for_file2; + + // Each writer should maintain max_written_offset and base_file_size + // independently even if there are multiple writers for the same file. + max_written_offset_for_file1 = 50; + ExtendFileTo(max_written_offset_for_file1); + remaining_quota -= max_written_offset_for_file1 - base_file_size_for_file1; + base_file_size_for_file1 = max_written_offset_for_file1; + + max_written_offset_for_file2 = 90; + ExtendFileTo(max_written_offset_for_file2); + remaining_quota -= max_written_offset_for_file2 - base_file_size_for_file2; + base_file_size_for_file2 = max_written_offset_for_file2; + + // Before requesting quota refresh, each writer should report their + // maximum_written_offset. UpdateMaxWrittenOffset returns updated + // base_file_size that the writer should calculate quota consumption based + // on that. + base_file_size_for_file1 = + open_file1->UpdateMaxWrittenOffset(max_written_offset_for_file1); + max_written_offset_for_file1 = base_file_size_for_file1; + EXPECT_EQ(100 - (50 - kInitialFileSize), reservation->remaining_quota()); + + base_file_size_for_file2 = + open_file2->UpdateMaxWrittenOffset(max_written_offset_for_file2); + max_written_offset_for_file2 = base_file_size_for_file2; + EXPECT_EQ(100 - (50 - kInitialFileSize) - (90 - 50), + reservation->remaining_quota()); + + RefreshQuota(reservation, 100); + EXPECT_EQ(100, reservation->remaining_quota()); + } + + EXPECT_EQ(90, GetFileSize()); + EXPECT_EQ(100, fake_backend()->on_memory_usage()); + EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage()); + + reservation = NULL; + + EXPECT_EQ(90, GetFileSize()); + EXPECT_EQ(0, fake_backend()->on_memory_usage()); + EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage()); +} + +TEST_F(QuotaReservationManagerTest, MultipleClient) { + GURL origin(kOrigin); + FileSystemType type = kType; + + scoped_refptr<QuotaReservation> reservation1 = + reservation_manager()->CreateReservation(origin, type); + EXPECT_EQ(0, reservation1->remaining_quota()); + RefreshQuota(reservation1, 100); + EXPECT_EQ(100, reservation1->remaining_quota()); + + scoped_refptr<QuotaReservation> reservation2 = + reservation_manager()->CreateReservation(origin, type); + EXPECT_EQ(0, reservation2->remaining_quota()); + RefreshQuota(reservation2, 500); + EXPECT_EQ(500, reservation2->remaining_quota()); + + // Attach a file to both of two reservations. + scoped_ptr<OpenFileHandle> open_file1 = + reservation1->GetOpenFileHandle(file_path()); + scoped_ptr<OpenFileHandle> open_file2 = + reservation2->GetOpenFileHandle(file_path()); + + // Each client should manage reserved quota and its consumption separately. + int64 remaining_quota1 = reservation1->remaining_quota(); + int64 base_file_size1 = open_file1->base_file_size(); + int64 max_written_offset1 = base_file_size1; + + int64 remaining_quota2 = reservation2->remaining_quota(); + int64 base_file_size2 = open_file2->base_file_size(); + int64 max_written_offset2 = base_file_size2; + + max_written_offset1 = 50; + remaining_quota1 -= max_written_offset1 - base_file_size1; + base_file_size1 = max_written_offset1; + ExtendFileTo(max_written_offset1); + + max_written_offset2 = 400; + remaining_quota2 -= max_written_offset2 - base_file_size2; + base_file_size2 = max_written_offset2; + ExtendFileTo(max_written_offset2); + + // For multiple Reservation case, RefreshQuota needs usage report only from + // associated OpenFile's. + base_file_size1 = + open_file1->UpdateMaxWrittenOffset(max_written_offset1); + max_written_offset1 = base_file_size1; + EXPECT_EQ(100 - (50 - kInitialFileSize), reservation1->remaining_quota()); + + RefreshQuota(reservation1, 200); + EXPECT_EQ(200, reservation1->remaining_quota()); + + base_file_size2 = + open_file2->UpdateMaxWrittenOffset(max_written_offset2); + max_written_offset2 = base_file_size2; + EXPECT_EQ(500 - (400 - 50), reservation2->remaining_quota()); + + RefreshQuota(reservation2, 150); + EXPECT_EQ(150, reservation2->remaining_quota()); + + open_file1.reset(); + open_file2.reset(); + + EXPECT_EQ(400, GetFileSize()); + EXPECT_EQ(200 + 150, fake_backend()->on_memory_usage()); + EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage()); + + reservation1 = NULL; + + EXPECT_EQ(400, GetFileSize()); + EXPECT_EQ(150, fake_backend()->on_memory_usage()); + EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage()); + + reservation2 = NULL; + + EXPECT_EQ(400, GetFileSize()); + EXPECT_EQ(0, fake_backend()->on_memory_usage()); + EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage()); +} + +// TODO(tzik): Add Truncate test. +// TODO(tzik): Add PluginCrash test and DropReservationManager test. + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/recursive_operation_delegate.cc b/chromium/webkit/browser/fileapi/recursive_operation_delegate.cc index 81f26ff1c71..7adc59680b0 100644 --- a/chromium/webkit/browser/fileapi/recursive_operation_delegate.cc +++ b/chromium/webkit/browser/fileapi/recursive_operation_delegate.cc @@ -27,6 +27,7 @@ RecursiveOperationDelegate::~RecursiveOperationDelegate() { void RecursiveOperationDelegate::Cancel() { canceled_ = true; + OnCancel(); } void RecursiveOperationDelegate::StartRecursiveOperation( @@ -48,6 +49,9 @@ FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() { return file_system_context_->operation_runner(); } +void RecursiveOperationDelegate::OnCancel() { +} + void RecursiveOperationDelegate::DidTryProcessFile( const FileSystemURL& root, base::PlatformFileError error) { diff --git a/chromium/webkit/browser/fileapi/recursive_operation_delegate.h b/chromium/webkit/browser/fileapi/recursive_operation_delegate.h index a5266777341..08e30507bce 100644 --- a/chromium/webkit/browser/fileapi/recursive_operation_delegate.h +++ b/chromium/webkit/browser/fileapi/recursive_operation_delegate.h @@ -115,6 +115,10 @@ class WEBKIT_STORAGE_BROWSER_EXPORT RecursiveOperationDelegate FileSystemOperationRunner* operation_runner(); + // Called when Cancel() is called. This is a hook to do something more + // in a derived class. By default, do nothing. + virtual void OnCancel(); + private: void DidTryProcessFile(const FileSystemURL& root, base::PlatformFileError error); diff --git a/chromium/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc b/chromium/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc deleted file mode 100644 index b3842279420..00000000000 --- a/chromium/webkit/browser/fileapi/recursive_operation_delegate_unittest.cc +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "webkit/browser/fileapi/recursive_operation_delegate.h" - -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/run_loop.h" -#include "testing/gtest/include/gtest/gtest.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_runner.h" -#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h" - -namespace fileapi { -namespace { - -class LoggingRecursiveOperation : public RecursiveOperationDelegate { - public: - struct LogEntry { - enum Type { - PROCESS_FILE, - PROCESS_DIRECTORY, - POST_PROCESS_DIRECTORY - }; - Type type; - FileSystemURL url; - }; - - LoggingRecursiveOperation(FileSystemContext* file_system_context, - const FileSystemURL& root, - const StatusCallback& callback) - : RecursiveOperationDelegate(file_system_context), - root_(root), - callback_(callback), - weak_factory_(this) { - } - virtual ~LoggingRecursiveOperation() {} - - const std::vector<LogEntry>& log_entries() const { return log_entries_; } - - // RecursiveOperationDelegate overrides. - virtual void Run() OVERRIDE { - NOTREACHED(); - } - - virtual void RunRecursively() OVERRIDE { - StartRecursiveOperation(root_, callback_); - } - - virtual void ProcessFile(const FileSystemURL& url, - const StatusCallback& callback) OVERRIDE { - RecordLogEntry(LogEntry::PROCESS_FILE, url); - operation_runner()->GetMetadata( - url, - base::Bind(&LoggingRecursiveOperation::DidGetMetadata, - weak_factory_.GetWeakPtr(), callback)); - } - - virtual void ProcessDirectory(const FileSystemURL& url, - const StatusCallback& callback) OVERRIDE { - RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url); - callback.Run(base::PLATFORM_FILE_OK); - } - - virtual void PostProcessDirectory(const FileSystemURL& url, - const StatusCallback& callback) OVERRIDE { - RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url); - callback.Run(base::PLATFORM_FILE_OK); - } - - private: - void RecordLogEntry(LogEntry::Type type, const FileSystemURL& url) { - LogEntry entry; - entry.type = type; - entry.url = url; - log_entries_.push_back(entry); - } - - void DidGetMetadata(const StatusCallback& callback, - base::PlatformFileError result, - const base::PlatformFileInfo& file_info) { - if (result != base::PLATFORM_FILE_OK) { - callback.Run(result); - return; - } - - callback.Run(file_info.is_directory ? - base::PLATFORM_FILE_ERROR_NOT_A_FILE : - base::PLATFORM_FILE_OK); - } - - FileSystemURL root_; - StatusCallback callback_; - std::vector<LogEntry> log_entries_; - - base::WeakPtrFactory<LoggingRecursiveOperation> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation); -}; - -void ReportStatus(base::PlatformFileError* out_error, - base::PlatformFileError error) { - DCHECK(out_error); - *out_error = error; -} - -// To test the Cancel() during operation, calls Cancel() of |operation| -// after |counter| times message posting. -void CallCancelLater(RecursiveOperationDelegate* operation, int counter) { - if (counter > 0) { - base::MessageLoopProxy::current()->PostTask( - FROM_HERE, - base::Bind(&CallCancelLater, base::Unretained(operation), counter - 1)); - return; - } - - operation->Cancel(); -} - -} // namespace - -class RecursiveOperationDelegateTest : public testing::Test { - protected: - virtual void SetUp() OVERRIDE { - EXPECT_TRUE(base_.CreateUniqueTempDir()); - sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem")); - } - - virtual void TearDown() OVERRIDE { - sandbox_file_system_.TearDown(); - } - - scoped_ptr<FileSystemOperationContext> NewContext() { - FileSystemOperationContext* context = - sandbox_file_system_.NewOperationContext(); - // Grant enough quota for all test cases. - context->set_allowed_bytes_growth(1000000); - return make_scoped_ptr(context); - } - - FileSystemFileUtil* file_util() { - return sandbox_file_system_.file_util(); - } - - FileSystemURL URLForPath(const std::string& path) const { - return sandbox_file_system_.CreateURLFromUTF8(path); - } - - FileSystemURL CreateFile(const std::string& path) { - FileSystemURL url = URLForPath(path); - bool created = false; - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->EnsureFileExists(NewContext().get(), - url, &created)); - EXPECT_TRUE(created); - return url; - } - - FileSystemURL CreateDirectory(const std::string& path) { - FileSystemURL url = URLForPath(path); - EXPECT_EQ(base::PLATFORM_FILE_OK, - file_util()->CreateDirectory(NewContext().get(), url, - false /* exclusive */, true)); - return url; - } - - private: - base::MessageLoop message_loop_; - - // Common temp base for nondestructive uses. - base::ScopedTempDir base_; - SandboxFileSystemTestHelper sandbox_file_system_; -}; - -TEST_F(RecursiveOperationDelegateTest, RootIsFile) { - FileSystemURL src_file(CreateFile("src")); - - base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; - scoped_ptr<FileSystemOperationContext> context = NewContext(); - scoped_ptr<LoggingRecursiveOperation> operation( - new LoggingRecursiveOperation( - context->file_system_context(), src_file, - base::Bind(&ReportStatus, &error))); - operation->RunRecursively(); - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(base::PLATFORM_FILE_OK, error); - - const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries = - operation->log_entries(); - ASSERT_EQ(1U, log_entries.size()); - const LoggingRecursiveOperation::LogEntry& entry = log_entries[0]; - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, entry.type); - EXPECT_EQ(src_file, entry.url); -} - -TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) { - FileSystemURL src_root(CreateDirectory("src")); - FileSystemURL src_dir1(CreateDirectory("src/dir1")); - FileSystemURL src_file1(CreateFile("src/file1")); - FileSystemURL src_file2(CreateFile("src/dir1/file2")); - FileSystemURL src_file3(CreateFile("src/dir1/file3")); - - base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; - scoped_ptr<FileSystemOperationContext> context = NewContext(); - scoped_ptr<LoggingRecursiveOperation> operation( - new LoggingRecursiveOperation( - context->file_system_context(), src_root, - base::Bind(&ReportStatus, &error))); - operation->RunRecursively(); - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(base::PLATFORM_FILE_OK, error); - - const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries = - operation->log_entries(); - ASSERT_EQ(8U, log_entries.size()); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, - log_entries[0].type); - EXPECT_EQ(src_root, log_entries[0].url); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, - log_entries[1].type); - EXPECT_EQ(src_root, log_entries[1].url); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, - log_entries[2].type); - EXPECT_EQ(src_file1, log_entries[2].url); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY, - log_entries[3].type); - EXPECT_EQ(src_dir1, log_entries[3].url); - - // The order of src/dir1/file2 and src/dir1/file3 depends on the file system - // implementation (can be swapped). - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, - log_entries[4].type); - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, - log_entries[5].type); - EXPECT_TRUE((src_file2 == log_entries[4].url && - src_file3 == log_entries[5].url) || - (src_file3 == log_entries[4].url && - src_file2 == log_entries[5].url)); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, - log_entries[6].type); - EXPECT_EQ(src_dir1, log_entries[6].url); - - EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY, - log_entries[7].type); - EXPECT_EQ(src_root, log_entries[7].url); -} - -TEST_F(RecursiveOperationDelegateTest, Cancel) { - FileSystemURL src_root(CreateDirectory("src")); - FileSystemURL src_dir1(CreateDirectory("src/dir1")); - FileSystemURL src_file1(CreateFile("src/file1")); - FileSystemURL src_file2(CreateFile("src/dir1/file2")); - - base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; - scoped_ptr<FileSystemOperationContext> context = NewContext(); - scoped_ptr<LoggingRecursiveOperation> operation( - new LoggingRecursiveOperation( - context->file_system_context(), src_root, - base::Bind(&ReportStatus, &error))); - operation->RunRecursively(); - - // Invoke Cancel(), after 5 times message posting. - CallCancelLater(operation.get(), 5); - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_database_test_helper.cc b/chromium/webkit/browser/fileapi/sandbox_database_test_helper.cc index 6364b2bd7f5..5c9176c0878 100644 --- a/chromium/webkit/browser/fileapi/sandbox_database_test_helper.cc +++ b/chromium/webkit/browser/fileapi/sandbox_database_test_helper.cc @@ -94,7 +94,6 @@ void DeleteDatabaseFile(const base::FilePath& db_path, // We may have multiple files for the same type, so don't break here. } } - } } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_directory_database.cc b/chromium/webkit/browser/fileapi/sandbox_directory_database.cc index 06ab1e50acf..dd376116eef 100644 --- a/chromium/webkit/browser/fileapi/sandbox_directory_database.cc +++ b/chromium/webkit/browser/fileapi/sandbox_directory_database.cc @@ -244,7 +244,7 @@ bool DatabaseCheckHelper::ScanDatabase() { // Ensure the backing file exists as a normal file. base::PlatformFileInfo platform_file_info; - if (!file_util::GetFileInfo( + if (!base::GetFileInfo( path_.Append(file_info.data_path), &platform_file_info) || platform_file_info.is_directory || platform_file_info.is_symbolic_link) { @@ -536,8 +536,10 @@ base::PlatformFileError SandboxDirectoryDatabase::AddFileInfo( return base::PLATFORM_FILE_ERROR_NOT_FOUND; } - if (!VerifyIsDirectory(info.parent_id)) + if (!IsDirectory(info.parent_id)) { + LOG(ERROR) << "New parent directory is a file!"; return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; + } // This would be a fine place to limit the number of files in a directory, if // we decide to add that restriction. @@ -586,7 +588,7 @@ bool SandboxDirectoryDatabase::UpdateFileInfo( if (!GetFileInfo(file_id, &old_info)) return false; if (old_info.parent_id != new_info.parent_id && - !VerifyIsDirectory(new_info.parent_id)) + !IsDirectory(new_info.parent_id)) return false; if (old_info.parent_id != new_info.parent_id || old_info.name != new_info.name) { @@ -751,7 +753,7 @@ bool SandboxDirectoryDatabase::Init(RecoveryOption recovery_option) { LOG(WARNING) << "Clearing SandboxDirectoryDatabase."; if (!base::DeleteFile(filesystem_data_directory_, true)) return false; - if (!file_util::CreateDirectory(filesystem_data_directory_)) + if (!base::CreateDirectory(filesystem_data_directory_)) return false; return Init(FAIL_ON_CORRUPTION); } @@ -774,6 +776,17 @@ bool SandboxDirectoryDatabase::RepairDatabase(const std::string& db_path) { return false; } +bool SandboxDirectoryDatabase::IsDirectory(FileId file_id) { + FileInfo info; + if (!file_id) + return true; // The root is a directory. + if (!GetFileInfo(file_id, &info)) + return false; + if (!info.is_directory()) + return false; + return true; +} + bool SandboxDirectoryDatabase::IsFileSystemConsistent() { if (!Init(FAIL_ON_CORRUPTION)) return false; @@ -856,19 +869,6 @@ bool SandboxDirectoryDatabase::GetLastFileId(FileId* file_id) { return true; } -bool SandboxDirectoryDatabase::VerifyIsDirectory(FileId file_id) { - FileInfo info; - if (!file_id) - return true; // The root is a directory. - if (!GetFileInfo(file_id, &info)) - return false; - if (!info.is_directory()) { - LOG(ERROR) << "New parent directory is a file!"; - return false; - } - return true; -} - // This does very few safety checks! bool SandboxDirectoryDatabase::AddFileInfoHelper( const FileInfo& info, FileId file_id, leveldb::WriteBatch* batch) { diff --git a/chromium/webkit/browser/fileapi/sandbox_directory_database.h b/chromium/webkit/browser/fileapi/sandbox_directory_database.h index 0ad948d630f..67be4328781 100644 --- a/chromium/webkit/browser/fileapi/sandbox_directory_database.h +++ b/chromium/webkit/browser/fileapi/sandbox_directory_database.h @@ -88,6 +88,8 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE SandboxDirectoryDatabase { // creation/destruction of SandboxDirectoryDatabase objects. bool GetNextInteger(int64* next); + bool IsDirectory(FileId file_id); + // Returns true if the database looks consistent with local filesystem. bool IsFileSystemConsistent(); @@ -108,7 +110,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE SandboxDirectoryDatabase { void ReportInitStatus(const leveldb::Status& status); bool StoreDefaultValues(); bool GetLastFileId(FileId* file_id); - bool VerifyIsDirectory(FileId file_id); bool AddFileInfoHelper( const FileInfo& info, FileId file_id, leveldb::WriteBatch* batch); bool RemoveFileInfoHelper(FileId file_id, leveldb::WriteBatch* batch); diff --git a/chromium/webkit/browser/fileapi/sandbox_directory_database_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_directory_database_unittest.cc index 9281446e2fd..b43dc23ee74 100644 --- a/chromium/webkit/browser/fileapi/sandbox_directory_database_unittest.cc +++ b/chromium/webkit/browser/fileapi/sandbox_directory_database_unittest.cc @@ -83,7 +83,7 @@ class SandboxDirectoryDatabaseTest : public testing::Test { base::FilePath local_path = path().Append(data_path); if (!base::DirectoryExists(local_path.DirName())) - ASSERT_TRUE(file_util::CreateDirectory(local_path.DirName())); + ASSERT_TRUE(base::CreateDirectory(local_path.DirName())); bool created = false; base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; @@ -102,7 +102,7 @@ class SandboxDirectoryDatabaseTest : public testing::Test { void ClearDatabaseAndDirectory() { db_.reset(); ASSERT_TRUE(base::DeleteFile(path(), true /* recursive */)); - ASSERT_TRUE(file_util::CreateDirectory(path())); + ASSERT_TRUE(base::CreateDirectory(path())); db_.reset(new SandboxDirectoryDatabase(path())); } @@ -141,6 +141,7 @@ class SandboxDirectoryDatabaseTest : public testing::Test { base::ScopedTempDir base_; scoped_ptr<SandboxDirectoryDatabase> db_; + private: DISALLOW_COPY_AND_ASSIGN(SandboxDirectoryDatabaseTest); }; diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_backend.cc b/chromium/webkit/browser/fileapi/sandbox_file_system_backend.cc index a5adc63e85d..cc022788aa4 100644 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_backend.cc +++ b/chromium/webkit/browser/fileapi/sandbox_file_system_backend.cc @@ -49,18 +49,12 @@ void SandboxFileSystemBackend::Initialize(FileSystemContext* context) { DCHECK(delegate_); // Set quota observers. - delegate_->AddFileUpdateObserver( - fileapi::kFileSystemTypeTemporary, - delegate_->quota_observer(), - delegate_->file_task_runner()); + delegate_->RegisterQuotaUpdateObserver(fileapi::kFileSystemTypeTemporary); delegate_->AddFileAccessObserver( fileapi::kFileSystemTypeTemporary, delegate_->quota_observer(), NULL); - delegate_->AddFileUpdateObserver( - fileapi::kFileSystemTypePersistent, - delegate_->quota_observer(), - delegate_->file_task_runner()); + delegate_->RegisterQuotaUpdateObserver(fileapi::kFileSystemTypePersistent); delegate_->AddFileAccessObserver( fileapi::kFileSystemTypePersistent, delegate_->quota_observer(), NULL); diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc index 1cb61bb3d60..bb4d48e41d1 100644 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc +++ b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc @@ -4,6 +4,8 @@ #include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h" +#include <vector> + #include "base/command_line.h" #include "base/file_util.h" #include "base/metrics/histogram.h" @@ -17,6 +19,9 @@ #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/file_system_usage_cache.h" #include "webkit/browser/fileapi/obfuscated_file_util.h" +#include "webkit/browser/fileapi/quota/quota_backend_impl.h" +#include "webkit/browser/fileapi/quota/quota_reservation.h" +#include "webkit/browser/fileapi/quota/quota_reservation_manager.h" #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" #include "webkit/browser/fileapi/sandbox_file_system_backend.h" #include "webkit/browser/fileapi/sandbox_quota_observer.h" @@ -36,6 +41,18 @@ const char kOpenFileSystemDetailNonThrottledLabel[] = "FileSystem.OpenFileSystemDetailNonthrottled"; int64 kMinimumStatsCollectionIntervalHours = 1; +// For type directory names in ObfuscatedFileUtil. +// TODO(kinuko,nhiroki): Each type string registration should be done +// via its own backend. +const char kTemporaryDirectoryName[] = "t"; +const char kPersistentDirectoryName[] = "p"; +const char kSyncableDirectoryName[] = "s"; + +const char* kPrepopulateTypes[] = { + kPersistentDirectoryName, + kTemporaryDirectoryName +}; + enum FileSystemError { kOK = 0, kIncognito, @@ -57,6 +74,18 @@ const base::FilePath::CharType kRestrictedChars[] = { FILE_PATH_LITERAL('/'), FILE_PATH_LITERAL('\\'), }; +std::string GetTypeStringForURL(const FileSystemURL& url) { + return SandboxFileSystemBackendDelegate::GetTypeString(url.type()); +} + +std::set<std::string> GetKnownTypeStrings() { + std::set<std::string> known_type_strings; + known_type_strings.insert(kTemporaryDirectoryName); + known_type_strings.insert(kPersistentDirectoryName); + known_type_strings.insert(kSyncableDirectoryName); + return known_type_strings; +} + class ObfuscatedOriginEnumerator : public SandboxFileSystemBackendDelegate::OriginEnumerator { public: @@ -70,7 +99,8 @@ class ObfuscatedOriginEnumerator } virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE { - return enum_->HasFileSystemType(type); + return enum_->HasTypeDirectory( + SandboxFileSystemBackendDelegate::GetTypeString(type)); } private: @@ -85,7 +115,9 @@ void OpenFileSystemOnFileThread( base::PlatformFileError* error_ptr) { DCHECK(error_ptr); const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT); - file_util->GetDirectoryForOriginAndType(origin_url, type, create, error_ptr); + file_util->GetDirectoryForOriginAndType( + origin_url, SandboxFileSystemBackendDelegate::GetTypeString(type), + create, error_ptr); if (*error_ptr != base::PLATFORM_FILE_OK) { UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kCreateDirectoryError, @@ -107,12 +139,36 @@ void DidOpenFileSystem( callback.Run(*error); } +template <typename T> +void DeleteSoon(base::SequencedTaskRunner* runner, T* ptr) { + if (!runner->DeleteSoon(FROM_HERE, ptr)) + delete ptr; +} + } // namespace const base::FilePath::CharType SandboxFileSystemBackendDelegate::kFileSystemDirectory[] = FILE_PATH_LITERAL("File System"); +// static +std::string SandboxFileSystemBackendDelegate::GetTypeString( + FileSystemType type) { + switch (type) { + case kFileSystemTypeTemporary: + return kTemporaryDirectoryName; + case kFileSystemTypePersistent: + return kPersistentDirectoryName; + case kFileSystemTypeSyncable: + case kFileSystemTypeSyncableForInternalSync: + return kSyncableDirectoryName; + case kFileSystemTypeUnknown: + default: + NOTREACHED() << "Unknown filesystem type requested:" << type; + return std::string(); + } +} + SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate( quota::QuotaManagerProxy* quota_manager_proxy, base::SequencedTaskRunner* file_task_runner, @@ -124,83 +180,50 @@ SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate( new ObfuscatedFileUtil( special_storage_policy, profile_path.Append(kFileSystemDirectory), - file_task_runner))), + file_task_runner, + base::Bind(&GetTypeStringForURL), + GetKnownTypeStrings(), + this))), file_system_usage_cache_(new FileSystemUsageCache(file_task_runner)), quota_observer_(new SandboxQuotaObserver( quota_manager_proxy, file_task_runner, obfuscated_file_util(), usage_cache())), + quota_reservation_manager_(new QuotaReservationManager( + scoped_ptr<QuotaReservationManager::QuotaBackend>( + new QuotaBackendImpl(file_task_runner_, + obfuscated_file_util(), + usage_cache(), + quota_manager_proxy)))), special_storage_policy_(special_storage_policy), file_system_options_(file_system_options), is_filesystem_opened_(false), weak_factory_(this) { -} - -SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() { - io_thread_checker_.DetachFromThread(); + // Prepopulate database only if it can run asynchronously (i.e. the current + // thread is not file_task_runner). Usually this is the case but may not + // in test code. if (!file_task_runner_->RunsTasksOnCurrentThread()) { - AsyncFileUtil* sandbox_file_util = sandbox_file_util_.release(); - SandboxQuotaObserver* quota_observer = quota_observer_.release(); - FileSystemUsageCache* file_system_usage_cache = - file_system_usage_cache_.release(); - if (!file_task_runner_->DeleteSoon(FROM_HERE, sandbox_file_util)) - delete sandbox_file_util; - if (!file_task_runner_->DeleteSoon(FROM_HERE, quota_observer)) - delete quota_observer; - if (!file_task_runner_->DeleteSoon(FROM_HERE, file_system_usage_cache)) - delete file_system_usage_cache; + std::vector<std::string> types_to_prepopulate( + &kPrepopulateTypes[0], + &kPrepopulateTypes[arraysize(kPrepopulateTypes)]); + file_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ObfuscatedFileUtil::MaybePrepopulateDatabase, + base::Unretained(obfuscated_file_util()), + types_to_prepopulate)); } } -bool SandboxFileSystemBackendDelegate::IsAccessValid( - const FileSystemURL& url) const { - if (!IsAllowedScheme(url.origin())) - return false; - - if (url.path().ReferencesParent()) - return false; - - // Return earlier if the path is '/', because VirtualPath::BaseName() - // returns '/' for '/' and we fail the "basename != '/'" check below. - // (We exclude '.' because it's disallowed by spec.) - if (VirtualPath::IsRootPath(url.path()) && - url.path() != base::FilePath(base::FilePath::kCurrentDirectory)) - return true; - - // Restricted names specified in - // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions - base::FilePath filename = VirtualPath::BaseName(url.path()); - // See if the name is allowed to create. - for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { - if (filename.value() == kRestrictedNames[i]) - return false; - } - for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { - if (filename.value().find(kRestrictedChars[i]) != - base::FilePath::StringType::npos) - return false; - } - - return true; -} - -bool SandboxFileSystemBackendDelegate::IsAllowedScheme(const GURL& url) const { - // Basically we only accept http or https. We allow file:// URLs - // only if --allow-file-access-from-files flag is given. - if (url.SchemeIsHTTPOrHTTPS()) - return true; - if (url.SchemeIsFileSystem()) - return url.inner_url() && IsAllowedScheme(*url.inner_url()); +SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() { + io_thread_checker_.DetachFromThread(); - for (size_t i = 0; - i < file_system_options_.additional_allowed_schemes().size(); - ++i) { - if (url.SchemeIs( - file_system_options_.additional_allowed_schemes()[i].c_str())) - return true; + if (!file_task_runner_->RunsTasksOnCurrentThread()) { + DeleteSoon(file_task_runner_.get(), quota_reservation_manager_.release()); + DeleteSoon(file_task_runner_.get(), sandbox_file_util_.release()); + DeleteSoon(file_task_runner_.get(), quota_observer_.release()); + DeleteSoon(file_task_runner_.get(), file_system_usage_cache_.release()); } - return false; } SandboxFileSystemBackendDelegate::OriginEnumerator* @@ -215,7 +238,7 @@ SandboxFileSystemBackendDelegate::GetBaseDirectoryForOriginAndType( bool create) { base::PlatformFileError error = base::PLATFORM_FILE_OK; base::FilePath path = obfuscated_file_util()->GetDirectoryForOriginAndType( - origin_url, type, create, &error); + origin_url, GetTypeString(type), create, &error); if (error != base::PLATFORM_FILE_OK) return base::FilePath(); return path; @@ -309,7 +332,7 @@ SandboxFileSystemBackendDelegate::DeleteOriginDataOnFileThread( file_system_context, origin_url, type); usage_cache()->CloseCacheFiles(); bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType( - origin_url, type); + origin_url, GetTypeString(type)); if (result && proxy) { proxy->NotifyStorageModified( quota::QuotaClient::kFileSystem, @@ -395,6 +418,15 @@ int64 SandboxFileSystemBackendDelegate::GetOriginUsageOnFileThread( return usage; } +scoped_refptr<QuotaReservation> +SandboxFileSystemBackendDelegate::CreateQuotaReservationOnFileTaskRunner( + const GURL& origin, + FileSystemType type) { + DCHECK(file_task_runner_->RunsTasksOnCurrentThread()); + DCHECK(quota_reservation_manager_); + return quota_reservation_manager_->CreateReservation(origin, type); +} + void SandboxFileSystemBackendDelegate::AddFileUpdateObserver( FileSystemType type, FileUpdateObserver* observer, @@ -449,6 +481,11 @@ const AccessObserverList* SandboxFileSystemBackendDelegate::GetAccessObservers( return &iter->second; } +void SandboxFileSystemBackendDelegate::RegisterQuotaUpdateObserver( + FileSystemType type) { + AddFileUpdateObserver(type, quota_observer_.get(), file_task_runner_.get()); +} + void SandboxFileSystemBackendDelegate::InvalidateUsageCache( const GURL& origin, FileSystemType type) { @@ -472,6 +509,56 @@ FileSystemFileUtil* SandboxFileSystemBackendDelegate::sync_file_util() { return static_cast<AsyncFileUtilAdapter*>(file_util())->sync_file_util(); } +bool SandboxFileSystemBackendDelegate::IsAccessValid( + const FileSystemURL& url) const { + if (!IsAllowedScheme(url.origin())) + return false; + + if (url.path().ReferencesParent()) + return false; + + // Return earlier if the path is '/', because VirtualPath::BaseName() + // returns '/' for '/' and we fail the "basename != '/'" check below. + // (We exclude '.' because it's disallowed by spec.) + if (VirtualPath::IsRootPath(url.path()) && + url.path() != base::FilePath(base::FilePath::kCurrentDirectory)) + return true; + + // Restricted names specified in + // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions + base::FilePath filename = VirtualPath::BaseName(url.path()); + // See if the name is allowed to create. + for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { + if (filename.value() == kRestrictedNames[i]) + return false; + } + for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { + if (filename.value().find(kRestrictedChars[i]) != + base::FilePath::StringType::npos) + return false; + } + + return true; +} + +bool SandboxFileSystemBackendDelegate::IsAllowedScheme(const GURL& url) const { + // Basically we only accept http or https. We allow file:// URLs + // only if --allow-file-access-from-files flag is given. + if (url.SchemeIsHTTPOrHTTPS()) + return true; + if (url.SchemeIsFileSystem()) + return url.inner_url() && IsAllowedScheme(*url.inner_url()); + + for (size_t i = 0; + i < file_system_options_.additional_allowed_schemes().size(); + ++i) { + if (url.SchemeIs( + file_system_options_.additional_allowed_schemes()[i].c_str())) + return true; + } + return false; +} + base::FilePath SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType( const GURL& origin_url, @@ -494,7 +581,7 @@ SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType( DCHECK(error_out); *error_out = base::PLATFORM_FILE_OK; base::FilePath base_path = sandbox_file_util->GetDirectoryForOriginAndType( - origin_url, type, false /* create */, error_out); + origin_url, GetTypeString(type), false /* create */, error_out); if (*error_out != base::PLATFORM_FILE_OK) return base::FilePath(); return base_path.Append(FileSystemUsageCache::kUsageFileName); @@ -563,4 +650,18 @@ ObfuscatedFileUtil* SandboxFileSystemBackendDelegate::obfuscated_file_util() { return static_cast<ObfuscatedFileUtil*>(sync_file_util()); } +// Declared in obfuscated_file_util.h. +// static +ObfuscatedFileUtil* ObfuscatedFileUtil::CreateForTesting( + quota::SpecialStoragePolicy* special_storage_policy, + const base::FilePath& file_system_directory, + base::SequencedTaskRunner* file_task_runner) { + return new ObfuscatedFileUtil(special_storage_policy, + file_system_directory, + file_task_runner, + base::Bind(&GetTypeStringForURL), + GetKnownTypeStrings(), + NULL); +} + } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h index 33d791e5920..dd5e11f1837 100644 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h +++ b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h @@ -11,6 +11,7 @@ #include <utility> #include "base/files/file_path.h" +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -43,6 +44,7 @@ class FileSystemOperationContext; class FileSystemURL; class FileSystemUsageCache; class ObfuscatedFileUtil; +class QuotaReservationManager; class SandboxFileSystemBackend; class SandboxFileSystemTestHelper; class SandboxQuotaObserver; @@ -70,6 +72,9 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate virtual bool HasFileSystemType(FileSystemType type) const = 0; }; + // Returns the type directory name in sandbox directory for given |type|. + static std::string GetTypeString(FileSystemType type); + SandboxFileSystemBackendDelegate( quota::QuotaManagerProxy* quota_manager_proxy, base::SequencedTaskRunner* file_task_runner, @@ -79,14 +84,6 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate virtual ~SandboxFileSystemBackendDelegate(); - // Performs API-specific validity checks on the given path |url|. - // Returns true if access to |url| is valid in this filesystem. - bool IsAccessValid(const FileSystemURL& url) const; - - // Returns true if the given |url|'s scheme is allowed to access - // filesystem. - bool IsAllowedScheme(const GURL& url) const; - // Returns an origin enumerator of sandbox filesystem. // This method can only be called on the file thread. OriginEnumerator* CreateOriginEnumerator(); @@ -141,6 +138,10 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate FileSystemContext* context, const GURL& origin_url, FileSystemType type) OVERRIDE; + virtual scoped_refptr<QuotaReservation> + CreateQuotaReservationOnFileTaskRunner( + const GURL& origin_url, + FileSystemType type) OVERRIDE; virtual void AddFileUpdateObserver( FileSystemType type, FileUpdateObserver* observer, @@ -160,6 +161,9 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate virtual const AccessObserverList* GetAccessObservers( FileSystemType type) const OVERRIDE; + // Registers quota observer for file updates on filesystem of |type|. + void RegisterQuotaUpdateObserver(FileSystemType type); + void InvalidateUsageCache(const GURL& origin_url, FileSystemType type); void StickyInvalidateUsageCache(const GURL& origin_url, @@ -188,6 +192,16 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate private: friend class SandboxQuotaObserver; friend class SandboxFileSystemTestHelper; + friend class QuotaBackendImpl; + FRIEND_TEST_ALL_PREFIXES(SandboxFileSystemBackendDelegateTest, IsAccessValid); + + // Performs API-specific validity checks on the given path |url|. + // Returns true if access to |url| is valid in this filesystem. + bool IsAccessValid(const FileSystemURL& url) const; + + // Returns true if the given |url|'s scheme is allowed to access + // filesystem. + bool IsAllowedScheme(const GURL& url) const; // Returns a path to the usage cache file. base::FilePath GetUsageCachePathForOriginAndType( @@ -212,6 +226,7 @@ class WEBKIT_STORAGE_BROWSER_EXPORT SandboxFileSystemBackendDelegate scoped_ptr<AsyncFileUtil> sandbox_file_util_; scoped_ptr<FileSystemUsageCache> file_system_usage_cache_; scoped_ptr<SandboxQuotaObserver> quota_observer_; + scoped_ptr<QuotaReservationManager> quota_reservation_manager_; scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_; diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc deleted file mode 100644 index 48c3b8c4e70..00000000000 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h" - -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" -#include "webkit/browser/fileapi/file_system_url.h" -#include "webkit/browser/fileapi/mock_file_system_options.h" - -namespace fileapi { - -namespace { - -FileSystemURL CreateFileSystemURL(const char* path) { - const GURL kOrigin("http://foo/"); - return FileSystemURL::CreateForTest( - kOrigin, kFileSystemTypeTemporary, base::FilePath::FromUTF8Unsafe(path)); -} - -} // namespace - -class SandboxFileSystemBackendDelegateTest : public testing::Test { - protected: - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - delegate_.reset(new SandboxFileSystemBackendDelegate( - NULL /* quota_manager_proxy */, - base::MessageLoopProxy::current().get(), - data_dir_.path(), - NULL /* special_storage_policy */, - CreateAllowFileAccessOptions())); - } - - base::ScopedTempDir data_dir_; - base::MessageLoop message_loop_; - scoped_ptr<SandboxFileSystemBackendDelegate> delegate_; -}; - -TEST_F(SandboxFileSystemBackendDelegateTest, IsAccessValid) { - // Normal case. - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("a"))); - - // Access to a path with parent references ('..') should be disallowed. - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("a/../b"))); - - // Access from non-allowed scheme should be disallowed. - EXPECT_FALSE(delegate_->IsAccessValid( - FileSystemURL::CreateForTest( - GURL("unknown://bar"), kFileSystemTypeTemporary, - base::FilePath::FromUTF8Unsafe("foo")))); - - // Access with restricted name should be disallowed. - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("."))); - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".."))); - - // This is also disallowed due to Windows XP parent path handling. - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("..."))); - - // These are identified as unsafe cases due to weird path handling - // on Windows. - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(" .."))); - EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".. "))); - - // Similar but safe cases. - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(" ."))); - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(". "))); - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("b."))); - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(".b"))); - - // A path that looks like a drive letter. - EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("c:"))); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_file_system_backend_unittest.cc deleted file mode 100644 index ed06d17ad70..00000000000 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_backend_unittest.cc +++ /dev/null @@ -1,325 +0,0 @@ -// 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_backend.h" - -#include <set> - -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/run_loop.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" -#include "webkit/browser/fileapi/file_system_backend.h" -#include "webkit/browser/fileapi/file_system_url.h" -#include "webkit/browser/fileapi/mock_file_system_options.h" -#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h" -#include "webkit/common/fileapi/file_system_util.h" - -// PS stands for path separator. -#if defined(FILE_PATH_USES_WIN_SEPARATORS) -#define PS "\\" -#else -#define PS "/" -#endif - -namespace fileapi { - -namespace { - -const struct RootPathTest { - fileapi::FileSystemType type; - const char* origin_url; - const char* expected_path; -} kRootPathTestCases[] = { - { fileapi::kFileSystemTypeTemporary, "http://foo:1/", - "000" PS "t" }, - { fileapi::kFileSystemTypePersistent, "http://foo:1/", - "000" PS "p" }, - { fileapi::kFileSystemTypeTemporary, "http://bar.com/", - "001" PS "t" }, - { fileapi::kFileSystemTypePersistent, "http://bar.com/", - "001" PS "p" }, - { fileapi::kFileSystemTypeTemporary, "https://foo:2/", - "002" PS "t" }, - { fileapi::kFileSystemTypePersistent, "https://foo:2/", - "002" PS "p" }, - { fileapi::kFileSystemTypeTemporary, "https://bar.com/", - "003" PS "t" }, - { fileapi::kFileSystemTypePersistent, "https://bar.com/", - "003" PS "p" }, -}; - -const struct RootPathFileURITest { - fileapi::FileSystemType type; - const char* origin_url; - const char* expected_path; - const char* virtual_path; -} kRootPathFileURITestCases[] = { - { fileapi::kFileSystemTypeTemporary, "file:///", - "000" PS "t", NULL }, - { fileapi::kFileSystemTypePersistent, "file:///", - "000" PS "p", NULL }, -}; - -FileSystemURL CreateFileSystemURL(const char* path) { - const GURL kOrigin("http://foo/"); - return FileSystemURL::CreateForTest( - kOrigin, kFileSystemTypeTemporary, base::FilePath::FromUTF8Unsafe(path)); -} - -void DidOpenFileSystem(base::PlatformFileError* error_out, - const GURL& origin_url, - const std::string& name, - base::PlatformFileError error) { - *error_out = error; -} - -} // namespace - -class SandboxFileSystemBackendTest : public testing::Test { - protected: - virtual void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - SetUpNewDelegate(CreateAllowFileAccessOptions()); - } - - void SetUpNewDelegate(const FileSystemOptions& options) { - delegate_.reset(new SandboxFileSystemBackendDelegate( - NULL /* quota_manager_proxy */, - base::MessageLoopProxy::current().get(), - data_dir_.path(), - NULL /* special_storage_policy */, - options)); - } - - void SetUpNewBackend(const FileSystemOptions& options) { - SetUpNewDelegate(options); - backend_.reset(new SandboxFileSystemBackend(delegate_.get())); - } - - SandboxFileSystemBackendDelegate::OriginEnumerator* - CreateOriginEnumerator() const { - return backend_->CreateOriginEnumerator(); - } - - void CreateOriginTypeDirectory(const GURL& origin, - fileapi::FileSystemType type) { - base::FilePath target = delegate_-> - GetBaseDirectoryForOriginAndType(origin, type, true); - ASSERT_TRUE(!target.empty()); - ASSERT_TRUE(base::DirectoryExists(target)); - } - - bool GetRootPath(const GURL& origin_url, - fileapi::FileSystemType type, - OpenFileSystemMode mode, - base::FilePath* root_path) { - base::PlatformFileError error = base::PLATFORM_FILE_OK; - backend_->OpenFileSystem( - origin_url, type, mode, - base::Bind(&DidOpenFileSystem, &error)); - base::RunLoop().RunUntilIdle(); - if (error != base::PLATFORM_FILE_OK) - return false; - base::FilePath returned_root_path = - delegate_->GetBaseDirectoryForOriginAndType( - origin_url, type, false /* create */); - if (root_path) - *root_path = returned_root_path; - return !returned_root_path.empty(); - } - - base::FilePath file_system_path() const { - return data_dir_.path().Append( - SandboxFileSystemBackendDelegate::kFileSystemDirectory); - } - - base::ScopedTempDir data_dir_; - base::MessageLoop message_loop_; - scoped_ptr<SandboxFileSystemBackendDelegate> delegate_; - scoped_ptr<SandboxFileSystemBackend> backend_; -}; - -TEST_F(SandboxFileSystemBackendTest, Empty) { - SetUpNewBackend(CreateAllowFileAccessOptions()); - scoped_ptr<SandboxFileSystemBackendDelegate::OriginEnumerator> enumerator( - CreateOriginEnumerator()); - ASSERT_TRUE(enumerator->Next().is_empty()); -} - -TEST_F(SandboxFileSystemBackendTest, EnumerateOrigins) { - SetUpNewBackend(CreateAllowFileAccessOptions()); - const char* temporary_origins[] = { - "http://www.bar.com/", - "http://www.foo.com/", - "http://www.foo.com:1/", - "http://www.example.com:8080/", - "http://www.google.com:80/", - }; - const char* persistent_origins[] = { - "http://www.bar.com/", - "http://www.foo.com:8080/", - "http://www.foo.com:80/", - }; - size_t temporary_size = ARRAYSIZE_UNSAFE(temporary_origins); - size_t persistent_size = ARRAYSIZE_UNSAFE(persistent_origins); - std::set<GURL> temporary_set, persistent_set; - for (size_t i = 0; i < temporary_size; ++i) { - CreateOriginTypeDirectory(GURL(temporary_origins[i]), - fileapi::kFileSystemTypeTemporary); - temporary_set.insert(GURL(temporary_origins[i])); - } - for (size_t i = 0; i < persistent_size; ++i) { - CreateOriginTypeDirectory(GURL(persistent_origins[i]), - kFileSystemTypePersistent); - persistent_set.insert(GURL(persistent_origins[i])); - } - - scoped_ptr<SandboxFileSystemBackendDelegate::OriginEnumerator> enumerator( - CreateOriginEnumerator()); - size_t temporary_actual_size = 0; - size_t persistent_actual_size = 0; - GURL current; - while (!(current = enumerator->Next()).is_empty()) { - SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current.spec()); - if (enumerator->HasFileSystemType(kFileSystemTypeTemporary)) { - ASSERT_TRUE(temporary_set.find(current) != temporary_set.end()); - ++temporary_actual_size; - } - if (enumerator->HasFileSystemType(kFileSystemTypePersistent)) { - ASSERT_TRUE(persistent_set.find(current) != persistent_set.end()); - ++persistent_actual_size; - } - } - - EXPECT_EQ(temporary_size, temporary_actual_size); - EXPECT_EQ(persistent_size, persistent_actual_size); -} - -TEST_F(SandboxFileSystemBackendTest, GetRootPathCreateAndExamine) { - std::vector<base::FilePath> returned_root_path( - ARRAYSIZE_UNSAFE(kRootPathTestCases)); - SetUpNewBackend(CreateAllowFileAccessOptions()); - - // Create a new root directory. - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPath (create) #" << i << " " - << kRootPathTestCases[i].expected_path); - - base::FilePath root_path; - EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url), - kRootPathTestCases[i].type, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - &root_path)); - - base::FilePath expected = file_system_path().AppendASCII( - kRootPathTestCases[i].expected_path); - EXPECT_EQ(expected.value(), root_path.value()); - EXPECT_TRUE(base::DirectoryExists(root_path)); - ASSERT_TRUE(returned_root_path.size() > i); - returned_root_path[i] = root_path; - } - - // Get the root directory with create=false and see if we get the - // same directory. - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPath (get) #" << i << " " - << kRootPathTestCases[i].expected_path); - - base::FilePath root_path; - EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url), - kRootPathTestCases[i].type, - OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, - &root_path)); - ASSERT_TRUE(returned_root_path.size() > i); - EXPECT_EQ(returned_root_path[i].value(), root_path.value()); - } -} - -TEST_F(SandboxFileSystemBackendTest, - GetRootPathCreateAndExamineWithNewBackend) { - std::vector<base::FilePath> returned_root_path( - ARRAYSIZE_UNSAFE(kRootPathTestCases)); - SetUpNewBackend(CreateAllowFileAccessOptions()); - - GURL origin_url("http://foo.com:1/"); - - base::FilePath root_path1; - EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - &root_path1)); - - SetUpNewBackend(CreateDisallowFileAccessOptions()); - base::FilePath root_path2; - EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary, - OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, - &root_path2)); - - EXPECT_EQ(root_path1.value(), root_path2.value()); -} - -TEST_F(SandboxFileSystemBackendTest, GetRootPathGetWithoutCreate) { - SetUpNewBackend(CreateDisallowFileAccessOptions()); - - // Try to get a root directory without creating. - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPath (create=false) #" << i << " " - << kRootPathTestCases[i].expected_path); - EXPECT_FALSE(GetRootPath(GURL(kRootPathTestCases[i].origin_url), - kRootPathTestCases[i].type, - OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, - NULL)); - } -} - -TEST_F(SandboxFileSystemBackendTest, GetRootPathInIncognito) { - SetUpNewBackend(CreateIncognitoFileSystemOptions()); - - // Try to get a root directory. - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathTestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPath (incognito) #" << i << " " - << kRootPathTestCases[i].expected_path); - EXPECT_FALSE( - GetRootPath(GURL(kRootPathTestCases[i].origin_url), - kRootPathTestCases[i].type, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - NULL)); - } -} - -TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURI) { - SetUpNewBackend(CreateDisallowFileAccessOptions()); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPathFileURI (disallow) #" - << i << " " << kRootPathFileURITestCases[i].expected_path); - EXPECT_FALSE( - GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url), - kRootPathFileURITestCases[i].type, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - NULL)); - } -} - -TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURIWithAllowFlag) { - SetUpNewBackend(CreateAllowFileAccessOptions()); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRootPathFileURITestCases); ++i) { - SCOPED_TRACE(testing::Message() << "RootPathFileURI (allow) #" - << i << " " << kRootPathFileURITestCases[i].expected_path); - base::FilePath root_path; - EXPECT_TRUE(GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url), - kRootPathFileURITestCases[i].type, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - &root_path)); - base::FilePath expected = file_system_path().AppendASCII( - kRootPathFileURITestCases[i].expected_path); - EXPECT_EQ(expected.value(), root_path.value()); - EXPECT_TRUE(base::DirectoryExists(root_path)); - } -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.cc b/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.cc deleted file mode 100644 index df579e488e4..00000000000 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.cc +++ /dev/null @@ -1,152 +0,0 @@ -// 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/message_loop_proxy.h" -#include "base/run_loop.h" -#include "url/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_operation_runner.h" -#include "webkit/browser/fileapi/file_system_url.h" -#include "webkit/browser/fileapi/file_system_usage_cache.h" -#include "webkit/browser/fileapi/mock_file_system_context.h" -#include "webkit/browser/fileapi/sandbox_file_system_backend.h" -#include "webkit/browser/quota/mock_special_storage_policy.h" -#include "webkit/common/fileapi/file_system_util.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::RunLoop().RunUntilIdle(); -} - -base::FilePath SandboxFileSystemTestHelper::GetOriginRootPath() { - return file_system_context_->sandbox_delegate()-> - 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_delegate()-> - 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_.get(), origin_, type_); -} - -int64 SandboxFileSystemTestHelper::ComputeCurrentOriginUsage() { - usage_cache()->CloseCacheFiles(); - int64 size = base::ComputeDirectorySize(GetOriginRootPath()); - if (base::PathExists(GetUsageCachePath())) - size -= FileSystemUsageCache::kUsageFileSize; - return size; -} - -int64 -SandboxFileSystemTestHelper::ComputeCurrentDirectoryDatabaseUsage() { - return base::ComputeDirectorySize( - GetOriginRootPath().AppendASCII("Paths")); -} - -FileSystemOperationRunner* SandboxFileSystemTestHelper::operation_runner() { - return file_system_context_->operation_runner(); -} - -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; -} - -void SandboxFileSystemTestHelper::AddFileChangeObserver( - FileChangeObserver* observer) { - file_system_context_->sandbox_backend()->GetQuotaUtil()-> - AddFileChangeObserver(type_, observer, NULL); -} - -FileSystemUsageCache* SandboxFileSystemTestHelper::usage_cache() { - return file_system_context()->sandbox_delegate()->usage_cache(); -} - -void SandboxFileSystemTestHelper::SetUpFileSystem() { - DCHECK(file_system_context_.get()); - DCHECK(file_system_context_->sandbox_backend()->CanHandleType(type_)); - - file_util_ = file_system_context_->sandbox_delegate()->sync_file_util(); - DCHECK(file_util_); - - // Prepare the origin's root directory. - file_system_context_->sandbox_delegate()-> - GetBaseDirectoryForOriginAndType(origin_, type_, 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/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.h b/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.h deleted file mode 100644 index 5a48872326c..00000000000 --- a/chromium/webkit/browser/fileapi/sandbox_file_system_test_helper.h +++ /dev/null @@ -1,103 +0,0 @@ -// 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 "url/gurl.h" -#include "webkit/browser/fileapi/file_system_url.h" -#include "webkit/browser/fileapi/file_system_usage_cache.h" -#include "webkit/browser/fileapi/task_runner_bound_observer_list.h" -#include "webkit/common/fileapi/file_system_types.h" -#include "webkit/common/fileapi/file_system_util.h" -#include "webkit/common/quota/quota_types.h" - -namespace base { -class FilePath; -} - -namespace quota { -class QuotaManagerProxy; -} - -namespace fileapi { - -class FileSystemContext; -class FileSystemFileUtil; -class FileSystemOperationContext; -class FileSystemOperationRunner; - -// 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(); - 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(); - - FileSystemOperationRunner* operation_runner(); - FileSystemOperationContext* NewOperationContext(); - - void AddFileChangeObserver(FileChangeObserver* observer); - - 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/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.cc b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.cc index 7ed4e8ce4af..901c4fd61bc 100644 --- a/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.cc +++ b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.cc @@ -12,14 +12,17 @@ namespace fileapi { // Special directory name for isolated origin. const base::FilePath::CharType -SandboxIsolatedOriginDatabase::kOriginDirectory[] = FILE_PATH_LITERAL("iso"); +SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory[] = + FILE_PATH_LITERAL("iso"); SandboxIsolatedOriginDatabase::SandboxIsolatedOriginDatabase( const std::string& origin, - const base::FilePath& file_system_directory) + const base::FilePath& file_system_directory, + const base::FilePath& origin_directory) : migration_checked_(false), origin_(origin), - file_system_directory_(file_system_directory) { + file_system_directory_(file_system_directory), + origin_directory_(origin_directory) { } SandboxIsolatedOriginDatabase::~SandboxIsolatedOriginDatabase() { @@ -27,16 +30,14 @@ SandboxIsolatedOriginDatabase::~SandboxIsolatedOriginDatabase() { bool SandboxIsolatedOriginDatabase::HasOriginPath( const std::string& origin) { - MigrateDatabaseIfNeeded(); return (origin_ == origin); } bool SandboxIsolatedOriginDatabase::GetPathForOrigin( const std::string& origin, base::FilePath* directory) { - MigrateDatabaseIfNeeded(); if (origin != origin_) return false; - *directory = base::FilePath(kOriginDirectory); + *directory = origin_directory_; return true; } @@ -47,20 +48,19 @@ bool SandboxIsolatedOriginDatabase::RemovePathForOrigin( bool SandboxIsolatedOriginDatabase::ListAllOrigins( std::vector<OriginRecord>* origins) { - MigrateDatabaseIfNeeded(); - origins->push_back(OriginRecord(origin_, base::FilePath(kOriginDirectory))); + origins->push_back(OriginRecord(origin_, origin_directory_)); return true; } void SandboxIsolatedOriginDatabase::DropDatabase() { } -void SandboxIsolatedOriginDatabase::MigrateBackDatabase( +void SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase( const std::string& origin, const base::FilePath& file_system_directory, SandboxOriginDatabase* database) { base::FilePath isolated_directory = - file_system_directory.Append(kOriginDirectory); + file_system_directory.Append(kObsoleteOriginDirectory); if (database->HasOriginPath(origin)) { // Don't bother. @@ -77,29 +77,4 @@ void SandboxIsolatedOriginDatabase::MigrateBackDatabase( } } -void SandboxIsolatedOriginDatabase::MigrateDatabaseIfNeeded() { - if (migration_checked_) - return; - - migration_checked_ = true; - // See if we have non-isolated version of sandbox database. - scoped_ptr<SandboxOriginDatabase> database( - new SandboxOriginDatabase(file_system_directory_)); - if (!database->HasOriginPath(origin_)) - return; - - base::FilePath directory_name; - if (database->GetPathForOrigin(origin_, &directory_name) && - directory_name != base::FilePath(kOriginDirectory)) { - base::FilePath from_path = file_system_directory_.Append(directory_name); - base::FilePath to_path = file_system_directory_.Append(kOriginDirectory); - - if (base::PathExists(to_path)) - base::DeleteFile(to_path, true /* recursive */); - base::Move(from_path, to_path); - } - - database->RemoveDatabase(); -} - } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.h b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.h index 2505659b064..d3a882a5ed4 100644 --- a/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.h +++ b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database.h @@ -5,20 +5,28 @@ #ifndef WEBKIT_BROWSER_FILEAPI_SANDBOX_ISOLATED_ORIGIN_DATABASE_H_ #define WEBKIT_BROWSER_FILEAPI_SANDBOX_ISOLATED_ORIGIN_DATABASE_H_ +#include <string> +#include <vector> + #include "webkit/browser/fileapi/sandbox_origin_database_interface.h" namespace fileapi { class SandboxOriginDatabase; +// This origin database implementation supports only one origin +// (therefore is expected to run very fast). class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE SandboxIsolatedOriginDatabase : public SandboxOriginDatabaseInterface { public: - static const base::FilePath::CharType kOriginDirectory[]; + static const base::FilePath::CharType kObsoleteOriginDirectory[]; - explicit SandboxIsolatedOriginDatabase( + // Initialize this database for |origin| which makes GetPathForOrigin return + // |origin_directory| (in |file_system_directory|). + SandboxIsolatedOriginDatabase( const std::string& origin, - const base::FilePath& file_system_directory); + const base::FilePath& file_system_directory, + const base::FilePath& origin_directory); virtual ~SandboxIsolatedOriginDatabase(); // SandboxOriginDatabaseInterface overrides. @@ -29,17 +37,21 @@ class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE SandboxIsolatedOriginDatabase virtual bool ListAllOrigins(std::vector<OriginRecord>* origins) OVERRIDE; virtual void DropDatabase() OVERRIDE; - static void MigrateBackDatabase( + // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33. + static void MigrateBackFromObsoleteOriginDatabase( const std::string& origin, const base::FilePath& file_system_directory, SandboxOriginDatabase* origin_database); + const std::string& origin() const { return origin_; } + private: void MigrateDatabaseIfNeeded(); bool migration_checked_; const std::string origin_; const base::FilePath file_system_directory_; + const base::FilePath origin_directory_; DISALLOW_COPY_AND_ASSIGN(SandboxIsolatedOriginDatabase); }; diff --git a/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database_unittest.cc index aad2c7e6cc3..047a9612636 100644 --- a/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database_unittest.cc +++ b/chromium/webkit/browser/fileapi/sandbox_isolated_origin_database_unittest.cc @@ -11,12 +11,17 @@ namespace fileapi { +namespace { +const base::FilePath::CharType kOriginDirectory[] = FILE_PATH_LITERAL("iso"); +} // namespace + TEST(SandboxIsolatedOriginDatabaseTest, BasicTest) { base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); std::string kOrigin("origin"); - SandboxIsolatedOriginDatabase database(kOrigin, dir.path()); + SandboxIsolatedOriginDatabase database(kOrigin, dir.path(), + base::FilePath(kOriginDirectory)); EXPECT_TRUE(database.HasOriginPath(kOrigin)); @@ -33,55 +38,4 @@ TEST(SandboxIsolatedOriginDatabaseTest, BasicTest) { EXPECT_EQ(path1, path2); } -TEST(SandboxIsolatedOriginDatabaseTest, MigrationTest) { - base::ScopedTempDir dir; - ASSERT_TRUE(dir.CreateUniqueTempDir()); - - std::string kOrigin("origin"); - std::string kFakeDirectoryData("0123456789"); - base::FilePath path; - base::FilePath old_db_path; - - // Initialize the directory with one origin using the regular - // SandboxOriginDatabase. - { - SandboxOriginDatabase database_old(dir.path()); - old_db_path = database_old.GetDatabasePath(); - EXPECT_FALSE(base::PathExists(old_db_path)); - EXPECT_TRUE(database_old.GetPathForOrigin(kOrigin, &path)); - EXPECT_FALSE(path.empty()); - EXPECT_TRUE(base::DirectoryExists(old_db_path)); - - // Populate the origin directory with some fake data. - base::FilePath directory_db_path = dir.path().Append(path); - ASSERT_TRUE(file_util::CreateDirectory(directory_db_path)); - EXPECT_EQ(static_cast<int>(kFakeDirectoryData.size()), - file_util::WriteFile(directory_db_path.AppendASCII("dummy"), - kFakeDirectoryData.data(), - kFakeDirectoryData.size())); - } - - // Re-open the directory using sandboxIsolatedOriginDatabase. - SandboxIsolatedOriginDatabase database(kOrigin, dir.path()); - - // The database is migrated from the old one, so we should still - // see the same origin. - EXPECT_TRUE(database.HasOriginPath(kOrigin)); - EXPECT_TRUE(database.GetPathForOrigin(kOrigin, &path)); - EXPECT_FALSE(path.empty()); - - // The directory content must be kept (or migrated if necessary), - // so we should see the same fake data. - std::string origin_db_data; - base::FilePath directory_db_path = dir.path().Append(path); - EXPECT_TRUE(base::DirectoryExists(directory_db_path)); - EXPECT_TRUE(base::PathExists(directory_db_path.AppendASCII("dummy"))); - EXPECT_TRUE(base::ReadFileToString( - directory_db_path.AppendASCII("dummy"), &origin_db_data)); - EXPECT_EQ(kFakeDirectoryData, origin_db_data); - - // After the migration the database must be gone. - EXPECT_FALSE(base::PathExists(old_db_path)); -} - } // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_origin_database.cc b/chromium/webkit/browser/fileapi/sandbox_origin_database.cc index 4ce83017cc0..ee01d227914 100644 --- a/chromium/webkit/browser/fileapi/sandbox_origin_database.cc +++ b/chromium/webkit/browser/fileapi/sandbox_origin_database.cc @@ -111,7 +111,7 @@ bool SandboxOriginDatabase::Init(InitOption init_option, case DELETE_ON_CORRUPTION: if (!base::DeleteFile(file_system_directory_, true)) return false; - if (!file_util::CreateDirectory(file_system_directory_)) + if (!base::CreateDirectory(file_system_directory_)) return false; return Init(init_option, FAIL_ON_CORRUPTION); } diff --git a/chromium/webkit/browser/fileapi/sandbox_origin_database.h b/chromium/webkit/browser/fileapi/sandbox_origin_database.h index 94cde472ade..dd3b32c95c3 100644 --- a/chromium/webkit/browser/fileapi/sandbox_origin_database.h +++ b/chromium/webkit/browser/fileapi/sandbox_origin_database.h @@ -5,6 +5,9 @@ #ifndef WEBKIT_BROWSER_FILEAPI_SANDBOX_ORIGIN_DATABASE_H_ #define WEBKIT_BROWSER_FILEAPI_SANDBOX_ORIGIN_DATABASE_H_ +#include <string> +#include <vector> + #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "webkit/browser/fileapi/sandbox_origin_database_interface.h" diff --git a/chromium/webkit/browser/fileapi/sandbox_origin_database_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_origin_database_unittest.cc index 867dc273aad..340baf22409 100644 --- a/chromium/webkit/browser/fileapi/sandbox_origin_database_unittest.cc +++ b/chromium/webkit/browser/fileapi/sandbox_origin_database_unittest.cc @@ -6,6 +6,7 @@ #include <functional> #include <limits> #include <string> +#include <vector> #include "base/file_util.h" #include "base/files/file_path.h" @@ -33,7 +34,7 @@ TEST(SandboxOriginDatabaseTest, BasicTest) { ASSERT_TRUE(dir.CreateUniqueTempDir()); const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); SandboxOriginDatabase database(kFSDir); std::string origin("origin"); @@ -63,7 +64,7 @@ TEST(SandboxOriginDatabaseTest, TwoPathTest) { ASSERT_TRUE(dir.CreateUniqueTempDir()); const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); SandboxOriginDatabase database(kFSDir); std::string origin0("origin0"); @@ -91,7 +92,7 @@ TEST(SandboxOriginDatabaseTest, DropDatabaseTest) { ASSERT_TRUE(dir.CreateUniqueTempDir()); const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); SandboxOriginDatabase database(kFSDir); std::string origin("origin"); @@ -119,7 +120,7 @@ TEST(SandboxOriginDatabaseTest, DeleteOriginTest) { ASSERT_TRUE(dir.CreateUniqueTempDir()); const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); SandboxOriginDatabase database(kFSDir); std::string origin("origin"); @@ -146,7 +147,7 @@ TEST(SandboxOriginDatabaseTest, ListOriginsTest) { ASSERT_TRUE(dir.CreateUniqueTempDir()); const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); std::vector<SandboxOriginDatabase::OriginRecord> origins; @@ -196,7 +197,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryTest) { const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); const base::FilePath kDBDir = kFSDir.Append(kOriginDatabaseName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); const std::string kOrigins[] = { "foo.example.com", @@ -216,13 +217,13 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryTest) { EXPECT_TRUE(database->GetPathForOrigin(kOrigins[i], &path)); if (i != 1) - EXPECT_TRUE(file_util::CreateDirectory(kFSDir.Append(path))); + EXPECT_TRUE(base::CreateDirectory(kFSDir.Append(path))); } database.reset(); const base::FilePath kGarbageDir = kFSDir.AppendASCII("foo"); const base::FilePath kGarbageFile = kGarbageDir.AppendASCII("bar"); - EXPECT_TRUE(file_util::CreateDirectory(kGarbageDir)); + EXPECT_TRUE(base::CreateDirectory(kGarbageDir)); bool created = false; base::PlatformFileError error; base::PlatformFile file = base::CreatePlatformFile( @@ -276,7 +277,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryForMissingDBFileTest) { const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName); const base::FilePath kDBDir = kFSDir.Append(kOriginDatabaseName); EXPECT_FALSE(base::PathExists(kFSDir)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir)); + EXPECT_TRUE(base::CreateDirectory(kFSDir)); const std::string kOrigin = "foo.example.com"; base::FilePath path; @@ -287,7 +288,7 @@ TEST(SandboxOriginDatabaseTest, DatabaseRecoveryForMissingDBFileTest) { EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path)); EXPECT_FALSE(path.empty()); EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path)); - EXPECT_TRUE(file_util::CreateDirectory(kFSDir.Append(path))); + EXPECT_TRUE(base::CreateDirectory(kFSDir.Append(path))); database.reset(); DeleteDatabaseFile(kDBDir, kLevelDBFileTypes[i]); diff --git a/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.cc b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.cc new file mode 100644 index 00000000000..06a44a07080 --- /dev/null +++ b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.cc @@ -0,0 +1,229 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h" + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_platform_file_closer.h" +#include "base/logging.h" +#include "base/pickle.h" +#include "base/platform_file.h" +#include "webkit/browser/fileapi/sandbox_isolated_origin_database.h" +#include "webkit/browser/fileapi/sandbox_origin_database.h" + +namespace fileapi { + +namespace { + +const base::FilePath::CharType kPrimaryDirectory[] = + FILE_PATH_LITERAL("primary"); +const base::FilePath::CharType kPrimaryOriginFile[] = + FILE_PATH_LITERAL("primary.origin"); + +bool WritePrimaryOriginFile(const base::FilePath& path, + const std::string& origin) { + base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; + bool created; + base::PlatformFile file = base::CreatePlatformFile( + path, + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_WRITE, + &created, &error); + base::ScopedPlatformFileCloser closer(&file); + if (error != base::PLATFORM_FILE_OK || + file == base::kInvalidPlatformFileValue) + return false; + base::TruncatePlatformFile(file, 0); + Pickle pickle; + pickle.WriteString(origin); + base::WritePlatformFile(file, 0, static_cast<const char*>(pickle.data()), + pickle.size()); + base::FlushPlatformFile(file); + return true; +} + +bool ReadPrimaryOriginFile(const base::FilePath& path, + std::string* origin) { + std::string buffer; + if (!base::ReadFileToString(path, &buffer)) + return false; + Pickle pickle(buffer.data(), buffer.size()); + PickleIterator iter(pickle); + return pickle.ReadString(&iter, origin) && !origin->empty(); +} + +} // namespace + +SandboxPrioritizedOriginDatabase::SandboxPrioritizedOriginDatabase( + const base::FilePath& file_system_directory) + : file_system_directory_(file_system_directory), + primary_origin_file_( + file_system_directory_.Append(kPrimaryOriginFile)) { +} + +SandboxPrioritizedOriginDatabase::~SandboxPrioritizedOriginDatabase() { +} + +bool SandboxPrioritizedOriginDatabase::InitializePrimaryOrigin( + const std::string& origin) { + if (!primary_origin_database_) { + if (!MaybeLoadPrimaryOrigin() && ResetPrimaryOrigin(origin)) { + MaybeMigrateDatabase(origin); + primary_origin_database_.reset( + new SandboxIsolatedOriginDatabase( + origin, + file_system_directory_, + base::FilePath(kPrimaryDirectory))); + return true; + } + } + + if (primary_origin_database_) + return primary_origin_database_->HasOriginPath(origin); + + return false; +} + +std::string SandboxPrioritizedOriginDatabase::GetPrimaryOrigin() { + MaybeLoadPrimaryOrigin(); + if (primary_origin_database_) + return primary_origin_database_->origin(); + return std::string(); +} + +bool SandboxPrioritizedOriginDatabase::HasOriginPath( + const std::string& origin) { + MaybeInitializeDatabases(false); + if (primary_origin_database_ && + primary_origin_database_->HasOriginPath(origin)) + return true; + if (origin_database_) + return origin_database_->HasOriginPath(origin); + return false; +} + +bool SandboxPrioritizedOriginDatabase::GetPathForOrigin( + const std::string& origin, base::FilePath* directory) { + MaybeInitializeDatabases(true); + if (primary_origin_database_ && + primary_origin_database_->GetPathForOrigin(origin, directory)) + return true; + DCHECK(origin_database_); + return origin_database_->GetPathForOrigin(origin, directory); +} + +bool SandboxPrioritizedOriginDatabase::RemovePathForOrigin( + const std::string& origin) { + MaybeInitializeDatabases(false); + if (primary_origin_database_ && + primary_origin_database_->HasOriginPath(origin)) { + primary_origin_database_.reset(); + base::DeleteFile(file_system_directory_.Append(kPrimaryOriginFile), + true /* recursive */); + return true; + } + if (origin_database_) + return origin_database_->RemovePathForOrigin(origin); + return true; +} + +bool SandboxPrioritizedOriginDatabase::ListAllOrigins( + std::vector<OriginRecord>* origins) { + // SandboxOriginDatabase may clear the |origins|, so call this before + // primary_origin_database_. + MaybeInitializeDatabases(false); + if (origin_database_ && !origin_database_->ListAllOrigins(origins)) + return false; + if (primary_origin_database_) + return primary_origin_database_->ListAllOrigins(origins); + return true; +} + +void SandboxPrioritizedOriginDatabase::DropDatabase() { + primary_origin_database_.reset(); + origin_database_.reset(); +} + +bool SandboxPrioritizedOriginDatabase::MaybeLoadPrimaryOrigin() { + if (primary_origin_database_) + return true; + std::string saved_origin; + if (!ReadPrimaryOriginFile(primary_origin_file_, &saved_origin)) + return false; + primary_origin_database_.reset( + new SandboxIsolatedOriginDatabase( + saved_origin, + file_system_directory_, + base::FilePath(kPrimaryDirectory))); + return true; +} + +bool SandboxPrioritizedOriginDatabase::ResetPrimaryOrigin( + const std::string& origin) { + DCHECK(!primary_origin_database_); + if (!WritePrimaryOriginFile(primary_origin_file_, origin)) + return false; + // We reset the primary origin directory too. + // (This means the origin file corruption causes data loss + // We could keep the directory there as the same origin will likely + // become the primary origin, but let's play conservatively.) + base::DeleteFile(file_system_directory_.Append(kPrimaryDirectory), + true /* recursive */); + return true; +} + +void SandboxPrioritizedOriginDatabase::MaybeMigrateDatabase( + const std::string& origin) { + MaybeInitializeNonPrimaryDatabase(false); + if (!origin_database_) + return; + if (origin_database_->HasOriginPath(origin)) { + base::FilePath directory_name; + if (origin_database_->GetPathForOrigin(origin, &directory_name) && + directory_name != base::FilePath(kPrimaryOriginFile)) { + base::FilePath from_path = file_system_directory_.Append(directory_name); + base::FilePath to_path = file_system_directory_.Append(kPrimaryDirectory); + + if (base::PathExists(to_path)) + base::DeleteFile(to_path, true /* recursive */); + base::Move(from_path, to_path); + } + + origin_database_->RemovePathForOrigin(origin); + } + + std::vector<OriginRecord> origins; + origin_database_->ListAllOrigins(&origins); + if (origins.empty()) { + origin_database_->RemoveDatabase(); + origin_database_.reset(); + } +} + +void SandboxPrioritizedOriginDatabase::MaybeInitializeDatabases( + bool create) { + MaybeLoadPrimaryOrigin(); + MaybeInitializeNonPrimaryDatabase(create); +} + +void SandboxPrioritizedOriginDatabase::MaybeInitializeNonPrimaryDatabase( + bool create) { + if (origin_database_) + return; + + origin_database_.reset(new SandboxOriginDatabase(file_system_directory_)); + if (!create && !base::DirectoryExists(origin_database_->GetDatabasePath())) { + origin_database_.reset(); + return; + } +} + +SandboxOriginDatabase* +SandboxPrioritizedOriginDatabase::GetSandboxOriginDatabase() { + MaybeInitializeNonPrimaryDatabase(true); + return origin_database_.get(); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.h b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.h new file mode 100644 index 00000000000..5509d0f75be --- /dev/null +++ b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BROWSER_FILEAPI_SANDBOX_PRIORITIZED_ORIGIN_DATABASE_H_ +#define WEBKIT_BROWSER_FILEAPI_SANDBOX_PRIORITIZED_ORIGIN_DATABASE_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "webkit/browser/fileapi/sandbox_origin_database_interface.h" + +namespace fileapi { + +class ObfuscatedFileUtil; +class SandboxIsolatedOriginDatabase; +class SandboxOriginDatabase; + +class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE SandboxPrioritizedOriginDatabase + : public SandboxOriginDatabaseInterface { + public: + explicit SandboxPrioritizedOriginDatabase( + const base::FilePath& file_system_directory); + virtual ~SandboxPrioritizedOriginDatabase(); + + // Sets |origin| as primary origin in this database (e.g. may + // allow faster access). + // Returns false if this database already has a primary origin + // which is different from |origin|. + bool InitializePrimaryOrigin(const std::string& origin); + std::string GetPrimaryOrigin(); + + // SandboxOriginDatabaseInterface overrides. + virtual bool HasOriginPath(const std::string& origin) OVERRIDE; + virtual bool GetPathForOrigin(const std::string& origin, + base::FilePath* directory) OVERRIDE; + virtual bool RemovePathForOrigin(const std::string& origin) OVERRIDE; + virtual bool ListAllOrigins(std::vector<OriginRecord>* origins) OVERRIDE; + virtual void DropDatabase() OVERRIDE; + + const base::FilePath& primary_origin_file() const { + return primary_origin_file_; + } + + private: + bool MaybeLoadPrimaryOrigin(); + bool ResetPrimaryOrigin(const std::string& origin); + void MaybeMigrateDatabase(const std::string& origin); + void MaybeInitializeDatabases(bool create); + void MaybeInitializeNonPrimaryDatabase(bool create); + + // For migration. + friend class ObfuscatedFileUtil; + SandboxOriginDatabase* GetSandboxOriginDatabase(); + + const base::FilePath file_system_directory_; + const base::FilePath primary_origin_file_; + scoped_ptr<SandboxOriginDatabase> origin_database_; + scoped_ptr<SandboxIsolatedOriginDatabase> primary_origin_database_; + + DISALLOW_COPY_AND_ASSIGN(SandboxPrioritizedOriginDatabase); +}; + +} // namespace fileapi + +#endif // WEBKIT_BROWSER_FILEAPI_SANDBOX_PRIORITIZED_ORIGIN_DATABASE_H_ diff --git a/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc new file mode 100644 index 00000000000..fc0b9589d85 --- /dev/null +++ b/chromium/webkit/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc @@ -0,0 +1,215 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/platform_file.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/sandbox_origin_database.h" +#include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h" + +namespace fileapi { + +TEST(SandboxPrioritizedOriginDatabaseTest, BasicTest) { + base::ScopedTempDir dir; + base::FilePath path; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + + const std::string kOrigin1("origin1"); + const std::string kOrigin2("origin2"); + + SandboxPrioritizedOriginDatabase database(dir.path()); + + // Set the kOrigin1 as a parimary origin. + EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1)); + + // Add two origins. + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path)); + + // Verify them. + EXPECT_TRUE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + EXPECT_FALSE(path.empty()); + EXPECT_TRUE(database.HasOriginPath(kOrigin2)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path)); + EXPECT_FALSE(path.empty()); + + std::vector<SandboxOriginDatabaseInterface::OriginRecord> origins; + database.ListAllOrigins(&origins); + ASSERT_EQ(2U, origins.size()); + EXPECT_TRUE(origins[0].origin == kOrigin1 || + origins[1].origin == kOrigin1); + EXPECT_TRUE(origins[0].origin == kOrigin2 || + origins[1].origin == kOrigin2); + EXPECT_NE(origins[0].path, origins[1].path); + + // Try remove path for kOrigin1. + database.RemovePathForOrigin(kOrigin1); + + // Verify the removal. + EXPECT_FALSE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.HasOriginPath(kOrigin2)); + database.ListAllOrigins(&origins); + ASSERT_EQ(1U, origins.size()); + EXPECT_EQ(kOrigin2, origins[0].origin); + + // Try remove path for kOrigin2. + database.RemovePathForOrigin(kOrigin2); + + // Verify the removal. + EXPECT_FALSE(database.HasOriginPath(kOrigin1)); + EXPECT_FALSE(database.HasOriginPath(kOrigin2)); + database.ListAllOrigins(&origins); + EXPECT_TRUE(origins.empty()); +} + +TEST(SandboxPrioritizedOriginDatabaseTest, SetPrimaryLaterTest) { + base::ScopedTempDir dir; + base::FilePath path; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + + const std::string kOrigin1("origin1"); + const std::string kOrigin2("origin2"); + + SandboxPrioritizedOriginDatabase database(dir.path()); + + EXPECT_TRUE(database.GetPrimaryOrigin().empty()); + + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path)); + + // Set the kOrigin1 as a parimary origin. + EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1)); + EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin()); + + // Regardless of whether it is initialized as primary or not + // they should just work. + EXPECT_TRUE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + EXPECT_FALSE(path.empty()); + EXPECT_TRUE(database.HasOriginPath(kOrigin2)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path)); + EXPECT_FALSE(path.empty()); +} + +TEST(SandboxPrioritizedOriginDatabaseTest, LostPrimaryOriginFileTest) { + base::ScopedTempDir dir; + base::FilePath path; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + + const std::string kOrigin1("origin1"); + const std::string kData("foo"); + + SandboxPrioritizedOriginDatabase database(dir.path()); + + EXPECT_TRUE(database.GetPrimaryOrigin().empty()); + + // Set the kOrigin1 as a parimary origin. + EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1)); + EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin()); + + // Make sure it works. + EXPECT_TRUE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + + // Reset the database. + database.DropDatabase(); + + // kOrigin1 should still be marked as primary. + EXPECT_TRUE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path)); + + // Corrupt the primary origin file. + file_util::WriteFile( + database.primary_origin_file(), kData.data(), kData.size()); + + // Reset the database. + database.DropDatabase(); + + // kOrigin1 is no longer marked as primary, and unfortunately we fail + // to find the data for the origin. + EXPECT_FALSE(database.HasOriginPath(kOrigin1)); +} + +TEST(SandboxPrioritizedOriginDatabaseTest, MigrationTest) { + base::ScopedTempDir dir; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + + const std::string kOrigin1("origin1"); + const std::string kOrigin2("origin2"); + const std::string kFakeDirectoryData1("0123456789"); + const std::string kFakeDirectoryData2("abcde"); + base::FilePath old_dir_db_path1, old_dir_db_path2; + base::FilePath path1, path2; + + // Initialize the directory with two origins using the regular + // SandboxOriginDatabase. + { + SandboxOriginDatabase database_old(dir.path()); + base::FilePath old_db_path = database_old.GetDatabasePath(); + EXPECT_FALSE(base::PathExists(old_db_path)); + + // Initialize paths for kOrigin1 and kOrigin2. + EXPECT_TRUE(database_old.GetPathForOrigin(kOrigin1, &path1)); + EXPECT_FALSE(path1.empty()); + EXPECT_TRUE(database_old.GetPathForOrigin(kOrigin2, &path2)); + EXPECT_FALSE(path2.empty()); + + EXPECT_TRUE(base::DirectoryExists(old_db_path)); + + // Populate the origin directory with some fake data. + old_dir_db_path1 = dir.path().Append(path1); + ASSERT_TRUE(base::CreateDirectory(old_dir_db_path1)); + EXPECT_EQ(static_cast<int>(kFakeDirectoryData1.size()), + file_util::WriteFile(old_dir_db_path1.AppendASCII("dummy"), + kFakeDirectoryData1.data(), + kFakeDirectoryData1.size())); + old_dir_db_path2 = dir.path().Append(path2); + ASSERT_TRUE(base::CreateDirectory(old_dir_db_path2)); + EXPECT_EQ(static_cast<int>(kFakeDirectoryData2.size()), + file_util::WriteFile(old_dir_db_path2.AppendASCII("dummy"), + kFakeDirectoryData2.data(), + kFakeDirectoryData2.size())); + } + + // Re-open the directory using sandboxPrioritizedOriginDatabase. + SandboxPrioritizedOriginDatabase database(dir.path()); + + // Set the kOrigin1 as a parimary origin. + // (Trying to initialize another origin should fail). + EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1)); + EXPECT_FALSE(database.InitializePrimaryOrigin(kOrigin2)); + + EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin()); + + // Regardless of whether the origin is registered as primary or not + // it should just work. + EXPECT_TRUE(database.HasOriginPath(kOrigin1)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path1)); + EXPECT_TRUE(database.HasOriginPath(kOrigin2)); + EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path2)); + + // The directory content must be kept (or migrated if necessary) as well. + std::string origin_db_data; + base::FilePath dir_db_path = dir.path().Append(path1); + EXPECT_TRUE(base::PathExists(dir_db_path.AppendASCII("dummy"))); + EXPECT_TRUE(base::ReadFileToString( + dir_db_path.AppendASCII("dummy"), &origin_db_data)); + EXPECT_EQ(kFakeDirectoryData1, origin_db_data); + + origin_db_data.clear(); + dir_db_path = dir.path().Append(path2); + EXPECT_TRUE(base::PathExists(dir_db_path.AppendASCII("dummy"))); + EXPECT_TRUE(base::ReadFileToString( + dir_db_path.AppendASCII("dummy"), &origin_db_data)); + EXPECT_EQ(kFakeDirectoryData2, origin_db_data); + + // After the migration the kOrigin1 directory database path must be gone. + EXPECT_FALSE(base::PathExists(old_dir_db_path1)); + EXPECT_TRUE(base::PathExists(old_dir_db_path2)); +} + +} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/test_file_set.cc b/chromium/webkit/browser/fileapi/test_file_set.cc index 7b0b0f0a7b7..109a0558491 100644 --- a/chromium/webkit/browser/fileapi/test_file_set.cc +++ b/chromium/webkit/browser/fileapi/test_file_set.cc @@ -43,7 +43,7 @@ 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)); + ASSERT_TRUE(base::CreateDirectory(path)); return; } base::PlatformFileError error_code; diff --git a/chromium/webkit/browser/fileapi/test_file_set.h b/chromium/webkit/browser/fileapi/test_file_set.h index 59e90ab6e0a..eb44521357a 100644 --- a/chromium/webkit/browser/fileapi/test_file_set.h +++ b/chromium/webkit/browser/fileapi/test_file_set.h @@ -29,7 +29,8 @@ 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); +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); diff --git a/chromium/webkit/browser/fileapi/test_file_system_backend.cc b/chromium/webkit/browser/fileapi/test_file_system_backend.cc deleted file mode 100644 index 44109cf6148..00000000000 --- a/chromium/webkit/browser/fileapi/test_file_system_backend.cc +++ /dev/null @@ -1,247 +0,0 @@ -// 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_system_backend.h" - -#include <set> -#include <string> -#include <vector> - -#include "base/file_util.h" -#include "base/sequenced_task_runner.h" -#include "webkit/browser/blob/file_stream_reader.h" -#include "webkit/browser/fileapi/copy_or_move_file_validator.h" -#include "webkit/browser/fileapi/file_observers.h" -#include "webkit/browser/fileapi/file_system_operation.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_util.h" -#include "webkit/browser/fileapi/native_file_util.h" -#include "webkit/browser/fileapi/sandbox_file_stream_writer.h" -#include "webkit/browser/quota/quota_manager.h" -#include "webkit/common/fileapi/file_system_util.h" - -namespace fileapi { - -namespace { - -class TestFileUtil : public LocalFileUtil { - public: - explicit TestFileUtil(const base::FilePath& base_path) - : base_path_(base_path) {} - virtual ~TestFileUtil() {} - - // LocalFileUtil overrides. - virtual base::PlatformFileError GetLocalFilePath( - FileSystemOperationContext* context, - const FileSystemURL& file_system_url, - base::FilePath* local_file_path) OVERRIDE { - *local_file_path = base_path_.Append(file_system_url.path()); - return base::PLATFORM_FILE_OK; - } - - private: - base::FilePath base_path_; -}; - -} // namespace - -// This only supports single origin. -class TestFileSystemBackend::QuotaUtil - : public FileSystemQuotaUtil, - public FileUpdateObserver { - public: - QuotaUtil(base::SequencedTaskRunner* task_runner) - : usage_(0), - task_runner_(task_runner) { - update_observers_ = update_observers_.AddObserver(this, task_runner_.get()); - } - virtual ~QuotaUtil() {} - - // FileSystemQuotaUtil overrides. - virtual base::PlatformFileError DeleteOriginDataOnFileThread( - FileSystemContext* context, - quota::QuotaManagerProxy* proxy, - const GURL& origin_url, - FileSystemType type) OVERRIDE { - NOTREACHED(); - return base::PLATFORM_FILE_OK; - } - - 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 AddFileUpdateObserver( - FileSystemType type, - FileUpdateObserver* observer, - base::SequencedTaskRunner* task_runner) OVERRIDE { - NOTIMPLEMENTED(); - } - - virtual void AddFileChangeObserver( - FileSystemType type, - FileChangeObserver* observer, - base::SequencedTaskRunner* task_runner) OVERRIDE { - change_observers_ = change_observers_.AddObserver(observer, task_runner); - } - - virtual void AddFileAccessObserver( - FileSystemType type, - FileAccessObserver* observer, - base::SequencedTaskRunner* task_runner) OVERRIDE { - NOTIMPLEMENTED(); - } - - virtual const UpdateObserverList* GetUpdateObservers( - FileSystemType type) const OVERRIDE { - return &update_observers_; - } - - virtual const ChangeObserverList* GetChangeObservers( - FileSystemType type) const OVERRIDE { - return &change_observers_; - } - - virtual const AccessObserverList* GetAccessObservers( - FileSystemType type) const OVERRIDE { - NOTIMPLEMENTED(); - return NULL; - } - - // 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 {} - - base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } - - private: - int64 usage_; - - scoped_refptr<base::SequencedTaskRunner> task_runner_; - - UpdateObserverList update_observers_; - ChangeObserverList change_observers_; -}; - -TestFileSystemBackend::TestFileSystemBackend( - base::SequencedTaskRunner* task_runner, - const base::FilePath& base_path) - : base_path_(base_path), - file_util_(new AsyncFileUtilAdapter(new TestFileUtil(base_path))), - quota_util_(new QuotaUtil(task_runner)), - require_copy_or_move_validator_(false) { -} - -TestFileSystemBackend::~TestFileSystemBackend() { -} - -bool TestFileSystemBackend::CanHandleType(FileSystemType type) const { - return (type == kFileSystemTypeTest); -} - -void TestFileSystemBackend::Initialize(FileSystemContext* context) { -} - -void TestFileSystemBackend::OpenFileSystem( - const GURL& origin_url, - FileSystemType type, - OpenFileSystemMode mode, - const OpenFileSystemCallback& callback) { - callback.Run(GetFileSystemRootURI(origin_url, type), - GetFileSystemName(origin_url, type), - base::PLATFORM_FILE_OK); -} - -AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil(FileSystemType type) { - return file_util_.get(); -} - -CopyOrMoveFileValidatorFactory* -TestFileSystemBackend::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 TestFileSystemBackend::InitializeCopyOrMoveFileValidatorFactory( - scoped_ptr<CopyOrMoveFileValidatorFactory> factory) { - if (!copy_or_move_file_validator_factory_) - copy_or_move_file_validator_factory_ = factory.Pass(); -} - -FileSystemOperation* TestFileSystemBackend::CreateFileSystemOperation( - const FileSystemURL& url, - FileSystemContext* context, - base::PlatformFileError* error_code) const { - scoped_ptr<FileSystemOperationContext> operation_context( - new FileSystemOperationContext(context)); - operation_context->set_update_observers(*GetUpdateObservers(url.type())); - operation_context->set_change_observers( - *quota_util_->GetChangeObservers(url.type())); - return FileSystemOperation::Create(url, context, operation_context.Pass()); -} - -scoped_ptr<webkit_blob::FileStreamReader> -TestFileSystemBackend::CreateFileStreamReader( - const FileSystemURL& url, - int64 offset, - const base::Time& expected_modification_time, - FileSystemContext* context) const { - return scoped_ptr<webkit_blob::FileStreamReader>( - webkit_blob::FileStreamReader::CreateForFileSystemFile( - context, url, offset, expected_modification_time)); -} - -scoped_ptr<fileapi::FileStreamWriter> -TestFileSystemBackend::CreateFileStreamWriter( - const FileSystemURL& url, - int64 offset, - FileSystemContext* context) const { - return scoped_ptr<fileapi::FileStreamWriter>( - new SandboxFileStreamWriter(context, url, offset, - *GetUpdateObservers(url.type()))); -} - -FileSystemQuotaUtil* TestFileSystemBackend::GetQuotaUtil() { - return quota_util_.get(); -} - -const UpdateObserverList* TestFileSystemBackend::GetUpdateObservers( - FileSystemType type) const { - return quota_util_->GetUpdateObservers(type); -} - -void TestFileSystemBackend::AddFileChangeObserver( - FileChangeObserver* observer) { - quota_util_->AddFileChangeObserver( - kFileSystemTypeTest, observer, quota_util_->task_runner()); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/test_file_system_backend.h b/chromium/webkit/browser/fileapi/test_file_system_backend.h deleted file mode 100644 index dc1cbce2298..00000000000 --- a/chromium/webkit/browser/fileapi/test_file_system_backend.h +++ /dev/null @@ -1,95 +0,0 @@ -// 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_SYSTEM_BACKEND_H_ -#define WEBKIT_BROWSER_FILEAPI_TEST_FILE_SYSTEM_BACKEND_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_backend.h" -#include "webkit/browser/fileapi/task_runner_bound_observer_list.h" -#include "webkit/browser/webkit_storage_browser_export.h" - -namespace base { -class SequencedTaskRunner; -} - -namespace fileapi { - -class AsyncFileUtilAdapter; -class FileSystemQuotaUtil; - -// This should be only used for testing. -// This file system backend uses LocalFileUtil and stores data file -// under the given directory. -class WEBKIT_STORAGE_BROWSER_EXPORT_PRIVATE TestFileSystemBackend - : public FileSystemBackend { - public: - TestFileSystemBackend( - base::SequencedTaskRunner* task_runner, - const base::FilePath& base_path); - virtual ~TestFileSystemBackend(); - - // FileSystemBackend implementation. - virtual bool CanHandleType(FileSystemType type) const OVERRIDE; - virtual void Initialize(FileSystemContext* context) OVERRIDE; - virtual void OpenFileSystem( - const GURL& origin_url, - FileSystemType type, - OpenFileSystemMode mode, - const OpenFileSystemCallback& callback) OVERRIDE; - virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; - virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( - FileSystemType type, - base::PlatformFileError* error_code) 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; - - // Initialize the CopyOrMoveFileValidatorFactory. Invalid to call more than - // once. - void InitializeCopyOrMoveFileValidatorFactory( - scoped_ptr<CopyOrMoveFileValidatorFactory> factory); - - const UpdateObserverList* GetUpdateObservers(FileSystemType type) const; - void AddFileChangeObserver(FileChangeObserver* observer); - - // 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> file_util_; - scoped_ptr<QuotaUtil> quota_util_; - - bool require_copy_or_move_validator_; - scoped_ptr<CopyOrMoveFileValidatorFactory> - copy_or_move_file_validator_factory_; - - DISALLOW_COPY_AND_ASSIGN(TestFileSystemBackend); -}; - -} // namespace fileapi - -#endif // WEBKIT_BROWSER_FILEAPI_TEST_FILE_SYSTEM_BACKEND_H_ diff --git a/chromium/webkit/browser/fileapi/transient_file_util_unittest.cc b/chromium/webkit/browser/fileapi/transient_file_util_unittest.cc deleted file mode 100644 index 3b6e284070d..00000000000 --- a/chromium/webkit/browser/fileapi/transient_file_util_unittest.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" -#include "base/platform_file.h" -#include "base/run_loop.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/isolated_context.h" -#include "webkit/browser/fileapi/mock_file_system_context.h" -#include "webkit/browser/fileapi/transient_file_util.h" -#include "webkit/common/blob/scoped_file.h" - -namespace fileapi { - -class TransientFileUtilTest : public testing::Test { - public: - TransientFileUtilTest() {} - virtual ~TransientFileUtilTest() {} - - virtual void SetUp() OVERRIDE { - file_system_context_ = CreateFileSystemContextForTesting( - NULL, base::FilePath(FILE_PATH_LITERAL("dummy"))); - transient_file_util_.reset(new TransientFileUtil); - - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - } - - virtual void TearDown() OVERRIDE { - file_system_context_ = NULL; - base::RunLoop().RunUntilIdle(); - } - - void CreateAndRegisterTemporaryFile( - FileSystemURL* file_url, - base::FilePath* file_path) { - EXPECT_TRUE( - file_util::CreateTemporaryFileInDir(data_dir_.path(), file_path)); - IsolatedContext* isolated_context = IsolatedContext::GetInstance(); - std::string name = "tmp"; - std::string fsid = isolated_context->RegisterFileSystemForPath( - kFileSystemTypeForTransientFile, - *file_path, - &name); - ASSERT_TRUE(!fsid.empty()); - base::FilePath virtual_path = isolated_context->CreateVirtualRootPath( - fsid).AppendASCII(name); - *file_url = file_system_context_->CreateCrackedFileSystemURL( - GURL("http://foo"), - kFileSystemTypeIsolated, - virtual_path); - } - - scoped_ptr<FileSystemOperationContext> NewOperationContext() { - return make_scoped_ptr( - new FileSystemOperationContext(file_system_context_.get())); - } - - FileSystemFileUtil* file_util() { return transient_file_util_.get(); } - - private: - base::MessageLoop message_loop_; - base::ScopedTempDir data_dir_; - scoped_refptr<FileSystemContext> file_system_context_; - scoped_ptr<TransientFileUtil> transient_file_util_; - - DISALLOW_COPY_AND_ASSIGN(TransientFileUtilTest); -}; - -TEST_F(TransientFileUtilTest, TransientFile) { - FileSystemURL temp_url; - base::FilePath temp_path; - - CreateAndRegisterTemporaryFile(&temp_url, &temp_path); - - base::PlatformFileError error; - base::PlatformFileInfo file_info; - base::FilePath path; - - // Make sure the file is there. - ASSERT_TRUE(temp_url.is_valid()); - ASSERT_TRUE(base::PathExists(temp_path)); - ASSERT_FALSE(base::DirectoryExists(temp_path)); - - // Create a snapshot file. - { - webkit_blob::ScopedFile scoped_file = - file_util()->CreateSnapshotFile(NewOperationContext().get(), - temp_url, - &error, - &file_info, - &path); - ASSERT_EQ(base::PLATFORM_FILE_OK, error); - ASSERT_EQ(temp_path, path); - ASSERT_FALSE(file_info.is_directory); - - // The file should be still there. - ASSERT_TRUE(base::PathExists(temp_path)); - ASSERT_EQ(base::PLATFORM_FILE_OK, - file_util()->GetFileInfo(NewOperationContext().get(), - temp_url, &file_info, &path)); - ASSERT_EQ(temp_path, path); - ASSERT_FALSE(file_info.is_directory); - } - - // The file's now scoped out. - base::RunLoop().RunUntilIdle(); - - // Now the temporary file and the transient filesystem must be gone too. - ASSERT_FALSE(base::PathExists(temp_path)); - ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, - file_util()->GetFileInfo(NewOperationContext().get(), - temp_url, &file_info, &path)); -} - -} // namespace fileapi diff --git a/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader.cc b/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader.cc index 3fb5ab4d31e..46a57f0957a 100644 --- a/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader.cc +++ b/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader.cc @@ -4,6 +4,8 @@ #include "webkit/browser/fileapi/upload_file_system_file_element_reader.h" +#include <algorithm> + #include "base/bind.h" #include "net/base/net_errors.h" #include "webkit/browser/blob/file_stream_reader.h" diff --git a/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc b/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc deleted file mode 100644 index 429edf2fe05..00000000000 --- a/chromium/webkit/browser/fileapi/upload_file_system_file_element_reader_unittest.cc +++ /dev/null @@ -1,278 +0,0 @@ -// 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/run_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/async_file_test_helper.h" -#include "webkit/browser/fileapi/file_system_backend.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/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, - OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, - base::Bind(&UploadFileSystemFileElementReaderTest::OnOpenFileSystem, - base::Unretained(this))); - base::RunLoop().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_.get(), - 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()); - } - - virtual void TearDown() OVERRIDE { - reader_.reset(); - base::RunLoop().RunUntilIdle(); - } - - 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)); - - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::CreateFileWithData( - file_system_context_, url, buf, buf_size)); - - base::PlatformFileInfo file_info; - ASSERT_EQ(base::PLATFORM_FILE_OK, - AsyncFileTestHelper::GetMetadata( - file_system_context_, url, &file_info)); - *modification_time = file_info.last_modified; - } - - void OnOpenFileSystem(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.get(), 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.get(), 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.get(), 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.get(), 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.get(), 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.get(), 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.get(), 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.get(), 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.get(), 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_.get(), 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.get(), 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_.get(), - 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_.get(), 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 |