summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 09:40:33 +0000
committermtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 09:40:33 +0000
commitc90fcaf6517ef913996510df9836bcfff7755d47 (patch)
tree5d8fe5f82d262a6412e6af18a41642c7e2a4d0de
parentd7bd6563eba1a5575bb6a12d985dace47c57423f (diff)
downloadchromium_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
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc24
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h24
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc6
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/provider_function.h2
-rw-r--r--chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc68
-rw-r--r--chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h3
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc35
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc3
-rw-r--r--chrome/browser/chromeos/file_system_provider/operations/read_directory.cc94
-rw-r--r--chrome/browser/chromeos/file_system_provider/operations/read_directory.h55
-rw-r--r--chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc269
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system.cc13
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system.h3
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h6
-rw-r--r--chrome/browser/chromeos/file_system_provider/request_value.cc8
-rw-r--r--chrome/browser/chromeos/file_system_provider/request_value.h12
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/extensions/api/file_system_provider.idl26
-rw-r--r--chrome/common/extensions/api/file_system_provider_internal.idl15
-rw-r--r--chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js51
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js2
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/read_directory/manifest.json18
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/read_directory/test.js201
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);