diff options
author | mtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-09 09:40:33 +0000 |
---|---|---|
committer | mtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-09 09:40:33 +0000 |
commit | c90fcaf6517ef913996510df9836bcfff7755d47 (patch) | |
tree | 5d8fe5f82d262a6412e6af18a41642c7e2a4d0de | |
parent | d7bd6563eba1a5575bb6a12d985dace47c57423f (diff) | |
download | chromium_src-c90fcaf6517ef913996510df9836bcfff7755d47.zip chromium_src-c90fcaf6517ef913996510df9836bcfff7755d47.tar.gz chromium_src-c90fcaf6517ef913996510df9836bcfff7755d47.tar.bz2 |
[fsp] Add support for reading directories.
This patch adds support to list contents of directories.
TBR=isherman@chromium.org
TEST=unit_tests, browser_test: *FileSystemProvider*ReadDirectory
BUG=248427
Review URL: https://codereview.chromium.org/246913003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269201 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 928 insertions, 13 deletions
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc index d37f23c..d080d52 100644 --- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc +++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc @@ -6,6 +6,7 @@ #include <string> +#include "base/memory/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" #include "chrome/browser/chromeos/file_system_provider/request_manager.h" @@ -125,4 +126,27 @@ FileSystemProviderInternalGetMetadataRequestedErrorFunction::RunWhenValid() { return true; } +bool FileSystemProviderInternalReadDirectoryRequestedSuccessFunction:: + RunWhenValid() { + using api::file_system_provider_internal::ReadDirectoryRequestedSuccess:: + Params; + scoped_ptr<Params> params(Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + const bool has_next = params->has_next; + FulfillRequest(RequestValue::CreateForReadDirectorySuccess(params.Pass()), + has_next); + return true; +} + +bool +FileSystemProviderInternalReadDirectoryRequestedErrorFunction::RunWhenValid() { + using api::file_system_provider_internal::ReadDirectoryRequestedError::Params; + const scoped_ptr<Params> params(Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + RejectRequest(ProviderErrorToFileError(params->error)); + return true; +} + } // namespace extensions diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h index b762701..21102a6b 100644 --- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h +++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h @@ -78,6 +78,30 @@ class FileSystemProviderInternalGetMetadataRequestedErrorFunction virtual bool RunWhenValid() OVERRIDE; }; +class FileSystemProviderInternalReadDirectoryRequestedSuccessFunction + : public FileSystemProviderInternalFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "fileSystemProviderInternal.readDirectoryRequestedSuccess", + FILESYSTEMPROVIDERINTERNAL_READDIRECTORYREQUESTEDSUCCESS) + + protected: + virtual ~FileSystemProviderInternalReadDirectoryRequestedSuccessFunction() {} + virtual bool RunWhenValid() OVERRIDE; +}; + +class FileSystemProviderInternalReadDirectoryRequestedErrorFunction + : public FileSystemProviderInternalFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "fileSystemProviderInternal.readDirectoryRequestedError", + FILESYSTEMPROVIDERINTERNAL_READDIRECTORYREQUESTEDERROR) + + protected: + virtual ~FileSystemProviderInternalReadDirectoryRequestedErrorFunction() {} + virtual bool RunWhenValid() OVERRIDE; +}; + } // namespace extensions #endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_FILE_SYSTEM_PROVIDER_API_H_ diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc index bef565d..db78129 100644 --- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc +++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc @@ -35,4 +35,10 @@ IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, GetMetadata) { << message_; } +IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, ReadDirectory) { + ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/read_directory", + kFlagLoadAsComponent)) + << message_; +} + } // namespace extensions diff --git a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h index ed689c2c..a035f8c 100644 --- a/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h +++ b/chrome/browser/chromeos/extensions/file_system_provider/provider_function.h @@ -73,10 +73,10 @@ class FileSystemProviderInternalFunction : public ChromeSyncExtensionFunction { // |request_manager_| have been fully initialized. virtual bool RunWhenValid() = 0; - private: // ChromeSyncExtensionFunction overrides. virtual bool RunSync() OVERRIDE; + private: // Parses the request in order to extract the request manager. If fails, then // sets a response and returns false. bool Parse(); diff --git a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc index 8e54dd0..f27aeb0 100644 --- a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc +++ b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc @@ -4,12 +4,31 @@ #include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h" +#include <string> + #include "base/files/file.h" #include "base/message_loop/message_loop_proxy.h" #include "extensions/browser/event_router.h" namespace chromeos { namespace file_system_provider { +namespace { + +// Adds a fake entry to the entry list. +void AddDirectoryEntry(fileapi::AsyncFileUtil::EntryList* entry_list, + const std::string& name, + fileapi::DirectoryEntry::DirectoryEntryType type, + int64 size, + std::string last_modified_time_string) { + base::Time last_modified_time; + const bool result = base::Time::FromString(last_modified_time_string.c_str(), + &last_modified_time); + DCHECK(result); + entry_list->push_back( + fileapi::DirectoryEntry(name, type, size, last_modified_time)); +} + +} // namespace FakeProvidedFileSystem::FakeProvidedFileSystem( const ProvidedFileSystemInfo& file_system_info) @@ -50,6 +69,55 @@ void FakeProvidedFileSystem::GetMetadata( FROM_HERE, base::Bind(callback, base::File::FILE_OK, file_info)); } +void FakeProvidedFileSystem::ReadDirectory( + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) { + // Return fake contents for the root directory only. + if (directory_path.AsUTF8Unsafe() != "/") { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(callback, + base::File::FILE_ERROR_NOT_FOUND, + fileapi::AsyncFileUtil::EntryList(), + false /* has_more */)); + return; + } + + { + fileapi::AsyncFileUtil::EntryList entry_list; + AddDirectoryEntry(&entry_list, + "hello.txt", + fileapi::DirectoryEntry::FILE, + 1024 /* size */, + "Thu Apr 24 00:46:52 UTC 2014"); + + AddDirectoryEntry(&entry_list, + "world.txt", + fileapi::DirectoryEntry::FILE, + 1024 /* size */, + "Wed Apr 23 00:20:30 UTC 2014"); + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind( + callback, base::File::FILE_OK, entry_list, true /* has_more */)); + } + + { + fileapi::AsyncFileUtil::EntryList entry_list; + AddDirectoryEntry(&entry_list, + "pictures", + fileapi::DirectoryEntry::DIRECTORY, + 0 /* size */, + "Tue May 22 00:40:50 UTC 2014"); + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind( + callback, base::File::FILE_OK, entry_list, false /* has_more */)); + } +} + const ProvidedFileSystemInfo& FakeProvidedFileSystem::GetFileSystemInfo() const { return file_system_info_; diff --git a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h index 0d37332..47dcdcf 100644 --- a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h +++ b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h @@ -31,6 +31,9 @@ class FakeProvidedFileSystem : public ProvidedFileSystemInterface { virtual void GetMetadata( const base::FilePath& entry_path, const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) OVERRIDE; + virtual void ReadDirectory( + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) OVERRIDE; virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE; virtual RequestManager* GetRequestManager() OVERRIDE; diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc index 7dce18f..cf41cbc 100644 --- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc +++ b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc @@ -45,6 +45,33 @@ void OnGetFileInfo(const fileapi::AsyncFileUtil::GetFileInfoCallback& callback, BrowserThread::IO, FROM_HERE, base::Bind(callback, result, file_info)); } +// Executes ReadDirectory on the UI thread. +void ReadDirectoryOnUIThread( + scoped_ptr<fileapi::FileSystemOperationContext> context, + const fileapi::FileSystemURL& url, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) { + util::FileSystemURLParser parser(url); + if (!parser.Parse()) { + callback.Run(base::File::FILE_ERROR_NOT_FOUND, + fileapi::AsyncFileUtil::EntryList(), + false /* has_more */); + return; + } + + parser.file_system()->ReadDirectory(parser.file_path(), callback); +} + +// Routes the response of ReadDirectory back to the IO thread. +void OnReadDirectory( + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback, + base::File::Error result, + const fileapi::AsyncFileUtil::EntryList& entry_list, + bool has_more) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(callback, result, entry_list, has_more)); +} + } // namespace ProviderAsyncFileUtil::ProviderAsyncFileUtil() {} @@ -110,8 +137,12 @@ void ProviderAsyncFileUtil::ReadDirectory( const fileapi::FileSystemURL& url, const ReadDirectoryCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - NOTIMPLEMENTED(); - callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), false); + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&ReadDirectoryOnUIThread, + base::Passed(&context), + url, + base::Bind(&OnReadDirectory, callback))); } void ProviderAsyncFileUtil::Touch( diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc index 2fe8f48..a02ce1c 100644 --- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc +++ b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc @@ -291,9 +291,10 @@ TEST_F(FileSystemProviderProviderAsyncFileUtilTest, ReadDirectory) { CreateOperationContext(), root_url_, base::Bind(&EventLogger::OnReadDirectory, logger.GetWeakPtr())); + base::RunLoop().RunUntilIdle(); ASSERT_TRUE(logger.error()); - EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, *logger.error()); + EXPECT_EQ(base::File::FILE_OK, *logger.error()); } TEST_F(FileSystemProviderProviderAsyncFileUtilTest, Touch) { diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory.cc b/chrome/browser/chromeos/file_system_provider/operations/read_directory.cc new file mode 100644 index 0000000..48437f5 --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/read_directory.cc @@ -0,0 +1,94 @@ +// Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/operations/read_directory.h" + +#include <string> + +#include "base/memory/linked_ptr.h" +#include "chrome/common/extensions/api/file_system_provider.h" +#include "chrome/common/extensions/api/file_system_provider_internal.h" + +namespace chromeos { +namespace file_system_provider { +namespace operations { +namespace { + +// Convert |input| into |output|. If parsing fails, then returns false. +bool ConvertRequestValueToEntryList(scoped_ptr<RequestValue> value, + fileapi::AsyncFileUtil::EntryList* output) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params; + + const Params* params = value->read_directory_success_params(); + if (!params) + return false; + + for (size_t i = 0; i < params->entries.size(); ++i) { + const linked_ptr<EntryMetadata> entry_metadata = params->entries[i]; + + fileapi::DirectoryEntry output_entry; + output_entry.is_directory = entry_metadata->is_directory; + output_entry.name = entry_metadata->name; + output_entry.size = static_cast<int64>(entry_metadata->size); + + std::string input_modification_time; + if (!entry_metadata->modification_time.additional_properties.GetString( + "value", &input_modification_time)) { + return false; + } + if (!base::Time::FromString(input_modification_time.c_str(), + &output_entry.last_modified_time)) { + return false; + } + + output->push_back(output_entry); + } + + return true; +} + +} // namespace + +ReadDirectory::ReadDirectory( + extensions::EventRouter* event_router, + const ProvidedFileSystemInfo& file_system_info, + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) + : Operation(event_router, file_system_info), + directory_path_(directory_path), + callback_(callback) { +} + +ReadDirectory::~ReadDirectory() { +} + +bool ReadDirectory::Execute(int request_id) { + scoped_ptr<base::ListValue> values(new base::ListValue); + values->AppendString(directory_path_.AsUTF8Unsafe()); + return SendEvent(request_id, + extensions::api::file_system_provider:: + OnReadDirectoryRequested::kEventName, + values.Pass()); +} + +void ReadDirectory::OnSuccess(int /* request_id */, + scoped_ptr<RequestValue> result, + bool has_next) { + fileapi::AsyncFileUtil::EntryList entry_list; + const bool convert_result = + ConvertRequestValueToEntryList(result.Pass(), &entry_list); + DCHECK(convert_result); + callback_.Run(base::File::FILE_OK, entry_list, has_next); +} + +void ReadDirectory::OnError(int /* request_id */, base::File::Error error) { + callback_.Run( + error, fileapi::AsyncFileUtil::EntryList(), false /* has_next */); +} + +} // namespace operations +} // namespace file_system_provider +} // namespace chromeos diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory.h b/chrome/browser/chromeos/file_system_provider/operations/read_directory.h new file mode 100644 index 0000000..68176d9 --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/read_directory.h @@ -0,0 +1,55 @@ +// Copyright 2014 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 CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_ +#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_ + +#include "base/files/file.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/chromeos/file_system_provider/operations/operation.h" +#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" +#include "chrome/browser/chromeos/file_system_provider/request_value.h" +#include "webkit/browser/fileapi/async_file_util.h" + +namespace base { +class FilePath; +} // namespace base + +namespace extensions { +class EventRouter; +} // namespace extensions + +namespace chromeos { +namespace file_system_provider { +namespace operations { + +// Bridge between fileapi read directory operation and providing extension's +// read directory request. Created per request. +class ReadDirectory : public Operation { + public: + ReadDirectory(extensions::EventRouter* event_router, + const ProvidedFileSystemInfo& file_system_info, + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback); + virtual ~ReadDirectory(); + + // Operation overrides. + virtual bool Execute(int request_id) OVERRIDE; + virtual void OnSuccess(int request_id, + scoped_ptr<RequestValue> result, + bool has_next) OVERRIDE; + virtual void OnError(int request_id, base::File::Error error) OVERRIDE; + + private: + base::FilePath directory_path_; + const fileapi::AsyncFileUtil::ReadDirectoryCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(ReadDirectory); +}; + +} // namespace operations +} // namespace file_system_provider +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_ diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc b/chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc new file mode 100644 index 0000000..1158ddf --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc @@ -0,0 +1,269 @@ +// Copyright 2014 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/files/file.h" +#include "base/files/file_path.h" +#include "base/json/json_reader.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h" +#include "chrome/common/extensions/api/file_system_provider.h" +#include "chrome/common/extensions/api/file_system_provider_internal.h" +#include "extensions/browser/event_router.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/fileapi/async_file_util.h" + +namespace chromeos { +namespace file_system_provider { +namespace operations { +namespace { + +const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; +const int kFileSystemId = 1; +const int kRequestId = 2; +const base::FilePath::CharType kDirectoryPath[] = "/directory"; + +// Fake event dispatcher implementation with extra logging capability. Acts as +// a providing extension end-point. +class LoggingDispatchEventImpl { + public: + explicit LoggingDispatchEventImpl(bool dispatch_reply) + : dispatch_reply_(dispatch_reply) {} + virtual ~LoggingDispatchEventImpl() {} + + bool OnDispatchEventImpl(scoped_ptr<extensions::Event> event) { + events_.push_back(event->DeepCopy()); + return dispatch_reply_; + } + + ScopedVector<extensions::Event>& events() { return events_; } + + private: + ScopedVector<extensions::Event> events_; + bool dispatch_reply_; + + DISALLOW_COPY_AND_ASSIGN(LoggingDispatchEventImpl); +}; + +// Callback invocation logger. Acts as a fileapi end-point. +class CallbackLogger { + public: + class Event { + public: + Event(base::File::Error result, + const fileapi::AsyncFileUtil::EntryList& entry_list, + bool has_more) + : result_(result), entry_list_(entry_list), has_more_(has_more) {} + virtual ~Event() {} + + base::File::Error result() { return result_; } + const fileapi::AsyncFileUtil::EntryList& entry_list() { + return entry_list_; + } + bool has_more() { return has_more_; } + + private: + base::File::Error result_; + fileapi::AsyncFileUtil::EntryList entry_list_; + bool has_more_; + + DISALLOW_COPY_AND_ASSIGN(Event); + }; + + CallbackLogger() : weak_ptr_factory_(this) {} + virtual ~CallbackLogger() {} + + void OnReadDirectory(base::File::Error result, + const fileapi::AsyncFileUtil::EntryList& entry_list, + bool has_more) { + events_.push_back(new Event(result, entry_list, has_more)); + } + + ScopedVector<Event>& events() { return events_; } + + base::WeakPtr<CallbackLogger> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + private: + ScopedVector<Event> events_; + bool dispatch_reply_; + base::WeakPtrFactory<CallbackLogger> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CallbackLogger); +}; + +} // namespace + +class FileSystemProviderOperationsReadDirectoryTest : public testing::Test { + protected: + FileSystemProviderOperationsReadDirectoryTest() {} + virtual ~FileSystemProviderOperationsReadDirectoryTest() {} + + virtual void SetUp() OVERRIDE { + file_system_info_ = + ProvidedFileSystemInfo(kExtensionId, + kFileSystemId, + "" /* file_system_name */, + base::FilePath() /* mount_path */); + } + + ProvidedFileSystemInfo file_system_info_; +}; + +TEST_F(FileSystemProviderOperationsReadDirectoryTest, Execute) { + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + ReadDirectory read_directory(NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnReadDirectory, + callback_logger.GetWeakPtr())); + read_directory.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(read_directory.Execute(kRequestId)); + + ASSERT_EQ(1u, dispatcher.events().size()); + extensions::Event* event = dispatcher.events()[0]; + EXPECT_EQ(extensions::api::file_system_provider::OnReadDirectoryRequested:: + kEventName, + event->event_name); + base::ListValue* event_args = event->event_args.get(); + ASSERT_EQ(3u, event_args->GetSize()); + + int event_file_system_id = -1; + EXPECT_TRUE(event_args->GetInteger(0, &event_file_system_id)); + EXPECT_EQ(kFileSystemId, event_file_system_id); + + int event_request_id = -1; + EXPECT_TRUE(event_args->GetInteger(1, &event_request_id)); + EXPECT_EQ(kRequestId, event_request_id); + + std::string event_directory_path; + EXPECT_TRUE(event_args->GetString(2, &event_directory_path)); + EXPECT_EQ(kDirectoryPath, event_directory_path); +} + +TEST_F(FileSystemProviderOperationsReadDirectoryTest, Execute_NoListener) { + LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */); + CallbackLogger callback_logger; + + ReadDirectory read_directory(NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnReadDirectory, + callback_logger.GetWeakPtr())); + read_directory.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_FALSE(read_directory.Execute(kRequestId)); +} + +TEST_F(FileSystemProviderOperationsReadDirectoryTest, OnSuccess) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params; + + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + ReadDirectory read_directory(NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnReadDirectory, + callback_logger.GetWeakPtr())); + read_directory.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(read_directory.Execute(kRequestId)); + + // Sample input as JSON. Keep in sync with file_system_provider_api.idl. + // As for now, it is impossible to create *::Params class directly, not from + // base::Value. + const std::string input = + "[\n" + " 1,\n" // kFileSystemId + " 2,\n" // kRequestId + " [\n" + " {\n" + " \"isDirectory\": false,\n" + " \"name\": \"blueberries.txt\",\n" + " \"size\": 4096,\n" + " \"modificationTime\": {\n" + " \"value\": \"Thu Apr 24 00:46:52 UTC 2014\"\n" + " }\n" + " }\n" + " ],\n" + " false\n" // has_next + "]\n"; + + int json_error_code; + std::string json_error_msg; + scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError( + input, base::JSON_PARSE_RFC, &json_error_code, &json_error_msg)); + ASSERT_TRUE(value.get()) << json_error_msg; + + base::ListValue* value_as_list; + ASSERT_TRUE(value->GetAsList(&value_as_list)); + scoped_ptr<Params> params(Params::Create(*value_as_list)); + ASSERT_TRUE(params.get()); + scoped_ptr<RequestValue> request_value( + RequestValue::CreateForReadDirectorySuccess(params.Pass())); + ASSERT_TRUE(request_value.get()); + + const bool has_next = false; + read_directory.OnSuccess(kRequestId, request_value.Pass(), has_next); + + ASSERT_EQ(1u, callback_logger.events().size()); + CallbackLogger::Event* event = callback_logger.events()[0]; + EXPECT_EQ(base::File::FILE_OK, event->result()); + + ASSERT_EQ(1u, event->entry_list().size()); + const fileapi::DirectoryEntry entry = event->entry_list()[0]; + EXPECT_FALSE(entry.is_directory); + EXPECT_EQ("blueberries.txt", entry.name); + EXPECT_EQ(4096, entry.size); + base::Time expected_time; + EXPECT_TRUE( + base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014", &expected_time)); + EXPECT_EQ(expected_time, entry.last_modified_time); +} + +TEST_F(FileSystemProviderOperationsReadDirectoryTest, OnError) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params; + + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + ReadDirectory read_directory(NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnReadDirectory, + callback_logger.GetWeakPtr())); + read_directory.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(read_directory.Execute(kRequestId)); + + read_directory.OnError(kRequestId, base::File::FILE_ERROR_TOO_MANY_OPENED); + + ASSERT_EQ(1u, callback_logger.events().size()); + CallbackLogger::Event* event = callback_logger.events()[0]; + EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, event->result()); + ASSERT_EQ(0u, event->entry_list().size()); +} + +} // namespace operations +} // namespace file_system_provider +} // namespace chromeos diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc index 88c5c30..7b372f4 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc @@ -6,6 +6,7 @@ #include "base/files/file.h" #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h" +#include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h" #include "chrome/browser/chromeos/file_system_provider/operations/unmount.h" #include "chrome/browser/chromeos/file_system_provider/request_manager.h" #include "chrome/common/extensions/api/file_system_provider.h" @@ -46,6 +47,18 @@ void ProvidedFileSystem::GetMetadata( } } +void ProvidedFileSystem::ReadDirectory( + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) { + if (!request_manager_.CreateRequest(make_scoped_ptr< + RequestManager::HandlerInterface>(new operations::ReadDirectory( + event_router_, file_system_info_, directory_path, callback)))) { + callback.Run(base::File::FILE_ERROR_SECURITY, + fileapi::AsyncFileUtil::EntryList(), + false /* has_more */); + } +} + const ProvidedFileSystemInfo& ProvidedFileSystem::GetFileSystemInfo() const { return file_system_info_; } diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system.h b/chrome/browser/chromeos/file_system_provider/provided_file_system.h index 551d2c0..6fffe4b 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system.h +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.h @@ -35,6 +35,9 @@ class ProvidedFileSystem : public ProvidedFileSystemInterface { virtual void GetMetadata( const base::FilePath& entry_path, const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) OVERRIDE; + virtual void ReadDirectory( + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) OVERRIDE; virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE; virtual RequestManager* GetRequestManager() OVERRIDE; diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h b/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h index 7cf174d..76be11d 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h @@ -36,6 +36,12 @@ class ProvidedFileSystemInterface { virtual void GetMetadata( const base::FilePath& entry_path, const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) = 0; + // Requests enumerating entries from the passed |directory_path|. The callback + // can be called multiple times until either an error is returned or the + // has_more field is set to false. + virtual void ReadDirectory( + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::ReadDirectoryCallback& callback) = 0; // Returns a provided file system info for this file system. virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const = 0; diff --git a/chrome/browser/chromeos/file_system_provider/request_value.cc b/chrome/browser/chromeos/file_system_provider/request_value.cc index 5bc8e42..ac85e66 100644 --- a/chrome/browser/chromeos/file_system_provider/request_value.cc +++ b/chrome/browser/chromeos/file_system_provider/request_value.cc @@ -29,6 +29,14 @@ scoped_ptr<RequestValue> RequestValue::CreateForGetMetadataSuccess( return result.Pass(); } +scoped_ptr<RequestValue> RequestValue::CreateForReadDirectorySuccess( + scoped_ptr<extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params> params) { + scoped_ptr<RequestValue> result(new RequestValue); + result->read_directory_success_params_ = params.Pass(); + return result.Pass(); +} + scoped_ptr<RequestValue> RequestValue::CreateForTesting( const std::string& params) { scoped_ptr<RequestValue> result(new RequestValue); diff --git a/chrome/browser/chromeos/file_system_provider/request_value.h b/chrome/browser/chromeos/file_system_provider/request_value.h index 8928733..2519473 100644 --- a/chrome/browser/chromeos/file_system_provider/request_value.h +++ b/chrome/browser/chromeos/file_system_provider/request_value.h @@ -31,6 +31,9 @@ class RequestValue { static scoped_ptr<RequestValue> CreateForGetMetadataSuccess( scoped_ptr<extensions::api::file_system_provider_internal:: GetMetadataRequestedSuccess::Params> params); + static scoped_ptr<RequestValue> CreateForReadDirectorySuccess( + scoped_ptr<extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params> params); static scoped_ptr<RequestValue> CreateForTesting(const std::string& params); @@ -46,6 +49,12 @@ class RequestValue { return get_metadata_success_params_.get(); } + const extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params* + read_directory_success_params() const { + return read_directory_success_params_.get(); + } + const std::string* testing_params() const { return testing_params_.get(); } private: @@ -54,6 +63,9 @@ class RequestValue { scoped_ptr<extensions::api::file_system_provider_internal:: GetMetadataRequestedSuccess::Params> get_metadata_success_params_; + scoped_ptr<extensions::api::file_system_provider_internal:: + ReadDirectoryRequestedSuccess::Params> + read_directory_success_params_; scoped_ptr<std::string> testing_params_; DISALLOW_COPY_AND_ASSIGN(RequestValue); diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 4b449eb..58cf1fd 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -384,6 +384,8 @@ 'browser/chromeos/file_system_provider/operations/get_metadata.h', 'browser/chromeos/file_system_provider/operations/operation.cc', 'browser/chromeos/file_system_provider/operations/operation.h', + 'browser/chromeos/file_system_provider/operations/read_directory.cc', + 'browser/chromeos/file_system_provider/operations/read_directory.h', 'browser/chromeos/file_system_provider/operations/unmount.cc', 'browser/chromeos/file_system_provider/operations/unmount.h', 'browser/chromeos/file_system_provider/provided_file_system.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 6a9e621..68f1f65 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -713,6 +713,7 @@ 'browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc', 'browser/chromeos/file_system_provider/mount_path_util_unittest.cc', 'browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc', + 'browser/chromeos/file_system_provider/operations/read_directory_unittest.cc', 'browser/chromeos/file_system_provider/provided_file_system_unittest.cc', 'browser/chromeos/file_system_provider/request_manager_unittest.cc', 'browser/chromeos/file_system_provider/service_unittest.cc', diff --git a/chrome/common/extensions/api/file_system_provider.idl b/chrome/common/extensions/api/file_system_provider.idl index 5ca4911..f8ed6ba 100644 --- a/chrome/common/extensions/api/file_system_provider.idl +++ b/chrome/common/extensions/api/file_system_provider.idl @@ -69,6 +69,12 @@ namespace fileSystemProvider { // Success callback for the <code>onGetMetadataRequested</code> event. callback MetadataCallback = void(EntryMetadata metadata); + // Success callback for the <code>onDirectoryRequested</code> event. If more + // entries will be returned, then <code>hasNext</code> must be true, and it + // has to be called again with additional entries. If no more entries are + // available, then <code>hasNext</code> must be set to false. + callback EntriesCallback = void(ResourceEntry[] entries, bool hasNext); + interface Functions { // Mounts a file system with the given <code>displayName</code>. // <code>displayName</code> will be shown in the left panel of @@ -85,7 +91,7 @@ namespace fileSystemProvider { // the providing extension can decide to perform unmounting if not requested // (eg. in case of lost connection, or a file error). If there is no file // system with the requested id, or unmounting fails, then the - // <code>errorCallback</code> will be called. + // <code>errorCallback</code> must be called. static void unmount(long fileSystemId, UnmountCallback successCallback, [nocompile] ErrorCallback errorCallback); @@ -93,10 +99,10 @@ namespace fileSystemProvider { interface Events { // Raised when unmounting for the file system with the <code>fileSystemId - // </code> identifier is requested. In response, the <code>unmount</code> - // API method should be called together with <code>successCallback</code>. - // If unmounting is not possible (eg. due to a pending operation), then - // <code>errorCallback</code> must be called. + // </code> identifier is requested. In the response, the <code>unmount + // </code> API method should be called together with <code>successCallback + // </code>. If unmounting is not possible (eg. due to a pending operation), + // then <code>errorCallback</code> must be called. [maxListeners=1] static void onUnmountRequested( long fileSystemId, ProviderSuccessCallback successCallback, @@ -111,6 +117,16 @@ namespace fileSystemProvider { DOMString entryPath, MetadataCallback successCallback, ErrorCallback errorCallback); + + // Raised when contents of a directory at <code>directoryPath</code> are + // requested. The results should be returned in chunks by calling the <code> + // successCallback</code> several times. In case of an error, <code> + // errorCallback</code> must be called. + [maxListeners=1] static void onReadDirectoryRequested( + long fileSystemId, + DOMString directoryPath, + EntriesCallback successCallback, + ErrorCallback errorCallback); }; }; diff --git a/chrome/common/extensions/api/file_system_provider_internal.idl b/chrome/common/extensions/api/file_system_provider_internal.idl index 30ebbc7..7a80d5b 100644 --- a/chrome/common/extensions/api/file_system_provider_internal.idl +++ b/chrome/common/extensions/api/file_system_provider_internal.idl @@ -33,6 +33,21 @@ namespace fileSystemProviderInternal { long fileSystemId, long requestId, fileSystemProvider.ProviderError error); + + // Internal. Success callback of the <code>onReadDirectoryRequested</code> + // event. Can be called multiple times per request. + static void readDirectoryRequestedSuccess( + long fileSystemId, + long requestId, + fileSystemProvider.EntryMetadata[] entries, + boolean hasNext); + + // Internal. Error callback of the <code>onReadDirectoryRequested</code> + // event. Must be called when reading a directory fails. + static void readDirectoryRequestedError( + long fileSystemId, + long requestId, + fileSystemProvider.ProviderError error); }; }; diff --git a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js index 37fb8ee..f59900c 100644 --- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js @@ -11,6 +11,34 @@ var eventBindings = require('event_bindings'); var fileSystemNatives = requireNative('file_system_natives'); var GetDOMError = fileSystemNatives.GetDOMError; +/** + * Annotates a date with its serialized value. + * @param {Date} date Input date. + * @return {Date} Date with an extra <code>value</code> attribute. + */ +function annotateDate(date) { + // Copy in case the input date is frozen. + var result = new Date(date.getTime()); + result.value = result.toString(); + return result; +} + +/** + * Annotates an entry metadata by serializing its modifiedTime value. + * @param {EntryMetadata} metadata Input metadata. + * @return {EntryMetadata} metadata Annotated metadata, which can be passed + * back to the C++ layer. + */ +function annotateMetadata(metadata) { + var result = { + isDirectory: metadata.isDirectory, + name: metadata.name, + size: metadata.size, + modificationTime: annotateDate(metadata.modificationTime) + }; + return result; +} + binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; @@ -104,10 +132,8 @@ eventBindings.registerArgumentMassager( var requestId = args[1]; var entryPath = args[2]; var onSuccessCallback = function(metadata) { - // Serialize the Date as a string. - metadata.modificationTime.value = metadata.modificationTime.toString(); fileSystemProviderInternal.getMetadataRequestedSuccess( - fileSystemId, requestId, metadata); + fileSystemId, requestId, annotateMetadata(metadata)); }; var onErrorCallback = function(error) { fileSystemProviderInternal.getMetadataRequestedError( @@ -116,4 +142,23 @@ eventBindings.registerArgumentMassager( dispatch([fileSystemId, entryPath, onSuccessCallback, onErrorCallback]); }); +eventBindings.registerArgumentMassager( + 'fileSystemProvider.onReadDirectoryRequested', + function(args, dispatch) { + var fileSystemId = args[0]; + var requestId = args[1]; + var directoryPath = args[2]; + var onSuccessCallback = function(entries, hasNext) { + var annotatedEntries = entries.map(annotateMetadata); + fileSystemProviderInternal.readDirectoryRequestedSuccess( + fileSystemId, requestId, annotatedEntries, hasNext); + }; + var onErrorCallback = function(error) { + fileSystemProviderInternal.readDirectoryRequestedError( + fileSystemId, requestId, error); + } + dispatch([ + fileSystemId, directoryPath, onSuccessCallback, onErrorCallback]); + }); + exports.binding = binding.generate(); diff --git a/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js index a93a323..1e78b9c 100644 --- a/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js +++ b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js @@ -36,7 +36,7 @@ var TESTING_FILE = Object.freeze({ * @param {string} entryPath Path of the requested entry. * @param {function(Object)} onSuccess Success callback with metadata passed * an argument. - * @param {function(string}} onError Error callback with an error code. + * @param {function(string)} onError Error callback with an error code. */ function onGetMetadataRequested( inFileSystemId, entryPath, onSuccess, onError) { diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json new file mode 100644 index 0000000..6692f8c --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json @@ -0,0 +1,18 @@ +{ + "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==", + "name": "chrome.fileSystemProvider.onReadDirectoryRequested", + "version": "0.1", + "manifest_version": 2, + "description": + "Test for chrome.fileSystemProvider.onReadDirectoryRequested().", + "permissions": [ + "fileSystemProvider", + "fileBrowserPrivate", + "fileBrowserHandler" + ], + "app": { + "background": { + "scripts": ["test.js"] + } + } +} diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_directory/test.js b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/test.js new file mode 100644 index 0000000..835f2ea --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_system_provider/read_directory/test.js @@ -0,0 +1,201 @@ +// Copyright 2014 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. + +'use strict'; + +var fileSystemId; +var fileSystem; + +/** + * @type {Object} + * @const + */ +var TESTING_ROOT = Object.freeze({ + isDirectory: true, + name: '', + size: 0, + modificationTime: new Date(2014, 4, 28, 10, 39, 15) +}); + +/** + * @type {Object} + * @const + */ +var TESTING_HELLO_DIR = Object.freeze({ + isDirectory: true, + name: 'hello', + size: 0, + modificationTime: new Date(2014, 3, 27, 9, 38, 14) +}); + +/** + * @type {Object} + * @const + */ +var TESTING_CANDIES_DIR = Object.freeze({ + isDirectory: true, + name: 'candies', + size: 0, + modificationTime: new Date(2014, 2, 26, 8, 37, 13) +}); + +/** + * @type {Object} + * @const + */ +var TESTING_TIRAMISU_FILE = Object.freeze({ + isDirectory: false, + name: 'tiramisu.txt', + size: 1986, + modificationTime: new Date(2014, 1, 25, 7, 36, 12) +}); + +/** + * Returns entries in the requested directory. + * + * @param {number} inFileSystemId ID of the file system. + * @param {string} directoryPath Path of the directory. + * @param {function(Array.<Object>, boolean)} onSuccess Success callback with + * a list of entries. May be called multiple times. + * @param {function(string)} onError Error callback with an error code. + */ +function onReadDirectoryRequested( + inFileSystemId, directoryPath, onSuccess, onError) { + if (inFileSystemId != fileSystemId) { + onError('SECURITY_ERROR'); // enum ProviderError. + return; + } + + if (directoryPath != '/' + TESTING_HELLO_DIR.name) { + onError('NOT_FOUND'); // enum ProviderError. + return; + } + + onSuccess([TESTING_TIRAMISU_FILE], true /* has_next */); + onSuccess([TESTING_CANDIES_DIR], false /* has_next */); +} + +/** + * Returns metadata for the requested entry. + * + * To successfully acquire a DirectoryEntry, or even a DOMFileSystem, this event + * must be implemented and return correct values. + * + * @param {number} inFileSystemId ID of the file system. + * @param {string} entryPath Path of the requested entry. + * @param {function(Object)} onSuccess Success callback with metadata passed + * an argument. + * @param {function(string)} onError Error callback with an error code. + */ +function onGetMetadataRequested( + inFileSystemId, entryPath, onSuccess, onError) { + if (inFileSystemId != fileSystemId) { + onError('SECURITY_ERROR'); // enum ProviderError. + return; + } + + if (entryPath == '/') { + onSuccess(TESTING_ROOT); + return; + } + + if (entryPath == '/' + TESTING_HELLO_DIR.name) { + onSuccess(TESTING_HELLO_DIR); + return; + } + + onError('NOT_FOUND'); // enum ProviderError. +} + +/** + * Sets up the tests. Called once per all test cases. In case of a failure, + * the callback is not called. + * + * @param {function()} callback Success callback. + */ +function setUp(callback) { + chrome.fileSystemProvider.mount('chocolate.zip', function(id) { + fileSystemId = id; + chrome.fileSystemProvider.onReadDirectoryRequested.addListener( + onReadDirectoryRequested); + chrome.fileSystemProvider.onGetMetadataRequested.addListener( + onGetMetadataRequested); + var volumeId = + 'provided:' + chrome.runtime.id + '-' + fileSystemId + '-user'; + + chrome.fileBrowserPrivate.requestFileSystem( + volumeId, + function(inFileSystem) { + chrome.test.assertTrue(!!inFileSystem); + + fileSystem = inFileSystem; + callback(); + }); + }, function() { + chrome.test.fail(); + }); +} + +/** + * Runs all of the test cases, one by one. + */ +function runTests() { + chrome.test.runTests([ + // Read contents of the /hello directory. This directory exists, so it + // should succeed. + function readEntriesSuccess() { + var onTestSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getDirectory( + 'hello', + {create: false}, + function(dirEntry) { + var dirReader = dirEntry.createReader(); + var entries = []; + var readEntriesNext = function() { + dirReader.readEntries(function(inEntries) { + Array.prototype.push.apply(entries, inEntries); + if (!inEntries.length) { + // No more entries, so verify. + chrome.test.assertEq(2, entries.length); + chrome.test.assertTrue(entries[0].isFile); + chrome.test.assertEq('tiramisu.txt', entries[0].name); + chrome.test.assertEq( + '/hello/tiramisu.txt', entries[0].fullPath); + chrome.test.assertTrue(entries[1].isDirectory); + chrome.test.assertEq('candies', entries[1].name); + chrome.test.assertEq('/hello/candies', entries[1].fullPath); + onTestSuccess(); + } else { + readEntriesNext(); + } + }, function(error) { + chrome.test.fail(); + }); + }; + readEntriesNext(); + }, + function(error) { + chrome.test.fail(); + }); + }, + // Read contents of a directory which does not exist, what should return an + // error. + function readEntriesError() { + var onTestSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getDirectory( + 'cranberries', + {create: false}, + function(dirEntry) { + chrome.test.fail(); + }, + function(error) { + chrome.test.assertEq('NotFoundError', error.name); + onTestSuccess(); + }); + } + ]); +} + +// Setup and run all of the test cases. +setUp(runTests); |