diff options
29 files changed, 946 insertions, 71 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 1bbd5c5..4aadc26 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 @@ -107,6 +107,8 @@ bool FileSystemProviderMountFunction::RunSync() { Service* service = Service::Get(GetProfile()); DCHECK(service); + if (!service) + return false; int file_system_id = service->MountFileSystem(extension_id(), params->display_name); @@ -132,11 +134,13 @@ bool FileSystemProviderMountFunction::RunSync() { bool FileSystemProviderUnmountFunction::RunSync() { using api::file_system_provider::Unmount::Params; - const scoped_ptr<Params> params(Params::Create(*args_)); + scoped_ptr<Params> params(Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); Service* service = Service::Get(GetProfile()); DCHECK(service); + if (!service) + return false; if (!service->UnmountFileSystem(extension_id(), params->file_system_id)) { // TODO(mtomasz): Pass more detailed errors, rather than just a bool. @@ -158,6 +162,8 @@ bool FileSystemProviderInternalUnmountRequestedSuccessFunction::RunSync() { Service* service = Service::Get(GetProfile()); DCHECK(service); + if (!service) + return false; ProvidedFileSystemInterface* file_system = service->GetProvidedFileSystem(extension_id(), params->file_system_id); @@ -197,6 +203,89 @@ bool FileSystemProviderInternalUnmountRequestedErrorFunction::RunSync() { Service* service = Service::Get(GetProfile()); DCHECK(service); + if (!service) + return false; + + ProvidedFileSystemInterface* file_system = + service->GetProvidedFileSystem(extension_id(), params->file_system_id); + if (!file_system) { + base::ListValue* result = new base::ListValue(); + result->Append( + CreateError(kNotFoundErrorName, kResponseFailedErrorMessage)); + SetResult(result); + return false; + } + + RequestManager* request_manager = file_system->GetRequestManager(); + DCHECK(request_manager); + + if (!request_manager->RejectRequest( + params->request_id, ProviderErrorToFileError(params->error))) { + // TODO(mtomasz): Pass more detailed errors, rather than just a bool. + base::ListValue* result = new base::ListValue(); + result->Append( + CreateError(kSecurityErrorName, kResponseFailedErrorMessage)); + SetResult(result); + return false; + } + + base::ListValue* result = new base::ListValue(); + SetResult(result); + return true; +} + +bool FileSystemProviderInternalGetMetadataRequestedSuccessFunction::RunSync() { + // TODO(mtomasz): Create a common class for these internal functions so + // most of the code could be easily reused. + using api::file_system_provider_internal::GetMetadataRequestedSuccess::Params; + scoped_ptr<Params> params(Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + Service* service = Service::Get(GetProfile()); + DCHECK(service); + if (!service) + return false; + + ProvidedFileSystemInterface* file_system = + service->GetProvidedFileSystem(extension_id(), params->file_system_id); + if (!file_system) { + base::ListValue* result = new base::ListValue(); + result->Append( + CreateError(kNotFoundErrorName, kResponseFailedErrorMessage)); + SetResult(result); + return false; + } + + RequestManager* request_manager = file_system->GetRequestManager(); + DCHECK(request_manager); + + const int request_id = params->request_id; + if (!request_manager->FulfillRequest( + request_id, + RequestValue::CreateForGetMetadataSuccess(params.Pass()), + false /* has_more */)) { + // TODO(mtomasz): Pass more detailed errors, rather than just a bool. + base::ListValue* result = new base::ListValue(); + result->Append( + CreateError(kSecurityErrorName, kResponseFailedErrorMessage)); + SetResult(result); + return false; + } + + base::ListValue* result = new base::ListValue(); + SetResult(result); + return true; +} + +bool FileSystemProviderInternalGetMetadataRequestedErrorFunction::RunSync() { + using api::file_system_provider_internal::UnmountRequestedError::Params; + const scoped_ptr<Params> params(Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + Service* service = Service::Get(GetProfile()); + DCHECK(service); + if (!service) + return false; ProvidedFileSystemInterface* file_system = service->GetProvidedFileSystem(extension_id(), params->file_system_id); 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 d5c945d..7e7deb2 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 @@ -53,6 +53,30 @@ class FileSystemProviderInternalUnmountRequestedErrorFunction virtual bool RunSync() OVERRIDE; }; +class FileSystemProviderInternalGetMetadataRequestedSuccessFunction + : public ChromeSyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "fileSystemProviderInternal.getMetadataRequestedSuccess", + FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDSUCCESS) + + protected: + virtual ~FileSystemProviderInternalGetMetadataRequestedSuccessFunction() {} + virtual bool RunSync() OVERRIDE; +}; + +class FileSystemProviderInternalGetMetadataRequestedErrorFunction + : public ChromeSyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "fileSystemProviderInternal.getMetadataRequestedError", + FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDERROR) + + protected: + virtual ~FileSystemProviderInternalGetMetadataRequestedErrorFunction() {} + virtual bool RunSync() 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 c1317e0..bef565d 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 @@ -29,4 +29,10 @@ IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, Unmount) { << message_; } +IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, GetMetadata) { + ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/get_metadata", + kFlagLoadAsComponent)) + << message_; +} + } // namespace extensions 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 68a424d..8e54dd0 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 @@ -24,6 +24,32 @@ void FakeProvidedFileSystem::RequestUnmount( FROM_HERE, base::Bind(callback, base::File::FILE_OK)); } +void FakeProvidedFileSystem::GetMetadata( + const base::FilePath& entry_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) { + // Return fake metadata for the root directory only. + if (entry_path.AsUTF8Unsafe() != "/") { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind( + callback, base::File::FILE_ERROR_NOT_FOUND, base::File::Info())); + return; + } + + base::File::Info file_info; + file_info.size = 0; + file_info.is_directory = true; + file_info.is_symbolic_link = false; + base::Time last_modified_time; + const bool result = base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014", + &last_modified_time); + DCHECK(result); + file_info.last_modified = last_modified_time; + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, base::Bind(callback, base::File::FILE_OK, file_info)); +} + 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 60a9f63..0d37332 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 @@ -5,8 +5,6 @@ #ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FAKE_PROVIDED_FILE_SYSTEM_H_ #define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FAKE_PROVIDED_FILE_SYSTEM_H_ -#include <string> - #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" @@ -30,6 +28,9 @@ class FakeProvidedFileSystem : public ProvidedFileSystemInterface { // ProvidedFileSystemInterface overrides. virtual void RequestUnmount( const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE; + virtual void GetMetadata( + const base::FilePath& entry_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& 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 b4a5cce..3ec9952 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 @@ -5,9 +5,12 @@ #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h" #include "base/callback.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/platform_file.h" +#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" +#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" #include "content/public/browser/browser_thread.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_url.h" @@ -18,6 +21,31 @@ using content::BrowserThread; namespace chromeos { namespace file_system_provider { namespace internal { +namespace { + +// Executes GetFileInfo on the UI thread. +void GetFileInfoOnUIThread( + scoped_ptr<fileapi::FileSystemOperationContext> context, + const fileapi::FileSystemURL& url, + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) { + util::FileSystemURLParser parser(url); + if (!parser.Parse()) { + callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); + return; + } + + parser.file_system()->GetMetadata(parser.file_path(), callback); +} + +// Routes the response of GetFileInfo back to the IO thread. +void OnGetFileInfo(const fileapi::AsyncFileUtil::GetFileInfoCallback& callback, + base::File::Error result, + const base::File::Info& file_info) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, base::Bind(callback, result, file_info)); +} + +} // namespace ProviderAsyncFileUtil::ProviderAsyncFileUtil() {} @@ -69,8 +97,12 @@ void ProviderAsyncFileUtil::GetFileInfo( const fileapi::FileSystemURL& url, const GetFileInfoCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - NOTIMPLEMENTED(); - callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&GetFileInfoOnUIThread, + base::Passed(&context), + url, + base::Bind(&OnGetFileInfo, callback))); } void ProviderAsyncFileUtil::ReadDirectory( 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 8b18bb8..2fe8f48 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 @@ -11,11 +11,17 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/platform_file.h" +#include "base/run_loop.h" +#include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h" #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h" -#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" +#include "chrome/browser/chromeos/file_system_provider/service.h" +#include "chrome/browser/chromeos/file_system_provider/service_factory.h" +#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_file_system_context.h" +#include "extensions/browser/extension_registry.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/browser/fileapi/async_file_util.h" #include "webkit/browser/fileapi/external_mount_points.h" @@ -28,7 +34,6 @@ namespace file_system_provider { namespace { const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; -const int kFileSystemId = 1; // Logs callbacks invocations on the tested operations. // TODO(mtomasz): Store and verify more arguments, once the operations return @@ -86,42 +91,6 @@ class EventLogger { DISALLOW_COPY_AND_ASSIGN(EventLogger); }; -// Registers an external mount point, and removes it once the object gets out -// of scope. To ensure that creating the mount point succeeded, call is_valid(). -class ScopedExternalMountPoint { - public: - ScopedExternalMountPoint(const std::string& mount_point_name, - const base::FilePath& mount_path, - fileapi::FileSystemType type) - : mount_point_name_(mount_point_name) { - fileapi::ExternalMountPoints* const mount_points = - fileapi::ExternalMountPoints::GetSystemInstance(); - DCHECK(mount_points); - is_valid_ = - mount_points->RegisterFileSystem(mount_point_name, - fileapi::kFileSystemTypeProvided, - fileapi::FileSystemMountOption(), - mount_path); - } - - virtual ~ScopedExternalMountPoint() { - if (!is_valid_) - return; - - // If successfully registered in the constructor, then unregister. - fileapi::ExternalMountPoints* const mount_points = - fileapi::ExternalMountPoints::GetSystemInstance(); - DCHECK(mount_points); - mount_points->RevokeFileSystem(mount_point_name_); - } - - bool is_valid() { return is_valid_; } - - private: - const std::string mount_point_name_; - bool is_valid_; -}; - // Creates a cracked FileSystemURL for tests. fileapi::FileSystemURL CreateFileSystemURL(const std::string& mount_point_name, const base::FilePath& file_path) { @@ -134,6 +103,13 @@ fileapi::FileSystemURL CreateFileSystemURL(const std::string& mount_point_name, base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path)); } +// Creates a Service instance. Used to be able to destroy the service in +// TearDown(). +KeyedService* CreateService(content::BrowserContext* context) { + return new Service(Profile::FromBrowserContext(context), + extensions::ExtensionRegistry::Get(context)); +} + } // namespace // Tests in this file are very lightweight and just test integration between @@ -148,17 +124,28 @@ class FileSystemProviderProviderAsyncFileUtilTest : public testing::Test { virtual void SetUp() OVERRIDE { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - profile_.reset(new TestingProfile); + profile_manager_.reset( + new TestingProfileManager(TestingBrowserProcess::GetGlobal())); + ASSERT_TRUE(profile_manager_->SetUp()); + profile_ = profile_manager_->CreateTestingProfile("testing-profile"); async_file_util_.reset(new internal::ProviderAsyncFileUtil); - const base::FilePath mount_path = - util::GetMountPath(profile_.get(), kExtensionId, kFileSystemId); + file_system_context_ = content::CreateFileSystemContextForTesting(NULL, data_dir_.path()); - const std::string mount_point_name = mount_path.BaseName().AsUTF8Unsafe(); - mount_point_.reset(new ScopedExternalMountPoint( - mount_point_name, mount_path, fileapi::kFileSystemTypeProvided)); - ASSERT_TRUE(mount_point_->is_valid()); + ServiceFactory::GetInstance()->SetTestingFactory(profile_, &CreateService); + Service* service = Service::Get(profile_); // Owned by its factory. + service->SetFileSystemFactoryForTests( + base::Bind(&FakeProvidedFileSystem::Create)); + + const int file_system_id = + service->MountFileSystem(kExtensionId, "testing-file-system"); + ASSERT_LT(0, file_system_id); + const ProvidedFileSystemInfo& file_system_info = + service->GetProvidedFileSystem(kExtensionId, file_system_id) + ->GetFileSystemInfo(); + const std::string mount_point_name = + file_system_info.mount_path().BaseName().AsUTF8Unsafe(); file_url_ = CreateFileSystemURL( mount_point_name, base::FilePath::FromUTF8Unsafe("hello/world.txt")); @@ -170,6 +157,12 @@ class FileSystemProviderProviderAsyncFileUtilTest : public testing::Test { ASSERT_TRUE(root_url_.is_valid()); } + virtual void TearDown() OVERRIDE { + // Setting the testing factory to NULL will destroy the created service + // associated with the testing profile. + ServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL); + } + scoped_ptr<fileapi::FileSystemOperationContext> CreateOperationContext() { return make_scoped_ptr( new fileapi::FileSystemOperationContext(file_system_context_.get())); @@ -177,10 +170,10 @@ class FileSystemProviderProviderAsyncFileUtilTest : public testing::Test { content::TestBrowserThreadBundle thread_bundle_; base::ScopedTempDir data_dir_; - scoped_ptr<TestingProfile> profile_; + scoped_ptr<TestingProfileManager> profile_manager_; + TestingProfile* profile_; // Owned by TestingProfileManager. scoped_ptr<fileapi::AsyncFileUtil> async_file_util_; scoped_refptr<fileapi::FileSystemContext> file_system_context_; - scoped_ptr<ScopedExternalMountPoint> mount_point_; fileapi::FileSystemURL file_url_; fileapi::FileSystemURL directory_url_; fileapi::FileSystemURL root_url_; @@ -283,11 +276,12 @@ TEST_F(FileSystemProviderProviderAsyncFileUtilTest, GetFileInfo) { async_file_util_->GetFileInfo( CreateOperationContext(), - file_url_, + root_url_, base::Bind(&EventLogger::OnGetFileInfo, 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, ReadDirectory) { diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util.cc b/chrome/browser/chromeos/file_system_provider/mount_path_util.cc index ed7016d..a7a351d 100644 --- a/chrome/browser/chromeos/file_system_provider/mount_path_util.cc +++ b/chrome/browser/chromeos/file_system_provider/mount_path_util.cc @@ -16,6 +16,9 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; namespace chromeos { namespace file_system_provider { @@ -50,6 +53,8 @@ FileSystemURLParser::~FileSystemURLParser() { } bool FileSystemURLParser::Parse() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (url_.type() != fileapi::kFileSystemTypeProvided) return false; @@ -80,7 +85,7 @@ bool FileSystemURLParser::Parse() { std::vector<base::FilePath::StringType> components; url_.virtual_path().GetComponents(&components); DCHECK_LT(0u, components.size()); - file_path_ = base::FilePath(); + file_path_ = base::FilePath::FromUTF8Unsafe("/"); for (size_t i = 1; i < components.size(); ++i) { // TODO(mtomasz): This could be optimized, to avoid unnecessary copies. file_path_ = file_path_.Append(components[i]); diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util.h b/chrome/browser/chromeos/file_system_provider/mount_path_util.h index 25f6129..91f91e9 100644 --- a/chrome/browser/chromeos/file_system_provider/mount_path_util.h +++ b/chrome/browser/chromeos/file_system_provider/mount_path_util.h @@ -25,16 +25,15 @@ base::FilePath GetMountPath(Profile* profile, int file_system_id); // Finds file system, which is responsible for handling the specified |url| by -// analysing the mount path. -// Also, extract the file path from the virtual path to be used by the file -// system operations. +// analysing the mount path. Also, extract the file path from the virtual path +// to be used by the file system operations. class FileSystemURLParser { public: explicit FileSystemURLParser(const fileapi::FileSystemURL& url); virtual ~FileSystemURLParser(); // Parses the |url| passed to the constructor. If parsing succeeds, then - // returns true. Otherwise, false. + // returns true. Otherwise, false. Must be called on UI thread. bool Parse(); ProvidedFileSystemInterface* file_system() const { return file_system_; } diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc index f4f843b..80fb16a 100644 --- a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc +++ b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc @@ -43,10 +43,12 @@ fileapi::FileSystemURL CreateFileSystemURL(Profile* profile, const fileapi::ExternalMountPoints* const mount_points = fileapi::ExternalMountPoints::GetSystemInstance(); DCHECK(mount_points); + DCHECK(file_path.IsAbsolute()); + base::FilePath relative_path(file_path.value().substr(1)); return mount_points->CreateCrackedFileSystemURL( GURL(origin), fileapi::kFileSystemTypeExternal, - base::FilePath(mount_path.BaseName().Append(file_path))); + base::FilePath(mount_path.BaseName().Append(relative_path))); } // Creates a Service instance. Used to be able to destroy the service in @@ -105,7 +107,8 @@ TEST_F(FileSystemProviderMountPathUtilTest, Parser) { kExtensionId, kFileSystemName); EXPECT_LT(0, file_system_id); - const base::FilePath kFilePath = base::FilePath("hello/world.txt"); + const base::FilePath kFilePath = + base::FilePath::FromUTF8Unsafe("/hello/world.txt"); const fileapi::FileSystemURL url = CreateFileSystemURL(profile_, kExtensionId, file_system_id, kFilePath); EXPECT_TRUE(url.is_valid()); @@ -124,7 +127,7 @@ TEST_F(FileSystemProviderMountPathUtilTest, Parser_RootPath) { kExtensionId, kFileSystemName); EXPECT_LT(0, file_system_id); - const base::FilePath kFilePath = base::FilePath(); + const base::FilePath kFilePath = base::FilePath::FromUTF8Unsafe("/"); const fileapi::FileSystemURL url = CreateFileSystemURL(profile_, kExtensionId, file_system_id, kFilePath); EXPECT_TRUE(url.is_valid()); @@ -143,7 +146,7 @@ TEST_F(FileSystemProviderMountPathUtilTest, Parser_WrongUrl) { kExtensionId, kFileSystemName); EXPECT_LT(0, file_system_id); - const base::FilePath kFilePath = base::FilePath("hello"); + const base::FilePath kFilePath = base::FilePath::FromUTF8Unsafe("/hello"); const fileapi::FileSystemURL url = CreateFileSystemURL( profile_, kExtensionId, file_system_id + 1, kFilePath); // It is impossible to create a cracked URL for a mount point which doesn't diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc b/chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc new file mode 100644 index 0000000..809351a --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc @@ -0,0 +1,85 @@ +// 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/get_metadata.h" + +#include <string> + +#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 |value| into |output|. If parsing fails, then returns false. +bool ConvertRequestValueToFileInfo(scoped_ptr<RequestValue> value, + base::File::Info* output) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params; + + const Params* params = value->get_metadata_success_params(); + if (!params) + return false; + + output->is_directory = params->metadata.is_directory; + output->size = static_cast<int64>(params->metadata.size); + output->is_symbolic_link = false; // Not supported. + + std::string input_modification_time; + if (!params->metadata.modification_time.additional_properties.GetString( + "value", &input_modification_time)) { + return false; + } + if (!base::Time::FromString(input_modification_time.c_str(), + &output->last_modified)) { + return false; + } + + return true; +} + +} // namespace + +GetMetadata::GetMetadata( + extensions::EventRouter* event_router, + const ProvidedFileSystemInfo& file_system_info, + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) + : Operation(event_router, file_system_info), + directory_path_(directory_path), + callback_(callback) { +} + +GetMetadata::~GetMetadata() { +} + +bool GetMetadata::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::OnGetMetadataRequested::kEventName, + values.Pass()); +} + +void GetMetadata::OnSuccess(int /* request_id */, + scoped_ptr<RequestValue> result, + bool has_next) { + base::File::Info file_info; + const bool convert_result = + ConvertRequestValueToFileInfo(result.Pass(), &file_info); + DCHECK(convert_result); + callback_.Run(base::File::FILE_OK, file_info); +} + +void GetMetadata::OnError(int /* request_id */, base::File::Error error) { + callback_.Run(error, base::File::Info()); +} + +} // namespace operations +} // namespace file_system_provider +} // namespace chromeos diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.h b/chrome/browser/chromeos/file_system_provider/operations/get_metadata.h new file mode 100644 index 0000000..804bc3e --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/get_metadata.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_GET_METADATA_H_ +#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_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 GetMetadata : public Operation { + public: + GetMetadata(extensions::EventRouter* event_router, + const ProvidedFileSystemInfo& file_system_info, + const base::FilePath& directory_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback); + virtual ~GetMetadata(); + + // 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::GetFileInfoCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(GetMetadata); +}; + +} // namespace operations +} // namespace file_system_provider +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_ diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc b/chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc new file mode 100644 index 0000000..4b091ac --- /dev/null +++ b/chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc @@ -0,0 +1,256 @@ +// 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/get_metadata.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 base::File::Info& file_info) + : result_(result), file_info_(file_info) {} + virtual ~Event() {} + + base::File::Error result() { return result_; } + const base::File::Info& file_info() { return file_info_; } + + private: + base::File::Error result_; + base::File::Info file_info_; + + DISALLOW_COPY_AND_ASSIGN(Event); + }; + + CallbackLogger() : weak_ptr_factory_(this) {} + virtual ~CallbackLogger() {} + + void OnGetMetadata(base::File::Error result, + const base::File::Info& file_info) { + events_.push_back(new Event(result, file_info)); + } + + 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 FileSystemProviderOperationsGetMetadataTest : public testing::Test { + protected: + FileSystemProviderOperationsGetMetadataTest() {} + virtual ~FileSystemProviderOperationsGetMetadataTest() {} + + virtual void SetUp() OVERRIDE { + file_system_info_ = + ProvidedFileSystemInfo(kExtensionId, + kFileSystemId, + "" /* file_system_name */, + base::FilePath() /* mount_path */); + } + + ProvidedFileSystemInfo file_system_info_; +}; + +TEST_F(FileSystemProviderOperationsGetMetadataTest, Execute) { + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + GetMetadata get_metadata( + NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnGetMetadata, callback_logger.GetWeakPtr())); + get_metadata.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(get_metadata.Execute(kRequestId)); + + ASSERT_EQ(1u, dispatcher.events().size()); + extensions::Event* event = dispatcher.events()[0]; + EXPECT_EQ( + extensions::api::file_system_provider::OnGetMetadataRequested::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(FileSystemProviderOperationsGetMetadataTest, Execute_NoListener) { + LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */); + CallbackLogger callback_logger; + + GetMetadata get_metadata( + NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnGetMetadata, callback_logger.GetWeakPtr())); + get_metadata.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_FALSE(get_metadata.Execute(kRequestId)); +} + +TEST_F(FileSystemProviderOperationsGetMetadataTest, OnSuccess) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params; + + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + GetMetadata get_metadata( + NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnGetMetadata, callback_logger.GetWeakPtr())); + get_metadata.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(get_metadata.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" + " \"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"; + + 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::CreateForGetMetadataSuccess(params.Pass())); + ASSERT_TRUE(request_value.get()); + + const bool has_next = false; + get_metadata.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()); + + const base::File::Info& file_info = event->file_info(); + EXPECT_FALSE(file_info.is_directory); + EXPECT_EQ(4096, file_info.size); + base::Time expected_time; + EXPECT_TRUE( + base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014", &expected_time)); + EXPECT_EQ(expected_time, file_info.last_modified); +} + +TEST_F(FileSystemProviderOperationsGetMetadataTest, OnError) { + using extensions::api::file_system_provider::EntryMetadata; + using extensions::api::file_system_provider_internal:: + GetMetadataRequestedError::Params; + + LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */); + CallbackLogger callback_logger; + + GetMetadata get_metadata( + NULL, + file_system_info_, + base::FilePath::FromUTF8Unsafe(kDirectoryPath), + base::Bind(&CallbackLogger::OnGetMetadata, callback_logger.GetWeakPtr())); + get_metadata.SetDispatchEventImplForTesting( + base::Bind(&LoggingDispatchEventImpl::OnDispatchEventImpl, + base::Unretained(&dispatcher))); + + EXPECT_TRUE(get_metadata.Execute(kRequestId)); + + get_metadata.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()); +} + +} // 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 5b39f33..88c5c30 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/file_system_provider/provided_file_system.h" #include "base/files/file.h" +#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.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" @@ -34,6 +35,17 @@ void ProvidedFileSystem::RequestUnmount( } } +void ProvidedFileSystem::GetMetadata( + const base::FilePath& entry_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) { + if (!request_manager_.CreateRequest( + make_scoped_ptr<RequestManager::HandlerInterface>( + new operations::GetMetadata( + event_router_, file_system_info_, entry_path, callback)))) { + callback.Run(base::File::FILE_ERROR_SECURITY, base::File::Info()); + } +} + 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 90bfcea..551d2c0 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system.h +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.h @@ -32,6 +32,9 @@ class ProvidedFileSystem : public ProvidedFileSystemInterface { // ProvidedFileSystemInterface overrides. virtual void RequestUnmount( const fileapi::AsyncFileUtil::StatusCallback& callback) OVERRIDE; + virtual void GetMetadata( + const base::FilePath& entry_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& 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 2af2430..7cf174d 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 @@ -31,6 +31,12 @@ class ProvidedFileSystemInterface { virtual void RequestUnmount( const fileapi::AsyncFileUtil::StatusCallback& callback) = 0; + // Requests metadata of the passed |entry_path|. It can be either a file + // or a directory. + virtual void GetMetadata( + const base::FilePath& entry_path, + const fileapi::AsyncFileUtil::GetFileInfoCallback& 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/provided_file_system_unittest.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc index 7adf76c..ba72300 100644 --- a/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(mtomasz): Move these test cases to operations/unmount_unittest.cc. + #include <string> #include <vector> diff --git a/chrome/browser/chromeos/file_system_provider/request_value.cc b/chrome/browser/chromeos/file_system_provider/request_value.cc index 105e6b0..5bc8e42 100644 --- a/chrome/browser/chromeos/file_system_provider/request_value.cc +++ b/chrome/browser/chromeos/file_system_provider/request_value.cc @@ -21,6 +21,14 @@ scoped_ptr<RequestValue> RequestValue::CreateForUnmountSuccess( return result.Pass(); } +scoped_ptr<RequestValue> RequestValue::CreateForGetMetadataSuccess( + scoped_ptr<extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params> params) { + scoped_ptr<RequestValue> result(new RequestValue); + result->get_metadata_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 33872b0c..8928733 100644 --- a/chrome/browser/chromeos/file_system_provider/request_value.h +++ b/chrome/browser/chromeos/file_system_provider/request_value.h @@ -28,9 +28,11 @@ class RequestValue { scoped_ptr<extensions::api::file_system_provider_internal:: UnmountRequestedSuccess::Params> params); - static scoped_ptr<RequestValue> CreateForTesting(const std::string& params); + static scoped_ptr<RequestValue> CreateForGetMetadataSuccess( + scoped_ptr<extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params> params); - const std::string* testing_params() const { return testing_params_.get(); } + static scoped_ptr<RequestValue> CreateForTesting(const std::string& params); const extensions::api::file_system_provider_internal:: UnmountRequestedSuccess::Params* @@ -38,9 +40,20 @@ class RequestValue { return unmount_success_params_.get(); } + const extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params* + get_metadata_success_params() const { + return get_metadata_success_params_.get(); + } + + const std::string* testing_params() const { return testing_params_.get(); } + private: scoped_ptr<extensions::api::file_system_provider_internal:: UnmountRequestedSuccess::Params> unmount_success_params_; + scoped_ptr<extensions::api::file_system_provider_internal:: + GetMetadataRequestedSuccess::Params> + get_metadata_success_params_; scoped_ptr<std::string> testing_params_; DISALLOW_COPY_AND_ASSIGN(RequestValue); diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc index 5b793c4..a040fc7 100644 --- a/chrome/browser/chromeos/fileapi/file_system_backend.cc +++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc @@ -81,6 +81,7 @@ bool FileSystemBackend::CanHandleType(fileapi::FileSystemType type) const { case fileapi::kFileSystemTypeNativeLocal: case fileapi::kFileSystemTypeNativeForPlatformApp: case fileapi::kFileSystemTypeDeviceMediaAsFileStorage: + case fileapi::kFileSystemTypeProvided: return true; default: return false; @@ -267,7 +268,8 @@ fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation( DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal || url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal || - url.type() == fileapi::kFileSystemTypeDrive); + url.type() == fileapi::kFileSystemTypeDrive || + url.type() == fileapi::kFileSystemTypeProvided); return fileapi::FileSystemOperation::Create( url, context, make_scoped_ptr(new fileapi::FileSystemOperationContext(context))); diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index a16601a..d5d3bae 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -381,6 +381,8 @@ 'browser/chromeos/file_system_provider/mount_path_util.cc', 'browser/chromeos/file_system_provider/mount_path_util.h', 'browser/chromeos/file_system_provider/observer.h', + 'browser/chromeos/file_system_provider/operations/get_metadata.cc', + '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/unmount.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index e15e05a..118b8e6 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -714,6 +714,7 @@ 'browser/chromeos/file_system_provider/fake_provided_file_system.h', '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/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 e10cb8c..d8f8619 100644 --- a/chrome/common/extensions/api/file_system_provider.idl +++ b/chrome/common/extensions/api/file_system_provider.idl @@ -29,6 +29,21 @@ namespace fileSystemProvider { IO }; + // Represents metadata of a file or a directory. + dictionary EntryMetadata { + // True if it is a directory. + boolean isDirectory; + + // Name of this entry (not full path name). + DOMString name; + + // File size in bytes. + double size; + + // The last modified time of this entry. + [instanceOf=Date] object modificationTime; + }; + // Callback to receive the result of mount() function. // <code>fileSystemID</code> will be a unique ID for the file system just // mounted. The ID is used to distinguish multiple file systems mounted @@ -51,6 +66,9 @@ namespace fileSystemProvider { // Callback to handle an error raised from the browser. [nocompile] callback ErrorCallback = void([instanceOf=DOMError] object error); + // Success callback for the <code>onGetMetadataRequested</code> event. + callback MetadataCallback = void(EntryMetadata metadata); + interface Functions { // Mounts a file system with the given <code>displayName</code>. // <code>displayName</code> will be shown in the left panel of @@ -74,15 +92,25 @@ namespace fileSystemProvider { }; interface Events { - // Raised, when the user requests unmounting of the file system with the - // <code>fileSystemId</code> identifier in the Files.app UI. In response, - // the <code>unmount</code> API method should be called. If unmounting is - // not possible (eg. due to pending operation), then <code>errorCallback - // </code> should be called, and <code>unmount</code> should not be called. + // 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. [maxListeners=1] static void onUnmountRequested( long fileSystemId, ProviderSuccessCallback successCallback, ProviderErrorCallback errorCallback); + + // Raised, when metadata of a file or a directory at <code>entryPath</code> + // is requested. The metadata should be returned with the <code> + // successCallback</code> call. In case of an error, <code>errorCallback + // </code> must be called. + [maxListeners=1] static void onGetMetadataRequested( + long fileSystemId, + DOMString entryPath, + MetadataCallback 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 80757bd..30ebbc7 100644 --- a/chrome/common/extensions/api/file_system_provider_internal.idl +++ b/chrome/common/extensions/api/file_system_provider_internal.idl @@ -19,6 +19,20 @@ namespace fileSystemProviderInternal { long fileSystemId, long requestId, fileSystemProvider.ProviderError error); + + // Internal. Success callback of the <code>onGetMetadataRequested</code> + // event. Must be called if metadata is available. + static void getMetadataRequestedSuccess( + long fileSystemId, + long requestId, + fileSystemProvider.EntryMetadata metadata); + + // Internal. Error callback of the <code>onGetMetadataRequested</code> + // event. Must be called when obtaining metadata fails. + static void getMetadataRequestedError( + 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 b896138..37fb8ee 100644 --- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js +++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js @@ -97,4 +97,23 @@ eventBindings.registerArgumentMassager( dispatch([fileSystemId, onSuccessCallback, onErrorCallback]); }); +eventBindings.registerArgumentMassager( + 'fileSystemProvider.onGetMetadataRequested', + function(args, dispatch) { + var fileSystemId = args[0]; + 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); + }; + var onErrorCallback = function(error) { + fileSystemProviderInternal.getMetadataRequestedError( + fileSystemId, requestId, error); + } + dispatch([fileSystemId, entryPath, onSuccessCallback, onErrorCallback]); + }); + exports.binding = binding.generate(); diff --git a/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json new file mode 100644 index 0000000..8d1ef3c --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/manifest.json @@ -0,0 +1,18 @@ +{ + "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==", + "name": "chrome.fileSystemProvider.onGetMetadataRequested", + "version": "0.1", + "manifest_version": 2, + "description": + "Test for chrome.fileSystemProvider.onGetMetadataRequested().", + "permissions": [ + "fileSystemProvider", + "fileBrowserPrivate", + "fileBrowserHandler" + ], + "app": { + "background": { + "scripts": ["test.js"] + } + } +} 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 new file mode 100644 index 0000000..a93a323 --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_system_provider/get_metadata/test.js @@ -0,0 +1,166 @@ +// 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(2013, 3, 27, 9, 38, 14) +}); + +/** + * @type {Object} + * @const + */ +var TESTING_FILE = Object.freeze({ + isDirectory: false, + name: 'tiramisu.txt', + size: 4096, + modificationTime: new Date(2014, 4, 28, 10, 39, 15) +}); + +/** + * Returns metadata for a requested entry. + * + * @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_FILE.name) { + onSuccess(TESTING_FILE); + 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.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 metadata of the root. + function getFileMetadataSuccess() { + var onSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getMetadata( + function(metadata) { + chrome.test.assertEq(TESTING_ROOT.size, metadata.size); + chrome.test.assertEq( + TESTING_ROOT.modificationTime.toString(), + metadata.modificationTime.toString()); + onSuccess(); + }, function(error) { + chrome.test.fail(error.name); + }); + }, + // Read metadata of an existing testing file. + function getFileMetadataSuccess() { + var onSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getFile( + TESTING_FILE.name, + {create: false}, + function(fileEntry) { + chrome.test.assertEq(TESTING_FILE.name, fileEntry.name); + chrome.test.assertEq( + TESTING_FILE.isDirectory, fileEntry.isDirectory); + fileEntry.getMetadata(function(metadata) { + chrome.test.assertEq(TESTING_FILE.size, metadata.size); + chrome.test.assertEq( + TESTING_FILE.modificationTime.toString(), + metadata.modificationTime.toString()); + onSuccess(); + }, function(error) { + chrome.test.fail(error.name); + }); + }, + function(error) { + chrome.test.fail(error.name); + }); + }, + // Read metadata of a directory which does not exist, what should return an + // error. DirectoryEntry.getDirectory() causes fetching metadata. + function getFileMetadataNotFound() { + var onSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getDirectory( + 'cranberries', + {create: false}, + function(dirEntry) { + chrome.test.fail(); + }, + function(error) { + chrome.test.assertEq('NotFoundError', error.name); + onSuccess(); + }); + }, + // Read metadata of a file using getDirectory(). An error should be returned + // because of type mismatching. DirectoryEntry.getDirectory() causes + // fetching metadata. + function getFileMetadataWrongType() { + var onSuccess = chrome.test.callbackPass(function() {}); + fileSystem.root.getDirectory( + TESTING_FILE.name, + {create: false}, + function(fileEntry) { + chrome.test.fail(); + }, + function(error) { + chrome.test.assertEq('TypeMismatchError', error.name); + onSuccess(); + }); + } + ]); +} + +// Setup and run all of the test cases. +setUp(runTests); diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 5486d77..90f8d7f 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -808,6 +808,8 @@ enum HistogramValue { BLUETOOTHSOCKET_GETSOCKETS, WEBSTOREPRIVATE_SIGNINFUNCTION, SHELL_CREATEWINDOW, + FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDSUCCESS, + FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDERROR, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 54b82f0..cb68f98 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -33765,6 +33765,10 @@ Therefore, the affected-histogram name has to have at least one dot in it. <int value="747" label="BLUETOOTHSOCKET_GETSOCKETS"/> <int value="748" label="WEBSTOREPRIVATE_SIGNINFUNCTION"/> <int value="749" label="SHELL_CREATEWINDOW"/> + <int value="750" + label="FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDSUCCESS"/> + <int value="751" + label="FILESYSTEMPROVIDERINTERNAL_GETMETADATAREQUESTEDERROR"/> </enum> <enum name="ExtensionInstallCause" type="int"> |